@korayda4/vibe-security 0.3.0

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 (183) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +240 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +333 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/engine/baseline.d.ts +19 -0
  7. package/dist/engine/baseline.js +48 -0
  8. package/dist/engine/baseline.js.map +1 -0
  9. package/dist/engine/config.d.ts +26 -0
  10. package/dist/engine/config.js +41 -0
  11. package/dist/engine/config.js.map +1 -0
  12. package/dist/engine/fixer.d.ts +22 -0
  13. package/dist/engine/fixer.js +87 -0
  14. package/dist/engine/fixer.js.map +1 -0
  15. package/dist/engine/language.d.ts +27 -0
  16. package/dist/engine/language.js +364 -0
  17. package/dist/engine/language.js.map +1 -0
  18. package/dist/engine/output.d.ts +6 -0
  19. package/dist/engine/output.js +155 -0
  20. package/dist/engine/output.js.map +1 -0
  21. package/dist/engine/reporter.d.ts +3 -0
  22. package/dist/engine/reporter.js +145 -0
  23. package/dist/engine/reporter.js.map +1 -0
  24. package/dist/engine/rules/_helpers.d.ts +9 -0
  25. package/dist/engine/rules/_helpers.js +64 -0
  26. package/dist/engine/rules/_helpers.js.map +1 -0
  27. package/dist/engine/rules/backend/fileUpload.d.ts +3 -0
  28. package/dist/engine/rules/backend/fileUpload.js +40 -0
  29. package/dist/engine/rules/backend/fileUpload.js.map +1 -0
  30. package/dist/engine/rules/backend/hashing.d.ts +3 -0
  31. package/dist/engine/rules/backend/hashing.js +40 -0
  32. package/dist/engine/rules/backend/hashing.js.map +1 -0
  33. package/dist/engine/rules/backend/idor.d.ts +3 -0
  34. package/dist/engine/rules/backend/idor.js +40 -0
  35. package/dist/engine/rules/backend/idor.js.map +1 -0
  36. package/dist/engine/rules/backend/index.d.ts +12 -0
  37. package/dist/engine/rules/backend/index.js +22 -0
  38. package/dist/engine/rules/backend/index.js.map +1 -0
  39. package/dist/engine/rules/backend/inputValidation.d.ts +3 -0
  40. package/dist/engine/rules/backend/inputValidation.js +37 -0
  41. package/dist/engine/rules/backend/inputValidation.js.map +1 -0
  42. package/dist/engine/rules/backend/jwt.d.ts +3 -0
  43. package/dist/engine/rules/backend/jwt.js +42 -0
  44. package/dist/engine/rules/backend/jwt.js.map +1 -0
  45. package/dist/engine/rules/backend/rateLimit.d.ts +3 -0
  46. package/dist/engine/rules/backend/rateLimit.js +45 -0
  47. package/dist/engine/rules/backend/rateLimit.js.map +1 -0
  48. package/dist/engine/rules/backend/sqlInjection.d.ts +3 -0
  49. package/dist/engine/rules/backend/sqlInjection.js +41 -0
  50. package/dist/engine/rules/backend/sqlInjection.js.map +1 -0
  51. package/dist/engine/rules/backend/tokenExpiry.d.ts +3 -0
  52. package/dist/engine/rules/backend/tokenExpiry.js +85 -0
  53. package/dist/engine/rules/backend/tokenExpiry.js.map +1 -0
  54. package/dist/engine/rules/backend/tokenInUrl.d.ts +3 -0
  55. package/dist/engine/rules/backend/tokenInUrl.js +112 -0
  56. package/dist/engine/rules/backend/tokenInUrl.js.map +1 -0
  57. package/dist/engine/rules/cicd/dependencies.d.ts +3 -0
  58. package/dist/engine/rules/cicd/dependencies.js +44 -0
  59. package/dist/engine/rules/cicd/dependencies.js.map +1 -0
  60. package/dist/engine/rules/cicd/dockerfile.d.ts +3 -0
  61. package/dist/engine/rules/cicd/dockerfile.js +42 -0
  62. package/dist/engine/rules/cicd/dockerfile.js.map +1 -0
  63. package/dist/engine/rules/cicd/index.d.ts +6 -0
  64. package/dist/engine/rules/cicd/index.js +10 -0
  65. package/dist/engine/rules/cicd/index.js.map +1 -0
  66. package/dist/engine/rules/cicd/secrets.d.ts +3 -0
  67. package/dist/engine/rules/cicd/secrets.js +41 -0
  68. package/dist/engine/rules/cicd/secrets.js.map +1 -0
  69. package/dist/engine/rules/database/encryption.d.ts +3 -0
  70. package/dist/engine/rules/database/encryption.js +37 -0
  71. package/dist/engine/rules/database/encryption.js.map +1 -0
  72. package/dist/engine/rules/database/index.d.ts +5 -0
  73. package/dist/engine/rules/database/index.js +8 -0
  74. package/dist/engine/rules/database/index.js.map +1 -0
  75. package/dist/engine/rules/database/privileges.d.ts +3 -0
  76. package/dist/engine/rules/database/privileges.js +40 -0
  77. package/dist/engine/rules/database/privileges.js.map +1 -0
  78. package/dist/engine/rules/frontend/clickjacking.d.ts +3 -0
  79. package/dist/engine/rules/frontend/clickjacking.js +41 -0
  80. package/dist/engine/rules/frontend/clickjacking.js.map +1 -0
  81. package/dist/engine/rules/frontend/cookies.d.ts +3 -0
  82. package/dist/engine/rules/frontend/cookies.js +35 -0
  83. package/dist/engine/rules/frontend/cookies.js.map +1 -0
  84. package/dist/engine/rules/frontend/envVars.d.ts +3 -0
  85. package/dist/engine/rules/frontend/envVars.js +34 -0
  86. package/dist/engine/rules/frontend/envVars.js.map +1 -0
  87. package/dist/engine/rules/frontend/eval.d.ts +3 -0
  88. package/dist/engine/rules/frontend/eval.js +35 -0
  89. package/dist/engine/rules/frontend/eval.js.map +1 -0
  90. package/dist/engine/rules/frontend/index.d.ts +9 -0
  91. package/dist/engine/rules/frontend/index.js +16 -0
  92. package/dist/engine/rules/frontend/index.js.map +1 -0
  93. package/dist/engine/rules/frontend/sourceMaps.d.ts +3 -0
  94. package/dist/engine/rules/frontend/sourceMaps.js +79 -0
  95. package/dist/engine/rules/frontend/sourceMaps.js.map +1 -0
  96. package/dist/engine/rules/frontend/xss.d.ts +3 -0
  97. package/dist/engine/rules/frontend/xss.js +70 -0
  98. package/dist/engine/rules/frontend/xss.js.map +1 -0
  99. package/dist/engine/rules/index.d.ts +8 -0
  100. package/dist/engine/rules/index.js +38 -0
  101. package/dist/engine/rules/index.js.map +1 -0
  102. package/dist/engine/rules/kotlin/index.d.ts +5 -0
  103. package/dist/engine/rules/kotlin/index.js +8 -0
  104. package/dist/engine/rules/kotlin/index.js.map +1 -0
  105. package/dist/engine/rules/kotlin/kotlinxHtml.d.ts +3 -0
  106. package/dist/engine/rules/kotlin/kotlinxHtml.js +54 -0
  107. package/dist/engine/rules/kotlin/kotlinxHtml.js.map +1 -0
  108. package/dist/engine/rules/kotlin/sqlInjection.d.ts +3 -0
  109. package/dist/engine/rules/kotlin/sqlInjection.js +57 -0
  110. package/dist/engine/rules/kotlin/sqlInjection.js.map +1 -0
  111. package/dist/engine/rules/network/cors.d.ts +3 -0
  112. package/dist/engine/rules/network/cors.js +106 -0
  113. package/dist/engine/rules/network/cors.js.map +1 -0
  114. package/dist/engine/rules/network/headers.d.ts +3 -0
  115. package/dist/engine/rules/network/headers.js +51 -0
  116. package/dist/engine/rules/network/headers.js.map +1 -0
  117. package/dist/engine/rules/network/index.d.ts +6 -0
  118. package/dist/engine/rules/network/index.js +10 -0
  119. package/dist/engine/rules/network/index.js.map +1 -0
  120. package/dist/engine/rules/network/tls.d.ts +3 -0
  121. package/dist/engine/rules/network/tls.js +40 -0
  122. package/dist/engine/rules/network/tls.js.map +1 -0
  123. package/dist/engine/rules/observability/errorHandler.d.ts +3 -0
  124. package/dist/engine/rules/observability/errorHandler.js +34 -0
  125. package/dist/engine/rules/observability/errorHandler.js.map +1 -0
  126. package/dist/engine/rules/observability/index.d.ts +5 -0
  127. package/dist/engine/rules/observability/index.js +8 -0
  128. package/dist/engine/rules/observability/index.js.map +1 -0
  129. package/dist/engine/rules/observability/logging.d.ts +3 -0
  130. package/dist/engine/rules/observability/logging.js +38 -0
  131. package/dist/engine/rules/observability/logging.js.map +1 -0
  132. package/dist/engine/rules/php/index.d.ts +6 -0
  133. package/dist/engine/rules/php/index.js +10 -0
  134. package/dist/engine/rules/php/index.js.map +1 -0
  135. package/dist/engine/rules/php/sqlInjection.d.ts +3 -0
  136. package/dist/engine/rules/php/sqlInjection.js +58 -0
  137. package/dist/engine/rules/php/sqlInjection.js.map +1 -0
  138. package/dist/engine/rules/php/weakHashing.d.ts +3 -0
  139. package/dist/engine/rules/php/weakHashing.js +36 -0
  140. package/dist/engine/rules/php/weakHashing.js.map +1 -0
  141. package/dist/engine/rules/php/xss.d.ts +3 -0
  142. package/dist/engine/rules/php/xss.js +56 -0
  143. package/dist/engine/rules/php/xss.js.map +1 -0
  144. package/dist/engine/rules/ruby/index.d.ts +6 -0
  145. package/dist/engine/rules/ruby/index.js +10 -0
  146. package/dist/engine/rules/ruby/index.js.map +1 -0
  147. package/dist/engine/rules/ruby/sqlInjection.d.ts +3 -0
  148. package/dist/engine/rules/ruby/sqlInjection.js +56 -0
  149. package/dist/engine/rules/ruby/sqlInjection.js.map +1 -0
  150. package/dist/engine/rules/ruby/strongParams.d.ts +3 -0
  151. package/dist/engine/rules/ruby/strongParams.js +35 -0
  152. package/dist/engine/rules/ruby/strongParams.js.map +1 -0
  153. package/dist/engine/rules/ruby/xss.d.ts +3 -0
  154. package/dist/engine/rules/ruby/xss.js +55 -0
  155. package/dist/engine/rules/ruby/xss.js.map +1 -0
  156. package/dist/engine/rules/rust/index.d.ts +6 -0
  157. package/dist/engine/rules/rust/index.js +10 -0
  158. package/dist/engine/rules/rust/index.js.map +1 -0
  159. package/dist/engine/rules/rust/secretInCode.d.ts +3 -0
  160. package/dist/engine/rules/rust/secretInCode.js +36 -0
  161. package/dist/engine/rules/rust/secretInCode.js.map +1 -0
  162. package/dist/engine/rules/rust/sqlInjection.d.ts +3 -0
  163. package/dist/engine/rules/rust/sqlInjection.js +55 -0
  164. package/dist/engine/rules/rust/sqlInjection.js.map +1 -0
  165. package/dist/engine/rules/rust/unsafeExpect.d.ts +3 -0
  166. package/dist/engine/rules/rust/unsafeExpect.js +56 -0
  167. package/dist/engine/rules/rust/unsafeExpect.js.map +1 -0
  168. package/dist/engine/scanner.d.ts +5 -0
  169. package/dist/engine/scanner.js +135 -0
  170. package/dist/engine/scanner.js.map +1 -0
  171. package/dist/engine/walker.d.ts +11 -0
  172. package/dist/engine/walker.js +90 -0
  173. package/dist/engine/walker.js.map +1 -0
  174. package/dist/index.d.ts +2 -0
  175. package/dist/index.js +7 -0
  176. package/dist/index.js.map +1 -0
  177. package/dist/server.d.ts +36 -0
  178. package/dist/server.js +360 -0
  179. package/dist/server.js.map +1 -0
  180. package/dist/types.d.ts +92 -0
  181. package/dist/types.js +2 -0
  182. package/dist/types.js.map +1 -0
  183. package/package.json +85 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Koray Demir
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,240 @@
1
+ # Vibe Security
2
+
3
+ > **The security guard for Vibe Coded projects.** Every line of AI-generated Frontend or Backend code is planned, tested, and scanned across all layers and languages. Auto-fix patches included for the most common mistakes.
4
+
5
+ [![npm](https://img.shields.io/npm/v/%40korayda4%2Fvibe-security?style=flat-square)](https://www.npmjs.com/package/@korayda4/vibe-security)
6
+ [![MCP](https://img.shields.io/badge/MCP-stdio-blue?style=flat-square)](https://modelcontextprotocol.io)
7
+ [![License](https://img.shields.io/badge/license-MIT-green?style=flat-square)](LICENSE)
8
+ [![Node](https://img.shields.io/badge/node-%3E%3D18-brightgreen?style=flat-square)](https://nodejs.org)
9
+
10
+ ---
11
+
12
+ ## What is Vibe Security?
13
+
14
+ Vibe Security is the **AI skill** for catching security mistakes in code generated by AI agents (Claude Code, VS Code Anthropic Claude extension, Cursor, Continue.dev).
15
+
16
+ For every change in a Vibe Coded project, our skill:
17
+
18
+ 1. **Plans** — maps the project's languages and frameworks (React, Next.js, Express, Django, .NET, etc.)
19
+ 2. **Tests** — runs the scan against 36 rules across 6 layers
20
+ 3. **Finds** — XSS, SQLi, IDOR, weak hashing, hardcoded secrets, misconfigured CORS, missing security headers, and more
21
+ 4. **Explains** — every finding has CWE, OWASP, threat model, and step-by-step fix
22
+ 5. **Fixes** — auto-patch for the most common mistakes (FE-001 DOMPurify, FE-004 source maps, NET-002 CORS)
23
+
24
+ ---
25
+
26
+ ## Install
27
+
28
+ ### From npm
29
+
30
+ ```bash
31
+ npm install -g @korayda4/vibe-security
32
+ ```
33
+
34
+ ### From GitHub (pre-publish or alternative channel)
35
+
36
+ ```bash
37
+ npx -y github:korayda4/Vibe-Security init
38
+ ```
39
+
40
+ ### MCP server config
41
+
42
+ **Claude Code** — `~/.claude/mcp_servers.json`:
43
+ ```json
44
+ {
45
+ "mcpServers": {
46
+ "vibe-security": { "command": "vibe-security" }
47
+ }
48
+ }
49
+ ```
50
+
51
+ **VS Code Anthropic Claude extension** — `.vscode/settings.json`:
52
+ ```json
53
+ {
54
+ "mcp.servers": {
55
+ "vibe-security": { "command": "vibe-security", "type": "stdio" }
56
+ }
57
+ }
58
+ ```
59
+
60
+ After install, restart your editor. The `/securityCheck` slash command will be available.
61
+
62
+ ---
63
+
64
+ ## Use
65
+
66
+ ```
67
+ /securityCheck full project scan, writes Security.md
68
+ /securityCheck --frontend only frontend layer
69
+ /securityCheck --fix preview + apply auto-fixes
70
+ /securityCheck --baseline only NEW findings vs baseline
71
+ /securityCheck --rule BE-004 single rule
72
+ ```
73
+
74
+ Or talk to Claude naturally:
75
+
76
+ > "Run a security scan on this project."
77
+ > "Any IDOR or SQL injection in the backend?"
78
+ > "Explain FE-001 in detail."
79
+
80
+ ---
81
+
82
+ ## Languages & Frameworks
83
+
84
+ ### Frontend technologies
85
+
86
+ React · Next.js · Vue.js · AngularJS · Svelte
87
+
88
+ Vibe Security scans every Frontend angle:
89
+
90
+ - XSS in `innerHTML` / `dangerouslySetInnerHTML` / `<%= raw %>` / `unsafe { + }`
91
+ - Cookie flags (HttpOnly, Secure, SameSite)
92
+ - Public env leaks (`NEXT_PUBLIC_*`, `REACT_APP_*`)
93
+ - Source maps in production
94
+ - Clickjacking protection
95
+ - `eval` / `Function` / `setTimeout(string)` misuse
96
+ - CSP / HSTS / X-Content-Type-Options / Referrer-Policy / Permissions-Policy
97
+
98
+ ### Backend technologies
99
+
100
+ Node.js · Express · Fastify · NestJS · Django · Flask · FastAPI · Rails · Sinatra · Laravel · Symfony · WordPress · Spring · ASP.NET Core · Blazor · Actix · Axum · Gin · Fiber · C/C++
101
+
102
+ Vibe Security scans every Backend angle:
103
+
104
+ - IDOR / BOLA — ownership not verified
105
+ - SQL / NoSQL injection (string concatenation, f-string, `format!`)
106
+ - JWT `alg: none`, weak secret, missing expiry
107
+ - Mass assignment — input not whitelisted
108
+ - Weak password hashing (MD5 / SHA-1 / SHA-256)
109
+ - Token in URL query/path
110
+ - Long-lived tokens without expiry
111
+ - Missing rate limiting on auth endpoints
112
+ - Unsafe file upload (original filename used)
113
+ - Hardcoded secrets / API keys / AWS keys
114
+
115
+ ### Detection sources
116
+
117
+ `package.json`, `composer.json`, `Gemfile`, `requirements.txt`, `pyproject.toml`, `go.mod`, `Cargo.toml`, `*.csproj`, `*.sln`, `CMakeLists.txt`. Smart detection via file extension, shebang (`#!/usr/bin/env python3`), and syntax sniffing.
118
+
119
+ ---
120
+
121
+ ## How it works in a Vibe Coded project
122
+
123
+ ```
124
+ User writes code with Claude
125
+
126
+
127
+ AI invokes /securityCheck ──► Vibe Security MCP server
128
+
129
+
130
+ 1. PLAN — detect languages, frameworks, project profile
131
+
132
+
133
+ 2. TEST — apply 36 rules across 6 layers (Frontend, Backend,
134
+ Network, Database, CI/CD, Observability)
135
+
136
+
137
+ 3. FIND — 36 known vulnerability patterns
138
+
139
+
140
+ 4. EXPLAIN — CWE + OWASP + threat model + remediation steps
141
+
142
+
143
+ 5. FIX — auto-patch for 3 rules; guidance for the rest
144
+
145
+
146
+ Security.md written to your project root (not in this repo)
147
+ ```
148
+
149
+ ---
150
+
151
+ ## Output
152
+
153
+ | Format | Use |
154
+ |---|---|
155
+ | `markdown` | Default `Security.md` in your project root |
156
+ | `sarif` | GitHub Code Scanning integration |
157
+ | `json` | Programmatic / CI |
158
+ | `junit` | Jenkins, GitLab CI |
159
+ | `compact` | PR comment summary |
160
+
161
+ **Important:** `Security.md` is generated at runtime in YOUR project. It is not part of this repo and is gitignored by default. Sample structure: [`docs/SECURITY_REPORT.example.md`](docs/SECURITY_REPORT.example.md).
162
+
163
+ ---
164
+
165
+ ## Configuration
166
+
167
+ `.vibe-security.json`:
168
+
169
+ ```json
170
+ {
171
+ "rules": { "FE-006": { "enabled": false } },
172
+ "ignore": ["**/vendor/**"],
173
+ "output": { "format": "sarif" }
174
+ }
175
+ ```
176
+
177
+ ---
178
+
179
+ ## CI/CD
180
+
181
+ ```yaml
182
+ - run: npx -y @korayda4/vibe-security scan . --format sarif --output vibe-security.sarif
183
+ - uses: github/codeql-action/upload-sarif@v3
184
+ with: { sarif_file: vibe-security.sarif }
185
+ ```
186
+
187
+ Baseline for PR-only-new-findings:
188
+
189
+ ```bash
190
+ vibe-security scan . --update-baseline --no-fail # main branch
191
+ vibe-security scan . --baseline .vibe-security-baseline.json # PR branch
192
+ ```
193
+
194
+ Full GitHub Actions: [`examples/github-actions.yml`](examples/github-actions.yml).
195
+
196
+ ---
197
+
198
+ ## Performance
199
+
200
+ | Project size | Time |
201
+ |---|---|
202
+ | 500 files | ~200 ms |
203
+ | 5,000 files | ~1.5 s |
204
+ | 50,000 files | ~15 s |
205
+
206
+ ---
207
+
208
+ ## Compatibility
209
+
210
+ | Platform | Status |
211
+ |---|---|
212
+ | Claude Code (CLI) | ✅ |
213
+ | VS Code Anthropic Claude extension | ✅ |
214
+ | Cursor | ✅ |
215
+ | Continue.dev | ✅ |
216
+ | Standalone CLI / CI | ✅ |
217
+
218
+ Node.js 18+.
219
+
220
+ ---
221
+
222
+ ## More
223
+
224
+ - [`docs/USAGE.md`](docs/USAGE.md) — every flag, every output format
225
+ - [`docs/TOKEN_SAFETY.md`](docs/TOKEN_SAFETY.md) — JWT, refresh tokens, rotation
226
+ - [`docs/VULNERABILITY_PRIORITY.md`](docs/VULNERABILITY_PRIORITY.md) — clusters by priority
227
+ - [`docs/CONTRIBUTING.md`](docs/CONTRIBUTING.md) — add a new rule
228
+ - [`ROADMAP.md`](ROADMAP.md) — phases and what is next
229
+
230
+ ---
231
+
232
+ ## Maintainer
233
+
234
+ Koray Demir · [koraydemirmc@gmail.com](mailto:koraydemirmc@gmail.com) · [github.com/korayda4](https://github.com/korayda4)
235
+
236
+ ---
237
+
238
+ ## License
239
+
240
+ MIT — see [`LICENSE`](LICENSE).
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,333 @@
1
+ #!/usr/bin/env node
2
+ import { scan } from './engine/scanner.js';
3
+ import { writeSecurityReport } from './engine/reporter.js';
4
+ import { applyFixes, formatFixSummary } from './engine/fixer.js';
5
+ import { formatOutput } from './engine/output.js';
6
+ import { readBaseline, writeBaseline, diffAgainstBaseline, } from './engine/baseline.js';
7
+ import { loadConfig } from './engine/config.js';
8
+ import path from 'node:path';
9
+ import fs from 'node:fs/promises';
10
+ function parseArgs(argv) {
11
+ const args = argv.slice(2);
12
+ const out = {
13
+ cmd: args[0] ?? 'scan',
14
+ target: args[1] ?? process.cwd(),
15
+ format: 'markdown',
16
+ fix: false,
17
+ dryRun: false,
18
+ updateBaseline: false,
19
+ watch: false,
20
+ noFail: false,
21
+ help: false,
22
+ };
23
+ for (let i = 2; i < args.length; i++) {
24
+ const a = args[i];
25
+ const next = args[i + 1];
26
+ switch (a) {
27
+ case '--format':
28
+ out.format = next ?? 'markdown';
29
+ i++;
30
+ break;
31
+ case '--output':
32
+ case '-o':
33
+ out.output = next;
34
+ i++;
35
+ break;
36
+ case '--fix':
37
+ out.fix = true;
38
+ break;
39
+ case '--dry-run':
40
+ out.fix = true;
41
+ out.dryRun = true;
42
+ break;
43
+ case '--baseline':
44
+ out.baseline = next;
45
+ i++;
46
+ break;
47
+ case '--update-baseline':
48
+ out.updateBaseline = true;
49
+ break;
50
+ case '--layers':
51
+ out.layers = (next ?? '').split(',').filter(Boolean);
52
+ i++;
53
+ break;
54
+ case '--rules':
55
+ out.rules = (next ?? '').split(',').filter(Boolean);
56
+ i++;
57
+ break;
58
+ case '--watch':
59
+ case '-w':
60
+ out.watch = true;
61
+ break;
62
+ case '--no-fail':
63
+ out.noFail = true;
64
+ break;
65
+ case '--help':
66
+ case '-h':
67
+ out.help = true;
68
+ break;
69
+ default:
70
+ if (!out.target || out.target === process.cwd()) {
71
+ out.target = a;
72
+ }
73
+ }
74
+ }
75
+ return out;
76
+ }
77
+ function printHelp() {
78
+ console.log(`Vibe Security CLI
79
+
80
+ Usage:
81
+ vibe-security scan [path] [options]
82
+ vibe-security list
83
+ vibe-security init
84
+ vibe-security baseline update [path]
85
+
86
+ Scan options:
87
+ --format <markdown|json|sarif|junit|compact> Output format (default: markdown)
88
+ --output, -o <path> Output file path
89
+ --fix Apply auto-fixes
90
+ --dry-run Preview auto-fixes without writing
91
+ --baseline <path> Show only new findings vs baseline
92
+ --update-baseline Write current findings to baseline
93
+ --layers <f,b,n,d,c,o> Scan only these layers
94
+ --rules <FE-001,BE-004> Run only these rules
95
+ --watch, -w Re-scan on file changes
96
+ --no-fail Exit 0 even with findings
97
+
98
+ Examples:
99
+ vibe-security scan . --format sarif --output vibe-security.sarif
100
+ vibe-security scan src/ --fix --dry-run
101
+ vibe-security scan . --baseline .vibe-security-baseline.json
102
+ vibe-security init # Create .vibe-security.json + slash command files
103
+ `);
104
+ }
105
+ async function cmdInit(rootDir) {
106
+ const configPath = path.join(rootDir, '.vibe-security.json');
107
+ const slashCommandPath = path.join(rootDir, '.claude', 'commands', 'securityCheck.md');
108
+ const vscodeSettingsPath = path.join(rootDir, '.vscode', 'settings.json');
109
+ const ghInstructionsPath = path.join(rootDir, '.github', 'instructions', 'security-check.instructions.md');
110
+ const config = {
111
+ rules: {
112
+ 'OBS-002': { severity: 'low' },
113
+ },
114
+ ignore: ['node_modules', 'dist', '.next', 'coverage'],
115
+ output: { format: 'markdown', path: 'Security.md' },
116
+ };
117
+ await fs.mkdir(path.dirname(configPath), { recursive: true });
118
+ await fs.writeFile(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
119
+ console.log(`✅ ${configPath}`);
120
+ for (const [target, content] of [
121
+ [slashCommandPath, SLASH_COMMAND_CLAUDE],
122
+ [vscodeSettingsPath, VSCODE_SETTINGS],
123
+ [ghInstructionsPath, SLASH_COMMAND_GITHUB],
124
+ ]) {
125
+ await fs.mkdir(path.dirname(target), { recursive: true });
126
+ await fs.writeFile(target, content, 'utf8');
127
+ console.log(`✅ ${target}`);
128
+ }
129
+ console.log('\n👉 Next:');
130
+ console.log(' • Claude Code: type /securityCheck');
131
+ console.log(' • VS Code: reload window, MCP server will be picked up automatically');
132
+ console.log(' • CI: add `npx -y vibe-security scan . --format sarif` to your workflow');
133
+ }
134
+ async function cmdList() {
135
+ const { getAllRules } = await import('./engine/rules/index.js');
136
+ const rules = getAllRules();
137
+ console.log('Available rules:\n');
138
+ for (const r of rules) {
139
+ console.log(` ${r.id.padEnd(8)} ${r.severity.toUpperCase().padEnd(10)} ${r.layer.padEnd(14)} ${r.title}`);
140
+ }
141
+ console.log(`\nTotal: ${rules.length}`);
142
+ }
143
+ async function cmdScan(args) {
144
+ const rootDir = path.resolve(args.target);
145
+ if (args.watch) {
146
+ return cmdWatch(args);
147
+ }
148
+ console.log(` Scanning ${rootDir}...`);
149
+ const config = await loadConfig(rootDir);
150
+ const result = await scan({
151
+ rootDir,
152
+ layers: args.layers,
153
+ ruleIds: args.rules,
154
+ ignore: config.ignore,
155
+ });
156
+ const baselinePath = args.baseline ?? config.baseline;
157
+ let displayFindings = result.findings;
158
+ if (baselinePath) {
159
+ const baseline = await readBaseline(baselinePath);
160
+ const diff = diffAgainstBaseline(result, baseline);
161
+ displayFindings = diff.newFindings;
162
+ console.log(`📋 Baseline: ${baseline ? `${baseline.findings.length} known` : 'none'} -> ${diff.newFindings.length} new, ${diff.resolvedFindings.length} resolved`);
163
+ }
164
+ if (args.fix) {
165
+ const fixSummary = await applyFixes(result, {
166
+ dryRun: args.dryRun,
167
+ onlyRuleIds: args.rules,
168
+ });
169
+ console.log('\n' + formatFixSummary(fixSummary, args.dryRun));
170
+ if (!args.dryRun && fixSummary.applied.length > 0) {
171
+ console.log(`\n🔧 ${fixSummary.applied.length} file(s) modified.`);
172
+ }
173
+ }
174
+ if (args.format === 'markdown') {
175
+ const reportPath = args.output ?? path.join(rootDir, 'Security.md');
176
+ const filtered = { ...result, findings: displayFindings };
177
+ await writeSecurityReport(filtered, reportPath);
178
+ console.log(`📝 Report: ${reportPath}`);
179
+ }
180
+ else {
181
+ const out = formatOutput(args.format, { ...result, findings: displayFindings });
182
+ const outPath = args.output ?? defaultOutputPath(rootDir, args.format);
183
+ await fs.writeFile(outPath, out, 'utf8');
184
+ console.log(`📝 ${args.format.toUpperCase()} -> ${outPath}`);
185
+ }
186
+ if (args.updateBaseline) {
187
+ const basePath = baselinePath ?? path.join(rootDir, '.vibe-security-baseline.json');
188
+ await writeBaseline(basePath, result);
189
+ console.log(`📌 Baseline updated: ${basePath}`);
190
+ }
191
+ const sevEmoji = {
192
+ critical: '🟣',
193
+ high: '🔴',
194
+ medium: '🟠',
195
+ low: '🟡',
196
+ info: '🔵',
197
+ };
198
+ console.log(`\n${displayFindings.length} finding(s) -- ` +
199
+ Object.entries(result.summary.bySeverity)
200
+ .filter(([, n]) => n > 0)
201
+ .map(([s, n]) => `${sevEmoji[s]} ${s}:${n}`)
202
+ .join(' - '));
203
+ return args.noFail || displayFindings.length === 0 ? 0 : 1;
204
+ }
205
+ function defaultOutputPath(rootDir, format) {
206
+ const ext = format === 'sarif' ? 'sarif' : format === 'json' ? 'json' : format === 'junit' ? 'xml' : 'md';
207
+ return path.join(rootDir, `vibe-security.${ext}`);
208
+ }
209
+ async function cmdWatch(args) {
210
+ const rootDir = path.resolve(args.target);
211
+ console.log(`👀 Watching ${rootDir} (Ctrl+C to stop)...`);
212
+ let running = false;
213
+ async function rescan() {
214
+ if (running)
215
+ return;
216
+ running = true;
217
+ try {
218
+ const result = await scan({ rootDir });
219
+ const reportPath = path.join(rootDir, 'Security.md');
220
+ await writeSecurityReport(result, reportPath);
221
+ const ts = new Date().toLocaleTimeString();
222
+ console.log(`[${ts}] ${result.summary.totalFindings} finding(s) -> ${reportPath}`);
223
+ }
224
+ finally {
225
+ running = false;
226
+ }
227
+ }
228
+ await rescan();
229
+ console.log(' (3s polling interval)');
230
+ const interval = setInterval(rescan, 3000);
231
+ process.on('SIGINT', () => {
232
+ clearInterval(interval);
233
+ process.exit(0);
234
+ });
235
+ return new Promise(() => { });
236
+ }
237
+ async function cmdBaseline(args) {
238
+ const sub = args.target; // second positional
239
+ const rootDir = process.cwd();
240
+ if (sub === 'update') {
241
+ const result = await scan({ rootDir });
242
+ const basePath = path.join(rootDir, '.vibe-security-baseline.json');
243
+ await writeBaseline(basePath, result);
244
+ console.log(`📌 Baseline updated: ${basePath} (${result.findings.length} entries)`);
245
+ return 0;
246
+ }
247
+ console.error('Usage: vibe-security baseline update');
248
+ return 2;
249
+ }
250
+ async function main() {
251
+ const args = parseArgs(process.argv);
252
+ if (args.help || args.cmd === '--help' || args.cmd === '-h') {
253
+ printHelp();
254
+ return 0;
255
+ }
256
+ if (args.cmd === 'list' || args.cmd === 'rules')
257
+ return cmdList().then(() => 0);
258
+ if (args.cmd === 'init')
259
+ return cmdInit(args.target).then(() => 0);
260
+ if (args.cmd === 'baseline')
261
+ return cmdBaseline(args);
262
+ if (args.cmd === 'scan' || args.cmd === undefined)
263
+ return cmdScan(args);
264
+ console.error(`Unknown command: ${args.cmd}`);
265
+ printHelp();
266
+ return 2;
267
+ }
268
+ main().catch((err) => {
269
+ console.error('Fatal:', err);
270
+ process.exit(1);
271
+ });
272
+ const SLASH_COMMAND_CLAUDE = `---
273
+ description: Run Vibe Security scan on the current project and report findings
274
+ ---
275
+
276
+ Scan this project for security issues.
277
+
278
+ Use the following MCP tools in order:
279
+
280
+ 1. **mcp__vibe-security__scan_project** -- Scan the entire project. Set the \`rootDir\` parameter to the current working directory.
281
+
282
+ 2. If the user provided arguments (\$ARGUMENTS):
283
+ - \`--frontend\` / \`--backend\` / \`--network\` / \`--database\` / \`--cicd\` / \`--observability\` -> filter via the \`layers\` parameter
284
+ - \`--fix\` -> call \`mcp__vibe-security__get_rule_detail\` for the top 3 critical rules to extract fix suggestions, then present an apply plan to the user
285
+ - \`--baseline\` -> compare \`mcp__vibe-security__scan_project\` output against the existing baseline, report only the new findings
286
+ - \`--rule <id>\` -> run only that rule (\`ruleIds\` parameter)
287
+ - \`--json\` -> request JSON output instead of the table format
288
+
289
+ 3. Summarize findings in order: **critical -> high -> medium -> low**.
290
+
291
+ 4. For the **first 3 findings**, write concrete fix suggestions (with code examples).
292
+
293
+ 5. \`Security.md\` is already written by the MCP server. Show the user the file path and total finding count.
294
+
295
+ Do NOT:
296
+ - Hide or downplay findings.
297
+ - Suggest a fix you are not sure about; point the user to the rule documentation instead.
298
+ - Ask clarifying questions before scanning -- scan directly.
299
+ `;
300
+ const VSCODE_SETTINGS = JSON.stringify({
301
+ 'mcp.servers': {
302
+ 'vibe-security': {
303
+ command: 'vibe-security',
304
+ type: 'stdio',
305
+ },
306
+ },
307
+ }, null, 2) + '\n';
308
+ const SLASH_COMMAND_GITHUB = `---
309
+ description: Auto-invoked when user runs /securityCheck -- runs Vibe Security MCP scan and explains findings
310
+ applyTo: "**"
311
+ ---
312
+
313
+ When the user runs /securityCheck (with or without arguments), you MUST:
314
+
315
+ 1. Call the MCP tool \`scan_project\` provided by the \`vibe-security\` server.
316
+ - \`rootDir\`: \${workspaceFolder}
317
+ - If the user passed --frontend, --backend, --network, --database, --cicd, or --observability, pass the matching \`layers\` filter.
318
+ - If the user passed --fix, also call \`get_rule_detail\` for the top 3 critical findings and present auto-fix suggestions inline.
319
+ - If the user passed --rule <id>, pass \`ruleIds: [<id>]\`.
320
+ 2. Summarize the result as a table: severity, count, top 3 files.
321
+ 3. For each Critical and High finding, show: file:line, snippet, one-line fix.
322
+ 4. Confirm that Security.md was written at the workspace root.
323
+ 5. If no findings: congratulate the user.
324
+
325
+ Do not ask clarifying questions before scanning -- just scan.
326
+
327
+ Available MCP tools:
328
+ - \`scan_project\` -- full project scan
329
+ - \`scan_file\` -- single file scan
330
+ - \`list_rules\` -- list all rules
331
+ - \`get_rule_detail\` -- get rule details (use this for auto-fix suggestions)
332
+ `;
333
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { Finding, ScanResult } from '../types.js';
2
+ export interface BaselineFile {
3
+ readonly version: number;
4
+ readonly createdAt: string;
5
+ readonly findings: readonly BaselineEntry[];
6
+ }
7
+ export interface BaselineEntry {
8
+ readonly ruleId: string;
9
+ readonly file: string;
10
+ readonly line: number;
11
+ readonly hash: string;
12
+ }
13
+ export declare function findingFingerprint(f: Finding): string;
14
+ export declare function readBaseline(filePath: string): Promise<BaselineFile | null>;
15
+ export declare function writeBaseline(filePath: string, result: ScanResult): Promise<void>;
16
+ export declare function diffAgainstBaseline(result: ScanResult, baseline: BaselineFile | null): {
17
+ newFindings: readonly Finding[];
18
+ resolvedFindings: readonly BaselineEntry[];
19
+ };
@@ -0,0 +1,48 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { createHash } from 'node:crypto';
4
+ const BASELINE_VERSION = 1;
5
+ export function findingFingerprint(f) {
6
+ return createHash('sha256')
7
+ .update(`${f.ruleId}|${f.file}|${f.match.line}|${f.match.snippet.trim()}`)
8
+ .digest('hex')
9
+ .slice(0, 16);
10
+ }
11
+ export async function readBaseline(filePath) {
12
+ try {
13
+ const raw = await fs.readFile(filePath, 'utf8');
14
+ const parsed = JSON.parse(raw);
15
+ if (parsed.version !== BASELINE_VERSION)
16
+ return null;
17
+ return parsed;
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ }
23
+ export async function writeBaseline(filePath, result) {
24
+ const entries = result.findings.map((f) => ({
25
+ ruleId: f.ruleId,
26
+ file: f.file,
27
+ line: f.match.line,
28
+ hash: findingFingerprint(f),
29
+ }));
30
+ const file = {
31
+ version: BASELINE_VERSION,
32
+ createdAt: new Date().toISOString(),
33
+ findings: entries,
34
+ };
35
+ await fs.mkdir(path.dirname(path.resolve(filePath)), { recursive: true });
36
+ await fs.writeFile(filePath, JSON.stringify(file, null, 2), 'utf8');
37
+ }
38
+ export function diffAgainstBaseline(result, baseline) {
39
+ if (!baseline) {
40
+ return { newFindings: result.findings, resolvedFindings: [] };
41
+ }
42
+ const currentHashes = new Set(result.findings.map(findingFingerprint));
43
+ const baselineHashes = new Set(baseline.findings.map((e) => e.hash));
44
+ const newFindings = result.findings.filter((f) => !baselineHashes.has(findingFingerprint(f)));
45
+ const resolvedFindings = baseline.findings.filter((e) => !currentHashes.has(e.hash));
46
+ return { newFindings, resolvedFindings };
47
+ }
48
+ //# sourceMappingURL=baseline.js.map