bastion-scan 0.1.0 → 0.1.3
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 +9 -9
- package/dist/index.js +118 -13
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<img alt="Build" src="https://img.shields.io/github/actions/workflow/status/absastreon/bastion/ci.yml?branch=main&style=flat-square" />
|
|
8
8
|
<img alt="Tests" src="https://img.shields.io/badge/tests-783%20passing-brightgreen?style=flat-square" />
|
|
9
9
|
<img alt="License" src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" />
|
|
10
|
-
<img alt="npm" src="https://img.shields.io/npm/v
|
|
10
|
+
<img alt="npm" src="https://img.shields.io/npm/v/bastion-scan?style=flat-square" />
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
13
|
---
|
|
@@ -26,19 +26,19 @@ Every finding comes with a prompt you can paste into Claude, ChatGPT, or Copilot
|
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
28
|
# Install globally
|
|
29
|
-
npm install -g
|
|
29
|
+
npm install -g bastion-scan
|
|
30
30
|
|
|
31
31
|
# Scan your project
|
|
32
|
-
npx bastion scan
|
|
32
|
+
npx bastion-scan scan
|
|
33
33
|
|
|
34
34
|
# Scan a live URL (headers, SSL, security.txt)
|
|
35
|
-
npx bastion scan --url https://yourapp.com
|
|
35
|
+
npx bastion-scan scan --url https://yourapp.com
|
|
36
36
|
|
|
37
37
|
# JSON output for CI/CD
|
|
38
|
-
npx bastion scan --format json
|
|
38
|
+
npx bastion-scan scan --format json
|
|
39
39
|
|
|
40
40
|
# Generate security configs for your stack
|
|
41
|
-
npx bastion scan --generate-configs
|
|
41
|
+
npx bastion-scan scan --generate-configs
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
---
|
|
@@ -48,7 +48,7 @@ npx bastion scan --generate-configs
|
|
|
48
48
|
| Check | What it does |
|
|
49
49
|
|-------|-------------|
|
|
50
50
|
| `.gitignore` coverage | Makes sure `.env`, `node_modules`, and keys are excluded |
|
|
51
|
-
| Hardcoded secrets |
|
|
51
|
+
| Hardcoded secrets | API keys from OpenAI, Anthropic, GitHub, Stripe, AWS, Google, Slack, and more |
|
|
52
52
|
| Dependency audit | Wraps `npm audit` and maps findings to severity levels |
|
|
53
53
|
| `.env.example` | Checks that a template exists with safe placeholder values |
|
|
54
54
|
| `security.txt` | Validates RFC 9116 Contact + Expires fields |
|
|
@@ -83,7 +83,7 @@ Bastion can output ready-to-paste configs for your stack:
|
|
|
83
83
|
Interactive CLI that walks you through creating a valid RFC 9116 `security.txt`:
|
|
84
84
|
|
|
85
85
|
```bash
|
|
86
|
-
npx bastion generate security-txt
|
|
86
|
+
npx bastion-scan generate security-txt
|
|
87
87
|
```
|
|
88
88
|
|
|
89
89
|
---
|
|
@@ -193,7 +193,7 @@ Floor is 0. Only `fail` results deduct. `warn`, `skip`, and `pass` don't affect
|
|
|
193
193
|
```
|
|
194
194
|
bastion/
|
|
195
195
|
├── packages/
|
|
196
|
-
│ ├── cli/ # npx bastion scan, 12 checks, 3 reporters
|
|
196
|
+
│ ├── cli/ # npx bastion-scan scan, 12 checks, 3 reporters
|
|
197
197
|
│ ├── shared/ # Types, checklist data, OWASP data, tools
|
|
198
198
|
│ └── web/ # Next.js 14 dashboard
|
|
199
199
|
└── docs/playbooks/ # Stack-specific security guides
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { createRequire } from "module";
|
|
|
7
7
|
import { Command, Option } from "commander";
|
|
8
8
|
import chalk2 from "chalk";
|
|
9
9
|
import ora from "ora";
|
|
10
|
-
import { OUTPUT_FORMATS } from "
|
|
10
|
+
import { OUTPUT_FORMATS } from "bastion-shared";
|
|
11
11
|
|
|
12
12
|
// src/scanner.ts
|
|
13
13
|
import { readdir, readFile as readFile9, stat } from "fs/promises";
|
|
@@ -196,40 +196,145 @@ var SCANNABLE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
196
196
|
]);
|
|
197
197
|
var IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", "build", ".git", "tests", "__tests__", "test", "fixtures"]);
|
|
198
198
|
var SECRET_PATTERNS = [
|
|
199
|
+
// ── OpenAI ──────────────────────────────────────────────────────────────
|
|
199
200
|
{
|
|
200
|
-
name: "OpenAI API key",
|
|
201
|
-
regex: /sk-[
|
|
202
|
-
description: "OpenAI API key detected"
|
|
201
|
+
name: "OpenAI API key (project)",
|
|
202
|
+
regex: /sk-proj-[a-zA-Z0-9_-]{20,}/,
|
|
203
|
+
description: "OpenAI project API key detected",
|
|
204
|
+
severity: "critical"
|
|
203
205
|
},
|
|
206
|
+
{
|
|
207
|
+
name: "OpenAI API key (service account)",
|
|
208
|
+
regex: /sk-svcacct-[a-zA-Z0-9_-]{20,}/,
|
|
209
|
+
description: "OpenAI service account key detected",
|
|
210
|
+
severity: "critical"
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
name: "OpenAI API key (legacy)",
|
|
214
|
+
regex: /sk-[a-zA-Z0-9]{20,}/,
|
|
215
|
+
description: "OpenAI API key detected",
|
|
216
|
+
severity: "critical"
|
|
217
|
+
},
|
|
218
|
+
// ── Anthropic ───────────────────────────────────────────────────────────
|
|
219
|
+
{
|
|
220
|
+
name: "Anthropic API key",
|
|
221
|
+
regex: /sk-ant-api[0-9]{2}-[a-zA-Z0-9_-]{40,}/,
|
|
222
|
+
description: "Anthropic API key detected",
|
|
223
|
+
severity: "critical"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: "Anthropic admin key",
|
|
227
|
+
regex: /sk-ant-admin[0-9]{2}-[a-zA-Z0-9_-]{40,}/,
|
|
228
|
+
description: "Anthropic admin API key detected",
|
|
229
|
+
severity: "critical"
|
|
230
|
+
},
|
|
231
|
+
// ── GitHub ──────────────────────────────────────────────────────────────
|
|
232
|
+
{
|
|
233
|
+
name: "GitHub PAT (classic)",
|
|
234
|
+
regex: /ghp_[a-zA-Z0-9]{36}/,
|
|
235
|
+
description: "GitHub personal access token (classic) detected",
|
|
236
|
+
severity: "critical"
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: "GitHub OAuth token",
|
|
240
|
+
regex: /gho_[a-zA-Z0-9]{36}/,
|
|
241
|
+
description: "GitHub OAuth access token detected",
|
|
242
|
+
severity: "critical"
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
name: "GitHub PAT (fine-grained)",
|
|
246
|
+
regex: /github_pat_[a-zA-Z0-9_]{82}/,
|
|
247
|
+
description: "GitHub fine-grained personal access token detected",
|
|
248
|
+
severity: "critical"
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
name: "GitHub app token",
|
|
252
|
+
regex: /ghs_[a-zA-Z0-9]{36}/,
|
|
253
|
+
description: "GitHub app installation token detected",
|
|
254
|
+
severity: "critical"
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
name: "GitHub refresh token",
|
|
258
|
+
regex: /ghr_[a-zA-Z0-9]{36}/,
|
|
259
|
+
description: "GitHub refresh token detected",
|
|
260
|
+
severity: "critical"
|
|
261
|
+
},
|
|
262
|
+
// ── Stripe ──────────────────────────────────────────────────────────────
|
|
204
263
|
{
|
|
205
264
|
name: "Stripe secret key",
|
|
206
|
-
regex: /sk_live_[
|
|
207
|
-
description: "Stripe secret key detected"
|
|
265
|
+
regex: /sk_live_[a-zA-Z0-9]{24,}/,
|
|
266
|
+
description: "Stripe secret key detected",
|
|
267
|
+
severity: "critical"
|
|
208
268
|
},
|
|
209
269
|
{
|
|
210
270
|
name: "Stripe publishable key",
|
|
211
|
-
regex: /pk_live_[
|
|
212
|
-
description: "Stripe publishable live key detected"
|
|
271
|
+
regex: /pk_live_[a-zA-Z0-9]{20,}/,
|
|
272
|
+
description: "Stripe publishable live key detected",
|
|
273
|
+
severity: "critical"
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
name: "Stripe restricted key",
|
|
277
|
+
regex: /rk_live_[a-zA-Z0-9]{24,}/,
|
|
278
|
+
description: "Stripe restricted API key detected",
|
|
279
|
+
severity: "critical"
|
|
213
280
|
},
|
|
281
|
+
{
|
|
282
|
+
name: "Stripe test key",
|
|
283
|
+
regex: /sk_test_[a-zA-Z0-9]{24,}/,
|
|
284
|
+
description: "Stripe test secret key detected",
|
|
285
|
+
severity: "high"
|
|
286
|
+
},
|
|
287
|
+
// ── AWS ─────────────────────────────────────────────────────────────────
|
|
214
288
|
{
|
|
215
289
|
name: "AWS access key",
|
|
216
290
|
regex: /AKIA[0-9A-Z]{16}/,
|
|
217
|
-
description: "AWS access key ID detected"
|
|
291
|
+
description: "AWS access key ID detected",
|
|
292
|
+
severity: "critical"
|
|
293
|
+
},
|
|
294
|
+
// ── Google ──────────────────────────────────────────────────────────────
|
|
295
|
+
{
|
|
296
|
+
name: "Google API key",
|
|
297
|
+
regex: /AIza[a-zA-Z0-9_-]{35}/,
|
|
298
|
+
description: "Google API key detected",
|
|
299
|
+
severity: "critical"
|
|
218
300
|
},
|
|
301
|
+
// ── Slack ───────────────────────────────────────────────────────────────
|
|
302
|
+
{
|
|
303
|
+
name: "Slack bot token",
|
|
304
|
+
regex: /xoxb-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,}/,
|
|
305
|
+
description: "Slack bot token detected",
|
|
306
|
+
severity: "critical"
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
name: "Slack user token",
|
|
310
|
+
regex: /xoxp-[0-9]{10,13}-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{32}/,
|
|
311
|
+
description: "Slack user token detected",
|
|
312
|
+
severity: "critical"
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
name: "Slack webhook URL",
|
|
316
|
+
regex: /https:\/\/hooks\.slack\.com\/services\/T[a-zA-Z0-9]{8,12}\/B[a-zA-Z0-9]{8,12}\/[a-zA-Z0-9]{24}/,
|
|
317
|
+
description: "Slack incoming webhook URL detected",
|
|
318
|
+
severity: "high"
|
|
319
|
+
},
|
|
320
|
+
// ── Generic ─────────────────────────────────────────────────────────────
|
|
219
321
|
{
|
|
220
322
|
name: "Generic API key assignment",
|
|
221
323
|
regex: /(?:api[_-]?key|apikey|api[_-]?secret)\s*[:=]\s*['"][A-Za-z0-9_\-/.]{8,}['"]/i,
|
|
222
|
-
description: "Hardcoded API key assignment detected"
|
|
324
|
+
description: "Hardcoded API key assignment detected",
|
|
325
|
+
severity: "critical"
|
|
223
326
|
},
|
|
224
327
|
{
|
|
225
328
|
name: "Bearer token",
|
|
226
329
|
regex: /['"]Bearer\s+[A-Za-z0-9_\-/.+]{20,}['"]/,
|
|
227
|
-
description: "Hardcoded Bearer token detected"
|
|
330
|
+
description: "Hardcoded Bearer token detected",
|
|
331
|
+
severity: "critical"
|
|
228
332
|
},
|
|
229
333
|
{
|
|
230
334
|
name: "Database connection string",
|
|
231
335
|
regex: /(?:mongodb(?:\+srv)?|postgres(?:ql)?|mysql|redis):\/\/[^:]+:[^@\s]+@/i,
|
|
232
|
-
description: "Database connection string with embedded password detected"
|
|
336
|
+
description: "Database connection string with embedded password detected",
|
|
337
|
+
severity: "critical"
|
|
233
338
|
}
|
|
234
339
|
];
|
|
235
340
|
var AI_PROMPT = "I found a hardcoded secret in my source code. Help me move it to an environment variable. Show me: (1) how to add it to .env, (2) how to read it with process.env or the equivalent for my framework, (3) how to add the key name to .env.example with a placeholder value, and (4) how to validate at startup that the variable is set.";
|
|
@@ -265,7 +370,7 @@ function scanContent(content, relativePath) {
|
|
|
265
370
|
id: "secrets",
|
|
266
371
|
name: `Hardcoded secret: ${pattern.name}`,
|
|
267
372
|
status: "fail",
|
|
268
|
-
severity:
|
|
373
|
+
severity: pattern.severity,
|
|
269
374
|
category: "Secrets",
|
|
270
375
|
location: `${relativePath}:${i + 1}`,
|
|
271
376
|
description: pattern.description,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bastion-scan",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Privacy-first security checker for web projects. 15 checks, zero data uploaded, actionable fixes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"dev": "tsup --watch"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"
|
|
40
|
+
"bastion-shared": "^0.1.3",
|
|
41
41
|
"chalk": "^5.6.2",
|
|
42
42
|
"commander": "^14.0.3",
|
|
43
43
|
"ora": "^9.3.0"
|