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.
- package/README.md +5 -3
- package/bin/cli.js +61 -7
- 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
|
-
|
|
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 (
|
|
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** | **
|
|
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)
|
|
348
|
-
|
|
349
|
-
|
|
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
|
-
|
|
573
|
-
|
|
574
|
-
|
|
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