secure-repo 1.0.8 → 1.1.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/bin/cli.js +128 -33
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -10,7 +10,8 @@ const FREE_DIR = path.join(TEMPLATES_DIR, "free");
|
|
|
10
10
|
|
|
11
11
|
const POLAR_ORGANIZATION_ID = "d55baa70-3a94-4549-901a-2b4c920ff122";
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
// Pro download endpoint (server-side proxy — token never ships in client code)
|
|
14
|
+
const PRO_DOWNLOAD_URL = "https://shipsecure.app/api/download-pro";
|
|
14
15
|
|
|
15
16
|
const args = process.argv.slice(2);
|
|
16
17
|
const command = args[0];
|
|
@@ -173,37 +174,40 @@ function verifyLicense(licenseKey) {
|
|
|
173
174
|
}
|
|
174
175
|
|
|
175
176
|
// ============================================================
|
|
176
|
-
// Download
|
|
177
|
+
// Download pro zip via server-side proxy
|
|
177
178
|
// ============================================================
|
|
178
|
-
function
|
|
179
|
+
function downloadProZip(destPath, licenseKey) {
|
|
179
180
|
return new Promise((resolve, reject) => {
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
181
|
+
const parsed = new URL(PRO_DOWNLOAD_URL);
|
|
182
|
+
const postData = JSON.stringify({ license_key: licenseKey });
|
|
183
|
+
const opts = {
|
|
184
|
+
hostname: parsed.hostname,
|
|
185
|
+
path: parsed.pathname,
|
|
186
|
+
method: "POST",
|
|
187
|
+
headers: {
|
|
188
|
+
"User-Agent": "secure-repo-cli",
|
|
189
|
+
"Content-Type": "application/json",
|
|
190
|
+
"Content-Length": Buffer.byteLength(postData),
|
|
191
|
+
},
|
|
192
|
+
};
|
|
194
193
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
194
|
+
const req = https.request(opts, (res) => {
|
|
195
|
+
if (res.statusCode !== 200) {
|
|
196
|
+
let body = "";
|
|
197
|
+
res.on("data", (chunk) => (body += chunk));
|
|
198
|
+
res.on("end", () => {
|
|
199
|
+
reject(new Error(`Download failed (HTTP ${res.statusCode}): ${body}`));
|
|
199
200
|
});
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const file = fs.createWriteStream(destPath);
|
|
204
|
+
res.pipe(file);
|
|
205
|
+
file.on("finish", () => { file.close(); resolve(); });
|
|
206
|
+
});
|
|
205
207
|
|
|
206
|
-
|
|
208
|
+
req.on("error", (err) => reject(new Error(`Network error: ${err.message}`)));
|
|
209
|
+
req.write(postData);
|
|
210
|
+
req.end();
|
|
207
211
|
});
|
|
208
212
|
}
|
|
209
213
|
|
|
@@ -269,6 +273,87 @@ function installFromZip(zipPath, outputDir, force) {
|
|
|
269
273
|
}
|
|
270
274
|
}
|
|
271
275
|
|
|
276
|
+
// ============================================================
|
|
277
|
+
// Agent instruction files — tell AI agents to read policy files
|
|
278
|
+
// ============================================================
|
|
279
|
+
const AGENT_INSTRUCTION = `# Security Policies — MUST READ
|
|
280
|
+
|
|
281
|
+
This project uses ShipSecure security policies. Before writing or modifying code, you MUST read and follow every policy file that exists in this repository.
|
|
282
|
+
|
|
283
|
+
## Policy Files
|
|
284
|
+
|
|
285
|
+
Read each of these files if they exist before making changes:
|
|
286
|
+
|
|
287
|
+
### Core
|
|
288
|
+
- SECURITY.md — Secrets management, attack surface, enforced architecture
|
|
289
|
+
- AUTH.md — Token handling, session rules, password policy, roles
|
|
290
|
+
- API.md — Input validation, rate limiting, error handling
|
|
291
|
+
|
|
292
|
+
### Extended
|
|
293
|
+
- DATABASE.md — Query safety, access control, migrations
|
|
294
|
+
- ENV_VARIABLES.md — Environment variable handling, secret rotation
|
|
295
|
+
- DEPLOYMENT.md — Deploy pipeline, environment isolation
|
|
296
|
+
- INCIDENT_RESPONSE.md — Breach response, escalation procedures
|
|
297
|
+
- ACCESS_CONTROL.md — Role-based access, permission boundaries
|
|
298
|
+
- DATA_PRIVACY.md — PII handling, data retention, GDPR compliance
|
|
299
|
+
- PAYMENTS.md — Payment processing, PCI compliance
|
|
300
|
+
- FILE_UPLOADS.md — Upload validation, storage security
|
|
301
|
+
- RATE_LIMITING.md — Throttling, abuse prevention
|
|
302
|
+
- THIRD_PARTY.md — Dependency security, vendor risk
|
|
303
|
+
- LOGGING_PII.md — Log sanitization, PII redaction
|
|
304
|
+
- TESTING.md — Security test requirements
|
|
305
|
+
- OBSERVABILITY.md — Monitoring, alerting, audit trails
|
|
306
|
+
- THREAT_MODEL.md — Known threats and mitigations
|
|
307
|
+
- PR_CHECKLIST.md — Pre-merge security checklist
|
|
308
|
+
- CONTRIBUTING_SECURITY.md — Security contribution guidelines
|
|
309
|
+
- VULNERABILITY_REPORTING.md — Responsible disclosure process
|
|
310
|
+
- POLICY_INDEX.md — Index of all policies
|
|
311
|
+
- FULL_AUDIT_CHECKLIST.md — 100+ point security audit checklist
|
|
312
|
+
|
|
313
|
+
### Stack Presets
|
|
314
|
+
- supabase-preset/ — Supabase-specific security rules (if present)
|
|
315
|
+
- firebase-preset/ — Firebase-specific security rules (if present)
|
|
316
|
+
|
|
317
|
+
## Rules
|
|
318
|
+
|
|
319
|
+
1. Always check policy files before writing code — if your task touches auth, APIs, database, payments, file uploads, or any area with a policy file, read that file first.
|
|
320
|
+
2. Never violate a policy — if a policy says "never do X", do not do X. Flag it if unsure.
|
|
321
|
+
3. Secrets are never hardcoded — no API keys, tokens, passwords, or credentials in source code.
|
|
322
|
+
4. Validate all input — every endpoint, every form, every external data source.
|
|
323
|
+
5. Follow the principle of least privilege — only request the permissions you need.
|
|
324
|
+
`;
|
|
325
|
+
|
|
326
|
+
function writeAgentFiles(outputDir, force) {
|
|
327
|
+
const agentFiles = [
|
|
328
|
+
{ path: "CLAUDE.md", name: "Claude" },
|
|
329
|
+
{ path: ".cursorrules", name: "Cursor" },
|
|
330
|
+
{ path: ".github/copilot-instructions.md", name: "GitHub Copilot" },
|
|
331
|
+
{ path: ".windsurfrules", name: "Windsurf" },
|
|
332
|
+
{ path: ".clinerules", name: "Cline" },
|
|
333
|
+
];
|
|
334
|
+
|
|
335
|
+
let written = 0;
|
|
336
|
+
let skipped = 0;
|
|
337
|
+
|
|
338
|
+
console.log("\n Agent instructions:");
|
|
339
|
+
agentFiles.forEach(({ path: filePath, name }) => {
|
|
340
|
+
const fullPath = path.join(outputDir, filePath);
|
|
341
|
+
const dir = path.dirname(fullPath);
|
|
342
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
343
|
+
|
|
344
|
+
if (fs.existsSync(fullPath) && !force) {
|
|
345
|
+
console.log(` [skip] ${filePath} (${name}) — use --force to overwrite`);
|
|
346
|
+
skipped++;
|
|
347
|
+
} else {
|
|
348
|
+
fs.writeFileSync(fullPath, AGENT_INSTRUCTION);
|
|
349
|
+
console.log(` [done] ${filePath} (${name})`);
|
|
350
|
+
written++;
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
return { written, skipped };
|
|
355
|
+
}
|
|
356
|
+
|
|
272
357
|
// ============================================================
|
|
273
358
|
// INIT — install templates (free, or free + pro with --key)
|
|
274
359
|
// ============================================================
|
|
@@ -300,11 +385,14 @@ async function init() {
|
|
|
300
385
|
console.log("\n Downloading pro templates...");
|
|
301
386
|
|
|
302
387
|
try {
|
|
303
|
-
await
|
|
388
|
+
await downloadProZip(zipPath, licenseKey);
|
|
304
389
|
const proResult = installFromZip(zipPath, outputDir, force);
|
|
305
390
|
|
|
306
|
-
|
|
307
|
-
const
|
|
391
|
+
// Write agent instruction files
|
|
392
|
+
const agentResult = writeAgentFiles(outputDir, force);
|
|
393
|
+
|
|
394
|
+
const totalCopied = freeResult.copied + proResult.copied + agentResult.written;
|
|
395
|
+
const totalSkipped = freeResult.skipped + proResult.skipped + agentResult.skipped;
|
|
308
396
|
|
|
309
397
|
console.log(`\n Done! ${totalCopied} files installed, ${totalSkipped} skipped.`);
|
|
310
398
|
console.log("\n Next steps:");
|
|
@@ -326,7 +414,13 @@ async function init() {
|
|
|
326
414
|
console.log(" Free templates:");
|
|
327
415
|
const result = copyFiles(FREE_DIR, outputDir, force);
|
|
328
416
|
|
|
329
|
-
|
|
417
|
+
// Write agent instruction files
|
|
418
|
+
const agentResult = writeAgentFiles(outputDir, force);
|
|
419
|
+
|
|
420
|
+
const totalCopied = result.copied + agentResult.written;
|
|
421
|
+
const totalSkipped = result.skipped + agentResult.skipped;
|
|
422
|
+
|
|
423
|
+
console.log(`\n Done! ${totalCopied} files added, ${totalSkipped} skipped.`);
|
|
330
424
|
console.log("\n Next steps:");
|
|
331
425
|
console.log(" 1. Customize the templates for your project");
|
|
332
426
|
console.log(" 2. Run: npx secure-repo audit");
|
|
@@ -370,9 +464,10 @@ function importPack() {
|
|
|
370
464
|
|
|
371
465
|
try {
|
|
372
466
|
const proResult = installFromZip(resolvedPath, outputDir, force);
|
|
467
|
+
const agentResult = writeAgentFiles(outputDir, force);
|
|
373
468
|
|
|
374
|
-
const totalCopied = freeResult.copied + proResult.copied;
|
|
375
|
-
const totalSkipped = freeResult.skipped + proResult.skipped;
|
|
469
|
+
const totalCopied = freeResult.copied + proResult.copied + agentResult.written;
|
|
470
|
+
const totalSkipped = freeResult.skipped + proResult.skipped + agentResult.skipped;
|
|
376
471
|
|
|
377
472
|
console.log(`\n Done! ${totalCopied} files imported, ${totalSkipped} skipped.\n`);
|
|
378
473
|
} catch (err) {
|
package/package.json
CHANGED