periderm-cli 0.1.12 → 0.1.14
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/api.js +16 -0
- package/dist/index.js +18 -2
- package/dist/review/deep.js +4 -1
- package/dist/scanner/index.js +3 -2
- package/dist/scanner/walk.js +9 -3
- package/package.json +1 -1
package/dist/api.js
CHANGED
|
@@ -58,3 +58,19 @@ export async function uploadScan(token, result) {
|
|
|
58
58
|
return { error: e.message || "Network error: failed to reach server" };
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
+
export async function fetchCloudPolicies(token) {
|
|
62
|
+
const { apiUrl } = readConfig();
|
|
63
|
+
try {
|
|
64
|
+
const res = await fetch(`${apiUrl}/api/policies`, {
|
|
65
|
+
method: "POST",
|
|
66
|
+
headers: { "Content-Type": "application/json" },
|
|
67
|
+
body: JSON.stringify({ token }),
|
|
68
|
+
});
|
|
69
|
+
if (!res.ok)
|
|
70
|
+
return { global_ignores: [], custom_ai_rules: "" };
|
|
71
|
+
return (await res.json());
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return { global_ignores: [], custom_ai_rules: "" };
|
|
75
|
+
}
|
|
76
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { scan } from "./scanner/index.js";
|
|
|
8
8
|
import { runDeepReview } from "./review/deep.js";
|
|
9
9
|
import { printTerminal } from "./report/terminal.js";
|
|
10
10
|
import { readConfig, writeConfig } from "./config.js";
|
|
11
|
-
import { verifyToken, uploadScan } from "./api.js";
|
|
11
|
+
import { verifyToken, uploadScan, fetchCloudPolicies } from "./api.js";
|
|
12
12
|
import http from "node:http";
|
|
13
13
|
import ora from "ora";
|
|
14
14
|
import { PERIDERM_ASCII } from "./constants.js";
|
|
@@ -50,12 +50,28 @@ program
|
|
|
50
50
|
verifySpinner.fail(`Couldn't connect — ${v.error}. Run \`periderm login\` again.`);
|
|
51
51
|
process.exit(1);
|
|
52
52
|
}
|
|
53
|
+
if (process.env.CI && v.plan === "starter") {
|
|
54
|
+
verifySpinner.fail("Automated CI/CD reporting requires the Scale or Unlimited plan.");
|
|
55
|
+
console.info(chalk.yellow(`\nUpgrade at ${cfg.apiUrl}/dashboard/billing\n`));
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
53
58
|
if (v.plan !== "unlimited" && (v.scans_remaining === undefined || v.scans_remaining <= 0)) {
|
|
54
59
|
verifySpinner.fail(`No scans left on your ${v.plan} plan.`);
|
|
55
60
|
console.info(chalk.yellow(`\nRenew or upgrade at ${cfg.apiUrl}/dashboard/billing\n`));
|
|
56
61
|
process.exit(1);
|
|
57
62
|
}
|
|
58
63
|
verifySpinner.stop();
|
|
64
|
+
// Fetch cloud policies for Unlimited users
|
|
65
|
+
let cloudIgnores = [];
|
|
66
|
+
let cloudAiRules = "";
|
|
67
|
+
if (v.plan === "unlimited") {
|
|
68
|
+
const policies = await fetchCloudPolicies(cfg.token);
|
|
69
|
+
cloudIgnores = policies.global_ignores ?? [];
|
|
70
|
+
cloudAiRules = policies.custom_ai_rules ?? "";
|
|
71
|
+
if (cloudIgnores.length > 0) {
|
|
72
|
+
console.info(chalk.dim(` ↳ Applying ${cloudIgnores.length} cloud ignore rule(s) from your dashboard policies.`));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
59
75
|
const startTime = Date.now();
|
|
60
76
|
console.info(chalk.cyan("Discovering project files…"));
|
|
61
77
|
let scanSpinner = ora({ text: "Initializing...", color: "cyan" }).start();
|
|
@@ -67,7 +83,7 @@ program
|
|
|
67
83
|
const empty = 20 - filled;
|
|
68
84
|
return `[${"█".repeat(filled)}${"░".repeat(empty)}] ${Math.round(pct * 100)}%`;
|
|
69
85
|
};
|
|
70
|
-
const result = await scan(root, {
|
|
86
|
+
const result = await scan(root, { cloudIgnores, cloudAiRules,
|
|
71
87
|
onProgress: (ev) => {
|
|
72
88
|
switch (ev.type) {
|
|
73
89
|
case "discovering":
|
package/dist/review/deep.js
CHANGED
|
@@ -84,6 +84,9 @@ function parseAction(raw) {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
export async function runDeepReview(root, scan, token, apiUrl, onStatus) {
|
|
87
|
+
const orgRulesSection = scan.cloudAiRules?.trim()
|
|
88
|
+
? `\n\nOrganization-specific instructions (MUST follow):\n${scan.cloudAiRules.trim()}`
|
|
89
|
+
: "";
|
|
87
90
|
const system = `You are Periderm's deep launch reviewer. Respond with JSON only.
|
|
88
91
|
|
|
89
92
|
Existing static scan found ${scan.findings.length} issues. Your job: find ADDITIONAL nuanced launch risks the deterministic scanner missed — legal/UX/business logic edge cases, contradictions, deceptive flows, subtle security gaps.
|
|
@@ -94,7 +97,7 @@ Tools (respond with {"action":"tool",...}):
|
|
|
94
97
|
|
|
95
98
|
When finished: {"action":"done","summary":"...","findings":[{"message":"...","severity":"high|medium|low|critical","file":"path","why":"...","fix":"..."}]}
|
|
96
99
|
|
|
97
|
-
Rules: max 5 new findings, be specific, cite files, no hallucinated files
|
|
100
|
+
Rules: max 5 new findings, be specific, cite files, no hallucinated files.${orgRulesSection}`;
|
|
98
101
|
const context = {
|
|
99
102
|
project: scan.projectName,
|
|
100
103
|
verdict: scan.verdict,
|
package/dist/scanner/index.js
CHANGED
|
@@ -11,10 +11,10 @@ import { renderMarkdown } from "../report/markdown.js";
|
|
|
11
11
|
import { mapWithProgress } from "./progress.js";
|
|
12
12
|
export { formatScanPath } from "./progress.js";
|
|
13
13
|
export async function scan(root, options = {}) {
|
|
14
|
-
const { onProgress } = options;
|
|
14
|
+
const { onProgress, cloudIgnores = [], cloudAiRules = "" } = options;
|
|
15
15
|
const findings = [];
|
|
16
16
|
onProgress?.({ type: "discovering" });
|
|
17
|
-
const files = await walk(root);
|
|
17
|
+
const files = await walk(root, cloudIgnores);
|
|
18
18
|
onProgress?.({ type: "discovered", total: files.length });
|
|
19
19
|
const estimatedChecks = files.length * PER_FILE_CHECK_COUNT +
|
|
20
20
|
REPO_WIDE_CHECK_COUNT +
|
|
@@ -96,5 +96,6 @@ export async function scan(root, options = {}) {
|
|
|
96
96
|
verdict,
|
|
97
97
|
markdown,
|
|
98
98
|
meta,
|
|
99
|
+
cloudAiRules: cloudAiRules || undefined,
|
|
99
100
|
};
|
|
100
101
|
}
|
package/dist/scanner/walk.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import fg from "fast-glob";
|
|
2
|
-
export async function walk(root) {
|
|
3
|
-
|
|
4
|
-
"**/*.{ts,tsx,js,jsx,mjs,cjs}",
|
|
2
|
+
export async function walk(root, extraIgnores = []) {
|
|
3
|
+
const baseIgnores = [
|
|
5
4
|
"!**/node_modules/**",
|
|
6
5
|
"!**/dist/**",
|
|
7
6
|
"!**/build/**",
|
|
@@ -12,5 +11,12 @@ export async function walk(root) {
|
|
|
12
11
|
"!**/*.d.ts",
|
|
13
12
|
"!**/routeTree.gen.*",
|
|
14
13
|
"!**/packages/cli/**", // never scan the scanner itself
|
|
14
|
+
];
|
|
15
|
+
// Cloud policy ignores: normalize to negated globs
|
|
16
|
+
const cloudIgnoreGlobs = extraIgnores.map((ig) => ig.startsWith("!") ? ig : `!${ig}`);
|
|
17
|
+
return fg([
|
|
18
|
+
"**/*.{ts,tsx,js,jsx,mjs,cjs}",
|
|
19
|
+
...baseIgnores,
|
|
20
|
+
...cloudIgnoreGlobs,
|
|
15
21
|
], { cwd: root, absolute: true, dot: false });
|
|
16
22
|
}
|