@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.
Files changed (3) hide show
  1. package/README.md +11 -9
  2. package/dist/cli.js +38 -5
  3. 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**, **insecure patterns (CWE)** and
4
- **vulnerable dependencies** — with no AI and no source upload.
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 + OSV lookups), so the
7
- same code always produces the same findings. Your source never leaves the machine it runs on.
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
- | Vulnerable dependencies | Lockfile parsing across ecosystems, resolved against the OSV database. |
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 does not upload source code,
83
- does not call a language model, and does not require an account. Dependency checks send
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
- log("usage: sphior-scan run <dir> [--sarif out.sarif] [--json out.json] [--fix-context out.json] [--fail-on high]");
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"]) log(sarifJson);
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
- log(
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.0",
4
- "description": "SPHIOR CODE deterministic scan engine — secrets + insecure-pattern (CWE) + SCA detection with tamper-evident evidence. One engine shared by CLI, LSP (VS Code / JetBrains) and CI.",
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": "./dist/cli.js"
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",