package com.limegroup.gnutella.spam; import java.util.HashSet; import java.util.Set; import junit.framework.Test; import org.limewire.core.settings.FilterSettings; import org.limewire.core.settings.SearchSettings; import org.limewire.io.ConnectableImpl; import com.google.inject.Injector; import com.limegroup.gnutella.LimeTestUtils; import com.limegroup.gnutella.RemoteFileDesc; import com.limegroup.gnutella.URN; import com.limegroup.gnutella.downloader.RemoteFileDescFactory; import com.limegroup.gnutella.messages.QueryRequest; import com.limegroup.gnutella.messages.QueryRequestFactory; import com.limegroup.gnutella.util.DataUtils; import com.limegroup.gnutella.util.LimeTestCase; import com.limegroup.gnutella.xml.LimeXMLDocument; import com.limegroup.gnutella.xml.LimeXMLDocumentFactory; public class SpamManagerTest extends LimeTestCase { /** keywords */ private static final String badger = " badger "; private static final String mushroom = " mushroom "; private static final String snake = " snake "; /** addresses */ private static final String addr1 = "1.1.1.1"; private static final String addr2 = "2.2.2.2"; private static final String addr3 = "3.3.3.3"; private static final String addr4 = "4.4.4.4"; private static final String blacklistAddress = "5.5.5.5"; private static final String blacklistRange = "5.*.*.*"; /** ports */ private static final int port1 = 1, port2 = 2, port3 = 3, port4 = 4; /** sizes */ private static final int size1 = 1000; private static final int size2 = 2000; private static final int size3 = 3000; private static final int size4 = 4000; /** xml docs */ private static final String xml1 = "<?xml version='1.0'?>" + "<audios xsi:noNamespaceSchemaLocation=" + "'http://www.limewire.com/schemas/audio.xsd'>" + "<audio title='badger'></audio></audios>"; private static final String xml2 = "<?xml version='1.0'?>" + "<videos xsi:noNamespaceSchemaLocation=" + "'http://www.limewire.com/schemas/video.xsd'>" + "<video title='mushroom'></video></videos>"; private static URN urn1, urn2, urn3, urn4; private static LimeXMLDocument doc1, doc2; private SpamManager manager; private LimeXMLDocumentFactory limeXMLDocumentFactory; private QueryRequestFactory queryRequestFactory; private RemoteFileDescFactory remoteFileDescFactory; public SpamManagerTest(String name) { super(name); } public static Test suite() { return buildTestSuite(SpamManagerTest.class); } @Override protected void setUp() throws Exception { SearchSettings.ENABLE_SPAM_FILTER.setValue(true); SearchSettings.FILTER_SPAM_RESULTS.setValue(0.5f); String[] whitelist = new String[] {addr1, addr2, addr3, addr4}; FilterSettings.WHITE_LISTED_IP_ADDRESSES.setValue(whitelist); String[] blacklist = new String[] {blacklistRange}; FilterSettings.HOSTILE_IPS.setValue(blacklist); urn1 = URN.createSHA1Urn("urn:sha1:PLSTHIPQGSSZTS5FJUPAKUZWUGYQYPFB"); urn2 = URN.createSHA1Urn("urn:sha1:ZLSTHIPQGSSZTS5FJUPAKUZWUGZQYPFB"); urn3 = URN.createSHA1Urn("urn:sha1:YLSTHIPQGSSZTS5FJUPAKUZWUGZQYPFB"); urn4 = URN.createSHA1Urn("urn:sha1:XLSTHIPQGSSZTS5FJUPAKUZWUGZQYPFB"); Injector injector = LimeTestUtils.createInjector(); limeXMLDocumentFactory = injector.getInstance(LimeXMLDocumentFactory.class); queryRequestFactory = injector.getInstance(QueryRequestFactory.class); remoteFileDescFactory = injector.getInstance(RemoteFileDescFactory.class); manager = injector.getInstance(SpamManager.class); manager.clearFilterData(); doc1 = limeXMLDocumentFactory.createLimeXMLDocument(xml1); doc2 = limeXMLDocumentFactory.createLimeXMLDocument(xml2); } /** * Tests that incoming results start with a zero spam rating */ public void testStartsAtZero() throws Exception { RemoteFileDesc result = createRFD(addr1, port1, badger, null, urn1, size1); assertFalse(result.isSpam()); assertEquals(0f, result.getSpamRating()); } /** * Tests that when the user marks one result as spam, it affects the * rating of subsequent related results, but not existing results or * subsequent unrelated results */ public void testSetSpam() throws Exception { // Two results arrive RemoteFileDesc marked = createRFD(addr1, port1, badger, null, urn1, size1); RemoteFileDesc related1 = createRFD(addr2, port2, badger+mushroom, null, urn2, size2); // Save the spam ratings before the user's action float markedRating = marked.getSpamRating(); float related1Rating = related1.getSpamRating(); // The user marks the first result as spam - its rating should increase // but the rating of the related result should not manager.handleUserMarkedSpam(new RemoteFileDesc[]{marked}); assertTrue(marked.isSpam()); assertGreaterThan(markedRating, marked.getSpamRating()); assertFalse(related1.isSpam()); assertEquals(related1Rating, related1.getSpamRating()); // Another related result arrives after the user's action - it should // receive a spam rating greater than zero but less than the rating // of the spam result RemoteFileDesc related2 = createRFD(addr3, port3, badger+snake, null, urn3, size3); assertGreaterThan(0f, related2.getSpamRating()); assertLessThan(marked.getSpamRating(), related2.getSpamRating()); // An unrelated result - it should receive a zero spam rating RemoteFileDesc unrelated = createRFD(addr4, port4, mushroom+snake, null, urn4, size4); assertFalse(unrelated.isSpam()); assertEquals(0f, unrelated.getSpamRating()); } /** * Tests that when the user marks one result as not spam, it affects the * rating of subsequent related results, but not existing results */ public void testSetNotSpam() throws Exception { // A result arrives and the user marks it as spam RemoteFileDesc marked = createRFD(addr1, port1, badger, null, urn1, size1); manager.handleUserMarkedSpam(new RemoteFileDesc[]{marked}); assertTrue(marked.isSpam()); float markedRating = marked.getSpamRating(); assertGreaterThan(0f, markedRating); // A related result arrives - it should receive a non-zero spam rating RemoteFileDesc related1 = createRFD(addr2, port2, badger+mushroom, null, urn2, size2); float related1Rating = related1.getSpamRating(); assertGreaterThan(0f, related1Rating); assertLessThan(markedRating, related1Rating); // Now the user marks the first result as NOT spam - its rating should // decrease but the rating of the related result should not manager.handleUserMarkedGood(new RemoteFileDesc[]{marked}); assertFalse(marked.isSpam()); assertLessThan(markedRating, marked.getSpamRating()); assertEquals(related1Rating, related1.getSpamRating()); // Another related result arrives after the user's action - it should // receive a lower rating than the first related result RemoteFileDesc related2 = createRFD(addr3, port3, badger+snake, null, urn3, size3); assertLessThan(related1Rating, related2.getSpamRating()); } /** * Tests that the URN is sufficient to identify spam */ public void testUrnIsEnough() throws Exception { // A result arrives and the user marks it as spam RemoteFileDesc marked = createRFD(addr1, port1, badger, null, urn1, size1); manager.handleUserMarkedSpam(new RemoteFileDesc[]{marked}); assertTrue(marked.isSpam()); assertGreaterThan(0f, marked.getSpamRating()); // Another result arrives, with the same URN but nothing else in // common - it should be marked as spam RemoteFileDesc sameURN = createRFD(addr2, port2, mushroom, null, urn1, size2); assertTrue(sameURN.isSpam()); assertGreaterThan(0f, sameURN.getSpamRating()); } /** * Tests that the ratings are lowered for keywords the user searches for */ public void testQueryLowers() throws Exception { // A result arrives and the user marks it as spam RemoteFileDesc marked = createRFD(addr1, port1, badger, null, urn1, size1); manager.handleUserMarkedSpam(new RemoteFileDesc[]{marked}); assertTrue(marked.isSpam()); assertGreaterThan(0f, marked.getSpamRating()); // A related result arrives - it should receive a non-zero spam rating RemoteFileDesc related1 = createRFD(addr2, port2, badger+mushroom, null, urn2, size2); assertGreaterThan(0f, related1.getSpamRating()); assertLessThan(marked.getSpamRating(), related1.getSpamRating()); // Now the user issues a query with related keywords QueryRequest qr = queryRequestFactory.createQuery(badger+snake); manager.startedQuery(qr); // Another related result arrives - it should receive a lower rating // than the first related result RemoteFileDesc related2 = createRFD(addr3, port3, badger+mushroom, null, urn3, size3); assertLessThan(related1.getSpamRating(), related2.getSpamRating()); } /** * Tests that the address (not port) of the result affects the spam rating */ public void testAddressAffects() throws Exception { // A result arrives and the user marks it as spam RemoteFileDesc marked = createRFD(addr1, port1, badger, null, urn1, size1); manager.handleUserMarkedSpam(new RemoteFileDesc[]{marked}); assertTrue(marked.isSpam()); assertGreaterThan(0f, marked.getSpamRating()); // A result with nothing in common but the address should receive a // non-zero rating (TODO: should it be considered spam?) RemoteFileDesc sameAddress = createRFD(addr1, port2, mushroom, null, urn2, size2); assertGreaterThan(0f, sameAddress.getSpamRating()); assertLessThan(marked.getSpamRating(), sameAddress.getSpamRating()); // A result with nothing in common should receive a zero spam rating RemoteFileDesc unrelated = createRFD(addr3, port3, snake, null, urn3, size3); assertFalse(unrelated.isSpam()); assertEquals(0f, unrelated.getSpamRating()); } /** * Tests that the size of the result affects the spam rating */ public void testSizeAffects() throws Exception { // A result arrives and the user marks it as spam RemoteFileDesc marked = createRFD(addr1, port1, badger, null, urn1, size1); manager.handleUserMarkedSpam(new RemoteFileDesc[]{marked}); assertTrue(marked.isSpam()); assertGreaterThan(0f, marked.getSpamRating()); // A result with nothing in common but the size should receive a // non-zero rating (TODO: should it be considered spam?) RemoteFileDesc sameAddress = createRFD(addr2, port2, mushroom, null, urn2, size1); assertGreaterThan(0f, sameAddress.getSpamRating()); assertLessThan(marked.getSpamRating(), sameAddress.getSpamRating()); // A result with nothing in common should receive a zero spam rating RemoteFileDesc unrelated = createRFD(addr3, port3, snake, null, urn3, size3); assertFalse(unrelated.isSpam()); assertEquals(0f, unrelated.getSpamRating()); } /** * Tests that the approximate size of the result affects the spam rating */ public void testApproximateSizeAffects() throws Exception { int size5 = 1024, size6 = 1025, size7 = 2048; // A result arrives and the user marks it as spam RemoteFileDesc marked = createRFD(addr1, port1, badger, null, urn1, size5); manager.handleUserMarkedSpam(new RemoteFileDesc[]{marked}); assertTrue(marked.isSpam()); assertGreaterThan(0f, marked.getSpamRating()); // A result with nothing in common but similar in size should receive // a non-zero rating (TODO: should it be considered spam?) RemoteFileDesc sameAddress = createRFD(addr2, port2, mushroom, null, urn2, size6); assertGreaterThan(0f, sameAddress.getSpamRating()); assertLessThan(marked.getSpamRating(), sameAddress.getSpamRating()); // A result with nothing in common should receive a zero spam rating RemoteFileDesc unrelated = createRFD(addr3, port3, snake, null, urn3, size7); assertFalse(unrelated.isSpam()); assertEquals(0f, unrelated.getSpamRating()); } /** * Tests that any XML documents in the result affect the spam rating */ public void testXMLAffects() throws Exception { // A result arrives and the user marks it as spam RemoteFileDesc marked = createRFD(addr1, port1, badger, doc1, urn1, size1); manager.handleUserMarkedSpam(new RemoteFileDesc[]{marked}); assertTrue(marked.isSpam()); assertGreaterThan(0f, marked.getSpamRating()); // A result with nothing in common but the XML should receive a // non-zero rating (TODO: should it be considered spam?) RemoteFileDesc sameAddress = createRFD(addr2, port2, mushroom, doc1, urn2, size2); assertGreaterThan(0f, sameAddress.getSpamRating()); assertLessThan(marked.getSpamRating(), sameAddress.getSpamRating()); // A result with nothing in common should receive a zero spam rating RemoteFileDesc unrelated = createRFD(addr3, port3, snake, doc2, urn3, size3); assertFalse(unrelated.isSpam()); assertEquals(0f, unrelated.getSpamRating()); } /** * Tests that the extension of the result affects the spam rating less * than an ordinary keyword */ public void testKeywordBeatsExtension() throws Exception { String ext1 = ".foo", ext2 = ".bar"; // A result arrives and the user marks it as spam RemoteFileDesc marked = createRFD(addr1, port1, badger+ext1, null, urn1, size1); manager.handleUserMarkedSpam(new RemoteFileDesc[]{marked}); assertTrue(marked.isSpam()); assertGreaterThan(0f, marked.getSpamRating()); // A result with nothing in common but the extension should receive a // non-zero rating RemoteFileDesc sameExt = createRFD(addr2, port2, mushroom+ext1, null, urn2, size2); assertGreaterThan(0f, sameExt.getSpamRating()); assertLessThan(marked.getSpamRating(), sameExt.getSpamRating()); // A result with nothing in common but an ordinary keyword should // receive a non-zero rating RemoteFileDesc sameWord = createRFD(addr3, port3, badger+ext2, null, urn3, size3); assertGreaterThan(0f, sameWord.getSpamRating()); assertLessThan(marked.getSpamRating(), sameWord.getSpamRating()); // The extension should have less effect than the keyword assertLessThan(sameWord.getSpamRating(), sameExt.getSpamRating()); // A result with nothing in common should receive a zero spam rating RemoteFileDesc unrelated = createRFD(addr4, port4, snake, null, urn4, size4); assertFalse(unrelated.isSpam()); assertEquals(0f, unrelated.getSpamRating()); } /** * Tests that private (LAN) addresses are ignored */ public void testPrivateAddressIgnored() throws Exception { String privateAddress = "192.168.0.1"; // A result arrives and the user marks it as spam RemoteFileDesc marked = createRFD(privateAddress, port1, badger, null, urn1, size1); manager.handleUserMarkedSpam(new RemoteFileDesc[]{marked}); assertTrue(marked.isSpam()); assertGreaterThan(0f, marked.getSpamRating()); // A result with nothing in common but the private address should // receive a zero rating RemoteFileDesc unrelated = createRFD(privateAddress, port2, mushroom, null, urn2, size2); assertFalse(unrelated.isSpam()); assertEquals(0f, unrelated.getSpamRating()); } public void testBlacklistAffects() throws Exception { // A result arrives from a whitelisted address - not spam RemoteFileDesc white = createRFD(addr1, port1, badger, null, urn1, size1); assertFalse(white.isSpam()); assertEquals(0f, white.getSpamRating()); // An identical result arrives from a blacklisted address - spam RemoteFileDesc black = createRFD(blacklistAddress, port1, badger, null, urn1, size1); assertTrue(black.isSpam()); assertEquals(1f, black.getSpamRating()); } private RemoteFileDesc createRFD(String addr, int port, String name, LimeXMLDocument doc, URN urn, int size) throws Exception { Set<URN> urns = new HashSet<URN>(); urns.add(urn); RemoteFileDesc rfd = remoteFileDescFactory.createRemoteFileDesc(new ConnectableImpl(addr, port, false), 1, name, size, DataUtils.EMPTY_GUID, 3, 3, false, doc, urns, false, "ALT", 0l); // This would normally be called by the SearchResultHandler manager.calculateSpamRating(rfd); return rfd; } }