html-validate 7.0.0 → 7.1.0
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/dist/cjs/core.d.ts +2 -1
- package/dist/cjs/core.js +219 -67
- package/dist/cjs/core.js.map +1 -1
- package/dist/es/core.d.ts +2 -1
- package/dist/es/core.js +219 -67
- package/dist/es/core.js.map +1 -1
- package/package.json +18 -18
package/dist/cjs/core.d.ts
CHANGED
|
@@ -333,12 +333,13 @@ declare abstract class Rule<ContextType = void, OptionsType = void> {
|
|
|
333
333
|
* not `"foo"`.
|
|
334
334
|
*
|
|
335
335
|
* @param keyword - Keyword to match against `include` and `exclude` options.
|
|
336
|
+
* @param matcher - Optional function to compare items with.
|
|
336
337
|
* @returns `true` if keyword is not present in `include` or is present in
|
|
337
338
|
* `exclude`.
|
|
338
339
|
*/
|
|
339
340
|
isKeywordIgnored<T extends IncludeExcludeOptions>(this: {
|
|
340
341
|
options: T;
|
|
341
|
-
}, keyword: string): boolean;
|
|
342
|
+
}, keyword: string, matcher?: (list: string[], it: string) => boolean): boolean;
|
|
342
343
|
/**
|
|
343
344
|
* Find all tags which has enabled given property.
|
|
344
345
|
*/
|
package/dist/cjs/core.js
CHANGED
|
@@ -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
|
}
|
|
@@ -3113,7 +3114,7 @@ var TRANSFORMER_API;
|
|
|
3113
3114
|
/** @public */
|
|
3114
3115
|
const name = "html-validate";
|
|
3115
3116
|
/** @public */
|
|
3116
|
-
const version = "7.
|
|
3117
|
+
const version = "7.1.0";
|
|
3117
3118
|
/** @public */
|
|
3118
3119
|
const homepage = "https://html-validate.org";
|
|
3119
3120
|
/** @public */
|
|
@@ -3276,17 +3277,18 @@ class Rule {
|
|
|
3276
3277
|
* not `"foo"`.
|
|
3277
3278
|
*
|
|
3278
3279
|
* @param keyword - Keyword to match against `include` and `exclude` options.
|
|
3280
|
+
* @param matcher - Optional function to compare items with.
|
|
3279
3281
|
* @returns `true` if keyword is not present in `include` or is present in
|
|
3280
3282
|
* `exclude`.
|
|
3281
3283
|
*/
|
|
3282
|
-
isKeywordIgnored(keyword) {
|
|
3284
|
+
isKeywordIgnored(keyword, matcher = (list, it) => list.includes(it)) {
|
|
3283
3285
|
const { include, exclude } = this.options;
|
|
3284
3286
|
/* ignore keyword if not present in "include" */
|
|
3285
|
-
if (include && !include
|
|
3287
|
+
if (include && !matcher(include, keyword)) {
|
|
3286
3288
|
return true;
|
|
3287
3289
|
}
|
|
3288
3290
|
/* ignore keyword if present in "excludes" */
|
|
3289
|
-
if (exclude && exclude
|
|
3291
|
+
if (exclude && matcher(exclude, keyword)) {
|
|
3290
3292
|
return true;
|
|
3291
3293
|
}
|
|
3292
3294
|
return false;
|
|
@@ -3434,7 +3436,7 @@ function ruleDocumentationUrl(filename) {
|
|
|
3434
3436
|
return `${homepage}/rules/${normalized}.html`;
|
|
3435
3437
|
}
|
|
3436
3438
|
|
|
3437
|
-
const defaults$
|
|
3439
|
+
const defaults$s = {
|
|
3438
3440
|
allowExternal: true,
|
|
3439
3441
|
allowRelative: true,
|
|
3440
3442
|
allowAbsolute: true,
|
|
@@ -3478,7 +3480,7 @@ function matchList(value, list) {
|
|
|
3478
3480
|
}
|
|
3479
3481
|
class AllowedLinks extends Rule {
|
|
3480
3482
|
constructor(options) {
|
|
3481
|
-
super({ ...defaults$
|
|
3483
|
+
super({ ...defaults$s, ...options });
|
|
3482
3484
|
this.allowExternal = parseAllow(this.options.allowExternal);
|
|
3483
3485
|
this.allowRelative = parseAllow(this.options.allowRelative);
|
|
3484
3486
|
this.allowAbsolute = parseAllow(this.options.allowAbsolute);
|
|
@@ -3779,13 +3781,13 @@ class CaseStyle {
|
|
|
3779
3781
|
}
|
|
3780
3782
|
}
|
|
3781
3783
|
|
|
3782
|
-
const defaults$
|
|
3784
|
+
const defaults$r = {
|
|
3783
3785
|
style: "lowercase",
|
|
3784
3786
|
ignoreForeign: true,
|
|
3785
3787
|
};
|
|
3786
3788
|
class AttrCase extends Rule {
|
|
3787
3789
|
constructor(options) {
|
|
3788
|
-
super({ ...defaults$
|
|
3790
|
+
super({ ...defaults$r, ...options });
|
|
3789
3791
|
this.style = new CaseStyle(this.options.style, "attr-case");
|
|
3790
3792
|
}
|
|
3791
3793
|
static schema() {
|
|
@@ -4125,7 +4127,7 @@ class AttrDelimiter extends Rule {
|
|
|
4125
4127
|
}
|
|
4126
4128
|
|
|
4127
4129
|
const DEFAULT_PATTERN = "[a-z0-9-:]+";
|
|
4128
|
-
const defaults$
|
|
4130
|
+
const defaults$q = {
|
|
4129
4131
|
pattern: DEFAULT_PATTERN,
|
|
4130
4132
|
ignoreForeign: true,
|
|
4131
4133
|
};
|
|
@@ -4162,7 +4164,7 @@ function generateDescription(name, pattern) {
|
|
|
4162
4164
|
}
|
|
4163
4165
|
class AttrPattern extends Rule {
|
|
4164
4166
|
constructor(options) {
|
|
4165
|
-
super({ ...defaults$
|
|
4167
|
+
super({ ...defaults$q, ...options });
|
|
4166
4168
|
this.pattern = generateRegexp(this.options.pattern);
|
|
4167
4169
|
}
|
|
4168
4170
|
static schema() {
|
|
@@ -4221,20 +4223,56 @@ var QuoteStyle;
|
|
|
4221
4223
|
QuoteStyle["SINGLE_QUOTE"] = "'";
|
|
4222
4224
|
QuoteStyle["DOUBLE_QUOTE"] = "\"";
|
|
4223
4225
|
QuoteStyle["AUTO_QUOTE"] = "auto";
|
|
4226
|
+
QuoteStyle["ANY_QUOTE"] = "any";
|
|
4224
4227
|
})(QuoteStyle || (QuoteStyle = {}));
|
|
4225
|
-
const defaults$
|
|
4228
|
+
const defaults$p = {
|
|
4226
4229
|
style: "auto",
|
|
4227
4230
|
unquoted: false,
|
|
4228
4231
|
};
|
|
4232
|
+
function describeError(context) {
|
|
4233
|
+
if (context) {
|
|
4234
|
+
switch (context.error) {
|
|
4235
|
+
case "style":
|
|
4236
|
+
return `Attribute \`${context.attr}\` must use \`${context.expected}\` instead of \`${context.actual}\`.`;
|
|
4237
|
+
case "unquoted":
|
|
4238
|
+
return `Attribute \`${context.attr}\` must not be unquoted.`;
|
|
4239
|
+
}
|
|
4240
|
+
}
|
|
4241
|
+
else {
|
|
4242
|
+
return "This attribute is not quoted properly.";
|
|
4243
|
+
}
|
|
4244
|
+
}
|
|
4245
|
+
function describeStyle(style, unquoted) {
|
|
4246
|
+
const description = [];
|
|
4247
|
+
switch (style) {
|
|
4248
|
+
case QuoteStyle.AUTO_QUOTE:
|
|
4249
|
+
description.push("- quoted with double quotes `\"` unless the value contains double quotes in which case single quotes `'` should be used instead");
|
|
4250
|
+
break;
|
|
4251
|
+
case QuoteStyle.ANY_QUOTE:
|
|
4252
|
+
description.push("- quoted with single quotes `'`");
|
|
4253
|
+
description.push('- quoted with double quotes `"`');
|
|
4254
|
+
break;
|
|
4255
|
+
case QuoteStyle.SINGLE_QUOTE:
|
|
4256
|
+
case QuoteStyle.DOUBLE_QUOTE: {
|
|
4257
|
+
const name = style === QuoteStyle.SINGLE_QUOTE ? "single" : "double";
|
|
4258
|
+
description.push(`- quoted with ${name} quotes \`${style}\``);
|
|
4259
|
+
break;
|
|
4260
|
+
}
|
|
4261
|
+
}
|
|
4262
|
+
if (unquoted) {
|
|
4263
|
+
description.push("- unquoted (if applicable)");
|
|
4264
|
+
}
|
|
4265
|
+
return `${description.join(" or\n")}\n`;
|
|
4266
|
+
}
|
|
4229
4267
|
class AttrQuotes extends Rule {
|
|
4230
4268
|
constructor(options) {
|
|
4231
|
-
super({ ...defaults$
|
|
4269
|
+
super({ ...defaults$p, ...options });
|
|
4232
4270
|
this.style = parseStyle$4(this.options.style);
|
|
4233
4271
|
}
|
|
4234
4272
|
static schema() {
|
|
4235
4273
|
return {
|
|
4236
4274
|
style: {
|
|
4237
|
-
enum: ["auto", "double", "single"],
|
|
4275
|
+
enum: ["auto", "double", "single", "any"],
|
|
4238
4276
|
type: "string",
|
|
4239
4277
|
},
|
|
4240
4278
|
unquoted: {
|
|
@@ -4242,19 +4280,20 @@ class AttrQuotes extends Rule {
|
|
|
4242
4280
|
},
|
|
4243
4281
|
};
|
|
4244
4282
|
}
|
|
4245
|
-
documentation() {
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4283
|
+
documentation(context) {
|
|
4284
|
+
const { style } = this;
|
|
4285
|
+
const { unquoted } = this.options;
|
|
4286
|
+
const description = [
|
|
4287
|
+
describeError(context),
|
|
4288
|
+
"",
|
|
4289
|
+
"Under the current configuration attributes must be:",
|
|
4290
|
+
"",
|
|
4291
|
+
describeStyle(style, unquoted),
|
|
4292
|
+
];
|
|
4293
|
+
return {
|
|
4294
|
+
description: description.join("\n"),
|
|
4295
|
+
url: ruleDocumentationUrl("@/rules/attr-quotes.ts"),
|
|
4296
|
+
};
|
|
4258
4297
|
}
|
|
4259
4298
|
setup() {
|
|
4260
4299
|
this.on("attr", (event) => {
|
|
@@ -4264,13 +4303,31 @@ class AttrQuotes extends Rule {
|
|
|
4264
4303
|
}
|
|
4265
4304
|
if (!event.quote) {
|
|
4266
4305
|
if (this.options.unquoted === false) {
|
|
4267
|
-
|
|
4306
|
+
const message = `Attribute "${event.key}" using unquoted value`;
|
|
4307
|
+
const context = {
|
|
4308
|
+
error: "unquoted",
|
|
4309
|
+
attr: event.key,
|
|
4310
|
+
};
|
|
4311
|
+
this.report(event.target, message, null, context);
|
|
4268
4312
|
}
|
|
4269
4313
|
return;
|
|
4270
4314
|
}
|
|
4315
|
+
/* if the style is set to any we skip the rest of the rule as the only
|
|
4316
|
+
* thing that matters is if the "unquoted" options triggers an error or
|
|
4317
|
+
* not */
|
|
4318
|
+
if (this.style === QuoteStyle.ANY_QUOTE) {
|
|
4319
|
+
return;
|
|
4320
|
+
}
|
|
4271
4321
|
const expected = this.resolveQuotemark(event.value.toString(), this.style);
|
|
4272
4322
|
if (event.quote !== expected) {
|
|
4273
|
-
|
|
4323
|
+
const message = `Attribute "${event.key}" used ${event.quote} instead of expected ${expected}`;
|
|
4324
|
+
const context = {
|
|
4325
|
+
error: "style",
|
|
4326
|
+
attr: event.key,
|
|
4327
|
+
actual: event.quote,
|
|
4328
|
+
expected,
|
|
4329
|
+
};
|
|
4330
|
+
this.report(event.target, message, null, context);
|
|
4274
4331
|
}
|
|
4275
4332
|
});
|
|
4276
4333
|
}
|
|
@@ -4291,6 +4348,8 @@ function parseStyle$4(style) {
|
|
|
4291
4348
|
return QuoteStyle.DOUBLE_QUOTE;
|
|
4292
4349
|
case "single":
|
|
4293
4350
|
return QuoteStyle.SINGLE_QUOTE;
|
|
4351
|
+
case "any":
|
|
4352
|
+
return QuoteStyle.ANY_QUOTE;
|
|
4294
4353
|
/* istanbul ignore next: covered by schema validation */
|
|
4295
4354
|
default:
|
|
4296
4355
|
throw new ConfigError(`Invalid style "${style}" for "attr-quotes" rule`);
|
|
@@ -4393,12 +4452,12 @@ class AttributeAllowedValues extends Rule {
|
|
|
4393
4452
|
}
|
|
4394
4453
|
}
|
|
4395
4454
|
|
|
4396
|
-
const defaults$
|
|
4455
|
+
const defaults$o = {
|
|
4397
4456
|
style: "omit",
|
|
4398
4457
|
};
|
|
4399
4458
|
class AttributeBooleanStyle extends Rule {
|
|
4400
4459
|
constructor(options) {
|
|
4401
|
-
super({ ...defaults$
|
|
4460
|
+
super({ ...defaults$o, ...options });
|
|
4402
4461
|
this.hasInvalidStyle = parseStyle$3(this.options.style);
|
|
4403
4462
|
}
|
|
4404
4463
|
static schema() {
|
|
@@ -4474,12 +4533,12 @@ function reportMessage$1(attr, style) {
|
|
|
4474
4533
|
return "";
|
|
4475
4534
|
}
|
|
4476
4535
|
|
|
4477
|
-
const defaults$
|
|
4536
|
+
const defaults$n = {
|
|
4478
4537
|
style: "omit",
|
|
4479
4538
|
};
|
|
4480
4539
|
class AttributeEmptyStyle extends Rule {
|
|
4481
4540
|
constructor(options) {
|
|
4482
|
-
super({ ...defaults$
|
|
4541
|
+
super({ ...defaults$n, ...options });
|
|
4483
4542
|
this.hasInvalidStyle = parseStyle$2(this.options.style);
|
|
4484
4543
|
}
|
|
4485
4544
|
static schema() {
|
|
@@ -4587,12 +4646,12 @@ function describePattern(pattern) {
|
|
|
4587
4646
|
}
|
|
4588
4647
|
}
|
|
4589
4648
|
|
|
4590
|
-
const defaults$
|
|
4649
|
+
const defaults$m = {
|
|
4591
4650
|
pattern: "kebabcase",
|
|
4592
4651
|
};
|
|
4593
4652
|
class ClassPattern extends Rule {
|
|
4594
4653
|
constructor(options) {
|
|
4595
|
-
super({ ...defaults$
|
|
4654
|
+
super({ ...defaults$m, ...options });
|
|
4596
4655
|
this.pattern = parsePattern(this.options.pattern);
|
|
4597
4656
|
}
|
|
4598
4657
|
static schema() {
|
|
@@ -4701,13 +4760,13 @@ class CloseOrder extends Rule {
|
|
|
4701
4760
|
}
|
|
4702
4761
|
}
|
|
4703
4762
|
|
|
4704
|
-
const defaults$
|
|
4763
|
+
const defaults$l = {
|
|
4705
4764
|
include: null,
|
|
4706
4765
|
exclude: null,
|
|
4707
4766
|
};
|
|
4708
4767
|
class Deprecated extends Rule {
|
|
4709
4768
|
constructor(options) {
|
|
4710
|
-
super({ ...defaults$
|
|
4769
|
+
super({ ...defaults$l, ...options });
|
|
4711
4770
|
}
|
|
4712
4771
|
static schema() {
|
|
4713
4772
|
return {
|
|
@@ -4870,12 +4929,12 @@ class NoStyleTag$1 extends Rule {
|
|
|
4870
4929
|
}
|
|
4871
4930
|
}
|
|
4872
4931
|
|
|
4873
|
-
const defaults$
|
|
4932
|
+
const defaults$k = {
|
|
4874
4933
|
style: "uppercase",
|
|
4875
4934
|
};
|
|
4876
4935
|
class DoctypeStyle extends Rule {
|
|
4877
4936
|
constructor(options) {
|
|
4878
|
-
super({ ...defaults$
|
|
4937
|
+
super({ ...defaults$k, ...options });
|
|
4879
4938
|
}
|
|
4880
4939
|
static schema() {
|
|
4881
4940
|
return {
|
|
@@ -4907,12 +4966,12 @@ class DoctypeStyle extends Rule {
|
|
|
4907
4966
|
}
|
|
4908
4967
|
}
|
|
4909
4968
|
|
|
4910
|
-
const defaults$
|
|
4969
|
+
const defaults$j = {
|
|
4911
4970
|
style: "lowercase",
|
|
4912
4971
|
};
|
|
4913
4972
|
class ElementCase extends Rule {
|
|
4914
4973
|
constructor(options) {
|
|
4915
|
-
super({ ...defaults$
|
|
4974
|
+
super({ ...defaults$j, ...options });
|
|
4916
4975
|
this.style = new CaseStyle(this.options.style, "element-case");
|
|
4917
4976
|
}
|
|
4918
4977
|
static schema() {
|
|
@@ -4978,14 +5037,14 @@ class ElementCase extends Rule {
|
|
|
4978
5037
|
}
|
|
4979
5038
|
}
|
|
4980
5039
|
|
|
4981
|
-
const defaults$
|
|
5040
|
+
const defaults$i = {
|
|
4982
5041
|
pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
|
|
4983
5042
|
whitelist: [],
|
|
4984
5043
|
blacklist: [],
|
|
4985
5044
|
};
|
|
4986
5045
|
class ElementName extends Rule {
|
|
4987
5046
|
constructor(options) {
|
|
4988
|
-
super({ ...defaults$
|
|
5047
|
+
super({ ...defaults$i, ...options });
|
|
4989
5048
|
// eslint-disable-next-line security/detect-non-literal-regexp
|
|
4990
5049
|
this.pattern = new RegExp(this.options.pattern);
|
|
4991
5050
|
}
|
|
@@ -5026,7 +5085,7 @@ class ElementName extends Rule {
|
|
|
5026
5085
|
...context.blacklist.map((cur) => `- ${cur}`),
|
|
5027
5086
|
];
|
|
5028
5087
|
}
|
|
5029
|
-
if (context.pattern !== defaults$
|
|
5088
|
+
if (context.pattern !== defaults$i.pattern) {
|
|
5030
5089
|
return [
|
|
5031
5090
|
`<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
|
|
5032
5091
|
"",
|
|
@@ -5428,7 +5487,7 @@ class EmptyTitle extends Rule {
|
|
|
5428
5487
|
}
|
|
5429
5488
|
}
|
|
5430
5489
|
|
|
5431
|
-
const defaults$
|
|
5490
|
+
const defaults$h = {
|
|
5432
5491
|
allowMultipleH1: false,
|
|
5433
5492
|
minInitialRank: "h1",
|
|
5434
5493
|
sectioningRoots: ["dialog", '[role="dialog"]'],
|
|
@@ -5459,7 +5518,7 @@ function parseMaxInitial(value) {
|
|
|
5459
5518
|
}
|
|
5460
5519
|
class HeadingLevel extends Rule {
|
|
5461
5520
|
constructor(options) {
|
|
5462
|
-
super({ ...defaults$
|
|
5521
|
+
super({ ...defaults$h, ...options });
|
|
5463
5522
|
this.stack = [];
|
|
5464
5523
|
this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
|
|
5465
5524
|
this.sectionRoots = this.options.sectioningRoots.map((it) => new Pattern(it));
|
|
@@ -5617,12 +5676,12 @@ class HeadingLevel extends Rule {
|
|
|
5617
5676
|
}
|
|
5618
5677
|
}
|
|
5619
5678
|
|
|
5620
|
-
const defaults$
|
|
5679
|
+
const defaults$g = {
|
|
5621
5680
|
pattern: "kebabcase",
|
|
5622
5681
|
};
|
|
5623
5682
|
class IdPattern extends Rule {
|
|
5624
5683
|
constructor(options) {
|
|
5625
|
-
super({ ...defaults$
|
|
5684
|
+
super({ ...defaults$g, ...options });
|
|
5626
5685
|
this.pattern = parsePattern(this.options.pattern);
|
|
5627
5686
|
}
|
|
5628
5687
|
static schema() {
|
|
@@ -5973,12 +6032,12 @@ function findLabelByParent(el) {
|
|
|
5973
6032
|
return [];
|
|
5974
6033
|
}
|
|
5975
6034
|
|
|
5976
|
-
const defaults$
|
|
6035
|
+
const defaults$f = {
|
|
5977
6036
|
maxlength: 70,
|
|
5978
6037
|
};
|
|
5979
6038
|
class LongTitle extends Rule {
|
|
5980
6039
|
constructor(options) {
|
|
5981
|
-
super({ ...defaults$
|
|
6040
|
+
super({ ...defaults$f, ...options });
|
|
5982
6041
|
this.maxlength = this.options.maxlength;
|
|
5983
6042
|
}
|
|
5984
6043
|
static schema() {
|
|
@@ -6125,13 +6184,13 @@ class MultipleLabeledControls extends Rule {
|
|
|
6125
6184
|
}
|
|
6126
6185
|
}
|
|
6127
6186
|
|
|
6128
|
-
const defaults$
|
|
6187
|
+
const defaults$e = {
|
|
6129
6188
|
include: null,
|
|
6130
6189
|
exclude: null,
|
|
6131
6190
|
};
|
|
6132
6191
|
class NoAutoplay extends Rule {
|
|
6133
6192
|
constructor(options) {
|
|
6134
|
-
super({ ...defaults$
|
|
6193
|
+
super({ ...defaults$e, ...options });
|
|
6135
6194
|
}
|
|
6136
6195
|
documentation(context) {
|
|
6137
6196
|
const tagName = context ? ` on <${context.tagName}>` : "";
|
|
@@ -6372,14 +6431,14 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
|
|
|
6372
6431
|
}
|
|
6373
6432
|
}
|
|
6374
6433
|
|
|
6375
|
-
const defaults$
|
|
6434
|
+
const defaults$d = {
|
|
6376
6435
|
include: null,
|
|
6377
6436
|
exclude: null,
|
|
6378
6437
|
allowedProperties: ["display"],
|
|
6379
6438
|
};
|
|
6380
6439
|
class NoInlineStyle extends Rule {
|
|
6381
6440
|
constructor(options) {
|
|
6382
|
-
super({ ...defaults$
|
|
6441
|
+
super({ ...defaults$d, ...options });
|
|
6383
6442
|
}
|
|
6384
6443
|
static schema() {
|
|
6385
6444
|
return {
|
|
@@ -6581,7 +6640,7 @@ class NoMultipleMain extends Rule {
|
|
|
6581
6640
|
}
|
|
6582
6641
|
}
|
|
6583
6642
|
|
|
6584
|
-
const defaults$
|
|
6643
|
+
const defaults$c = {
|
|
6585
6644
|
relaxed: false,
|
|
6586
6645
|
};
|
|
6587
6646
|
const textRegexp = /([<>]|&(?![a-zA-Z0-9#]+;))/g;
|
|
@@ -6598,7 +6657,7 @@ const replacementTable = {
|
|
|
6598
6657
|
};
|
|
6599
6658
|
class NoRawCharacters extends Rule {
|
|
6600
6659
|
constructor(options) {
|
|
6601
|
-
super({ ...defaults$
|
|
6660
|
+
super({ ...defaults$c, ...options });
|
|
6602
6661
|
this.relaxed = this.options.relaxed;
|
|
6603
6662
|
}
|
|
6604
6663
|
static schema() {
|
|
@@ -6776,13 +6835,13 @@ class NoRedundantRole extends Rule {
|
|
|
6776
6835
|
}
|
|
6777
6836
|
|
|
6778
6837
|
const xmlns = /^(.+):.+$/;
|
|
6779
|
-
const defaults$
|
|
6838
|
+
const defaults$b = {
|
|
6780
6839
|
ignoreForeign: true,
|
|
6781
6840
|
ignoreXML: true,
|
|
6782
6841
|
};
|
|
6783
6842
|
class NoSelfClosing extends Rule {
|
|
6784
6843
|
constructor(options) {
|
|
6785
|
-
super({ ...defaults$
|
|
6844
|
+
super({ ...defaults$b, ...options });
|
|
6786
6845
|
}
|
|
6787
6846
|
static schema() {
|
|
6788
6847
|
return {
|
|
@@ -6915,13 +6974,13 @@ const replacement = {
|
|
|
6915
6974
|
reset: '<button type="reset">',
|
|
6916
6975
|
image: '<button type="button">',
|
|
6917
6976
|
};
|
|
6918
|
-
const defaults$
|
|
6977
|
+
const defaults$a = {
|
|
6919
6978
|
include: null,
|
|
6920
6979
|
exclude: null,
|
|
6921
6980
|
};
|
|
6922
6981
|
class PreferButton extends Rule {
|
|
6923
6982
|
constructor(options) {
|
|
6924
|
-
super({ ...defaults$
|
|
6983
|
+
super({ ...defaults$a, ...options });
|
|
6925
6984
|
}
|
|
6926
6985
|
static schema() {
|
|
6927
6986
|
return {
|
|
@@ -6996,7 +7055,7 @@ class PreferButton extends Rule {
|
|
|
6996
7055
|
}
|
|
6997
7056
|
}
|
|
6998
7057
|
|
|
6999
|
-
const defaults$
|
|
7058
|
+
const defaults$9 = {
|
|
7000
7059
|
mapping: {
|
|
7001
7060
|
article: "article",
|
|
7002
7061
|
banner: "header",
|
|
@@ -7026,7 +7085,7 @@ const defaults$8 = {
|
|
|
7026
7085
|
};
|
|
7027
7086
|
class PreferNativeElement extends Rule {
|
|
7028
7087
|
constructor(options) {
|
|
7029
|
-
super({ ...defaults$
|
|
7088
|
+
super({ ...defaults$9, ...options });
|
|
7030
7089
|
}
|
|
7031
7090
|
static schema() {
|
|
7032
7091
|
return {
|
|
@@ -7146,8 +7205,66 @@ class PreferTbody extends Rule {
|
|
|
7146
7205
|
}
|
|
7147
7206
|
}
|
|
7148
7207
|
|
|
7208
|
+
const defaults$8 = {
|
|
7209
|
+
tags: ["script", "style"],
|
|
7210
|
+
};
|
|
7211
|
+
class RequireCSPNonce extends Rule {
|
|
7212
|
+
constructor(options) {
|
|
7213
|
+
super({ ...defaults$8, ...options });
|
|
7214
|
+
}
|
|
7215
|
+
static schema() {
|
|
7216
|
+
return {
|
|
7217
|
+
tags: {
|
|
7218
|
+
type: "array",
|
|
7219
|
+
items: {
|
|
7220
|
+
enum: ["script", "style"],
|
|
7221
|
+
type: "string",
|
|
7222
|
+
},
|
|
7223
|
+
},
|
|
7224
|
+
};
|
|
7225
|
+
}
|
|
7226
|
+
documentation() {
|
|
7227
|
+
return {
|
|
7228
|
+
description: [
|
|
7229
|
+
"Required Content-Security-Policy (CSP) nonce is missing or empty.",
|
|
7230
|
+
"",
|
|
7231
|
+
"This is set by the `nonce` attribute and must match the `Content-Security-Policy` header.",
|
|
7232
|
+
"For instance, if the header contains `script-src 'nonce-r4nd0m'` the `nonce` attribute must be set to `nonce=\"r4nd0m\">`",
|
|
7233
|
+
"",
|
|
7234
|
+
"The nonce should be unique per each request and set to a cryptography secure random token.",
|
|
7235
|
+
"It is used to prevent cross site scripting (XSS) by preventing malicious actors from injecting scripts onto the page.",
|
|
7236
|
+
].join("\n"),
|
|
7237
|
+
url: ruleDocumentationUrl("@/rules/require-csp-nonce.ts"),
|
|
7238
|
+
};
|
|
7239
|
+
}
|
|
7240
|
+
setup() {
|
|
7241
|
+
this.on("tag:end", (event) => {
|
|
7242
|
+
var _a;
|
|
7243
|
+
const { tags } = this.options;
|
|
7244
|
+
const node = event.previous;
|
|
7245
|
+
/* ignore other tags */
|
|
7246
|
+
if (!node || !tags.includes(node.tagName)) {
|
|
7247
|
+
return;
|
|
7248
|
+
}
|
|
7249
|
+
/* ignore if nonce is set to non-empty value (or dynamic) */
|
|
7250
|
+
const nonce = (_a = node.getAttribute("nonce")) === null || _a === void 0 ? void 0 : _a.value;
|
|
7251
|
+
if (nonce && nonce !== "") {
|
|
7252
|
+
return;
|
|
7253
|
+
}
|
|
7254
|
+
/* ignore <script src> */
|
|
7255
|
+
if (node.is("script") && node.hasAttribute("src")) {
|
|
7256
|
+
return;
|
|
7257
|
+
}
|
|
7258
|
+
const message = `required CSP nonce is missing`;
|
|
7259
|
+
this.report(node, message, node.location);
|
|
7260
|
+
});
|
|
7261
|
+
}
|
|
7262
|
+
}
|
|
7263
|
+
|
|
7149
7264
|
const defaults$7 = {
|
|
7150
7265
|
target: "all",
|
|
7266
|
+
include: null,
|
|
7267
|
+
exclude: null,
|
|
7151
7268
|
};
|
|
7152
7269
|
const crossorigin = new RegExp("^(\\w+://|//)"); /* e.g. https:// or // */
|
|
7153
7270
|
const supportSri = {
|
|
@@ -7165,6 +7282,32 @@ class RequireSri extends Rule {
|
|
|
7165
7282
|
enum: ["all", "crossorigin"],
|
|
7166
7283
|
type: "string",
|
|
7167
7284
|
},
|
|
7285
|
+
include: {
|
|
7286
|
+
anyOf: [
|
|
7287
|
+
{
|
|
7288
|
+
items: {
|
|
7289
|
+
type: "string",
|
|
7290
|
+
},
|
|
7291
|
+
type: "array",
|
|
7292
|
+
},
|
|
7293
|
+
{
|
|
7294
|
+
type: "null",
|
|
7295
|
+
},
|
|
7296
|
+
],
|
|
7297
|
+
},
|
|
7298
|
+
exclude: {
|
|
7299
|
+
anyOf: [
|
|
7300
|
+
{
|
|
7301
|
+
items: {
|
|
7302
|
+
type: "string",
|
|
7303
|
+
},
|
|
7304
|
+
type: "array",
|
|
7305
|
+
},
|
|
7306
|
+
{
|
|
7307
|
+
type: "null",
|
|
7308
|
+
},
|
|
7309
|
+
],
|
|
7310
|
+
},
|
|
7168
7311
|
};
|
|
7169
7312
|
}
|
|
7170
7313
|
documentation() {
|
|
@@ -7177,11 +7320,13 @@ class RequireSri extends Rule {
|
|
|
7177
7320
|
this.on("tag:end", (event) => {
|
|
7178
7321
|
/* only handle thats supporting and requires sri */
|
|
7179
7322
|
const node = event.previous;
|
|
7180
|
-
if (!(this.supportSri(node) && this.needSri(node)))
|
|
7323
|
+
if (!(this.supportSri(node) && this.needSri(node))) {
|
|
7181
7324
|
return;
|
|
7325
|
+
}
|
|
7182
7326
|
/* check if sri attribute is present */
|
|
7183
|
-
if (node.hasAttribute("integrity"))
|
|
7327
|
+
if (node.hasAttribute("integrity")) {
|
|
7184
7328
|
return;
|
|
7329
|
+
}
|
|
7185
7330
|
this.report(node, `SRI "integrity" attribute is required on <${node.tagName}> element`, node.location);
|
|
7186
7331
|
});
|
|
7187
7332
|
}
|
|
@@ -7189,19 +7334,25 @@ class RequireSri extends Rule {
|
|
|
7189
7334
|
return Object.keys(supportSri).includes(node.tagName);
|
|
7190
7335
|
}
|
|
7191
7336
|
needSri(node) {
|
|
7192
|
-
if (this.target === "all")
|
|
7193
|
-
return true;
|
|
7194
7337
|
const attr = this.elementSourceAttr(node);
|
|
7195
|
-
if (!attr || attr.value === null || attr.isDynamic) {
|
|
7338
|
+
if (!attr || attr.value === null || attr.value === "" || attr.isDynamic) {
|
|
7196
7339
|
return false;
|
|
7197
7340
|
}
|
|
7198
7341
|
const url = attr.value.toString();
|
|
7199
|
-
|
|
7342
|
+
if (this.target === "all" || crossorigin.test(url)) {
|
|
7343
|
+
return !this.isIgnored(url);
|
|
7344
|
+
}
|
|
7345
|
+
return false;
|
|
7200
7346
|
}
|
|
7201
7347
|
elementSourceAttr(node) {
|
|
7202
7348
|
const key = supportSri[node.tagName];
|
|
7203
7349
|
return node.getAttribute(key);
|
|
7204
7350
|
}
|
|
7351
|
+
isIgnored(url) {
|
|
7352
|
+
return this.isKeywordIgnored(url, (list, it) => {
|
|
7353
|
+
return list.some((pattern) => it.includes(pattern));
|
|
7354
|
+
});
|
|
7355
|
+
}
|
|
7205
7356
|
}
|
|
7206
7357
|
|
|
7207
7358
|
class ScriptElement extends Rule {
|
|
@@ -9999,6 +10150,7 @@ const bundledRules = {
|
|
|
9999
10150
|
"prefer-button": PreferButton,
|
|
10000
10151
|
"prefer-native-element": PreferNativeElement,
|
|
10001
10152
|
"prefer-tbody": PreferTbody,
|
|
10153
|
+
"require-csp-nonce": RequireCSPNonce,
|
|
10002
10154
|
"require-sri": RequireSri,
|
|
10003
10155
|
"script-element": ScriptElement,
|
|
10004
10156
|
"script-type": ScriptType,
|