secure-repo 1.2.1 → 1.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 (3) hide show
  1. package/README.md +5 -3
  2. package/bin/cli.js +61 -7
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -93,7 +93,7 @@ Every template is:
93
93
 
94
94
  ## What You Get (Free)
95
95
 
96
- Three templates that cover the foundations:
96
+ Four templates that cover the foundations:
97
97
 
98
98
  **SECURITY.md** — No secrets in code. Privileged keys server-side only. Database access control mandatory. Server endpoints for all writes. Incident response steps.
99
99
 
@@ -101,6 +101,8 @@ Three templates that cover the foundations:
101
101
 
102
102
  **API.md** — Input validation on every endpoint. Rate limiting on all public routes. Error responses that don't leak internals. Endpoint conventions. CORS rules. Pagination enforcement.
103
103
 
104
+ **ACCESSIBILITY.md** — WCAG 2.1 AA compliance. Semantic HTML. Keyboard navigation. Screen reader support. Color contrast. Font sizes. Touch targets.
105
+
104
106
  Each file includes:
105
107
  - Rules marked "MUST FOLLOW"
106
108
  - Code patterns to copy
@@ -181,12 +183,12 @@ npx secure-repo list # Show all available templates
181
183
  | | Free | Pro Pack |
182
184
  |--|------|---------|
183
185
  | Security audit command | Included | Included |
184
- | Core security policies (3 files) | Included | Included |
186
+ | Core security policies (4 files) | Included | Included |
185
187
  | Deep engineering standards (18 files) | - | Included |
186
188
  | 100+ point audit checklist | - | Included |
187
189
  | Stack presets (Supabase, Firebase) | - | Included |
188
190
  | Code examples (5 files) | - | Included |
189
- | **Total policy files** | **3** | **30** |
191
+ | **Total policy files** | **4** | **31** |
190
192
 
191
193
  ---
192
194
 
package/bin/cli.js CHANGED
@@ -213,6 +213,31 @@ function downloadProZip(destPath, licenseKey) {
213
213
  });
214
214
  }
215
215
 
216
+ // ============================================================
217
+ // Detect stack from package.json dependencies
218
+ // ============================================================
219
+ function detectStacks(projectDir) {
220
+ const pkgPath = path.join(projectDir, "package.json");
221
+ if (!fs.existsSync(pkgPath)) return [];
222
+
223
+ try {
224
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
225
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
226
+ const stacks = [];
227
+
228
+ if (allDeps["@supabase/supabase-js"] || allDeps["@supabase/ssr"] || allDeps["@supabase/auth-helpers-nextjs"]) {
229
+ stacks.push("supabase");
230
+ }
231
+ if (allDeps["firebase"] || allDeps["firebase-admin"] || allDeps["firebase-functions"]) {
232
+ stacks.push("firebase");
233
+ }
234
+
235
+ return stacks;
236
+ } catch {
237
+ return [];
238
+ }
239
+ }
240
+
216
241
  // ============================================================
217
242
  // Extract zip and install pro templates
218
243
  // ============================================================
@@ -244,13 +269,18 @@ function installFromZip(zipPath, outputDir, force) {
244
269
  totalSkipped += r.skipped;
245
270
  }
246
271
 
247
- // Copy presets
272
+ // Copy presets — only install presets matching detected stack
248
273
  const presetsDir = path.join(tempDir, "presets");
249
274
  if (fs.existsSync(presetsDir)) {
275
+ const detectedStacks = detectStacks(outputDir);
250
276
  const presets = fs.readdirSync(presetsDir).filter((f) =>
251
277
  fs.statSync(path.join(presetsDir, f)).isDirectory()
252
278
  );
253
279
  presets.forEach((preset) => {
280
+ if (detectedStacks.length > 0 && !detectedStacks.includes(preset)) {
281
+ console.log(`\n Preset: ${preset} (skipped — not detected in package.json)`);
282
+ return;
283
+ }
254
284
  const presetDest = path.join(outputDir, `${preset}-preset`);
255
285
  console.log(`\n Preset: ${preset} (-> ${preset}-preset/)`);
256
286
  const r = copyFiles(path.join(presetsDir, preset), presetDest, force);
@@ -344,9 +374,20 @@ function writeAgentFiles(outputDir, force) {
344
374
  const dir = path.dirname(fullPath);
345
375
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
346
376
 
347
- if (fs.existsSync(fullPath) && !force) {
348
- console.log(` [skip] ${filePath} (${name}) — use --force to overwrite`);
349
- skipped++;
377
+ if (fs.existsSync(fullPath)) {
378
+ const existing = fs.readFileSync(fullPath, "utf8");
379
+ const isShipSecureBoilerplate = existing.includes("# Security Policies — MUST READ") && existing.includes("ShipSecure");
380
+ if (isShipSecureBoilerplate && force) {
381
+ fs.writeFileSync(fullPath, AGENT_INSTRUCTION);
382
+ console.log(` [done] ${filePath} (${name})`);
383
+ written++;
384
+ } else if (isShipSecureBoilerplate) {
385
+ console.log(` [skip] ${filePath} (${name}) — use --force to overwrite`);
386
+ skipped++;
387
+ } else {
388
+ console.log(` [skip] ${filePath} (${name}) — existing content detected, won't overwrite`);
389
+ skipped++;
390
+ }
350
391
  } else {
351
392
  fs.writeFileSync(fullPath, AGENT_INSTRUCTION);
352
393
  console.log(` [done] ${filePath} (${name})`);
@@ -569,9 +610,22 @@ function audit() {
569
610
  envFiles.forEach((envFile) => {
570
611
  const envPath = path.join(targetDir, envFile);
571
612
  if (fs.existsSync(envPath)) {
572
- console.log(` [FAIL] ${envFile} exists in repo may contain secrets`);
573
- issues++;
574
- envFilesFound++;
613
+ // Check if file is git-ignored before flagging
614
+ let isIgnored = false;
615
+ try {
616
+ execSync(`git check-ignore -q "${envPath}"`, { stdio: "pipe" });
617
+ isIgnored = true;
618
+ } catch {
619
+ // exit code 1 = not ignored
620
+ }
621
+ if (isIgnored) {
622
+ console.log(` [pass] ${envFile} exists but is gitignored`);
623
+ passed++;
624
+ } else {
625
+ console.log(` [FAIL] ${envFile} exists and is NOT gitignored — may contain secrets`);
626
+ issues++;
627
+ envFilesFound++;
628
+ }
575
629
  }
576
630
  });
577
631
  if (envFilesFound === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "secure-repo",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
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"