@adcp/eslint-plugin 0.0.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/README.md +163 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/rules/no-credential-read-from-args.d.ts +32 -0
- package/dist/rules/no-credential-read-from-args.d.ts.map +1 -0
- package/dist/rules/no-credential-read-from-args.js +360 -0
- package/dist/rules/no-credential-read-from-args.js.map +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# @adcp/eslint-plugin
|
|
2
|
+
|
|
3
|
+
Build-time lint rules for [@adcp/sdk](https://www.npmjs.com/package/@adcp/sdk)
|
|
4
|
+
adopters. Catches the credential-handling antipatterns that the SDK's runtime
|
|
5
|
+
guards already flag, but at code-write time — so the bug never ships.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm i -D @adcp/eslint-plugin eslint
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Wire up
|
|
14
|
+
|
|
15
|
+
**Flat config** (`eslint.config.js`):
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
import adcp from '@adcp/eslint-plugin';
|
|
19
|
+
|
|
20
|
+
export default [
|
|
21
|
+
{
|
|
22
|
+
plugins: { '@adcp': adcp },
|
|
23
|
+
rules: {
|
|
24
|
+
'@adcp/no-credential-read-from-args': 'error',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Legacy `.eslintrc`**:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"plugins": ["@adcp"],
|
|
35
|
+
"extends": ["plugin:@adcp/recommended"]
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Rules
|
|
40
|
+
|
|
41
|
+
### `@adcp/no-credential-read-from-args`
|
|
42
|
+
|
|
43
|
+
Flags reads of credential-shaped keys off the buyer-supplied `args` bag
|
|
44
|
+
inside `extractContext` / `synthesizeFromArgs` platform method
|
|
45
|
+
implementations.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
// ❌ flagged
|
|
49
|
+
const platform = {
|
|
50
|
+
extractContext(args) {
|
|
51
|
+
return { token: args.snap_access_token };
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// ✅ ok — re-derive bearers from authInfo + token cache
|
|
56
|
+
const platform = {
|
|
57
|
+
extractContext(args, ctx) {
|
|
58
|
+
return {
|
|
59
|
+
accountId: args.account_id, // non-secret upstream ID
|
|
60
|
+
token: tokenCache.get(ctx.authInfo), // secret comes from authInfo
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Detection keys on **method name**, not interface type — duck-typed
|
|
67
|
+
`definePlatform` shapes and class methods that don't `implements` the
|
|
68
|
+
interface explicitly are both covered.
|
|
69
|
+
|
|
70
|
+
Credential-name patterns are imported directly from
|
|
71
|
+
[`@adcp/sdk/server`](https://github.com/adcontextprotocol/adcp-client/blob/main/src/lib/server/credential-policy.ts)'s
|
|
72
|
+
`DEFAULT_CREDENTIAL_PATTERNS`. The runtime guard
|
|
73
|
+
(`credentialPolicy: 'authInfo-only'`) and this rule share one regex set —
|
|
74
|
+
adding a pattern to the SDK surfaces it here automatically.
|
|
75
|
+
|
|
76
|
+
#### Option: `additionalPatterns`
|
|
77
|
+
|
|
78
|
+
Adopters who extend the runtime matcher with `credentialPolicy.patterns.extend`
|
|
79
|
+
can mirror the same strings here for lint parity:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
// eslint.config.js
|
|
83
|
+
import adcp from '@adcp/eslint-plugin';
|
|
84
|
+
|
|
85
|
+
export default [
|
|
86
|
+
{
|
|
87
|
+
plugins: { '@adcp': adcp },
|
|
88
|
+
rules: {
|
|
89
|
+
'@adcp/no-credential-read-from-args': [
|
|
90
|
+
'error',
|
|
91
|
+
{ additionalPatterns: ['platform_session_key', 'vendor_bearer'] },
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
// matching runtime config — keep the two lists in sync
|
|
100
|
+
createAdcpServer({
|
|
101
|
+
credentialPolicy: {
|
|
102
|
+
mode: 'authInfo-only',
|
|
103
|
+
patterns: DEFAULT_CREDENTIAL_PATTERNS.extend(['platform_session_key', 'vendor_bearer']),
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Each entry is compiled as `new RegExp(pattern, 'i')` and appended to
|
|
109
|
+
`DEFAULT_CREDENTIAL_PATTERNS`. A fully-replaceable `credentialPolicy.matcher`
|
|
110
|
+
function has no lint analogue — see [Known gaps](#known-gaps).
|
|
111
|
+
|
|
112
|
+
## Known gaps
|
|
113
|
+
|
|
114
|
+
This rule is a code-write-time nudge, not the security boundary. The SDK's
|
|
115
|
+
runtime guard (`credentialPolicy: 'authInfo-only'`) is what enforces the
|
|
116
|
+
contract on the wire. The patterns below intentionally pass the linter
|
|
117
|
+
because catching them at AST time would require cross-function or
|
|
118
|
+
control-flow analysis and the false-positive cost is too high; the
|
|
119
|
+
runtime guard catches all of them at dispatch.
|
|
120
|
+
|
|
121
|
+
- **Aliasing** — `const a = args; a.access_token` (the rule only scans
|
|
122
|
+
reads rooted at the `args` parameter name).
|
|
123
|
+
- **Spread** — `const ctx = { ...args }; ctx.access_token` (same — `ctx`
|
|
124
|
+
isn't `args`).
|
|
125
|
+
- **Helper indirection** — `extractField(args, 'access_token')` (the
|
|
126
|
+
credential string lives in a sibling-function argument; cross-function
|
|
127
|
+
scope is out of scope).
|
|
128
|
+
- **Computed access with a non-literal key** — `args[someVar]` (the rule
|
|
129
|
+
can't statically evaluate the key).
|
|
130
|
+
- **Credential reads inside helper functions called from
|
|
131
|
+
`extractContext`** — only the body of the flagged method itself is
|
|
132
|
+
scanned; helpers it calls are not (cross-function scope).
|
|
133
|
+
- **A fully-replaceable `credentialPolicy.matcher`** — function matchers
|
|
134
|
+
can't be expressed as a regex pattern list, so `additionalPatterns`
|
|
135
|
+
can't mirror them. If you replace the matcher entirely at runtime,
|
|
136
|
+
add explicit `additionalPatterns` entries here for the names you want
|
|
137
|
+
flagged at lint time.
|
|
138
|
+
|
|
139
|
+
For all of the above, rely on `credentialPolicy: 'authInfo-only'` at the
|
|
140
|
+
request boundary — it doesn't care how the read was spelled in source.
|
|
141
|
+
|
|
142
|
+
## Why this exists
|
|
143
|
+
|
|
144
|
+
The SDK already enforces credential discipline at the request boundary via
|
|
145
|
+
`createAdcpServer({ credentialPolicy: 'authInfo-only' })`, which rejects
|
|
146
|
+
incoming requests that smuggle credential-shaped keys through the `args`
|
|
147
|
+
bag. That runtime guard catches mistakes at dispatch time.
|
|
148
|
+
|
|
149
|
+
This plugin catches the same class of mistake earlier — at code-write
|
|
150
|
+
time, in the editor, in CI, before the code is deployed. Same regex set,
|
|
151
|
+
different boundary.
|
|
152
|
+
|
|
153
|
+
See [`docs/guides/CTX-METADATA-SAFETY.md`](https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/CTX-METADATA-SAFETY.md)
|
|
154
|
+
for the broader credential-discipline guidance.
|
|
155
|
+
|
|
156
|
+
## Phase 2
|
|
157
|
+
|
|
158
|
+
Phase 1 (this release) ships the `no-credential-read-from-args` rule.
|
|
159
|
+
Phase 2 (tracked in
|
|
160
|
+
[#1541](https://github.com/adcontextprotocol/adcp-client/issues/1541))
|
|
161
|
+
will add an `adcp doctor` CLI subcommand that wraps the linter for
|
|
162
|
+
adopters who don't already run ESLint, and a suggestion-level
|
|
163
|
+
`prefer-authinfo-credential-channel` rule with autofix hints.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @adcp/eslint-plugin entry.
|
|
3
|
+
*
|
|
4
|
+
* Exposes the rules namespace and a `recommended` config. Adopters wire it
|
|
5
|
+
* as:
|
|
6
|
+
*
|
|
7
|
+
* ```js
|
|
8
|
+
* // .eslintrc.cjs
|
|
9
|
+
* module.exports = {
|
|
10
|
+
* plugins: ['@adcp'],
|
|
11
|
+
* extends: ['plugin:@adcp/recommended'],
|
|
12
|
+
* };
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Or, in flat-config style:
|
|
16
|
+
*
|
|
17
|
+
* ```js
|
|
18
|
+
* import adcp from '@adcp/eslint-plugin';
|
|
19
|
+
*
|
|
20
|
+
* export default [
|
|
21
|
+
* {
|
|
22
|
+
* plugins: { '@adcp': adcp },
|
|
23
|
+
* rules: { '@adcp/no-credential-read-from-args': 'error' },
|
|
24
|
+
* },
|
|
25
|
+
* ];
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare const rules: {
|
|
29
|
+
'no-credential-read-from-args': import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<"credentialReadFromArgs", [({
|
|
30
|
+
additionalPatterns?: string[];
|
|
31
|
+
} | undefined)?], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener> & {
|
|
32
|
+
name: string;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
export declare const configs: {
|
|
36
|
+
recommended: {
|
|
37
|
+
plugins: string[];
|
|
38
|
+
rules: {
|
|
39
|
+
'@adcp/no-credential-read-from-args': string;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,eAAO,MAAM,KAAK;;;;;;CAEjB,CAAC;AAEF,eAAO,MAAM,OAAO;;;;;;;CAOnB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @adcp/eslint-plugin entry.
|
|
4
|
+
*
|
|
5
|
+
* Exposes the rules namespace and a `recommended` config. Adopters wire it
|
|
6
|
+
* as:
|
|
7
|
+
*
|
|
8
|
+
* ```js
|
|
9
|
+
* // .eslintrc.cjs
|
|
10
|
+
* module.exports = {
|
|
11
|
+
* plugins: ['@adcp'],
|
|
12
|
+
* extends: ['plugin:@adcp/recommended'],
|
|
13
|
+
* };
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* Or, in flat-config style:
|
|
17
|
+
*
|
|
18
|
+
* ```js
|
|
19
|
+
* import adcp from '@adcp/eslint-plugin';
|
|
20
|
+
*
|
|
21
|
+
* export default [
|
|
22
|
+
* {
|
|
23
|
+
* plugins: { '@adcp': adcp },
|
|
24
|
+
* rules: { '@adcp/no-credential-read-from-args': 'error' },
|
|
25
|
+
* },
|
|
26
|
+
* ];
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
30
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
31
|
+
};
|
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
+
exports.configs = exports.rules = void 0;
|
|
34
|
+
const no_credential_read_from_args_1 = __importDefault(require("./rules/no-credential-read-from-args"));
|
|
35
|
+
exports.rules = {
|
|
36
|
+
'no-credential-read-from-args': no_credential_read_from_args_1.default,
|
|
37
|
+
};
|
|
38
|
+
exports.configs = {
|
|
39
|
+
recommended: {
|
|
40
|
+
plugins: ['@adcp'],
|
|
41
|
+
rules: {
|
|
42
|
+
'@adcp/no-credential-read-from-args': 'error',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;;;;;;AAEH,wGAA4E;AAE/D,QAAA,KAAK,GAAG;IACnB,8BAA8B,EAAE,sCAAwB;CACzD,CAAC;AAEW,QAAA,OAAO,GAAG;IACrB,WAAW,EAAE;QACX,OAAO,EAAE,CAAC,OAAO,CAAC;QAClB,KAAK,EAAE;YACL,oCAAoC,EAAE,OAAO;SAC9C;KACF;CACF,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flag credential-shaped reads off the buyer-supplied `args` bag inside
|
|
3
|
+
* platform method implementations.
|
|
4
|
+
*
|
|
5
|
+
* Adopters keep reading `args.<vendor>_access_token` (and similar) inside
|
|
6
|
+
* `extractContext` / `synthesizeFromArgs`, silently trusting buyer-supplied
|
|
7
|
+
* identity material. The SDK's `credentialPolicy: 'authInfo-only'` catches
|
|
8
|
+
* this at dispatch; this rule catches it at code-write time.
|
|
9
|
+
*
|
|
10
|
+
* Detection is method-name keyed (`extractContext`, `synthesizeFromArgs`),
|
|
11
|
+
* not interface-type keyed — TypeScript type-graph analysis is unreliable
|
|
12
|
+
* against duck-typed `definePlatform` shapes and class methods that don't
|
|
13
|
+
* `implements` the interface explicitly.
|
|
14
|
+
*
|
|
15
|
+
* Credential-name patterns are the {@link DEFAULT_CREDENTIAL_PATTERNS}
|
|
16
|
+
* exported from `@adcp/sdk/server`. Importing the constant directly keeps
|
|
17
|
+
* the rule and the runtime guard in lockstep — adding a regex to the SDK
|
|
18
|
+
* surfaces it at lint time automatically on next adopter `npm install`.
|
|
19
|
+
*
|
|
20
|
+
* @see adcontextprotocol/adcp-client#1541
|
|
21
|
+
*/
|
|
22
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
23
|
+
type Options = [
|
|
24
|
+
{
|
|
25
|
+
additionalPatterns?: string[];
|
|
26
|
+
}?
|
|
27
|
+
];
|
|
28
|
+
declare const _default: ESLintUtils.RuleModule<"credentialReadFromArgs", Options, unknown, ESLintUtils.RuleListener> & {
|
|
29
|
+
name: string;
|
|
30
|
+
};
|
|
31
|
+
export default _default;
|
|
32
|
+
//# sourceMappingURL=no-credential-read-from-args.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-credential-read-from-args.d.ts","sourceRoot":"","sources":["../../src/rules/no-credential-read-from-args.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,WAAW,EAA4B,MAAM,0BAA0B,CAAC;AAWjF,KAAK,OAAO,GAAG;IACb;QACE,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;KAC/B,CAAC;CACH,CAAC;;;;AA0MF,wBA2JG"}
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Flag credential-shaped reads off the buyer-supplied `args` bag inside
|
|
4
|
+
* platform method implementations.
|
|
5
|
+
*
|
|
6
|
+
* Adopters keep reading `args.<vendor>_access_token` (and similar) inside
|
|
7
|
+
* `extractContext` / `synthesizeFromArgs`, silently trusting buyer-supplied
|
|
8
|
+
* identity material. The SDK's `credentialPolicy: 'authInfo-only'` catches
|
|
9
|
+
* this at dispatch; this rule catches it at code-write time.
|
|
10
|
+
*
|
|
11
|
+
* Detection is method-name keyed (`extractContext`, `synthesizeFromArgs`),
|
|
12
|
+
* not interface-type keyed — TypeScript type-graph analysis is unreliable
|
|
13
|
+
* against duck-typed `definePlatform` shapes and class methods that don't
|
|
14
|
+
* `implements` the interface explicitly.
|
|
15
|
+
*
|
|
16
|
+
* Credential-name patterns are the {@link DEFAULT_CREDENTIAL_PATTERNS}
|
|
17
|
+
* exported from `@adcp/sdk/server`. Importing the constant directly keeps
|
|
18
|
+
* the rule and the runtime guard in lockstep — adding a regex to the SDK
|
|
19
|
+
* surfaces it at lint time automatically on next adopter `npm install`.
|
|
20
|
+
*
|
|
21
|
+
* @see adcontextprotocol/adcp-client#1541
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
25
|
+
const server_1 = require("@adcp/sdk/server");
|
|
26
|
+
const FLAGGED_METHOD_NAMES = new Set(['extractContext', 'synthesizeFromArgs']);
|
|
27
|
+
const createRule = utils_1.ESLintUtils.RuleCreator(name => `https://github.com/adcontextprotocol/adcp-client/blob/main/packages/eslint-plugin/README.md#${name}`);
|
|
28
|
+
/**
|
|
29
|
+
* Build the active pattern set from the SDK defaults plus any
|
|
30
|
+
* adopter-supplied `additionalPatterns`. Adopters using
|
|
31
|
+
* `credentialPolicy.patterns.extend(...)` at runtime mirror the same
|
|
32
|
+
* strings here for lint parity. A fully-replaceable
|
|
33
|
+
* `credentialPolicy.matcher` function has no lint analogue — that's a
|
|
34
|
+
* known gap documented in the README.
|
|
35
|
+
*/
|
|
36
|
+
function buildPatternMatcher(extra) {
|
|
37
|
+
const extraRegexes = extra.map(p => new RegExp(p, 'i'));
|
|
38
|
+
const patterns = [...server_1.DEFAULT_CREDENTIAL_PATTERNS, ...extraRegexes];
|
|
39
|
+
return (name) => patterns.some(rx => rx.test(name));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Return the property name from a MemberExpression's `property` slot when
|
|
43
|
+
* the access is a plain identifier (`foo.bar`) or a string-literal computed
|
|
44
|
+
* access (`foo['bar']`). Returns `null` for dynamic / non-literal accesses
|
|
45
|
+
* — the rule cannot statically evaluate those and stays silent rather than
|
|
46
|
+
* false-positive.
|
|
47
|
+
*/
|
|
48
|
+
function propertyNameOf(node) {
|
|
49
|
+
if (!node.computed && node.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
50
|
+
return node.property.name;
|
|
51
|
+
}
|
|
52
|
+
if (node.computed && node.property.type === utils_1.AST_NODE_TYPES.Literal && typeof node.property.value === 'string') {
|
|
53
|
+
return node.property.value;
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Walk an arbitrary expression and return `true` when it bottoms out on
|
|
59
|
+
* `<argsParamName>` — handles direct reference, `args.<x>`, and
|
|
60
|
+
* `args.<x>.<y>` chains. The rule fires when ANY MemberExpression in
|
|
61
|
+
* the chain reads a credential-shaped key, so detection is per-segment;
|
|
62
|
+
* this helper is the "is this rooted at args?" check used to scope the
|
|
63
|
+
* traversal.
|
|
64
|
+
*/
|
|
65
|
+
function isRootedAtIdentifier(expr, name) {
|
|
66
|
+
let cur = expr;
|
|
67
|
+
while (cur.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
68
|
+
cur = cur.object;
|
|
69
|
+
}
|
|
70
|
+
return cur.type === utils_1.AST_NODE_TYPES.Identifier && cur.name === name;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Extract the parameter name to scan for. The platform-method shape is
|
|
74
|
+
* `(args, ctx) => …` or `(args: SomeArgs, ctx) => …`. We key on the
|
|
75
|
+
* first parameter regardless of declared type — the rule is structural.
|
|
76
|
+
*
|
|
77
|
+
* Handles:
|
|
78
|
+
* - Plain identifier: `(args) => …`
|
|
79
|
+
* - Typed identifier: `(args: Foo) => …`
|
|
80
|
+
* - Default-value: `(args = {}) => …` — unwrap `AssignmentPattern.left`
|
|
81
|
+
* - Destructured first param: `({ access_token }) => …` (flagged via
|
|
82
|
+
* the destructure scan, not via this name)
|
|
83
|
+
* - Destructured with default: `({ access_token } = {}) => …` (same)
|
|
84
|
+
*
|
|
85
|
+
* Returns `null` when the function takes no params or when the first
|
|
86
|
+
* param has no nameable identifier (pure destructure).
|
|
87
|
+
*/
|
|
88
|
+
function firstParamIdentifierName(fn) {
|
|
89
|
+
const first = fn.params[0];
|
|
90
|
+
if (!first)
|
|
91
|
+
return null;
|
|
92
|
+
if (first.type === utils_1.AST_NODE_TYPES.Identifier)
|
|
93
|
+
return first.name;
|
|
94
|
+
if (first.type === utils_1.AST_NODE_TYPES.AssignmentPattern) {
|
|
95
|
+
const left = first.left;
|
|
96
|
+
if (left.type === utils_1.AST_NODE_TYPES.Identifier)
|
|
97
|
+
return left.name;
|
|
98
|
+
}
|
|
99
|
+
// ObjectPattern / ArrayPattern — destructure cases are caught by the
|
|
100
|
+
// ObjectPattern scan below, not here.
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Walk the first parameter's `ObjectPattern` (including when wrapped in
|
|
105
|
+
* an `AssignmentPattern` for default-value forms like
|
|
106
|
+
* `({ access_token } = {})`) and collect every destructured key, including
|
|
107
|
+
* nested patterns. Reports paths like `args.context.access_token` so the
|
|
108
|
+
* error message reflects the depth of the destructure.
|
|
109
|
+
*
|
|
110
|
+
* Recurses through nested `ObjectPattern`s (`{ context: { access_token } }`)
|
|
111
|
+
* and handles renamed properties (`{ access_token: tok }` — fires on
|
|
112
|
+
* `access_token`, the source key, not the alias). `RestElement` is
|
|
113
|
+
* recorded as a binding but never matched against credential patterns:
|
|
114
|
+
* the post-extraction `rest.access_token` read is an aliasing pattern
|
|
115
|
+
* documented as out-of-scope.
|
|
116
|
+
*/
|
|
117
|
+
function destructuredKeys(fn) {
|
|
118
|
+
const first = fn.params[0];
|
|
119
|
+
if (!first)
|
|
120
|
+
return [];
|
|
121
|
+
let pattern = first;
|
|
122
|
+
if (pattern.type === utils_1.AST_NODE_TYPES.AssignmentPattern) {
|
|
123
|
+
pattern = pattern.left;
|
|
124
|
+
}
|
|
125
|
+
if (pattern.type !== utils_1.AST_NODE_TYPES.ObjectPattern)
|
|
126
|
+
return [];
|
|
127
|
+
const out = [];
|
|
128
|
+
collectObjectPatternKeys(pattern, ['args'], out);
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Walk an ObjectPattern's properties, recursing into nested
|
|
133
|
+
* ObjectPatterns. `pathSoFar` accumulates the dotted-path segments used
|
|
134
|
+
* to render the error message (e.g. `['args', 'context']` →
|
|
135
|
+
* `args.context.access_token`).
|
|
136
|
+
*/
|
|
137
|
+
function collectObjectPatternKeys(pattern, pathSoFar, out) {
|
|
138
|
+
for (const prop of pattern.properties) {
|
|
139
|
+
if (prop.type === utils_1.AST_NODE_TYPES.RestElement)
|
|
140
|
+
continue;
|
|
141
|
+
if (prop.type !== utils_1.AST_NODE_TYPES.Property)
|
|
142
|
+
continue;
|
|
143
|
+
const keyName = propertyKeyName(prop.key, prop.computed);
|
|
144
|
+
if (keyName === null)
|
|
145
|
+
continue;
|
|
146
|
+
// `prop.value` is the binding target — either an Identifier (plain or
|
|
147
|
+
// renamed), an ObjectPattern (nested destructure), or an
|
|
148
|
+
// AssignmentPattern wrapping either of those.
|
|
149
|
+
let valueNode = prop.value;
|
|
150
|
+
if (valueNode.type === utils_1.AST_NODE_TYPES.AssignmentPattern) {
|
|
151
|
+
valueNode = valueNode.left;
|
|
152
|
+
}
|
|
153
|
+
if (valueNode.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
|
|
154
|
+
collectObjectPatternKeys(valueNode, [...pathSoFar, keyName], out);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
// Leaf binding — report at the source key (`prop.key`), not the
|
|
158
|
+
// alias (`prop.value`), so the error highlights the credential name
|
|
159
|
+
// the buyer would supply, not the local variable name the adopter
|
|
160
|
+
// chose.
|
|
161
|
+
out.push({
|
|
162
|
+
path: [...pathSoFar, keyName].join('.'),
|
|
163
|
+
node: prop.key,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function propertyKeyName(key, computed) {
|
|
168
|
+
if (!computed && key.type === utils_1.AST_NODE_TYPES.Identifier)
|
|
169
|
+
return key.name;
|
|
170
|
+
if (key.type === utils_1.AST_NODE_TYPES.Literal && typeof key.value === 'string')
|
|
171
|
+
return key.value;
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Return the method name for any function node, when that function is
|
|
176
|
+
* itself the value of a property / method in an object literal or a class
|
|
177
|
+
* body. Returns `null` for free-standing function declarations or
|
|
178
|
+
* arrow-callback expressions whose enclosing context isn't a method
|
|
179
|
+
* binding. The rule fires only when this returns a name in
|
|
180
|
+
* {@link FLAGGED_METHOD_NAMES}.
|
|
181
|
+
*
|
|
182
|
+
* Cases handled:
|
|
183
|
+
* - Object property: `{ extractContext: (args) => … }` / `{ extractContext(args) { … } }`
|
|
184
|
+
* - Class method: `class Foo { extractContext(args) { … } }`
|
|
185
|
+
*
|
|
186
|
+
* `FunctionDeclaration`s (`function extractContext(args) { … }`) are
|
|
187
|
+
* intentionally NOT matched — free-standing functions named
|
|
188
|
+
* `extractContext` are rare and the false-positive cost outweighs the
|
|
189
|
+
* duck-typed coverage win.
|
|
190
|
+
*/
|
|
191
|
+
function methodNameForFunction(fn) {
|
|
192
|
+
if (fn.type === utils_1.AST_NODE_TYPES.FunctionDeclaration) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
const parent = fn.parent;
|
|
196
|
+
if (!parent)
|
|
197
|
+
return null;
|
|
198
|
+
if (parent.type === utils_1.AST_NODE_TYPES.Property) {
|
|
199
|
+
const key = parent.key;
|
|
200
|
+
if (!parent.computed && key.type === utils_1.AST_NODE_TYPES.Identifier)
|
|
201
|
+
return key.name;
|
|
202
|
+
if (key.type === utils_1.AST_NODE_TYPES.Literal && typeof key.value === 'string')
|
|
203
|
+
return key.value;
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
if (parent.type === utils_1.AST_NODE_TYPES.MethodDefinition) {
|
|
207
|
+
const key = parent.key;
|
|
208
|
+
if (!parent.computed && key.type === utils_1.AST_NODE_TYPES.Identifier)
|
|
209
|
+
return key.name;
|
|
210
|
+
if (key.type === utils_1.AST_NODE_TYPES.Literal && typeof key.value === 'string')
|
|
211
|
+
return key.value;
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
exports.default = createRule({
|
|
217
|
+
name: 'no-credential-read-from-args',
|
|
218
|
+
meta: {
|
|
219
|
+
type: 'problem',
|
|
220
|
+
docs: {
|
|
221
|
+
description: 'Disallow reading credential-shaped keys off the buyer-supplied args bag inside extractContext / synthesizeFromArgs.',
|
|
222
|
+
},
|
|
223
|
+
messages: {
|
|
224
|
+
credentialReadFromArgs: 'Reading credential-shaped key {{path}} from `args` trusts buyer-supplied identity. Re-derive bearers per request from `ctx.authInfo` + your token cache; embed only non-secret upstream IDs in `args`.',
|
|
225
|
+
},
|
|
226
|
+
schema: [
|
|
227
|
+
{
|
|
228
|
+
type: 'object',
|
|
229
|
+
properties: {
|
|
230
|
+
additionalPatterns: {
|
|
231
|
+
type: 'array',
|
|
232
|
+
items: { type: 'string' },
|
|
233
|
+
description: 'Extra regex patterns appended to DEFAULT_CREDENTIAL_PATTERNS. Mirror your credentialPolicy.patterns.extend contents here for lint parity.',
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
additionalProperties: false,
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
},
|
|
240
|
+
defaultOptions: [{}],
|
|
241
|
+
create(context) {
|
|
242
|
+
const options = context.options[0] ?? {};
|
|
243
|
+
const isCredentialName = buildPatternMatcher(options.additionalPatterns ?? []);
|
|
244
|
+
/**
|
|
245
|
+
* Stack of `{ argsParamName }` frames for the platform methods we're
|
|
246
|
+
* currently inside. The rule fires only when the stack is non-empty.
|
|
247
|
+
* Nested functions inside a flagged method inherit the parent frame;
|
|
248
|
+
* arrow callbacks that close over `args` still trip the scan.
|
|
249
|
+
*/
|
|
250
|
+
const stack = [];
|
|
251
|
+
function enterFunction(fn) {
|
|
252
|
+
const methodName = methodNameForFunction(fn);
|
|
253
|
+
if (methodName !== null && FLAGGED_METHOD_NAMES.has(methodName)) {
|
|
254
|
+
const argsParamName = firstParamIdentifierName(fn);
|
|
255
|
+
stack.push({ argsParamName });
|
|
256
|
+
// Destructure form: `({ access_token, ... }) => …` (with or
|
|
257
|
+
// without an `AssignmentPattern` default-value wrapper, and
|
|
258
|
+
// including nested patterns) — flag at each leaf destructure
|
|
259
|
+
// site. MemberExpression traversal can't see these because the
|
|
260
|
+
// access never happens.
|
|
261
|
+
for (const { path, node } of destructuredKeys(fn)) {
|
|
262
|
+
// `path` is `args.<...>.<key>`; the leaf segment is the key
|
|
263
|
+
// whose name decides credential-shape.
|
|
264
|
+
const segments = path.split('.');
|
|
265
|
+
const leaf = segments[segments.length - 1];
|
|
266
|
+
if (leaf && isCredentialName(leaf)) {
|
|
267
|
+
context.report({
|
|
268
|
+
node,
|
|
269
|
+
messageId: 'credentialReadFromArgs',
|
|
270
|
+
data: { path },
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function exitFunction(fn) {
|
|
277
|
+
const methodName = methodNameForFunction(fn);
|
|
278
|
+
if (methodName !== null && FLAGGED_METHOD_NAMES.has(methodName)) {
|
|
279
|
+
stack.pop();
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function currentFrame() {
|
|
283
|
+
if (stack.length === 0)
|
|
284
|
+
return null;
|
|
285
|
+
return stack[stack.length - 1] ?? null;
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
FunctionDeclaration: enterFunction,
|
|
289
|
+
'FunctionDeclaration:exit': exitFunction,
|
|
290
|
+
FunctionExpression: enterFunction,
|
|
291
|
+
'FunctionExpression:exit': exitFunction,
|
|
292
|
+
ArrowFunctionExpression: enterFunction,
|
|
293
|
+
'ArrowFunctionExpression:exit': exitFunction,
|
|
294
|
+
VariableDeclarator(node) {
|
|
295
|
+
const frame = currentFrame();
|
|
296
|
+
if (!frame || frame.argsParamName === null)
|
|
297
|
+
return;
|
|
298
|
+
// Only handle `const { ... } = args` shapes — the args parameter
|
|
299
|
+
// name has to appear on the right-hand side as an Identifier.
|
|
300
|
+
if (!node.init || node.init.type !== utils_1.AST_NODE_TYPES.Identifier)
|
|
301
|
+
return;
|
|
302
|
+
if (node.init.name !== frame.argsParamName)
|
|
303
|
+
return;
|
|
304
|
+
if (node.id.type !== utils_1.AST_NODE_TYPES.ObjectPattern)
|
|
305
|
+
return;
|
|
306
|
+
const out = [];
|
|
307
|
+
collectObjectPatternKeys(node.id, [frame.argsParamName], out);
|
|
308
|
+
for (const entry of out) {
|
|
309
|
+
const segments = entry.path.split('.');
|
|
310
|
+
const leaf = segments[segments.length - 1];
|
|
311
|
+
if (leaf && isCredentialName(leaf)) {
|
|
312
|
+
context.report({
|
|
313
|
+
node: entry.node,
|
|
314
|
+
messageId: 'credentialReadFromArgs',
|
|
315
|
+
data: { path: entry.path },
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
MemberExpression(node) {
|
|
321
|
+
const frame = currentFrame();
|
|
322
|
+
if (!frame || frame.argsParamName === null)
|
|
323
|
+
return;
|
|
324
|
+
// Only scan accesses rooted at the args parameter.
|
|
325
|
+
if (!isRootedAtIdentifier(node.object, frame.argsParamName)) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const propName = propertyNameOf(node);
|
|
329
|
+
if (propName === null)
|
|
330
|
+
return;
|
|
331
|
+
if (!isCredentialName(propName))
|
|
332
|
+
return;
|
|
333
|
+
// Reconstruct the dotted path for the error message. Walk from the
|
|
334
|
+
// member expression's leftmost identifier through each step.
|
|
335
|
+
const segments = [];
|
|
336
|
+
let cur = node;
|
|
337
|
+
while (cur.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
338
|
+
const seg = propertyNameOf(cur);
|
|
339
|
+
if (seg === null) {
|
|
340
|
+
// Dynamic segment in the middle of the chain — bail; the error
|
|
341
|
+
// message would be misleading. The current node's read is still
|
|
342
|
+
// flagged on the way back up if it itself matches.
|
|
343
|
+
segments.unshift('<dynamic>');
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
segments.unshift(seg);
|
|
347
|
+
}
|
|
348
|
+
cur = cur.object;
|
|
349
|
+
}
|
|
350
|
+
const path = `${frame.argsParamName}.${segments.join('.')}`;
|
|
351
|
+
context.report({
|
|
352
|
+
node,
|
|
353
|
+
messageId: 'credentialReadFromArgs',
|
|
354
|
+
data: { path },
|
|
355
|
+
});
|
|
356
|
+
},
|
|
357
|
+
};
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
//# sourceMappingURL=no-credential-read-from-args.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-credential-read-from-args.js","sourceRoot":"","sources":["../../src/rules/no-credential-read-from-args.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@adcp/eslint-plugin",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "ESLint rules for AdCP adopters — flags credential-handling antipatterns at lint time. Build-time sibling to the SDK's `credentialPolicy: 'authInfo-only'` runtime guard.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "commonjs",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc -p tsconfig.json",
|
|
13
|
+
"test": "tsx --test --test-force-exit test/*.test.ts",
|
|
14
|
+
"clean": "rm -rf dist/"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"adcp",
|
|
18
|
+
"eslint",
|
|
19
|
+
"eslint-plugin",
|
|
20
|
+
"credentials",
|
|
21
|
+
"security"
|
|
22
|
+
],
|
|
23
|
+
"author": {
|
|
24
|
+
"name": "AdCP Community",
|
|
25
|
+
"email": "maintainers@adcontextprotocol.org",
|
|
26
|
+
"url": "https://adcontextprotocol.org"
|
|
27
|
+
},
|
|
28
|
+
"license": "Apache-2.0",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/adcontextprotocol/adcp-client.git",
|
|
32
|
+
"directory": "packages/eslint-plugin"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/adcontextprotocol/adcp-client#readme",
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/adcontextprotocol/adcp-client/issues"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"eslint": "^8.0.0 || ^9.0.0 || ^10.0.0"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@adcp/sdk": "^7.3.0",
|
|
43
|
+
"@typescript-eslint/utils": "^8.0.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"typescript": "^5.3.0"
|
|
47
|
+
}
|
|
48
|
+
}
|