acidtest 0.8.0 → 1.0.1

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 (96) hide show
  1. package/.github/workflows/acidtest-pr-comment.yml +219 -0
  2. package/README.md +176 -36
  3. package/dist/analysis/dataflow-graph.d.ts +19 -0
  4. package/dist/analysis/dataflow-graph.d.ts.map +1 -0
  5. package/dist/analysis/dataflow-graph.js +365 -0
  6. package/dist/analysis/dataflow-graph.js.map +1 -0
  7. package/dist/analysis/dataflow-types.d.ts +86 -0
  8. package/dist/analysis/dataflow-types.d.ts.map +1 -0
  9. package/dist/analysis/dataflow-types.js +8 -0
  10. package/dist/analysis/dataflow-types.js.map +1 -0
  11. package/dist/analysis/dataflow.test.d.ts +7 -0
  12. package/dist/analysis/dataflow.test.d.ts.map +1 -0
  13. package/dist/analysis/dataflow.test.js +257 -0
  14. package/dist/analysis/dataflow.test.js.map +1 -0
  15. package/dist/analysis/taint-propagation.d.ts +30 -0
  16. package/dist/analysis/taint-propagation.d.ts.map +1 -0
  17. package/dist/analysis/taint-propagation.js +207 -0
  18. package/dist/analysis/taint-propagation.js.map +1 -0
  19. package/dist/index.js +1 -1
  20. package/dist/layers/code.d.ts +1 -1
  21. package/dist/layers/code.d.ts.map +1 -1
  22. package/dist/layers/code.js +247 -3
  23. package/dist/layers/code.js.map +1 -1
  24. package/dist/layers/code.test.js +196 -0
  25. package/dist/layers/code.test.js.map +1 -1
  26. package/dist/layers/crossref.d.ts.map +1 -1
  27. package/dist/layers/crossref.js +7 -0
  28. package/dist/layers/crossref.js.map +1 -1
  29. package/dist/layers/dataflow.d.ts +29 -0
  30. package/dist/layers/dataflow.d.ts.map +1 -0
  31. package/dist/layers/dataflow.js +217 -0
  32. package/dist/layers/dataflow.js.map +1 -0
  33. package/dist/layers/injection.d.ts.map +1 -1
  34. package/dist/layers/injection.js +8 -1
  35. package/dist/layers/injection.js.map +1 -1
  36. package/dist/layers/permissions.d.ts.map +1 -1
  37. package/dist/layers/permissions.js +7 -0
  38. package/dist/layers/permissions.js.map +1 -1
  39. package/dist/mcp-server.js +1 -1
  40. package/dist/parsers/parser-interface.d.ts +31 -0
  41. package/dist/parsers/parser-interface.d.ts.map +1 -0
  42. package/dist/parsers/parser-interface.js +6 -0
  43. package/dist/parsers/parser-interface.js.map +1 -0
  44. package/dist/parsers/parsers.test.d.ts +5 -0
  45. package/dist/parsers/parsers.test.d.ts.map +1 -0
  46. package/dist/parsers/parsers.test.js +111 -0
  47. package/dist/parsers/parsers.test.js.map +1 -0
  48. package/dist/parsers/python-parser.d.ts +18 -0
  49. package/dist/parsers/python-parser.d.ts.map +1 -0
  50. package/dist/parsers/python-parser.js +120 -0
  51. package/dist/parsers/python-parser.js.map +1 -0
  52. package/dist/parsers/typescript-parser.d.ts +16 -0
  53. package/dist/parsers/typescript-parser.d.ts.map +1 -0
  54. package/dist/parsers/typescript-parser.js +112 -0
  55. package/dist/parsers/typescript-parser.js.map +1 -0
  56. package/dist/patterns/dangerous-calls-python.json +220 -0
  57. package/dist/patterns/dangerous-imports-python.json +256 -0
  58. package/dist/patterns/insecure-crypto.json +163 -0
  59. package/dist/patterns/prototype-pollution.json +72 -0
  60. package/dist/patterns/python-deserialization.json +94 -0
  61. package/dist/patterns/regex-dos.json +50 -0
  62. package/dist/patterns/sql-injection.json +91 -0
  63. package/dist/patterns/xss-injection.json +115 -0
  64. package/dist/reporter.d.ts.map +1 -1
  65. package/dist/reporter.js +6 -0
  66. package/dist/reporter.js.map +1 -1
  67. package/dist/scanner.d.ts +1 -1
  68. package/dist/scanner.d.ts.map +1 -1
  69. package/dist/scanner.js +48 -5
  70. package/dist/scanner.js.map +1 -1
  71. package/dist/scanner.test.js +31 -0
  72. package/dist/scanner.test.js.map +1 -1
  73. package/dist/schemas/pattern.schema.json +139 -0
  74. package/dist/test-corpus/validate-corpus.d.ts +7 -0
  75. package/dist/test-corpus/validate-corpus.d.ts.map +1 -0
  76. package/dist/test-corpus/validate-corpus.js +341 -0
  77. package/dist/test-corpus/validate-corpus.js.map +1 -0
  78. package/dist/types.d.ts +4 -2
  79. package/dist/types.d.ts.map +1 -1
  80. package/dist/validation/pattern-validator.d.ts +34 -0
  81. package/dist/validation/pattern-validator.d.ts.map +1 -0
  82. package/dist/validation/pattern-validator.js +168 -0
  83. package/dist/validation/pattern-validator.js.map +1 -0
  84. package/dist/validation/pattern-validator.test.d.ts +5 -0
  85. package/dist/validation/pattern-validator.test.d.ts.map +1 -0
  86. package/dist/validation/pattern-validator.test.js +222 -0
  87. package/dist/validation/pattern-validator.test.js.map +1 -0
  88. package/dist/validation/validate-patterns.d.ts +6 -0
  89. package/dist/validation/validate-patterns.d.ts.map +1 -0
  90. package/dist/validation/validate-patterns.js +55 -0
  91. package/dist/validation/validate-patterns.js.map +1 -0
  92. package/package.json +11 -4
  93. package/test-fixtures/fixture-no-manifest-node/README.md +4 -0
  94. package/test-fixtures/fixture-no-manifest-node/index.js +24 -0
  95. package/test-fixtures/fixture-no-manifest-python/README.md +4 -0
  96. package/test-fixtures/fixture-no-manifest-python/app.py +24 -0
