Skip to content

fix(eslint-plugin): close 3 false-negatives + add additionalPatterns option (#1758)#1762

Merged
bokelley merged 1 commit into
mainfrom
fix/eslint-plugin-false-negatives
May 15, 2026
Merged

fix(eslint-plugin): close 3 false-negatives + add additionalPatterns option (#1758)#1762
bokelley merged 1 commit into
mainfrom
fix/eslint-plugin-false-negatives

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

Closes #1758.

Summary

Closes the three false-negative gaps in no-credential-read-from-args (shipped in #1752, on main but not yet released — the existing minor changeset is still pending) and adds an additionalPatterns rule option for adopters extending the runtime matcher.

Bug-class fixes

  1. Destructure-then-readextractContext(args) { const { access_token } = args; ... } previously passed. New VariableDeclarator visitor handles this, recursing through nested ObjectPatterns and reporting at the source key (not the alias). Shares collectObjectPatternKeys with the first-param destructure scanner so both forms produce identical paths.
  2. AssignmentPattern default-value silently disabled scanningextractContext(args = {}) previously made firstParamIdentifierName return null, which disabled the MemberExpression scanner. Now unwraps AssignmentPattern.left to recover the identifier name. extractContext({ access_token } = {}) likewise — destructuredKeys unwraps the AssignmentPattern wrapper before pattern-walking.
  3. Nested destructure in first paramextractContext({ context: { access_token } }) previously only inspected the top-level pattern. collectObjectPatternKeys now recurses into nested ObjectPatterns and tracks the full path so the error message reads args.context.access_token. Nested renames ({ context: { access_token: tok } }) fire on the source key. RestElement skipped (documented gap).

False-positive fix

Free-standing function extractContext(args) { return args.access_token; } no longer fires. Dropped the FunctionDeclaration branch from methodNameForFunction — only Property (object literals) and MethodDefinition (class bodies) are matched. Class methods still fire (three regression tests cover this).

Rule option

New additionalPatterns: string[] option. Each entry compiled as new RegExp(p, 'i') and appended to DEFAULT_CREDENTIAL_PATTERNS. Adopters mirror their credentialPolicy.patterns.extend(...) runtime config for lint parity. Fully-replaceable credentialPolicy.matcher functions documented as a known gap (no regex analogue).

README

Adds Known gaps section listing aliasing / spread / helper-indirection / computed-key / cross-function bypasses that pass the linter by design — runtime credentialPolicy: 'authInfo-only' is the security boundary. Adds an additionalPatterns option example with matching runtime config.

Test count

14 → 30. New invalid cases: destructure-then-read (incl. rename, with-default, with-leaf-default), default-value param, destructure-with-default first param, nested destructure (plain + rename), additionalPatterns (member + destructure), class-method regression for each of the three fixes. New valid cases: free-standing function extractContext, spread aliasing, plain aliasing, destructure-then-read of a non-credential key.

Changeset

patch on @adcp/eslint-plugin.

Test plan

  • cd packages/eslint-plugin && npm test — 30/30 pass
  • cd packages/eslint-plugin && npm run build — clean
  • npm run format:check — clean
  • All 14 pre-existing tests still pass (verified via test count delta)

🤖 Generated with Claude Code

…option (#1758)

Three bug-class false-negatives in `no-credential-read-from-args` now
fire:

- Destructure-then-read: `extractContext(args) { const { access_token }
  = args; ... }` (new `VariableDeclarator` visitor; recurses through
  nested `ObjectPattern`s and reports at the source key, not the alias).
- `AssignmentPattern` default-value silently disabled scanning:
  `extractContext(args = {}) { args.access_token }` now unwraps
  `AssignmentPattern.left` in `firstParamIdentifierName`.
  `extractContext({ access_token } = {})` likewise.
- Nested destructure in first param: `extractContext({ context: {
  access_token } })` now recurses and reports the full dotted path
  (`args.context.access_token`). Nested renames fire on the source key.

New `additionalPatterns` rule option lets adopters who extend the
runtime matcher via `credentialPolicy.patterns.extend(...)` mirror the
same strings at lint time. Compiled with the `i` flag and appended to
`DEFAULT_CREDENTIAL_PATTERNS`. Fully-replaceable `credentialPolicy.matcher`
functions stay a documented gap.

False-positive fix: free-standing `function extractContext(args) { ... }`
no longer fires — only `Property` and `MethodDefinition` method
bindings are matched. Class methods still fire (regression-covered).

README adds a `Known gaps` section documenting aliasing / spread /
helper-indirection / computed-key / cross-function bypasses that pass
the linter by design.

Test count: 14 → 30.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley bokelley merged commit 569e97c into main May 15, 2026
10 checks passed
@bokelley bokelley deleted the fix/eslint-plugin-false-negatives branch May 15, 2026 14:04
bokelley added a commit that referenced this pull request May 15, 2026
…1766) (#1767)

* fix(ci): run packages/eslint-plugin/ tests as part of root npm test (#1766)

Chain `npm test --workspaces --if-present` onto the root test script so
CI's Test & Build job (which runs `npm test`) exercises the ESLint
plugin's rule tests. Plugin tests landed in PR #1762 but weren't running
in CI, so plugin regressions could land on main silently.

`--workspaces --if-present` is forward-compatible: any future workspace
that adds a `test` script is automatically picked up. `packages/client-shim`
has no `test` script and is correctly skipped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(ci): avoid root test recursion by targeting plugin workspace explicitly

Root package.json declares workspaces as [".", "packages/*"], so
`npm test --workspaces` re-invokes the root test script, which recurses
indefinitely until CI cancels. Switch to an explicit
`--workspace=packages/eslint-plugin` invocation so the plugin tests run
exactly once. Future workspaces with test scripts get appended explicitly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(eslint-plugin): close no-credential-read-from-args false-negatives (destructure, default-param, nested destructure) + custom-patterns option

1 participant