@sphior/scan 0.1.1 → 0.1.2
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/cli.js +38 -5
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -17013,7 +17013,7 @@ function collectSourceFiles(rootDir, opts = {}) {
|
|
|
17013
17013
|
}
|
|
17014
17014
|
|
|
17015
17015
|
// src/engine/cli.ts
|
|
17016
|
-
import { writeFileSync } from "node:fs";
|
|
17016
|
+
import { existsSync, statSync as statSync2, writeFileSync } from "node:fs";
|
|
17017
17017
|
|
|
17018
17018
|
// src/engine/mcp/fix-context.ts
|
|
17019
17019
|
var DATA_BOUNDARY_NOTE = "SPHIOR does not transmit your source code. This payload contains only finding metadata and deterministic remediation guidance. Read the actual code from your local workspace.";
|
|
@@ -17119,27 +17119,55 @@ function parseFlags(rest) {
|
|
|
17119
17119
|
function runCli(argv, ctx = {}) {
|
|
17120
17120
|
const log = ctx.log ?? (() => {
|
|
17121
17121
|
});
|
|
17122
|
+
const logErr = ctx.logErr ?? log;
|
|
17122
17123
|
const writeFile = ctx.writeFile ?? ((p, d) => writeFileSync(p, d));
|
|
17123
17124
|
const [cmd, dir, ...rest] = argv;
|
|
17124
17125
|
if (cmd !== "run" || !dir) {
|
|
17125
|
-
|
|
17126
|
+
logErr("usage: sphior-scan run <dir> [--sarif out.sarif] [--json out.json] [--fix-context out.json] [--fail-on high]");
|
|
17126
17127
|
return { exitCode: 2, error: "bad_args" };
|
|
17127
17128
|
}
|
|
17128
17129
|
const flags2 = parseFlags(rest);
|
|
17130
|
+
if (!existsSync(dir) || !statSync2(dir).isDirectory()) {
|
|
17131
|
+
logErr(`error: \u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${dir}`);
|
|
17132
|
+
logErr(" \u8D70\u67FB\u3057\u3066\u3044\u306A\u3044\u305F\u3081\u300C\u554F\u984C\u306A\u3057\u300D\u3068\u306F\u5831\u544A\u3057\u307E\u305B\u3093\u3002\u30D1\u30B9\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
|
|
17133
|
+
return { exitCode: 2, error: "dir_not_found" };
|
|
17134
|
+
}
|
|
17129
17135
|
const files = collectSourceFiles(dir);
|
|
17130
17136
|
const result = runScan({ files });
|
|
17131
17137
|
const sarifJson = canonicalJson(result.sarif);
|
|
17132
17138
|
if (flags2.sarif) writeFile(flags2.sarif, sarifJson);
|
|
17133
17139
|
if (flags2.json) writeFile(flags2.json, canonicalJson(result.findings));
|
|
17134
17140
|
if (flags2["fix-context"]) writeFile(flags2["fix-context"], canonicalJson(buildFixContexts(result.findings)));
|
|
17135
|
-
if (!flags2.sarif && !flags2.json && !flags2["fix-context"])
|
|
17141
|
+
if (!flags2.sarif && !flags2.json && !flags2["fix-context"]) {
|
|
17142
|
+
if (ctx.isTty) {
|
|
17143
|
+
const rows = [...result.findings].sort((a, b) => SEV_ORDER[b.severity] - SEV_ORDER[a.severity]).map((f) => ({
|
|
17144
|
+
sev: f.severity,
|
|
17145
|
+
loc: `${f.location.path}:${f.location.start_line}`,
|
|
17146
|
+
text: `${f.title}${f.cwe ? ` (${f.cwe})` : ""}`
|
|
17147
|
+
}));
|
|
17148
|
+
const locW = Math.max(0, ...rows.map((r) => r.loc.length));
|
|
17149
|
+
for (const r of rows) logErr(` ${r.sev.padEnd(8)} ${r.loc.padEnd(locW)} ${r.text}`);
|
|
17150
|
+
if (rows.length > 0) logErr("");
|
|
17151
|
+
} else {
|
|
17152
|
+
log(sarifJson);
|
|
17153
|
+
}
|
|
17154
|
+
}
|
|
17155
|
+
if (files.length === 0) {
|
|
17156
|
+
logErr(`warning: ${dir} \u306B\u8D70\u67FB\u5BFE\u8C61\u306E\u30D5\u30A1\u30A4\u30EB\u304C\u3042\u308A\u307E\u305B\u3093\u3067\u3057\u305F\uFF080 \u4EF6\u8D70\u67FB\uFF09\u3002`);
|
|
17157
|
+
}
|
|
17136
17158
|
const counts = result.findings.reduce((acc, f) => {
|
|
17137
17159
|
acc[f.severity] = (acc[f.severity] ?? 0) + 1;
|
|
17138
17160
|
return acc;
|
|
17139
17161
|
}, {});
|
|
17140
|
-
|
|
17162
|
+
logErr(
|
|
17141
17163
|
`SPHIOR CODE: ${result.findings.length} findings (critical=${counts.critical ?? 0} high=${counts.high ?? 0} medium=${counts.medium ?? 0} low=${counts.low ?? 0}) engine=${result.engine_version} ruleset=${result.ruleset_version}`
|
|
17142
17164
|
);
|
|
17165
|
+
if (ctx.isTty && !flags2.sarif && !flags2.json && !flags2["fix-context"]) {
|
|
17166
|
+
logErr("");
|
|
17167
|
+
logErr(" --fix-context fc.json AI \u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u306B\u6E21\u3057\u3066\u4FEE\u6B63\u3055\u305B\u308B");
|
|
17168
|
+
logErr(" --sarif out.sarif GitHub code scanning / CI \u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u3078");
|
|
17169
|
+
logErr(" --fail-on high \u6307\u5B9A\u4EE5\u4E0A\u306E\u91CD\u5927\u5EA6\u304C\u3042\u308C\u3070 exit 1\uFF08CI \u7528\uFF09");
|
|
17170
|
+
}
|
|
17143
17171
|
let exitCode = 0;
|
|
17144
17172
|
const failOn = flags2["fail-on"];
|
|
17145
17173
|
if (failOn && SEV_ORDER[failOn] !== void 0) {
|
|
@@ -23959,7 +23987,12 @@ async function main() {
|
|
|
23959
23987
|
})));
|
|
23960
23988
|
const res = runCli(process.argv.slice(2), {
|
|
23961
23989
|
cwd: process.cwd(),
|
|
23962
|
-
log: (m) => console.log(m)
|
|
23990
|
+
log: (m) => console.log(m),
|
|
23991
|
+
// データ(SARIF)
|
|
23992
|
+
logErr: (m) => console.error(m),
|
|
23993
|
+
// 診断(サマリ・警告・ヒント)
|
|
23994
|
+
// 端末なら人が読む形、パイプ/リダイレクトなら SARIF(`| jq` を壊さない)
|
|
23995
|
+
isTty: Boolean(process.stdout.isTTY)
|
|
23963
23996
|
});
|
|
23964
23997
|
process.exit(res.exitCode);
|
|
23965
23998
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sphior/scan",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Deterministic source scanner — hardcoded secrets and insecure patterns (CWE) with AST/taint analysis. No AI, no network, no source upload. CLI + LSP for editors and CI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|