eslint-plugin-anthropic-security 0.1.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eslint-plugin-anthropic-security",
3
- "version": "0.1.1",
4
- "description": "ESLint plugin for Anthropic SDK security — catches hardcoded API keys in the Claude client options.",
3
+ "version": "0.3.0",
4
+ "description": "ESLint plugin for Anthropic SDK security — catches hardcoded Claude API keys, the browser escape hatch that ships them to every visitor, and system prompts assembled from untrusted input.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
7
7
  "types": "./src/index.d.ts",
@@ -75,7 +75,7 @@
75
75
  }
76
76
  },
77
77
  "dependencies": {
78
- "@interlace/eslint-devkit": "^1.8.0"
78
+ "@interlace/eslint-devkit": "^1.10.0"
79
79
  },
80
80
  "funding": {
81
81
  "type": "github",
package/src/index.js CHANGED
@@ -1,33 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.configs = exports.plugin = exports.rules = exports.noHardcodedApiKey = void 0;
4
- var no_hardcoded_api_key_2 = require("./rules/no-hardcoded-api-key");
5
- Object.defineProperty(exports, "noHardcodedApiKey", { enumerable: true, get: function () { return no_hardcoded_api_key_2.noHardcodedApiKey; } });
6
- exports.rules = {
7
- get 'no-hardcoded-api-key'() { return require("./rules/no-hardcoded-api-key").noHardcodedApiKey; },
8
-
9
- };
10
- exports.plugin = {
11
- meta: {
12
- name: 'eslint-plugin-anthropic-security',
13
- version: '0.1.1',
14
- },
15
- rules: exports.rules,
16
- };
17
- const enabled = {
18
- plugins: {
19
- 'anthropic-security': exports.plugin,
20
- },
21
- rules: {
22
- 'anthropic-security/no-hardcoded-api-key': 'error',
23
- },
24
- };
25
- exports.configs = {
26
- minimal: enabled,
27
- recommended: enabled,
28
- strict: enabled,
29
- };
30
- exports.default = {
31
- ...exports.plugin,
32
- configs: exports.configs,
33
- };
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.configs=exports.plugin=exports.rules=exports.noUntrustedContentInPrompt=exports.noHardcodedApiKey=exports.noBrowserApiKeyExposure=void 0;var no_browser_api_key_exposure_2=require("./rules/no-browser-api-key-exposure");Object.defineProperty(exports,"noBrowserApiKeyExposure",{enumerable:true,get:function(){return no_browser_api_key_exposure_2.noBrowserApiKeyExposure}});var no_hardcoded_api_key_2=require("./rules/no-hardcoded-api-key");Object.defineProperty(exports,"noHardcodedApiKey",{enumerable:true,get:function(){return no_hardcoded_api_key_2.noHardcodedApiKey}});var no_untrusted_content_in_prompt_2=require("./rules/no-untrusted-content-in-prompt");Object.defineProperty(exports,"noUntrustedContentInPrompt",{enumerable:true,get:function(){return no_untrusted_content_in_prompt_2.noUntrustedContentInPrompt}});exports.rules={get"no-browser-api-key-exposure"(){return require("./rules/no-browser-api-key-exposure").noBrowserApiKeyExposure},get"no-hardcoded-api-key"(){return require("./rules/no-hardcoded-api-key").noHardcodedApiKey},get"no-untrusted-content-in-prompt"(){return require("./rules/no-untrusted-content-in-prompt").noUntrustedContentInPrompt}};exports.plugin={meta:{name:"eslint-plugin-anthropic-security",version:"0.3.0"},rules:exports.rules};const enabled={plugins:{"anthropic-security":exports.plugin},rules:{"anthropic-security/no-browser-api-key-exposure":"error","anthropic-security/no-hardcoded-api-key":"error"}};const strict={plugins:{"anthropic-security":exports.plugin},rules:{...enabled.rules,"anthropic-security/no-untrusted-content-in-prompt":"error"}};exports.configs={minimal:enabled,recommended:enabled,strict};exports.default={...exports.plugin,configs:exports.configs};
package/src/oxlint.js CHANGED
@@ -1,3 +1 @@
1
- "use strict";
2
- const index_1 = require("./index");
3
- module.exports = index_1.plugin;
1
+ "use strict";const index_1=require("./index");module.exports=index_1.plugin;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noBrowserApiKeyExposure=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");exports.noBrowserApiKeyExposure=(0,eslint_devkit_1.createBrowserEscapeHatchRule)({ruleName:"no-browser-api-key-exposure",vendor:"Anthropic",modules:["@anthropic-ai"],docsUrl:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-anthropic-security/docs/rules/no-browser-api-key-exposure.md",documentationLink:"https://docs.anthropic.com/en/api/getting-started"});
@@ -1,92 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noHardcodedApiKey = void 0;
4
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
5
- const ANTHROPIC_MODULE_PREFIX = '@anthropic-ai/';
6
- const KEY_PROPS = new Set(['apiKey', 'authToken']);
7
- function readCredential(options) {
8
- for (const prop of options.properties) {
9
- if (prop.type === 'SpreadElement')
10
- return { kind: 'unreadable' };
11
- if (prop.computed)
12
- continue;
13
- const name = prop.key.type === 'Identifier' ? prop.key.name : String(prop.key.value);
14
- if (!KEY_PROPS.has(name))
15
- continue;
16
- if (prop.value.type !== 'Literal')
17
- return { kind: 'safe' };
18
- if (typeof prop.value.value === 'string' && prop.value.value.length > 0) {
19
- return { kind: 'literal', prop: name };
20
- }
21
- return { kind: 'safe' };
22
- }
23
- return { kind: 'safe' };
24
- }
25
- exports.noHardcodedApiKey = (0, eslint_devkit_1.createRule)({
26
- name: 'no-hardcoded-api-key',
27
- meta: {
28
- type: 'problem',
29
- docs: {
30
- url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-anthropic-security/docs/rules/no-hardcoded-api-key.md',
31
- description: 'Forbid a literal API key in the Anthropic client options',
32
- cwe: 'CWE-798',
33
- cvss: 9.1,
34
- },
35
- messages: {
36
- hardcodedApiKey: (0, eslint_devkit_1.formatLLMMessage)({
37
- icon: eslint_devkit_1.MessageIcons.SECURITY,
38
- issueName: 'Hardcoded Anthropic Credential',
39
- cwe: 'CWE-798',
40
- owasp: 'A07:2021',
41
- cvss: 9.1,
42
- description: '{{prop}} is a string literal, so the credential is committed to source control and readable by anyone with repository access',
43
- severity: 'CRITICAL',
44
- compliance: ['SOC2', 'PCI-DSS'],
45
- fix: 'Read it from the environment: new Anthropic({ {{prop}}: process.env.ANTHROPIC_API_KEY })',
46
- documentationLink: 'https://docs.anthropic.com/en/api/getting-started',
47
- }),
48
- },
49
- schema: [],
50
- },
51
- defaultOptions: [],
52
- create(context) {
53
- let importsAnthropic = false;
54
- const candidates = [];
55
- function inspect(node, args) {
56
- const options = args[0];
57
- if (options?.type !== 'ObjectExpression')
58
- return;
59
- const verdict = readCredential(options);
60
- if (verdict.kind === 'literal') {
61
- candidates.push({ node, prop: verdict.prop });
62
- }
63
- }
64
- return {
65
- ImportDeclaration(node) {
66
- if (node.source.value.startsWith(ANTHROPIC_MODULE_PREFIX))
67
- importsAnthropic = true;
68
- },
69
- NewExpression(node) {
70
- inspect(node, [...node.arguments]);
71
- },
72
- CallExpression(node) {
73
- if (node.callee.type === 'Identifier' &&
74
- node.callee.name === 'require' &&
75
- node.arguments[0]?.type === 'Literal' &&
76
- typeof node.arguments[0].value === 'string' &&
77
- node.arguments[0].value.startsWith(ANTHROPIC_MODULE_PREFIX)) {
78
- importsAnthropic = true;
79
- return;
80
- }
81
- inspect(node, [...node.arguments]);
82
- },
83
- 'Program:exit'() {
84
- if (!importsAnthropic)
85
- return;
86
- for (const { node, prop } of candidates) {
87
- context.report({ node, messageId: 'hardcodedApiKey', data: { prop } });
88
- }
89
- },
90
- };
91
- },
92
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noHardcodedApiKey=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");exports.noHardcodedApiKey=(0,eslint_devkit_1.createSdkApiKeyRule)({ruleName:"no-hardcoded-api-key",vendor:"Anthropic",modules:["@anthropic-ai"],keyProps:["apiKey","authToken"],fixTemplate:"new Anthropic({ {{prop}}: process.env.ANTHROPIC_API_KEY })",docsUrl:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-anthropic-security/docs/rules/no-hardcoded-api-key.md",documentationLink:"https://docs.anthropic.com/en/api/getting-started"});
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noUntrustedContentInPrompt=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");exports.noUntrustedContentInPrompt=(0,eslint_devkit_1.createSystemPromptInjectionRule)({ruleName:"no-untrusted-content-in-prompt",vendor:"Anthropic",modules:["@anthropic-ai"],requestPaths:["messages.create","messages.stream"],systemPromptProps:["system"],docsUrl:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-anthropic-security/docs/rules/no-untrusted-content-in-prompt.md",documentationLink:"https://docs.anthropic.com/en/docs/system-prompts"});