{"action":"created","comment":{"url":"https://api.github.com/repos/shapesecurity/salvation/pulls/comments/50777820","id":50777820,"diff_hunk":"@@ -366,67 +548,154 @@ private boolean eat(@Nonnull Class<? extends Token> c) {\n                 String portString = matcher.group(\"port\");\n                 int port;\n                 if (portString == null) {\n-                    port = scheme == null ?\n-                        Constants.EMPTY_PORT :\n-                        SchemeHostPortTriple.defaultPortForProtocol(scheme);\n+                    port = scheme == null ? Constants.EMPTY_PORT : SchemeHostPortTriple.defaultPortForProtocol(scheme);\n                 } else {\n-                    port = portString.equals(\":*\") ?\n-                        Constants.WILDCARD_PORT :\n-                        Integer.parseInt(portString.substring(1));\n+                    port =\n+                        portString.equals(\":*\") ? Constants.WILDCARD_PORT : Integer.parseInt(portString.substring(1));\n                 }\n                 String host = matcher.group(\"host\");\n                 String path = matcher.group(\"path\");\n                 return new HostSource(scheme, host, port, path);\n             }\n         }\n-        throw this.createError(\"expecting ancestor-source but found \" + token.value);\n+        this.error(token, \"Expecting ancestor-source but found \" + token.value);\n+        throw INVALID_ANCESTOR_SOURCE;\n     }\n \n-    @Nonnull private Set<SandboxValue> parseSandboxTokenList() throws ParseException {\n+    @Nonnull private Set<ReferrerValue> parseReferrerTokenList() throws DirectiveParseException {\n+\n+        Set<ReferrerValue> referrerTokens = new LinkedHashSet<>();\n+        if (this.hasNext(DirectiveValueToken.class)) {\n+            boolean parseException = false;\n+            Token dv = this.advance();\n+            for (SubDirectiveValueToken subdv : splitByWSP(dv)) {\n+                try {\n+                    referrerTokens.add(this.parseReferrerToken(subdv));\n+                } catch (DirectiveValueParseException e) {\n+                    parseException = true;\n+                }\n+            }\n+            if (parseException) {\n+                throw INVALID_REFERRER_TOKEN_LIST;\n+            }\n+        }\n+        if (referrerTokens.size() != 1) {\n+            this.error(\"The referrer directive must contain exactly one referrer-token\");\n+            throw INVALID_REFERRER_TOKEN_LIST;\n+        }\n+        return referrerTokens;\n+    }\n+\n+    @Nonnull private ReferrerValue parseReferrerToken(@Nonnull SubDirectiveValueToken token)\n+        throws DirectiveValueParseException {\n+        Matcher matcher = Constants.referrerTokenPattern.matcher(token.value);\n+        if (matcher.find()) {\n+            return new ReferrerValue(token.value);\n+        }\n+        this.error(token, \"Expecting referrer-token but found \" + token.value);\n+        throw INVALID_REFERRER_TOKEN;\n+    }\n+\n+    @Nonnull private Set<SandboxValue> parseSandboxTokenList() throws DirectiveParseException {\n         Set<SandboxValue> sandboxTokens = new LinkedHashSet<>();\n-        while (this.hasNext(DirectiveValueToken.class)) {\n-            sandboxTokens.add(this.parseSandboxToken());\n+        if (this.hasNext(DirectiveValueToken.class)) {\n+            boolean parseException = false;\n+            Token dv = this.advance();\n+            for (SubDirectiveValueToken subdv : splitByWSP(dv)) {\n+                try {\n+                    sandboxTokens.add(this.parseSandboxToken(subdv));\n+                } catch (DirectiveValueParseException e) {\n+                    parseException = true;\n+                }\n+            }\n+            if (parseException) {\n+                throw INVALID_SANDBOX_TOKEN_LIST;\n+            }\n         }\n         return sandboxTokens;\n     }\n \n-    @Nonnull private SandboxValue parseSandboxToken() throws ParseException {\n-        Token token = this.advance();\n-        Matcher matcher = Constants.sandboxTokenPattern.matcher(token.value);\n+    @Nonnull private SandboxValue parseSandboxToken(@Nonnull SubDirectiveValueToken token) throws DirectiveValueParseException {\n+        Matcher matcher = Constants.sandboxEnumeratedTokenPattern.matcher(token.value);\n         if (matcher.find()) {\n             return new SandboxValue(token.value);\n+        } else {\n+            this.warn(token, \"The sandbox directive should contain only allow-forms, allow-modals, \"","path":"src/main/java/com/shapesecurity/salvation/Parser.java","position":842,"original_position":842,"commit_id":"70ada182323327c931be1456e15b8b85c2e9aa80","original_commit_id":"70ada182323327c931be1456e15b8b85c2e9aa80","user":{"login":"michaelficarra","id":218840,"avatar_url":"https://avatars.githubusercontent.com/u/218840?v=3","gravatar_id":"","url":"https://api.github.com/users/michaelficarra","html_url":"https://github.com/michaelficarra","followers_url":"https://api.github.com/users/michaelficarra/followers","following_url":"https://api.github.com/users/michaelficarra/following{/other_user}","gists_url":"https://api.github.com/users/michaelficarra/gists{/gist_id}","starred_url":"https://api.github.com/users/michaelficarra/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/michaelficarra/subscriptions","organizations_url":"https://api.github.com/users/michaelficarra/orgs","repos_url":"https://api.github.com/users/michaelficarra/repos","events_url":"https://api.github.com/users/michaelficarra/events{/privacy}","received_events_url":"https://api.github.com/users/michaelficarra/received_events","type":"User","site_admin":false},"body":"How is this not an error? And if we know all of the possible values, shouldn't `SandboxValue` be an `enum` now?","created_at":"2016-01-26T00:06:04Z","updated_at":"2016-01-26T00:06:04Z","html_url":"https://github.com/shapesecurity/salvation/pull/97#discussion_r50777820","pull_request_url":"https://api.github.com/repos/shapesecurity/salvation/pulls/97","_links":{"self":{"href":"https://api.github.com/repos/shapesecurity/salvation/pulls/comments/50777820"},"html":{"href":"https://github.com/shapesecurity/salvation/pull/97#discussion_r50777820"},"pull_request":{"href":"https://api.github.com/repos/shapesecurity/salvation/pulls/97"}}},"pull_request":{"url":"https://api.github.com/repos/shapesecurity/salvation/pulls/97","id":57143122,"html_url":"https://github.com/shapesecurity/salvation/pull/97","diff_url":"https://github.com/shapesecurity/salvation/pull/97.diff","patch_url":"https://github.com/shapesecurity/salvation/pull/97.patch","issue_url":"https://api.github.com/repos/shapesecurity/salvation/issues/97","number":97,"state":"open","locked":false,"title":"Fixes #93: permissive parsing","user":{"login":"shekyan","id":1631049,"avatar_url":"https://avatars.githubusercontent.com/u/1631049?v=3","gravatar_id":"","url":"https://api.github.com/users/shekyan","html_url":"https://github.com/shekyan","followers_url":"https://api.github.com/users/shekyan/followers","following_url":"https://api.github.com/users/shekyan/following{/other_user}","gists_url":"https://api.github.com/users/shekyan/gists{/gist_id}","starred_url":"https://api.github.com/users/shekyan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/shekyan/subscriptions","organizations_url":"https://api.github.com/users/shekyan/orgs","repos_url":"https://api.github.com/users/shekyan/repos","events_url":"https://api.github.com/users/shekyan/events{/privacy}","received_events_url":"https://api.github.com/users/shekyan/received_events","type":"User","site_admin":false},"body":"","created_at":"2016-01-25T22:46:32Z","updated_at":"2016-01-26T00:06:04Z","closed_at":null,"merged_at":null,"merge_commit_sha":"aa0e7e25172ad11e0a3050d205f6dc775931a613","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/shapesecurity/salvation/pulls/97/commits","review_comments_url":"https://api.github.com/repos/shapesecurity/salvation/pulls/97/comments","review_comment_url":"https://api.github.com/repos/shapesecurity/salvation/pulls/comments{/number}","comments_url":"https://api.github.com/repos/shapesecurity/salvation/issues/97/comments","statuses_url":"https://api.github.com/repos/shapesecurity/salvation/statuses/70ada182323327c931be1456e15b8b85c2e9aa80","head":{"label":"shapesecurity:GH-93","ref":"GH-93","sha":"70ada182323327c931be1456e15b8b85c2e9aa80","user":{"login":"shapesecurity","id":7728313,"avatar_url":"https://avatars.githubusercontent.com/u/7728313?v=3","gravatar_id":"","url":"https://api.github.com/users/shapesecurity","html_url":"https://github.com/shapesecurity","followers_url":"https://api.github.com/users/shapesecurity/followers","following_url":"https://api.github.com/users/shapesecurity/following{/other_user}","gists_url":"https://api.github.com/users/shapesecurity/gists{/gist_id}","starred_url":"https://api.github.com/users/shapesecurity/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/shapesecurity/subscriptions","organizations_url":"https://api.github.com/users/shapesecurity/orgs","repos_url":"https://api.github.com/users/shapesecurity/repos","events_url":"https://api.github.com/users/shapesecurity/events{/privacy}","received_events_url":"https://api.github.com/users/shapesecurity/received_events","type":"Organization","site_admin":false},"repo":{"id":35701371,"name":"salvation","full_name":"shapesecurity/salvation","owner":{"login":"shapesecurity","id":7728313,"avatar_url":"https://avatars.githubusercontent.com/u/7728313?v=3","gravatar_id":"","url":"https://api.github.com/users/shapesecurity","html_url":"https://github.com/shapesecurity","followers_url":"https://api.github.com/users/shapesecurity/followers","following_url":"https://api.github.com/users/shapesecurity/following{/other_user}","gists_url":"https://api.github.com/users/shapesecurity/gists{/gist_id}","starred_url":"https://api.github.com/users/shapesecurity/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/shapesecurity/subscriptions","organizations_url":"https://api.github.com/users/shapesecurity/orgs","repos_url":"https://api.github.com/users/shapesecurity/repos","events_url":"https://api.github.com/users/shapesecurity/events{/privacy}","received_events_url":"https://api.github.com/users/shapesecurity/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/shapesecurity/salvation","description":"Parse Content Security Policy headers, warn about policy errors, safely manipulate, render, and optimise policies","fork":false,"url":"https://api.github.com/repos/shapesecurity/salvation","forks_url":"https://api.github.com/repos/shapesecurity/salvation/forks","keys_url":"https://api.github.com/repos/shapesecurity/salvation/keys{/key_id}","collaborators_url":"https://api.github.com/repos/shapesecurity/salvation/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/shapesecurity/salvation/teams","hooks_url":"https://api.github.com/repos/shapesecurity/salvation/hooks","issue_events_url":"https://api.github.com/repos/shapesecurity/salvation/issues/events{/number}","events_url":"https://api.github.com/repos/shapesecurity/salvation/events","assignees_url":"https://api.github.com/repos/shapesecurity/salvation/assignees{/user}","branches_url":"https://api.github.com/repos/shapesecurity/salvation/branches{/branch}","tags_url":"https://api.github.com/repos/shapesecurity/salvation/tags","blobs_url":"https://api.github.com/repos/shapesecurity/salvation/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/shapesecurity/salvation/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/shapesecurity/salvation/git/refs{/sha}","trees_url":"https://api.github.com/repos/shapesecurity/salvation/git/trees{/sha}","statuses_url":"https://api.github.com/repos/shapesecurity/salvation/statuses/{sha}","languages_url":"https://api.github.com/repos/shapesecurity/salvation/languages","stargazers_url":"https://api.github.com/repos/shapesecurity/salvation/stargazers","contributors_url":"https://api.github.com/repos/shapesecurity/salvation/contributors","subscribers_url":"https://api.github.com/repos/shapesecurity/salvation/subscribers","subscription_url":"https://api.github.com/repos/shapesecurity/salvation/subscription","commits_url":"https://api.github.com/repos/shapesecurity/salvation/commits{/sha}","git_commits_url":"https://api.github.com/repos/shapesecurity/salvation/git/commits{/sha}","comments_url":"https://api.github.com/repos/shapesecurity/salvation/comments{/number}","issue_comment_url":"https://api.github.com/repos/shapesecurity/salvation/issues/comments{/number}","contents_url":"https://api.github.com/repos/shapesecurity/salvation/contents/{+path}","compare_url":"https://api.github.com/repos/shapesecurity/salvation/compare/{base}...{head}","merges_url":"https://api.github.com/repos/shapesecurity/salvation/merges","archive_url":"https://api.github.com/repos/shapesecurity/salvation/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/shapesecurity/salvation/downloads","issues_url":"https://api.github.com/repos/shapesecurity/salvation/issues{/number}","pulls_url":"https://api.github.com/repos/shapesecurity/salvation/pulls{/number}","milestones_url":"https://api.github.com/repos/shapesecurity/salvation/milestones{/number}","notifications_url":"https://api.github.com/repos/shapesecurity/salvation/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/shapesecurity/salvation/labels{/name}","releases_url":"https://api.github.com/repos/shapesecurity/salvation/releases{/id}","deployments_url":"https://api.github.com/repos/shapesecurity/salvation/deployments","created_at":"2015-05-15T23:14:49Z","updated_at":"2016-01-07T21:50:26Z","pushed_at":"2016-01-25T22:46:32Z","git_url":"git://github.com/shapesecurity/salvation.git","ssh_url":"git@github.com:shapesecurity/salvation.git","clone_url":"https://github.com/shapesecurity/salvation.git","svn_url":"https://github.com/shapesecurity/salvation","homepage":"http://cspvalidator.org","size":599,"stargazers_count":19,"watchers_count":19,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":3,"mirror_url":null,"open_issues_count":9,"forks":3,"open_issues":9,"watchers":19,"default_branch":"master"}},"base":{"label":"shapesecurity:master","ref":"master","sha":"2b41e905be268d41987761306dba946d9fea5c76","user":{"login":"shapesecurity","id":7728313,"avatar_url":"https://avatars.githubusercontent.com/u/7728313?v=3","gravatar_id":"","url":"https://api.github.com/users/shapesecurity","html_url":"https://github.com/shapesecurity","followers_url":"https://api.github.com/users/shapesecurity/followers","following_url":"https://api.github.com/users/shapesecurity/following{/other_user}","gists_url":"https://api.github.com/users/shapesecurity/gists{/gist_id}","starred_url":"https://api.github.com/users/shapesecurity/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/shapesecurity/subscriptions","organizations_url":"https://api.github.com/users/shapesecurity/orgs","repos_url":"https://api.github.com/users/shapesecurity/repos","events_url":"https://api.github.com/users/shapesecurity/events{/privacy}","received_events_url":"https://api.github.com/users/shapesecurity/received_events","type":"Organization","site_admin":false},"repo":{"id":35701371,"name":"salvation","full_name":"shapesecurity/salvation","owner":{"login":"shapesecurity","id":7728313,"avatar_url":"https://avatars.githubusercontent.com/u/7728313?v=3","gravatar_id":"","url":"https://api.github.com/users/shapesecurity","html_url":"https://github.com/shapesecurity","followers_url":"https://api.github.com/users/shapesecurity/followers","following_url":"https://api.github.com/users/shapesecurity/following{/other_user}","gists_url":"https://api.github.com/users/shapesecurity/gists{/gist_id}","starred_url":"https://api.github.com/users/shapesecurity/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/shapesecurity/subscriptions","organizations_url":"https://api.github.com/users/shapesecurity/orgs","repos_url":"https://api.github.com/users/shapesecurity/repos","events_url":"https://api.github.com/users/shapesecurity/events{/privacy}","received_events_url":"https://api.github.com/users/shapesecurity/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/shapesecurity/salvation","description":"Parse Content Security Policy headers, warn about policy errors, safely manipulate, render, and optimise policies","fork":false,"url":"https://api.github.com/repos/shapesecurity/salvation","forks_url":"https://api.github.com/repos/shapesecurity/salvation/forks","keys_url":"https://api.github.com/repos/shapesecurity/salvation/keys{/key_id}","collaborators_url":"https://api.github.com/repos/shapesecurity/salvation/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/shapesecurity/salvation/teams","hooks_url":"https://api.github.com/repos/shapesecurity/salvation/hooks","issue_events_url":"https://api.github.com/repos/shapesecurity/salvation/issues/events{/number}","events_url":"https://api.github.com/repos/shapesecurity/salvation/events","assignees_url":"https://api.github.com/repos/shapesecurity/salvation/assignees{/user}","branches_url":"https://api.github.com/repos/shapesecurity/salvation/branches{/branch}","tags_url":"https://api.github.com/repos/shapesecurity/salvation/tags","blobs_url":"https://api.github.com/repos/shapesecurity/salvation/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/shapesecurity/salvation/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/shapesecurity/salvation/git/refs{/sha}","trees_url":"https://api.github.com/repos/shapesecurity/salvation/git/trees{/sha}","statuses_url":"https://api.github.com/repos/shapesecurity/salvation/statuses/{sha}","languages_url":"https://api.github.com/repos/shapesecurity/salvation/languages","stargazers_url":"https://api.github.com/repos/shapesecurity/salvation/stargazers","contributors_url":"https://api.github.com/repos/shapesecurity/salvation/contributors","subscribers_url":"https://api.github.com/repos/shapesecurity/salvation/subscribers","subscription_url":"https://api.github.com/repos/shapesecurity/salvation/subscription","commits_url":"https://api.github.com/repos/shapesecurity/salvation/commits{/sha}","git_commits_url":"https://api.github.com/repos/shapesecurity/salvation/git/commits{/sha}","comments_url":"https://api.github.com/repos/shapesecurity/salvation/comments{/number}","issue_comment_url":"https://api.github.com/repos/shapesecurity/salvation/issues/comments{/number}","contents_url":"https://api.github.com/repos/shapesecurity/salvation/contents/{+path}","compare_url":"https://api.github.com/repos/shapesecurity/salvation/compare/{base}...{head}","merges_url":"https://api.github.com/repos/shapesecurity/salvation/merges","archive_url":"https://api.github.com/repos/shapesecurity/salvation/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/shapesecurity/salvation/downloads","issues_url":"https://api.github.com/repos/shapesecurity/salvation/issues{/number}","pulls_url":"https://api.github.com/repos/shapesecurity/salvation/pulls{/number}","milestones_url":"https://api.github.com/repos/shapesecurity/salvation/milestones{/number}","notifications_url":"https://api.github.com/repos/shapesecurity/salvation/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/shapesecurity/salvation/labels{/name}","releases_url":"https://api.github.com/repos/shapesecurity/salvation/releases{/id}","deployments_url":"https://api.github.com/repos/shapesecurity/salvation/deployments","created_at":"2015-05-15T23:14:49Z","updated_at":"2016-01-07T21:50:26Z","pushed_at":"2016-01-25T22:46:32Z","git_url":"git://github.com/shapesecurity/salvation.git","ssh_url":"git@github.com:shapesecurity/salvation.git","clone_url":"https://github.com/shapesecurity/salvation.git","svn_url":"https://github.com/shapesecurity/salvation","homepage":"http://cspvalidator.org","size":599,"stargazers_count":19,"watchers_count":19,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":3,"mirror_url":null,"open_issues_count":9,"forks":3,"open_issues":9,"watchers":19,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/shapesecurity/salvation/pulls/97"},"html":{"href":"https://github.com/shapesecurity/salvation/pull/97"},"issue":{"href":"https://api.github.com/repos/shapesecurity/salvation/issues/97"},"comments":{"href":"https://api.github.com/repos/shapesecurity/salvation/issues/97/comments"},"review_comments":{"href":"https://api.github.com/repos/shapesecurity/salvation/pulls/97/comments"},"review_comment":{"href":"https://api.github.com/repos/shapesecurity/salvation/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/shapesecurity/salvation/pulls/97/commits"},"statuses":{"href":"https://api.github.com/repos/shapesecurity/salvation/statuses/70ada182323327c931be1456e15b8b85c2e9aa80"}}}}