@stll/oxlint-plugin 0.2.1 → 0.3.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.
@@ -5,119 +5,511 @@ Object.defineProperties(exports, {
5
5
  const require_utils = require("./utils.cjs");
6
6
  let _oxlint_plugins = require("@oxlint/plugins");
7
7
  //#region src/no-unsafe-inner-html.ts
8
+ const DOM_ELEMENT_PRODUCERS = [
9
+ {
10
+ name: "body",
11
+ kind: "property",
12
+ receiver: "document"
13
+ },
14
+ {
15
+ name: "head",
16
+ kind: "property",
17
+ receiver: "document"
18
+ },
19
+ {
20
+ name: "documentElement",
21
+ kind: "property",
22
+ receiver: "document"
23
+ },
24
+ {
25
+ name: "activeElement",
26
+ kind: "property",
27
+ receiver: "document"
28
+ },
29
+ {
30
+ name: "scrollingElement",
31
+ kind: "property",
32
+ receiver: "document"
33
+ },
34
+ {
35
+ name: "fullscreenElement",
36
+ kind: "property",
37
+ receiver: "document"
38
+ },
39
+ {
40
+ name: "createElement",
41
+ kind: "method",
42
+ receiver: "document"
43
+ },
44
+ {
45
+ name: "createElementNS",
46
+ kind: "method",
47
+ receiver: "document"
48
+ },
49
+ {
50
+ name: "getElementById",
51
+ kind: "method",
52
+ receiver: "document"
53
+ },
54
+ {
55
+ name: "querySelector",
56
+ kind: "method",
57
+ receiver: "either"
58
+ },
59
+ {
60
+ name: "closest",
61
+ kind: "method",
62
+ receiver: "element"
63
+ },
64
+ {
65
+ name: "elementFromPoint",
66
+ kind: "method",
67
+ receiver: "document"
68
+ },
69
+ {
70
+ name: "getElementsByClassName",
71
+ kind: "collection",
72
+ receiver: "either"
73
+ },
74
+ {
75
+ name: "getElementsByTagName",
76
+ kind: "collection",
77
+ receiver: "either"
78
+ },
79
+ {
80
+ name: "getElementsByTagNameNS",
81
+ kind: "collection",
82
+ receiver: "either"
83
+ },
84
+ {
85
+ name: "getElementsByName",
86
+ kind: "collection",
87
+ receiver: "document"
88
+ },
89
+ {
90
+ name: "querySelectorAll",
91
+ kind: "collection",
92
+ receiver: "either"
93
+ },
94
+ {
95
+ name: "parentElement",
96
+ kind: "property",
97
+ receiver: "element"
98
+ },
99
+ {
100
+ name: "firstElementChild",
101
+ kind: "property",
102
+ receiver: "element"
103
+ },
104
+ {
105
+ name: "lastElementChild",
106
+ kind: "property",
107
+ receiver: "element"
108
+ },
109
+ {
110
+ name: "nextElementSibling",
111
+ kind: "property",
112
+ receiver: "element"
113
+ },
114
+ {
115
+ name: "previousElementSibling",
116
+ kind: "property",
117
+ receiver: "element"
118
+ },
119
+ {
120
+ name: "offsetParent",
121
+ kind: "property",
122
+ receiver: "element"
123
+ }
124
+ ];
125
+ const WAIVER_RE = /^\s*safe-html:\s*\S/u;
126
+ const REACT_HTML_PROPS = [
127
+ {
128
+ name: "dangerouslySetInnerHTML",
129
+ kind: "payload",
130
+ htmlKey: "__html"
131
+ },
132
+ {
133
+ name: "srcDoc",
134
+ kind: "value"
135
+ },
136
+ {
137
+ name: "srcdoc",
138
+ kind: "value"
139
+ }
140
+ ];
141
+ const REACT_ELEMENT_FACTORIES = /* @__PURE__ */ new Set([
142
+ "createElement",
143
+ "React.createElement",
144
+ "jsx",
145
+ "jsxs",
146
+ "jsxDEV"
147
+ ]);
148
+ const HTML_PROPERTIES = /* @__PURE__ */ new Set([
149
+ "innerHTML",
150
+ "outerHTML",
151
+ "srcdoc"
152
+ ]);
153
+ const HTML_FIRST_ARGUMENT_METHODS = /* @__PURE__ */ new Set(["createContextualFragment", "setHTMLUnsafe"]);
154
+ const DOCUMENT_WRITE_METHODS = /* @__PURE__ */ new Set(["write", "writeln"]);
155
+ const DOCUMENT_MEMBERS = /* @__PURE__ */ new Set([
156
+ "contentDocument",
157
+ "document",
158
+ "ownerDocument"
159
+ ]);
8
160
  const HTML_SANITIZER_LEAF_RE = /^(?:escape(?:html|xml)|sanitize(?:html|dom)|purify(?:html|dom)|dompurify)$/iu;
9
- const DOM_PURIFY_RE = /^dompurify$/iu;
10
161
  const SANITIZER_RESULT_MEMBERS = /* @__PURE__ */ new Set([
11
162
  "value",
12
163
  "data",
13
164
  "html"
14
165
  ]);
15
- const ESCAPE_HATCH_RE = /^\s*safe-html:/u;
16
- const isComment = (value) => typeof value === "object" && value !== null && typeof value.value === "string" && typeof value.loc === "object" && value.loc !== null;
17
- const isJsxIdentifier = (node, name) => typeof node === "object" && node !== null && node.type === "JSXIdentifier" && node.name === name;
18
- const isHtmlSanitizerCalleeName = (dotted) => {
166
+ const SANITIZER_POLICY = {
167
+ allowlist: "allowlist",
168
+ provenance: "provenance"
169
+ };
170
+ const SANITIZER_POLICY_VALUES = Object.values(SANITIZER_POLICY);
171
+ const sanitizerPolicyFrom = (option) => {
172
+ if (option === void 0) return SANITIZER_POLICY.allowlist;
173
+ if (typeof option !== "object" || option === null || Array.isArray(option)) throw new Error("no-unsafe-inner-html options must be an object");
174
+ const policy = Reflect.get(option, "sanitizerPolicy");
175
+ switch (policy) {
176
+ case void 0:
177
+ case SANITIZER_POLICY.allowlist: return SANITIZER_POLICY.allowlist;
178
+ case SANITIZER_POLICY.provenance: return SANITIZER_POLICY.provenance;
179
+ default: throw new Error(`unknown sanitizer policy: ${String(policy)}`);
180
+ }
181
+ };
182
+ const staticCalleeName = (node) => {
183
+ const expression = require_utils.unwrapExpression(node);
184
+ if (require_utils.isIdentifier(expression)) return expression.name;
185
+ if (expression?.type !== "MemberExpression") return null;
186
+ const receiver = staticCalleeName(expression.object);
187
+ const property = require_utils.memberPropertyName(expression);
188
+ return receiver === null || property === null ? null : `${receiver}.${property}`;
189
+ };
190
+ const isSanitizerCall = (node) => {
191
+ if (!require_utils.isAstNode(node) || node.type !== "CallExpression") return false;
192
+ const dotted = staticCalleeName(node.callee);
193
+ if (dotted === null) return false;
19
194
  const parts = dotted.split(".");
20
195
  const leaf = parts.at(-1);
21
196
  if (leaf === void 0) return false;
22
197
  if (HTML_SANITIZER_LEAF_RE.test(leaf)) return true;
23
- const objectName = parts.at(-2);
24
- return leaf === "sanitize" && DOM_PURIFY_RE.test(objectName ?? "");
198
+ return leaf === "sanitize" && /^dompurify$/iu.test(parts.at(-2) ?? "");
25
199
  };
26
- const isSanitizerCall = (node) => {
27
- if (node.type !== "CallExpression") return false;
28
- const dotted = require_utils.getCalleeName(node.callee);
29
- return dotted !== null && isHtmlSanitizerCalleeName(dotted);
200
+ const isSanitizerResult = (node) => {
201
+ const expression = require_utils.unwrapExpression(node);
202
+ if (expression?.type !== "MemberExpression") return false;
203
+ const property = require_utils.memberPropertyName(expression);
204
+ return property !== null && SANITIZER_RESULT_MEMBERS.has(property) && isSanitizerCall(expression.object);
205
+ };
206
+ const hasLocation = (node) => typeof node === "object" && node !== null && "loc" in node && typeof node.loc === "object" && node.loc !== null && "start" in node.loc && typeof node.loc.start === "object" && node.loc.start !== null && "line" in node.loc.start && typeof node.loc.start.line === "number";
207
+ const startLine = (node) => hasLocation(node) ? node.loc.start.line : NaN;
208
+ const isWaiverComment = (value) => typeof value === "object" && value !== null && "value" in value && typeof value.value === "string" && WAIVER_RE.test(value.value) && "loc" in value && typeof value.loc === "object" && value.loc !== null;
209
+ const jsxAttributeName = (node) => require_utils.isAstNode(node) && node.type === "JSXIdentifier" && typeof node.name === "string" ? node.name : null;
210
+ const isProvenSafeValue = (node, sanitizerPolicy) => {
211
+ const expression = require_utils.unwrapExpression(node);
212
+ if (expression === null) return false;
213
+ if (require_utils.isStringLiteral(expression)) return true;
214
+ if (sanitizerPolicy === SANITIZER_POLICY.allowlist && (isSanitizerCall(expression) || isSanitizerResult(expression))) return true;
215
+ return expression.type === "TemplateLiteral" && Array.isArray(expression.expressions) && expression.expressions.every((entry) => isProvenSafeValue(entry, sanitizerPolicy));
30
216
  };
31
- const isProvenSafeValue = (node) => {
32
- if (!node || typeof node.type !== "string") return false;
33
- if (node.type === "Literal" && typeof node.value === "string") return true;
34
- if (node.type === "TemplateLiteral") return Array.isArray(node.expressions) && node.expressions.every(isProvenSafeValue);
35
- if (isSanitizerCall(node)) return true;
36
- if (node.type === "MemberExpression" && node.computed === false && isProvenSafeMemberLeaf(node)) return true;
37
- return false;
217
+ const objectPropertyKey = (property) => {
218
+ if (!require_utils.isAstNode(property)) return null;
219
+ if (property.computed === true) return require_utils.staticStringValue(property.key);
220
+ return require_utils.getPropertyName(property.key);
38
221
  };
39
- const isProvenSafeMemberLeaf = (node) => {
40
- const propertyName = require_utils.getPropertyName(node.property);
41
- if (propertyName === null || !SANITIZER_RESULT_MEMBERS.has(propertyName)) return false;
42
- return isSanitizerCall(node.object);
222
+ const isOpaqueProperty = (property) => property.type === "SpreadElement" || property.type === "Property" && property.computed === true && objectPropertyKey(property) === null;
223
+ const objectProperties = (node) => {
224
+ const expression = require_utils.unwrapExpression(node);
225
+ if (expression?.type !== "ObjectExpression" || !Array.isArray(expression.properties)) return [];
226
+ return expression.properties.filter(require_utils.isAstNode);
43
227
  };
44
- const isDangerouslySetInnerHtmlValue = (property) => {
45
- if (require_utils.getPropertyName(property.key) !== "__html") return false;
46
- const objectExpression = property.parent;
47
- if (objectExpression?.type !== "ObjectExpression") return false;
48
- let current = objectExpression.parent;
49
- while (current?.type === "TSAsExpression" || current?.type === "TSSatisfiesExpression") current = current.parent;
50
- if (current?.type !== "JSXExpressionContainer") return false;
51
- const attribute = current.parent;
52
- return attribute?.type === "JSXAttribute" && isJsxIdentifier(attribute.name, "dangerouslySetInnerHTML");
228
+ const appliedArgumentsFor = (args) => {
229
+ const applied = require_utils.unwrapExpression(args.at(1));
230
+ if (applied === null || applied.type === "Literal" && applied.value === null || require_utils.isIdentifier(applied, "undefined")) return {
231
+ status: "known",
232
+ args: []
233
+ };
234
+ if (applied.type !== "ArrayExpression" || !Array.isArray(applied.elements)) return {
235
+ status: "opaque",
236
+ args: [],
237
+ spread: args.at(1),
238
+ tail: []
239
+ };
240
+ return require_utils.resolveLiteralArraySpreads(applied.elements);
241
+ };
242
+ const invokedArguments = (call) => {
243
+ if (!require_utils.isAstNode(call)) return {
244
+ status: "known",
245
+ args: []
246
+ };
247
+ const rawArguments = Array.isArray(call.arguments) ? call.arguments : [];
248
+ const resolved = require_utils.resolveLiteralArraySpreads(rawArguments);
249
+ const callee = require_utils.unwrapExpression(call.callee);
250
+ if (callee?.type !== "MemberExpression") return resolved;
251
+ const method = require_utils.memberPropertyName(callee);
252
+ if (method === "call") return {
253
+ ...resolved,
254
+ args: resolved.args.slice(1)
255
+ };
256
+ if (method !== "apply") return resolved;
257
+ if (resolved.status === "opaque") {
258
+ if (resolved.args.length >= 2) return appliedArgumentsFor(resolved.args);
259
+ return {
260
+ ...resolved,
261
+ args: [],
262
+ tail: []
263
+ };
264
+ }
265
+ return appliedArgumentsFor(resolved.args);
266
+ };
267
+ const isDocumentReceiver = (node) => {
268
+ const receiver = require_utils.unwrapExpression(node);
269
+ if (require_utils.isIdentifier(receiver, "document")) return true;
270
+ if (receiver?.type !== "MemberExpression") return false;
271
+ const name = require_utils.memberPropertyName(receiver);
272
+ return name !== null && DOCUMENT_MEMBERS.has(name);
53
273
  };
54
274
  var no_unsafe_inner_html_default = (0, _oxlint_plugins.eslintCompatPlugin)({
55
275
  meta: { name: "no-unsafe-inner-html" },
56
276
  rules: { "no-unsafe-inner-html": {
57
277
  meta: {
58
278
  type: "problem",
279
+ schema: [{
280
+ type: "object",
281
+ properties: { sanitizerPolicy: {
282
+ type: "string",
283
+ enum: SANITIZER_POLICY_VALUES
284
+ } },
285
+ additionalProperties: false
286
+ }],
59
287
  messages: {
60
- unsafeInnerHtml: "Raw HTML injected into the DOM must come from a provably sanitized/escaped value: a static string, an HTML/DOM sanitize/escape call (or its .value/.data/.html result). Wrap the value in a sanitizer, or add a `// safe-html: <reason naming the sanitizer/source>` comment on the line directly above if it is escaped at its source.",
288
+ unsafeInnerHtml: "Dynamic HTML injected into the DOM must be static, proven by the configured sanitizer policy, or have an adjacent `// safe-html: <provenance>` comment naming the trusted source. Function names outside the allowlist are not proof because a local identity function can shadow them.",
61
289
  unsafeInnerHtmlSpread: "Do not spread an object into dangerouslySetInnerHTML. Keep `__html` inline so this rule can prove the HTML value is sanitized or escaped."
62
290
  }
63
291
  },
64
292
  createOnce(context) {
65
- const escapeHatchLines = /* @__PURE__ */ new Set();
66
- const recordEscapeHatches = (node) => {
67
- const comments = node && Array.isArray(node.comments) ? node.comments.filter(isComment) : [];
68
- for (const comment of comments) if (ESCAPE_HATCH_RE.test(comment.value)) escapeHatchLines.add(comment.loc.end.line);
293
+ const isKnownDomTarget = ({ node, scope, seen = /* @__PURE__ */ new Set(), kind = "element" }) => {
294
+ const target = require_utils.unwrapExpression(node);
295
+ if (require_utils.isIdentifier(target)) {
296
+ let current = scope;
297
+ while (current !== null) {
298
+ const variable = current.set.get(target.name);
299
+ if (variable === void 0) {
300
+ current = current.upper;
301
+ continue;
302
+ }
303
+ if (variable.defs.length === 0) return kind === "element" && isDocumentReceiver(target);
304
+ if (seen.has(variable)) return false;
305
+ seen.add(variable);
306
+ const definition = variable.defs.at(0);
307
+ if (variable.defs.length !== 1 || definition?.type !== "Variable" || definition.node.type !== "VariableDeclarator" || definition.node.id.type !== "Identifier" || definition.node.init === null || variable.references.some((reference) => reference.isWrite() && !reference.init)) return false;
308
+ return isKnownDomTarget({
309
+ node: definition.node.init,
310
+ scope: context.sourceCode.getScope(definition.node),
311
+ seen,
312
+ kind
313
+ });
314
+ }
315
+ }
316
+ const hasProducerReceiver = (receiver, producer) => {
317
+ if (producer.receiver !== "element" && isDocumentReceiver(receiver)) return true;
318
+ return producer.receiver !== "document" && isKnownDomTarget({
319
+ node: receiver,
320
+ scope,
321
+ seen
322
+ });
323
+ };
324
+ const isElementCollection = (collection) => isKnownDomTarget({
325
+ node: collection,
326
+ scope,
327
+ seen,
328
+ kind: "collection"
329
+ });
330
+ if (kind === "element" && target?.type === "MemberExpression") {
331
+ const property = require_utils.memberPropertyName(target);
332
+ const producer = DOM_ELEMENT_PRODUCERS.find((entry) => entry.kind === "property" && entry.name === property);
333
+ if (producer !== void 0) return hasProducerReceiver(target.object, producer);
334
+ const index = require_utils.unwrapExpression(target.property);
335
+ if (target.computed === true && index?.type === "Literal" && typeof index.value === "number" && Number.isInteger(index.value) && index.value >= 0) return isElementCollection(target.object);
336
+ }
337
+ if (target?.type === "CallExpression") {
338
+ const callee = require_utils.invokedCallee(target);
339
+ if (callee?.type !== "MemberExpression") return false;
340
+ const method = require_utils.memberPropertyName(callee);
341
+ if (kind === "element" && method === "item" && isElementCollection(callee.object)) return true;
342
+ const producer = DOM_ELEMENT_PRODUCERS.find((entry) => entry.kind === (kind === "collection" ? "collection" : "method") && entry.name === method);
343
+ return producer !== void 0 && hasProducerReceiver(callee.object, producer);
344
+ }
345
+ return kind === "element" && isDocumentReceiver(target);
69
346
  };
70
- const hasEscapeHatchAbove = (node) => escapeHatchLines.has(node.loc.start.line - 1);
71
- const reportIfUnsafe = (node) => {
72
- if (isProvenSafeValue(node)) return;
73
- if (hasEscapeHatchAbove(node)) return;
74
- context.report({
347
+ let sanitizerPolicy = SANITIZER_POLICY.allowlist;
348
+ const waiverLines = /* @__PURE__ */ new Set();
349
+ const sinks = [];
350
+ const observeSink = (node, kind) => {
351
+ if (require_utils.isAstNode(node)) sinks.push({
75
352
  node,
76
- messageId: "unsafeInnerHtml"
353
+ kind
77
354
  });
78
355
  };
79
- const reportPayloadSpreads = (objectNode) => {
80
- for (const property of objectNode.properties) {
81
- if (property?.type !== "SpreadElement") continue;
82
- context.report({
83
- node: property,
84
- messageId: "unsafeInnerHtmlSpread"
85
- });
356
+ const consumeWaiver = (node) => {
357
+ const line = startLine(node) - 1;
358
+ if (!waiverLines.has(line)) return false;
359
+ waiverLines.delete(line);
360
+ return true;
361
+ };
362
+ const reportIfUnsafe = (node) => {
363
+ observeSink(node, isProvenSafeValue(node, sanitizerPolicy) ? "safe" : "unsafe");
364
+ };
365
+ const checkDangerousPayload = (value, htmlKey) => {
366
+ const expression = require_utils.unwrapExpression(value);
367
+ if (expression?.type !== "ObjectExpression") {
368
+ reportIfUnsafe(value);
369
+ return;
370
+ }
371
+ for (const property of objectProperties(expression)) {
372
+ if (isOpaqueProperty(property)) {
373
+ observeSink(property, "opaque-payload");
374
+ continue;
375
+ }
376
+ if (property.type === "Property" && objectPropertyKey(property) === htmlKey) reportIfUnsafe(property.value);
377
+ }
378
+ };
379
+ const checkReactHtmlProp = (prop, value) => {
380
+ switch (prop.kind) {
381
+ case "payload":
382
+ checkDangerousPayload(value, prop.htmlKey);
383
+ return;
384
+ case "value":
385
+ reportIfUnsafe(value);
386
+ return;
387
+ default: throw new Error(`Unknown React HTML prop: ${String(prop)}`);
388
+ }
389
+ };
390
+ const checkHtmlPropertySources = (sources, domTarget) => {
391
+ for (const source of sources) {
392
+ if (require_utils.unwrapExpression(source)?.type !== "ObjectExpression") {
393
+ if (domTarget) reportIfUnsafe(source);
394
+ continue;
395
+ }
396
+ for (const property of objectProperties(source)) {
397
+ if (isOpaqueProperty(property)) {
398
+ if (domTarget) reportIfUnsafe(property);
399
+ continue;
400
+ }
401
+ const key = property.type === "Property" ? objectPropertyKey(property) : null;
402
+ if (key !== null && HTML_PROPERTIES.has(key)) reportIfUnsafe(property.value);
403
+ }
86
404
  }
87
405
  };
88
406
  return {
89
407
  before() {
90
- escapeHatchLines.clear();
408
+ sanitizerPolicy = sanitizerPolicyFrom(context.options.at(0));
409
+ waiverLines.clear();
410
+ sinks.length = 0;
411
+ },
412
+ "Program:exit"() {
413
+ sinks.sort((left, right) => left.node.range[0] - right.node.range[0]);
414
+ for (const { node, kind } of sinks) {
415
+ const waived = consumeWaiver(node);
416
+ if (kind === "safe" || kind === "unsafe" && waived) continue;
417
+ context.report({
418
+ node,
419
+ messageId: kind === "opaque-payload" ? "unsafeInnerHtmlSpread" : "unsafeInnerHtml"
420
+ });
421
+ }
422
+ sinks.length = 0;
91
423
  },
92
424
  Program(node) {
93
- recordEscapeHatches(node);
425
+ const comments = Array.isArray(node.comments) ? node.comments : [];
426
+ for (const comment of comments) if (isWaiverComment(comment)) waiverLines.add(comment.loc.end.line);
94
427
  },
95
428
  JSXAttribute(node) {
96
- if (!isJsxIdentifier(node.name, "dangerouslySetInnerHTML")) return;
97
- const value = node.value;
98
- if (value?.type !== "JSXExpressionContainer") return;
99
- if (!value.expression) return;
100
- const expression = require_utils.unwrapExpression(value.expression);
101
- if (expression?.type === "ObjectExpression") {
102
- reportPayloadSpreads(expression);
103
- return;
104
- }
105
- reportIfUnsafe(value.expression);
429
+ const name = jsxAttributeName(node.name);
430
+ const prop = REACT_HTML_PROPS.find((entry) => entry.name === name);
431
+ if (prop === void 0) return;
432
+ const value = require_utils.unwrapExpression(node.value);
433
+ checkReactHtmlProp(prop, value?.type === "JSXExpressionContainer" ? value.expression : value);
106
434
  },
107
435
  Property(node) {
108
- if (!isDangerouslySetInnerHtmlValue(node)) return;
109
- reportIfUnsafe(node.value);
436
+ const key = objectPropertyKey(node);
437
+ const prop = REACT_HTML_PROPS.find((entry) => entry.name === key);
438
+ if (prop?.kind !== "payload") return;
439
+ const container = node.parent;
440
+ if (require_utils.isAstNode(container) && container.type === "ObjectExpression") checkReactHtmlProp(prop, node.value);
110
441
  },
111
442
  AssignmentExpression(node) {
112
- const target = node.left;
113
- if (target.type !== "MemberExpression" || target.computed !== false || !require_utils.isIdentifier(target.property, "innerHTML")) return;
114
- reportIfUnsafe(node.right);
443
+ const target = require_utils.unwrapExpression(node.left);
444
+ if (target?.type !== "MemberExpression") return;
445
+ const name = require_utils.memberPropertyName(target);
446
+ if (name !== null && HTML_PROPERTIES.has(name)) reportIfUnsafe(node.right);
447
+ },
448
+ CallExpression(node) {
449
+ const call = node;
450
+ const callee = require_utils.isAstNode(call) ? require_utils.invokedCallee(call) : null;
451
+ const invoked = invokedArguments(node);
452
+ const factory = staticCalleeName(callee);
453
+ if (factory !== null && REACT_ELEMENT_FACTORIES.has(factory)) {
454
+ for (const property of objectProperties(invoked.args.at(1))) {
455
+ if (property.type !== "Property") continue;
456
+ const key = objectPropertyKey(property);
457
+ const prop = REACT_HTML_PROPS.find((entry) => entry.name === key);
458
+ if (prop?.kind === "value") checkReactHtmlProp(prop, property.value);
459
+ }
460
+ return;
461
+ }
462
+ if (callee?.type !== "MemberExpression") return;
463
+ const method = require_utils.memberPropertyName(callee);
464
+ if (method === null) return;
465
+ if (HTML_FIRST_ARGUMENT_METHODS.has(method)) {
466
+ if (invoked.status === "opaque") {
467
+ reportIfUnsafe(node);
468
+ return;
469
+ }
470
+ reportIfUnsafe(invoked.args.at(0));
471
+ return;
472
+ }
473
+ if (method === "insertAdjacentHTML") {
474
+ if (invoked.status === "opaque") {
475
+ reportIfUnsafe(node);
476
+ return;
477
+ }
478
+ reportIfUnsafe(invoked.args.at(1));
479
+ return;
480
+ }
481
+ if (method === "setAttribute") {
482
+ if (invoked.status === "opaque") {
483
+ reportIfUnsafe(node);
484
+ return;
485
+ }
486
+ if (require_utils.staticStringValue(invoked.args.at(0))?.toLowerCase() === "srcdoc") reportIfUnsafe(invoked.args.at(1));
487
+ return;
488
+ }
489
+ if (DOCUMENT_WRITE_METHODS.has(method) && isDocumentReceiver(callee.object)) {
490
+ if (invoked.status === "opaque") {
491
+ reportIfUnsafe(node);
492
+ return;
493
+ }
494
+ observeSink(node, invoked.args.every((argument) => isProvenSafeValue(argument, sanitizerPolicy)) ? "safe" : "unsafe");
495
+ return;
496
+ }
497
+ if (require_utils.isIdentifier(callee.object, "Object") && method === "assign") {
498
+ const domTarget = isKnownDomTarget({
499
+ node: invoked.args.at(0),
500
+ scope: context.sourceCode.getScope(node)
501
+ });
502
+ checkHtmlPropertySources([...invoked.args.slice(1), ...invoked.status === "opaque" ? invoked.tail : []], domTarget);
503
+ if (invoked.status === "opaque" && domTarget) reportIfUnsafe(invoked.spread);
504
+ }
115
505
  }
116
506
  };
117
507
  }
118
508
  } }
119
509
  });
120
510
  //#endregion
511
+ exports.DOM_ELEMENT_PRODUCERS = DOM_ELEMENT_PRODUCERS;
512
+ exports.REACT_HTML_PROPS = REACT_HTML_PROPS;
121
513
  exports.default = no_unsafe_inner_html_default;
122
514
 
123
515
  //# sourceMappingURL=no-unsafe-inner-html.cjs.map