@jmylchreest/aide-plugin 0.0.45 → 0.0.47
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/bin/aide-wrapper.sh +8 -2
- package/package.json +1 -1
- package/skills/semgrep/SKILL.md +70 -0
- package/src/core/skill-matcher.ts +52 -0
- package/src/core/types.ts +2 -0
- package/src/opencode/hooks.ts +17 -1
package/bin/aide-wrapper.sh
CHANGED
|
@@ -126,9 +126,15 @@ if [[ "$NEEDS_DOWNLOAD" == "true" ]]; then
|
|
|
126
126
|
log "Downloading binary..."
|
|
127
127
|
log "Using downloader: $DOWNLOADER"
|
|
128
128
|
|
|
129
|
-
# Use tsx for .ts files (dev), node for .js files (npm install)
|
|
129
|
+
# Use bun/tsx for .ts files (dev), node for .js files (npm install)
|
|
130
130
|
if [[ "$DOWNLOADER" == *.ts ]]; then
|
|
131
|
-
|
|
131
|
+
if command -v bun &>/dev/null; then
|
|
132
|
+
RUNNER="bun"
|
|
133
|
+
elif command -v tsx &>/dev/null; then
|
|
134
|
+
RUNNER="tsx"
|
|
135
|
+
else
|
|
136
|
+
RUNNER="npx --yes tsx"
|
|
137
|
+
fi
|
|
132
138
|
else
|
|
133
139
|
RUNNER="node"
|
|
134
140
|
fi
|
package/package.json
CHANGED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: semgrep
|
|
3
|
+
description: Run Semgrep security and code quality analysis
|
|
4
|
+
triggers:
|
|
5
|
+
- semgrep
|
|
6
|
+
- security scan
|
|
7
|
+
- sast scan
|
|
8
|
+
- vulnerability scan
|
|
9
|
+
- code security
|
|
10
|
+
- security audit
|
|
11
|
+
requires_binary:
|
|
12
|
+
- semgrep
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Semgrep Security Analysis
|
|
16
|
+
|
|
17
|
+
Run Semgrep to detect security vulnerabilities and code quality issues in the codebase.
|
|
18
|
+
|
|
19
|
+
## Workflow
|
|
20
|
+
|
|
21
|
+
### 1. Run Semgrep scan
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Auto-detect rules for the project's languages
|
|
25
|
+
semgrep scan --config auto --json --quiet 2>/dev/null | head -c 50000
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
If JSON output is too large, use text output:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
semgrep scan --config auto --quiet 2>/dev/null
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 2. For specific rule sets
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Security-focused rules only
|
|
38
|
+
semgrep scan --config "p/security-audit" --json --quiet
|
|
39
|
+
|
|
40
|
+
# OWASP Top 10
|
|
41
|
+
semgrep scan --config "p/owasp-top-ten" --json --quiet
|
|
42
|
+
|
|
43
|
+
# Language-specific
|
|
44
|
+
semgrep scan --config "p/golang" --json --quiet
|
|
45
|
+
semgrep scan --config "p/python" --json --quiet
|
|
46
|
+
semgrep scan --config "p/typescript" --json --quiet
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 3. Triage results
|
|
50
|
+
|
|
51
|
+
For each finding:
|
|
52
|
+
|
|
53
|
+
1. Read the file and surrounding context
|
|
54
|
+
2. Assess whether the finding is a true positive or false positive
|
|
55
|
+
3. For true positives, fix the issue following the suggestion in the finding
|
|
56
|
+
4. For false positives, consider adding a `# nosemgrep` inline comment with justification
|
|
57
|
+
|
|
58
|
+
### 4. Scan specific files
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Scan only changed files
|
|
62
|
+
semgrep scan --config auto --json --quiet -- path/to/file.py
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Common Issues
|
|
66
|
+
|
|
67
|
+
- **Too many findings**: Use `--severity ERROR` to focus on critical issues first
|
|
68
|
+
- **Slow scan**: Use `--config auto` instead of multiple rule packs to avoid re-scanning
|
|
69
|
+
- **Missing rules**: Install additional rules with `semgrep registry`
|
|
70
|
+
- **False positives**: Add `# nosemgrep: rule-id` with a comment explaining why
|
|
@@ -8,8 +8,35 @@
|
|
|
8
8
|
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
9
9
|
import { join, basename, extname } from "path";
|
|
10
10
|
import { homedir } from "os";
|
|
11
|
+
import { execSync } from "child_process";
|
|
11
12
|
import type { Skill, SkillMatchResult } from "./types.js";
|
|
12
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Cache of binary existence checks to avoid repeated shell invocations.
|
|
16
|
+
* Maps binary name to boolean (exists on PATH).
|
|
17
|
+
*/
|
|
18
|
+
const binaryExistsCache = new Map<string, boolean>();
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Check if a binary exists on PATH.
|
|
22
|
+
* Results are cached for the lifetime of the process.
|
|
23
|
+
*/
|
|
24
|
+
function binaryExists(name: string): boolean {
|
|
25
|
+
const cached = binaryExistsCache.get(name);
|
|
26
|
+
if (cached !== undefined) return cached;
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const cmd =
|
|
30
|
+
process.platform === "win32" ? `where ${name}` : `command -v ${name}`;
|
|
31
|
+
execSync(cmd, { stdio: "ignore", timeout: 2000 });
|
|
32
|
+
binaryExistsCache.set(name, true);
|
|
33
|
+
return true;
|
|
34
|
+
} catch {
|
|
35
|
+
binaryExistsCache.set(name, false);
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
13
40
|
// Skill search locations relative to cwd
|
|
14
41
|
const SKILL_LOCATIONS = [".aide/skills", "skills"];
|
|
15
42
|
|
|
@@ -129,6 +156,22 @@ export function parseSkillFrontmatter(
|
|
|
129
156
|
meta.platforms = platforms;
|
|
130
157
|
}
|
|
131
158
|
|
|
159
|
+
// Parse requires_binary array (e.g. "requires_binary:\n - semgrep")
|
|
160
|
+
const requiresBinary: string[] = [];
|
|
161
|
+
const binaryMatch = yamlContent.match(
|
|
162
|
+
/requires_binary:\s*\n((?:\s+-\s*.+\n?)*)/,
|
|
163
|
+
);
|
|
164
|
+
if (binaryMatch) {
|
|
165
|
+
const blines = binaryMatch[1].split("\n");
|
|
166
|
+
for (const line of blines) {
|
|
167
|
+
const itemMatch = line.match(/^\s+-\s*["']?([^"'\n]+)["']?\s*$/);
|
|
168
|
+
if (itemMatch) requiresBinary.push(itemMatch[1].trim());
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (requiresBinary.length > 0) {
|
|
172
|
+
meta.requires_binary = requiresBinary;
|
|
173
|
+
}
|
|
174
|
+
|
|
132
175
|
return { meta, body };
|
|
133
176
|
}
|
|
134
177
|
|
|
@@ -176,6 +219,7 @@ export function loadSkill(path: string): Skill | null {
|
|
|
176
219
|
triggers,
|
|
177
220
|
description: meta.description as string | undefined,
|
|
178
221
|
platforms: meta.platforms as string[] | undefined,
|
|
222
|
+
requires_binary: meta.requires_binary as string[] | undefined,
|
|
179
223
|
content: body,
|
|
180
224
|
};
|
|
181
225
|
} catch {
|
|
@@ -253,6 +297,14 @@ export function matchSkills(
|
|
|
253
297
|
if (!skill.platforms.includes(platform)) continue;
|
|
254
298
|
}
|
|
255
299
|
|
|
300
|
+
// Binary gate: skip skills that require binaries not on PATH
|
|
301
|
+
if (skill.requires_binary && skill.requires_binary.length > 0) {
|
|
302
|
+
const allPresent = skill.requires_binary.every((bin) =>
|
|
303
|
+
binaryExists(bin),
|
|
304
|
+
);
|
|
305
|
+
if (!allPresent) continue;
|
|
306
|
+
}
|
|
307
|
+
|
|
256
308
|
let score = 0;
|
|
257
309
|
|
|
258
310
|
for (const trigger of skill.triggers) {
|
package/src/core/types.ts
CHANGED
|
@@ -113,6 +113,8 @@ export interface Skill {
|
|
|
113
113
|
description?: string;
|
|
114
114
|
/** Optional platform restriction. If set, only matched on listed platforms ("opencode", "claude-code"). */
|
|
115
115
|
platforms?: string[];
|
|
116
|
+
/** Optional binary requirement. If set, skill is only matched when all listed binaries exist on PATH. */
|
|
117
|
+
requires_binary?: string[];
|
|
116
118
|
content: string;
|
|
117
119
|
}
|
|
118
120
|
|
package/src/opencode/hooks.ts
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* Stop (blocking) → session.idle re-prompts via session.prompt() for persistence
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
import { execFileSync } from "child_process";
|
|
28
|
+
import { execFileSync, execSync } from "child_process";
|
|
29
29
|
import { join } from "path";
|
|
30
30
|
import { findAideBinary } from "../core/aide-client.js";
|
|
31
31
|
import {
|
|
@@ -224,6 +224,22 @@ function createConfigHandler(
|
|
|
224
224
|
) {
|
|
225
225
|
continue;
|
|
226
226
|
}
|
|
227
|
+
// Skip skills requiring binaries not on PATH
|
|
228
|
+
if (skill.requires_binary && skill.requires_binary.length > 0) {
|
|
229
|
+
const allPresent = skill.requires_binary.every((bin) => {
|
|
230
|
+
try {
|
|
231
|
+
const cmd =
|
|
232
|
+
process.platform === "win32"
|
|
233
|
+
? `where ${bin}`
|
|
234
|
+
: `command -v ${bin}`;
|
|
235
|
+
execSync(cmd, { stdio: "ignore", timeout: 2000 });
|
|
236
|
+
return true;
|
|
237
|
+
} catch {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
if (!allPresent) continue;
|
|
242
|
+
}
|
|
227
243
|
const commandName = `aide:${skill.name}`;
|
|
228
244
|
// Only register if not already defined (user config takes priority)
|
|
229
245
|
if (!input.command[commandName]) {
|