html-validate 7.0.0 → 7.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{LICENCE → LICENSE} +0 -0
- package/dist/cjs/core.d.ts +2 -1
- package/dist/cjs/core.js +239 -84
- package/dist/cjs/core.js.map +1 -1
- package/dist/es/core.d.ts +2 -1
- package/dist/es/core.js +239 -84
- package/dist/es/core.js.map +1 -1
- package/elements/html5.js +6 -0
- package/package.json +32 -37
package/dist/cjs/core.js
CHANGED
|
@@ -1763,7 +1763,7 @@ function stripslashes(value) {
|
|
|
1763
1763
|
return value.replace(/\\(.)/g, "$1");
|
|
1764
1764
|
}
|
|
1765
1765
|
function escapeSelectorComponent(text) {
|
|
1766
|
-
return text.toString().replace(/([
|
|
1766
|
+
return text.toString().replace(/([^a-z0-9_-])/gi, "\\$1");
|
|
1767
1767
|
}
|
|
1768
1768
|
class Matcher {
|
|
1769
1769
|
}
|
|
@@ -2102,6 +2102,7 @@ class HtmlElement extends DOMNode {
|
|
|
2102
2102
|
}
|
|
2103
2103
|
const parts = [];
|
|
2104
2104
|
let root;
|
|
2105
|
+
/* eslint-disable-next-line @typescript-eslint/no-this-alias */
|
|
2105
2106
|
for (root = this; root.parent; root = root.parent) {
|
|
2106
2107
|
/* .. */
|
|
2107
2108
|
}
|
|
@@ -2645,12 +2646,15 @@ class Validator {
|
|
|
2645
2646
|
if (value === null || value === undefined) {
|
|
2646
2647
|
return false;
|
|
2647
2648
|
}
|
|
2649
|
+
const caseInsensitiveValue = value.toLowerCase();
|
|
2648
2650
|
return rule.enum.some((entry) => {
|
|
2649
2651
|
if (entry instanceof RegExp) {
|
|
2652
|
+
/* regular expressions are matched case-sensitive */
|
|
2650
2653
|
return !!value.match(entry);
|
|
2651
2654
|
}
|
|
2652
2655
|
else {
|
|
2653
|
-
|
|
2656
|
+
/* strings matched case-insensitive */
|
|
2657
|
+
return caseInsensitiveValue === entry;
|
|
2654
2658
|
}
|
|
2655
2659
|
});
|
|
2656
2660
|
}
|
|
@@ -3113,7 +3117,7 @@ var TRANSFORMER_API;
|
|
|
3113
3117
|
/** @public */
|
|
3114
3118
|
const name = "html-validate";
|
|
3115
3119
|
/** @public */
|
|
3116
|
-
const version = "7.
|
|
3120
|
+
const version = "7.1.2";
|
|
3117
3121
|
/** @public */
|
|
3118
3122
|
const homepage = "https://html-validate.org";
|
|
3119
3123
|
/** @public */
|
|
@@ -3276,17 +3280,18 @@ class Rule {
|
|
|
3276
3280
|
* not `"foo"`.
|
|
3277
3281
|
*
|
|
3278
3282
|
* @param keyword - Keyword to match against `include` and `exclude` options.
|
|
3283
|
+
* @param matcher - Optional function to compare items with.
|
|
3279
3284
|
* @returns `true` if keyword is not present in `include` or is present in
|
|
3280
3285
|
* `exclude`.
|
|
3281
3286
|
*/
|
|
3282
|
-
isKeywordIgnored(keyword) {
|
|
3287
|
+
isKeywordIgnored(keyword, matcher = (list, it) => list.includes(it)) {
|
|
3283
3288
|
const { include, exclude } = this.options;
|
|
3284
3289
|
/* ignore keyword if not present in "include" */
|
|
3285
|
-
if (include && !include
|
|
3290
|
+
if (include && !matcher(include, keyword)) {
|
|
3286
3291
|
return true;
|
|
3287
3292
|
}
|
|
3288
3293
|
/* ignore keyword if present in "excludes" */
|
|
3289
|
-
if (exclude && exclude
|
|
3294
|
+
if (exclude && matcher(exclude, keyword)) {
|
|
3290
3295
|
return true;
|
|
3291
3296
|
}
|
|
3292
3297
|
return false;
|
|
@@ -3434,7 +3439,7 @@ function ruleDocumentationUrl(filename) {
|
|
|
3434
3439
|
return `${homepage}/rules/${normalized}.html`;
|
|
3435
3440
|
}
|
|
3436
3441
|
|
|
3437
|
-
const defaults$
|
|
3442
|
+
const defaults$s = {
|
|
3438
3443
|
allowExternal: true,
|
|
3439
3444
|
allowRelative: true,
|
|
3440
3445
|
allowAbsolute: true,
|
|
@@ -3447,11 +3452,11 @@ const mapping$1 = {
|
|
|
3447
3452
|
script: "src",
|
|
3448
3453
|
};
|
|
3449
3454
|
const description = {
|
|
3450
|
-
["external" /* EXTERNAL */]: "External links are not allowed by current configuration.",
|
|
3451
|
-
["relative-base" /* RELATIVE_BASE */]: "Links relative to <base> are not allowed by current configuration.",
|
|
3452
|
-
["relative-path" /* RELATIVE_PATH */]: "Relative links are not allowed by current configuration.",
|
|
3453
|
-
["absolute" /* ABSOLUTE */]: "Absolute links are not allowed by current configuration.",
|
|
3454
|
-
["anchor" /* ANCHOR */]: null,
|
|
3455
|
+
["external" /* Style.EXTERNAL */]: "External links are not allowed by current configuration.",
|
|
3456
|
+
["relative-base" /* Style.RELATIVE_BASE */]: "Links relative to <base> are not allowed by current configuration.",
|
|
3457
|
+
["relative-path" /* Style.RELATIVE_PATH */]: "Relative links are not allowed by current configuration.",
|
|
3458
|
+
["absolute" /* Style.ABSOLUTE */]: "Absolute links are not allowed by current configuration.",
|
|
3459
|
+
["anchor" /* Style.ANCHOR */]: null,
|
|
3455
3460
|
};
|
|
3456
3461
|
function parseAllow(value) {
|
|
3457
3462
|
if (typeof value === "boolean") {
|
|
@@ -3478,7 +3483,7 @@ function matchList(value, list) {
|
|
|
3478
3483
|
}
|
|
3479
3484
|
class AllowedLinks extends Rule {
|
|
3480
3485
|
constructor(options) {
|
|
3481
|
-
super({ ...defaults$
|
|
3486
|
+
super({ ...defaults$s, ...options });
|
|
3482
3487
|
this.allowExternal = parseAllow(this.options.allowExternal);
|
|
3483
3488
|
this.allowRelative = parseAllow(this.options.allowRelative);
|
|
3484
3489
|
this.allowAbsolute = parseAllow(this.options.allowAbsolute);
|
|
@@ -3524,19 +3529,19 @@ class AllowedLinks extends Rule {
|
|
|
3524
3529
|
const link = event.value.toString();
|
|
3525
3530
|
const style = this.getStyle(link);
|
|
3526
3531
|
switch (style) {
|
|
3527
|
-
case "anchor" /* ANCHOR */:
|
|
3532
|
+
case "anchor" /* Style.ANCHOR */:
|
|
3528
3533
|
/* anchor links are always allowed by this rule */
|
|
3529
3534
|
break;
|
|
3530
|
-
case "absolute" /* ABSOLUTE */:
|
|
3535
|
+
case "absolute" /* Style.ABSOLUTE */:
|
|
3531
3536
|
this.handleAbsolute(link, event, style);
|
|
3532
3537
|
break;
|
|
3533
|
-
case "external" /* EXTERNAL */:
|
|
3538
|
+
case "external" /* Style.EXTERNAL */:
|
|
3534
3539
|
this.handleExternal(link, event, style);
|
|
3535
3540
|
break;
|
|
3536
|
-
case "relative-base" /* RELATIVE_BASE */:
|
|
3541
|
+
case "relative-base" /* Style.RELATIVE_BASE */:
|
|
3537
3542
|
this.handleRelativeBase(link, event, style);
|
|
3538
3543
|
break;
|
|
3539
|
-
case "relative-path" /* RELATIVE_PATH */:
|
|
3544
|
+
case "relative-path" /* Style.RELATIVE_PATH */:
|
|
3540
3545
|
this.handleRelativePath(link, event, style);
|
|
3541
3546
|
break;
|
|
3542
3547
|
}
|
|
@@ -3554,21 +3559,21 @@ class AllowedLinks extends Rule {
|
|
|
3554
3559
|
getStyle(value) {
|
|
3555
3560
|
/* http://example.net or //example.net */
|
|
3556
3561
|
if (value.match(/^([a-z]+:)?\/\//g)) {
|
|
3557
|
-
return "external" /* EXTERNAL */;
|
|
3562
|
+
return "external" /* Style.EXTERNAL */;
|
|
3558
3563
|
}
|
|
3559
3564
|
switch (value[0]) {
|
|
3560
3565
|
/* /foo/bar */
|
|
3561
3566
|
case "/":
|
|
3562
|
-
return "absolute" /* ABSOLUTE */;
|
|
3567
|
+
return "absolute" /* Style.ABSOLUTE */;
|
|
3563
3568
|
/* ../foo/bar */
|
|
3564
3569
|
case ".":
|
|
3565
|
-
return "relative-path" /* RELATIVE_PATH */;
|
|
3570
|
+
return "relative-path" /* Style.RELATIVE_PATH */;
|
|
3566
3571
|
/* #foo */
|
|
3567
3572
|
case "#":
|
|
3568
|
-
return "anchor" /* ANCHOR */;
|
|
3573
|
+
return "anchor" /* Style.ANCHOR */;
|
|
3569
3574
|
/* foo/bar */
|
|
3570
3575
|
default:
|
|
3571
|
-
return "relative-base" /* RELATIVE_BASE */;
|
|
3576
|
+
return "relative-base" /* Style.RELATIVE_BASE */;
|
|
3572
3577
|
}
|
|
3573
3578
|
}
|
|
3574
3579
|
handleAbsolute(target, event, style) {
|
|
@@ -3779,13 +3784,13 @@ class CaseStyle {
|
|
|
3779
3784
|
}
|
|
3780
3785
|
}
|
|
3781
3786
|
|
|
3782
|
-
const defaults$
|
|
3787
|
+
const defaults$r = {
|
|
3783
3788
|
style: "lowercase",
|
|
3784
3789
|
ignoreForeign: true,
|
|
3785
3790
|
};
|
|
3786
3791
|
class AttrCase extends Rule {
|
|
3787
3792
|
constructor(options) {
|
|
3788
|
-
super({ ...defaults$
|
|
3793
|
+
super({ ...defaults$r, ...options });
|
|
3789
3794
|
this.style = new CaseStyle(this.options.style, "attr-case");
|
|
3790
3795
|
}
|
|
3791
3796
|
static schema() {
|
|
@@ -4125,7 +4130,7 @@ class AttrDelimiter extends Rule {
|
|
|
4125
4130
|
}
|
|
4126
4131
|
|
|
4127
4132
|
const DEFAULT_PATTERN = "[a-z0-9-:]+";
|
|
4128
|
-
const defaults$
|
|
4133
|
+
const defaults$q = {
|
|
4129
4134
|
pattern: DEFAULT_PATTERN,
|
|
4130
4135
|
ignoreForeign: true,
|
|
4131
4136
|
};
|
|
@@ -4162,7 +4167,7 @@ function generateDescription(name, pattern) {
|
|
|
4162
4167
|
}
|
|
4163
4168
|
class AttrPattern extends Rule {
|
|
4164
4169
|
constructor(options) {
|
|
4165
|
-
super({ ...defaults$
|
|
4170
|
+
super({ ...defaults$q, ...options });
|
|
4166
4171
|
this.pattern = generateRegexp(this.options.pattern);
|
|
4167
4172
|
}
|
|
4168
4173
|
static schema() {
|
|
@@ -4221,20 +4226,56 @@ var QuoteStyle;
|
|
|
4221
4226
|
QuoteStyle["SINGLE_QUOTE"] = "'";
|
|
4222
4227
|
QuoteStyle["DOUBLE_QUOTE"] = "\"";
|
|
4223
4228
|
QuoteStyle["AUTO_QUOTE"] = "auto";
|
|
4229
|
+
QuoteStyle["ANY_QUOTE"] = "any";
|
|
4224
4230
|
})(QuoteStyle || (QuoteStyle = {}));
|
|
4225
|
-
const defaults$
|
|
4231
|
+
const defaults$p = {
|
|
4226
4232
|
style: "auto",
|
|
4227
4233
|
unquoted: false,
|
|
4228
4234
|
};
|
|
4235
|
+
function describeError(context) {
|
|
4236
|
+
if (context) {
|
|
4237
|
+
switch (context.error) {
|
|
4238
|
+
case "style":
|
|
4239
|
+
return `Attribute \`${context.attr}\` must use \`${context.expected}\` instead of \`${context.actual}\`.`;
|
|
4240
|
+
case "unquoted":
|
|
4241
|
+
return `Attribute \`${context.attr}\` must not be unquoted.`;
|
|
4242
|
+
}
|
|
4243
|
+
}
|
|
4244
|
+
else {
|
|
4245
|
+
return "This attribute is not quoted properly.";
|
|
4246
|
+
}
|
|
4247
|
+
}
|
|
4248
|
+
function describeStyle(style, unquoted) {
|
|
4249
|
+
const description = [];
|
|
4250
|
+
switch (style) {
|
|
4251
|
+
case QuoteStyle.AUTO_QUOTE:
|
|
4252
|
+
description.push("- quoted with double quotes `\"` unless the value contains double quotes in which case single quotes `'` should be used instead");
|
|
4253
|
+
break;
|
|
4254
|
+
case QuoteStyle.ANY_QUOTE:
|
|
4255
|
+
description.push("- quoted with single quotes `'`");
|
|
4256
|
+
description.push('- quoted with double quotes `"`');
|
|
4257
|
+
break;
|
|
4258
|
+
case QuoteStyle.SINGLE_QUOTE:
|
|
4259
|
+
case QuoteStyle.DOUBLE_QUOTE: {
|
|
4260
|
+
const name = style === QuoteStyle.SINGLE_QUOTE ? "single" : "double";
|
|
4261
|
+
description.push(`- quoted with ${name} quotes \`${style}\``);
|
|
4262
|
+
break;
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
4265
|
+
if (unquoted) {
|
|
4266
|
+
description.push("- unquoted (if applicable)");
|
|
4267
|
+
}
|
|
4268
|
+
return `${description.join(" or\n")}\n`;
|
|
4269
|
+
}
|
|
4229
4270
|
class AttrQuotes extends Rule {
|
|
4230
4271
|
constructor(options) {
|
|
4231
|
-
super({ ...defaults$
|
|
4272
|
+
super({ ...defaults$p, ...options });
|
|
4232
4273
|
this.style = parseStyle$4(this.options.style);
|
|
4233
4274
|
}
|
|
4234
4275
|
static schema() {
|
|
4235
4276
|
return {
|
|
4236
4277
|
style: {
|
|
4237
|
-
enum: ["auto", "double", "single"],
|
|
4278
|
+
enum: ["auto", "double", "single", "any"],
|
|
4238
4279
|
type: "string",
|
|
4239
4280
|
},
|
|
4240
4281
|
unquoted: {
|
|
@@ -4242,19 +4283,20 @@ class AttrQuotes extends Rule {
|
|
|
4242
4283
|
},
|
|
4243
4284
|
};
|
|
4244
4285
|
}
|
|
4245
|
-
documentation() {
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4286
|
+
documentation(context) {
|
|
4287
|
+
const { style } = this;
|
|
4288
|
+
const { unquoted } = this.options;
|
|
4289
|
+
const description = [
|
|
4290
|
+
describeError(context),
|
|
4291
|
+
"",
|
|
4292
|
+
"Under the current configuration attributes must be:",
|
|
4293
|
+
"",
|
|
4294
|
+
describeStyle(style, unquoted),
|
|
4295
|
+
];
|
|
4296
|
+
return {
|
|
4297
|
+
description: description.join("\n"),
|
|
4298
|
+
url: ruleDocumentationUrl("@/rules/attr-quotes.ts"),
|
|
4299
|
+
};
|
|
4258
4300
|
}
|
|
4259
4301
|
setup() {
|
|
4260
4302
|
this.on("attr", (event) => {
|
|
@@ -4264,13 +4306,31 @@ class AttrQuotes extends Rule {
|
|
|
4264
4306
|
}
|
|
4265
4307
|
if (!event.quote) {
|
|
4266
4308
|
if (this.options.unquoted === false) {
|
|
4267
|
-
|
|
4309
|
+
const message = `Attribute "${event.key}" using unquoted value`;
|
|
4310
|
+
const context = {
|
|
4311
|
+
error: "unquoted",
|
|
4312
|
+
attr: event.key,
|
|
4313
|
+
};
|
|
4314
|
+
this.report(event.target, message, null, context);
|
|
4268
4315
|
}
|
|
4269
4316
|
return;
|
|
4270
4317
|
}
|
|
4318
|
+
/* if the style is set to any we skip the rest of the rule as the only
|
|
4319
|
+
* thing that matters is if the "unquoted" options triggers an error or
|
|
4320
|
+
* not */
|
|
4321
|
+
if (this.style === QuoteStyle.ANY_QUOTE) {
|
|
4322
|
+
return;
|
|
4323
|
+
}
|
|
4271
4324
|
const expected = this.resolveQuotemark(event.value.toString(), this.style);
|
|
4272
4325
|
if (event.quote !== expected) {
|
|
4273
|
-
|
|
4326
|
+
const message = `Attribute "${event.key}" used ${event.quote} instead of expected ${expected}`;
|
|
4327
|
+
const context = {
|
|
4328
|
+
error: "style",
|
|
4329
|
+
attr: event.key,
|
|
4330
|
+
actual: event.quote,
|
|
4331
|
+
expected,
|
|
4332
|
+
};
|
|
4333
|
+
this.report(event.target, message, null, context);
|
|
4274
4334
|
}
|
|
4275
4335
|
});
|
|
4276
4336
|
}
|
|
@@ -4291,6 +4351,8 @@ function parseStyle$4(style) {
|
|
|
4291
4351
|
return QuoteStyle.DOUBLE_QUOTE;
|
|
4292
4352
|
case "single":
|
|
4293
4353
|
return QuoteStyle.SINGLE_QUOTE;
|
|
4354
|
+
case "any":
|
|
4355
|
+
return QuoteStyle.ANY_QUOTE;
|
|
4294
4356
|
/* istanbul ignore next: covered by schema validation */
|
|
4295
4357
|
default:
|
|
4296
4358
|
throw new ConfigError(`Invalid style "${style}" for "attr-quotes" rule`);
|
|
@@ -4393,12 +4455,12 @@ class AttributeAllowedValues extends Rule {
|
|
|
4393
4455
|
}
|
|
4394
4456
|
}
|
|
4395
4457
|
|
|
4396
|
-
const defaults$
|
|
4458
|
+
const defaults$o = {
|
|
4397
4459
|
style: "omit",
|
|
4398
4460
|
};
|
|
4399
4461
|
class AttributeBooleanStyle extends Rule {
|
|
4400
4462
|
constructor(options) {
|
|
4401
|
-
super({ ...defaults$
|
|
4463
|
+
super({ ...defaults$o, ...options });
|
|
4402
4464
|
this.hasInvalidStyle = parseStyle$3(this.options.style);
|
|
4403
4465
|
}
|
|
4404
4466
|
static schema() {
|
|
@@ -4474,12 +4536,12 @@ function reportMessage$1(attr, style) {
|
|
|
4474
4536
|
return "";
|
|
4475
4537
|
}
|
|
4476
4538
|
|
|
4477
|
-
const defaults$
|
|
4539
|
+
const defaults$n = {
|
|
4478
4540
|
style: "omit",
|
|
4479
4541
|
};
|
|
4480
4542
|
class AttributeEmptyStyle extends Rule {
|
|
4481
4543
|
constructor(options) {
|
|
4482
|
-
super({ ...defaults$
|
|
4544
|
+
super({ ...defaults$n, ...options });
|
|
4483
4545
|
this.hasInvalidStyle = parseStyle$2(this.options.style);
|
|
4484
4546
|
}
|
|
4485
4547
|
static schema() {
|
|
@@ -4587,12 +4649,12 @@ function describePattern(pattern) {
|
|
|
4587
4649
|
}
|
|
4588
4650
|
}
|
|
4589
4651
|
|
|
4590
|
-
const defaults$
|
|
4652
|
+
const defaults$m = {
|
|
4591
4653
|
pattern: "kebabcase",
|
|
4592
4654
|
};
|
|
4593
4655
|
class ClassPattern extends Rule {
|
|
4594
4656
|
constructor(options) {
|
|
4595
|
-
super({ ...defaults$
|
|
4657
|
+
super({ ...defaults$m, ...options });
|
|
4596
4658
|
this.pattern = parsePattern(this.options.pattern);
|
|
4597
4659
|
}
|
|
4598
4660
|
static schema() {
|
|
@@ -4701,13 +4763,13 @@ class CloseOrder extends Rule {
|
|
|
4701
4763
|
}
|
|
4702
4764
|
}
|
|
4703
4765
|
|
|
4704
|
-
const defaults$
|
|
4766
|
+
const defaults$l = {
|
|
4705
4767
|
include: null,
|
|
4706
4768
|
exclude: null,
|
|
4707
4769
|
};
|
|
4708
4770
|
class Deprecated extends Rule {
|
|
4709
4771
|
constructor(options) {
|
|
4710
|
-
super({ ...defaults$
|
|
4772
|
+
super({ ...defaults$l, ...options });
|
|
4711
4773
|
}
|
|
4712
4774
|
static schema() {
|
|
4713
4775
|
return {
|
|
@@ -4870,12 +4932,12 @@ class NoStyleTag$1 extends Rule {
|
|
|
4870
4932
|
}
|
|
4871
4933
|
}
|
|
4872
4934
|
|
|
4873
|
-
const defaults$
|
|
4935
|
+
const defaults$k = {
|
|
4874
4936
|
style: "uppercase",
|
|
4875
4937
|
};
|
|
4876
4938
|
class DoctypeStyle extends Rule {
|
|
4877
4939
|
constructor(options) {
|
|
4878
|
-
super({ ...defaults$
|
|
4940
|
+
super({ ...defaults$k, ...options });
|
|
4879
4941
|
}
|
|
4880
4942
|
static schema() {
|
|
4881
4943
|
return {
|
|
@@ -4907,12 +4969,12 @@ class DoctypeStyle extends Rule {
|
|
|
4907
4969
|
}
|
|
4908
4970
|
}
|
|
4909
4971
|
|
|
4910
|
-
const defaults$
|
|
4972
|
+
const defaults$j = {
|
|
4911
4973
|
style: "lowercase",
|
|
4912
4974
|
};
|
|
4913
4975
|
class ElementCase extends Rule {
|
|
4914
4976
|
constructor(options) {
|
|
4915
|
-
super({ ...defaults$
|
|
4977
|
+
super({ ...defaults$j, ...options });
|
|
4916
4978
|
this.style = new CaseStyle(this.options.style, "element-case");
|
|
4917
4979
|
}
|
|
4918
4980
|
static schema() {
|
|
@@ -4978,14 +5040,14 @@ class ElementCase extends Rule {
|
|
|
4978
5040
|
}
|
|
4979
5041
|
}
|
|
4980
5042
|
|
|
4981
|
-
const defaults$
|
|
5043
|
+
const defaults$i = {
|
|
4982
5044
|
pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
|
|
4983
5045
|
whitelist: [],
|
|
4984
5046
|
blacklist: [],
|
|
4985
5047
|
};
|
|
4986
5048
|
class ElementName extends Rule {
|
|
4987
5049
|
constructor(options) {
|
|
4988
|
-
super({ ...defaults$
|
|
5050
|
+
super({ ...defaults$i, ...options });
|
|
4989
5051
|
// eslint-disable-next-line security/detect-non-literal-regexp
|
|
4990
5052
|
this.pattern = new RegExp(this.options.pattern);
|
|
4991
5053
|
}
|
|
@@ -5026,7 +5088,7 @@ class ElementName extends Rule {
|
|
|
5026
5088
|
...context.blacklist.map((cur) => `- ${cur}`),
|
|
5027
5089
|
];
|
|
5028
5090
|
}
|
|
5029
|
-
if (context.pattern !== defaults$
|
|
5091
|
+
if (context.pattern !== defaults$i.pattern) {
|
|
5030
5092
|
return [
|
|
5031
5093
|
`<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
|
|
5032
5094
|
"",
|
|
@@ -5428,7 +5490,7 @@ class EmptyTitle extends Rule {
|
|
|
5428
5490
|
}
|
|
5429
5491
|
}
|
|
5430
5492
|
|
|
5431
|
-
const defaults$
|
|
5493
|
+
const defaults$h = {
|
|
5432
5494
|
allowMultipleH1: false,
|
|
5433
5495
|
minInitialRank: "h1",
|
|
5434
5496
|
sectioningRoots: ["dialog", '[role="dialog"]'],
|
|
@@ -5459,7 +5521,7 @@ function parseMaxInitial(value) {
|
|
|
5459
5521
|
}
|
|
5460
5522
|
class HeadingLevel extends Rule {
|
|
5461
5523
|
constructor(options) {
|
|
5462
|
-
super({ ...defaults$
|
|
5524
|
+
super({ ...defaults$h, ...options });
|
|
5463
5525
|
this.stack = [];
|
|
5464
5526
|
this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
|
|
5465
5527
|
this.sectionRoots = this.options.sectioningRoots.map((it) => new Pattern(it));
|
|
@@ -5617,12 +5679,12 @@ class HeadingLevel extends Rule {
|
|
|
5617
5679
|
}
|
|
5618
5680
|
}
|
|
5619
5681
|
|
|
5620
|
-
const defaults$
|
|
5682
|
+
const defaults$g = {
|
|
5621
5683
|
pattern: "kebabcase",
|
|
5622
5684
|
};
|
|
5623
5685
|
class IdPattern extends Rule {
|
|
5624
5686
|
constructor(options) {
|
|
5625
|
-
super({ ...defaults$
|
|
5687
|
+
super({ ...defaults$g, ...options });
|
|
5626
5688
|
this.pattern = parsePattern(this.options.pattern);
|
|
5627
5689
|
}
|
|
5628
5690
|
static schema() {
|
|
@@ -5973,12 +6035,12 @@ function findLabelByParent(el) {
|
|
|
5973
6035
|
return [];
|
|
5974
6036
|
}
|
|
5975
6037
|
|
|
5976
|
-
const defaults$
|
|
6038
|
+
const defaults$f = {
|
|
5977
6039
|
maxlength: 70,
|
|
5978
6040
|
};
|
|
5979
6041
|
class LongTitle extends Rule {
|
|
5980
6042
|
constructor(options) {
|
|
5981
|
-
super({ ...defaults$
|
|
6043
|
+
super({ ...defaults$f, ...options });
|
|
5982
6044
|
this.maxlength = this.options.maxlength;
|
|
5983
6045
|
}
|
|
5984
6046
|
static schema() {
|
|
@@ -6125,13 +6187,13 @@ class MultipleLabeledControls extends Rule {
|
|
|
6125
6187
|
}
|
|
6126
6188
|
}
|
|
6127
6189
|
|
|
6128
|
-
const defaults$
|
|
6190
|
+
const defaults$e = {
|
|
6129
6191
|
include: null,
|
|
6130
6192
|
exclude: null,
|
|
6131
6193
|
};
|
|
6132
6194
|
class NoAutoplay extends Rule {
|
|
6133
6195
|
constructor(options) {
|
|
6134
|
-
super({ ...defaults$
|
|
6196
|
+
super({ ...defaults$e, ...options });
|
|
6135
6197
|
}
|
|
6136
6198
|
documentation(context) {
|
|
6137
6199
|
const tagName = context ? ` on <${context.tagName}>` : "";
|
|
@@ -6372,14 +6434,14 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
|
|
|
6372
6434
|
}
|
|
6373
6435
|
}
|
|
6374
6436
|
|
|
6375
|
-
const defaults$
|
|
6437
|
+
const defaults$d = {
|
|
6376
6438
|
include: null,
|
|
6377
6439
|
exclude: null,
|
|
6378
6440
|
allowedProperties: ["display"],
|
|
6379
6441
|
};
|
|
6380
6442
|
class NoInlineStyle extends Rule {
|
|
6381
6443
|
constructor(options) {
|
|
6382
|
-
super({ ...defaults$
|
|
6444
|
+
super({ ...defaults$d, ...options });
|
|
6383
6445
|
}
|
|
6384
6446
|
static schema() {
|
|
6385
6447
|
return {
|
|
@@ -6581,7 +6643,7 @@ class NoMultipleMain extends Rule {
|
|
|
6581
6643
|
}
|
|
6582
6644
|
}
|
|
6583
6645
|
|
|
6584
|
-
const defaults$
|
|
6646
|
+
const defaults$c = {
|
|
6585
6647
|
relaxed: false,
|
|
6586
6648
|
};
|
|
6587
6649
|
const textRegexp = /([<>]|&(?![a-zA-Z0-9#]+;))/g;
|
|
@@ -6598,7 +6660,7 @@ const replacementTable = {
|
|
|
6598
6660
|
};
|
|
6599
6661
|
class NoRawCharacters extends Rule {
|
|
6600
6662
|
constructor(options) {
|
|
6601
|
-
super({ ...defaults$
|
|
6663
|
+
super({ ...defaults$c, ...options });
|
|
6602
6664
|
this.relaxed = this.options.relaxed;
|
|
6603
6665
|
}
|
|
6604
6666
|
static schema() {
|
|
@@ -6776,13 +6838,13 @@ class NoRedundantRole extends Rule {
|
|
|
6776
6838
|
}
|
|
6777
6839
|
|
|
6778
6840
|
const xmlns = /^(.+):.+$/;
|
|
6779
|
-
const defaults$
|
|
6841
|
+
const defaults$b = {
|
|
6780
6842
|
ignoreForeign: true,
|
|
6781
6843
|
ignoreXML: true,
|
|
6782
6844
|
};
|
|
6783
6845
|
class NoSelfClosing extends Rule {
|
|
6784
6846
|
constructor(options) {
|
|
6785
|
-
super({ ...defaults$
|
|
6847
|
+
super({ ...defaults$b, ...options });
|
|
6786
6848
|
}
|
|
6787
6849
|
static schema() {
|
|
6788
6850
|
return {
|
|
@@ -6915,13 +6977,13 @@ const replacement = {
|
|
|
6915
6977
|
reset: '<button type="reset">',
|
|
6916
6978
|
image: '<button type="button">',
|
|
6917
6979
|
};
|
|
6918
|
-
const defaults$
|
|
6980
|
+
const defaults$a = {
|
|
6919
6981
|
include: null,
|
|
6920
6982
|
exclude: null,
|
|
6921
6983
|
};
|
|
6922
6984
|
class PreferButton extends Rule {
|
|
6923
6985
|
constructor(options) {
|
|
6924
|
-
super({ ...defaults$
|
|
6986
|
+
super({ ...defaults$a, ...options });
|
|
6925
6987
|
}
|
|
6926
6988
|
static schema() {
|
|
6927
6989
|
return {
|
|
@@ -6996,7 +7058,7 @@ class PreferButton extends Rule {
|
|
|
6996
7058
|
}
|
|
6997
7059
|
}
|
|
6998
7060
|
|
|
6999
|
-
const defaults$
|
|
7061
|
+
const defaults$9 = {
|
|
7000
7062
|
mapping: {
|
|
7001
7063
|
article: "article",
|
|
7002
7064
|
banner: "header",
|
|
@@ -7026,7 +7088,7 @@ const defaults$8 = {
|
|
|
7026
7088
|
};
|
|
7027
7089
|
class PreferNativeElement extends Rule {
|
|
7028
7090
|
constructor(options) {
|
|
7029
|
-
super({ ...defaults$
|
|
7091
|
+
super({ ...defaults$9, ...options });
|
|
7030
7092
|
}
|
|
7031
7093
|
static schema() {
|
|
7032
7094
|
return {
|
|
@@ -7146,8 +7208,66 @@ class PreferTbody extends Rule {
|
|
|
7146
7208
|
}
|
|
7147
7209
|
}
|
|
7148
7210
|
|
|
7211
|
+
const defaults$8 = {
|
|
7212
|
+
tags: ["script", "style"],
|
|
7213
|
+
};
|
|
7214
|
+
class RequireCSPNonce extends Rule {
|
|
7215
|
+
constructor(options) {
|
|
7216
|
+
super({ ...defaults$8, ...options });
|
|
7217
|
+
}
|
|
7218
|
+
static schema() {
|
|
7219
|
+
return {
|
|
7220
|
+
tags: {
|
|
7221
|
+
type: "array",
|
|
7222
|
+
items: {
|
|
7223
|
+
enum: ["script", "style"],
|
|
7224
|
+
type: "string",
|
|
7225
|
+
},
|
|
7226
|
+
},
|
|
7227
|
+
};
|
|
7228
|
+
}
|
|
7229
|
+
documentation() {
|
|
7230
|
+
return {
|
|
7231
|
+
description: [
|
|
7232
|
+
"Required Content-Security-Policy (CSP) nonce is missing or empty.",
|
|
7233
|
+
"",
|
|
7234
|
+
"This is set by the `nonce` attribute and must match the `Content-Security-Policy` header.",
|
|
7235
|
+
"For instance, if the header contains `script-src 'nonce-r4nd0m'` the `nonce` attribute must be set to `nonce=\"r4nd0m\">`",
|
|
7236
|
+
"",
|
|
7237
|
+
"The nonce should be unique per each request and set to a cryptography secure random token.",
|
|
7238
|
+
"It is used to prevent cross site scripting (XSS) by preventing malicious actors from injecting scripts onto the page.",
|
|
7239
|
+
].join("\n"),
|
|
7240
|
+
url: ruleDocumentationUrl("@/rules/require-csp-nonce.ts"),
|
|
7241
|
+
};
|
|
7242
|
+
}
|
|
7243
|
+
setup() {
|
|
7244
|
+
this.on("tag:end", (event) => {
|
|
7245
|
+
var _a;
|
|
7246
|
+
const { tags } = this.options;
|
|
7247
|
+
const node = event.previous;
|
|
7248
|
+
/* ignore other tags */
|
|
7249
|
+
if (!node || !tags.includes(node.tagName)) {
|
|
7250
|
+
return;
|
|
7251
|
+
}
|
|
7252
|
+
/* ignore if nonce is set to non-empty value (or dynamic) */
|
|
7253
|
+
const nonce = (_a = node.getAttribute("nonce")) === null || _a === void 0 ? void 0 : _a.value;
|
|
7254
|
+
if (nonce && nonce !== "") {
|
|
7255
|
+
return;
|
|
7256
|
+
}
|
|
7257
|
+
/* ignore <script src> */
|
|
7258
|
+
if (node.is("script") && node.hasAttribute("src")) {
|
|
7259
|
+
return;
|
|
7260
|
+
}
|
|
7261
|
+
const message = `required CSP nonce is missing`;
|
|
7262
|
+
this.report(node, message, node.location);
|
|
7263
|
+
});
|
|
7264
|
+
}
|
|
7265
|
+
}
|
|
7266
|
+
|
|
7149
7267
|
const defaults$7 = {
|
|
7150
7268
|
target: "all",
|
|
7269
|
+
include: null,
|
|
7270
|
+
exclude: null,
|
|
7151
7271
|
};
|
|
7152
7272
|
const crossorigin = new RegExp("^(\\w+://|//)"); /* e.g. https:// or // */
|
|
7153
7273
|
const supportSri = {
|
|
@@ -7165,6 +7285,32 @@ class RequireSri extends Rule {
|
|
|
7165
7285
|
enum: ["all", "crossorigin"],
|
|
7166
7286
|
type: "string",
|
|
7167
7287
|
},
|
|
7288
|
+
include: {
|
|
7289
|
+
anyOf: [
|
|
7290
|
+
{
|
|
7291
|
+
items: {
|
|
7292
|
+
type: "string",
|
|
7293
|
+
},
|
|
7294
|
+
type: "array",
|
|
7295
|
+
},
|
|
7296
|
+
{
|
|
7297
|
+
type: "null",
|
|
7298
|
+
},
|
|
7299
|
+
],
|
|
7300
|
+
},
|
|
7301
|
+
exclude: {
|
|
7302
|
+
anyOf: [
|
|
7303
|
+
{
|
|
7304
|
+
items: {
|
|
7305
|
+
type: "string",
|
|
7306
|
+
},
|
|
7307
|
+
type: "array",
|
|
7308
|
+
},
|
|
7309
|
+
{
|
|
7310
|
+
type: "null",
|
|
7311
|
+
},
|
|
7312
|
+
],
|
|
7313
|
+
},
|
|
7168
7314
|
};
|
|
7169
7315
|
}
|
|
7170
7316
|
documentation() {
|
|
@@ -7177,11 +7323,13 @@ class RequireSri extends Rule {
|
|
|
7177
7323
|
this.on("tag:end", (event) => {
|
|
7178
7324
|
/* only handle thats supporting and requires sri */
|
|
7179
7325
|
const node = event.previous;
|
|
7180
|
-
if (!(this.supportSri(node) && this.needSri(node)))
|
|
7326
|
+
if (!(this.supportSri(node) && this.needSri(node))) {
|
|
7181
7327
|
return;
|
|
7328
|
+
}
|
|
7182
7329
|
/* check if sri attribute is present */
|
|
7183
|
-
if (node.hasAttribute("integrity"))
|
|
7330
|
+
if (node.hasAttribute("integrity")) {
|
|
7184
7331
|
return;
|
|
7332
|
+
}
|
|
7185
7333
|
this.report(node, `SRI "integrity" attribute is required on <${node.tagName}> element`, node.location);
|
|
7186
7334
|
});
|
|
7187
7335
|
}
|
|
@@ -7189,19 +7337,25 @@ class RequireSri extends Rule {
|
|
|
7189
7337
|
return Object.keys(supportSri).includes(node.tagName);
|
|
7190
7338
|
}
|
|
7191
7339
|
needSri(node) {
|
|
7192
|
-
if (this.target === "all")
|
|
7193
|
-
return true;
|
|
7194
7340
|
const attr = this.elementSourceAttr(node);
|
|
7195
|
-
if (!attr || attr.value === null || attr.isDynamic) {
|
|
7341
|
+
if (!attr || attr.value === null || attr.value === "" || attr.isDynamic) {
|
|
7196
7342
|
return false;
|
|
7197
7343
|
}
|
|
7198
7344
|
const url = attr.value.toString();
|
|
7199
|
-
|
|
7345
|
+
if (this.target === "all" || crossorigin.test(url)) {
|
|
7346
|
+
return !this.isIgnored(url);
|
|
7347
|
+
}
|
|
7348
|
+
return false;
|
|
7200
7349
|
}
|
|
7201
7350
|
elementSourceAttr(node) {
|
|
7202
7351
|
const key = supportSri[node.tagName];
|
|
7203
7352
|
return node.getAttribute(key);
|
|
7204
7353
|
}
|
|
7354
|
+
isIgnored(url) {
|
|
7355
|
+
return this.isKeywordIgnored(url, (list, it) => {
|
|
7356
|
+
return list.some((pattern) => it.includes(pattern));
|
|
7357
|
+
});
|
|
7358
|
+
}
|
|
7205
7359
|
}
|
|
7206
7360
|
|
|
7207
7361
|
class ScriptElement extends Rule {
|
|
@@ -9999,6 +10153,7 @@ const bundledRules = {
|
|
|
9999
10153
|
"prefer-button": PreferButton,
|
|
10000
10154
|
"prefer-native-element": PreferNativeElement,
|
|
10001
10155
|
"prefer-tbody": PreferTbody,
|
|
10156
|
+
"require-csp-nonce": RequireCSPNonce,
|
|
10002
10157
|
"require-sri": RequireSri,
|
|
10003
10158
|
"script-element": ScriptElement,
|
|
10004
10159
|
"script-type": ScriptType,
|