@securityreviewai/securityreview-kit 0.1.35 → 0.1.36
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/README.md +1 -1
- package/package.json +2 -2
- package/src/generators/rules/content.js +12 -0
- package/src/generators/rules/content.md +36 -108
- package/src/generators/rules/cursor.js +4 -0
- package/src/generators/rules/skill.md +17 -28
- package/src/generators/rules/vscode.js +80 -7
- package/src/generators/rules/vscode.test.js +41 -0
- package/src/utils/constants.js +12 -0
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ npx @securityreviewai/securityreview-kit init --switch-project
|
|
|
29
29
|
|---|---|---|---|
|
|
30
30
|
| Cursor | `cursor` | `.cursor/mcp.json` | `.cursor/rules/srai-security-review.mdc`, `.cursor/rules/ctm_sync_rule.mdc`, `.cursor/commands/ctm_sync.md`, `.cursor/agents/ctm_sync.md`, `.cursor/commands/create-ide-workflow.md`, `.cursor/commands/srai-profile.md`, `.cursor/skills/threat-modelling/SKILL.md` |
|
|
31
31
|
| Claude Code | `claude` | `.claude/settings.json` | `CLAUDE.md` |
|
|
32
|
-
| VS Code Copilot | `vscode` | `.vscode/mcp.json` | `.github/copilot-instructions.md`, `.github/skills/guardrails-profiler/SKILL.md`, `.github/skills/guardrails-selection/SKILL.md` |
|
|
32
|
+
| VS Code Copilot | `vscode` | `.vscode/mcp.json` | `.github/copilot-instructions.md`, `.github/skills/threat-modelling/SKILL.md`, `.github/skills/guardrails-profiler/SKILL.md`, `.github/skills/guardrails-selection/SKILL.md`, `.github/agents/ctm_sync.agent.md`, `.github/hooks/srai-session-policy.json` |
|
|
33
33
|
| Windsurf | `windsurf` | `.windsurf/mcp_config.json` | `.windsurf/rules/srai-security-review.md` |
|
|
34
34
|
| Codex | `codex` | `.codex/config.toml` | `AGENTS.md` |
|
|
35
35
|
| Gemini CLI | `gemini` | `.gemini/settings.json` | `GEMINI.md` |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@securityreviewai/securityreview-kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.36",
|
|
4
4
|
"description": "Bootstrap security-review-mcp for AI IDEs and CLI tools",
|
|
5
5
|
"author": "Debarshi Das <debarshi.das@we45.com>",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"node": ">=18"
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
|
-
"test": "node --test
|
|
20
|
+
"test": "node --test",
|
|
21
21
|
"start": "node bin/securityreview-kit.js"
|
|
22
22
|
},
|
|
23
23
|
"keywords": [
|
|
@@ -44,6 +44,18 @@ function readTemplate(templateFileName, options = {}) {
|
|
|
44
44
|
'.cursor/skills/guardrails-selection',
|
|
45
45
|
options.guardrailsSelectionSkillDir,
|
|
46
46
|
);
|
|
47
|
+
out = injectPathPlaceholder(
|
|
48
|
+
out,
|
|
49
|
+
'{{THREAT_MODELLING_SKILL_DIR}}',
|
|
50
|
+
'.cursor/skills/threat-modelling',
|
|
51
|
+
options.threatModellingSkillDir,
|
|
52
|
+
);
|
|
53
|
+
out = injectPathPlaceholder(
|
|
54
|
+
out,
|
|
55
|
+
'{{CTM_SYNC_AGENT_PATH}}',
|
|
56
|
+
'.cursor/agents/ctm_sync.md',
|
|
57
|
+
options.ctmSyncAgentPath,
|
|
58
|
+
);
|
|
47
59
|
|
|
48
60
|
return out;
|
|
49
61
|
}
|
|
@@ -1,126 +1,54 @@
|
|
|
1
|
-
# Security
|
|
2
|
-
|
|
3
|
-
**THIS RULE IS NON-NEGOTIABLE AND TRIGGERED BEFORE YOU EVEN START THINKING.** You MUST think security-first and perform on-the-go threat modeling using the PWNISMS framework BEFORE and DURING every task whether it's TODO or PLAN or codegen. This is not optional. This is not deferred to a later review. Every line of code you write must be informed by threat awareness.
|
|
4
|
-
|
|
5
|
-
> **Skill Reference:** Read and apply `.cursor/skills/threat-modelling/SKILL.md` for the full PWNISMS framework — the 7 threat categories, code generation rules, and post-generation checklist. You MUST walk through all 7 categories for every code task that seems security relevant.
|
|
6
|
-
>
|
|
7
|
-
> **Guardrail Selection Skill:** In IDEs that support workspace skills, read and apply `{{GUARDRAILS_SELECTION_SKILL_DIR}}/SKILL.md` when fetching guardrails. In other IDEs, follow that same workflow explicitly: analyze the task, shortlist relevant guardrails, and hydrate the exact shortlist with `get_guardrail_by_id`.
|
|
8
|
-
|
|
9
|
-
### When NOT to apply
|
|
10
|
-
|
|
11
|
-
Skip PWNISMS, guardrails, and the MCP flow entirely for tasks that have **no security surface**:
|
|
12
|
-
|
|
13
|
-
- Documentation-only changes (README, comments, changelogs)
|
|
14
|
-
- Code formatting, linting, or style fixes
|
|
15
|
-
- Renaming variables/files with no logic change
|
|
16
|
-
- General Q&A, explanations, or research that produces no code
|
|
17
|
-
- UI copy, typo fixes, or purely cosmetic changes
|
|
18
|
-
- Adding or updating tests that don't touch auth, crypto, or input handling
|
|
19
|
-
|
|
20
|
-
If in doubt, do a quick mental scan: *does this change touch auth, input handling, secrets, network, data storage, or dependencies?* If **no**, skip. If **yes** or **maybe**, apply.
|
|
21
|
-
|
|
22
|
-
**MANDATORY POST THREAT MODELLING** ALWAYS invoke `ctm_sync` immediately after threat modelling is generated or updated. This is required on every threat-model run.
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## Two pillars of security enforcement
|
|
27
|
-
|
|
28
|
-
This workflow enforces security through two complementary pillars:
|
|
29
|
-
|
|
30
|
-
1. **Secure by Design (PWNISMS)** — Universal threat-category analysis applied to every security-relevant task. Identifies risks and drives architectural/design-level mitigations.
|
|
31
|
-
2. **Secure by Code (Vibe Guardrails)** — Project-specific coding dos and don'ts fetched from SRAI via `get_guardrails`, shortlisted intentionally, and then hydrated via `get_guardrail_by_id`. Enforces concrete implementation rules derived from prior threat reviews, compliance requirements, and team decisions.
|
|
32
|
-
|
|
33
|
-
Both pillars are **mandatory** for any code-generation task with a security surface. PWNISMS catches broad threats; guardrails enforce project-specific coding standards.
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
## MCP Enhancement — `security-review-mcp`
|
|
1
|
+
# Security Review Kit Agent Instructions
|
|
38
2
|
|
|
39
3
|
Configured SRAI project name: `<SRAI_PROJECT_NAME>`
|
|
40
4
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
Before any MCP call, classify the task:
|
|
44
|
-
|
|
45
|
-
1. Bug fix or refactor with no security surface change — Enrich your context with the profile exploration below, skip review tools
|
|
46
|
-
2. Non-security task — Skip MCP entirely
|
|
47
|
-
3. New feature — Start with full profile exploration, then full MCP flow
|
|
48
|
-
|
|
49
|
-
When the `security-review-mcp` MCP server is available, use it to **enrich and persist** your threat analysis. This is a force multiplier on top of PWNISMS, not a replacement.
|
|
50
|
-
|
|
51
|
-
### Enriching Your Analysis — Step-by-Step Profile Exploration
|
|
52
|
-
|
|
53
|
-
Rather than fetching the entire profile at once, walk through each reasoning block below **in order**. Each step builds context for the next and directly informs your PWNISMS threat modeling.
|
|
54
|
-
|
|
55
|
-
1. **Find the project** — Call `find_project_by_name` with `name="<SRAI_PROJECT_NAME>"`. If that project does not exist, call `list_projects`. If no project exists, call `create_project`.
|
|
56
|
-
|
|
57
|
-
2. **Understand what the project does** — Call `get_project_profile_description`.
|
|
58
|
-
- Use this to frame the threat surface: what data flows exist, what the system's purpose is, and where trust boundaries lie.
|
|
59
|
-
|
|
60
|
-
3. **Discover technology categories** — Call `list_profile_technology_categories`.
|
|
61
|
-
- This reveals the broad technology areas (e.g., frontend, backend, database, cloud). Use this to scope which PWNISMS categories are most relevant.
|
|
62
|
-
|
|
63
|
-
4. **Deep-dive each technology category** — For each category returned above, call `get_project_profile_technology_category` with the category name.
|
|
64
|
-
- Understand specific frameworks, libraries, and versions. Flag known-vulnerable dependencies for the **Supply Chain (S)** PWNISMS category.
|
|
5
|
+
These instructions are always active for security-relevant coding work. Keep this file as the routing policy; use the skills and agents below for the detailed workflows.
|
|
65
6
|
|
|
66
|
-
|
|
67
|
-
- Identify deployment topology, trust boundaries, and data flow patterns. Feed this into **Workload (W)** and **Network (N)** analysis.
|
|
7
|
+
## Core Workflow
|
|
68
8
|
|
|
69
|
-
|
|
70
|
-
- Map user roles and privilege levels. This directly informs **IAM (I)** and **Product (P)** threat categories.
|
|
9
|
+
For any task that touches auth, authorization, input handling, secrets, network, data storage, dependencies, new APIs/endpoints, infrastructure, or code handling untrusted data:
|
|
71
10
|
|
|
72
|
-
|
|
73
|
-
-
|
|
11
|
+
1. **Fetch Vibe Guardrails first.**
|
|
12
|
+
- Read and follow `{{GUARDRAILS_SELECTION_SKILL_DIR}}/SKILL.md`.
|
|
13
|
+
- Resolve the SRAI project with `find_project_by_name` using `name="<SRAI_PROJECT_NAME>"`.
|
|
14
|
+
- Call `get_guardrails`, shortlist only the relevant project guardrails, then hydrate the exact shortlist with `get_guardrail_by_id`.
|
|
15
|
+
- Preserve that exact shortlist in context for implementation and CTM sync.
|
|
74
16
|
|
|
75
|
-
|
|
76
|
-
-
|
|
77
|
-
-
|
|
17
|
+
2. **Run PWNISMS threat modelling before implementation.**
|
|
18
|
+
- Read and follow `{{THREAT_MODELLING_SKILL_DIR}}/SKILL.md`.
|
|
19
|
+
- Explicitly walk all seven PWNISMS categories: Product, Workload, Network, IAM, Secrets, Monitoring, and Supply Chain.
|
|
20
|
+
- If a category is not applicable, say so briefly and continue.
|
|
21
|
+
- Cross-reference each meaningful threat with the shortlisted guardrails.
|
|
78
22
|
|
|
79
|
-
|
|
80
|
-
-
|
|
81
|
-
-
|
|
23
|
+
3. **Implement secure code using both inputs.**
|
|
24
|
+
- Treat applicable `must` guardrails as mandatory.
|
|
25
|
+
- Treat applicable `must_not` guardrails as hard prohibitions.
|
|
26
|
+
- Use the PWNISMS findings to guide design, validation, authorization, logging, secrets handling, dependency choices, and abuse controls.
|
|
27
|
+
- If PWNISMS reveals a recurring security rule that is not covered by existing guardrails, create and apply an `ide_generated` guardrail in context.
|
|
82
28
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
- Keep the shortlisted exact guardrails in context for the entire code-generation task and for later `ctm_sync`.
|
|
89
|
-
- Cross-reference with PWNISMS findings: if a threat has no corresponding guardrail, flag it as a candidate for a new guardrail.
|
|
29
|
+
4. **Run CTM sync last.**
|
|
30
|
+
- After threat modelling is created or updated, or after guardrails are enforced during implementation, invoke the `ctm_sync` agent/workflow.
|
|
31
|
+
- Use `{{CTM_SYNC_AGENT_PATH}}` for the detailed payload contract.
|
|
32
|
+
- Pass the current threat model summary, the stable `chat_session_id`, the exact existing guardrails shortlisted earlier, and any `ide_generated` guardrails.
|
|
33
|
+
- Do not re-query guardrails during CTM sync; reuse the shortlist selected before implementation.
|
|
90
34
|
|
|
91
|
-
|
|
92
|
-
- `get_threat_scenarios` — Cross-reference with your PWNISMS findings
|
|
93
|
-
- `get_countermeasures` — Apply recommended mitigations
|
|
94
|
-
- `get_components` — Understand the system architecture
|
|
95
|
-
- `get_data_dictionaries` — Identify sensitive data assets and stepping stones
|
|
96
|
-
- `get_security_objectives` — Understand compliance requirements
|
|
97
|
-
- `get_findings` — Review aggregated security insights
|
|
35
|
+
## When To Skip
|
|
98
36
|
|
|
99
|
-
|
|
100
|
-
- Upload context via `create_document_from_content`
|
|
101
|
-
- `create_review` → `start_workflow` → poll `get_workflow_status`
|
|
37
|
+
Skip the SRAI security workflow only for tasks with no security surface, such as documentation-only changes, typo fixes, pure formatting, variable renames with no logic change, or general Q&A that produces no code.
|
|
102
38
|
|
|
103
|
-
|
|
39
|
+
If in doubt, run the workflow. A quick PWNISMS pass is better than silently missing a security boundary.
|
|
104
40
|
|
|
105
|
-
|
|
41
|
+
## Do Not Use Project Profile Exploration
|
|
106
42
|
|
|
107
|
-
|
|
43
|
+
Do not call project-profile exploration tools during normal coding tasks. The old profile walkthrough is no longer part of the agent workflow.
|
|
108
44
|
|
|
109
|
-
|
|
110
|
-
- **First time** a `chat_session_id` is seen for a project: create an AI IDE workflow via `create_ai_ide_workflow` with a short name `session1`, `session2`, … (next free index) and put `chat_session_id:<id>` in the workflow **description** so it can be found later.
|
|
111
|
-
- **Same chat later:** find the workflow whose description contains that `chat_session_id` marker and push `create_ai_ide_event` to that `workflow_id` — do **not** create another workflow for the same session.
|
|
45
|
+
The normal coding workflow is guardrails selection, PWNISMS threat modelling, secure implementation, then CTM sync.
|
|
112
46
|
|
|
113
|
-
|
|
47
|
+
## Tool Reference
|
|
114
48
|
|
|
115
|
-
|
|
|
49
|
+
| Purpose | Tools |
|
|
116
50
|
|---|---|
|
|
117
|
-
|
|
|
118
|
-
|
|
|
119
|
-
|
|
|
120
|
-
|
|
|
121
|
-
| **Reviews** | `create_review`, `list_reviews`, `get_review`, `get_review_overview` |
|
|
122
|
-
| **Workflow** | `start_workflow`, `get_workflow_status`, `start_next_workflow_job`, `start_workflow_job`, `retry_workflow_job` |
|
|
123
|
-
| **Analysis** | `get_threat_scenarios`, `get_countermeasures`, `get_components`, `get_data_dictionaries`, `get_security_objectives`, `get_findings`, `get_security_test_cases` |
|
|
124
|
-
| **Integrations** | `fetch_jira_issue`, `fetch_confluence_page`, `search_confluence_pages`, `fetch_and_link_to_srai` |
|
|
125
|
-
| **AI IDE CTM** | `create_ai_ide_workflow`, `create_ai_ide_event` (and any `list_*` AI IDE workflow tools exposed by the server) |
|
|
126
|
-
| **Vibe profile & default packs** | `update_vibe_profile`, `write_default_pack` (used by the guardrails profiler / init flow — not part of `ctm_sync`) |
|
|
51
|
+
| Project resolution | `find_project_by_name`, `list_projects`, `create_project`, `get_project` |
|
|
52
|
+
| Guardrails | `get_guardrails`, `get_guardrail_by_id` |
|
|
53
|
+
| CTM sync | `create_ai_ide_workflow`, `create_ai_ide_event`, `get_current_user` or equivalent user identity tool |
|
|
54
|
+
| Profiler only | `update_vibe_profile`, `write_default_pack` are used by init-time profiling, not normal coding tasks |
|
|
@@ -3,7 +3,9 @@ import { join } from 'node:path';
|
|
|
3
3
|
import {
|
|
4
4
|
GUARDRAILS_PROFILER_SKILL_REL_DIR,
|
|
5
5
|
GUARDRAILS_SELECTION_SKILL_REL_DIR,
|
|
6
|
+
CTM_SYNC_AGENT_REL_PATH,
|
|
6
7
|
MCP_SERVER_NAME,
|
|
8
|
+
THREAT_MODELLING_SKILL_REL_DIR,
|
|
7
9
|
} from '../../utils/constants.js';
|
|
8
10
|
import { readJson, writeJson, writeText } from '../../utils/fs-helpers.js';
|
|
9
11
|
import {
|
|
@@ -74,6 +76,8 @@ export function generate(cwd, options = {}) {
|
|
|
74
76
|
const optionsWithSkillDirs = {
|
|
75
77
|
...options,
|
|
76
78
|
guardrailsSelectionSkillDir: GUARDRAILS_SELECTION_SKILL_REL_DIR.cursor,
|
|
79
|
+
threatModellingSkillDir: THREAT_MODELLING_SKILL_REL_DIR.cursor,
|
|
80
|
+
ctmSyncAgentPath: CTM_SYNC_AGENT_REL_PATH.cursor,
|
|
77
81
|
};
|
|
78
82
|
const baseRulePath = join(cwd, '.cursor', 'rules', 'srai-security-review.mdc');
|
|
79
83
|
const ctmSyncTriggerRulePath = join(cwd, '.cursor', 'rules', 'ctm_sync_rule.mdc');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: PWNISMS Threat Modelling
|
|
3
|
-
description: Security-first threat modelling workflow for code and architecture tasks. Walks all 7 PWNISMS categories,
|
|
3
|
+
description: Security-first threat modelling workflow for code and architecture tasks. Walks all 7 PWNISMS categories, enforces vibe guardrails (secure by code), and synchronizes findings via CTM sync. Use before, during, and after implementation.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# PWNISMS — Security-First Threat Modelling
|
|
@@ -14,28 +14,18 @@ For EVERY security-relevant task (feature, bug fix, refactor, infra change, arch
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
-
## Phase 0 —
|
|
18
|
-
|
|
19
|
-
Before deep analysis,
|
|
20
|
-
|
|
21
|
-
1.
|
|
22
|
-
2.
|
|
23
|
-
3.
|
|
24
|
-
4.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
9. **Existing reviews** — `list_reviews`. If a completed review exists:
|
|
30
|
-
- `get_threat_scenarios` — prior threats to cross-reference
|
|
31
|
-
- `get_countermeasures` — existing mitigations to leverage
|
|
32
|
-
- `get_components` — system architecture context
|
|
33
|
-
- `get_data_dictionaries` — sensitive data assets
|
|
34
|
-
- `get_security_objectives` — compliance targets
|
|
35
|
-
- `get_findings` — aggregated insights
|
|
36
|
-
10. **Vibe Guardrails** — Use `{{GUARDRAILS_SELECTION_SKILL_DIR}}/SKILL.md` with `project_id` to load the guardrail catalog, shortlist the relevant guardrails, and hydrate the exact implementation set with `get_guardrail_by_id`.
|
|
37
|
-
|
|
38
|
-
If SRAI is not available, proceed with whatever context the user provides — files, diffs, PRs, architecture docs.
|
|
17
|
+
## Phase 0 — Guardrail Context
|
|
18
|
+
|
|
19
|
+
Before deep analysis, ensure the project-specific guardrail shortlist exists:
|
|
20
|
+
|
|
21
|
+
1. Use `{{GUARDRAILS_SELECTION_SKILL_DIR}}/SKILL.md`.
|
|
22
|
+
2. Resolve the project with `find_project_by_name` using `name="<SRAI_PROJECT_NAME>"`.
|
|
23
|
+
3. Call `get_guardrails`, shortlist intentionally for this task, then hydrate the exact shortlist with `get_guardrail_by_id`.
|
|
24
|
+
4. Keep the shortlisted existing guardrails in context for implementation and `ctm_sync`.
|
|
25
|
+
|
|
26
|
+
Do not perform project-profile exploration as part of PWNISMS. The old profile tools are not part of this workflow. Ground the threat model in the user request, repository code, diffs, architecture docs the user provides, and the shortlisted guardrails.
|
|
27
|
+
|
|
28
|
+
If SRAI is not available, proceed with the user-provided context and repository evidence, then clearly note that project guardrails could not be fetched.
|
|
39
29
|
|
|
40
30
|
---
|
|
41
31
|
|
|
@@ -47,7 +37,7 @@ Collect these quickly before deep analysis:
|
|
|
47
37
|
- **Assets**: What must be protected (PII, credentials, tokens, configs, accounts, workflows)?
|
|
48
38
|
- **Entry points**: How data enters/leaves (HTTP, queues, schedulers, CLI, webhooks, integrations)?
|
|
49
39
|
- **Trust boundaries**: Where data crosses users/services/networks/privilege levels?
|
|
50
|
-
- **Existing guardrails**: What shortlisted project-specific dos and don'ts apply (from Phase 0
|
|
40
|
+
- **Existing guardrails**: What shortlisted project-specific dos and don'ts apply (from Phase 0)?
|
|
51
41
|
|
|
52
42
|
If the user provided specific code, diffs, or architecture artifacts, prioritize those as primary evidence.
|
|
53
43
|
|
|
@@ -71,7 +61,7 @@ If the user provided specific code, diffs, or architecture artifacts, prioritize
|
|
|
71
61
|
|
|
72
62
|
4. **Prioritize**
|
|
73
63
|
- Select the top 3-7 risks by impact and likelihood.
|
|
74
|
-
- Factor in existing mitigations from
|
|
64
|
+
- Factor in existing mitigations from the codebase, user-provided context, and guardrails.
|
|
75
65
|
|
|
76
66
|
5. **Mitigate**
|
|
77
67
|
- Propose concrete, implementable controls for each prioritized risk.
|
|
@@ -225,16 +215,15 @@ The `ctm_sync` agent builds and pushes an event payload containing:
|
|
|
225
215
|
- **Best practices achieved**: security patterns followed during implementation
|
|
226
216
|
- **Secure code snippets**: security-relevant code with explanations
|
|
227
217
|
- **Guardrails applied**: all guardrails enforced during this session — both existing ones shortlisted earlier via `get_guardrails` + `get_guardrail_by_id` (`source: "existing"`) and new ones the IDE agent created on the fly (`source: "ide_generated"`), each with satisfaction status
|
|
228
|
-
- **Project profile updates**: architecture notes, tech categories, user groups, compliance requirements, language stacks
|
|
229
218
|
|
|
230
219
|
### How to invoke
|
|
231
220
|
|
|
232
|
-
Use the `ctm_sync` agent
|
|
221
|
+
Use the host's `ctm_sync` agent/workflow with:
|
|
233
222
|
- A clear description of what was threat-modeled
|
|
234
223
|
- The `chat_session_id` for workflow routing
|
|
235
224
|
- Whether this is a new threat model or an update
|
|
236
225
|
|
|
237
|
-
See
|
|
226
|
+
See `{{CTM_SYNC_AGENT_PATH}}` for the full workflow and payload schema.
|
|
238
227
|
|
|
239
228
|
---
|
|
240
229
|
|
|
@@ -1,17 +1,90 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
1
2
|
import { join } from 'node:path';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
CTM_SYNC_AGENT_REL_PATH,
|
|
5
|
+
GUARDRAILS_SELECTION_SKILL_REL_DIR,
|
|
6
|
+
THREAT_MODELLING_SKILL_REL_DIR,
|
|
7
|
+
} from '../../utils/constants.js';
|
|
8
|
+
import { upsertSentinelBlock, writeText } from '../../utils/fs-helpers.js';
|
|
9
|
+
import { getCtmSyncWorkflowContent, getRuleContent, getThreatModellingSkillContent } from './content.js';
|
|
10
|
+
|
|
11
|
+
function writeGeneratedText(filePath, content) {
|
|
12
|
+
const action = existsSync(filePath) ? 'updated' : 'created';
|
|
13
|
+
writeText(filePath, content);
|
|
14
|
+
return { filePath, action };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getCopilotSessionHookContent() {
|
|
18
|
+
const additionalContext = [
|
|
19
|
+
'## MANDATORY SECURITY GATE (Session Policy)',
|
|
20
|
+
'',
|
|
21
|
+
'For any request with security impact (auth, authorization, crypto, input handling, secrets, network, data storage, dependencies, new APIs/endpoints, infrastructure, or code handling untrusted data), follow this order:',
|
|
22
|
+
'',
|
|
23
|
+
'1. Fetch Vibe Guardrails first using .github/skills/guardrails-selection/SKILL.md. Resolve the project, call get_guardrails, shortlist relevant guardrails, then call get_guardrail_by_id for the shortlist.',
|
|
24
|
+
'2. Run PWNISMS threat modelling using .github/skills/threat-modelling/SKILL.md. Explicitly walk Product, Workload, Network, IAM, Secrets, Monitoring, and Supply Chain.',
|
|
25
|
+
'3. Implement secure code using both the hydrated guardrails and PWNISMS findings.',
|
|
26
|
+
'4. Run the ctm_sync custom agent using .github/agents/ctm_sync.agent.md after threat modelling or guardrail enforcement. Reuse the exact guardrail shortlist; do not re-query guardrails during CTM sync.',
|
|
27
|
+
'',
|
|
28
|
+
'Do not use project-profile exploration tools during normal coding tasks. No blocking and no deferral: guardrails first, PWNISMS second, implementation third, ctm_sync last.',
|
|
29
|
+
].join('\n');
|
|
30
|
+
|
|
31
|
+
const commandPayload = JSON.stringify({
|
|
32
|
+
hookSpecificOutput: {
|
|
33
|
+
hookEventName: 'SessionStart',
|
|
34
|
+
additionalContext,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return JSON.stringify(
|
|
39
|
+
{
|
|
40
|
+
hooks: {
|
|
41
|
+
SessionStart: [
|
|
42
|
+
{
|
|
43
|
+
type: 'command',
|
|
44
|
+
command: `printf '%s\\n' '${commandPayload.replaceAll("'", "'\\''")}'`,
|
|
45
|
+
timeout: 5,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
null,
|
|
51
|
+
2,
|
|
52
|
+
) + '\n';
|
|
53
|
+
}
|
|
5
54
|
|
|
6
55
|
/**
|
|
7
56
|
* Generate VS Code Copilot instructions — appends to .github/copilot-instructions.md
|
|
8
57
|
*/
|
|
9
58
|
export function generate(cwd, options = {}) {
|
|
10
|
-
const
|
|
11
|
-
const content = getRuleContent({
|
|
59
|
+
const optionsWithSkillDirs = {
|
|
12
60
|
...options,
|
|
13
61
|
guardrailsSelectionSkillDir: GUARDRAILS_SELECTION_SKILL_REL_DIR.vscode,
|
|
14
|
-
|
|
62
|
+
threatModellingSkillDir: THREAT_MODELLING_SKILL_REL_DIR.vscode,
|
|
63
|
+
ctmSyncAgentPath: CTM_SYNC_AGENT_REL_PATH.vscode,
|
|
64
|
+
};
|
|
65
|
+
const filePath = join(cwd, '.github', 'copilot-instructions.md');
|
|
66
|
+
const content = getRuleContent(optionsWithSkillDirs);
|
|
15
67
|
const action = upsertSentinelBlock(filePath, content);
|
|
16
|
-
|
|
68
|
+
|
|
69
|
+
const threatSkillPath = join(cwd, THREAT_MODELLING_SKILL_REL_DIR.vscode, 'SKILL.md');
|
|
70
|
+
const threatSkill = writeGeneratedText(
|
|
71
|
+
threatSkillPath,
|
|
72
|
+
getThreatModellingSkillContent(optionsWithSkillDirs),
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const ctmSyncAgentPath = join(cwd, CTM_SYNC_AGENT_REL_PATH.vscode);
|
|
76
|
+
const ctmSyncAgent = writeGeneratedText(
|
|
77
|
+
ctmSyncAgentPath,
|
|
78
|
+
getCtmSyncWorkflowContent(optionsWithSkillDirs),
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const hooksPath = join(cwd, '.github', 'hooks', 'srai-session-policy.json');
|
|
82
|
+
const hooks = writeGeneratedText(hooksPath, getCopilotSessionHookContent());
|
|
83
|
+
|
|
84
|
+
return [
|
|
85
|
+
{ filePath, action, kind: 'rule' },
|
|
86
|
+
{ ...threatSkill, kind: 'skill' },
|
|
87
|
+
{ ...ctmSyncAgent, kind: 'agent' },
|
|
88
|
+
{ ...hooks, kind: 'hooks' },
|
|
89
|
+
];
|
|
17
90
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { existsSync, mkdtempSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { test } from 'node:test';
|
|
5
|
+
import assert from 'node:assert/strict';
|
|
6
|
+
import { generate } from './vscode.js';
|
|
7
|
+
|
|
8
|
+
test('VS Code Copilot generator writes instructions, skills, agent, and hooks', () => {
|
|
9
|
+
const cwd = mkdtempSync(join(tmpdir(), 'securityreview-kit-vscode-'));
|
|
10
|
+
|
|
11
|
+
const results = generate(cwd, { projectName: 'SmokeProject' });
|
|
12
|
+
|
|
13
|
+
const expectedPaths = [
|
|
14
|
+
'.github/copilot-instructions.md',
|
|
15
|
+
'.github/skills/threat-modelling/SKILL.md',
|
|
16
|
+
'.github/agents/ctm_sync.agent.md',
|
|
17
|
+
'.github/hooks/srai-session-policy.json',
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
for (const relPath of expectedPaths) {
|
|
21
|
+
assert.equal(existsSync(join(cwd, relPath)), true, `${relPath} should exist`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
assert.deepEqual(results.map((entry) => entry.kind), ['rule', 'skill', 'agent', 'hooks']);
|
|
25
|
+
|
|
26
|
+
const instructions = readFileSync(join(cwd, '.github/copilot-instructions.md'), 'utf8');
|
|
27
|
+
assert.match(instructions, /\.github\/skills\/guardrails-selection\/SKILL\.md/);
|
|
28
|
+
assert.match(instructions, /\.github\/skills\/threat-modelling\/SKILL\.md/);
|
|
29
|
+
assert.match(instructions, /\.github\/agents\/ctm_sync\.agent\.md/);
|
|
30
|
+
assert.doesNotMatch(instructions, /\.cursor/);
|
|
31
|
+
|
|
32
|
+
const threatSkill = readFileSync(join(cwd, '.github/skills/threat-modelling/SKILL.md'), 'utf8');
|
|
33
|
+
for (const heading of ['Product', 'Workload', 'Network', 'IAM', 'Secrets', 'Monitoring', 'Supply Chain']) {
|
|
34
|
+
assert.match(threatSkill, new RegExp(heading));
|
|
35
|
+
}
|
|
36
|
+
assert.match(threatSkill, /\.github\/agents\/ctm_sync\.agent\.md/);
|
|
37
|
+
assert.doesNotMatch(threatSkill, /get_project_profile_description/);
|
|
38
|
+
|
|
39
|
+
const hooks = JSON.parse(readFileSync(join(cwd, '.github/hooks/srai-session-policy.json'), 'utf8'));
|
|
40
|
+
assert.equal(hooks.hooks.SessionStart[0].type, 'command');
|
|
41
|
+
});
|
package/src/utils/constants.js
CHANGED
|
@@ -75,5 +75,17 @@ export const GUARDRAILS_SELECTION_SKILL_REL_DIR = {
|
|
|
75
75
|
codex: '.codex/skills/guardrails-selection',
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
+
/** Relative workspace dirs for the PWNISMS threat-modelling skill (per IDE / CLI). */
|
|
79
|
+
export const THREAT_MODELLING_SKILL_REL_DIR = {
|
|
80
|
+
cursor: '.cursor/skills/threat-modelling',
|
|
81
|
+
vscode: '.github/skills/threat-modelling',
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/** Relative workspace paths for the CTM sync agent/workflow (per IDE / CLI). */
|
|
85
|
+
export const CTM_SYNC_AGENT_REL_PATH = {
|
|
86
|
+
cursor: '.cursor/agents/ctm_sync.md',
|
|
87
|
+
vscode: '.github/agents/ctm_sync.agent.md',
|
|
88
|
+
};
|
|
89
|
+
|
|
78
90
|
export const SENTINEL_START = '<!-- securityreview-kit:start -->';
|
|
79
91
|
export const SENTINEL_END = '<!-- securityreview-kit:end -->';
|