diff --git a/extensions/amp-ad/0.1/validator-amp-ad.protoascii b/extensions/amp-ad/0.1/validator-amp-ad.protoascii new file mode 100644 index 000000000000..8503aa677b8c --- /dev/null +++ b/extensions/amp-ad/0.1/validator-amp-ad.protoascii @@ -0,0 +1,106 @@ +# +# Copyright 2016 The AMP HTML Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the license. + +# In the AMP Runtime, amp-ad and amp-embed are implemented in the samei +# extension, so we include them both here. + +tags: { # amp-ad + tag_name: "SCRIPT" + spec_name: "amp-ad extension .js script" + mandatory_parent: "HEAD" + attrs: { + name: "async" + mandatory: true + value: "" + } + attrs: { + name: "custom-element" + mandatory: true + value: "amp-ad" + dispatch_key: true + } + attrs: { + name: "src" + mandatory: true + value_regex: "https://cdn\\.ampproject\\.org/v0/amp-ad-(latest|0\\.1).js" + } + attrs: { + name: "type" + value: "text/javascript" + } + cdata: { + blacklisted_cdata_regex: { + regex: "." + error_message: "contents" + } + } + spec_url: "https://www.ampproject.org/docs/reference/extended/amp-ad.html" +} +tags: { # + tag_name: "AMP-AD" + # Typically we'd require the extension j/s, but for historical reasons + # many pages don't have it, so it ends up late loaded. + # TODO(gregable): Make the lack of the extension a deprecation warning. + # also_requires_tag: "amp-ad extension .js script" + disallowed_ancestor: "AMP-SIDEBAR" + attrs: { name: "alt" } + attrs: { name: "json" } + attrs: { + name: "src" + value_url: { + allowed_protocol: "https" + allow_relative: true # Will be set to false at a future date. + } + } + attrs: { name: "type" mandatory: true } + attr_lists: "extended-amp-global" + spec_url: "https://www.ampproject.org/docs/reference/amp-ad.html" + amp_layout: { + supported_layouts: FILL + supported_layouts: FIXED + supported_layouts: FIXED_HEIGHT + supported_layouts: FLEX_ITEM + supported_layouts: NODISPLAY + supported_layouts: RESPONSIVE + } +} +tags: { # + tag_name: "AMP-EMBED" + # Typically we'd require the extension j/s, but for historical reasons + # many pages don't have it, so it ends up late loaded. + # TODO(gregable): Make the lack of the extension a deprecation warning. + # also_requires_tag: "amp-ad extension .js script" + disallowed_ancestor: "AMP-SIDEBAR" + attrs: { name: "alt" } + attrs: { name: "json" } + attrs: { + name: "src" + value_url: { + allowed_protocol: "https" + allow_relative: true # Will be set to false at a future date. + } + } + attrs: { name: "type" mandatory: true } + attr_lists: "extended-amp-global" + spec_url: "https://www.ampproject.org/docs/reference/amp-embed.html" + amp_layout: { + supported_layouts: FILL + supported_layouts: FIXED + supported_layouts: FIXED_HEIGHT + supported_layouts: FLEX_ITEM + supported_layouts: NODISPLAY + supported_layouts: RESPONSIVE + } +} diff --git a/extensions/amp-form/0.1/validator-amp-form.protoascii b/extensions/amp-form/0.1/validator-amp-form.protoascii new file mode 100644 index 000000000000..689d045c83fe --- /dev/null +++ b/extensions/amp-form/0.1/validator-amp-form.protoascii @@ -0,0 +1,50 @@ +# +# Copyright 2016 The AMP HTML Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the license. +# +tags: { # amp-form + # Accepted form elements can be found in validator-main.protoascii + # under section "4.10 Forms" + tag_name: "SCRIPT" + spec_name: "amp-form extension .js script" + mandatory_parent: "HEAD" + attrs: { + name: "async" + mandatory: true + value: "" + } + attrs: { + name: "custom-element" + mandatory: true + value: "amp-form" + dispatch_key: true + } + attrs: { + name: "src" + mandatory: true + value_regex: "https://cdn\\.ampproject\\.org/v0/amp-form-(latest|0\\.1).js" + } + attrs: { + name: "type" + value: "text/javascript" + } + cdata: { + blacklisted_cdata_regex: { + regex: "." + error_message: "contents" + } + } + # TODO: update with ampproject.org url when available + spec_url: "https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md" +} diff --git a/extensions/amp-mustache/0.1/test/validator-amp-mustache.html b/extensions/amp-mustache/0.1/test/validator-amp-mustache.html index 002ddf555adf..0eeda64db10e 100644 --- a/extensions/amp-mustache/0.1/test/validator-amp-mustache.html +++ b/extensions/amp-mustache/0.1/test/validator-amp-mustache.html @@ -46,6 +46,9 @@

+

+

+

