2121
2222import static fi .iki .elonen .NanoHTTPD .newFixedLengthResponse ;
2323import static org .hamcrest .MatcherAssert .assertThat ;
24+ import static org .hamcrest .Matchers .emptyString ;
2425import static org .hamcrest .Matchers .equalTo ;
2526import static org .hamcrest .Matchers .greaterThan ;
2627import static org .hamcrest .Matchers .hasSize ;
3132import fi .iki .elonen .NanoHTTPD .Response ;
3233import java .util .List ;
3334import java .util .Map ;
35+ import java .util .stream .Stream ;
3436import org .apache .commons .lang3 .ArrayUtils ;
3537import org .junit .jupiter .api .Test ;
3638import org .junit .jupiter .params .ParameterizedTest ;
39+ import org .junit .jupiter .params .provider .Arguments ;
40+ import org .junit .jupiter .params .provider .CsvSource ;
3741import org .junit .jupiter .params .provider .EnumSource ;
42+ import org .junit .jupiter .params .provider .MethodSource ;
3843import org .junit .jupiter .params .provider .ValueSource ;
3944import org .parosproxy .paros .core .scanner .Alert ;
4045import org .parosproxy .paros .core .scanner .Plugin ;
@@ -172,6 +177,13 @@ void shouldAlertIfAttackResponseListsWindowsDirectories() throws Exception {
172177 assertThat (alertsRaised .get (0 ).getAttack (), is (equalTo ("c:/" )));
173178 assertThat (alertsRaised .get (0 ).getRisk (), is (equalTo (Alert .RISK_HIGH )));
174179 assertThat (alertsRaised .get (0 ).getConfidence (), is (equalTo (Alert .CONFIDENCE_MEDIUM )));
180+ assertThat (
181+ alertsRaised .get (0 ).getOtherInfo (),
182+ is (
183+ equalTo (
184+ "While the evidence field indicates Windows, the rule actually "
185+ + "checked that the response contains matches for all of the "
186+ + "following: Windows, Program Files." )));
175187 assertThat (alertsRaised .get (0 ).getAlertRef (), is (equalTo ("6-3" )));
176188 }
177189
@@ -190,6 +202,13 @@ void shouldAlertIfAttackResponseListsLinuxDirectories() throws Exception {
190202 assertThat (alertsRaised .get (0 ).getAttack (), is (equalTo ("/" )));
191203 assertThat (alertsRaised .get (0 ).getRisk (), is (equalTo (Alert .RISK_HIGH )));
192204 assertThat (alertsRaised .get (0 ).getConfidence (), is (equalTo (Alert .CONFIDENCE_MEDIUM )));
205+ assertThat (
206+ alertsRaised .get (0 ).getOtherInfo (),
207+ is (
208+ equalTo (
209+ "While the evidence field indicates etc, the rule actually "
210+ + "checked that the response contains matches for all of the "
211+ + "following: proc, etc, boot, tmp, home." )));
193212 assertThat (alertsRaised .get (0 ).getAlertRef (), is (equalTo ("6-3" )));
194213 }
195214
@@ -208,6 +227,13 @@ void shouldAlertIfAttackResponseListsLinuxDirectoriesInPlainText() throws Except
208227 assertThat (alertsRaised .get (0 ).getAttack (), is (equalTo ("/" )));
209228 assertThat (alertsRaised .get (0 ).getRisk (), is (equalTo (Alert .RISK_HIGH )));
210229 assertThat (alertsRaised .get (0 ).getConfidence (), is (equalTo (Alert .CONFIDENCE_MEDIUM )));
230+ assertThat (
231+ alertsRaised .get (0 ).getOtherInfo (),
232+ is (
233+ equalTo (
234+ "While the evidence field indicates etc, the rule actually "
235+ + "checked that the response contains matches for all of the "
236+ + "following: proc, etc, boot, tmp, home." )));
211237 assertThat (alertsRaised .get (0 ).getAlertRef (), is (equalTo ("6-3" )));
212238 }
213239
@@ -281,6 +307,7 @@ void shouldRaiseAlertIfResponseHasPasswdFileContentAndPayloadIsNullByteBased()
281307 // Then
282308 assertThat (alertsRaised , hasSize (1 ));
283309 assertThat (alertsRaised .get (0 ).getAlertRef (), is (equalTo ("6-2" )));
310+ assertThat (alertsRaised .get (0 ).getOtherInfo (), is (emptyString ()));
284311 }
285312
286313 @ Test
@@ -297,6 +324,7 @@ void shouldRaiseAlertIfResponseHasSystemINIFileContentAndPayloadIsNullByteBased(
297324 // Then
298325 assertThat (alertsRaised , hasSize (1 ));
299326 assertThat (alertsRaised .get (0 ).getAlertRef (), is (equalTo ("6-1" )));
327+ assertThat (alertsRaised .get (0 ).getOtherInfo (), is (emptyString ()));
300328 }
301329
302330 @ ParameterizedTest
@@ -317,6 +345,7 @@ void shouldAlertOnCheckFiveBelowHighThresholdUnderValidConditions(AlertThreshold
317345 assertThat (alertsRaised , hasSize (1 ));
318346 assertThat (alertsRaised .get (0 ).getConfidence (), is (equalTo (Alert .CONFIDENCE_LOW )));
319347 assertThat (alertsRaised .get (0 ).getAlertRef (), is (equalTo ("6-5" )));
348+ assertThat (alertsRaised .get (0 ).getOtherInfo (), is (emptyString ()));
320349 }
321350
322351 @ Test
@@ -365,6 +394,88 @@ void shouldNotAlertOnCheckFiveAtLowThresholdUnderInvalidConditions(String errorT
365394 assertThat (alertsRaised , hasSize (0 ));
366395 }
367396
397+ @ ParameterizedTest
398+ @ CsvSource ({
399+ // Windows will always happen before ignoring Linux, if only Linux pre-check matches
400+ "foo etc root tmp bin boot dev home mnt opt proc bar, 13" ,
401+ "foo Program Files Users Windows bar, 13" ,
402+ "foo etc root tmp bin boot dev home mnt opt proc Program Files Users Windows bar, 11"
403+ })
404+ void shouldSkipDirChecksIfResponseHasDirEvidenceToStartWith (String content , int expected )
405+ throws Exception {
406+ // Given
407+ HttpMessage msg = getHttpMessage ("/?p=v" );
408+ msg .setResponseBody (content );
409+ rule .init (msg , parent );
410+ // When
411+ rule .scan ();
412+ // Then
413+ assertThat (httpMessagesSent , hasSize (equalTo (expected )));
414+ }
415+
416+ private static Stream <Arguments > handlersProvider () {
417+ return Stream .of (
418+ // List Linux dirs on attack, but skip windiws payloads since it has windows
419+ // evidence to start with
420+ Arguments .of (
421+ new ListLinuxDirsOnAttack ("/" , "p" , "/" ),
422+ "foo Program Files Users Windows bar" ,
423+ List .of (
424+ "c:/" ,
425+ "c:\\ " ,
426+ "..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ " ,
427+ "\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ ..\\ " ,
428+ "file:///c:/" ,
429+ "file:///c:\\ " ,
430+ "file:\\ \\ \\ c:\\ " ,
431+ "file:\\ \\ \\ c:/" ,
432+ "file:\\ \\ \\ " ,
433+ "d:\\ " ,
434+ "d:/" ,
435+ "file:///d:/" ,
436+ "file:///d:\\ " ,
437+ "file:\\ \\ \\ d:\\ " ,
438+ "file:\\ \\ \\ d:/" )),
439+ // List win dirs on attack, but skip linux payloads since it has linux
440+ // evidence to start with
441+ Arguments .of (
442+ new ListWinDirsOnAttack ("/" , "p" , "c:/" ),
443+ "foo etc root tmp bin boot dev home mnt opt proc bar" ,
444+ List .of (
445+ "/" ,
446+ "../../../../../../../../../../../../../../../../" ,
447+ "/../../../../../../../../../../../../../../../../" ,
448+ "file:///" )));
449+ }
450+
451+ @ ParameterizedTest
452+ @ MethodSource ("handlersProvider" )
453+ void shouldNotSkipIfGoodCandidateForOnlyOneTech (
454+ NanoServerHandler handler , String baseBody , List <String > skippedPayloads )
455+ throws Exception {
456+ // Given
457+ nano .addHandler (handler );
458+ HttpMessage msg = getHttpMessage ("/?p=v" );
459+ msg .setResponseBody (baseBody );
460+ rule .init (msg , parent );
461+ // When
462+ rule .scan ();
463+ // Then
464+ assertSkippedPayloadsNotUsed (httpMessagesSent , skippedPayloads );
465+ assertThat (alertsRaised , hasSize (equalTo (1 )));
466+ assertThat (alertsRaised .get (0 ).getAlertRef (), is (equalTo ("6-3" )));
467+ }
468+
469+ private static void assertSkippedPayloadsNotUsed (
470+ List <HttpMessage > httpMessagesSent , List <String > skippedPayloads ) {
471+ for (HttpMessage m : httpMessagesSent ) {
472+ String paramVal = m .getUrlParams ().first ().getValue ();
473+ for (String payload : skippedPayloads ) {
474+ assertThat (paramVal .equals (payload ), is (equalTo (false )));
475+ }
476+ }
477+ }
478+
368479 private abstract static class ListDirsOnAttack extends NanoServerHandler {
369480
370481 private final String param ;
0 commit comments