-
-
Notifications
You must be signed in to change notification settings - Fork 734
ascanrules: Address External Redirect False Positives #6677
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
a3c7e33
to
a252c5b
Compare
Great job! No new security vulnerabilities introduced in this pull requestCommunicate with Checkmarx by submitting a PR comment with @Checkmarx followed by one of the supported commands. Learn about the supported commands here. |
10ed03c
to
6b120f2
Compare
...les/src/test/java/org/zaproxy/zap/extension/ascanrules/ExternalRedirectScanRuleUnitTest.java
Outdated
Show resolved
Hide resolved
3c91fd7
to
436c2b7
Compare
Deconflicted |
@@ -531,6 +531,68 @@ void shouldReportRedirectWithJsLocationMethods(String jsMethod) throws Exception | |||
assertThat(alertsRaised.get(0).getEvidence().startsWith(HttpHeader.HTTP), equalTo(true)); | |||
} | |||
|
|||
private static Stream<Arguments> provideCommentStrings() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- This should have malformed escapes and comments.
- Better run the test coverage, no escapes are actually being tested (the escape char needs to be escaped).
- All comments should lead to an alert if it wasn't for the fix, if you revert the scan rule changes and run the test only two cases fail, they all should fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For your first bullet can you be more specific? Or provide some examples?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e.g. \u A
, \x B
, /* not closed …
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got all that, I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Missing cases for
\u{HHH}
(1 to 6 digits long) and octal, unicode and hex broken half way through, from looking at the latest push. - The hex escape is just two chars (the other ones are not really needed).
- There should also be a case for single quote strings and template literals (containing comment chars to make sure those are properly accepted).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
\xHH
not \xHHHH
the latter is just one hex escape followed by two chars (case w/ malformed (mid) hex escape
is incorrect, to be middle it would be \xH H
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, tweaked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parsing logic keeps changing everyday which is fine but that means the tests should be updated to cover the new cases, there are still conditions not covered, which should to make sure there are no bugs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was changing often to cover cases you kept thinking of, which is great because I wasn't seeing/catching them, and I thought I was extending the test inputs to cover things but maybe not sufficiently.
Just added a few for nested comments 🤷♂️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found, added, and updated code for more edge cases.
0d6d8cb
to
6c9a24c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
The External Redirect scan rule has been updated to reduce false positives by excluding JavaScript code that appears within comments from triggering redirect vulnerability alerts.
- Added JavaScript comment parsing logic to filter out commented code before pattern matching
- Enhanced test coverage with comprehensive comment scenarios including various escape sequences and edge cases
- Updated changelog to document the false positive improvement
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
File | Description |
---|---|
ExternalRedirectScanRule.java | Added JavaScript comment extraction logic and state machine parser to filter out commented code |
ExternalRedirectScanRuleUnitTest.java | Added comprehensive test cases covering various JavaScript comment scenarios and edge cases |
CHANGELOG.md | Documented the false positive fix for JavaScript comments |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
.../ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/ExternalRedirectScanRule.java
Outdated
Show resolved
Hide resolved
.../ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/ExternalRedirectScanRule.java
Outdated
Show resolved
Hide resolved
6c9a24c
to
ff39efb
Compare
.../ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/ExternalRedirectScanRule.java
Outdated
Show resolved
Hide resolved
Matcher matcher = pattern.matcher(value); | ||
// We know the payload is present. | ||
// Ensure the value has something we're likely interested in before dealing with comments | ||
if (JS_PRE_CHECKS.stream().noneMatch(chk -> StringUtils.containsIgnoreCase(value, chk))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest keeping the previous check (even if that means matching twice), instead of adding additional checks which must be kept in sync with the patterns.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean the domain check? Then drop the simple JS checks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pattern
check. Yes, the JS_PRE_CHECKS
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So do matcher.find
both before and after?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth doing the StringUtils.startsWithIgnoreCase
as well, better extract a method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done & done
.../ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/ExternalRedirectScanRule.java
Outdated
Show resolved
Hide resolved
.../ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/ExternalRedirectScanRule.java
Outdated
Show resolved
Hide resolved
ff39efb
to
40fd2ef
Compare
Got all those I believe |
40fd2ef
to
89de190
Compare
9b9d595
to
d6c8475
Compare
Set<String> comments = | ||
ExternalRedirectScanRule.extractJsComments(BODY_TEMPLATE.formatted(content)); | ||
// Then | ||
assertThat(comments, hasSize(1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The numbers are not enough, it should be checked the content as well otherwise we don't know if the boundaries are properly detected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've adjusted this. Indeed some of the results are perhaps not as we'd wish. I've added comments to the situations I feel are debatable .
d6c8475
to
125facc
Compare
Signed-off-by: kingthorin <[email protected]>
125facc
to
525d355
Compare
// What do we really want in this case? | ||
"console.log(\"example\"); /* console.log('comment'); ", | ||
"/* console.log('comment'); \n</script></head><body><H1>Redirect</H1></body></html>"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this rule I think we only deal with JS inside HTML, so capturing to the closing script tag is probably reasonable.
In the future we could have a boolean that indicates if the end of script tag is used, or some other behavior to pure JS files.
Arguments.of( | ||
"Template expression with inner comment", | ||
// Is this the behavior we want? I believe this should either be both or | ||
// just trailing. The current result seems wrong to me. | ||
"console.log(`outer ${ /* inner comment */ 42 }`); // trailing comment", | ||
"/* inner comment */"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I propose that this should simply capture the trailing comment.
// This results in a zero comments situation. I believe that's likely fine as | ||
// the unterminated literal should be an app failure on it's own | ||
// Arguments.of( | ||
// "Unterminated template literal", | ||
// "console.log(`unterminated template ${1+1} // | ||
// comment not terminated", "?"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thoughts?
Overview
The External Redirect scan rules has been updated to account for potential false positives involving JavaScript comments.