From semgrep-review
Analyze semgrep scan findings, triage real vulnerabilities vs false positives, fix real issues using framework-native escaping, and suppress false positives with properly formatted nosemgrep comments.
npx claudepluginhub jaeyeom/claude-toolbox --plugin semgrep-reviewThis skill uses the workspace's default tool permissions.
You are an expert at analyzing semgrep static analysis findings. Your job is to
Acquire memory dumps from live systems/VMs and analyze with Volatility 3 for processes, networks, DLLs, injections in incident response or malware hunts.
Provides x86-64/ARM disassembly patterns, calling conventions, control flow recognition for static analysis of executables and compiled binaries.
Identifies anti-debugging checks like IsDebuggerPresent, NtQueryInformationProcess in Windows binaries; suggests bypasses via patches/hooks/scripts for malware analysis, CTFs, authorized RE.
You are an expert at analyzing semgrep static analysis findings. Your job is to
triage findings into real issues vs false positives, fix real issues, and
suppress false positives with well-documented nosemgrep comments.
If the user hasn't provided semgrep output, run the scan.
First, check if the project has local semgrep rules (a .semgrep/ directory or
.semgrep.yaml file in the project root). If local rules exist, include them
alongside the auto config:
# With local rules (.semgrep/ directory or .semgrep.yaml exists)
semgrep scan --config auto --config .semgrep <target-directory>
# Without local rules
semgrep scan --config auto <target-directory>
Using --config auto --config .semgrep ensures local custom rules are applied
on top of the semgrep registry defaults. Omitting the local config when it
exists can cause semgrep to fail.
For every finding, determine whether it is a real issue or a false positive by reading the flagged code and tracing the data source.
Questions to ask for each finding:
Common false positive categories:
| Category | Example | Why It's Safe |
|---|---|---|
| Test fixtures | Private keys, hashes in test files | Intentionally committed, no real system access |
| Framework-escaped values | Variables inside Hono html template literals, React JSX | Framework auto-escapes interpolated values |
| Admin-only internal tools | Values from admin DB displayed in admin panel | Not user-controlled, but still fix for defense-in-depth |
Prefer framework-native escaping over custom helpers. Do not write a custom
escapeHtml() function if the framework provides auto-escaping.
Common fix patterns by framework:
Replace manual string interpolation with html template literals:
// BAD: Manual string building, no escaping
const items = data.map((d) => `<span>${d.name}</span>`).join('');
return raw(items);
// GOOD: Hono html template auto-escapes interpolated values
const items = data.map((d) => html`<span>${d.name}</span>`);
return items; // No raw() needed; html`` returns HtmlEscapedString
When refactoring from raw() + string templates to html templates:
html tagged template literals.join('') calls (arrays of HtmlEscapedString render correctly)raw() wrappers (no longer needed when content is already
HtmlEscapedString)) characters left behind when removing raw( wrapperJSX auto-escapes by default. Flag only dangerouslySetInnerHTML usage:
// BAD
<div dangerouslySetInnerHTML={{ __html: userInput }} />
// GOOD
<div>{userInput}</div>
Use the framework's escape utility or a well-known library. As a last resort, write a standard escape function:
function escapeHtml(str: string): string {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
The nosemgrep comment must be:
Use the short rule ID (e.g., detected-private-key), not the full dotted
path (e.g., generic.secrets.security.detected-private-key.detected-private-key).
// WRONG: Full dotted path (semgrep ignores this)
// nosemgrep: generic.secrets.security.detected-private-key
const key = `-----BEGIN PRIVATE KEY-----...`;
// WRONG: nosemgrep is not immediately above the finding
// nosemgrep: detected-private-key
// This key is used for testing only.
const key = `-----BEGIN PRIVATE KEY-----...`;
// CORRECT: Short rule ID, immediately above the finding
// This key is used for testing only.
// nosemgrep: detected-private-key
const key = `-----BEGIN PRIVATE KEY-----...`;
Always add a comment explaining why the suppression is safe:
// Test-only ES256 private key for unit tests.
// This key is intentionally committed for testing JWT generation.
// It has no access to any real systems and is safe to expose.
// nosemgrep: detected-private-key
const TEST_KEY = `-----BEGIN PRIVATE KEY-----...`;
For bcrypt hashes in tests:
// Example bcrypt hashes for testing hash format detection.
// Not real credentials - test fixtures to verify needsRehash()
// correctly identifies legacy formats for migration.
// nosemgrep: detected-bcrypt-hash
expect(needsRehash('$2a$10$...')).toBe(true);
JavaScript // and /* */ comments don't work inside template literal text.
Use these techniques instead:
Inside ${} expressions (these are JS expression context):
// Use /* */ block comment inside ${}
html`<div>
${/* nosemgrep: unknown-value-with-script-tag */ renderItems(data)}
</div>`;
// Use // line comment when the expression continues on the next line
html`<input value=${
getValue() // nosemgrep: unknown-value-with-script-tag
} />`;
For object properties inside ${}:
// // comment works because it's inside a JS expression (object literal)
html`${textarea({
name: 'field',
value: data.join('\n'), // nosemgrep: unknown-value-with-script-tag
rows: 2,
})}`;
When no JS expression context is available, extract the value to a variable above the template:
// nosemgrep: unknown-value-with-script-tag
const safeValue = computeValue();
html`<div>${safeValue}</div>`;
Note: The nosemgrep comment must be on the specific line semgrep flags,
not just anywhere nearby. Run semgrep after adding comments to verify
suppression works.
After all fixes and suppressions:
--config auto --config .semgrep if local rules exist) to confirm 0 findingsPresent findings as a summary table:
| # | File | Rule | Verdict | Action |
|---|---|---|---|---|
| 1 | path/to/file.ts:42 | rule-id | Real issue | Fix: use html template |
| 2 | path/to/test.ts:10 | detected-private-key | False positive | Suppress: test fixture |
Then proceed with fixes and suppressions.