@nodatachat/guard 2.0.0 → 2.2.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.md +5 -5
- package/README.md +5 -5
- package/dist/activation.js +1 -1
- package/dist/cli.js +106 -126
- package/dist/fixers/registry.js +1 -1
- package/dist/reporter.js +29 -2
- package/dist/types.d.ts +10 -2
- package/package.json +3 -3
package/LICENSE.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# NoData Guard License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026
|
|
3
|
+
Copyright (c) 2026 Capsule Ltd. All rights reserved.
|
|
4
4
|
|
|
5
5
|
## Terms
|
|
6
6
|
|
|
7
|
-
This software is proprietary. You may use it under the terms of your
|
|
7
|
+
This software is proprietary. You may use it under the terms of your Capsule subscription agreement.
|
|
8
8
|
|
|
9
9
|
### Permitted Uses
|
|
10
10
|
- Running scans on your own projects with a valid license key
|
|
@@ -19,10 +19,10 @@ This software is proprietary. You may use it under the terms of your NoDataChat
|
|
|
19
19
|
|
|
20
20
|
### Data Privacy
|
|
21
21
|
- Guard runs locally on your machine
|
|
22
|
-
- Source code is never uploaded to
|
|
22
|
+
- Source code is never uploaded to Capsule servers
|
|
23
23
|
- Only metadata reports (table names, counts, scores) are transmitted
|
|
24
24
|
- You can opt out of metadata transmission with --skip-send
|
|
25
25
|
|
|
26
|
-
For the full license agreement, visit: https://
|
|
26
|
+
For the full license agreement, visit: https://nodatacapsule.com/legal/guard-license
|
|
27
27
|
|
|
28
|
-
Contact: legal@
|
|
28
|
+
Contact: legal@nodatacapsule.com
|
package/README.md
CHANGED
|
@@ -79,7 +79,7 @@ npx nodata-guard --license-key $NDC_LICENSE --ci --fail-on critical
|
|
|
79
79
|
|
|
80
80
|
## Dashboard
|
|
81
81
|
|
|
82
|
-
After running Guard, log in at [
|
|
82
|
+
After running Guard, log in at [nodatacapsule.com/guard](https://nodatacapsule.com/guard) with your license key to see:
|
|
83
83
|
|
|
84
84
|
- Score trends over time
|
|
85
85
|
- Scan history with comparison
|
|
@@ -114,7 +114,7 @@ Proprietary — see [LICENSE.md](LICENSE.md)
|
|
|
114
114
|
|
|
115
115
|
## Links
|
|
116
116
|
|
|
117
|
-
- [Website](https://
|
|
118
|
-
- [Guard Dashboard](https://
|
|
119
|
-
- [Getting Started](https://
|
|
120
|
-
- [Pricing](https://
|
|
117
|
+
- [Website](https://nodatacapsule.com)
|
|
118
|
+
- [Guard Dashboard](https://nodatacapsule.com/guard)
|
|
119
|
+
- [Getting Started](https://nodatacapsule.com/guard/onboarding)
|
|
120
|
+
- [Pricing](https://nodatacapsule.com/pricing)
|
package/dist/activation.js
CHANGED
|
@@ -18,7 +18,7 @@ const crypto_1 = require("crypto");
|
|
|
18
18
|
const fs_1 = require("fs");
|
|
19
19
|
const path_1 = require("path");
|
|
20
20
|
const GUARD_VERSION = "2.0.0";
|
|
21
|
-
const API_BASE = process.env.GUARD_API_BASE || "https://
|
|
21
|
+
const API_BASE = process.env.GUARD_API_BASE || "https://nodatacapsule.com/api/guard";
|
|
22
22
|
// Dev license keys that work without server (for development and demos)
|
|
23
23
|
const DEV_LICENSES = new Set(["NDC-DEV", "NDC-DEMO", "NDC-TEST"]);
|
|
24
24
|
// ── Project fingerprint (directory structure only, no file contents) ──
|
package/dist/cli.js
CHANGED
|
@@ -22,7 +22,6 @@ const activation_1 = require("./activation");
|
|
|
22
22
|
const code_scanner_1 = require("./code-scanner");
|
|
23
23
|
const db_scanner_1 = require("./db-scanner");
|
|
24
24
|
const reporter_1 = require("./reporter");
|
|
25
|
-
const registry_1 = require("./fixers/registry");
|
|
26
25
|
const scheduler_1 = require("./fixers/scheduler");
|
|
27
26
|
const VERSION = "2.0.0";
|
|
28
27
|
async function main() {
|
|
@@ -35,10 +34,8 @@ async function main() {
|
|
|
35
34
|
let failOn = null;
|
|
36
35
|
let outputDir = process.cwd();
|
|
37
36
|
let skipSend = false;
|
|
38
|
-
let fixMode = "none";
|
|
39
37
|
let schedulePreset;
|
|
40
38
|
let ciProvider;
|
|
41
|
-
let onlyFixers;
|
|
42
39
|
for (let i = 0; i < args.length; i++) {
|
|
43
40
|
switch (args[i]) {
|
|
44
41
|
case "--license-key":
|
|
@@ -63,13 +60,14 @@ async function main() {
|
|
|
63
60
|
skipSend = true;
|
|
64
61
|
break;
|
|
65
62
|
case "--fix-plan":
|
|
66
|
-
|
|
63
|
+
console.log("\n ⚠ --fix-plan is deprecated. Capsule provides recommendations only.\n Run without this flag to see recommendations.\n");
|
|
67
64
|
break;
|
|
68
65
|
case "--fix":
|
|
69
|
-
|
|
66
|
+
console.log("\n ⚠ --fix is deprecated. Capsule does not modify your code.\n Recommendations are provided in the scan output.\n");
|
|
70
67
|
break;
|
|
71
68
|
case "--fix-only":
|
|
72
|
-
|
|
69
|
+
i++;
|
|
70
|
+
console.log("\n ⚠ --fix-only is deprecated. Capsule provides recommendations only.\n");
|
|
73
71
|
break;
|
|
74
72
|
case "--schedule":
|
|
75
73
|
schedulePreset = args[++i] || "weekly";
|
|
@@ -87,12 +85,44 @@ async function main() {
|
|
|
87
85
|
}
|
|
88
86
|
// Env fallbacks
|
|
89
87
|
if (!licenseKey)
|
|
90
|
-
licenseKey = process.env.NDC_LICENSE || process.env.NODATA_LICENSE_KEY;
|
|
88
|
+
licenseKey = process.env.NDC_LICENSE || process.env.NODATA_LICENSE_KEY || process.env.NODATA_API_KEY || process.env.NDC_API_KEY;
|
|
91
89
|
if (!dbUrl)
|
|
92
90
|
dbUrl = process.env.DATABASE_URL;
|
|
91
|
+
// Shared Protect config fallback (~/.nodata/config.json)
|
|
93
92
|
if (!licenseKey) {
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
try {
|
|
94
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
95
|
+
const configPath = require("path").join(home, ".nodata", "config.json");
|
|
96
|
+
if (require("fs").existsSync(configPath)) {
|
|
97
|
+
const config = JSON.parse(require("fs").readFileSync(configPath, "utf-8"));
|
|
98
|
+
if (config.api_key) {
|
|
99
|
+
licenseKey = config.api_key;
|
|
100
|
+
console.log(`\n \x1b[32m✓\x1b[0m Using API key from NoData Protect (~/.nodata/config.json)\n`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch { /* ignore */ }
|
|
105
|
+
}
|
|
106
|
+
// Capsule config fallback (.capsule.json)
|
|
107
|
+
if (!licenseKey) {
|
|
108
|
+
try {
|
|
109
|
+
const capsulePath = require("path").join(projectDir, ".capsule.json");
|
|
110
|
+
if (require("fs").existsSync(capsulePath)) {
|
|
111
|
+
const config = JSON.parse(require("fs").readFileSync(capsulePath, "utf-8"));
|
|
112
|
+
if (config.licenseKey) {
|
|
113
|
+
licenseKey = config.licenseKey;
|
|
114
|
+
console.log(`\n \x1b[32m✓\x1b[0m Using API key from Capsule config (.capsule.json)\n`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch { /* ignore */ }
|
|
119
|
+
}
|
|
120
|
+
if (!licenseKey) {
|
|
121
|
+
console.error("\n \x1b[31m✗\x1b[0m No API key found.\n");
|
|
122
|
+
console.error(" Options:");
|
|
123
|
+
console.error(" 1. Run \x1b[36mnpx @nodatachat/protect init\x1b[0m first (creates key automatically)");
|
|
124
|
+
console.error(" 2. Pass \x1b[36m--license-key YOUR_KEY\x1b[0m");
|
|
125
|
+
console.error(" 3. Set \x1b[36mNDC_LICENSE=YOUR_KEY\x1b[0m in environment\n");
|
|
96
126
|
process.exit(1);
|
|
97
127
|
}
|
|
98
128
|
// ── Schedule setup (no scan needed) ──
|
|
@@ -204,12 +234,13 @@ async function main() {
|
|
|
204
234
|
if (!skipSend) {
|
|
205
235
|
log(ciMode, "Sending metadata to NoData...");
|
|
206
236
|
try {
|
|
207
|
-
const res = await fetch("https://
|
|
237
|
+
const res = await fetch("https://nodatacapsule.com/api/guard/report", {
|
|
208
238
|
method: "POST",
|
|
209
239
|
headers: {
|
|
210
240
|
"Content-Type": "application/json",
|
|
211
241
|
"X-Scan-Id": activation.scan_id,
|
|
212
242
|
"X-Activation-Key": activation.activation_key,
|
|
243
|
+
"X-License-Key": licenseKey || "",
|
|
213
244
|
},
|
|
214
245
|
body: JSON.stringify(metadata),
|
|
215
246
|
signal: AbortSignal.timeout(10000),
|
|
@@ -250,102 +281,48 @@ async function main() {
|
|
|
250
281
|
console.log(" Your data never left your machine.");
|
|
251
282
|
console.log(" Diff the two files to verify.\n");
|
|
252
283
|
}
|
|
253
|
-
// ── Step 8:
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
scanResults: {
|
|
261
|
-
piiFields: piiFields.map(f => ({
|
|
262
|
-
table: f.table, column: f.column, pii_type: f.pii_type,
|
|
263
|
-
encrypted: f.encrypted, encryption_pattern: f.encryption_pattern,
|
|
264
|
-
})),
|
|
265
|
-
routes: routes.map(r => ({ path: r.path, has_auth: r.has_auth, auth_type: r.auth_type })),
|
|
266
|
-
secrets: secrets.map(s => ({
|
|
267
|
-
file: s.file, line: s.line, type: s.type,
|
|
268
|
-
severity: s.severity, is_env_interpolated: s.is_env_interpolated,
|
|
269
|
-
})),
|
|
270
|
-
rls: dbResult?.rls || [],
|
|
271
|
-
framework: stack.framework,
|
|
272
|
-
database: stack.database,
|
|
273
|
-
},
|
|
274
|
-
stack: {
|
|
275
|
-
framework: stack.framework,
|
|
276
|
-
database: stack.database,
|
|
277
|
-
language: "typescript",
|
|
278
|
-
hosting: "vercel",
|
|
279
|
-
},
|
|
280
|
-
};
|
|
281
|
-
const capsuleResult = await (0, registry_1.runCapsule)(fixerContext, {
|
|
282
|
-
mode: fixMode === "plan" ? "plan" : "apply",
|
|
283
|
-
fixers: onlyFixers,
|
|
284
|
-
dryRun: fixMode === "plan",
|
|
285
|
-
}, (msg) => log(ciMode, msg));
|
|
286
|
-
if (!ciMode) {
|
|
284
|
+
// ── Step 8: Recommendations ──
|
|
285
|
+
// NOTE: Capsule does NOT auto-fix code. We provide recommendations only.
|
|
286
|
+
// Applying fixes requires understanding of the target system — tables,
|
|
287
|
+
// relationships, business logic — and must be done by the customer's team.
|
|
288
|
+
if (!ciMode) {
|
|
289
|
+
const topFindings = metadata.findings || [];
|
|
290
|
+
if (topFindings.length > 0) {
|
|
287
291
|
console.log("");
|
|
288
292
|
console.log(" ══════════════════════════════════════");
|
|
289
|
-
console.log(
|
|
293
|
+
console.log(" RECOMMENDATIONS");
|
|
290
294
|
console.log(" ══════════════════════════════════════");
|
|
291
|
-
console.log(`
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
295
|
+
console.log(` ${topFindings.length} issues found. Top priorities:\n`);
|
|
296
|
+
const shown = topFindings.slice(0, 10);
|
|
297
|
+
for (const f of shown) {
|
|
298
|
+
const sev = f.severity === "critical" ? "\x1b[31mCRITICAL\x1b[0m"
|
|
299
|
+
: f.severity === "high" ? "\x1b[33mHIGH\x1b[0m"
|
|
300
|
+
: "\x1b[36mMEDIUM\x1b[0m";
|
|
301
|
+
console.log(` ${sev} ${f.title}`);
|
|
302
|
+
console.log(` \x1b[32m→ ${f.fix_suggestion}\x1b[0m`);
|
|
303
|
+
if (f.soc_control)
|
|
304
|
+
console.log(` SOC: ${f.soc_control}`);
|
|
305
|
+
console.log("");
|
|
296
306
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
console.log(` Proof hash: ${capsuleResult.proofHash.slice(0, 16)}...`);
|
|
300
|
-
console.log(" ──────────────────────────────────────");
|
|
301
|
-
// Print per-fixer summary
|
|
302
|
-
for (const plan of capsuleResult.plans) {
|
|
303
|
-
const icon = plan.actions.every(a => a.status === "applied") ? "✅"
|
|
304
|
-
: plan.actions.some(a => a.status === "failed") ? "❌" : "📋";
|
|
305
|
-
console.log(` ${icon} ${plan.nameHe}: ${plan.totalActions} actions (${plan.autoFixable} auto, ${plan.manualRequired} manual)`);
|
|
307
|
+
if (topFindings.length > 10) {
|
|
308
|
+
console.log(` ... and ${topFindings.length - 10} more. See full report.\n`);
|
|
306
309
|
}
|
|
310
|
+
console.log(" ──────────────────────────────────────");
|
|
311
|
+
console.log(" ⚠ Capsule provides recommendations only.");
|
|
312
|
+
console.log(" Fixes require understanding of your system —");
|
|
313
|
+
console.log(" tables, relationships, and business logic.");
|
|
314
|
+
console.log(" Work with your team to apply changes safely.");
|
|
307
315
|
console.log(" ══════════════════════════════════════\n");
|
|
308
|
-
//
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
(
|
|
312
|
-
log(ciMode, `Fix plan saved: ${plansPath}`);
|
|
313
|
-
// Write SQL migrations to files
|
|
314
|
-
for (const plan of capsuleResult.plans) {
|
|
315
|
-
for (const action of plan.actions) {
|
|
316
|
-
if (action.type === "file-create" && action.target.includes("migrations/")) {
|
|
317
|
-
const migPath = (0, path_1.resolve)(outputDir, action.target);
|
|
318
|
-
const migDir = (0, path_1.resolve)(outputDir, "migrations");
|
|
319
|
-
if (!(0, fs_1.existsSync)(migDir)) {
|
|
320
|
-
const { mkdirSync } = require("fs");
|
|
321
|
-
mkdirSync(migDir, { recursive: true });
|
|
322
|
-
}
|
|
323
|
-
(0, fs_1.writeFileSync)(migPath, action.content, "utf-8");
|
|
324
|
-
log(ciMode, `Migration: ${migPath}`);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
// Send notifications if configured
|
|
331
|
-
if (config.notify && fixMode === "apply") {
|
|
332
|
-
const notifyPayload = {
|
|
333
|
-
event: capsuleResult.applied > 0 ? "fix-applied" : "scan-complete",
|
|
334
|
-
timestamp: new Date().toISOString(),
|
|
335
|
-
projectName: full.project_name || "unknown",
|
|
336
|
-
projectHash: full.proof_hash?.slice(0, 16) || "",
|
|
316
|
+
// Save recommendations to file
|
|
317
|
+
const recsPath = (0, path_1.resolve)(outputDir, "nodata-recommendations.json");
|
|
318
|
+
(0, fs_1.writeFileSync)(recsPath, JSON.stringify({
|
|
319
|
+
generated_at: new Date().toISOString(),
|
|
337
320
|
score: full.overall_score,
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
fixesApplied: capsuleResult.applied,
|
|
344
|
-
fixesFailed: capsuleResult.failed,
|
|
345
|
-
scanId: activation.scan_id,
|
|
346
|
-
proofHash: capsuleResult.proofHash,
|
|
347
|
-
};
|
|
348
|
-
await (0, registry_1.sendNotifications)(config, notifyPayload.event, notifyPayload, (msg) => log(ciMode, msg));
|
|
321
|
+
total_findings: topFindings.length,
|
|
322
|
+
disclaimer: "These are recommendations only. Capsule does not modify your code. Fixes require understanding of your system architecture, database schema, and business logic. Work with your development team to implement changes safely.",
|
|
323
|
+
recommendations: topFindings,
|
|
324
|
+
}, null, 2), "utf-8");
|
|
325
|
+
log(ciMode, `Recommendations: ${recsPath}`);
|
|
349
326
|
}
|
|
350
327
|
}
|
|
351
328
|
// ── CI mode: exit code ──
|
|
@@ -392,14 +369,16 @@ function loadOrCreateConfig(projectDir, overrides) {
|
|
|
392
369
|
}
|
|
393
370
|
function printHelp() {
|
|
394
371
|
console.log(`
|
|
395
|
-
NoData Guard v${VERSION} — Security Scanner +
|
|
372
|
+
NoData Guard v${VERSION} — Security Scanner + Recommendations
|
|
373
|
+
|
|
374
|
+
Scans your project locally for security issues and provides
|
|
375
|
+
actionable recommendations. Your code never leaves your machine.
|
|
376
|
+
Capsule does NOT modify your code — only you and your team can.
|
|
396
377
|
|
|
397
378
|
Usage:
|
|
398
|
-
npx nodata-guard --license-key NDC-XXXX
|
|
399
|
-
npx nodata-guard --license-key NDC-XXXX --
|
|
400
|
-
npx nodata-guard --license-key NDC-XXXX --
|
|
401
|
-
npx nodata-guard --license-key NDC-XXXX --schedule weekly # Setup CI schedule
|
|
402
|
-
npx nodata-guard --license-key NDC-XXXX --db $DATABASE_URL --fix # Full scan + fix
|
|
379
|
+
npx nodata-guard --license-key NDC-XXXX # Scan + recommend
|
|
380
|
+
npx nodata-guard --license-key NDC-XXXX --db $DATABASE_URL # Full scan (code + DB)
|
|
381
|
+
npx nodata-guard --license-key NDC-XXXX --schedule weekly # Setup CI schedule
|
|
403
382
|
|
|
404
383
|
Scan Options:
|
|
405
384
|
--license-key <key> NoData license key (or set NDC_LICENSE env var)
|
|
@@ -410,13 +389,6 @@ function printHelp() {
|
|
|
410
389
|
--fail-on <level> Exit 1 if issues at: critical | high | medium
|
|
411
390
|
--skip-send Don't send metadata to NoData
|
|
412
391
|
|
|
413
|
-
Capsule Options:
|
|
414
|
-
--fix-plan Generate fix plan (dry-run, no changes)
|
|
415
|
-
--fix Apply all auto-fixable remediation
|
|
416
|
-
--fix-only <fixers> Only run specific fixers (comma-separated)
|
|
417
|
-
Fixers: pii-encrypt, rls, secrets, routes-auth,
|
|
418
|
-
headers, csrf, rate-limit, gitignore
|
|
419
|
-
|
|
420
392
|
Schedule Options:
|
|
421
393
|
--schedule <preset> Install CI workflow: daily | weekly | monthly
|
|
422
394
|
--ci-provider <name> CI provider: github-actions | gitlab-ci | bitbucket | auto
|
|
@@ -425,31 +397,39 @@ function printHelp() {
|
|
|
425
397
|
Configure in .nodata-guard.json → notify: { email, webhook, slack, telegram }
|
|
426
398
|
|
|
427
399
|
Output files:
|
|
428
|
-
nodata-full-report.json
|
|
429
|
-
nodata-metadata-only.json
|
|
430
|
-
nodata-
|
|
431
|
-
migrations/*.sql Generated SQL migrations
|
|
400
|
+
nodata-full-report.json Full report — STAYS LOCAL
|
|
401
|
+
nodata-metadata-only.json Metadata only — sent to dashboard
|
|
402
|
+
nodata-recommendations.json Prioritized recommendations
|
|
432
403
|
|
|
433
|
-
What we
|
|
434
|
-
|
|
404
|
+
What we provide:
|
|
405
|
+
✓ Scan — find weak points (PII, routes, secrets, encryption)
|
|
406
|
+
✓ Recommend — prioritized actions with SOC control mapping
|
|
407
|
+
✓ Prove — cryptographic proof chain of scan results
|
|
408
|
+
✓ Monitor — track score over time via dashboard
|
|
409
|
+
|
|
410
|
+
What we NEVER do:
|
|
411
|
+
✗ Modify your code, database, or configuration
|
|
412
|
+
✗ Receive data values, source code, or credentials
|
|
413
|
+
|
|
414
|
+
Important:
|
|
415
|
+
Recommendations require understanding of YOUR system —
|
|
416
|
+
tables, relationships, business logic. Work with your team
|
|
417
|
+
to implement changes safely.
|
|
435
418
|
|
|
436
419
|
Examples:
|
|
437
|
-
#
|
|
420
|
+
# Scan and get recommendations
|
|
438
421
|
npx nodata-guard --license-key NDC-XXXX
|
|
439
422
|
|
|
440
|
-
# Full scan
|
|
441
|
-
npx nodata-guard --license-key NDC-XXXX --db $DATABASE_URL
|
|
442
|
-
|
|
443
|
-
# Only fix security headers and CSRF
|
|
444
|
-
npx nodata-guard --license-key NDC-XXXX --fix --fix-only headers,csrf
|
|
423
|
+
# Full scan with DB probe
|
|
424
|
+
npx nodata-guard --license-key NDC-XXXX --db $DATABASE_URL
|
|
445
425
|
|
|
446
426
|
# Setup weekly CI scan with GitHub Actions
|
|
447
427
|
npx nodata-guard --license-key NDC-XXXX --schedule weekly
|
|
448
428
|
|
|
449
|
-
# CI pipeline
|
|
429
|
+
# CI pipeline — fail on critical issues
|
|
450
430
|
npx nodata-guard --ci --fail-on critical
|
|
451
431
|
|
|
452
|
-
Documentation: https://
|
|
432
|
+
Documentation: https://nodatacapsule.com/guard
|
|
453
433
|
`);
|
|
454
434
|
}
|
|
455
435
|
main().catch((err) => {
|
package/dist/fixers/registry.js
CHANGED
|
@@ -156,7 +156,7 @@ async function sendNotifications(config, event, payload, onLog) {
|
|
|
156
156
|
if (config.notify.email?.length) {
|
|
157
157
|
for (const email of config.notify.email) {
|
|
158
158
|
try {
|
|
159
|
-
await fetch("https://
|
|
159
|
+
await fetch("https://nodatacapsule.com/api/guard/notify", {
|
|
160
160
|
method: "POST",
|
|
161
161
|
headers: { "Content-Type": "application/json" },
|
|
162
162
|
body: JSON.stringify({ channel: "email", to: email, payload }),
|
package/dist/reporter.js
CHANGED
|
@@ -123,8 +123,35 @@ function generateReports(input) {
|
|
|
123
123
|
critical: criticalSecrets,
|
|
124
124
|
high: highSecrets + plaintextPII,
|
|
125
125
|
medium: medSecrets,
|
|
126
|
-
|
|
126
|
+
recommendations_available: plaintextPII, // each plaintext field has a recommendation
|
|
127
127
|
},
|
|
128
|
+
// Fix suggestions — safe to send (just recommendation text, no code/values)
|
|
129
|
+
findings: [
|
|
130
|
+
...allPII.filter(f => !f.encrypted).map(f => ({
|
|
131
|
+
severity: "high",
|
|
132
|
+
category: "pii",
|
|
133
|
+
rule_id: `PII_${f.pii_type.toUpperCase()}`,
|
|
134
|
+
title: `${f.table}.${f.column} — ${f.pii_type} stored in plaintext`,
|
|
135
|
+
fix_suggestion: `Add AES-256-GCM encryption to ${f.table}.${f.column} via Capsule field permissions`,
|
|
136
|
+
soc_control: "CC6.7",
|
|
137
|
+
})),
|
|
138
|
+
...(input.code?.secrets.filter(s => !s.is_env_interpolated) || []).map(s => ({
|
|
139
|
+
severity: s.severity,
|
|
140
|
+
category: "secrets",
|
|
141
|
+
rule_id: `SECRET_${s.type.toUpperCase().replace(/-/g, "_")}`,
|
|
142
|
+
title: `Hardcoded ${s.type} in ${s.file}:${s.line}`,
|
|
143
|
+
fix_suggestion: `Move to .env and encrypt with @nodatachat/protect`,
|
|
144
|
+
soc_control: "CC6.1",
|
|
145
|
+
})),
|
|
146
|
+
...(input.code?.routes.filter(r => !r.has_auth) || []).slice(0, 20).map(r => ({
|
|
147
|
+
severity: "high",
|
|
148
|
+
category: "auth",
|
|
149
|
+
rule_id: "ROUTE_NO_AUTH",
|
|
150
|
+
title: `Unprotected route: ${r.path}`,
|
|
151
|
+
fix_suggestion: `Add authentication middleware (withAuth or API key check)`,
|
|
152
|
+
soc_control: "CC6.3",
|
|
153
|
+
})),
|
|
154
|
+
],
|
|
128
155
|
proof_hash: proofHash,
|
|
129
156
|
privacy: {
|
|
130
157
|
contains_data_values: false,
|
|
@@ -176,7 +203,7 @@ function generateReports(input) {
|
|
|
176
203
|
critical_issues: criticalSecrets,
|
|
177
204
|
high_issues: highSecrets + plaintextPII,
|
|
178
205
|
medium_issues: medSecrets,
|
|
179
|
-
|
|
206
|
+
recommendations_available: plaintextPII,
|
|
180
207
|
},
|
|
181
208
|
proof_hash: proofHash,
|
|
182
209
|
metadata_preview: metadata,
|
package/dist/types.d.ts
CHANGED
|
@@ -84,7 +84,7 @@ export interface FullReport {
|
|
|
84
84
|
critical_issues: number;
|
|
85
85
|
high_issues: number;
|
|
86
86
|
medium_issues: number;
|
|
87
|
-
|
|
87
|
+
recommendations_available: number;
|
|
88
88
|
};
|
|
89
89
|
proof_hash: string;
|
|
90
90
|
metadata_preview: MetadataReport;
|
|
@@ -140,8 +140,16 @@ export interface MetadataReport {
|
|
|
140
140
|
critical: number;
|
|
141
141
|
high: number;
|
|
142
142
|
medium: number;
|
|
143
|
-
|
|
143
|
+
recommendations_available: number;
|
|
144
144
|
};
|
|
145
|
+
findings: Array<{
|
|
146
|
+
severity: "critical" | "high" | "medium" | "low";
|
|
147
|
+
category: string;
|
|
148
|
+
rule_id: string;
|
|
149
|
+
title: string;
|
|
150
|
+
fix_suggestion: string;
|
|
151
|
+
soc_control: string;
|
|
152
|
+
}>;
|
|
145
153
|
proof_hash: string;
|
|
146
154
|
privacy: {
|
|
147
155
|
contains_data_values: false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodatachat/guard",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "NoData Guard — continuous security scanner. Runs locally, reports only metadata. Your data never leaves your machine.",
|
|
5
5
|
"main": "./dist/cli.js",
|
|
6
6
|
"types": "./dist/cli.d.ts",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"devsecops"
|
|
50
50
|
],
|
|
51
51
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
52
|
-
"homepage": "https://
|
|
52
|
+
"homepage": "https://nodatacapsule.com/guard",
|
|
53
53
|
"repository": {
|
|
54
54
|
"type": "git",
|
|
55
55
|
"url": "https://github.com/nodatachat/guard"
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"bugs": {
|
|
58
58
|
"url": "https://github.com/nodatachat/guard/issues"
|
|
59
59
|
},
|
|
60
|
-
"author": "
|
|
60
|
+
"author": "Capsule <support@nodatacapsule.com>"
|
|
61
61
|
}
|