@@ -0,0 +1,168 @@
1
+ /**
2
+ * JSON Schema-based validator for AcidTest pattern files
3
+ */
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+ import { fileURLToPath } from 'url';
7
+ import { createRequire } from 'module';
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+ const require = createRequire(import.meta.url);
10
+ // Load dependencies via require to avoid ESM issues
11
+ const Ajv = require('ajv');
12
+ const addFormats = require('ajv-formats');
13
+ // Load the JSON Schema
14
+ const schemaPath = path.join(__dirname, '../schemas/pattern.schema.json');
15
+ const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf-8'));
16
+ // Initialize AJV with options
17
+ const ajv = new Ajv({
18
+ allErrors: true,
19
+ verbose: true,
20
+ strict: true,
21
+ allowUnionTypes: true,
22
+ });
23
+ // Add format validation
24
+ addFormats(ajv);
25
+ // Compile the schema
26
+ const validate = ajv.compile(schema);
27
+ /**
28
+ * Validate a single pattern file
29
+ * @param filePath Path to the pattern JSON file
30
+ * @returns ValidationResult with validation status and errors
31
+ */
32
+ export function validatePattern(filePath) {
33
+ try {
34
+ // Read and parse the file
35
+ const fileContent = fs.readFileSync(filePath, 'utf-8');
36
+ let data;
37
+ try {
38
+ data = JSON.parse(fileContent);
39
+ }
40
+ catch (parseError) {
41
+ return {
42
+ valid: false,
43
+ errors: [
44
+ {
45
+ message: `JSON Parse Error: ${parseError instanceof Error ? parseError.message : 'Invalid JSON'}`,
46
+ path: filePath,
47
+ },
48
+ ],
49
+ file: filePath,
50
+ };
51
+ }
52
+ // Validate against schema
53
+ const isValid = validate(data);
54
+ if (!isValid && validate.errors) {
55
+ const errors = validate.errors.map((error) => ({
56
+ message: formatErrorMessage(error),
57
+ path: error.instancePath || '/',
58
+ keyword: error.keyword,
59
+ params: error.params,
60
+ }));
61
+ return {
62
+ valid: false,
63
+ errors,
64
+ file: filePath,
65
+ };
66
+ }
67
+ // Additional validation: Check for duplicate pattern IDs
68
+ const patternData = data;
69
+ const seenIds = new Set();
70
+ const duplicateErrors = [];
71
+ patternData.patterns.forEach((pattern, index) => {
72
+ if (seenIds.has(pattern.id)) {
73
+ duplicateErrors.push({
74
+ message: `Duplicate pattern ID "${pattern.id}" found at index ${index}`,
75
+ path: `/patterns/${index}/id`,
76
+ });
77
+ }
78
+ seenIds.add(pattern.id);
79
+ // Validate regex patterns can compile
80
+ if (pattern.match.type === 'regex') {
81
+ try {
82
+ new RegExp(pattern.match.value, pattern.match.flags || '');
83
+ }
84
+ catch (regexError) {
85
+ duplicateErrors.push({
86
+ message: `Invalid regex pattern at /patterns/${index}: ${regexError instanceof Error ? regexError.message : 'Invalid regex'}`,
87
+ path: `/patterns/${index}/match/value`,
88
+ });
89
+ }
90
+ }
91
+ });
92
+ if (duplicateErrors.length > 0) {
93
+ return {
94
+ valid: false,
95
+ errors: duplicateErrors,
96
+ file: filePath,
97
+ patternCount: patternData.patterns.length,
98
+ };
99
+ }
100
+ return {
101
+ valid: true,
102
+ errors: [],
103
+ file: filePath,
104
+ patternCount: patternData.patterns.length,
105
+ };
106
+ }
107
+ catch (error) {
108
+ return {
109
+ valid: false,
110
+ errors: [
111
+ {
112
+ message: `Unexpected error: ${error instanceof Error ? error.message : 'Unknown error'}`,
113
+ path: filePath,
114
+ },
115
+ ],
116
+ file: filePath,
117
+ };
118
+ }
119
+ }
120
+ /**
121
+ * Validate all pattern files in a directory
122
+ * @param patternsDir Directory containing pattern JSON files
123
+ * @returns Array of ValidationResult for each file
124
+ */
125
+ export function validateAllPatterns(patternsDir) {
126
+ try {
127
+ const files = fs.readdirSync(patternsDir).filter((f) => f.endsWith('.json'));
128
+ return files.map((file) => validatePattern(path.join(patternsDir, file)));
129
+ }
130
+ catch (error) {
131
+ return [
132
+ {
133
+ valid: false,
134
+ errors: [
135
+ {
136
+ message: `Failed to read patterns directory: ${error instanceof Error ? error.message : 'Unknown error'}`,
137
+ path: patternsDir,
138
+ },
139
+ ],
140
+ },
141
+ ];
142
+ }
143
+ }
144
+ /**
145
+ * Format AJV error into human-readable message
146
+ */
147
+ function formatErrorMessage(error) {
148
+ const path = error.instancePath || '/';
149
+ switch (error.keyword) {
150
+ case 'required':
151
+ return `Missing required field: ${error.params?.missingProperty} at ${path}`;
152
+ case 'enum':
153
+ return `Invalid value at ${path}. Must be one of: ${JSON.stringify(error.params?.allowedValues)}`;
154
+ case 'pattern':
155
+ return `Value at ${path} does not match required pattern: ${error.params?.pattern}`;
156
+ case 'minLength':
157
+ return `Value at ${path} is too short (minimum length: ${error.params?.limit})`;
158
+ case 'minItems':
159
+ return `Array at ${path} has too few items (minimum: ${error.params?.limit})`;
160
+ case 'type':
161
+ return `Invalid type at ${path}. Expected: ${error.params?.type}`;
162
+ case 'additionalProperties':
163
+ return `Unexpected property "${error.params?.additionalProperty}" at ${path}`;
164
+ default:
165
+ return `${error.message || 'Validation error'} at ${path}`;
166
+ }
167
+ }
168
+ //# sourceMappingURL=pattern-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern-validator.js","sourceRoot":"","sources":["../../src/validation/pattern-validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Tests for pattern validation
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=pattern-validator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern-validator.test.d.ts","sourceRoot":"","sources":["../../src/validation/pattern-validator.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Tests for pattern validation
3
+ */
4
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
5
+ import { mkdirSync, writeFileSync, rmSync } from 'fs';
6
+ import { join } from 'path';
7
+ import { validatePattern, validateAllPatterns } from './pattern-validator.js';
8
+ describe('Pattern Validator', () => {
9
+ const testDir = join(process.cwd(), 'test-validation-temp');
10
+ beforeEach(() => {
11
+ mkdirSync(testDir, { recursive: true });
12
+ });
13
+ afterEach(() => {
14
+ rmSync(testDir, { recursive: true, force: true });
15
+ });
16
+ it('should validate a valid pattern file', () => {
17
+ const validPattern = {
18
+ category: 'test-patterns',
19
+ patterns: [
20
+ {
21
+ id: 'tp-001',
22
+ name: 'test-pattern',
23
+ description: 'A test pattern',
24
+ severity: 'HIGH',
25
+ match: {
26
+ type: 'regex',
27
+ value: 'test.*pattern',
28
+ flags: 'i'
29
+ },
30
+ layer: 'markdown'
31
+ }
32
+ ]
33
+ };
34
+ const filePath = join(testDir, 'valid-pattern.json');
35
+ writeFileSync(filePath, JSON.stringify(validPattern, null, 2));
36
+ const result = validatePattern(filePath);
37
+ expect(result.valid).toBe(true);
38
+ expect(result.errors).toHaveLength(0);
39
+ expect(result.patternCount).toBe(1);
40
+ });
41
+ it('should fail validation when required field is missing (name)', () => {
42
+ const invalidPattern = {
43
+ category: 'test-patterns',
44
+ patterns: [
45
+ {
46
+ id: 'tp-001',
47
+ // name is missing
48
+ severity: 'HIGH',
49
+ match: {
50
+ type: 'regex',
51
+ value: 'test.*pattern'
52
+ },
53
+ layer: 'markdown'
54
+ }
55
+ ]
56
+ };
57
+ const filePath = join(testDir, 'missing-name.json');
58
+ writeFileSync(filePath, JSON.stringify(invalidPattern, null, 2));
59
+ const result = validatePattern(filePath);
60
+ expect(result.valid).toBe(false);
61
+ expect(result.errors.length).toBeGreaterThan(0);
62
+ expect(result.errors[0].message).toMatch(/name/i);
63
+ });
64
+ it('should fail validation with invalid severity enum', () => {
65
+ const invalidPattern = {
66
+ category: 'test-patterns',
67
+ patterns: [
68
+ {
69
+ id: 'tp-001',
70
+ name: 'test-pattern',
71
+ severity: 'INVALID_SEVERITY', // Invalid severity
72
+ match: {
73
+ type: 'regex',
74
+ value: 'test.*pattern'
75
+ },
76
+ layer: 'markdown'
77
+ }
78
+ ]
79
+ };
80
+ const filePath = join(testDir, 'invalid-severity.json');
81
+ writeFileSync(filePath, JSON.stringify(invalidPattern, null, 2));
82
+ const result = validatePattern(filePath);
83
+ expect(result.valid).toBe(false);
84
+ expect(result.errors.length).toBeGreaterThan(0);
85
+ expect(result.errors[0].message).toMatch(/(severity|enum)/i);
86
+ });
87
+ it('should fail validation with invalid match type', () => {
88
+ const invalidPattern = {
89
+ category: 'test-patterns',
90
+ patterns: [
91
+ {
92
+ id: 'tp-001',
93
+ name: 'test-pattern',
94
+ severity: 'HIGH',
95
+ match: {
96
+ type: 'invalid-type', // Invalid match type
97
+ value: 'test.*pattern'
98
+ },
99
+ layer: 'markdown'
100
+ }
101
+ ]
102
+ };
103
+ const filePath = join(testDir, 'invalid-match-type.json');
104
+ writeFileSync(filePath, JSON.stringify(invalidPattern, null, 2));
105
+ const result = validatePattern(filePath);
106
+ expect(result.valid).toBe(false);
107
+ expect(result.errors.length).toBeGreaterThan(0);
108
+ expect(result.errors[0].message).toMatch(/(match|type|enum)/i);
109
+ });
110
+ it('should validate all current pattern files in src/patterns/', () => {
111
+ const patternsDir = join(process.cwd(), 'src', 'patterns');
112
+ const results = validateAllPatterns(patternsDir);
113
+ // All pattern files should be valid
114
+ const allValid = results.every((result) => result.valid);
115
+ expect(allValid).toBe(true);
116
+ // Should have validated at least 6 files (based on current patterns)
117
+ expect(results.length).toBeGreaterThanOrEqual(6);
118
+ // Display any errors for debugging
119
+ const errors = results.filter((r) => !r.valid);
120
+ if (errors.length > 0) {
121
+ console.error('Pattern validation errors:');
122
+ errors.forEach((error) => {
123
+ console.error(` File: ${error.file}`);
124
+ error.errors.forEach((e) => {
125
+ console.error(` - ${e.message}`);
126
+ });
127
+ });
128
+ }
129
+ });
130
+ it('should detect duplicate pattern IDs in the same file', () => {
131
+ const duplicatePattern = {
132
+ category: 'test-patterns',
133
+ patterns: [
134
+ {
135
+ id: 'tp-001',
136
+ name: 'first-pattern',
137
+ severity: 'HIGH',
138
+ match: {
139
+ type: 'regex',
140
+ value: 'test1'
141
+ },
142
+ layer: 'markdown'
143
+ },
144
+ {
145
+ id: 'tp-001', // Duplicate ID
146
+ name: 'second-pattern',
147
+ severity: 'HIGH',
148
+ match: {
149
+ type: 'regex',
150
+ value: 'test2'
151
+ },
152
+ layer: 'markdown'
153
+ }
154
+ ]
155
+ };
156
+ const filePath = join(testDir, 'duplicate-ids.json');
157
+ writeFileSync(filePath, JSON.stringify(duplicatePattern, null, 2));
158
+ const result = validatePattern(filePath);
159
+ expect(result.valid).toBe(false);
160
+ expect(result.errors.length).toBeGreaterThan(0);
161
+ expect(result.errors[0].message).toMatch(/duplicate/i);
162
+ });
163
+ it('should detect invalid regex patterns', () => {
164
+ const invalidRegexPattern = {
165
+ category: 'test-patterns',
166
+ patterns: [
167
+ {
168
+ id: 'tp-001',
169
+ name: 'invalid-regex',
170
+ severity: 'HIGH',
171
+ match: {
172
+ type: 'regex',
173
+ value: '[invalid(regex' // Invalid regex
174
+ },
175
+ layer: 'markdown'
176
+ }
177
+ ]
178
+ };
179
+ const filePath = join(testDir, 'invalid-regex.json');
180
+ writeFileSync(filePath, JSON.stringify(invalidRegexPattern, null, 2));
181
+ const result = validatePattern(filePath);
182
+ expect(result.valid).toBe(false);
183
+ expect(result.errors.length).toBeGreaterThan(0);
184
+ expect(result.errors[0].message).toMatch(/regex/i);
185
+ });
186
+ it('should validate remediation structure when present', () => {
187
+ const patternWithRemediation = {
188
+ category: 'test-patterns',
189
+ patterns: [
190
+ {
191
+ id: 'tp-001',
192
+ name: 'test-pattern',
193
+ severity: 'HIGH',
194
+ match: {
195
+ type: 'regex',
196
+ value: 'test'
197
+ },
198
+ layer: 'code',
199
+ remediation: {
200
+ title: 'Fix this issue',
201
+ suggestions: [
202
+ 'Do this',
203
+ 'Then do that'
204
+ ],
205
+ autofix: true,
206
+ fixAction: {
207
+ type: 'replace',
208
+ pattern: 'old',
209
+ replacement: 'new'
210
+ }
211
+ }
212
+ }
213
+ ]
214
+ };
215
+ const filePath = join(testDir, 'with-remediation.json');
216
+ writeFileSync(filePath, JSON.stringify(patternWithRemediation, null, 2));
217
+ const result = validatePattern(filePath);
218
+ expect(result.valid).toBe(true);
219
+ expect(result.errors).toHaveLength(0);
220
+ });
221
+ });
222
+ //# sourceMappingURL=pattern-validator.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern-validator.test.js","sourceRoot":"","sources":["../../src/validation/pattern-validator.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAG9E,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,sBAAsB,CAAC,CAAC;IAE5D,UAAU,CAAC,GAAG,EAAE;QACd,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,YAAY,GAAoB;YACpC,QAAQ,EAAE,eAAe;YACzB,QAAQ,EAAE;gBACR;oBACE,EAAE,EAAE,QAAQ;oBACZ,IAAI,EAAE,cAAc;oBACpB,WAAW,EAAE,gBAAgB;oBAC7B,QAAQ,EAAE,MAAM;oBAChB,KAAK,EAAE;wBACL,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,eAAe;wBACtB,KAAK,EAAE,GAAG;qBACX;oBACD,KAAK,EAAE,UAAU;iBAClB;aACF;SACF,CAAC;QAEF,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;QACrD,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAE/D,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;QAEzC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,cAAc,GAAG;YACrB,QAAQ,EAAE,eAAe;YACzB,QAAQ,EAAE;gBACR;oBACE,EAAE,EAAE,QAAQ;oBACZ,kBAAkB;oBAClB,QAAQ,EAAE,MAAM;oBAChB,KAAK,EAAE;wBACL,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,eAAe;qBACvB;oBACD,KAAK,EAAE,UAAU;iBAClB;aACF;SACF,CAAC;QAEF,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;QACpD,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAEjE,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;QAEzC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,cAAc,GAAG;YACrB,QAAQ,EAAE,eAAe;YACzB,QAAQ,EAAE;gBACR;oBACE,EAAE,EAAE,QAAQ;oBACZ,IAAI,EAAE,cAAc;oBACpB,QAAQ,EAAE,kBAAkB,EAAE,mBAAmB;oBACjD,KAAK,EAAE;wBACL,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,eAAe;qBACvB;oBACD,KAAK,EAAE,UAAU;iBAClB;aACF;SACF,CAAC;QAEF,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,uBAAuB,CAAC,CAAC;QACxD,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAEjE,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;QAEzC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,cAAc,GAAG;YACrB,QAAQ,EAAE,eAAe;YACzB,QAAQ,EAAE;gBACR;oBACE,EAAE,EAAE,QAAQ;oBACZ,IAAI,EAAE,cAAc;oBACpB,QAAQ,EAAE,MAAM;oBAChB,KAAK,EAAE;wBACL,IAAI,EAAE,cAAc,EAAE,qBAAqB;wBAC3C,KAAK,EAAE,eAAe;qBACvB;oBACD,KAAK,EAAE,UAAU;iBAClB;aACF;SACF,CAAC;QAEF,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,yBAAyB,CAAC,CAAC;QAC1D,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAEjE,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;QAEzC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;QAC3D,MAAM,OAAO,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;QAEjD,oCAAoC;QACpC,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACzD,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE5B,qEAAqE;QACrE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QAEjD,mCAAmC;QACnC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC/C,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;YAC5C,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;gBACvB,OAAO,CAAC,KAAK,CAAC,WAAW,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;gBACvC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;oBACzB,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;gBACtC,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,gBAAgB,GAAG;YACvB,QAAQ,EAAE,eAAe;YACzB,QAAQ,EAAE;gBACR;oBACE,EAAE,EAAE,QAAQ;oBACZ,IAAI,EAAE,eAAe;oBACrB,QAAQ,EAAE,MAAM;oBAChB,KAAK,EAAE;wBACL,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,OAAO;qBACf;oBACD,KAAK,EAAE,UAAU;iBAClB;gBACD;oBACE,EAAE,EAAE,QAAQ,EAAE,eAAe;oBAC7B,IAAI,EAAE,gBAAgB;oBACtB,QAAQ,EAAE,MAAM;oBAChB,KAAK,EAAE;wBACL,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,OAAO;qBACf;oBACD,KAAK,EAAE,UAAU;iBAClB;aACF;SACF,CAAC;QAEF,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;QACrD,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAEnE,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;QAEzC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,mBAAmB,GAAG;YAC1B,QAAQ,EAAE,eAAe;YACzB,QAAQ,EAAE;gBACR;oBACE,EAAE,EAAE,QAAQ;oBACZ,IAAI,EAAE,eAAe;oBACrB,QAAQ,EAAE,MAAM;oBAChB,KAAK,EAAE;wBACL,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,gBAAgB,CAAC,gBAAgB;qBACzC;oBACD,KAAK,EAAE,UAAU;iBAClB;aACF;SACF,CAAC;QAEF,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;QACrD,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,mBAAmB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAEtE,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;QAEzC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,sBAAsB,GAAoB;YAC9C,QAAQ,EAAE,eAAe;YACzB,QAAQ,EAAE;gBACR;oBACE,EAAE,EAAE,QAAQ;oBACZ,IAAI,EAAE,cAAc;oBACpB,QAAQ,EAAE,MAAM;oBAChB,KAAK,EAAE;wBACL,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,MAAM;qBACd;oBACD,KAAK,EAAE,MAAM;oBACb,WAAW,EAAE;wBACX,KAAK,EAAE,gBAAgB;wBACvB,WAAW,EAAE;4BACX,SAAS;4BACT,cAAc;yBACf;wBACD,OAAO,EAAE,IAAI;wBACb,SAAS,EAAE;4BACT,IAAI,EAAE,SAAS;4BACf,OAAO,EAAE,KAAK;4BACd,WAAW,EAAE,KAAK;yBACnB;qBACF;iBACF;aACF;SACF,CAAC;QAEF,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,uBAAuB,CAAC,CAAC;QACxD,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,sBAAsB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAEzE,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;QAEzC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI script to validate all AcidTest pattern files
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=validate-patterns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-patterns.d.ts","sourceRoot":"","sources":["../../src/validation/validate-patterns.ts"],"names":[],"mappings":";AAEA;;GAEG"}
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI script to validate all AcidTest pattern files
4
+ */
5
+ import path from 'path';
6
+ import { fileURLToPath } from 'url';
7
+ import { validateAllPatterns } from './pattern-validator.js';
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+ const patternsDir = path.join(__dirname, '../patterns');
10
+ console.log('Validating AcidTest pattern files...\n');
11
+ // Validate all patterns
12
+ const results = validateAllPatterns(patternsDir);
13
+ // Track statistics
14
+ let totalFiles = 0;
15
+ let validFiles = 0;
16
+ let totalPatterns = 0;
17
+ let hasErrors = false;
18
+ // Process and display results
19
+ for (const result of results) {
20
+ totalFiles++;
21
+ if (result.valid) {
22
+ validFiles++;
23
+ totalPatterns += result.patternCount || 0;
24
+ const fileName = result.file ? path.basename(result.file) : 'unknown';
25
+ console.log(`✓ ${fileName} (${result.patternCount || 0} pattern${result.patternCount !== 1 ? 's' : ''})`);
26
+ }
27
+ else {
28
+ hasErrors = true;
29
+ const fileName = result.file ? path.basename(result.file) : 'unknown';
30
+ console.error(`✗ ${fileName}\n`);
31
+ // Display all errors for this file
32
+ for (const error of result.errors) {
33
+ console.error(` [Error] ${error.message}`);
34
+ if (error.path && error.path !== fileName) {
35
+ console.error(` Path: ${error.path}`);
36
+ }
37
+ console.error('');
38
+ }
39
+ }
40
+ }
41
+ // Display summary
42
+ console.log(`${'─'.repeat(50)}`);
43
+ console.log('Validation Results:');
44
+ console.log(` Files checked: ${totalFiles}`);
45
+ console.log(` Valid files: ${validFiles}`);
46
+ console.log(` Patterns validated: ${totalPatterns}`);
47
+ if (hasErrors) {
48
+ console.log(` Status: ✗ FAILED\n`);
49
+ process.exit(1);
50
+ }
51
+ else {
52
+ console.log(` Status: ✓ PASSED\n`);
53
+ process.exit(0);
54
+ }
55
+ //# sourceMappingURL=validate-patterns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-patterns.js","sourceRoot":"","sources":["../../src/validation/validate-patterns.ts"],"names":[],"mappings":";AAEA;;GAEG;AAEH,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7D,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC/D,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AAExD,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;AAEtD,wBAAwB;AACxB,MAAM,OAAO,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;AAEjD,mBAAmB;AACnB,IAAI,UAAU,GAAG,CAAC,CAAC;AACnB,IAAI,UAAU,GAAG,CAAC,CAAC;AACnB,IAAI,aAAa,GAAG,CAAC,CAAC;AACtB,IAAI,SAAS,GAAG,KAAK,CAAC;AAEtB,8BAA8B;AAC9B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;IAC7B,UAAU,EAAE,CAAC;IAEb,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,UAAU,EAAE,CAAC;QACb,aAAa,IAAI,MAAM,CAAC,YAAY,IAAI,CAAC,CAAC;QAC1C,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,KAAK,QAAQ,KAAK,MAAM,CAAC,YAAY,IAAI,CAAC,WAAW,MAAM,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC5G,CAAC;SAAM,CAAC;QACN,SAAS,GAAG,IAAI,CAAC;QACjB,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACtE,OAAO,CAAC,KAAK,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC;QAEjC,mCAAmC;QACnC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClC,OAAO,CAAC,KAAK,CAAC,aAAa,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5C,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC1C,OAAO,CAAC,KAAK,CAAC,WAAW,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACzC,CAAC;YACD,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;AACH,CAAC;AAED,kBAAkB;AAClB,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;AACjC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;AACnC,OAAO,CAAC,GAAG,CAAC,oBAAoB,UAAU,EAAE,CAAC,CAAC;AAC9C,OAAO,CAAC,GAAG,CAAC,kBAAkB,UAAU,EAAE,CAAC,CAAC;AAC5C,OAAO,CAAC,GAAG,CAAC,yBAAyB,aAAa,EAAE,CAAC,CAAC;AAEtD,IAAI,SAAS,EAAE,CAAC;IACd,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;IACpC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;KAAM,CAAC;IACN,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;IACpC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
package/package.json CHANGED
@@ -1,20 +1,22 @@
1
1
  {
2
2
  "name": "acidtest",
3
- "version": "0.8.0",
3
+ "version": "1.0.1",
4
4
  "type": "module",
5
5
  "description": "Security scanner for AI agent skills. Scan before you install.",
6
6
  "bin": {
7
7
  "acidtest": "./dist/index.js"
8
8
  },
9
9
  "scripts": {
10
- "validate": "node scripts/validate-patterns.js",
11
- "build": "node scripts/validate-patterns.js && tsc && mkdir -p dist/patterns && cp src/patterns/*.json dist/patterns/",
10
+ "validate:patterns": "tsx src/validation/validate-patterns.ts",
11
+ "prebuild": "npm run validate:patterns",
12
+ "build": "tsc && mkdir -p dist/patterns dist/schemas && cp src/patterns/*.json dist/patterns/ && cp src/schemas/*.json dist/schemas/",
12
13
  "dev": "npm run build && node dist/index.js",
13
14
  "watch": "tsc --watch",
14
15
  "test": "vitest run",
15
16
  "test:watch": "vitest",
16
17
  "test:ui": "vitest --ui",
17
- "test:coverage": "vitest run --coverage"
18
+ "test:coverage": "vitest run --coverage",
19
+ "test:corpus": "tsx src/test-corpus/validate-corpus.ts"
18
20
  },
19
21
  "files": [
20
22
  "dist",
@@ -41,17 +43,22 @@
41
43
  "homepage": "https://acidtest.dev",
42
44
  "dependencies": {
43
45
  "@modelcontextprotocol/sdk": "^1.26.0",
46
+ "ajv": "^8.17.1",
47
+ "ajv-formats": "^3.0.1",
44
48
  "chalk": "^5.3.0",
45
49
  "chokidar": "^5.0.0",
46
50
  "cli-table3": "^0.6.5",
47
51
  "glob": "^10.3.10",
48
52
  "gray-matter": "^4.0.3",
49
53
  "ora": "^9.3.0",
54
+ "tree-sitter": "^0.21.1",
55
+ "tree-sitter-python": "^0.21.0",
50
56
  "typescript": "^5.3.3"
51
57
  },
52
58
  "devDependencies": {
53
59
  "@types/node": "^20.11.0",
54
60
  "@vitest/ui": "^4.0.18",
61
+ "tsx": "^4.21.0",
55
62
  "vitest": "^4.0.18"
56
63
  },
57
64
  "engines": {
@@ -0,0 +1,4 @@
1
+ # Test Node.js Application
2
+
3
+ This is a test application without SKILL.md or MCP manifest.
4
+ Used to verify AcidTest can scan generic code.
@@ -0,0 +1,24 @@
1
+ // Simple Express.js-style application without SKILL.md
2
+ const express = require('express');
3
+ const fs = require('fs');
4
+
5
+ const app = express();
6
+ const PORT = process.env.PORT || 3000;
7
+
8
+ // Some potentially risky patterns to detect
9
+ app.get('/data', (req, res) => {
10
+ // Reading from filesystem
11
+ const filePath = `/tmp/${req.query.file}`;
12
+ const content = fs.readFileSync(filePath, 'utf-8');
13
+ res.send(content);
14
+ });
15
+
16
+ app.get('/eval', (req, res) => {
17
+ // Code injection vulnerability
18
+ const result = eval(req.query.code);
19
+ res.json({ result });
20
+ });
21
+
22
+ app.listen(PORT, () => {
23
+ console.log(`Server running on port ${PORT}`);
24
+ });
@@ -0,0 +1,4 @@
1
+ # Test Python Application
2
+
3
+ This is a test Python Flask application without SKILL.md or MCP manifest.
4
+ Used to verify AcidTest can scan generic Python code.
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env python3
2
+ # Simple Flask application without SKILL.md
3
+ import os
4
+ import subprocess
5
+ from flask import Flask, request
6
+
7
+ app = Flask(__name__)
8
+
9
+ @app.route('/exec')
10
+ def execute_command():
11
+ # Shell execution vulnerability
12
+ cmd = request.args.get('cmd')
13
+ result = subprocess.check_output(cmd, shell=True)
14
+ return result
15
+
16
+ @app.route('/env')
17
+ def get_env():
18
+ # Access environment variables
19
+ api_key = os.environ.get('API_KEY')
20
+ secret = os.environ.get('SECRET_TOKEN')
21
+ return {'api_key': api_key, 'secret': secret}
22
+
23
+ if __name__ == '__main__':
24
+ app.run(debug=True)