secure-repo 1.0.5 → 1.0.7

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 (2) hide show
  1. package/bin/cli.js +31 -4
  2. package/package.json +1 -1
package/bin/cli.js CHANGED
@@ -31,7 +31,7 @@ const DANGER_PATTERNS = [
31
31
  { pattern: /sk_live_[a-zA-Z0-9]+/, label: "Stripe live secret key" },
32
32
  { pattern: /sk_test_[a-zA-Z0-9]+/, label: "Stripe test secret key" },
33
33
  { pattern: /eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+/, label: "JWT token" },
34
- { pattern: /SUPABASE_SERVICE_ROLE_KEY/, label: "Supabase service role key reference" },
34
+ { pattern: /SUPABASE_SERVICE_ROLE_KEY\s*[:=]\s*['"][^'"]+['"]/, label: "Hardcoded Supabase service role key" },
35
35
  { pattern: /password\s*[:=]\s*['"][^'"]+['"]/, label: "Hardcoded password" },
36
36
  { pattern: /api[_-]?key\s*[:=]\s*['"][^'"]+['"]/, label: "Hardcoded API key" },
37
37
  ];
@@ -393,6 +393,19 @@ function audit() {
393
393
  let issues = 0;
394
394
  let warnings = 0;
395
395
  let passed = 0;
396
+ let score = 0;
397
+
398
+ // Score weights (total = 100)
399
+ const SCORE_WEIGHTS = {
400
+ policyHigh: 10, // 4 high-severity files × 10 = 40
401
+ policyMedium: 5, // 3 medium-severity files × 5 = 15
402
+ gitignoreEnv: 10, // .env in .gitignore
403
+ noEnvFiles: 10, // no committed .env files
404
+ envExample: 5, // .env.example exists
405
+ noSecrets: 10, // no hardcoded secrets
406
+ noInsecureUrls: 5, // no http:// in env example
407
+ lockFile: 5, // dependency lock file
408
+ };
396
409
 
397
410
  // --- Check for recommended files ---
398
411
  console.log(" Policy files:");
@@ -401,6 +414,7 @@ function audit() {
401
414
  if (fs.existsSync(filePath)) {
402
415
  console.log(` [pass] ${file}`);
403
416
  passed++;
417
+ score += severity === "high" ? SCORE_WEIGHTS.policyHigh : SCORE_WEIGHTS.policyMedium;
404
418
  } else if (severity === "high") {
405
419
  console.log(` [FAIL] ${file} — missing (high priority)`);
406
420
  issues++;
@@ -418,6 +432,7 @@ function audit() {
418
432
  if (gitignore.includes(".env")) {
419
433
  console.log(" [pass] .env is in .gitignore");
420
434
  passed++;
435
+ score += SCORE_WEIGHTS.gitignoreEnv;
421
436
  } else {
422
437
  console.log(" [FAIL] .env is NOT in .gitignore");
423
438
  issues++;
@@ -429,19 +444,25 @@ function audit() {
429
444
 
430
445
  // --- Check for committed .env files ---
431
446
  const envFiles = [".env", ".env.local", ".env.production"];
447
+ let envFilesFound = 0;
432
448
  envFiles.forEach((envFile) => {
433
449
  const envPath = path.join(targetDir, envFile);
434
450
  if (fs.existsSync(envPath)) {
435
451
  console.log(` [FAIL] ${envFile} exists in repo — may contain secrets`);
436
452
  issues++;
453
+ envFilesFound++;
437
454
  }
438
455
  });
456
+ if (envFilesFound === 0) {
457
+ score += SCORE_WEIGHTS.noEnvFiles;
458
+ }
439
459
 
440
460
  // --- Check for .env.example ---
441
461
  const envExamplePath = path.join(targetDir, ".env.example");
442
462
  if (fs.existsSync(envExamplePath)) {
443
463
  console.log(" [pass] .env.example exists");
444
464
  passed++;
465
+ score += SCORE_WEIGHTS.envExample;
445
466
  } else {
446
467
  console.log(" [warn] .env.example missing — document required env vars");
447
468
  warnings++;
@@ -487,6 +508,7 @@ function audit() {
487
508
  if (secretsFound === 0) {
488
509
  console.log(" [pass] No obvious secrets found in source files");
489
510
  passed++;
511
+ score += SCORE_WEIGHTS.noSecrets;
490
512
  }
491
513
 
492
514
  // --- Check for HTTPS enforcement (look for http:// in env example) ---
@@ -499,6 +521,7 @@ function audit() {
499
521
  } else {
500
522
  console.log(" [pass] No insecure URLs in .env.example");
501
523
  passed++;
524
+ score += SCORE_WEIGHTS.noInsecureUrls;
502
525
  }
503
526
  }
504
527
 
@@ -508,15 +531,19 @@ function audit() {
508
531
  if (hasLockFile) {
509
532
  console.log(" [pass] Dependency lock file exists");
510
533
  passed++;
534
+ score += SCORE_WEIGHTS.lockFile;
511
535
  } else if (fs.existsSync(path.join(targetDir, "package.json"))) {
512
536
  console.log(" [warn] No lock file found — pin your dependencies");
513
537
  warnings++;
514
538
  }
515
539
 
540
+ // --- Security Score ---
541
+ console.log("\n ════════════════════════════════════");
542
+ console.log(` Security Score: ${score} / 100`);
543
+ console.log(" ════════════════════════════════════");
544
+
516
545
  // --- Summary ---
517
- console.log("\n ────────────────────────────────────");
518
- console.log(` Results: ${passed} passed, ${warnings} warnings, ${issues} issues`);
519
- console.log(" ────────────────────────────────────");
546
+ console.log(`\n Results: ${passed} passed, ${warnings} warnings, ${issues} issues`);
520
547
 
521
548
  if (issues > 0) {
522
549
  console.log(`\n ${issues} issue(s) found. Fix these before shipping.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "secure-repo",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Drop production-grade security standards into any repo. Audit your repo for security issues. Templates for AI-assisted development.",
5
5
  "bin": {
6
6
  "secure-repo": "./bin/cli.js"