Skip to content
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

pscanrulesBeta: handle credentialless COEP directive #6180

Merged
merged 13 commits into from
Feb 19, 2025
1 change: 1 addition & 0 deletions addOns/pscanrulesBeta/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Unreleased
### Changed
- Replace usage of CWE-200 for the In Page Banner Information Leak scan rule (Issue 8731).
- Add support for 'credentialless' COEP value in the Insufficient Site Isolation Against Spectre Vulnerability scan rule (Issue 8840).

## [42] - 2025-01-15
### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,11 @@ List<AlertBuilder> build(HttpResponseHeader responseHeader) {
// unsafe-none is the default value. It disables COEP checks.
alerts.addAll(
filterReportHeader(coepHeader)
.filter(header -> !"require-corp".equalsIgnoreCase(header))
.filter(
header ->
!"require-corp".equalsIgnoreCase(header)
&& !"credentialless"
.equalsIgnoreCase(header))
.map(this::alert)
.collect(Collectors.toList()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package org.zaproxy.zap.extension.pscanrulesBeta;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
Expand All @@ -32,6 +33,7 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.parosproxy.paros.core.scanner.Alert;
import org.parosproxy.paros.network.HttpMalformedHeaderException;
import org.parosproxy.paros.network.HttpMessage;
import org.zaproxy.addon.commonlib.CommonAlertTag;

Expand Down Expand Up @@ -316,17 +318,9 @@ void shouldRaiseAlertGivenCoepHeaderIsMissing() throws Exception {
}

@Test
void shouldRaiseAlertGivenCoepHeaderIsNotEqualsToRequireCorp() throws Exception {
// Ref: https://html.spec.whatwg.org/multipage/origin.html#the-headers
void shouldRaiseAlertGivenCoepHeaderIsNotExpectedValue() throws Exception {
// Given
HttpMessage msg = new HttpMessage();
msg.setRequestHeader("GET / HTTP/1.1");
msg.setResponseHeader(
"HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/html; charset=iso-8859-1\r\n"
+ "Cross-Origin-Resource-Policy: same-origin\r\n"
+ "Cross-Origin-Embedder-Policy: unsafe-none\r\n"
+ "Cross-Origin-Opener-Policy: same-origin\r\n");
HttpMessage msg = getCoepMessage("unsafe-none");
given(passiveScanData.isSuccess(any())).willReturn(true);

// When
Expand All @@ -340,6 +334,36 @@ void shouldRaiseAlertGivenCoepHeaderIsNotEqualsToRequireCorp() throws Exception
assertThat(alertsRaised.get(0).getEvidence(), equalTo("unsafe-none"));
}

@ParameterizedTest
@ValueSource(strings = {"require-corp", "credentialless"})
void shouldNotRaiseAlertGivenCoepHeaderIsAnExpectedValue(String directive) throws Exception {
// Given
HttpMessage msg = getCoepMessage(directive);
given(passiveScanData.isSuccess(any())).willReturn(true);

// When
scanHttpResponseReceive(msg);

// Then
assertThat(alertsRaised, is(empty()));
}

private static HttpMessage getCoepMessage(String directive)
throws HttpMalformedHeaderException {
HttpMessage msg = new HttpMessage();
msg.setRequestHeader("GET / HTTP/1.1");
msg.setResponseHeader(
"""
HTTP/1.1 200 OK\r
Content-Type: text/html; charset=iso-8859-1\r
Cross-Origin-Resource-Policy: same-origin\r
Cross-Origin-Embedder-Policy: %s\r
Cross-Origin-Opener-Policy: same-origin
"""
.formatted(directive));
return msg;
}

@Test
void shouldRaiseAlertGivenCoopHeaderIsMissing() throws Exception {
// Given
Expand Down