acidtest 0.1.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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +104 -0
  3. package/dist/index.d.ts +7 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +170 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/layers/code.d.ts +10 -0
  8. package/dist/layers/code.d.ts.map +1 -0
  9. package/dist/layers/code.js +196 -0
  10. package/dist/layers/code.js.map +1 -0
  11. package/dist/layers/crossref.d.ts +10 -0
  12. package/dist/layers/crossref.d.ts.map +1 -0
  13. package/dist/layers/crossref.js +143 -0
  14. package/dist/layers/crossref.js.map +1 -0
  15. package/dist/layers/injection.d.ts +10 -0
  16. package/dist/layers/injection.d.ts.map +1 -0
  17. package/dist/layers/injection.js +88 -0
  18. package/dist/layers/injection.js.map +1 -0
  19. package/dist/layers/permissions.d.ts +10 -0
  20. package/dist/layers/permissions.d.ts.map +1 -0
  21. package/dist/layers/permissions.js +120 -0
  22. package/dist/layers/permissions.js.map +1 -0
  23. package/dist/pattern-loader.d.ts +14 -0
  24. package/dist/pattern-loader.d.ts.map +1 -0
  25. package/dist/pattern-loader.js +50 -0
  26. package/dist/pattern-loader.js.map +1 -0
  27. package/dist/patterns/credential-patterns.json +77 -0
  28. package/dist/patterns/dangerous-imports.json +113 -0
  29. package/dist/patterns/exfiltration-sinks.json +77 -0
  30. package/dist/patterns/obfuscation.json +65 -0
  31. package/dist/patterns/prompt-injection.json +125 -0
  32. package/dist/patterns/sensitive-paths.json +89 -0
  33. package/dist/reporter.d.ts +14 -0
  34. package/dist/reporter.d.ts.map +1 -0
  35. package/dist/reporter.js +175 -0
  36. package/dist/reporter.js.map +1 -0
  37. package/dist/scanner.d.ts +15 -0
  38. package/dist/scanner.d.ts.map +1 -0
  39. package/dist/scanner.js +176 -0
  40. package/dist/scanner.js.map +1 -0
  41. package/dist/scoring.d.ts +23 -0
  42. package/dist/scoring.d.ts.map +1 -0
  43. package/dist/scoring.js +85 -0
  44. package/dist/scoring.js.map +1 -0
  45. package/dist/types.d.ts +112 -0
  46. package/dist/types.d.ts.map +1 -0
  47. package/dist/types.js +5 -0
  48. package/dist/types.js.map +1 -0
  49. package/package.json +46 -0
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Main scanner orchestrator
3
+ * Coordinates all four scanning layers
4
+ */
5
+ import type { ScanResult } from './types.js';
6
+ /**
7
+ * Main scan function
8
+ * Scans a skill directory or SKILL.md file
9
+ */
10
+ export declare function scanSkill(skillPath: string): Promise<ScanResult>;
11
+ /**
12
+ * Scan multiple skills in a directory
13
+ */
14
+ export declare function scanAllSkills(directory: string): Promise<ScanResult[]>;
15
+ //# sourceMappingURL=scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAmB,UAAU,EAAW,MAAM,YAAY,CAAC;AASvE;;;GAGG;AACH,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAmDtE;AAqGD;;GAEG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAmB5E"}
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Main scanner orchestrator
3
+ * Coordinates all four scanning layers
4
+ */
5
+ import { readFileSync, existsSync, statSync } from 'fs';
6
+ import { join, basename, extname } from 'path';
7
+ import { glob } from 'glob';
8
+ import matter from 'gray-matter';
9
+ import { scanPermissions } from './layers/permissions.js';
10
+ import { scanInjection } from './layers/injection.js';
11
+ import { scanCode } from './layers/code.js';
12
+ import { scanCrossReference } from './layers/crossref.js';
13
+ import { calculateScore, determineStatus, generateRecommendation } from './scoring.js';
14
+ const VERSION = '0.1.0';
15
+ /**
16
+ * Main scan function
17
+ * Scans a skill directory or SKILL.md file
18
+ */
19
+ export async function scanSkill(skillPath) {
20
+ // Load the skill
21
+ const skill = await loadSkill(skillPath);
22
+ // Run all four scanning layers
23
+ const layer1 = await scanPermissions(skill);
24
+ const layer2 = await scanInjection(skill);
25
+ const layer3 = await scanCode(skill);
26
+ // Combine findings from layers 1-3 for cross-reference
27
+ const previousFindings = [
28
+ ...layer1.findings,
29
+ ...layer2.findings,
30
+ ...layer3.findings
31
+ ];
32
+ const layer4 = await scanCrossReference(skill, previousFindings);
33
+ // Combine all findings
34
+ const allFindings = [
35
+ ...layer1.findings,
36
+ ...layer2.findings,
37
+ ...layer3.findings,
38
+ ...layer4.findings
39
+ ];
40
+ // Calculate score and status
41
+ const score = calculateScore(allFindings);
42
+ const status = determineStatus(score);
43
+ const recommendation = generateRecommendation(status, allFindings);
44
+ // Build result
45
+ const result = {
46
+ tool: 'acidtest',
47
+ version: VERSION,
48
+ skill: {
49
+ name: skill.name,
50
+ path: skill.path
51
+ },
52
+ score,
53
+ status,
54
+ permissions: {
55
+ bins: skill.metadata.bins,
56
+ env: skill.metadata.env,
57
+ tools: skill.metadata['allowed-tools']
58
+ },
59
+ findings: allFindings,
60
+ recommendation
61
+ };
62
+ return result;
63
+ }
64
+ /**
65
+ * Load a skill from a directory or SKILL.md file
66
+ */
67
+ async function loadSkill(skillPath) {
68
+ let skillMdPath;
69
+ let skillDir;
70
+ // Determine if path is a directory or file
71
+ if (existsSync(skillPath) && statSync(skillPath).isDirectory()) {
72
+ skillDir = skillPath;
73
+ skillMdPath = join(skillPath, 'SKILL.md');
74
+ }
75
+ else if (basename(skillPath) === 'SKILL.md') {
76
+ skillMdPath = skillPath;
77
+ skillDir = join(skillPath, '..');
78
+ }
79
+ else {
80
+ throw new Error('Path must be a skill directory or SKILL.md file');
81
+ }
82
+ // Check if SKILL.md exists
83
+ if (!existsSync(skillMdPath)) {
84
+ throw new Error(`SKILL.md not found at: ${skillMdPath}`);
85
+ }
86
+ // Read and parse SKILL.md
87
+ const skillContent = readFileSync(skillMdPath, 'utf-8');
88
+ const parsed = matter(skillContent);
89
+ // Extract metadata and markdown
90
+ const metadata = parsed.data;
91
+ const markdownContent = parsed.content;
92
+ // Determine skill name
93
+ const skillName = metadata.name ||
94
+ basename(skillDir) ||
95
+ 'unknown-skill';
96
+ // Find all code files (.ts, .js, .mjs, .cjs)
97
+ const codeFiles = await findCodeFiles(skillDir);
98
+ return {
99
+ name: skillName,
100
+ path: skillPath,
101
+ metadata,
102
+ markdownContent,
103
+ codeFiles
104
+ };
105
+ }
106
+ /**
107
+ * Find all code files in skill directory
108
+ */
109
+ async function findCodeFiles(skillDir) {
110
+ const codeFiles = [];
111
+ // Search for .ts, .js, .mjs, .cjs files
112
+ const patterns = [
113
+ join(skillDir, '**/*.ts'),
114
+ join(skillDir, '**/*.js'),
115
+ join(skillDir, '**/*.mjs'),
116
+ join(skillDir, '**/*.cjs')
117
+ ];
118
+ for (const pattern of patterns) {
119
+ try {
120
+ const files = await glob(pattern, {
121
+ ignore: ['**/node_modules/**', '**/dist/**', '**/build/**']
122
+ });
123
+ for (const filePath of files) {
124
+ try {
125
+ const content = readFileSync(filePath, 'utf-8');
126
+ const ext = extname(filePath).slice(1); // Remove leading dot
127
+ // Determine extension type
128
+ let extension;
129
+ if (ext === 'ts')
130
+ extension = 'ts';
131
+ else if (ext === 'mjs')
132
+ extension = 'mjs';
133
+ else if (ext === 'cjs')
134
+ extension = 'cjs';
135
+ else
136
+ extension = 'js';
137
+ codeFiles.push({
138
+ path: filePath,
139
+ content,
140
+ extension
141
+ });
142
+ }
143
+ catch (error) {
144
+ // Skip files that can't be read
145
+ console.warn(`Warning: Could not read file: ${filePath}`);
146
+ }
147
+ }
148
+ }
149
+ catch (error) {
150
+ // Skip pattern if glob fails
151
+ }
152
+ }
153
+ return codeFiles;
154
+ }
155
+ /**
156
+ * Scan multiple skills in a directory
157
+ */
158
+ export async function scanAllSkills(directory) {
159
+ const results = [];
160
+ // Find all SKILL.md files
161
+ const pattern = join(directory, '**/SKILL.md');
162
+ const skillFiles = await glob(pattern, {
163
+ ignore: ['**/node_modules/**']
164
+ });
165
+ for (const skillFile of skillFiles) {
166
+ try {
167
+ const result = await scanSkill(skillFile);
168
+ results.push(result);
169
+ }
170
+ catch (error) {
171
+ console.warn(`Warning: Could not scan skill at ${skillFile}:`, error.message);
172
+ }
173
+ }
174
+ return results;
175
+ }
176
+ //# sourceMappingURL=scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Scoring engine
3
+ * Calculates trust score from security findings
4
+ */
5
+ import type { Finding, Status, Severity } from './types.js';
6
+ /**
7
+ * Calculate trust score from findings
8
+ * Score starts at 100 and deductions are made for each finding
9
+ */
10
+ export declare function calculateScore(findings: Finding[]): number;
11
+ /**
12
+ * Determine overall status from score
13
+ */
14
+ export declare function determineStatus(score: number): Status;
15
+ /**
16
+ * Generate recommendation based on status and findings
17
+ */
18
+ export declare function generateRecommendation(status: Status, findings: Finding[]): string;
19
+ /**
20
+ * Get severity counts from findings
21
+ */
22
+ export declare function getSeverityCounts(findings: Finding[]): Record<Severity, number>;
23
+ //# sourceMappingURL=scoring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scoring.d.ts","sourceRoot":"","sources":["../src/scoring.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAa5D;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAU1D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKrD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,OAAO,EAAE,GAClB,MAAM,CAwCR;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAc/E"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Scoring engine
3
+ * Calculates trust score from security findings
4
+ */
5
+ /**
6
+ * Severity point deductions
7
+ */
8
+ const SEVERITY_POINTS = {
9
+ CRITICAL: 25,
10
+ HIGH: 15,
11
+ MEDIUM: 8,
12
+ LOW: 3,
13
+ INFO: 0
14
+ };
15
+ /**
16
+ * Calculate trust score from findings
17
+ * Score starts at 100 and deductions are made for each finding
18
+ */
19
+ export function calculateScore(findings) {
20
+ let score = 100;
21
+ for (const finding of findings) {
22
+ const deduction = SEVERITY_POINTS[finding.severity];
23
+ score -= deduction;
24
+ }
25
+ // Floor at 0
26
+ return Math.max(0, score);
27
+ }
28
+ /**
29
+ * Determine overall status from score
30
+ */
31
+ export function determineStatus(score) {
32
+ if (score >= 80)
33
+ return 'PASS';
34
+ if (score >= 50)
35
+ return 'WARN';
36
+ if (score >= 20)
37
+ return 'FAIL';
38
+ return 'DANGER';
39
+ }
40
+ /**
41
+ * Generate recommendation based on status and findings
42
+ */
43
+ export function generateRecommendation(status, findings) {
44
+ // Check for critical permission mismatches or exfiltration
45
+ const hasCriticalMismatch = findings.some(f => f.severity === 'CRITICAL' && f.category === 'permission-mismatch');
46
+ const hasExfiltration = findings.some(f => f.category.includes('exfiltration') || f.title.toLowerCase().includes('exfiltrate'));
47
+ const hasPromptInjection = findings.some(f => f.category === 'prompt-injection' && f.severity === 'CRITICAL');
48
+ if (hasCriticalMismatch || hasExfiltration) {
49
+ return 'Do not install. Undeclared data exfiltration detected.';
50
+ }
51
+ if (hasPromptInjection) {
52
+ return 'Do not install. Prompt injection attempt detected.';
53
+ }
54
+ switch (status) {
55
+ case 'DANGER':
56
+ return 'Do not install. Skill presents severe security risks.';
57
+ case 'FAIL':
58
+ return 'Do not install without thorough review. Multiple security issues detected.';
59
+ case 'WARN':
60
+ return 'Review recommended. Some security concerns detected.';
61
+ case 'PASS':
62
+ return findings.length === 0
63
+ ? 'Skill appears safe to install.'
64
+ : 'Skill appears relatively safe, but review findings.';
65
+ default:
66
+ return 'Unable to determine safety.';
67
+ }
68
+ }
69
+ /**
70
+ * Get severity counts from findings
71
+ */
72
+ export function getSeverityCounts(findings) {
73
+ const counts = {
74
+ CRITICAL: 0,
75
+ HIGH: 0,
76
+ MEDIUM: 0,
77
+ LOW: 0,
78
+ INFO: 0
79
+ };
80
+ for (const finding of findings) {
81
+ counts[finding.severity]++;
82
+ }
83
+ return counts;
84
+ }
85
+ //# sourceMappingURL=scoring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scoring.js","sourceRoot":"","sources":["../src/scoring.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;GAEG;AACH,MAAM,eAAe,GAA6B;IAChD,QAAQ,EAAE,EAAE;IACZ,IAAI,EAAE,EAAE;IACR,MAAM,EAAE,CAAC;IACT,GAAG,EAAE,CAAC;IACN,IAAI,EAAE,CAAC;CACR,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,QAAmB;IAChD,IAAI,KAAK,GAAG,GAAG,CAAC;IAEhB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,SAAS,GAAG,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACpD,KAAK,IAAI,SAAS,CAAC;IACrB,CAAC;IAED,aAAa;IACb,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,MAAM,CAAC;IAC/B,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,MAAM,CAAC;IAC/B,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,MAAM,CAAC;IAC/B,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAc,EACd,QAAmB;IAEnB,2DAA2D;IAC3D,MAAM,mBAAmB,GAAG,QAAQ,CAAC,IAAI,CACvC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,IAAI,CAAC,CAAC,QAAQ,KAAK,qBAAqB,CACvE,CAAC;IAEF,MAAM,eAAe,GAAG,QAAQ,CAAC,IAAI,CACnC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,CACzF,CAAC;IAEF,MAAM,kBAAkB,GAAG,QAAQ,CAAC,IAAI,CACtC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,kBAAkB,IAAI,CAAC,CAAC,QAAQ,KAAK,UAAU,CACpE,CAAC;IAEF,IAAI,mBAAmB,IAAI,eAAe,EAAE,CAAC;QAC3C,OAAO,wDAAwD,CAAC;IAClE,CAAC;IAED,IAAI,kBAAkB,EAAE,CAAC;QACvB,OAAO,oDAAoD,CAAC;IAC9D,CAAC;IAED,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,QAAQ;YACX,OAAO,uDAAuD,CAAC;QAEjE,KAAK,MAAM;YACT,OAAO,4EAA4E,CAAC;QAEtF,KAAK,MAAM;YACT,OAAO,sDAAsD,CAAC;QAEhE,KAAK,MAAM;YACT,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAC1B,CAAC,CAAC,gCAAgC;gBAClC,CAAC,CAAC,qDAAqD,CAAC;QAE5D;YACE,OAAO,6BAA6B,CAAC;IACzC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAmB;IACnD,MAAM,MAAM,GAA6B;QACvC,QAAQ,EAAE,CAAC;QACX,IAAI,EAAE,CAAC;QACP,MAAM,EAAE,CAAC;QACT,GAAG,EAAE,CAAC;QACN,IAAI,EAAE,CAAC;KACR,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;IAC7B,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Core TypeScript interfaces for AcidTest security scanner
3
+ */
4
+ export type Severity = 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW' | 'INFO';
5
+ export type Status = 'PASS' | 'WARN' | 'FAIL' | 'DANGER';
6
+ export type Layer = 'permissions' | 'markdown' | 'code' | 'crossref';
7
+ export type PatternMatchType = 'regex' | 'ast' | 'exact';
8
+ /**
9
+ * Pattern match configuration
10
+ */
11
+ export interface PatternMatch {
12
+ type: PatternMatchType;
13
+ value: string;
14
+ flags?: string;
15
+ }
16
+ /**
17
+ * Detection pattern definition
18
+ */
19
+ export interface Pattern {
20
+ id: string;
21
+ name: string;
22
+ description?: string;
23
+ severity: Severity;
24
+ match: PatternMatch;
25
+ layer: Layer;
26
+ category?: string;
27
+ }
28
+ /**
29
+ * Pattern category file structure
30
+ */
31
+ export interface PatternCategory {
32
+ category: string;
33
+ patterns: Pattern[];
34
+ }
35
+ /**
36
+ * Skill metadata from YAML frontmatter
37
+ */
38
+ export interface SkillMetadata {
39
+ name?: string;
40
+ description?: string;
41
+ version?: string;
42
+ env?: string[];
43
+ bins?: string[];
44
+ 'allowed-tools'?: string[];
45
+ [key: string]: unknown;
46
+ }
47
+ /**
48
+ * Parsed skill structure
49
+ */
50
+ export interface Skill {
51
+ name: string;
52
+ path: string;
53
+ metadata: SkillMetadata;
54
+ markdownContent: string;
55
+ codeFiles: CodeFile[];
56
+ }
57
+ /**
58
+ * Code file structure
59
+ */
60
+ export interface CodeFile {
61
+ path: string;
62
+ content: string;
63
+ extension: 'ts' | 'js' | 'mjs' | 'cjs';
64
+ }
65
+ /**
66
+ * Individual security finding
67
+ */
68
+ export interface Finding {
69
+ severity: Severity;
70
+ category: string;
71
+ title: string;
72
+ file?: string;
73
+ line?: number;
74
+ detail: string;
75
+ evidence?: string;
76
+ patternId?: string;
77
+ }
78
+ /**
79
+ * Layer scan result
80
+ */
81
+ export interface LayerResult {
82
+ layer: Layer;
83
+ findings: Finding[];
84
+ }
85
+ /**
86
+ * Complete scan result
87
+ */
88
+ export interface ScanResult {
89
+ tool: string;
90
+ version: string;
91
+ skill: {
92
+ name: string;
93
+ path: string;
94
+ };
95
+ score: number;
96
+ status: Status;
97
+ permissions: {
98
+ bins?: string[];
99
+ env?: string[];
100
+ tools?: string[];
101
+ };
102
+ findings: Finding[];
103
+ recommendation: string;
104
+ }
105
+ /**
106
+ * CLI options
107
+ */
108
+ export interface CliOptions {
109
+ json?: boolean;
110
+ verbose?: boolean;
111
+ }
112
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Core TypeScript interfaces for AcidTest security scanner
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "acidtest",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Security scanner for AI agent skills. Scan before you install.",
6
+ "bin": {
7
+ "acidtest": "./dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc && mkdir -p dist/patterns && cp src/patterns/*.json dist/patterns/",
11
+ "dev": "npm run build && node dist/index.js",
12
+ "watch": "tsc --watch"
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "keywords": [
18
+ "security",
19
+ "agent",
20
+ "agentskills",
21
+ "openclaw",
22
+ "moltbot",
23
+ "skill-scanner",
24
+ "prompt-injection",
25
+ "ai-security"
26
+ ],
27
+ "author": "Currently",
28
+ "license": "MIT",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/currentlycurrently/acidtest"
32
+ },
33
+ "homepage": "https://acidtest.dev",
34
+ "dependencies": {
35
+ "chalk": "^5.3.0",
36
+ "glob": "^10.3.10",
37
+ "gray-matter": "^4.0.3",
38
+ "typescript": "^5.3.3"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^20.11.0"
42
+ },
43
+ "engines": {
44
+ "node": ">=18.0.0"
45
+ }
46
+ }