domains / security / semgrep-rule-variant-creator / semgrep-rule-variant-creator
The trailofbits copy is the operational source: it defines the shipped workflow, the required arguments, the referencesDir resolution procedure, and the resume semantics. The sickn33 copy is a broader procedural guide that adds material the trailofbits copy does not contain: explicit When/When NOT to Use boundaries, an output directory layout, a table of rationalizations to reject, a strictness level, and a detailed four-phase workflow with applicability verdicts and test-first requirements. Because each copy has steps and constraints the other lacks, crowning one would hide a real alternative.
| Repository | Path |
|---|---|
| trailofbits/skills | plugins/semgrep-rule-variant-creator/skills/semgrep-rule-variant-creator/SKILL.md |
| sickn33/agentic-awesome-skills | plugins/agentic-awesome-skills-claude/skills/semgrep-rule-variant-creator/SKILL.md |
| Role | Repository | Path |
|---|---|---|
| peer | trailofbits/skills | plugins/semgrep-rule-variant-creator/skills/semgrep-rule-variant-creator/SKILL.md |
| peer | sickn33/agentic-awesome-skills | plugins/agentic-awesome-skills-claude/skills/semgrep-rule-variant-creator/SKILL.md |
| adapter | sickn33/agentic-awesome-skills | plugins/agentic-awesome-skills/skills/semgrep-rule-variant-creator/SKILL.md |
| adapter | sickn33/agentic-awesome-skills | skills/semgrep-rule-variant-creator/SKILL.md |
---
name: semgrep-rule-variant-creator
description: Creates language variants of existing Semgrep rules. Use when porting a Semgrep rule to specified target languages. Takes an existing rule and target languages as input, produces independent rule+test directories for each language.
---
# Semgrep Rule Variant Creator
Port an existing Semgrep rule to other languages, one independent test-driven cycle per language.
## When to Use
- Porting an existing Semgrep rule to one or more target languages.
- Creating language-specific variants of a universal vulnerability pattern.
- Expanding rule coverage across a polyglot codebase.
## When NOT to Use
- Creating a new Semgrep rule from scratch; use `semgrep-rule-creator` instead.
- Running existing rules against code.
- Languages where the vulnerability pattern fundamentally does not apply.
- Minor syntax variations within the same language.
## Inputs
- Existing Semgrep rule: YAML file path or YAML rule content.
- Target languages: one or more languages to port to.
- Output directory: where the variant directories should land.
## Output
For each applicable target language, produce:
```
<original-rule-id>-<language>/
├── <original-rule-id>-<language>.yaml
└── <original-rule-id>-<language>.<ext>
```
## Workflow Invocation
The trailofbits implementation ships as a dynamic workflow:
```
/semgrep-rule-variant-creator:port-rule-to-languages
```
Pass the required arguments:
```json
{
"rulePath": "<path to the rule being ported>",
"languages": ["Go", "Java"],
"referencesDir": "<absolute path to the references directory>",
"outputDir": "<where the variant directories should land>"
}
```
`referencesDir` must be a resolved absolute path. Resolve it in order, first hit wins:
1. Claude Code: `ls -d -- "${CLAUDE_PLUGIN_ROOT}/skills/semgrep-rule-variant-creator/references"`
2. Codex: the same command with `${CODEX_PLUGIN_ROOT}`, if that variable is set instead.
3. Neither set: `find ~/.claude ~/.codex . -type d -path '*/semgrep-rule-variant-creator/skills/*/references' -print -quit 2>/dev/null`
Confirm the directory holds both reference files with `ls -1 -- "<that path>"`. Pass the path exactly as printed. If all three come back empty, stop and say so rather than assembling a path by hand.
The rule travels as a path, not as text: every phase reads the file.
## Four-Phase Cycle Per Language
For each target language, complete the full cycle before moving to the next language.
### Phase 1: Applicability Analysis
Determine if the pattern applies to the target language.
Analysis criteria:
1. Does the vulnerability class exist in the target language?
2. Does an equivalent construct exist?
3. Are the semantics similar enough for meaningful detection?
Verdict options:
- `APPLICABLE` → Proceed with variant creation.
- `APPLICABLE_WITH_ADAPTATION` → Proceed but significant changes needed.
- `NOT_APPLICABLE` → Skip this language, document why.
A `NOT_APPLICABLE` verdict goes to an independent refuter before the language is dropped.
### Phase 2: Test Creation (Test-First)
Always write tests before the rule.
Create a test file with target language idioms:
- Minimum 2 vulnerable cases (`ruleid:`).
- Minimum 2 safe cases (`ok:`).
- Include language-specific edge cases.
### Phase 3: Rule Creation
1. Analyze AST: `semgrep --dump-ast -l <lang> test-file`.
2. Translate patterns to target language syntax.
3. Update metadata: language key, message, rule ID.
4. Adapt for idioms: handle language-specific constructs.
### Phase 4: Validation
```bash
semgrep --validate --config rule.yaml
semgrep --test --config rule.yaml test-file
```
Output MUST show `All tests passed`. Failed validation is retried up to three times rather than trusting one agent to iterate until green.
## Strictness
- Applicability analysis is mandatory.
- Each language is independent; complete the full cycle before moving to the next.
- Test-first for each variant.
- 100% test pass required.
## Rationalizations to Reject
- "Pattern structure is identical" — different ASTs across languages; always dump AST for the target language.
- "Same vulnerability, same detection" — data flow differs between languages; analyze target language idioms.
- "Rule doesn't need tests since original worked" — language edge cases differ; write new test cases.
- "Skip applicability — it obviously applies" — some patterns are language-specific; complete applicability analysis first.
- "I'll create all variants then test" — errors compound; complete the full cycle per language.
- "Library equivalent is close enough" — surface similarity hides differences; verify API semantics match.
- "Just translate the syntax 1:1" — languages have different idioms; research target language patterns.
## Resume Semantics
If a run is interrupted while the session is still alive, relaunch it with:
```
Workflow({scriptPath: "…", resumeFromRunId: "<runId>", args: {…}})
```
Pass the same arguments again. Arguments are not saved with a run, so a resume that omits them fails the pre-flight check. With them, languages that finished replay from cache and only the unfinished ones re-run.
Resume is same-session only. A session limit ends the session, and runs are stored under that session's own directory, so the next session cannot reach them. A run id it cannot resolve is not an error — the workflow starts from scratch under that id and re-runs every language at full cost. Check the id is still there before counting on a resume:
```
ls -d ~/.claude/projects/*/*/subagents/workflows/*/
```
If you cannot confirm the id, or the session ended, re-invoke with the same arguments and point `outputDir` somewhere fresh. The script never deletes a directory, so a language that flipped to `NOT_APPLICABLE` on the second run leaves the first run's variant behind.
## Foundational Reference
The `semgrep-rule-creator` skill is the authoritative reference for Semgrep rule creation fundamentals. Consult it for:
- When to use taint mode vs pattern matching.
- Test-first methodology.
- Anti-patterns to avoid.
- Iterating until tests pass.
- Rule optimization.
When porting a rule, you are applying these same principles in a new language context. If uncertain about rule structure or approach, refer to `semgrep-rule-creator` first.