<{{ not-actually-parsed-as-an-html-tag-so-allowed }}> diff --git a/extensions/amp-mustache/0.1/test/validator-amp-mustache.out b/extensions/amp-mustache/0.1/test/validator-amp-mustache.out index 350871b433bf..8affa8725d18 100644 --- a/extensions/amp-mustache/0.1/test/validator-amp-mustache.out +++ b/extensions/amp-mustache/0.1/test/validator-amp-mustache.out @@ -14,25 +14,28 @@ amp-mustache/0.1/test/validator-amp-mustache.html:45:2 Mustache template syntax amp-mustache/0.1/test/validator-amp-mustache.html:46:2 The attribute 'title' in tag 'p' is set to '{{{notallowed}}}', which contains unescaped Mustache template syntax. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] amp-mustache/0.1/test/validator-amp-mustache.html:47:2 The attribute 'title' in tag 'p' is set to '{{¬allowed}}', which contains unescaped Mustache template syntax. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] amp-mustache/0.1/test/validator-amp-mustache.html:48:2 The attribute 'title' in tag 'p' is set to '{{>notallowed}}', which contains a Mustache template partial. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] -amp-mustache/0.1/test/validator-amp-mustache.html:53:2 Mustache template syntax in attribute name '{{' in tag 'p'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] -amp-mustache/0.1/test/validator-amp-mustache.html:54:2 Mustache template syntax in attribute name '{{' in tag 'p'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] -amp-mustache/0.1/test/validator-amp-mustache.html:55:2 Mustache template syntax in attribute name 'data-{{' in tag 'p'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] -amp-mustache/0.1/test/validator-amp-mustache.html:56:2 Mustache template syntax in attribute name 'data-{{{' in tag 'p'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] -amp-mustache/0.1/test/validator-amp-mustache.html:57:2 Mustache template syntax in attribute name 'data-{{' in tag 'p'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:49:2 The attribute 'data-title' in tag 'p' is set to '{{{notallowed}}}', which contains unescaped Mustache template syntax. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:50:2 The attribute 'data-title' in tag 'p' is set to '{{¬allowed}}', which contains unescaped Mustache template syntax. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:51:2 The attribute 'data-title' in tag 'p' is set to '{{>notallowed}}', which contains a Mustache template partial. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:56:2 Mustache template syntax in attribute name '{{' in tag 'p'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:57:2 Mustache template syntax in attribute name '{{' in tag 'p'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] amp-mustache/0.1/test/validator-amp-mustache.html:58:2 Mustache template syntax in attribute name 'data-{{' in tag 'p'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] -amp-mustache/0.1/test/validator-amp-mustache.html:59:2 Mustache template syntax in attribute name 'data-{{' in tag 'p'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:59:2 Mustache template syntax in attribute name 'data-{{{' in tag 'p'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] amp-mustache/0.1/test/validator-amp-mustache.html:60:2 Mustache template syntax in attribute name 'data-{{' in tag 'p'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] amp-mustache/0.1/test/validator-amp-mustache.html:61:2 Mustache template syntax in attribute name 'data-{{' in tag 'p'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] -amp-mustache/0.1/test/validator-amp-mustache.html:62:2 Mustache template syntax in attribute name '{{' in tag 'p'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] -amp-mustache/0.1/test/validator-amp-mustache.html:63:2 Mustache template syntax in attribute name '{{' in tag 'p'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] -amp-mustache/0.1/test/validator-amp-mustache.html:64:2 The attribute 'title' in tag 'p' is set to '{{{ notallowed }}}', which contains unescaped Mustache template syntax. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] -amp-mustache/0.1/test/validator-amp-mustache.html:65:2 The attribute 'title' in tag 'p' is set to '{{ ¬allowed }}', which contains unescaped Mustache template syntax. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] -amp-mustache/0.1/test/validator-amp-mustache.html:66:2 The attribute 'title' in tag 'p' is set to '{{ >notallowed }}', which contains a Mustache template partial. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] -amp-mustache/0.1/test/validator-amp-mustache.html:67:2 The attribute 'title' in tag 'p' is set to '{{& notallowed }}', which contains unescaped Mustache template syntax. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] -amp-mustache/0.1/test/validator-amp-mustache.html:68:2 The attribute 'title' in tag 'p' is set to '{{> notallowed }}', which contains a Mustache template partial. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] -amp-mustache/0.1/test/validator-amp-mustache.html:69:2 The attribute 'title' in tag 'p' is set to '{{ & notallowed }}', which contains unescaped Mustache template syntax. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] -amp-mustache/0.1/test/validator-amp-mustache.html:70:2 The attribute 'title' in tag 'p' is set to '{{ > notallowed }}', which contains a Mustache template partial. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] -amp-mustache/0.1/test/validator-amp-mustache.html:95:4 The tag 'template' may not appear as a descendant of tag 'template'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] -amp-mustache/0.1/test/validator-amp-mustache.html:107:0 The attribute 'autoplay' in tag 'amp-audio' is set to the invalid value '{{invalid}}'. (see https://www.ampproject.org/docs/reference/extended/amp-audio.html) [AMP_TAG_PROBLEM] -amp-mustache/0.1/test/validator-amp-mustache.html:121:7 The tag 'amp-audio extension .js script' is missing or incorrect, but required by 'amp-audio'. (see https://www.ampproject.org/docs/reference/extended/amp-audio.html) [AMP_TAG_PROBLEM] -amp-mustache/0.1/test/validator-amp-mustache.html:121:7 The tag 'amp-mustache extension .js script' is missing or incorrect, but required by 'template'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_TAG_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:62:2 Mustache template syntax in attribute name 'data-{{' in tag 'p'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:63:2 Mustache template syntax in attribute name 'data-{{' in tag 'p'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:64:2 Mustache template syntax in attribute name 'data-{{' in tag 'p'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:65:2 Mustache template syntax in attribute name '{{' in tag 'p'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:66:2 Mustache template syntax in attribute name '{{' in tag 'p'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:67:2 The attribute 'title' in tag 'p' is set to '{{{ notallowed }}}', which contains unescaped Mustache template syntax. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:68:2 The attribute 'title' in tag 'p' is set to '{{ ¬allowed }}', which contains unescaped Mustache template syntax. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:69:2 The attribute 'title' in tag 'p' is set to '{{ >notallowed }}', which contains a Mustache template partial. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:70:2 The attribute 'title' in tag 'p' is set to '{{& notallowed }}', which contains unescaped Mustache template syntax. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:71:2 The attribute 'title' in tag 'p' is set to '{{> notallowed }}', which contains a Mustache template partial. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:72:2 The attribute 'title' in tag 'p' is set to '{{ & notallowed }}', which contains unescaped Mustache template syntax. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:73:2 The attribute 'title' in tag 'p' is set to '{{ > notallowed }}', which contains a Mustache template partial. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:98:4 The tag 'template' may not appear as a descendant of tag 'template'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_HTML_TEMPLATE_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:110:0 The attribute 'autoplay' in tag 'amp-audio' is set to the invalid value '{{invalid}}'. (see https://www.ampproject.org/docs/reference/extended/amp-audio.html) [AMP_TAG_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:124:7 The tag 'amp-audio extension .js script' is missing or incorrect, but required by 'amp-audio'. (see https://www.ampproject.org/docs/reference/extended/amp-audio.html) [AMP_TAG_PROBLEM] +amp-mustache/0.1/test/validator-amp-mustache.html:124:7 The tag 'amp-mustache extension .js script' is missing or incorrect, but required by 'template'. (see https://www.ampproject.org/docs/reference/extended/amp-mustache.html) [AMP_TAG_PROBLEM] diff --git a/validator/index.js b/validator/index.js index b5183d0cddd1..733b7c50dfa5 100755 --- a/validator/index.js +++ b/validator/index.js @@ -30,6 +30,12 @@ var url = require('url'); var util = require('util'); var vm = require('vm'); +/** + * Determines if str begins with prefix. + * @param {!string} str + * @param {!string} prefix + * @returns {!boolean} + */ function hasPrefix(str, prefix) { return str.indexOf(prefix) == 0; } @@ -263,7 +269,7 @@ Validator.prototype.validateString = result.errors.push(error); } return result; -} +}; /** * A global static map used by the getInstance function to avoid loading @@ -320,7 +326,7 @@ function logValidationResult(filename, validationResult, color) { msg += (error.severity === 'ERROR' ? colors.red : colors.magenta)( error.message); } else { - msg += error.message + msg += error.message; } if (error.specUrl) { msg += ' (see ' + error.specUrl + ')'; @@ -393,7 +399,7 @@ function main() { } } if (program.format === 'json') { - console.log(JSON.stringify(jsonOut)) + console.log(JSON.stringify(jsonOut)); } }) .catch(function(error) { diff --git a/validator/testdata/feature_tests/forms.html b/validator/testdata/feature_tests/forms.html new file mode 100644 index 000000000000..8cf887a20233 --- /dev/null +++ b/validator/testdata/feature_tests/forms.html @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + +
+
+ + + +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + + + + +
+ + + + +
+ + diff --git a/validator/testdata/feature_tests/forms.out b/validator/testdata/feature_tests/forms.out new file mode 100644 index 000000000000..4b780f907da5 --- /dev/null +++ b/validator/testdata/feature_tests/forms.out @@ -0,0 +1,13 @@ +FAIL +feature_tests/forms.html:46:2 The relative URL '/subscribe' for attribute 'action' in tag 'form' is disallowed. (see https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md) [DISALLOWED_HTML] +feature_tests/forms.html:50:2 Invalid URL protocol 'http:' for attribute 'action' in tag 'form'. (see https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md) [DISALLOWED_HTML] +feature_tests/forms.html:54:2 The domain 'cdn.ampproject.org' for attribute 'action' in tag 'form' is disallowed. (see https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md) [DISALLOWED_HTML] +feature_tests/forms.html:58:2 The attribute 'target' in tag 'form' is set to the invalid value '_new'. (see https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md) [DISALLOWED_HTML] +feature_tests/forms.html:62:2 The tag 'input' may only appear as a descendant of tag 'form'. (see https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md) [DISALLOWED_HTML] +feature_tests/forms.html:63:2 The tag 'select' may only appear as a descendant of tag 'form'. (see https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md) [DISALLOWED_HTML] +feature_tests/forms.html:64:4 The tag 'option' may only appear as a descendant of tag 'form'. (see https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md) [DISALLOWED_HTML] +feature_tests/forms.html:66:2 The tag 'textarea' may only appear as a descendant of tag 'form'. (see https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md) [DISALLOWED_HTML] +feature_tests/forms.html:69:4 The attribute 'type' in tag 'input' is set to the invalid value 'button'. (see https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md) [DISALLOWED_HTML] +feature_tests/forms.html:70:4 The attribute 'type' in tag 'input' is set to the invalid value 'file'. (see https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md) [DISALLOWED_HTML] +feature_tests/forms.html:71:4 The attribute 'type' in tag 'input' is set to the invalid value 'image'. (see https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md) [DISALLOWED_HTML] +feature_tests/forms.html:72:4 The attribute 'type' in tag 'input' is set to the invalid value 'password'. (see https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md) [DISALLOWED_HTML] diff --git a/validator/testdata/feature_tests/urls.html b/validator/testdata/feature_tests/urls.html index bae63c27120d..d57c347d42fd 100644 --- a/validator/testdata/feature_tests/urls.html +++ b/validator/testdata/feature_tests/urls.html @@ -42,7 +42,6 @@ Invalid protocol Invalid protocol Valid protocol - Malformed URL @@ -57,8 +56,6 @@ - - diff --git a/validator/testdata/feature_tests/urls.out b/validator/testdata/feature_tests/urls.out index aa6e4e78cedf..d528a36ea01b 100644 --- a/validator/testdata/feature_tests/urls.out +++ b/validator/testdata/feature_tests/urls.out @@ -5,25 +5,23 @@ feature_tests/urls.html:40:2 Invalid URL protocol 'javascript:' for attribute 'h feature_tests/urls.html:41:2 Invalid URL protocol 'javascript:' for attribute 'href' in tag 'a'. [DISALLOWED_HTML] feature_tests/urls.html:42:2 Invalid URL protocol 'vbscript:' for attribute 'href' in tag 'a'. [DISALLOWED_HTML] feature_tests/urls.html:43:2 Invalid URL protocol 'data:' for attribute 'href' in tag 'a'. [DISALLOWED_HTML] -feature_tests/urls.html:45:2 Malformed URL 'http://site:80808/%AFpage' for attribute 'href' in tag 'a'. [DISALLOWED_HTML] -feature_tests/urls.html:47:2 Invalid URL protocol 'javascript:' for attribute 'srcset' in tag 'amp-img'. (see https://www.ampproject.org/docs/reference/amp-img.html) [AMP_TAG_PROBLEM] -feature_tests/urls.html:49:2 Multiple image candidates with the same width or pixel density found in attribute 'srcset' in tag 'amp-img'. (see https://www.ampproject.org/docs/reference/amp-img.html) [DISALLOWED_HTML] -feature_tests/urls.html:57:2 Invalid URL protocol 'javascript:' for attribute 'srcset' in tag 'amp-img'. (see https://www.ampproject.org/docs/reference/amp-img.html) [AMP_TAG_PROBLEM] -feature_tests/urls.html:59:2 Invalid URL protocol 'javascript:' for attribute 'srcset' in tag 'amp-img'. (see https://www.ampproject.org/docs/reference/amp-img.html) [AMP_TAG_PROBLEM] -feature_tests/urls.html:61:2 Malformed URL 'http://site:80808/%AFimg' for attribute 'srcset' in tag 'amp-img'. (see https://www.ampproject.org/docs/reference/amp-img.html) [AMP_TAG_PROBLEM] -feature_tests/urls.html:63:2 Missing URL for attribute 'srcset' in tag 'amp-img'. (see https://www.ampproject.org/docs/reference/amp-img.html) [AMP_TAG_PROBLEM] -feature_tests/urls.html:65:2 Multiple image candidates with the same width or pixel density found in attribute 'srcset' in tag 'amp-img'. (see https://www.ampproject.org/docs/reference/amp-img.html) [DISALLOWED_HTML] -feature_tests/urls.html:67:2 The attribute 'srcset' in tag 'amp-img' is set to the invalid value 'image 1x.png 1x, image 2x.png 2x'. (see https://www.ampproject.org/docs/reference/amp-img.html) [AMP_TAG_PROBLEM] -feature_tests/urls.html:72:2 Missing URL for attribute 'src' in tag 'amp-ad'. (see https://www.ampproject.org/docs/reference/amp-ad.html) [AMP_TAG_PROBLEM] -feature_tests/urls.html:73:2 Missing URL for attribute 'src' in tag 'amp-anim'. (see https://www.ampproject.org/docs/reference/extended/amp-anim.html) [AMP_TAG_PROBLEM] -feature_tests/urls.html:74:2 Missing URL for attribute 'src' in tag 'amp-audio'. (see https://www.ampproject.org/docs/reference/extended/amp-audio.html) [AMP_TAG_PROBLEM] -feature_tests/urls.html:75:2 Missing URL for attribute 'src' in tag 'amp-iframe'. (see https://www.ampproject.org/docs/reference/extended/amp-iframe.html) [AMP_TAG_PROBLEM] -feature_tests/urls.html:76:2 Missing URL for attribute 'src' in tag 'amp-img'. (see https://www.ampproject.org/docs/reference/amp-img.html) [AMP_TAG_PROBLEM] -feature_tests/urls.html:77:2 Missing URL for attribute 'src' in tag 'amp-pixel'. (see https://www.ampproject.org/docs/reference/amp-pixel.html) [AMP_TAG_PROBLEM] -feature_tests/urls.html:78:2 Missing URL for attribute 'src' in tag 'amp-video'. (see https://www.ampproject.org/docs/reference/amp-video.html) [AMP_TAG_PROBLEM] -feature_tests/urls.html:80:2 Invalid URL protocol 'http:' for attribute 'src' in tag 'amp-ad'. (see https://www.ampproject.org/docs/reference/amp-ad.html) [AMP_TAG_PROBLEM] -feature_tests/urls.html:81:2 Invalid URL protocol 'http:' for attribute 'src' in tag 'amp-iframe'. (see https://www.ampproject.org/docs/reference/extended/amp-iframe.html) [AMP_TAG_PROBLEM] -feature_tests/urls.html:82:2 Invalid URL protocol 'http:' for attribute 'src' in tag 'amp-pixel'. (see https://www.ampproject.org/docs/reference/amp-pixel.html) [AMP_TAG_PROBLEM] -feature_tests/urls.html:83:2 Invalid URL protocol 'http:' for attribute 'src' in tag 'amp-video'. (see https://www.ampproject.org/docs/reference/amp-video.html) [AMP_TAG_PROBLEM] -feature_tests/urls.html:84:2 Invalid URL protocol 'j a v a s c r i p t :' for attribute 'href' in tag 'a'. [DISALLOWED_HTML] -feature_tests/urls.html:86:7 The mandatory tag 'link rel=canonical' is missing or incorrect. (see https://www.ampproject.org/docs/reference/spec.html#required-markup) [MANDATORY_AMP_TAG_MISSING_OR_INCORRECT] +feature_tests/urls.html:46:2 Invalid URL protocol 'javascript:' for attribute 'srcset' in tag 'amp-img'. (see https://www.ampproject.org/docs/reference/amp-img.html) [AMP_TAG_PROBLEM] +feature_tests/urls.html:48:2 Multiple image candidates with the same width or pixel density found in attribute 'srcset' in tag 'amp-img'. (see https://www.ampproject.org/docs/reference/amp-img.html) [DISALLOWED_HTML] +feature_tests/urls.html:56:2 Invalid URL protocol 'javascript:' for attribute 'srcset' in tag 'amp-img'. (see https://www.ampproject.org/docs/reference/amp-img.html) [AMP_TAG_PROBLEM] +feature_tests/urls.html:58:2 Invalid URL protocol 'javascript:' for attribute 'srcset' in tag 'amp-img'. (see https://www.ampproject.org/docs/reference/amp-img.html) [AMP_TAG_PROBLEM] +feature_tests/urls.html:60:2 Missing URL for attribute 'srcset' in tag 'amp-img'. (see https://www.ampproject.org/docs/reference/amp-img.html) [AMP_TAG_PROBLEM] +feature_tests/urls.html:62:2 Multiple image candidates with the same width or pixel density found in attribute 'srcset' in tag 'amp-img'. (see https://www.ampproject.org/docs/reference/amp-img.html) [DISALLOWED_HTML] +feature_tests/urls.html:64:2 The attribute 'srcset' in tag 'amp-img' is set to the invalid value 'image 1x.png 1x, image 2x.png 2x'. (see https://www.ampproject.org/docs/reference/amp-img.html) [AMP_TAG_PROBLEM] +feature_tests/urls.html:69:2 Missing URL for attribute 'src' in tag 'amp-ad'. (see https://www.ampproject.org/docs/reference/amp-ad.html) [AMP_TAG_PROBLEM] +feature_tests/urls.html:70:2 Missing URL for attribute 'src' in tag 'amp-anim'. (see https://www.ampproject.org/docs/reference/extended/amp-anim.html) [AMP_TAG_PROBLEM] +feature_tests/urls.html:71:2 Missing URL for attribute 'src' in tag 'amp-audio'. (see https://www.ampproject.org/docs/reference/extended/amp-audio.html) [AMP_TAG_PROBLEM] +feature_tests/urls.html:72:2 Missing URL for attribute 'src' in tag 'amp-iframe'. (see https://www.ampproject.org/docs/reference/extended/amp-iframe.html) [AMP_TAG_PROBLEM] +feature_tests/urls.html:73:2 Missing URL for attribute 'src' in tag 'amp-img'. (see https://www.ampproject.org/docs/reference/amp-img.html) [AMP_TAG_PROBLEM] +feature_tests/urls.html:74:2 Missing URL for attribute 'src' in tag 'amp-pixel'. (see https://www.ampproject.org/docs/reference/amp-pixel.html) [AMP_TAG_PROBLEM] +feature_tests/urls.html:75:2 Missing URL for attribute 'src' in tag 'amp-video'. (see https://www.ampproject.org/docs/reference/amp-video.html) [AMP_TAG_PROBLEM] +feature_tests/urls.html:77:2 Invalid URL protocol 'http:' for attribute 'src' in tag 'amp-ad'. (see https://www.ampproject.org/docs/reference/amp-ad.html) [AMP_TAG_PROBLEM] +feature_tests/urls.html:78:2 Invalid URL protocol 'http:' for attribute 'src' in tag 'amp-iframe'. (see https://www.ampproject.org/docs/reference/extended/amp-iframe.html) [AMP_TAG_PROBLEM] +feature_tests/urls.html:79:2 Invalid URL protocol 'http:' for attribute 'src' in tag 'amp-pixel'. (see https://www.ampproject.org/docs/reference/amp-pixel.html) [AMP_TAG_PROBLEM] +feature_tests/urls.html:80:2 Invalid URL protocol 'http:' for attribute 'src' in tag 'amp-video'. (see https://www.ampproject.org/docs/reference/amp-video.html) [AMP_TAG_PROBLEM] +feature_tests/urls.html:81:2 Invalid URL protocol 'j a v a s c r i p t :' for attribute 'href' in tag 'a'. [DISALLOWED_HTML] +feature_tests/urls.html:83:7 The mandatory tag 'link rel=canonical' is missing or incorrect. (see https://www.ampproject.org/docs/reference/spec.html#required-markup) [MANDATORY_AMP_TAG_MISSING_OR_INCORRECT] diff --git a/validator/validator-full.js b/validator/validator-full.js index 1acf7d3cfd98..7f69a4c42e2f 100644 --- a/validator/validator-full.js +++ b/validator/validator-full.js @@ -145,6 +145,10 @@ amp.validator.ValidationResult.prototype.outputToTerminal = function( terminal.warn(errorLine(url, error)); } } + if (errorCategoryFilter === null && errors.length !== 0) { + terminal.info('See also https://validator.ampproject.org/#url=' + + encodeURIComponent(goog.uri.utils.removeFragment(url))); + } }; /** @@ -337,6 +341,8 @@ amp.validator.categorizeError = function(error) { amp.validator.ValidationError.Code.CSS_SYNTAX_INVALID_URL || error.code === amp.validator.ValidationError.Code.CSS_SYNTAX_INVALID_URL_PROTOCOL || + error.code === + amp.validator.ValidationError.Code.CSS_SYNTAX_DISALLOWED_DOMAIN || error.code === amp.validator.ValidationError.Code .CSS_SYNTAX_DISALLOWED_RELATIVE_URL) && @@ -519,6 +525,7 @@ amp.validator.categorizeError = function(error) { if (error.code == amp.validator.ValidationError.Code.MISSING_URL || error.code == amp.validator.ValidationError.Code.INVALID_URL || error.code == amp.validator.ValidationError.Code.INVALID_URL_PROTOCOL || + error.code == amp.validator.ValidationError.Code.DISALLOWED_DOMAIN || error.code == amp.validator.ValidationError.Code.DISALLOWED_RELATIVE_URL) { if (goog.string./*OK*/ startsWith(error.params[1], 'amp-')) { diff --git a/validator/validator-main.protoascii b/validator/validator-main.protoascii index e84532111218..50da911e73ec 100644 --- a/validator/validator-main.protoascii +++ b/validator/validator-main.protoascii @@ -20,12 +20,12 @@ # in production from crashing. This id is not relevant to validator.js # because thus far, engine (validator.js) and spec file # (validator-main.protoascii) are always released together. -min_validator_revision_required: 135 +min_validator_revision_required: 143 # The spec file revision allows the validator engine to distinguish # newer versions of the spec file. This is currently a Google internal # mechanism, validator.js does not use this facility. However, any # change to this file (validator-main.js) requires updating this revision id. -spec_file_revision: 231 +spec_file_revision: 235 # Validator extensions. # ===================== @@ -716,6 +716,7 @@ attr_lists: { allowed_protocol: "fb-messenger" allowed_protocol: "sms" allowed_protocol: "tel" + allowed_protocol: "threema" allowed_protocol: "viber" allowed_protocol: "whatsapp" } @@ -800,6 +801,7 @@ tags: { allowed_protocol: "fb-messenger" allowed_protocol: "sms" allowed_protocol: "tel" + allowed_protocol: "threema" allowed_protocol: "viber" allowed_protocol: "whatsapp" allow_relative: true @@ -1902,7 +1904,94 @@ tags: { } # 4.10 Forms -# Not allowed, except for button. +# Form elements are allowed only when amp-form extension script is included +# with the exception of the button element which is always allowed. +# 4.10.3 The form element +tags { + tag_name: "FORM" + also_requires_tag: "amp-form extension .js script" + attrs: { name: "accept" } + attrs: { name: "accept-charset" } + attrs: { + name: "action" + mandatory: true + value_url: { + allow_relative: false + allowed_protocol: "https" + disallowed_domain: "cdn.ampproject.org" + } + } + attrs: { name: "action-xhr" } + attrs: { name: "autocomplete" } + attrs: { name: "enctype" } + attrs: { name: "method" } + attrs: { name: "name" } + attrs: { name: "novalidate" } + attrs: { + name: "target" + mandatory: true + value_regex_casei: "(_blank|_top)" + } + # TODO: update with ampproject.org url when available + spec_url: "https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md" +} +# 4.10.4 The label element +tags: { + tag_name: "LABEL" + mandatory_ancestor: "FORM" + also_requires_tag: "amp-form extension .js script" + attrs: { name: "for" } + attrs: { name: "form" } + # TODO: update with ampproject.org url when available + spec_url: "https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md" +} +# 4.10.5 The input element +tags: { + tag_name: "INPUT" + mandatory_ancestor: "FORM" + also_requires_tag: "amp-form extension .js script" + attrs: { name: "accept" } + attrs: { name: "accesskey" } + attrs: { name: "autocomplete" } + attrs: { name: "autofocus" } + attrs: { name: "checked" } + attrs: { name: "disabled" } + attrs: { name: "form" } + attrs: { name: "formmethod" } + attrs: { name: "formnovalidate" } + attrs: { name: "height" } + attrs: { name: "inputmode" } + attrs: { name: "list" } + attrs: { name: "max" } + attrs: { name: "maxlength" } + attrs: { name: "min" } + attrs: { name: "minlength" } + attrs: { name: "multiple" } + attrs: { name: "name" } + attrs: { name: "pattern" } + attrs: { name: "placeholder" } + attrs: { name: "readonly" } + attrs: { name: "required" } + attrs: { name: "selectiondirection" } + attrs: { name: "size" } + attrs: { name: "spellcheck" } + attrs: { name: "step" } + attrs: { name: "tabindex" } + attrs: { + name: "type" + blacklisted_value_regex: "(^|\\s)(" # Values are space separated. + "button|" + "file|" + "image|" + "password|" + ")(\\s|$)" + } + attrs: { name: "value" } + attrs: { name: "width" } + # TODO: update with ampproject.org url when available + spec_url: "https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md" +} +# 4.10.6 The button element tags: { tag_name: "BUTTON" attrs: { @@ -1921,6 +2010,69 @@ tags: { attrs: { name: "type" } attrs: { name: "value" } } +# 4.10.7 The select element +tags: { + tag_name: "SELECT" + mandatory_ancestor: "FORM" + also_requires_tag: "amp-form extension .js script" + attrs: { name: "autofocus" } + attrs: { name: "disabled" } + attrs: { name: "form" } + attrs: { name: "multiple" } + attrs: { name: "name" } + attrs: { name: "required" } + attrs: { name: "size" } + # TODO: update with ampproject.org url when available + spec_url: "https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md" +} +# 4.10.10 The option element +tags: { + tag_name: "OPTION" + mandatory_ancestor: "FORM" + also_requires_tag: "amp-form extension .js script" + attrs: { name: "disabled" } + attrs: { name: "label" } + attrs: { name: "selected" } + attrs: { name: "value" } + # TODO: update with ampproject.org url when available + spec_url: "https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md" +} +# 4.10.11 The textarea element +tags: { + tag_name: "TEXTAREA" + mandatory_ancestor: "FORM" + also_requires_tag: "amp-form extension .js script" + attrs: { name: "autocomplete" } + attrs: { name: "autofocus" } + attrs: { name: "cols" } + attrs: { name: "disabled" } + attrs: { name: "form" } + attrs: { name: "maxlength" } + attrs: { name: "minlength" } + attrs: { name: "name" } + attrs: { name: "placeholder" } + attrs: { name: "readonly" } + attrs: { name: "required" } + attrs: { name: "rows" } + attrs: { name: "selectiondirection" } + attrs: { name: "selectionend" } + attrs: { name: "selectionstart" } + attrs: { name: "spellcheck" } + attrs: { name: "wrap"} + # TODO: update with ampproject.org url when available + spec_url: "https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md" +} +# 4.10.16 The fieldset element +tags: { + tag_name: "FIELDSET" + mandatory_ancestor: "FORM" + also_requires_tag: "amp-form extension .js script" + attrs: { name: "disabled" } + attrs: { name: "form" } + attrs: { name: "name" } + # TODO: update with ampproject.org url when available + spec_url: "https://github.com/ampproject/amphtml/blob/master/extensions/amp-form/amp-form.md" +} # 4.11 Scripting # 4.11.1 The script element @@ -2066,58 +2218,6 @@ attr_lists: { attrs: { name: "media" } attrs: { name: "noloading" value: "" } } -# In the AMP Runtime, amp-ad and amp-embed are implemented in the same way, -# so if you're changing amp-ad consider changing amp-embed as well. -tags: { # - tag_name: "AMP-AD" - disallowed_ancestor: "AMP-SIDEBAR" - attrs: { name: "alt" } - attrs: { name: "json" } - attrs: { - name: "src" - value_url: { - allowed_protocol: "https" - allow_relative: true # Will be set to false at a future date. - } - } - attrs: { name: "type" mandatory: true } - attr_lists: "extended-amp-global" - spec_url: "https://www.ampproject.org/docs/reference/amp-ad.html" - amp_layout: { - supported_layouts: FILL - supported_layouts: FIXED - supported_layouts: FIXED_HEIGHT - supported_layouts: FLEX_ITEM - supported_layouts: NODISPLAY - supported_layouts: RESPONSIVE - } -} -# In the AMP Runtime, amp-ad and amp-embed are implemented in the same way, -# so if you're changing amp-embed consider changing amp-ad as well. -tags: { # - tag_name: "AMP-EMBED" - disallowed_ancestor: "AMP-SIDEBAR" - attrs: { name: "alt" } - attrs: { name: "json" } - attrs: { - name: "src" - value_url: { - allowed_protocol: "https" - allow_relative: true # Will be set to false at a future date. - } - } - attrs: { name: "type" mandatory: true } - attr_lists: "extended-amp-global" - spec_url: "https://www.ampproject.org/docs/reference/amp-embed.html" - amp_layout: { - supported_layouts: FILL - supported_layouts: FIXED - supported_layouts: FIXED_HEIGHT - supported_layouts: FLEX_ITEM - supported_layouts: NODISPLAY - supported_layouts: RESPONSIVE - } -} tags: { # tag_name: "AMP-IMG" # is whitelisted for , otherwise: @@ -2392,6 +2492,10 @@ error_formats { code: INVALID_URL_PROTOCOL format: "Invalid URL protocol '%3:' for attribute '%1' in tag '%2'." } +error_formats { + code: DISALLOWED_DOMAIN + format: "The domain '%3' for attribute '%1' in tag '%2' is disallowed." +} error_formats { code: DISALLOWED_RELATIVE_URL format: "The relative URL '%3' for attribute '%1' in tag '%2' is disallowed." @@ -2548,6 +2652,10 @@ error_formats { code: CSS_SYNTAX_INVALID_URL_PROTOCOL format: "CSS syntax error in tag '%1' - invalid url protocol '%2:'." } +error_formats { + code: CSS_SYNTAX_DISALLOWED_DOMAIN + format: "CSS syntax error in tag '%1' - invalid domain '%2'." +} error_formats { code: CSS_SYNTAX_DISALLOWED_RELATIVE_URL format: "CSS syntax error in tag '%1' - disallowed relative url '%2'." diff --git a/validator/validator.js b/validator/validator.js index a674c5704f28..ca3cee5be900 100644 --- a/validator/validator.js +++ b/validator/validator.js @@ -31,10 +31,10 @@ goog.require('amp.validator.ValidationError.Severity'); goog.require('amp.validator.ValidationResult'); goog.require('amp.validator.ValidationResult.Status'); goog.require('amp.validator.ValidatorRules'); -goog.require('goog.Uri'); goog.require('goog.array'); goog.require('goog.asserts'); goog.require('goog.string'); +goog.require('goog.uri.utils'); goog.require('parse_css.BlockType'); goog.require('parse_css.ParsedCssUrl'); goog.require('parse_css.RuleVisitor'); @@ -935,7 +935,7 @@ class ParsedUrlSpec { this.spec_ = spec; /** - * @type {!Object} + * @type {!Object} * @private */ this.allowedProtocols_ = {}; @@ -944,6 +944,17 @@ class ParsedUrlSpec { this.allowedProtocols_[protocol] = 0; } } + + /** + * @type {!Object} + * @private + */ + this.disallowedDomains_ = {}; + if (this.spec_ !== null) { + for (const domain of this.spec_.disallowedDomain) { + this.disallowedDomains_[domain] = 0; + } + } } /** @@ -990,10 +1001,8 @@ class ParsedUrlSpec { } return; } - let uri; - try { - uri = goog.Uri.parse(url); - } catch (ex) { + const urlComponents = goog.uri.utils.split(url); + if (urlComponents === null) { if (amp.validator.GENERATE_DETAILED_ERRORS) { adapter.invalidUrl(context, url, tagSpec, result); } else { @@ -1001,17 +1010,18 @@ class ParsedUrlSpec { } return; } - if (uri.hasScheme() && - !this.allowedProtocols_.hasOwnProperty(uri.getScheme().toLowerCase())) { + const scheme = urlComponents[goog.uri.utils.ComponentIndex.SCHEME]; + if (scheme && + !this.allowedProtocols_.hasOwnProperty(scheme.toLowerCase())) { if (amp.validator.GENERATE_DETAILED_ERRORS) { adapter.invalidUrlProtocol( - context, uri.getScheme().toLowerCase(), tagSpec, result); + context, scheme.toLowerCase(), tagSpec, result); } else { result.status = amp.validator.ValidationResult.Status.FAIL; } return; } - if (!this.spec_.allowRelative && !uri.hasScheme()) { + if (!this.spec_.allowRelative && (scheme === undefined)) { if (amp.validator.GENERATE_DETAILED_ERRORS) { adapter.disallowedRelativeUrl(context, url, tagSpec, result); } else { @@ -1019,6 +1029,17 @@ class ParsedUrlSpec { } return; } + const domain = urlComponents[goog.uri.utils.ComponentIndex.DOMAIN]; + if (domain && + this.disallowedDomains_.hasOwnProperty(domain.toLowerCase())) { + if (amp.validator.GENERATE_DETAILED_ERRORS) { + adapter.disallowedDomain( + context, domain.toLowerCase(), tagSpec, result); + } else { + result.status = amp.validator.ValidationResult.Status.FAIL; + } + return; + } } } @@ -1073,6 +1094,21 @@ ParsedUrlSpec.AttrErrorAdapter_ = class { tagSpec.specUrl, result); } + /** + * @param {!Context} context + * @param {string} domain + * @param {!amp.validator.TagSpec} tagSpec + * @param {!amp.validator.ValidationResult} result + */ + disallowedDomain(context, domain, tagSpec, result) { + context.addError( + amp.validator.ValidationError.Severity.ERROR, + amp.validator.ValidationError.Code.DISALLOWED_DOMAIN, + context.getDocLocator(), + /* params */[this.attrName_, getTagSpecName(tagSpec), domain], + tagSpec.specUrl, result); + } + /** * @param {!Context} context * @param {string} url @@ -1147,6 +1183,20 @@ ParsedUrlSpec.StylesheetErrorAdapter_ = class { result); } + /** + * @param {!Context} context + * @param {string} domain + * @param {!amp.validator.TagSpec} tagSpec + * @param {!amp.validator.ValidationResult} result + */ + disallowedDomain(context, domain, tagSpec, result) { + context.addError( + amp.validator.ValidationError.Severity.ERROR, + amp.validator.ValidationError.Code.CSS_SYNTAX_DISALLOWED_DOMAIN, + this.lineCol_, + /* params */[getTagSpecName(tagSpec), domain], tagSpec.specUrl, result); + } + /** * @param {!Context} context * @param {string} url @@ -1890,7 +1940,7 @@ function makeDispatchKey(attrName, attrValue, mandatoryParent) { */ class ParsedTagSpec { /** - * @param {string} templateSpecUrl + * @param {?string} templateSpecUrl * @param {!ParsedAttrLists} parsedAttrLists * @param {!Object} tagspecIdsByTagSpecName * @param {boolean} shouldRecordTagspecValidated @@ -1935,7 +1985,7 @@ class ParsedTagSpec { */ this.mandatoryOneofs_ = []; /** - * @type {string} + * @type {?string} * @private */ this.templateSpecUrl_ = templateSpecUrl; @@ -2427,6 +2477,13 @@ class ParsedTagSpec { if (result.status === amp.validator.ValidationResult.Status.FAIL) { return; } + if (hasTemplateAncestor) { + this.validateAttrValueBelowTemplateTag( + context, attrName, attrValue, result); + if (result.status === amp.validator.ValidationResult.Status.FAIL) { + return; + } + } continue; } if (hasTemplateAncestor) { @@ -2830,50 +2887,54 @@ function specificity(code) { return 35; case amp.validator.ValidationError.Code.MISSING_URL: return 36; - case amp.validator.ValidationError.Code.INVALID_URL_PROTOCOL: + case amp.validator.ValidationError.Code.DISALLOWED_DOMAIN: return 37; - case amp.validator.ValidationError.Code.INVALID_URL: + case amp.validator.ValidationError.Code.INVALID_URL_PROTOCOL: return 38; - case amp.validator.ValidationError.Code.CSS_SYNTAX_STRAY_TRAILING_BACKSLASH: + case amp.validator.ValidationError.Code.INVALID_URL: return 39; - case amp.validator.ValidationError.Code.CSS_SYNTAX_UNTERMINATED_COMMENT: + case amp.validator.ValidationError.Code.CSS_SYNTAX_STRAY_TRAILING_BACKSLASH: return 40; - case amp.validator.ValidationError.Code.CSS_SYNTAX_UNTERMINATED_STRING: + case amp.validator.ValidationError.Code.CSS_SYNTAX_UNTERMINATED_COMMENT: return 41; - case amp.validator.ValidationError.Code.CSS_SYNTAX_BAD_URL: + case amp.validator.ValidationError.Code.CSS_SYNTAX_UNTERMINATED_STRING: return 42; + case amp.validator.ValidationError.Code.CSS_SYNTAX_BAD_URL: + return 43; case amp.validator.ValidationError.Code .CSS_SYNTAX_EOF_IN_PRELUDE_OF_QUALIFIED_RULE: - return 43; - case amp.validator.ValidationError.Code.CSS_SYNTAX_INVALID_DECLARATION: return 44; - case amp.validator.ValidationError.Code.CSS_SYNTAX_INCOMPLETE_DECLARATION: + case amp.validator.ValidationError.Code.CSS_SYNTAX_INVALID_DECLARATION: return 45; - case amp.validator.ValidationError.Code.CSS_SYNTAX_ERROR_IN_PSEUDO_SELECTOR: + case amp.validator.ValidationError.Code.CSS_SYNTAX_INCOMPLETE_DECLARATION: return 46; - case amp.validator.ValidationError.Code.CSS_SYNTAX_MISSING_SELECTOR: + case amp.validator.ValidationError.Code.CSS_SYNTAX_ERROR_IN_PSEUDO_SELECTOR: return 47; - case amp.validator.ValidationError.Code.CSS_SYNTAX_NOT_A_SELECTOR_START: + case amp.validator.ValidationError.Code.CSS_SYNTAX_MISSING_SELECTOR: return 48; + case amp.validator.ValidationError.Code.CSS_SYNTAX_NOT_A_SELECTOR_START: + return 49; case amp.validator.ValidationError.Code .CSS_SYNTAX_UNPARSED_INPUT_REMAINS_IN_SELECTOR: - return 49; - case amp.validator.ValidationError.Code.CSS_SYNTAX_MISSING_URL: return 50; - case amp.validator.ValidationError.Code.CSS_SYNTAX_INVALID_URL: + case amp.validator.ValidationError.Code.CSS_SYNTAX_MISSING_URL: return 51; - case amp.validator.ValidationError.Code.CSS_SYNTAX_INVALID_URL_PROTOCOL: + case amp.validator.ValidationError.Code.CSS_SYNTAX_DISALLOWED_DOMAIN: return 52; - case amp.validator.ValidationError.Code.CSS_SYNTAX_DISALLOWED_RELATIVE_URL: + case amp.validator.ValidationError.Code.CSS_SYNTAX_INVALID_URL: return 53; - case amp.validator.ValidationError.Code.INCORRECT_NUM_CHILD_TAGS: + case amp.validator.ValidationError.Code.CSS_SYNTAX_INVALID_URL_PROTOCOL: return 54; - case amp.validator.ValidationError.Code.DISALLOWED_CHILD_TAG_NAME: + case amp.validator.ValidationError.Code.CSS_SYNTAX_DISALLOWED_RELATIVE_URL: return 55; - case amp.validator.ValidationError.Code.DISALLOWED_FIRST_CHILD_TAG_NAME: + case amp.validator.ValidationError.Code.INCORRECT_NUM_CHILD_TAGS: return 56; - case amp.validator.ValidationError.Code.CSS_SYNTAX_INVALID_ATTR_SELECTOR: + case amp.validator.ValidationError.Code.DISALLOWED_CHILD_TAG_NAME: return 57; + case amp.validator.ValidationError.Code.DISALLOWED_FIRST_CHILD_TAG_NAME: + return 58; + case amp.validator.ValidationError.Code.CSS_SYNTAX_INVALID_ATTR_SELECTOR: + return 59; case amp.validator.ValidationError.Code.GENERAL_DISALLOWED_TAG: return 100; case amp.validator.ValidationError.Code.DEPRECATED_ATTR: @@ -2953,7 +3014,8 @@ class ParsedValidatorRules { for (let i = 0; i < rules.tags.length; ++i) { const tag = rules.tags[i]; - goog.asserts.assert(rules.templateSpecUrl != null); + if (amp.validator.GENERATE_DETAILED_ERRORS) + goog.asserts.assert(rules.templateSpecUrl != null); const parsedTagSpec = new ParsedTagSpec( rules.templateSpecUrl, parsedAttrLists, tagspecIdsByTagSpecName, shouldRecordTagspecValidated(tag, tagSpecNamesToTrack), tag, i); diff --git a/validator/validator.proto b/validator/validator.proto index 7998910a8595..e3681dc69a53 100644 --- a/validator/validator.proto +++ b/validator/validator.proto @@ -56,6 +56,8 @@ message UrlSpec { optional bool allow_relative = 2 [default = true]; // Whether the empty string '' is allowed for this URL value. optional bool allow_empty = 3; + // Must be lowercase. + repeated string disallowed_domain = 4; } // Attributes that are not covered by at least one of these specs are @@ -388,7 +390,7 @@ message ValidationError { DEV_WARNING = 3; // DEPRECATED DO NOT USE. } optional Severity severity = 6 [ default = ERROR ]; - // NEXT AVAILABLE TAG: 62 + // NEXT AVAILABLE TAG: 64 enum Code { UNKNOWN_CODE = 0; MANDATORY_TAG_MISSING = 1; @@ -418,6 +420,7 @@ message ValidationError { MISSING_URL = 35; INVALID_URL = 36; INVALID_URL_PROTOCOL = 37; + DISALLOWED_DOMAIN = 62; DISALLOWED_RELATIVE_URL = 49; DISALLOWED_PROPERTY_IN_ATTR_VALUE = 16; MUTUALLY_EXCLUSIVE_ATTRS = 17; @@ -450,6 +453,7 @@ message ValidationError { CSS_SYNTAX_MISSING_URL = 52; CSS_SYNTAX_INVALID_URL = 53; CSS_SYNTAX_INVALID_URL_PROTOCOL = 54; + CSS_SYNTAX_DISALLOWED_DOMAIN = 63; CSS_SYNTAX_DISALLOWED_RELATIVE_URL = 55; CSS_SYNTAX_INVALID_ATTR_SELECTOR = 59; }