diff --git a/src/main/java/com/shapesecurity/salvation/Parser.java b/src/main/java/com/shapesecurity/salvation/Parser.java index b382a119..cce27e3a 100644 --- a/src/main/java/com/shapesecurity/salvation/Parser.java +++ b/src/main/java/com/shapesecurity/salvation/Parser.java @@ -52,7 +52,7 @@ public class Parser { private static final String unsafeInlineWarningMessage = "The \"'unsafe-inline'\" keyword-source has no effect in source lists that contain hash-source or nonce-source in CSP2 and later. " + explanation; private static final String strictDynamicWarningMessage = "The host-source and scheme-source expressions, as well as the \"'unsafe-inline'\" and \"'self'\" keyword-sources have no effect in source lists that contain \"'strict-dynamic'\" in CSP3 and later. " + explanation; private static final String unsafeHashedWithoutHashWarningMessage = "The \"'unsafe-hashed-attributes'\" keyword-source has no effect in source lists that do not contain hash-source in CSP3 and later."; - private enum SeenStates {SEEN_HASH, SEEN_HOST_OR_SCHEME_SOURCE, SEEN_NONE, SEEN_NONCE, SEEN_SELF, SEEN_STRICT_DYNAMIC, SEEN_UNSAFE_EVAL, SEEN_UNSAFE_INLINE, SEEN_UNSAFE_HASHED_ATTR}; + private enum SeenStates {SEEN_HASH, SEEN_HOST_OR_SCHEME_SOURCE, SEEN_NONE, SEEN_NONCE, SEEN_SELF, SEEN_STRICT_DYNAMIC, SEEN_UNSAFE_EVAL, SEEN_UNSAFE_INLINE, SEEN_UNSAFE_HASHED_ATTR, SEEN_REPORT_SAMPLE}; @Nonnull protected final Token[] tokens; @Nonnull private final Origin origin; protected int index = 0; @@ -391,6 +391,8 @@ private void enforceMissingDirectiveValue(@Nonnull Token directiveNameToken) thr seenStates.add(SeenStates.SEEN_HOST_OR_SCHEME_SOURCE); } else if (se == KeywordSource.UnsafeHashedAttributes) { seenStates.add(SeenStates.SEEN_UNSAFE_HASHED_ATTR); + } else if (se == KeywordSource.ReportSample) { + seenStates.add(SeenStates.SEEN_REPORT_SAMPLE); } sourceExpressions.add(se); } catch (DirectiveValueParseException e) { @@ -441,6 +443,8 @@ private void enforceMissingDirectiveValue(@Nonnull Token directiveNameToken) thr return KeywordSource.UnsafeRedirect; case "'unsafe-hashed-attributes'": return KeywordSource.UnsafeHashedAttributes; + case "'report-sample'": + return KeywordSource.ReportSample; default: checkForUnquotedKeyword(token); if (token.value.startsWith("'nonce-")) { diff --git a/src/main/java/com/shapesecurity/salvation/directiveValues/KeywordSource.java b/src/main/java/com/shapesecurity/salvation/directiveValues/KeywordSource.java index a33b0cf4..42b5d5b3 100644 --- a/src/main/java/com/shapesecurity/salvation/directiveValues/KeywordSource.java +++ b/src/main/java/com/shapesecurity/salvation/directiveValues/KeywordSource.java @@ -1,13 +1,13 @@ package com.shapesecurity.salvation.directiveValues; +import javax.annotation.Nonnull; + import com.shapesecurity.salvation.data.GUID; import com.shapesecurity.salvation.data.Origin; import com.shapesecurity.salvation.data.URI; import com.shapesecurity.salvation.interfaces.MatchesSource; -import javax.annotation.Nonnull; - public class KeywordSource implements SourceExpression, AncestorSource, MatchesSource { @Nonnull public static final KeywordSource Self = new KeywordSource("self"); @Nonnull public static final KeywordSource UnsafeInline = new KeywordSource("unsafe-inline"); @@ -15,6 +15,7 @@ public class KeywordSource implements SourceExpression, AncestorSource, MatchesS @Nonnull public static final KeywordSource UnsafeRedirect = new KeywordSource("unsafe-redirect"); @Nonnull public static final KeywordSource StrictDynamic = new KeywordSource("strict-dynamic"); @Nonnull public static final KeywordSource UnsafeHashedAttributes = new KeywordSource("unsafe-hashed-attributes"); + @Nonnull public static final KeywordSource ReportSample = new KeywordSource("report-sample"); @Nonnull private final String value; private KeywordSource(@Nonnull String value) { diff --git a/src/test/java/com/shapesecurity/salvation/ParserTest.java b/src/test/java/com/shapesecurity/salvation/ParserTest.java index 25ea6009..7f469b1f 100644 --- a/src/test/java/com/shapesecurity/salvation/ParserTest.java +++ b/src/test/java/com/shapesecurity/salvation/ParserTest.java @@ -1050,4 +1050,34 @@ public class ParserTest extends CSPTest { assertEquals(1, p.getDirectives().size()); assertEquals(0, notices.size()); } + + @Test public void testReportSample() { + Policy p; + ArrayList notices = new ArrayList<>(); + p = parseWithNotices("default-src 'report-sample'", notices); + assertEquals(1, p.getDirectives().size()); + assertEquals(0, notices.size()); + + notices.clear(); + p = parseWithNotices("script-src 'report-sample' 'report-sample'", notices); + assertEquals(1, p.getDirectives().size()); + assertEquals("script-src 'report-sample'", p.getDirectiveByType(ScriptSrcDirective.class).show()); + assertEquals(0, notices.size()); + + notices.clear(); + p = parseWithNotices("style-src 'report-sample' 'unsafe-hashed-attributes' 'report-sample' 'nonce-123'", notices); + assertEquals(1, p.getDirectives().size()); + assertEquals(2, notices.size()); + assertEquals("Invalid base64-value (should be multiple of 4 bytes: 3).", notices.get(0).message); + + notices.clear(); + p = parseWithNotices("default-src 'strict-dynamic' 'report-sample'", notices); + assertEquals(1, p.getDirectives().size()); + assertEquals(0, notices.size()); + + notices.clear(); + p = parseWithNotices("img-src 'report-sample'", notices); + assertEquals(1, p.getDirectives().size()); + assertEquals(0, notices.size()); + } }