@sphior/scan 0.1.0 → 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/README.md +11 -9
- package/dist/cli.js +38 -5
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# @sphior/scan
|
|
2
2
|
|
|
3
|
-
Deterministic security scanner for source code — **secrets
|
|
4
|
-
**
|
|
3
|
+
Deterministic security scanner for source code — **hardcoded secrets** and
|
|
4
|
+
**insecure patterns (CWE)** — with no AI, no network and no source upload.
|
|
5
5
|
|
|
6
|
-
Detection is 100% deterministic (regex + entropy + AST/taint analysis
|
|
7
|
-
|
|
6
|
+
Detection is 100% deterministic (regex + entropy + AST/taint analysis), so the same code always
|
|
7
|
+
produces the same findings. The scan makes no network requests at all: your source never leaves
|
|
8
|
+
the machine it runs on, and it works offline.
|
|
8
9
|
|
|
9
10
|
```bash
|
|
10
11
|
npx @sphior/scan run ./src
|
|
@@ -61,7 +62,10 @@ which is what you want in CI.
|
|
|
61
62
|
|---|---|
|
|
62
63
|
| Hardcoded secrets | Provider-specific patterns + entropy analysis. Well-known documentation placeholders are excluded, so example keys don't create noise. |
|
|
63
64
|
| Insecure patterns (CWE) | AST-based, including taint tracking from untrusted sources to dangerous sinks (SQL injection, XSS, command injection and similar). |
|
|
64
|
-
|
|
65
|
+
|
|
66
|
+
Dependency (SCA) scanning is **not** part of this CLI. The engine accepts SCA input, but the
|
|
67
|
+
`run` command only analyses source files. Dependency vulnerabilities are surfaced by SPHIOR CODE
|
|
68
|
+
on GitHub, which resolves lockfiles against the OSV database.
|
|
65
69
|
|
|
66
70
|
Findings carry a rule id, severity, CWE where applicable, and the exact file and line.
|
|
67
71
|
|
|
@@ -79,10 +83,8 @@ analysed locally — nothing is sent anywhere.
|
|
|
79
83
|
|
|
80
84
|
## Privacy
|
|
81
85
|
|
|
82
|
-
The scanner reads your files and reports what it found. It
|
|
83
|
-
|
|
84
|
-
package names and versions (never source) to the OSV database to look up known
|
|
85
|
-
vulnerabilities.
|
|
86
|
+
The scanner reads your files and reports what it found. It makes no network requests: no source
|
|
87
|
+
upload, no telemetry, no language model, no account. It runs correctly with networking disabled.
|
|
86
88
|
|
|
87
89
|
## Links
|
|
88
90
|
|
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,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sphior/scan",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.2",
|
|
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": {
|
|
7
|
-
"sphior-scan": "
|
|
7
|
+
"sphior-scan": "dist/cli.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"dist",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
],
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
|
15
|
-
"url": "https://github.com/Toraastrta/Sphior.git",
|
|
15
|
+
"url": "git+https://github.com/Toraastrta/Sphior.git",
|
|
16
16
|
"directory": "ige-web/packages/sphior-scan"
|
|
17
17
|
},
|
|
18
18
|
"homepage": "https://sphior.com/code",
|