History

Lint-enforce Redacted unwrap allowlist

Rhys Sullivan committed 3 months ago

Commit f7a2a461 parent 1c65fc825 files · +124 −6Browse files

Showing the changes to scripts (2 of 25 files). Show all

Unified · Split

2 files
  1. scripts/oxlint-plugin-executor.js +2 −0
  2. scripts/oxlint-plugin-executor/rules/no-redacted-unwrap.js +39 −0
scripts/oxlint-plugin-executor.js+2−0
@@ -18,6 +18,7 @@ import noPromiseReject from "./oxlint-plugin-executor/rules/no-promise-reject.js · 17 unmodified lines
1818 import noRawDurableObjectId from "./oxlint-plugin-executor/rules/no-raw-durable-object-id.js";
1919 import noRawFetch from "./oxlint-plugin-executor/rules/no-raw-fetch.js";
2020 import noRawErrorThrow from "./oxlint-plugin-executor/rules/no-raw-error-throw.js";
21+import noRedactedUnwrap from "./oxlint-plugin-executor/rules/no-redacted-unwrap.js";
2122 import noRedundantPrimitiveCast from "./oxlint-plugin-executor/rules/no-redundant-primitive-cast.js";
2223 import noRedundantErrorFactory from "./oxlint-plugin-executor/rules/no-redundant-error-factory.js";
2324 import noSchemaClass from "./oxlint-plugin-executor/rules/no-schema-class.js";
@@ -62,6 +63,7 @@ export default { · 38 unmodified lines
6263 "no-raw-durable-object-id": noRawDurableObjectId,
6364 "no-raw-fetch": noRawFetch,
6465 "no-raw-error-throw": noRawErrorThrow,
66+ "no-redacted-unwrap": noRedactedUnwrap,
6567 "no-redundant-primitive-cast": noRedundantPrimitiveCast,
6668 "no-redundant-error-factory": noRedundantErrorFactory,
6769 "no-schema-class": noSchemaClass,
scripts/oxlint-plugin-executor/rules/no-redacted-unwrap.js+39−0
@@ -0,0 +1,39 @@
1+import {
2+ getPropertyName,
3+ isDeclarationFile,
4+ isIdentifier,
5+ isTestLike,
6+ unwrapExpression,
7+} from "../utils.js";
8+
9+const message =
10+ 'Unwrapping a credential is a boundary decision, not a convenience. Keep the value `Redacted` and let the wire/persistence line unwrap it; if this IS that line, mark it: `// oxlint-disable-next-line executor/no-redacted-unwrap -- boundary: <why the plaintext has to cross here>`. A missed unwrap on a write path does not throw — `Redacted`\'s toJSON renders the literal "<redacted>" and it is persisted instead.';
11+
12+// Matches the reference too, not just the call: `.map(Redacted.value)` and
13+// `SchemaGetter.transform(Redacted.value<string>)` unwrap exactly as much as an
14+// applied call does.
15+const isRedactedUnwrap = (node) =>
16+ isIdentifier(unwrapExpression(node.object), "Redacted") &&
17+ getPropertyName(node.property) === "value";
18+
19+export default {
20+ meta: {
21+ type: "problem",
22+ docs: {
23+ description: "Disallow Redacted.value outside allowlisted credential boundaries.",
24+ },
25+ },
26+ create(context) {
27+ // Tests unwrap to assert the bytes that actually reach a backend — the
28+ // assertion that catches a missed unwrap in the first place.
29+ if (isTestLike(context.filename) || isDeclarationFile(context.filename)) return {};
30+
31+ return {
32+ MemberExpression(node) {
33+ if (isRedactedUnwrap(node)) {
34+ context.report({ node, message });
35+ }
36+ },
37+ };
38+ },
39+};

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t