secure-repo 1.0.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 ADDED
@@ -0,0 +1,26 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Secure Repo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ---
24
+
25
+ NOTE: This MIT license applies to the free templates (templates/free/).
26
+ Pro, premium, and preset templates are licensed separately under purchase terms.
package/README.md ADDED
@@ -0,0 +1,212 @@
1
+ # Secure Repo
2
+
3
+ **Drop production-grade security standards into any repository in 30 seconds.**
4
+
5
+ ```bash
6
+ npx secure-repo init
7
+ ```
8
+
9
+ That's it. Your repo now has battle-tested security policies, checklists, and enforcement rules used in real production SaaS applications.
10
+
11
+ ---
12
+
13
+ ## Audit Your Repo
14
+
15
+ Not sure where you stand? Run an instant security audit:
16
+
17
+ ```bash
18
+ npx secure-repo audit
19
+ ```
20
+
21
+ ```
22
+ secure-repo audit
23
+
24
+ Scanning repository for security issues...
25
+
26
+ Policy files:
27
+ [FAIL] SECURITY.md — missing (high priority)
28
+ [FAIL] AUTH.md — missing (high priority)
29
+ [FAIL] API.md — missing (high priority)
30
+ [warn] DATABASE.md — missing
31
+ [warn] DEPLOYMENT.md — missing
32
+
33
+ Environment files:
34
+ [pass] .env is in .gitignore
35
+ [pass] .env.example exists
36
+
37
+ Secret scanning:
38
+ [pass] No obvious secrets found in source files
39
+
40
+ ────────────────────────────────────
41
+ Results: 3 passed, 2 warnings, 3 issues
42
+ ────────────────────────────────────
43
+
44
+ 3 issue(s) found. Fix these before shipping.
45
+ Run: npx secure-repo init
46
+ ```
47
+
48
+ Zero setup. Zero dependencies. Just run it.
49
+
50
+ ---
51
+
52
+ ## The Problem
53
+
54
+ You're building a SaaS app. You know you should have security policies, but:
55
+
56
+ - Writing them from scratch takes days
57
+ - You don't know what you're missing until something breaks
58
+ - AI coding agents generate insecure code when there's no policy file to guide them
59
+ - Your team has no shared standard for "how we handle auth" or "what counts as a safe API endpoint"
60
+
61
+ ## The Solution
62
+
63
+ ```bash
64
+ npx secure-repo init
65
+ ```
66
+
67
+ ```
68
+ secure-repo - Adding production standards to your project
69
+
70
+ Free templates:
71
+ [done] SECURITY.md
72
+ [done] AUTH.md
73
+ [done] API.md
74
+
75
+ Done! 3 files added.
76
+ ```
77
+
78
+ Every template is:
79
+
80
+ - **Opinionated** — makes decisions so you don't have to
81
+ - **Actionable** — every rule is verifiable, every pattern is copy-pastable
82
+ - **AI-agent friendly** — coding agents read these files and write safer code
83
+
84
+ ---
85
+
86
+ ## What You Get (Free)
87
+
88
+ Three templates that cover the foundations:
89
+
90
+ **SECURITY.md** — No secrets in code. Privileged keys server-side only. Database access control mandatory. Server endpoints for all writes. Incident response steps.
91
+
92
+ **AUTH.md** — JWT verification rules. Token storage (httpOnly cookies, not localStorage). Password hashing (bcrypt/argon2). Rate limiting on login. Session revocation. Role-based access.
93
+
94
+ **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.
95
+
96
+ Each file includes:
97
+ - Rules marked "MUST FOLLOW"
98
+ - Code patterns to copy
99
+ - A "Required Checks Before Merge" checklist
100
+
101
+ ---
102
+
103
+ ## Pro Pack
104
+
105
+ 27 additional files for teams that want complete coverage. Sold separately.
106
+
107
+ ### Pro Templates (18 files)
108
+
109
+ | Template | What it covers |
110
+ |----------|---------------|
111
+ | `DATABASE.md` | Access control patterns, migration safety, table design |
112
+ | `DEPLOYMENT.md` | CI/CD pipeline, rollback procedures, zero-downtime deploys |
113
+ | `INCIDENT_RESPONSE.md` | Severity levels, response steps, post-mortem template, runbooks |
114
+ | `OBSERVABILITY.md` | Structured logging, PII redaction, alerting rules, health checks |
115
+ | `TESTING.md` | Security test patterns with code examples |
116
+ | `ENV_VARIABLES.md` | Secrets management, rotation, client vs server vars |
117
+ | `PAYMENTS.md` | Stripe webhooks, subscription management, PCI rules |
118
+ | `DATA_PRIVACY.md` | GDPR, retention schedules, account deletion flow |
119
+ | `FILE_UPLOADS.md` | Upload validation, signed URLs, serving rules |
120
+ | `RATE_LIMITING.md` | Per-endpoint limits, implementation patterns |
121
+ | `THIRD_PARTY.md` | Integration inventory, webhook security, OAuth |
122
+ | `ACCESS_CONTROL.md` | Roles, permissions, escalation, break-glass procedures |
123
+ | `LOGGING_PII.md` | What to log, what to redact, compliance rules |
124
+ | `PR_CHECKLIST.md` | Copy-paste security checklist for every PR |
125
+ | `THREAT_MODEL.md` | Lightweight threat modeling template |
126
+ | `VULNERABILITY_REPORTING.md` | Responsible disclosure policy |
127
+ | `CONTRIBUTING_SECURITY.md` | Contributor security rules |
128
+ | `POLICY_INDEX.md` | One-page index linking all policies |
129
+
130
+ ### Premium: 100+ Point Security Audit
131
+
132
+ `FULL_AUDIT_CHECKLIST.md` — Complete production security audit with severity ratings, explanations for every item, and an audit summary template. This is what security consultants charge thousands to produce.
133
+
134
+ ### Stack Presets
135
+
136
+ - **Supabase** — RLS policies, `auth.uid()` patterns, service role rules, function hardening (6 files)
137
+ - **Firebase** — Firestore security rules, Firebase Auth, Cloud Functions, custom claims (3 files)
138
+
139
+ ### Code Examples
140
+
141
+ - `next-route-handler.ts` — Secure API route with auth + validation + error handling
142
+ - `rate-limit.ts` — Rate limiting middleware (in-memory + Redis)
143
+ - `zod-validate.ts` — Reusable validation schemas
144
+ - `supabase-rls.sql` — 8 RLS policy patterns
145
+ - `firebase-rules.txt` — 8 Firestore rule patterns
146
+
147
+ **Get the pro pack:** [polar.sh/third-space-labs](https://polar.sh/third-space-labs)
148
+
149
+ After purchase, install with one command:
150
+
151
+ ```bash
152
+ npx secure-repo init --key <your-license-key>
153
+ ```
154
+
155
+ ---
156
+
157
+ ## Commands
158
+
159
+ ```bash
160
+ npx secure-repo init # Add free security templates
161
+ npx secure-repo init --key <key> # Add free + pro templates (with license key)
162
+ npx secure-repo audit # Scan your repo for security issues
163
+ npx secure-repo import <zip> # Import pro templates from zip (offline)
164
+ npx secure-repo check # Check if your templates are outdated
165
+ npx secure-repo list # Show all available templates
166
+ ```
167
+
168
+ ---
169
+
170
+ ## Free vs Pro
171
+
172
+ | | Free | Pro Pack |
173
+ |--|------|---------|
174
+ | Security audit command | Included | Included |
175
+ | Core security policies (3 files) | Included | Included |
176
+ | Deep engineering standards (18 files) | - | Included |
177
+ | 100+ point audit checklist | - | Included |
178
+ | Stack presets (Supabase, Firebase) | - | Included |
179
+ | Code examples (5 files) | - | Included |
180
+ | **Total policy files** | **3** | **30** |
181
+
182
+ ---
183
+
184
+ ## Why This Matters for AI-Assisted Development
185
+
186
+ If you use Cursor, Claude Code, Copilot, or any AI coding agent — these files change how the agent writes code in your repo.
187
+
188
+ When an AI agent sees `SECURITY.md` in your project root, it follows those rules. When it sees `API.md`, it validates input and handles errors correctly. When it sees `AUTH.md`, it checks tokens server-side.
189
+
190
+ **Without policy files:** The agent guesses. It writes code that works but may not be secure.
191
+
192
+ **With policy files:** The agent follows your standards. Every generated endpoint validates input, checks auth, and handles errors safely.
193
+
194
+ Secure Repo gives your AI agents the rules they need to write production-safe code.
195
+
196
+ ---
197
+
198
+ ## Support This Project
199
+
200
+ If Secure Repo helps you ship safer software, consider [sponsoring development](https://github.com/sponsors/sebiomoa).
201
+
202
+ ---
203
+
204
+ ## License
205
+
206
+ Free templates (`templates/free/`) are [MIT licensed](LICENSE).
207
+
208
+ Pro pack templates are licensed for personal and commercial use by the purchaser.
209
+
210
+ ---
211
+
212
+ *This is not legal advice. These templates do not replace professional security audits for regulated industries. They are practical engineering tools for building more secure applications.*
package/bin/cli.js ADDED
@@ -0,0 +1,589 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const https = require("https");
6
+ const { execSync } = require("child_process");
7
+
8
+ const TEMPLATES_DIR = path.join(__dirname, "..", "templates");
9
+ const FREE_DIR = path.join(TEMPLATES_DIR, "free");
10
+
11
+ const POLAR_ORGANIZATION_ID = "d55baa70-3a94-4549-901a-2b4c920ff122";
12
+
13
+ const PRO_ZIP_URL = "https://github.com/sebiomoa/secure-repo/releases/latest/download/secure-repo-pro.zip";
14
+
15
+ const args = process.argv.slice(2);
16
+ const command = args[0];
17
+
18
+ // Files that a secure repo should have
19
+ const RECOMMENDED_FILES = [
20
+ { file: "SECURITY.md", category: "security", severity: "high" },
21
+ { file: "AUTH.md", category: "security", severity: "high" },
22
+ { file: "API.md", category: "security", severity: "high" },
23
+ { file: "DATABASE.md", category: "security", severity: "medium" },
24
+ { file: "DEPLOYMENT.md", category: "operations", severity: "medium" },
25
+ { file: "INCIDENT_RESPONSE.md", category: "operations", severity: "medium" },
26
+ { file: "ENV_VARIABLES.md", category: "security", severity: "high" },
27
+ { file: "CONTRIBUTING.md", category: "process", severity: "low" },
28
+ ];
29
+
30
+ // Known dangerous patterns to scan for
31
+ const DANGER_PATTERNS = [
32
+ { pattern: /sk_live_[a-zA-Z0-9]+/, label: "Stripe live secret key" },
33
+ { pattern: /sk_test_[a-zA-Z0-9]+/, label: "Stripe test secret key" },
34
+ { pattern: /eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+/, label: "JWT token" },
35
+ { pattern: /SUPABASE_SERVICE_ROLE_KEY/, label: "Supabase service role key reference" },
36
+ { pattern: /password\s*[:=]\s*['"][^'"]+['"]/, label: "Hardcoded password" },
37
+ { pattern: /api[_-]?key\s*[:=]\s*['"][^'"]+['"]/, label: "Hardcoded API key" },
38
+ ];
39
+
40
+ function printHelp() {
41
+ console.log(`
42
+ secure-repo - Production-grade security standards for your repo
43
+
44
+ Usage:
45
+ npx secure-repo init Add free security templates
46
+ npx secure-repo init --key <key> Add free + pro templates (requires license key)
47
+ npx secure-repo audit Scan your repo for security issues
48
+ npx secure-repo import <file> Import pro templates from a zip file (offline)
49
+ npx secure-repo check Check which templates are outdated
50
+ npx secure-repo list Show available free templates
51
+
52
+ Options:
53
+ --key Your license key (from purchase)
54
+ --force Overwrite existing files
55
+ --output Output directory (default: current directory)
56
+
57
+ Free templates (always included):
58
+ SECURITY.md Secrets management, attack surface, enforced architecture
59
+ AUTH.md Token handling, session rules, password policy, roles
60
+ API.md Input validation, rate limiting, error handling
61
+
62
+ Pro templates (purchase at https://polar.sh/third-space-labs):
63
+ 30 additional files — templates, audit checklist, stack presets, examples
64
+ Install with: npx secure-repo init --key <your-license-key>
65
+ `);
66
+ }
67
+
68
+ function listTemplates() {
69
+ console.log("\n Free templates (included):\n");
70
+ if (fs.existsSync(FREE_DIR)) {
71
+ fs.readdirSync(FREE_DIR).forEach((f) => console.log(` ${f}`));
72
+ }
73
+
74
+ console.log("\n Pro templates (sold separately):\n");
75
+ const proFiles = [
76
+ "DATABASE.md", "DEPLOYMENT.md", "INCIDENT_RESPONSE.md", "OBSERVABILITY.md",
77
+ "TESTING.md", "ENV_VARIABLES.md", "PAYMENTS.md", "DATA_PRIVACY.md",
78
+ "FILE_UPLOADS.md", "RATE_LIMITING.md", "THIRD_PARTY.md", "ACCESS_CONTROL.md",
79
+ "LOGGING_PII.md", "PR_CHECKLIST.md", "THREAT_MODEL.md",
80
+ "VULNERABILITY_REPORTING.md", "CONTRIBUTING_SECURITY.md", "POLICY_INDEX.md",
81
+ ];
82
+ proFiles.forEach((f) => console.log(` ${f}`));
83
+
84
+ console.log("\n Premium:\n");
85
+ console.log(" FULL_AUDIT_CHECKLIST.md (100+ point security audit)");
86
+
87
+ console.log("\n Stack presets:\n");
88
+ console.log(" supabase/ (6 files)");
89
+ console.log(" firebase/ (3 files)");
90
+
91
+ console.log("\n Code examples:\n");
92
+ console.log(" next-route-handler.ts, rate-limit.ts, zod-validate.ts");
93
+ console.log(" supabase-rls.sql, firebase-rules.txt");
94
+
95
+ console.log("\n Get pro: https://polar.sh/third-space-labs\n");
96
+ }
97
+
98
+ function getArg(flag) {
99
+ const idx = args.indexOf(flag);
100
+ if (idx === -1 || idx + 1 >= args.length) return null;
101
+ return args[idx + 1];
102
+ }
103
+
104
+ function copyFiles(srcDir, destDir, force) {
105
+ if (!fs.existsSync(srcDir)) return { copied: 0, skipped: 0 };
106
+ if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
107
+
108
+ const files = fs.readdirSync(srcDir);
109
+ let copied = 0;
110
+ let skipped = 0;
111
+
112
+ files.forEach((file) => {
113
+ const srcPath = path.join(srcDir, file);
114
+ if (fs.statSync(srcPath).isDirectory()) return;
115
+
116
+ const destPath = path.join(destDir, file);
117
+ if (fs.existsSync(destPath) && !force) {
118
+ console.log(` [skip] ${file} (use --force to overwrite)`);
119
+ skipped++;
120
+ } else {
121
+ fs.copyFileSync(srcPath, destPath);
122
+ console.log(` [done] ${file}`);
123
+ copied++;
124
+ }
125
+ });
126
+
127
+ return { copied, skipped };
128
+ }
129
+
130
+ // ============================================================
131
+ // Polar license verification
132
+ // ============================================================
133
+ function verifyLicense(licenseKey) {
134
+ return new Promise((resolve, reject) => {
135
+ const postData = JSON.stringify({
136
+ key: licenseKey,
137
+ organization_id: POLAR_ORGANIZATION_ID,
138
+ });
139
+
140
+ const req = https.request(
141
+ {
142
+ hostname: "api.polar.sh",
143
+ path: "/v1/users/license-keys/validate",
144
+ method: "POST",
145
+ headers: {
146
+ "Content-Type": "application/json",
147
+ "Content-Length": Buffer.byteLength(postData),
148
+ },
149
+ },
150
+ (res) => {
151
+ let data = "";
152
+ res.on("data", (chunk) => (data += chunk));
153
+ res.on("end", () => {
154
+ try {
155
+ const json = JSON.parse(data);
156
+ if (json.status === "granted" || json.status === "active") {
157
+ resolve(json);
158
+ } else {
159
+ const msg = typeof json.detail === "string" ? json.detail : "Invalid license key";
160
+ reject(new Error(msg));
161
+ }
162
+ } catch {
163
+ reject(new Error("Failed to verify license"));
164
+ }
165
+ });
166
+ }
167
+ );
168
+
169
+ req.on("error", (err) => reject(new Error(`Network error: ${err.message}`)));
170
+ req.write(postData);
171
+ req.end();
172
+ });
173
+ }
174
+
175
+ // ============================================================
176
+ // Download file from URL
177
+ // ============================================================
178
+ function downloadFile(url, destPath) {
179
+ return new Promise((resolve, reject) => {
180
+ const file = fs.createWriteStream(destPath);
181
+
182
+ function follow(url) {
183
+ https.get(url, (res) => {
184
+ // Follow redirects
185
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
186
+ follow(res.headers.location);
187
+ return;
188
+ }
189
+
190
+ if (res.statusCode !== 200) {
191
+ reject(new Error(`Download failed (HTTP ${res.statusCode})`));
192
+ return;
193
+ }
194
+
195
+ res.pipe(file);
196
+ file.on("finish", () => {
197
+ file.close();
198
+ resolve();
199
+ });
200
+ }).on("error", (err) => {
201
+ fs.unlink(destPath, () => {});
202
+ reject(new Error(`Download error: ${err.message}`));
203
+ });
204
+ }
205
+
206
+ follow(url);
207
+ });
208
+ }
209
+
210
+ // ============================================================
211
+ // Extract zip and install pro templates
212
+ // ============================================================
213
+ function installFromZip(zipPath, outputDir, force) {
214
+ const tempDir = path.join(outputDir, ".secure-repo-temp");
215
+
216
+ try {
217
+ fs.mkdirSync(tempDir, { recursive: true });
218
+ execSync(`unzip -o "${zipPath}" -d "${tempDir}"`, { stdio: "pipe" });
219
+
220
+ let totalCopied = 0;
221
+ let totalSkipped = 0;
222
+
223
+ // Copy pro templates
224
+ const proDir = path.join(tempDir, "pro");
225
+ if (fs.existsSync(proDir)) {
226
+ console.log("\n Pro templates:");
227
+ const r = copyFiles(proDir, outputDir, force);
228
+ totalCopied += r.copied;
229
+ totalSkipped += r.skipped;
230
+ }
231
+
232
+ // Copy premium templates
233
+ const premiumDir = path.join(tempDir, "premium");
234
+ if (fs.existsSync(premiumDir)) {
235
+ console.log("\n Premium:");
236
+ const r = copyFiles(premiumDir, outputDir, force);
237
+ totalCopied += r.copied;
238
+ totalSkipped += r.skipped;
239
+ }
240
+
241
+ // Copy presets
242
+ const presetsDir = path.join(tempDir, "presets");
243
+ if (fs.existsSync(presetsDir)) {
244
+ const presets = fs.readdirSync(presetsDir).filter((f) =>
245
+ fs.statSync(path.join(presetsDir, f)).isDirectory()
246
+ );
247
+ presets.forEach((preset) => {
248
+ const presetDest = path.join(outputDir, `${preset}-preset`);
249
+ console.log(`\n Preset: ${preset} (-> ${preset}-preset/)`);
250
+ const r = copyFiles(path.join(presetsDir, preset), presetDest, force);
251
+ totalCopied += r.copied;
252
+ totalSkipped += r.skipped;
253
+ });
254
+ }
255
+
256
+ // Copy examples
257
+ const examplesDir = path.join(tempDir, "examples");
258
+ if (fs.existsSync(examplesDir)) {
259
+ const examplesDest = path.join(outputDir, "examples");
260
+ console.log("\n Code examples (-> examples/):");
261
+ const r = copyFiles(examplesDir, examplesDest, force);
262
+ totalCopied += r.copied;
263
+ totalSkipped += r.skipped;
264
+ }
265
+
266
+ return { copied: totalCopied, skipped: totalSkipped };
267
+ } finally {
268
+ fs.rmSync(tempDir, { recursive: true, force: true });
269
+ }
270
+ }
271
+
272
+ // ============================================================
273
+ // INIT — install templates (free, or free + pro with --key)
274
+ // ============================================================
275
+ async function init() {
276
+ const force = args.includes("--force");
277
+ const outputDir = getArg("--output") || process.cwd();
278
+ const licenseKey = getArg("--key");
279
+
280
+ if (licenseKey) {
281
+ // Pro install: verify key, download, extract
282
+ console.log("\n secure-repo - Installing pro templates\n");
283
+ console.log(" Verifying license key...");
284
+
285
+ try {
286
+ await verifyLicense(licenseKey);
287
+ console.log(" License valid!\n");
288
+ } catch (err) {
289
+ console.log(`\n License verification failed: ${err.message}`);
290
+ console.log(" Purchase at: https://polar.sh/third-space-labs\n");
291
+ process.exit(1);
292
+ }
293
+
294
+ // Install free templates first
295
+ console.log(" Free templates:");
296
+ const freeResult = copyFiles(FREE_DIR, outputDir, force);
297
+
298
+ // Download and install pro templates
299
+ const zipPath = path.join(outputDir, ".secure-repo-pro.zip");
300
+ console.log("\n Downloading pro templates...");
301
+
302
+ try {
303
+ await downloadFile(PRO_ZIP_URL, zipPath);
304
+ const proResult = installFromZip(zipPath, outputDir, force);
305
+
306
+ const totalCopied = freeResult.copied + proResult.copied;
307
+ const totalSkipped = freeResult.skipped + proResult.skipped;
308
+
309
+ console.log(`\n Done! ${totalCopied} files installed, ${totalSkipped} skipped.`);
310
+ console.log("\n Next steps:");
311
+ console.log(" 1. Customize the templates for your project");
312
+ console.log(" 2. Run: npx secure-repo audit");
313
+ console.log();
314
+ } catch (err) {
315
+ console.log(`\n Download failed: ${err.message}`);
316
+ console.log(" Try offline install: npx secure-repo import <zip-file>\n");
317
+ process.exit(1);
318
+ } finally {
319
+ // Clean up downloaded zip
320
+ if (fs.existsSync(zipPath)) fs.unlinkSync(zipPath);
321
+ }
322
+ } else {
323
+ // Free install
324
+ console.log("\n secure-repo - Adding production standards to your project\n");
325
+
326
+ console.log(" Free templates:");
327
+ const result = copyFiles(FREE_DIR, outputDir, force);
328
+
329
+ console.log(`\n Done! ${result.copied} files added, ${result.skipped} skipped.`);
330
+ console.log("\n Next steps:");
331
+ console.log(" 1. Customize the templates for your project");
332
+ console.log(" 2. Run: npx secure-repo audit");
333
+ console.log(" 3. Get pro templates: npx secure-repo init --key <your-key>");
334
+ console.log(" Purchase at: https://polar.sh/third-space-labs");
335
+ console.log();
336
+ }
337
+ }
338
+
339
+ // ============================================================
340
+ // IMPORT — import pro templates from zip (offline fallback)
341
+ // ============================================================
342
+ function importPack() {
343
+ const zipPath = args[1];
344
+
345
+ if (!zipPath) {
346
+ console.log("\n Usage: npx secure-repo import <path-to-zip>\n");
347
+ console.log(" Offline alternative to: npx secure-repo init --key <key>");
348
+ console.log(" Get the pro pack at: https://polar.sh/third-space-labs\n");
349
+ return;
350
+ }
351
+
352
+ const resolvedPath = path.resolve(zipPath);
353
+
354
+ if (!fs.existsSync(resolvedPath)) {
355
+ console.log(`\n File not found: ${resolvedPath}\n`);
356
+ process.exit(1);
357
+ }
358
+
359
+ const force = args.includes("--force");
360
+ const outputDir = getArg("--output") || process.cwd();
361
+
362
+ console.log("\n secure-repo - Importing pro templates\n");
363
+
364
+ // Install free templates too
365
+ console.log(" Free templates:");
366
+ const freeResult = copyFiles(FREE_DIR, outputDir, force);
367
+
368
+ try {
369
+ const proResult = installFromZip(resolvedPath, outputDir, force);
370
+
371
+ const totalCopied = freeResult.copied + proResult.copied;
372
+ const totalSkipped = freeResult.skipped + proResult.skipped;
373
+
374
+ console.log(`\n Done! ${totalCopied} files imported, ${totalSkipped} skipped.\n`);
375
+ } catch (err) {
376
+ console.log(`\n Failed to extract zip: ${err.message}\n`);
377
+ process.exit(1);
378
+ }
379
+ }
380
+
381
+ // ============================================================
382
+ // AUDIT — scan repo for security issues (the viral command)
383
+ // ============================================================
384
+ function audit() {
385
+ const targetDir = getArg("--output") || process.cwd();
386
+
387
+ console.log("\n secure-repo audit\n");
388
+ console.log(" Scanning repository for security issues...\n");
389
+
390
+ let issues = 0;
391
+ let warnings = 0;
392
+ let passed = 0;
393
+
394
+ // --- Check for recommended files ---
395
+ console.log(" Policy files:");
396
+ RECOMMENDED_FILES.forEach(({ file, severity }) => {
397
+ const filePath = path.join(targetDir, file);
398
+ if (fs.existsSync(filePath)) {
399
+ console.log(` [pass] ${file}`);
400
+ passed++;
401
+ } else if (severity === "high") {
402
+ console.log(` [FAIL] ${file} — missing (high priority)`);
403
+ issues++;
404
+ } else {
405
+ console.log(` [warn] ${file} — missing`);
406
+ warnings++;
407
+ }
408
+ });
409
+
410
+ // --- Check .gitignore for .env ---
411
+ console.log("\n Environment files:");
412
+ const gitignorePath = path.join(targetDir, ".gitignore");
413
+ if (fs.existsSync(gitignorePath)) {
414
+ const gitignore = fs.readFileSync(gitignorePath, "utf8");
415
+ if (gitignore.includes(".env")) {
416
+ console.log(" [pass] .env is in .gitignore");
417
+ passed++;
418
+ } else {
419
+ console.log(" [FAIL] .env is NOT in .gitignore");
420
+ issues++;
421
+ }
422
+ } else {
423
+ console.log(" [FAIL] No .gitignore found");
424
+ issues++;
425
+ }
426
+
427
+ // --- Check for committed .env files ---
428
+ const envFiles = [".env", ".env.local", ".env.production"];
429
+ envFiles.forEach((envFile) => {
430
+ const envPath = path.join(targetDir, envFile);
431
+ if (fs.existsSync(envPath)) {
432
+ console.log(` [FAIL] ${envFile} exists in repo — may contain secrets`);
433
+ issues++;
434
+ }
435
+ });
436
+
437
+ // --- Check for .env.example ---
438
+ const envExamplePath = path.join(targetDir, ".env.example");
439
+ if (fs.existsSync(envExamplePath)) {
440
+ console.log(" [pass] .env.example exists");
441
+ passed++;
442
+ } else {
443
+ console.log(" [warn] .env.example missing — document required env vars");
444
+ warnings++;
445
+ }
446
+
447
+ // --- Scan for hardcoded secrets in common files ---
448
+ console.log("\n Secret scanning:");
449
+ const scanExtensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".env.example"];
450
+ let secretsFound = 0;
451
+
452
+ function scanDir(dir, depth) {
453
+ if (depth > 5) return;
454
+ if (!fs.existsSync(dir)) return;
455
+
456
+ const entries = fs.readdirSync(dir);
457
+ entries.forEach((entry) => {
458
+ if (entry.startsWith(".") || entry === "node_modules" || entry === ".next" || entry === "bin" || entry === "lib") return;
459
+ const fullPath = path.join(dir, entry);
460
+
461
+ try {
462
+ const stat = fs.statSync(fullPath);
463
+ if (stat.isDirectory()) {
464
+ scanDir(fullPath, depth + 1);
465
+ } else if (scanExtensions.some((ext) => entry.endsWith(ext))) {
466
+ const content = fs.readFileSync(fullPath, "utf8");
467
+ DANGER_PATTERNS.forEach(({ pattern, label }) => {
468
+ if (pattern.test(content)) {
469
+ const relative = path.relative(targetDir, fullPath);
470
+ console.log(` [FAIL] ${relative} — ${label}`);
471
+ secretsFound++;
472
+ issues++;
473
+ }
474
+ });
475
+ }
476
+ } catch {
477
+ // skip unreadable files
478
+ }
479
+ });
480
+ }
481
+
482
+ scanDir(targetDir, 0);
483
+
484
+ if (secretsFound === 0) {
485
+ console.log(" [pass] No obvious secrets found in source files");
486
+ passed++;
487
+ }
488
+
489
+ // --- Check for HTTPS enforcement (look for http:// in env example) ---
490
+ console.log("\n Configuration:");
491
+ if (fs.existsSync(envExamplePath)) {
492
+ const envExample = fs.readFileSync(envExamplePath, "utf8");
493
+ if (envExample.includes("http://") && !envExample.includes("localhost")) {
494
+ console.log(" [warn] .env.example contains http:// URLs (should be https://)");
495
+ warnings++;
496
+ } else {
497
+ console.log(" [pass] No insecure URLs in .env.example");
498
+ passed++;
499
+ }
500
+ }
501
+
502
+ // --- Check for package-lock.json or equivalent ---
503
+ const lockFiles = ["package-lock.json", "yarn.lock", "pnpm-lock.yaml", "bun.lockb"];
504
+ const hasLockFile = lockFiles.some((f) => fs.existsSync(path.join(targetDir, f)));
505
+ if (hasLockFile) {
506
+ console.log(" [pass] Dependency lock file exists");
507
+ passed++;
508
+ } else if (fs.existsSync(path.join(targetDir, "package.json"))) {
509
+ console.log(" [warn] No lock file found — pin your dependencies");
510
+ warnings++;
511
+ }
512
+
513
+ // --- Summary ---
514
+ console.log("\n ────────────────────────────────────");
515
+ console.log(` Results: ${passed} passed, ${warnings} warnings, ${issues} issues`);
516
+ console.log(" ────────────────────────────────────");
517
+
518
+ if (issues > 0) {
519
+ console.log(`\n ${issues} issue(s) found. Fix these before shipping.`);
520
+ console.log(" Run: npx secure-repo init (adds missing policy files)");
521
+ } else if (warnings > 0) {
522
+ console.log("\n No critical issues. Some improvements recommended.");
523
+ } else {
524
+ console.log("\n Looking good! Your repo meets basic security standards.");
525
+ }
526
+
527
+ console.log();
528
+ return issues;
529
+ }
530
+
531
+ // ============================================================
532
+ // CHECK — compare local templates against latest version
533
+ // ============================================================
534
+ function check() {
535
+ const destDir = process.cwd();
536
+
537
+ console.log("\n Checking installed templates...\n");
538
+
539
+ if (!fs.existsSync(FREE_DIR)) {
540
+ console.log(" Could not find template source files.\n");
541
+ return;
542
+ }
543
+
544
+ fs.readdirSync(FREE_DIR).forEach((file) => {
545
+ const localPath = path.join(destDir, file);
546
+ if (!fs.existsSync(localPath)) {
547
+ console.log(` [missing] ${file}`);
548
+ return;
549
+ }
550
+ const local = fs.readFileSync(localPath, "utf8");
551
+ const latest = fs.readFileSync(path.join(FREE_DIR, file), "utf8");
552
+ if (local === latest) {
553
+ console.log(` [current] ${file}`);
554
+ } else {
555
+ console.log(` [outdated] ${file} — run init --force to update`);
556
+ }
557
+ });
558
+
559
+ console.log();
560
+ }
561
+
562
+ // ============================================================
563
+ // Main
564
+ // ============================================================
565
+ switch (command) {
566
+ case "init":
567
+ init();
568
+ break;
569
+ case "audit":
570
+ audit();
571
+ break;
572
+ case "import":
573
+ importPack();
574
+ break;
575
+ case "list":
576
+ listTemplates();
577
+ break;
578
+ case "check":
579
+ check();
580
+ break;
581
+ case "help":
582
+ case "--help":
583
+ case "-h":
584
+ printHelp();
585
+ break;
586
+ default:
587
+ printHelp();
588
+ break;
589
+ }
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "secure-repo",
3
+ "version": "1.0.0",
4
+ "description": "Drop production-grade security standards into any repo. Audit your repo for security issues. Templates for AI-assisted development.",
5
+ "bin": {
6
+ "secure-repo": "./bin/cli.js"
7
+ },
8
+ "keywords": [
9
+ "security",
10
+ "audit",
11
+ "templates",
12
+ "saas",
13
+ "nextjs",
14
+ "supabase",
15
+ "firebase",
16
+ "production",
17
+ "standards",
18
+ "authentication",
19
+ "api-security",
20
+ "security-audit",
21
+ "checklist",
22
+ "devtools",
23
+ "ai-coding"
24
+ ],
25
+ "author": "sebiomoa",
26
+ "license": "MIT",
27
+ "files": [
28
+ "bin/",
29
+ "templates/free/"
30
+ ],
31
+ "engines": {
32
+ "node": ">=16"
33
+ }
34
+ }
@@ -0,0 +1,111 @@
1
+ # API Standards
2
+
3
+ All API endpoints in this repository must follow these rules. Insecure or unvalidated endpoints are the most common attack vector in modern web apps.
4
+
5
+ ## Rules (MUST FOLLOW)
6
+
7
+ - **Validate all input**
8
+ - Every endpoint must validate request body, query params, and path params.
9
+ - Use a schema validation library (e.g. zod, yup, joi).
10
+ - Reject invalid payloads with 400 and a safe error message.
11
+ - Never pass raw user input to database queries or system commands.
12
+
13
+ - **Rate limit all public endpoints**
14
+ - Login, signup, password reset: strict limits (5-10 req/min per IP).
15
+ - Public read endpoints: moderate limits (60-120 req/min per IP).
16
+ - Authenticated endpoints: per-user limits where appropriate.
17
+ - Return 429 when limits are exceeded.
18
+
19
+ - **Authenticate before processing**
20
+ - Protected endpoints must verify auth before any business logic runs.
21
+ - Check auth first, validate input second, then process.
22
+ - Return 401 for missing auth, 403 for insufficient permissions.
23
+
24
+ - **Never expose internal errors**
25
+ - Return generic error messages to clients (e.g. "Something went wrong").
26
+ - Log full error details server-side only.
27
+ - Never leak stack traces, SQL errors, or internal paths in responses.
28
+
29
+ - **Use consistent error format**
30
+ ```json
31
+ {
32
+ "error": {
33
+ "code": "VALIDATION_ERROR",
34
+ "message": "Email is required."
35
+ }
36
+ }
37
+ ```
38
+
39
+ - **Idempotency for write operations**
40
+ - POST endpoints that create resources should support idempotency keys where possible.
41
+ - PUT and DELETE must be idempotent by design.
42
+
43
+ ## Endpoint Conventions
44
+
45
+ ### Naming
46
+
47
+ ```
48
+ GET /api/resources -> list
49
+ GET /api/resources/:id -> get one
50
+ POST /api/resources -> create
51
+ PUT /api/resources/:id -> update (full replace)
52
+ PATCH /api/resources/:id -> update (partial)
53
+ DELETE /api/resources/:id -> delete
54
+ ```
55
+
56
+ ### Response codes
57
+
58
+ | Code | Use |
59
+ |------|-----|
60
+ | 200 | Successful read or update |
61
+ | 201 | Successful creation |
62
+ | 204 | Successful deletion (no body) |
63
+ | 400 | Invalid input |
64
+ | 401 | Not authenticated |
65
+ | 403 | Not authorized |
66
+ | 404 | Resource not found |
67
+ | 409 | Conflict (duplicate, version mismatch) |
68
+ | 429 | Rate limited |
69
+ | 500 | Server error (never expose details) |
70
+
71
+ ## Request/Response Rules
72
+
73
+ - **Content-Type**: Always set and check `Content-Type: application/json` for JSON endpoints.
74
+ - **CORS**: Only allow specific origins. Never use `*` on authenticated endpoints.
75
+ - **Pagination**: List endpoints must support pagination. Default limit: 20-50. Max limit: 100.
76
+ - **Filtering**: Validate filter params against an allowlist. Never pass raw filter values to queries.
77
+
78
+ ## Server Route Pattern (Next.js)
79
+
80
+ Every API route should follow this structure:
81
+
82
+ ```typescript
83
+ export async function POST(request: Request) {
84
+ // 1. Authenticate
85
+ const session = await verifySession(request);
86
+ if (!session) return Response.json({ error: { code: "UNAUTHORIZED" } }, { status: 401 });
87
+
88
+ // 2. Validate input
89
+ const body = await request.json();
90
+ const parsed = schema.safeParse(body);
91
+ if (!parsed.success) return Response.json({ error: { code: "VALIDATION_ERROR" } }, { status: 400 });
92
+
93
+ // 3. Process
94
+ const result = await doTheThing(parsed.data);
95
+
96
+ // 4. Respond
97
+ return Response.json(result, { status: 200 });
98
+ }
99
+ ```
100
+
101
+ ## Required Checks Before Merge
102
+
103
+ Before merging any API changes:
104
+
105
+ - [ ] All inputs are validated with a schema
106
+ - [ ] Rate limiting is configured for public endpoints
107
+ - [ ] Auth is checked before business logic
108
+ - [ ] Error responses do not leak internal details
109
+ - [ ] CORS is configured for specific origins only
110
+ - [ ] New endpoints are documented
111
+ - [ ] No raw user input reaches database queries or shell commands
@@ -0,0 +1,99 @@
1
+ # Authentication & Authorization Policy
2
+
3
+ This repository handles authenticated user sessions and role-based access. Authentication mistakes can expose user data or privileged operations.
4
+
5
+ Treat all authentication logic as **security-critical**.
6
+
7
+ ## Rules (MUST FOLLOW)
8
+
9
+ - **Never trust client authentication state**
10
+ - The server must always verify tokens or sessions.
11
+ - Client-side auth state is for UI rendering only.
12
+
13
+ - **JWT tokens must be verified server-side**
14
+ - Verify signature against your secret/public key.
15
+ - Verify expiration (`exp` claim).
16
+ - Verify issuer (`iss`) and audience (`aud`) if applicable.
17
+ - Reject tokens that fail any check.
18
+
19
+ - **Use short-lived access tokens**
20
+ - Access tokens: 15 minutes or less.
21
+ - Refresh tokens: rotate on every use, invalidate old ones.
22
+ - Never reuse refresh tokens.
23
+
24
+ - **Do not store tokens in localStorage**
25
+ - Prefer `httpOnly`, `Secure`, `SameSite=Strict` cookies.
26
+ - If localStorage is unavoidable, document the risk and compensate with short TTLs.
27
+
28
+ - **Session revocation must be supported**
29
+ - Users must be able to log out of all devices.
30
+ - Token revocation or session invalidation must be server-enforced.
31
+
32
+ - **Password rules**
33
+ - Minimum 8 characters. No maximum below 128.
34
+ - Use bcrypt, scrypt, or argon2 for hashing. Never SHA-256 or MD5 for passwords.
35
+ - Enforce rate limiting on login attempts (max 5 per minute per IP/account).
36
+
37
+ - **Magic link / OTP rules**
38
+ - Links and codes expire within 10 minutes.
39
+ - Single use only. Invalidate after first use.
40
+ - Rate limit generation (max 3 per 15 minutes per email).
41
+
42
+ ## Authorization Model
43
+
44
+ Use role-based access control (RBAC).
45
+
46
+ ### Default roles:
47
+
48
+ | Role | Description |
49
+ |------|-------------|
50
+ | `anonymous` | Unauthenticated visitor. Read-only public data. |
51
+ | `authenticated` | Logged-in user. Access own data only. |
52
+ | `admin` | Full access. Verified server-side. |
53
+
54
+ ### Rules:
55
+
56
+ - Admin actions must require server-side role verification.
57
+ - Never trust role claims sent from the client.
58
+ - Roles must be validated against database state, not just JWT claims.
59
+ - Role escalation (user -> admin) must require re-authentication.
60
+
61
+ ## Protected Endpoints
62
+
63
+ All endpoints requiring authentication must:
64
+
65
+ 1. Verify session or JWT before processing.
66
+ 2. Validate request input with a schema (e.g. zod).
67
+ 3. Enforce role-based permissions.
68
+ 4. Return 401 for missing auth, 403 for insufficient permissions.
69
+
70
+ ### Endpoint patterns:
71
+
72
+ ```
73
+ /api/user/** -> requires authenticated role
74
+ /api/account/** -> requires authenticated role + ownership check
75
+ /api/admin/** -> requires admin role (verified server-side)
76
+ ```
77
+
78
+ ## Common Auth Vulnerabilities To Avoid
79
+
80
+ - Accepting expired or malformed tokens
81
+ - Storing tokens in unsafe storage (localStorage, query params, cookies without httpOnly)
82
+ - Trusting client-provided role claims
83
+ - Allowing unauthenticated password reset flows
84
+ - Missing brute-force protections on login
85
+ - Not invalidating sessions on password change
86
+ - Open redirect after login (validate redirect URLs server-side)
87
+ - CSRF on state-changing auth endpoints (use SameSite cookies or CSRF tokens)
88
+
89
+ ## Required Checks Before Merge
90
+
91
+ Before merging any authentication-related changes:
92
+
93
+ - [ ] Session/token verification works correctly server-side
94
+ - [ ] Protected endpoints reject anonymous users with 401
95
+ - [ ] Admin routes require and verify admin role server-side
96
+ - [ ] Tokens expire correctly and cannot be reused after expiration
97
+ - [ ] Password changes invalidate existing sessions
98
+ - [ ] Rate limiting is active on login and token generation endpoints
99
+ - [ ] No auth tokens appear in URLs, logs, or error messages
@@ -0,0 +1,117 @@
1
+ # Security Policy
2
+
3
+ This repository handles production user data and privileged database access. Treat security requirements as **non-optional**.
4
+
5
+ ## Agent Rules (MUST FOLLOW)
6
+
7
+ - **No secrets in code**
8
+ - Never commit: Database passwords, JWT secrets, API keys, service account keys, or admin credentials.
9
+ - Client-side env vars must be limited to public-safe values only (e.g. public API URLs, publishable keys).
10
+
11
+ - **Assume public keys are public**
12
+ - Anyone can extract client-side keys from a web app.
13
+ - All data protection must be enforced via database access control, safe RPCs, and server-side checks.
14
+
15
+ - **Privileged key usage**
16
+ - Admin or service-level keys may only be used in:
17
+ - Server-side route handlers (e.g. Next.js `src/app/api/**`, Express routes)
18
+ - Serverless functions or edge functions
19
+ - Trusted backend workers
20
+ - Never expose privileged keys to the browser.
21
+
22
+ - **Database access control is mandatory**
23
+ - Any table with user data must have access control (row-level security, middleware checks, or ORM-level policies).
24
+ - Avoid permissive write rules that allow any user to insert or modify any row.
25
+
26
+ - **Prefer server endpoints for public write actions**
27
+ - Any endpoint that records data (analytics, clicks, form submissions) must:
28
+ - Validate input (e.g. zod)
29
+ - Apply rate limits
30
+ - Use server-side database writes with privileged credentials
31
+ - Client components must not write to the database directly. Use a server route.
32
+
33
+ - **Function and procedure hardening**
34
+ - Database functions that run with elevated privileges must:
35
+ - Have a fixed, safe execution context.
36
+ - Restrict execution to the roles that need it.
37
+
38
+ - **Do not increase attack surface**
39
+ - Do not attach privileged objects to `window` or global scope unless strictly required.
40
+ - Avoid broad CORS (`*`) except where explicitly intended and reviewed.
41
+
42
+ ## Required Checks Before Merge
43
+
44
+ - **Repository scan**
45
+ - Search for leaked keys:
46
+ - Database passwords or connection strings
47
+ - JWTs (`eyJ...`)
48
+ - Any secret keys, tokens, or credentials
49
+
50
+ - **Database review** (required after any DB/access control change)
51
+ - Verify access control policies cover new tables and columns.
52
+ - Run security and performance checks available in your database platform.
53
+
54
+ - **Verify public write paths**
55
+ - Confirm no tables allow anonymous inserts without validation/rate limiting.
56
+ - Confirm any public write action is routed through a server endpoint.
57
+
58
+ ## Patterns To Use
59
+
60
+ - **Server-side event tracking**
61
+ - Use a server API route with rate limiting and privileged database writes.
62
+ - Example endpoints:
63
+ - `/api/track-click` -> records event via server-side DB client
64
+ - `/api/track-event` -> records event via server-side DB client
65
+
66
+ - **Restrict direct database function execution**
67
+ - Sensitive database functions must not be callable by anonymous or public roles.
68
+ - Execution should be restricted to server-side service accounts.
69
+
70
+ - **Public analytics**
71
+ - Prefer a server endpoint that accepts sanitized payloads and enforces bot checks.
72
+
73
+ - **Admin-only actions**
74
+ - Admin endpoints must use privileged database credentials and proper authorization.
75
+ - Avoid using public/anonymous database clients inside admin routes.
76
+
77
+ ## Enforced Architecture
78
+
79
+ ### Option A: Server routes for any privileged writes
80
+
81
+ - Direct writes to protected tables from client code are not allowed.
82
+ - All privileged writes must go through server-side route handlers.
83
+ - Server routes must:
84
+ - Validate input (e.g. zod)
85
+ - Rate limit
86
+ - Authenticate via `Authorization: Bearer <JWT>` or session cookie
87
+ - Use privileged credentials for DB writes
88
+
89
+ ### Option B: Anonymous activity is local-only
90
+
91
+ - Anonymous activity must remain local until login.
92
+ - Cloud state for user data is **authenticated-only**:
93
+ - User lists and collections
94
+ - Recently viewed items
95
+ - Progress tracking
96
+ - Any user-specific data
97
+
98
+ ## Database Hardening Rules
99
+
100
+ - **Protected tables**
101
+ - Admin tables, user tokens, user data tables, and any sensitive business data.
102
+
103
+ - **Access control and grants**
104
+ - Admin tables are accessible only via privileged server-side credentials.
105
+ - Public tables allow read-only access where appropriate.
106
+ - Table writes are server-side only (via server endpoints).
107
+
108
+ - **Function grants + hardening**
109
+ - Sensitive database functions are restricted to privileged roles only.
110
+ - Functions with elevated privileges must have a fixed, safe execution context.
111
+
112
+ ## Incident Response (If a key is exposed)
113
+
114
+ 1. Rotate impacted keys immediately.
115
+ 2. Audit access control policies and function grants.
116
+ 3. Review logs for unusual access patterns.
117
+ 4. Add a regression test/checklist entry to prevent recurrence.