@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.
- package/LICENSE +21 -0
- package/README.md +240 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +333 -0
- package/dist/cli.js.map +1 -0
- package/dist/engine/baseline.d.ts +19 -0
- package/dist/engine/baseline.js +48 -0
- package/dist/engine/baseline.js.map +1 -0
- package/dist/engine/config.d.ts +26 -0
- package/dist/engine/config.js +41 -0
- package/dist/engine/config.js.map +1 -0
- package/dist/engine/fixer.d.ts +22 -0
- package/dist/engine/fixer.js +87 -0
- package/dist/engine/fixer.js.map +1 -0
- package/dist/engine/language.d.ts +27 -0
- package/dist/engine/language.js +364 -0
- package/dist/engine/language.js.map +1 -0
- package/dist/engine/output.d.ts +6 -0
- package/dist/engine/output.js +155 -0
- package/dist/engine/output.js.map +1 -0
- package/dist/engine/reporter.d.ts +3 -0
- package/dist/engine/reporter.js +145 -0
- package/dist/engine/reporter.js.map +1 -0
- package/dist/engine/rules/_helpers.d.ts +9 -0
- package/dist/engine/rules/_helpers.js +64 -0
- package/dist/engine/rules/_helpers.js.map +1 -0
- package/dist/engine/rules/backend/fileUpload.d.ts +3 -0
- package/dist/engine/rules/backend/fileUpload.js +40 -0
- package/dist/engine/rules/backend/fileUpload.js.map +1 -0
- package/dist/engine/rules/backend/hashing.d.ts +3 -0
- package/dist/engine/rules/backend/hashing.js +40 -0
- package/dist/engine/rules/backend/hashing.js.map +1 -0
- package/dist/engine/rules/backend/idor.d.ts +3 -0
- package/dist/engine/rules/backend/idor.js +40 -0
- package/dist/engine/rules/backend/idor.js.map +1 -0
- package/dist/engine/rules/backend/index.d.ts +12 -0
- package/dist/engine/rules/backend/index.js +22 -0
- package/dist/engine/rules/backend/index.js.map +1 -0
- package/dist/engine/rules/backend/inputValidation.d.ts +3 -0
- package/dist/engine/rules/backend/inputValidation.js +37 -0
- package/dist/engine/rules/backend/inputValidation.js.map +1 -0
- package/dist/engine/rules/backend/jwt.d.ts +3 -0
- package/dist/engine/rules/backend/jwt.js +42 -0
- package/dist/engine/rules/backend/jwt.js.map +1 -0
- package/dist/engine/rules/backend/rateLimit.d.ts +3 -0
- package/dist/engine/rules/backend/rateLimit.js +45 -0
- package/dist/engine/rules/backend/rateLimit.js.map +1 -0
- package/dist/engine/rules/backend/sqlInjection.d.ts +3 -0
- package/dist/engine/rules/backend/sqlInjection.js +41 -0
- package/dist/engine/rules/backend/sqlInjection.js.map +1 -0
- package/dist/engine/rules/backend/tokenExpiry.d.ts +3 -0
- package/dist/engine/rules/backend/tokenExpiry.js +85 -0
- package/dist/engine/rules/backend/tokenExpiry.js.map +1 -0
- package/dist/engine/rules/backend/tokenInUrl.d.ts +3 -0
- package/dist/engine/rules/backend/tokenInUrl.js +112 -0
- package/dist/engine/rules/backend/tokenInUrl.js.map +1 -0
- package/dist/engine/rules/cicd/dependencies.d.ts +3 -0
- package/dist/engine/rules/cicd/dependencies.js +44 -0
- package/dist/engine/rules/cicd/dependencies.js.map +1 -0
- package/dist/engine/rules/cicd/dockerfile.d.ts +3 -0
- package/dist/engine/rules/cicd/dockerfile.js +42 -0
- package/dist/engine/rules/cicd/dockerfile.js.map +1 -0
- package/dist/engine/rules/cicd/index.d.ts +6 -0
- package/dist/engine/rules/cicd/index.js +10 -0
- package/dist/engine/rules/cicd/index.js.map +1 -0
- package/dist/engine/rules/cicd/secrets.d.ts +3 -0
- package/dist/engine/rules/cicd/secrets.js +41 -0
- package/dist/engine/rules/cicd/secrets.js.map +1 -0
- package/dist/engine/rules/database/encryption.d.ts +3 -0
- package/dist/engine/rules/database/encryption.js +37 -0
- package/dist/engine/rules/database/encryption.js.map +1 -0
- package/dist/engine/rules/database/index.d.ts +5 -0
- package/dist/engine/rules/database/index.js +8 -0
- package/dist/engine/rules/database/index.js.map +1 -0
- package/dist/engine/rules/database/privileges.d.ts +3 -0
- package/dist/engine/rules/database/privileges.js +40 -0
- package/dist/engine/rules/database/privileges.js.map +1 -0
- package/dist/engine/rules/frontend/clickjacking.d.ts +3 -0
- package/dist/engine/rules/frontend/clickjacking.js +41 -0
- package/dist/engine/rules/frontend/clickjacking.js.map +1 -0
- package/dist/engine/rules/frontend/cookies.d.ts +3 -0
- package/dist/engine/rules/frontend/cookies.js +35 -0
- package/dist/engine/rules/frontend/cookies.js.map +1 -0
- package/dist/engine/rules/frontend/envVars.d.ts +3 -0
- package/dist/engine/rules/frontend/envVars.js +34 -0
- package/dist/engine/rules/frontend/envVars.js.map +1 -0
- package/dist/engine/rules/frontend/eval.d.ts +3 -0
- package/dist/engine/rules/frontend/eval.js +35 -0
- package/dist/engine/rules/frontend/eval.js.map +1 -0
- package/dist/engine/rules/frontend/index.d.ts +9 -0
- package/dist/engine/rules/frontend/index.js +16 -0
- package/dist/engine/rules/frontend/index.js.map +1 -0
- package/dist/engine/rules/frontend/sourceMaps.d.ts +3 -0
- package/dist/engine/rules/frontend/sourceMaps.js +79 -0
- package/dist/engine/rules/frontend/sourceMaps.js.map +1 -0
- package/dist/engine/rules/frontend/xss.d.ts +3 -0
- package/dist/engine/rules/frontend/xss.js +70 -0
- package/dist/engine/rules/frontend/xss.js.map +1 -0
- package/dist/engine/rules/index.d.ts +8 -0
- package/dist/engine/rules/index.js +38 -0
- package/dist/engine/rules/index.js.map +1 -0
- package/dist/engine/rules/kotlin/index.d.ts +5 -0
- package/dist/engine/rules/kotlin/index.js +8 -0
- package/dist/engine/rules/kotlin/index.js.map +1 -0
- package/dist/engine/rules/kotlin/kotlinxHtml.d.ts +3 -0
- package/dist/engine/rules/kotlin/kotlinxHtml.js +54 -0
- package/dist/engine/rules/kotlin/kotlinxHtml.js.map +1 -0
- package/dist/engine/rules/kotlin/sqlInjection.d.ts +3 -0
- package/dist/engine/rules/kotlin/sqlInjection.js +57 -0
- package/dist/engine/rules/kotlin/sqlInjection.js.map +1 -0
- package/dist/engine/rules/network/cors.d.ts +3 -0
- package/dist/engine/rules/network/cors.js +106 -0
- package/dist/engine/rules/network/cors.js.map +1 -0
- package/dist/engine/rules/network/headers.d.ts +3 -0
- package/dist/engine/rules/network/headers.js +51 -0
- package/dist/engine/rules/network/headers.js.map +1 -0
- package/dist/engine/rules/network/index.d.ts +6 -0
- package/dist/engine/rules/network/index.js +10 -0
- package/dist/engine/rules/network/index.js.map +1 -0
- package/dist/engine/rules/network/tls.d.ts +3 -0
- package/dist/engine/rules/network/tls.js +40 -0
- package/dist/engine/rules/network/tls.js.map +1 -0
- package/dist/engine/rules/observability/errorHandler.d.ts +3 -0
- package/dist/engine/rules/observability/errorHandler.js +34 -0
- package/dist/engine/rules/observability/errorHandler.js.map +1 -0
- package/dist/engine/rules/observability/index.d.ts +5 -0
- package/dist/engine/rules/observability/index.js +8 -0
- package/dist/engine/rules/observability/index.js.map +1 -0
- package/dist/engine/rules/observability/logging.d.ts +3 -0
- package/dist/engine/rules/observability/logging.js +38 -0
- package/dist/engine/rules/observability/logging.js.map +1 -0
- package/dist/engine/rules/php/index.d.ts +6 -0
- package/dist/engine/rules/php/index.js +10 -0
- package/dist/engine/rules/php/index.js.map +1 -0
- package/dist/engine/rules/php/sqlInjection.d.ts +3 -0
- package/dist/engine/rules/php/sqlInjection.js +58 -0
- package/dist/engine/rules/php/sqlInjection.js.map +1 -0
- package/dist/engine/rules/php/weakHashing.d.ts +3 -0
- package/dist/engine/rules/php/weakHashing.js +36 -0
- package/dist/engine/rules/php/weakHashing.js.map +1 -0
- package/dist/engine/rules/php/xss.d.ts +3 -0
- package/dist/engine/rules/php/xss.js +56 -0
- package/dist/engine/rules/php/xss.js.map +1 -0
- package/dist/engine/rules/ruby/index.d.ts +6 -0
- package/dist/engine/rules/ruby/index.js +10 -0
- package/dist/engine/rules/ruby/index.js.map +1 -0
- package/dist/engine/rules/ruby/sqlInjection.d.ts +3 -0
- package/dist/engine/rules/ruby/sqlInjection.js +56 -0
- package/dist/engine/rules/ruby/sqlInjection.js.map +1 -0
- package/dist/engine/rules/ruby/strongParams.d.ts +3 -0
- package/dist/engine/rules/ruby/strongParams.js +35 -0
- package/dist/engine/rules/ruby/strongParams.js.map +1 -0
- package/dist/engine/rules/ruby/xss.d.ts +3 -0
- package/dist/engine/rules/ruby/xss.js +55 -0
- package/dist/engine/rules/ruby/xss.js.map +1 -0
- package/dist/engine/rules/rust/index.d.ts +6 -0
- package/dist/engine/rules/rust/index.js +10 -0
- package/dist/engine/rules/rust/index.js.map +1 -0
- package/dist/engine/rules/rust/secretInCode.d.ts +3 -0
- package/dist/engine/rules/rust/secretInCode.js +36 -0
- package/dist/engine/rules/rust/secretInCode.js.map +1 -0
- package/dist/engine/rules/rust/sqlInjection.d.ts +3 -0
- package/dist/engine/rules/rust/sqlInjection.js +55 -0
- package/dist/engine/rules/rust/sqlInjection.js.map +1 -0
- package/dist/engine/rules/rust/unsafeExpect.d.ts +3 -0
- package/dist/engine/rules/rust/unsafeExpect.js +56 -0
- package/dist/engine/rules/rust/unsafeExpect.js.map +1 -0
- package/dist/engine/scanner.d.ts +5 -0
- package/dist/engine/scanner.js +135 -0
- package/dist/engine/scanner.js.map +1 -0
- package/dist/engine/walker.d.ts +11 -0
- package/dist/engine/walker.js +90 -0
- package/dist/engine/walker.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +36 -0
- package/dist/server.js +360 -0
- package/dist/server.js.map +1 -0
- package/dist/types.d.ts +92 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- 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
|
+
[](https://www.npmjs.com/package/@korayda4/vibe-security)
|
|
6
|
+
[](https://modelcontextprotocol.io)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](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
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
|
package/dist/cli.js.map
ADDED
|
@@ -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
|