@vibekiln/cutline-mcp-cli 0.3.0 → 0.4.1
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/dist/auth/keychain.js
CHANGED
|
@@ -4365,6 +4365,39 @@ var UNIVERSAL_CONSTRAINTS = [
|
|
|
4365
4365
|
file_patterns: ["**/api/auth/**", "**/api/login*", "**/api/callback*", "**/api/checkout*", "**/middleware/**"],
|
|
4366
4366
|
framework: "baseline"
|
|
4367
4367
|
},
|
|
4368
|
+
{
|
|
4369
|
+
id_suffix: "sensitive_tokens_not_in_urls",
|
|
4370
|
+
category: "security",
|
|
4371
|
+
summary: "Auth/session/API tokens MUST NOT be passed in URL query parameters during redirects. Sensitive tokens in URLs leak via logs, browser history, and referrers.",
|
|
4372
|
+
keywords: ["token", "query-param", "callback", "returnUrl", "redirect", "referrer", "url-leakage"],
|
|
4373
|
+
severity: "critical",
|
|
4374
|
+
action: "Use Authorization headers or httpOnly cookies for token transport. Validate callback/return URLs against an allowlist and never append bearer/session tokens to redirected URLs.",
|
|
4375
|
+
checklist_ref: "D11",
|
|
4376
|
+
file_patterns: ["**/auth/**", "**/api/auth/**", "**/api/**/checkout*", "**/mcp-auth*", "**/session/**", "**/middleware/**"],
|
|
4377
|
+
framework: "baseline"
|
|
4378
|
+
},
|
|
4379
|
+
{
|
|
4380
|
+
id_suffix: "no_secrets_in_query_params",
|
|
4381
|
+
category: "security",
|
|
4382
|
+
summary: "Secrets (revalidation secrets, API secrets, webhook secrets) MUST NOT be transported via URL query parameters. Query-string secrets are leaked through logs, referrers, and browser history.",
|
|
4383
|
+
keywords: ["secret", "query-param", "url", "revalidate", "webhook", "referrer", "leakage"],
|
|
4384
|
+
severity: "critical",
|
|
4385
|
+
action: "Accept secrets only via headers or signed request bodies. Reject secret-bearing query params in production endpoints. Rotate any secret previously sent in URLs.",
|
|
4386
|
+
checklist_ref: "D12",
|
|
4387
|
+
file_patterns: ["**/api/**", "**/webhooks/**", "**/revalidate/**", "**/middleware/**"],
|
|
4388
|
+
framework: "baseline"
|
|
4389
|
+
},
|
|
4390
|
+
{
|
|
4391
|
+
id_suffix: "no_state_change_get_cookie_auth",
|
|
4392
|
+
category: "security",
|
|
4393
|
+
summary: "State-changing operations MUST NOT be reachable via GET when cookie authentication is accepted. GET + cookie auth creates CSRF risk.",
|
|
4394
|
+
keywords: ["csrf", "get", "state-change", "cookie-auth", "origin-check", "referer", "method-safety"],
|
|
4395
|
+
severity: "critical",
|
|
4396
|
+
action: "Use POST/PUT/DELETE for side effects. If GET fallback is unavoidable, enforce strict same-origin checks (Origin/Referer/sec-fetch-site) and avoid cookie-based auth fallback where possible.",
|
|
4397
|
+
checklist_ref: "D13",
|
|
4398
|
+
file_patterns: ["**/api/**", "**/auth/**", "**/checkout/**", "**/middleware/**"],
|
|
4399
|
+
framework: "baseline"
|
|
4400
|
+
},
|
|
4368
4401
|
{
|
|
4369
4402
|
id_suffix: "ai_cost_caps",
|
|
4370
4403
|
category: "security",
|
|
@@ -5551,6 +5584,9 @@ D7. Do sensitive actions (account deletion, email change, role escalation) requi
|
|
|
5551
5584
|
D8. Is payment/billing logic validated server-side? Can prices or quantities be tampered with client-side?
|
|
5552
5585
|
D9. Are redirect URLs validated against an allowlist? Can open redirects be exploited for phishing?
|
|
5553
5586
|
D10. Are webhook signatures verified before processing payment or event data?
|
|
5587
|
+
D11. Are auth/session/API tokens kept out of URL query params (including callback/returnUrl redirects) and transported via headers or httpOnly cookies instead?
|
|
5588
|
+
D12. Are secrets (revalidate/API/webhook/etc.) kept out of URL query params and accepted only via headers or signed bodies?
|
|
5589
|
+
D13. Are side-effecting endpoints using non-GET methods, and are cookie-auth GET fallbacks protected with strict same-origin checks?
|
|
5554
5590
|
|
|
5555
5591
|
### E. Security Rules & Infrastructure
|
|
5556
5592
|
E1. For Firestore/database rules: do they enforce per-user data isolation?
|
|
@@ -5629,7 +5665,7 @@ Return a JSON object with exactly these fields:
|
|
|
5629
5665
|
- targetUsers (string): Who uses this product, from a security perspective.
|
|
5630
5666
|
- referenceClasses (string[]): Security frameworks or standards that apply (e.g., "OWASP Top 10 2021", "SOC 2 Type II").
|
|
5631
5667
|
- constraints (object?): Resource constraints \u2014 team, budget_usd, deadline_days, must_ship_scope.
|
|
5632
|
-
- checklist_summary (object): Keys are checklist IDs (A1-A8, B1-B6, C1-
|
|
5668
|
+
- checklist_summary (object): Keys are checklist IDs (A1-A8, B1-B6, C1-C8, D1-D13, E1-E7, F1-F4, G-*, H1-H3, I1-I8, J1-J6, K1-K8), values are "pass"|"fail"|"warn"|"not_applicable". This forces systematic coverage.
|
|
5633
5669
|
- compliance_signals (array of {framework: "pci_dss"|"hipaa"|"fedramp"|"gdpr_ccpa"|"owasp_llm"|"glba"|"ferpa_coppa"|"csa_ccm"|"ios_app_store", signal: string, confidence: number}?): Detected compliance framework signals. Return [] if none.
|
|
5634
5670
|
|
|
5635
5671
|
Be concrete and specific. Reference file paths and line numbers where possible. If a checklist item cannot be assessed from the provided files, mark it "not_applicable" and note why. Cover ALL sections A through K.`;
|
|
@@ -5855,6 +5891,11 @@ var SECURITY_PATH_PATTERNS = [
|
|
|
5855
5891
|
/\/permissions?/i,
|
|
5856
5892
|
/\/roles?/i,
|
|
5857
5893
|
/\/tokens?/i,
|
|
5894
|
+
/\/redirect/i,
|
|
5895
|
+
/\/callback/i,
|
|
5896
|
+
/returnurl/i,
|
|
5897
|
+
/\/mcp-auth/i,
|
|
5898
|
+
/checkout-link/i,
|
|
5858
5899
|
/\/csrf/i,
|
|
5859
5900
|
/\/rate-limit/i,
|
|
5860
5901
|
/\/encrypt/i,
|
|
@@ -6538,6 +6579,11 @@ var SECURITY_PATH_PATTERNS2 = [
|
|
|
6538
6579
|
/billing/i,
|
|
6539
6580
|
/stripe/i,
|
|
6540
6581
|
/webhook/i,
|
|
6582
|
+
/redirect/i,
|
|
6583
|
+
/callback/i,
|
|
6584
|
+
/returnurl/i,
|
|
6585
|
+
/mcp-auth/i,
|
|
6586
|
+
/checkout-link/i,
|
|
6541
6587
|
// Scalability & reliability patterns
|
|
6542
6588
|
/\/db\//i,
|
|
6543
6589
|
/queries?\//i,
|
|
@@ -7047,7 +7093,7 @@ function deltaStr(current, previous) {
|
|
|
7047
7093
|
return " (no change)";
|
|
7048
7094
|
return diff > 0 ? ` (**+${diff}** since last scan)` : ` (**${diff}** since last scan)`;
|
|
7049
7095
|
}
|
|
7050
|
-
function formatAuditOutput(result, reportId, publicSiteUrl = "https://thecutline.ai", hiddenAuditDimensions = []) {
|
|
7096
|
+
function formatAuditOutput(result, reportId, publicSiteUrl = "https://thecutline.ai", hiddenAuditDimensions = [], fullReport = false) {
|
|
7051
7097
|
const m = result.metrics;
|
|
7052
7098
|
const p = result.previousMetrics;
|
|
7053
7099
|
const isRescan = !!p;
|
|
@@ -7135,24 +7181,32 @@ function formatAuditOutput(result, reportId, publicSiteUrl = "https://thecutline
|
|
|
7135
7181
|
const totalFindings = visibleFindings.length;
|
|
7136
7182
|
const criticalCount = visibleFindings.filter((g) => g.severity === "critical" || g.severity === "high").length;
|
|
7137
7183
|
if (totalFindings > 0) {
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
lines.push(``, `## ${remaining.length} More Finding${remaining.length > 1 ? "s" : ""} Detected`);
|
|
7143
|
-
const teaserLimit = Math.min(remaining.length, 5);
|
|
7144
|
-
for (let i = 0; i < teaserLimit; i++) {
|
|
7145
|
-
lines.push(`- [${remaining[i].severity.toUpperCase()}] ${remaining[i].title}`);
|
|
7184
|
+
if (fullReport) {
|
|
7185
|
+
lines.push(``, `## Findings`, ``);
|
|
7186
|
+
for (const finding of visibleFindings) {
|
|
7187
|
+
lines.push(`**[${finding.severity.toUpperCase()}] ${finding.title}**`, `*Category: ${finding.category}*`, finding.description || "Address this finding to improve your readiness scores.", ``);
|
|
7146
7188
|
}
|
|
7147
|
-
|
|
7148
|
-
|
|
7189
|
+
lines.push(`> Re-run \`code_audit\` after fixes to measure score improvements.`);
|
|
7190
|
+
} else {
|
|
7191
|
+
const topFinding = visibleFindings[0];
|
|
7192
|
+
lines.push(``, `## #1 Finding \u2014 Fix This Now`, ``, `**[${topFinding.severity.toUpperCase()}] ${topFinding.title}**`, `*Category: ${topFinding.category}*`, ``, topFinding.description || "Address this finding to improve your readiness scores.", ``, `> Fix this issue, then re-run \`code_audit\` to see your scores improve.`);
|
|
7193
|
+
const remaining = visibleFindings.slice(1);
|
|
7194
|
+
if (remaining.length > 0) {
|
|
7195
|
+
lines.push(``, `## ${remaining.length} More Finding${remaining.length > 1 ? "s" : ""} Detected`);
|
|
7196
|
+
const teaserLimit = Math.min(remaining.length, 5);
|
|
7197
|
+
for (let i = 0; i < teaserLimit; i++) {
|
|
7198
|
+
lines.push(`- [${remaining[i].severity.toUpperCase()}] ${remaining[i].title}`);
|
|
7199
|
+
}
|
|
7200
|
+
if (remaining.length > teaserLimit) {
|
|
7201
|
+
lines.push(`- ... and **${remaining.length - teaserLimit} more**`);
|
|
7202
|
+
}
|
|
7149
7203
|
}
|
|
7150
7204
|
}
|
|
7151
7205
|
}
|
|
7152
7206
|
lines.push(``, `---`, ``);
|
|
7153
|
-
if (totalFindings > 1) {
|
|
7207
|
+
if (!fullReport && totalFindings > 1) {
|
|
7154
7208
|
lines.push(`### Unlock Full Analysis`, ``, `You fixed one \u2014 **${totalFindings - 1} findings** remain (${criticalCount} critical/high).`, `Upgrade to Cutline Pro for:`, `- Full details and remediation for every finding`, `- Prioritized RGR remediation plans your coding agent can execute`, `- Product-specific deep dive with feature-level constraint mapping`, `- Continuous score tracking with this audit as a baseline prior`, ``, `\u2192 Run \`cutline-mcp upgrade\` or visit **https://thecutline.ai/upgrade**`);
|
|
7155
|
-
} else if (totalFindings
|
|
7209
|
+
} else if (totalFindings >= 1) {
|
|
7156
7210
|
lines.push(`### Next Steps`, ``, `Fix the finding above, then re-scan to see your scores improve.`, `When ready, run a **deep dive** for product-specific analysis \u2014`, `these generic scores will serve as a prior for your product graph.`);
|
|
7157
7211
|
} else {
|
|
7158
7212
|
lines.push(`### Next Steps`, ``, `No critical findings detected. Run a **deep dive** for product-specific`, `analysis with feature coverage and the generic scores as a prior.`);
|
|
@@ -8377,15 +8431,17 @@ Why AI: ${idea.whyAI}`
|
|
|
8377
8431
|
return "engineering";
|
|
8378
8432
|
return "security";
|
|
8379
8433
|
};
|
|
8434
|
+
let hasPremiumSubscription = false;
|
|
8380
8435
|
{
|
|
8381
8436
|
const rateInfo = await getScanRateLimit();
|
|
8437
|
+
hasPremiumSubscription = rateInfo.subscription === "active" || rateInfo.subscription === "trialing";
|
|
8382
8438
|
const now = /* @__PURE__ */ new Date();
|
|
8383
8439
|
const resetAt = rateInfo.periodStart ? new Date(rateInfo.periodStart) : /* @__PURE__ */ new Date(0);
|
|
8384
8440
|
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
8385
8441
|
if (resetAt < monthStart) {
|
|
8386
8442
|
await updateScanRateLimit({ free_scan_counter: { count: 1, reset_at: now.toISOString() } });
|
|
8387
8443
|
} else if (rateInfo.scanCount >= 3) {
|
|
8388
|
-
if (
|
|
8444
|
+
if (!hasPremiumSubscription) {
|
|
8389
8445
|
throw new McpError(ErrorCode.InvalidRequest, "Free scan limit reached (3/month). Run `cutline-mcp upgrade` in your terminal, or visit https://thecutline.ai/upgrade");
|
|
8390
8446
|
}
|
|
8391
8447
|
} else {
|
|
@@ -8454,7 +8510,7 @@ Why AI: ${idea.whyAI}`
|
|
|
8454
8510
|
console.error("[code_audit] Report persistence failed (non-fatal):", e);
|
|
8455
8511
|
}
|
|
8456
8512
|
return {
|
|
8457
|
-
content: [{ type: "text", text: formatAuditOutput(result, reportId, reportSiteUrl, hiddenAuditDimensions) }]
|
|
8513
|
+
content: [{ type: "text", text: formatAuditOutput(result, reportId, reportSiteUrl, hiddenAuditDimensions, hasPremiumSubscription) }]
|
|
8458
8514
|
};
|
|
8459
8515
|
}
|
|
8460
8516
|
const authCtx = await resolveAuthContext(args.auth_token);
|
|
@@ -12,6 +12,13 @@ export function saveConfig(config) {
|
|
|
12
12
|
ensureConfigDir();
|
|
13
13
|
const current = loadConfig();
|
|
14
14
|
const newConfig = { ...current, ...config };
|
|
15
|
+
// Treat explicit undefined values as key deletion so callers can clear
|
|
16
|
+
// persisted fields (for example during logout).
|
|
17
|
+
for (const [key, value] of Object.entries(config)) {
|
|
18
|
+
if (value === undefined) {
|
|
19
|
+
delete newConfig[key];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
15
22
|
// Remove legacy fields that are no longer stored (e.g. firebaseApiKey)
|
|
16
23
|
// to prevent stale values from causing cross-project auth mismatches
|
|
17
24
|
delete newConfig.firebaseApiKey;
|
package/package.json
CHANGED