secureflow-cli 1.1.0 → 2.0.0
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/git.d.ts +2 -2
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +10 -10
- package/dist/git.js.map +1 -1
- package/dist/index.js +117 -41
- package/dist/index.js.map +1 -1
- package/dist/lib/api-client.d.ts +31 -0
- package/dist/lib/api-client.d.ts.map +1 -0
- package/dist/lib/api-client.js +45 -0
- package/dist/lib/api-client.js.map +1 -0
- package/dist/sarif.d.ts +84 -0
- package/dist/sarif.d.ts.map +1 -0
- package/dist/sarif.js +135 -0
- package/dist/sarif.js.map +1 -0
- package/dist/scanner.d.ts +5 -0
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scanner.js +107 -40
- package/dist/scanner.js.map +1 -1
- package/package.json +7 -4
package/dist/git.d.ts
CHANGED
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
*/
|
|
22
22
|
/** A problem with the repository or the git invocation, not with the content. */
|
|
23
23
|
export declare class GitError extends Error {
|
|
24
|
-
readonly cause?: unknown;
|
|
25
|
-
constructor(message: string, cause?: unknown);
|
|
24
|
+
readonly cause?: unknown | undefined;
|
|
25
|
+
constructor(message: string, cause?: unknown | undefined);
|
|
26
26
|
}
|
|
27
27
|
/** Whether the current directory is inside a work tree. */
|
|
28
28
|
export declare function isGitRepository(): boolean;
|
package/dist/git.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAaH,iFAAiF;AACjF,qBAAa,QAAS,SAAQ,KAAK;
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAaH,iFAAiF;AACjF,qBAAa,QAAS,SAAQ,KAAK;IAG/B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO;gBADxB,OAAO,EAAE,MAAM,EACN,KAAK,CAAC,EAAE,OAAO,YAAA;CAK3B;AASD,2DAA2D;AAC3D,wBAAgB,eAAe,IAAI,OAAO,CAMzC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,IAAI,MAAM,EAAE,CAgBzC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM7D"}
|
package/dist/git.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* The inverse is just as bad: unstaged debug code blocks a commit that does not
|
|
20
20
|
* contain it, which teaches people to reach for `--no-verify`.
|
|
21
21
|
*/
|
|
22
|
-
import { execFileSync } from
|
|
22
|
+
import { execFileSync } from "child_process";
|
|
23
23
|
/**
|
|
24
24
|
* Buffer ceiling for git output.
|
|
25
25
|
*
|
|
@@ -34,19 +34,19 @@ export class GitError extends Error {
|
|
|
34
34
|
constructor(message, cause) {
|
|
35
35
|
super(message);
|
|
36
36
|
this.cause = cause;
|
|
37
|
-
this.name =
|
|
37
|
+
this.name = "GitError";
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
function git(args) {
|
|
41
|
-
return execFileSync(
|
|
42
|
-
encoding:
|
|
41
|
+
return execFileSync("git", args, {
|
|
42
|
+
encoding: "utf-8",
|
|
43
43
|
maxBuffer: MAX_GIT_BUFFER,
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
/** Whether the current directory is inside a work tree. */
|
|
47
47
|
export function isGitRepository() {
|
|
48
48
|
try {
|
|
49
|
-
return git([
|
|
49
|
+
return git(["rev-parse", "--is-inside-work-tree"]).trim() === "true";
|
|
50
50
|
}
|
|
51
51
|
catch {
|
|
52
52
|
return false;
|
|
@@ -68,15 +68,15 @@ export function isGitRepository() {
|
|
|
68
68
|
export function getStagedFiles() {
|
|
69
69
|
let output;
|
|
70
70
|
try {
|
|
71
|
-
output = git([
|
|
71
|
+
output = git(["diff", "--cached", "--name-only", "-z", "--diff-filter=ACMR"]);
|
|
72
72
|
}
|
|
73
73
|
catch (error) {
|
|
74
74
|
throw new GitError(isGitRepository()
|
|
75
|
-
?
|
|
76
|
-
:
|
|
75
|
+
? "Could not list staged files. `git diff --cached` failed."
|
|
76
|
+
: "Not a git repository (or git is not on PATH).", error);
|
|
77
77
|
}
|
|
78
78
|
// NUL-separated, with a trailing separator on a non-empty list.
|
|
79
|
-
return output.split(
|
|
79
|
+
return output.split("\u0000").filter((entry) => entry.length > 0);
|
|
80
80
|
}
|
|
81
81
|
/**
|
|
82
82
|
* The staged content of `path` — the bytes that will actually be committed.
|
|
@@ -87,7 +87,7 @@ export function getStagedFiles() {
|
|
|
87
87
|
*/
|
|
88
88
|
export function readStagedContent(path) {
|
|
89
89
|
try {
|
|
90
|
-
return git([
|
|
90
|
+
return git(["show", `:${path}`]);
|
|
91
91
|
}
|
|
92
92
|
catch {
|
|
93
93
|
return null;
|
package/dist/git.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C;;;;;;GAMG;AACH,MAAM,cAAc,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAExC,iFAAiF;AACjF,MAAM,OAAO,QAAS,SAAQ,KAAK;
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C;;;;;;GAMG;AACH,MAAM,cAAc,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAExC,iFAAiF;AACjF,MAAM,OAAO,QAAS,SAAQ,KAAK;IAGtB;IAFX,YACE,OAAe,EACN,KAAe;QAExB,KAAK,CAAC,OAAO,CAAC,CAAC;QAFN,UAAK,GAAL,KAAK,CAAU;QAGxB,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAED,SAAS,GAAG,CAAC,IAAc;IACzB,OAAO,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE;QAC/B,QAAQ,EAAE,OAAO;QACjB,SAAS,EAAE,cAAc;KAC1B,CAAC,CAAC;AACL,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC;QACH,OAAO,GAAG,CAAC,CAAC,WAAW,EAAE,uBAAuB,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,MAAM,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,cAAc;IAC5B,IAAI,MAAc,CAAC;IAEnB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,IAAI,EAAE,oBAAoB,CAAC,CAAC,CAAC;IAChF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,QAAQ,CAChB,eAAe,EAAE;YACf,CAAC,CAAC,0DAA0D;YAC5D,CAAC,CAAC,+CAA+C,EACnD,KAAK,CACN,CAAC;IACJ,CAAC;IAED,gEAAgE;IAChE,OAAO,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACpE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,IAAI,CAAC;QACH,OAAO,GAAG,CAAC,CAAC,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,69 +1,145 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import { GitError, getStagedFiles, readStagedContent } from "./git.js";
|
|
4
|
+
import { scanFile, formatScanResults } from "./scanner.js";
|
|
5
|
+
import { NetworkUnavailableError, requestAiFileScan } from "./lib/api-client.js";
|
|
6
|
+
const VERBOSE = process.argv.includes("--verbose");
|
|
7
|
+
const NO_AI = process.argv.includes("--no-ai");
|
|
8
|
+
function parseFormatArg() {
|
|
9
|
+
const formatIndex = process.argv.findIndex((arg) => arg === "--format");
|
|
10
|
+
if (formatIndex !== -1) {
|
|
11
|
+
const valStr = process.argv[formatIndex + 1];
|
|
12
|
+
if (valStr) {
|
|
13
|
+
const val = valStr.toLowerCase();
|
|
14
|
+
if (val === "sarif" || val === "json" || val === "text") {
|
|
15
|
+
return val;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return "text";
|
|
20
|
+
}
|
|
21
|
+
function parseOutputArg() {
|
|
22
|
+
const outIndex = process.argv.findIndex((arg) => arg === "-o" || arg === "--output");
|
|
23
|
+
if (outIndex !== -1) {
|
|
24
|
+
const valStr = process.argv[outIndex + 1];
|
|
25
|
+
if (valStr) {
|
|
26
|
+
return valStr;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
12
31
|
function reportSkipped(result) {
|
|
13
32
|
if (VERBOSE && result.skipped) {
|
|
14
|
-
console.log(`
|
|
33
|
+
console.log(` ↷ skipped ${result.path} (${result.skipped})`);
|
|
15
34
|
}
|
|
16
35
|
}
|
|
17
36
|
function reportViolations(result) {
|
|
18
37
|
for (const violation of result.violations) {
|
|
19
38
|
console.error(`🚨 [SecureFlow] Secret logging detected in ${result.path}:${violation.line}`);
|
|
20
|
-
console.error(`
|
|
21
|
-
console.error(`
|
|
39
|
+
console.error(` -> ${violation.text}`);
|
|
40
|
+
console.error(` why: ${violation.reason} passed to a console call`);
|
|
22
41
|
}
|
|
23
42
|
}
|
|
24
|
-
function
|
|
43
|
+
function reportAiFinding(finding) {
|
|
44
|
+
console.error(`🤖 [SecureFlow AI] ${finding.severity} ${finding.type} in ${finding.fileLocation}${finding.lineStart ? `:${finding.lineStart}` : ""}`);
|
|
45
|
+
console.error(` -> ${finding.description}`);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Best-effort AI-powered scan on top of the always-on local scan above.
|
|
49
|
+
* Never throws and never delays the commit beyond its own short internal
|
|
50
|
+
* timeout -- if the network is down, this is a silent (or
|
|
51
|
+
* --verbose-logged) no-op and the local scan result stands on its own,
|
|
52
|
+
* unchanged.
|
|
53
|
+
*/
|
|
54
|
+
async function runAiScanIfAvailable(stagedForAi) {
|
|
55
|
+
if (NO_AI || stagedForAi.length === 0)
|
|
56
|
+
return [];
|
|
57
|
+
try {
|
|
58
|
+
return await requestAiFileScan(stagedForAi);
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
if (err instanceof NetworkUnavailableError) {
|
|
62
|
+
if (VERBOSE) {
|
|
63
|
+
console.warn("⚠️ [SecureFlow] AI scan unreachable -- continuing with local scan only.");
|
|
64
|
+
}
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
throw err;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function main() {
|
|
71
|
+
const format = parseFormatArg();
|
|
72
|
+
const outputPath = parseOutputArg();
|
|
25
73
|
let staged;
|
|
26
74
|
try {
|
|
27
75
|
staged = getStagedFiles();
|
|
28
76
|
}
|
|
29
77
|
catch (error) {
|
|
30
|
-
// Distinguished rather than collapsed into "Are you in a git repository?",
|
|
31
|
-
// which was previously printed for a buffer overflow, a missing git binary
|
|
32
|
-
// and a genuine non-repository alike.
|
|
33
78
|
console.error(`❌ [SecureFlow] ${error instanceof GitError ? error.message : String(error)}`);
|
|
34
79
|
return 1;
|
|
35
80
|
}
|
|
36
|
-
|
|
37
|
-
console.log('✅ SecureFlow scan passed (nothing staged).');
|
|
38
|
-
return 0;
|
|
39
|
-
}
|
|
81
|
+
const fileResults = [];
|
|
40
82
|
const unreadable = [];
|
|
83
|
+
const stagedForAi = [];
|
|
41
84
|
let violationCount = 0;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
85
|
+
if (staged.length > 0) {
|
|
86
|
+
for (const path of staged) {
|
|
87
|
+
const content = readStagedContent(path);
|
|
88
|
+
if (content === null) {
|
|
89
|
+
unreadable.push(path);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
stagedForAi.push({ path, content });
|
|
93
|
+
const result = scanFile(path, content);
|
|
94
|
+
fileResults.push(result);
|
|
95
|
+
if (format === "text") {
|
|
96
|
+
reportSkipped(result);
|
|
97
|
+
reportViolations(result);
|
|
98
|
+
}
|
|
99
|
+
violationCount += result.violations.length;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// AI-powered pass, additive on top of the local scan above. Only
|
|
103
|
+
// affects the text output/exit code today -- JSON/SARIF export stays
|
|
104
|
+
// local-scan-only for now so existing automated consumers of those
|
|
105
|
+
// formats aren't changed by this PR.
|
|
106
|
+
const aiFindings = await runAiScanIfAvailable(stagedForAi);
|
|
107
|
+
if (format === "text") {
|
|
108
|
+
for (const finding of aiFindings) {
|
|
109
|
+
reportAiFinding(finding);
|
|
50
110
|
}
|
|
51
|
-
const result = scanFile(path, content);
|
|
52
|
-
reportSkipped(result);
|
|
53
|
-
reportViolations(result);
|
|
54
|
-
violationCount += result.violations.length;
|
|
55
111
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
112
|
+
const aiViolationCount = aiFindings.filter((f) => f.severity === "HIGH" || f.severity === "CRITICAL").length;
|
|
113
|
+
if (format === "sarif" || format === "json") {
|
|
114
|
+
const outputString = formatScanResults(fileResults, format);
|
|
115
|
+
if (outputPath) {
|
|
116
|
+
fs.writeFileSync(outputPath, outputString, "utf-8");
|
|
117
|
+
console.log(`📄 [SecureFlow] Scan report exported in ${format.toUpperCase()} format to ${outputPath}`);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
console.log(outputString);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else if (outputPath) {
|
|
124
|
+
const textOutput = formatScanResults(fileResults, "text");
|
|
125
|
+
fs.writeFileSync(outputPath, textOutput, "utf-8");
|
|
126
|
+
console.log(`📄 [SecureFlow] Scan report written to ${outputPath}`);
|
|
60
127
|
}
|
|
61
|
-
if (
|
|
62
|
-
console.
|
|
128
|
+
if (unreadable.length > 0 && format === "text") {
|
|
129
|
+
console.warn(`⚠️ [SecureFlow] Could not read ${unreadable.length} staged entr${unreadable.length === 1 ? "y" : "ies"} (submodule, symlink or conflicted): ${unreadable.join(", ")}`);
|
|
130
|
+
}
|
|
131
|
+
if (violationCount > 0 || aiViolationCount > 0) {
|
|
132
|
+
if (format === "text") {
|
|
133
|
+
console.error(`\n❌ SecureFlow blocked this commit: ${violationCount} secret-logging violation${violationCount === 1 ? "" : "s"}${aiViolationCount > 0
|
|
134
|
+
? ` and ${aiViolationCount} AI-detected HIGH/CRITICAL finding${aiViolationCount === 1 ? "" : "s"}`
|
|
135
|
+
: ""}. Remove the exposed secrets/env variables, then re-stage.`);
|
|
136
|
+
}
|
|
63
137
|
return 1;
|
|
64
138
|
}
|
|
65
|
-
|
|
139
|
+
if (format === "text") {
|
|
140
|
+
console.log(`✅ SecureFlow scan passed (${staged.length} staged file(s)).`);
|
|
141
|
+
}
|
|
66
142
|
return 0;
|
|
67
143
|
}
|
|
68
|
-
process.exit(
|
|
144
|
+
main().then((code) => process.exit(code));
|
|
69
145
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin client for the AI-powered scanning endpoint the SecureFlow GitHub
|
|
3
|
+
* App uses (src/lib/armor/scanner.ts, exposed at POST /api/cli/scan).
|
|
4
|
+
* Gives the CLI parity with the App's AI review, additive on top of the
|
|
5
|
+
* always-available local scan in `scanner.ts`.
|
|
6
|
+
*/
|
|
7
|
+
export interface StagedFileForAiScan {
|
|
8
|
+
path: string;
|
|
9
|
+
content: string;
|
|
10
|
+
}
|
|
11
|
+
/** Mirrors `ScanFinding` from src/lib/armor/scanner.ts. */
|
|
12
|
+
export interface AiFinding {
|
|
13
|
+
type: string;
|
|
14
|
+
severity: "LOW" | "MEDIUM" | "HIGH" | "CRITICAL";
|
|
15
|
+
description: string;
|
|
16
|
+
fileLocation: string;
|
|
17
|
+
codeSnippet: string;
|
|
18
|
+
lineStart?: number;
|
|
19
|
+
lineEnd?: number;
|
|
20
|
+
}
|
|
21
|
+
export declare class NetworkUnavailableError extends Error {
|
|
22
|
+
constructor(cause?: unknown);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Sends staged file contents to the hosted AI scanner. Fails fast (short
|
|
26
|
+
* timeout) and always throws `NetworkUnavailableError` on any failure so
|
|
27
|
+
* callers can swallow it and fall back to the local-only scan -- never
|
|
28
|
+
* block a commit on connectivity.
|
|
29
|
+
*/
|
|
30
|
+
export declare function requestAiFileScan(files: StagedFileForAiScan[], baseUrl?: string): Promise<AiFinding[]>;
|
|
31
|
+
//# sourceMappingURL=api-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/lib/api-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,2DAA2D;AAC3D,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,KAAK,CAAC,EAAE,OAAO;CAK5B;AAKD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,mBAAmB,EAAE,EAC5B,OAAO,GAAE,MAAyB,GACjC,OAAO,CAAC,SAAS,EAAE,CAAC,CAuBtB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin client for the AI-powered scanning endpoint the SecureFlow GitHub
|
|
3
|
+
* App uses (src/lib/armor/scanner.ts, exposed at POST /api/cli/scan).
|
|
4
|
+
* Gives the CLI parity with the App's AI review, additive on top of the
|
|
5
|
+
* always-available local scan in `scanner.ts`.
|
|
6
|
+
*/
|
|
7
|
+
export class NetworkUnavailableError extends Error {
|
|
8
|
+
constructor(cause) {
|
|
9
|
+
super("SecureFlow API unreachable");
|
|
10
|
+
this.name = "NetworkUnavailableError";
|
|
11
|
+
this.cause = cause;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const DEFAULT_TIMEOUT_MS = 5_000;
|
|
15
|
+
const DEFAULT_BASE_URL = "https://secure-flow-six.vercel.app";
|
|
16
|
+
/**
|
|
17
|
+
* Sends staged file contents to the hosted AI scanner. Fails fast (short
|
|
18
|
+
* timeout) and always throws `NetworkUnavailableError` on any failure so
|
|
19
|
+
* callers can swallow it and fall back to the local-only scan -- never
|
|
20
|
+
* block a commit on connectivity.
|
|
21
|
+
*/
|
|
22
|
+
export async function requestAiFileScan(files, baseUrl = DEFAULT_BASE_URL) {
|
|
23
|
+
const controller = new AbortController();
|
|
24
|
+
const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);
|
|
25
|
+
try {
|
|
26
|
+
const res = await fetch(`${baseUrl}/api/cli/scan`, {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: { "Content-Type": "application/json" },
|
|
29
|
+
body: JSON.stringify({ files }),
|
|
30
|
+
signal: controller.signal,
|
|
31
|
+
});
|
|
32
|
+
if (!res.ok) {
|
|
33
|
+
throw new Error(`SecureFlow API responded with ${res.status}`);
|
|
34
|
+
}
|
|
35
|
+
const data = (await res.json());
|
|
36
|
+
return data.findings ?? [];
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
throw new NetworkUnavailableError(err);
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
clearTimeout(timeout);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=api-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../src/lib/api-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAkBH,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAChD,YAAY,KAAe;QACzB,KAAK,CAAC,4BAA4B,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;QACtC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF;AAED,MAAM,kBAAkB,GAAG,KAAK,CAAC;AACjC,MAAM,gBAAgB,GAAG,oCAAoC,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,KAA4B,EAC5B,UAAkB,gBAAgB;IAElC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,kBAAkB,CAAC,CAAC;IAEzE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,eAAe,EAAE;YACjD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC;YAC/B,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,iCAAiC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACjE,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA8B,CAAC;QAC7D,OAAO,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;IAC7B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,uBAAuB,CAAC,GAAG,CAAC,CAAC;IACzC,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;AACH,CAAC"}
|
package/dist/sarif.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SARIF (Static Analysis Results Interchange Format) Exporter for SecureFlow CLI (#728)
|
|
3
|
+
*
|
|
4
|
+
* Converts CLI scan results into standardized OASIS SARIF v2.1.0 schema format
|
|
5
|
+
* for direct ingestion by GitHub Advanced Security, GitLab Security Dashboards, and enterprise CI/CD systems.
|
|
6
|
+
*/
|
|
7
|
+
import type { FileScanResult } from "./scanner.js";
|
|
8
|
+
export interface SarifArtifactLocation {
|
|
9
|
+
uri: string;
|
|
10
|
+
uriBaseId?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface SarifRegion {
|
|
13
|
+
startLine: number;
|
|
14
|
+
startColumn?: number;
|
|
15
|
+
endLine?: number;
|
|
16
|
+
endColumn?: number;
|
|
17
|
+
snippet?: {
|
|
18
|
+
text: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export interface SarifPhysicalLocation {
|
|
22
|
+
artifactLocation: SarifArtifactLocation;
|
|
23
|
+
region: SarifRegion;
|
|
24
|
+
}
|
|
25
|
+
export interface SarifLocation {
|
|
26
|
+
physicalLocation: SarifPhysicalLocation;
|
|
27
|
+
}
|
|
28
|
+
export interface SarifReportingDescriptor {
|
|
29
|
+
id: string;
|
|
30
|
+
name: string;
|
|
31
|
+
shortDescription: {
|
|
32
|
+
text: string;
|
|
33
|
+
};
|
|
34
|
+
fullDescription?: {
|
|
35
|
+
text: string;
|
|
36
|
+
};
|
|
37
|
+
defaultConfiguration?: {
|
|
38
|
+
level: "error" | "warning" | "note" | "none";
|
|
39
|
+
};
|
|
40
|
+
helpUri?: string;
|
|
41
|
+
properties?: Record<string, unknown>;
|
|
42
|
+
}
|
|
43
|
+
export interface SarifResult {
|
|
44
|
+
ruleId: string;
|
|
45
|
+
ruleIndex?: number;
|
|
46
|
+
level: "error" | "warning" | "note" | "none";
|
|
47
|
+
message: {
|
|
48
|
+
text: string;
|
|
49
|
+
};
|
|
50
|
+
locations: SarifLocation[];
|
|
51
|
+
properties?: Record<string, unknown>;
|
|
52
|
+
}
|
|
53
|
+
export interface SarifRun {
|
|
54
|
+
tool: {
|
|
55
|
+
driver: {
|
|
56
|
+
name: string;
|
|
57
|
+
organization?: string;
|
|
58
|
+
version?: string;
|
|
59
|
+
semanticVersion?: string;
|
|
60
|
+
informationUri?: string;
|
|
61
|
+
rules: SarifReportingDescriptor[];
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
results: SarifResult[];
|
|
65
|
+
}
|
|
66
|
+
export interface SarifDocument {
|
|
67
|
+
$schema: string;
|
|
68
|
+
version: "2.1.0";
|
|
69
|
+
runs: SarifRun[];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Maps CLI scan results into a valid OASIS SARIF v2.1.0 document object.
|
|
73
|
+
*/
|
|
74
|
+
export declare function generateSarifReport(scanResults: FileScanResult[], options?: {
|
|
75
|
+
toolVersion?: string;
|
|
76
|
+
repoUri?: string;
|
|
77
|
+
}): SarifDocument;
|
|
78
|
+
/**
|
|
79
|
+
* Format SARIF report as pretty JSON string.
|
|
80
|
+
*/
|
|
81
|
+
export declare function formatSarifJson(scanResults: FileScanResult[], options?: {
|
|
82
|
+
toolVersion?: string;
|
|
83
|
+
}): string;
|
|
84
|
+
//# sourceMappingURL=sarif.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarif.d.ts","sourceRoot":"","sources":["../src/sarif.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED,MAAM,WAAW,qBAAqB;IACpC,gBAAgB,EAAE,qBAAqB,CAAC;IACxC,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,gBAAgB,EAAE,qBAAqB,CAAC;CACzC;AAED,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE;QAChB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,eAAe,CAAC,EAAE;QAChB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,oBAAoB,CAAC,EAAE;QACrB,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;KAC9C,CAAC;IACF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;IAC7C,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE;QACJ,MAAM,EAAE;YACN,IAAI,EAAE,MAAM,CAAC;YACb,YAAY,CAAC,EAAE,MAAM,CAAC;YACtB,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,eAAe,CAAC,EAAE,MAAM,CAAC;YACzB,cAAc,CAAC,EAAE,MAAM,CAAC;YACxB,KAAK,EAAE,wBAAwB,EAAE,CAAC;SACnC,CAAC;KACH,CAAC;IACF,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,QAAQ,EAAE,CAAC;CAClB;AA4CD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,cAAc,EAAE,EAC7B,OAAO,CAAC,EAAE;IACR,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GACA,aAAa,CAmFf;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,cAAc,EAAE,EAC7B,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACjC,MAAM,CAGR"}
|
package/dist/sarif.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SARIF (Static Analysis Results Interchange Format) Exporter for SecureFlow CLI (#728)
|
|
3
|
+
*
|
|
4
|
+
* Converts CLI scan results into standardized OASIS SARIF v2.1.0 schema format
|
|
5
|
+
* for direct ingestion by GitHub Advanced Security, GitLab Security Dashboards, and enterprise CI/CD systems.
|
|
6
|
+
*/
|
|
7
|
+
const DEFAULT_RULE_DEFINITIONS = {
|
|
8
|
+
"environment variable": {
|
|
9
|
+
id: "SECUREFLOW-001",
|
|
10
|
+
name: "ConsoleSecretLoggingEnvironmentVariable",
|
|
11
|
+
shortDescription: {
|
|
12
|
+
text: "Console logging of environment variable containing potential secret",
|
|
13
|
+
},
|
|
14
|
+
fullDescription: {
|
|
15
|
+
text: "Passing process.env or other environment variable getters into console methods exposes credentials in build logs or standard output.",
|
|
16
|
+
},
|
|
17
|
+
defaultConfiguration: {
|
|
18
|
+
level: "error",
|
|
19
|
+
},
|
|
20
|
+
helpUri: "https://github.com/GauravKarakoti/SecureFlow#rules",
|
|
21
|
+
},
|
|
22
|
+
"secret-named identifier": {
|
|
23
|
+
id: "SECUREFLOW-002",
|
|
24
|
+
name: "ConsoleSecretLoggingSecretIdentifier",
|
|
25
|
+
shortDescription: {
|
|
26
|
+
text: "Console logging of secret-named identifier or credential parameter",
|
|
27
|
+
},
|
|
28
|
+
fullDescription: {
|
|
29
|
+
text: "Identifiers containing secret, password, apikey, token, or auth parameters passed directly into console logging statements.",
|
|
30
|
+
},
|
|
31
|
+
defaultConfiguration: {
|
|
32
|
+
level: "error",
|
|
33
|
+
},
|
|
34
|
+
helpUri: "https://github.com/GauravKarakoti/SecureFlow#rules",
|
|
35
|
+
},
|
|
36
|
+
"generic-secret-logging": {
|
|
37
|
+
id: "SECUREFLOW-000",
|
|
38
|
+
name: "ConsoleSecretLoggingGeneric",
|
|
39
|
+
shortDescription: {
|
|
40
|
+
text: "Potential secret credential logging in console output",
|
|
41
|
+
},
|
|
42
|
+
defaultConfiguration: {
|
|
43
|
+
level: "error",
|
|
44
|
+
},
|
|
45
|
+
helpUri: "https://github.com/GauravKarakoti/SecureFlow#rules",
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Maps CLI scan results into a valid OASIS SARIF v2.1.0 document object.
|
|
50
|
+
*/
|
|
51
|
+
export function generateSarifReport(scanResults, options) {
|
|
52
|
+
const version = options?.toolVersion || "0.1.0";
|
|
53
|
+
const rulesMap = new Map();
|
|
54
|
+
const sarifResults = [];
|
|
55
|
+
for (const fileResult of scanResults) {
|
|
56
|
+
if (!fileResult.violations || fileResult.violations.length === 0) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
for (const violation of fileResult.violations) {
|
|
60
|
+
const reasonKey = violation.reason || "generic-secret-logging";
|
|
61
|
+
let ruleInfo = rulesMap.get(reasonKey);
|
|
62
|
+
if (!ruleInfo) {
|
|
63
|
+
const descriptor = DEFAULT_RULE_DEFINITIONS[reasonKey] || {
|
|
64
|
+
id: `SECUREFLOW-${rulesMap.size + 100}`,
|
|
65
|
+
name: `ConsoleSecretLoggingCustomRule${rulesMap.size + 1}`,
|
|
66
|
+
shortDescription: {
|
|
67
|
+
text: `Console logging of ${violation.reason}`,
|
|
68
|
+
},
|
|
69
|
+
defaultConfiguration: {
|
|
70
|
+
level: "error",
|
|
71
|
+
},
|
|
72
|
+
helpUri: "https://github.com/GauravKarakoti/SecureFlow#rules",
|
|
73
|
+
};
|
|
74
|
+
ruleInfo = { descriptor, index: rulesMap.size };
|
|
75
|
+
rulesMap.set(reasonKey, ruleInfo);
|
|
76
|
+
}
|
|
77
|
+
sarifResults.push({
|
|
78
|
+
ruleId: ruleInfo.descriptor.id,
|
|
79
|
+
ruleIndex: ruleInfo.index,
|
|
80
|
+
level: ruleInfo.descriptor.defaultConfiguration?.level || "error",
|
|
81
|
+
message: {
|
|
82
|
+
text: `[SecureFlow] ${violation.reason} passed to console call: "${violation.text}"`,
|
|
83
|
+
},
|
|
84
|
+
locations: [
|
|
85
|
+
{
|
|
86
|
+
physicalLocation: {
|
|
87
|
+
artifactLocation: {
|
|
88
|
+
uri: fileResult.path.replace(/\\/g, "/"),
|
|
89
|
+
},
|
|
90
|
+
region: {
|
|
91
|
+
startLine: Math.max(1, violation.line),
|
|
92
|
+
snippet: {
|
|
93
|
+
text: violation.text,
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
properties: {
|
|
100
|
+
reason: violation.reason,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const rulesArray = Array.from(rulesMap.values()).map((r) => r.descriptor);
|
|
106
|
+
if (rulesArray.length === 0) {
|
|
107
|
+
// Appended `!` to override 'undefined' error resulting from Record<string, ...> signature
|
|
108
|
+
rulesArray.push(DEFAULT_RULE_DEFINITIONS["generic-secret-logging"]);
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
$schema: "https://json.schemastore.org/sarif-2.1.0.json",
|
|
112
|
+
version: "2.1.0",
|
|
113
|
+
runs: [
|
|
114
|
+
{
|
|
115
|
+
tool: {
|
|
116
|
+
driver: {
|
|
117
|
+
name: "SecureFlow CLI",
|
|
118
|
+
semanticVersion: version,
|
|
119
|
+
informationUri: "https://github.com/GauravKarakoti/SecureFlow",
|
|
120
|
+
rules: rulesArray,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
results: sarifResults,
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Format SARIF report as pretty JSON string.
|
|
130
|
+
*/
|
|
131
|
+
export function formatSarifJson(scanResults, options) {
|
|
132
|
+
const sarifDoc = generateSarifReport(scanResults, options);
|
|
133
|
+
return JSON.stringify(sarifDoc, null, 2);
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=sarif.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarif.js","sourceRoot":"","sources":["../src/sarif.ts"],"names":[],"mappings":"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"}
|
package/dist/scanner.d.ts
CHANGED
|
@@ -85,4 +85,9 @@ export interface FileScanResult {
|
|
|
85
85
|
}
|
|
86
86
|
/** Scan one staged blob. */
|
|
87
87
|
export declare function scanFile(path: string, content: string): FileScanResult;
|
|
88
|
+
export type OutputFormat = "text" | "json" | "sarif";
|
|
89
|
+
/**
|
|
90
|
+
* Format scan results based on the chosen output format ('text' | 'json' | 'sarif').
|
|
91
|
+
*/
|
|
92
|
+
export declare function formatScanResults(results: FileScanResult[], format?: OutputFormat): string;
|
|
88
93
|
//# sourceMappingURL=scanner.d.ts.map
|
package/dist/scanner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,6BAA6B;AAC7B,MAAM,WAAW,SAAS;IACxB,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;CAChB;AAwFD;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,QAAa,CAAC;AAE5C,+CAA+C;AAC/C,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CASzE;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEpD;AAKD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA4EzD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYjF;AAED,uCAAuC;AACvC,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAM7D;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,CA2B7D;AAED,0CAA0C;AAC1C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,4BAA4B;AAC5B,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CAUtE;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAErD;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,cAAc,EAAE,EACzB,MAAM,GAAE,YAAqB,GAC5B,MAAM,CAmBR"}
|
package/dist/scanner.js
CHANGED
|
@@ -6,13 +6,14 @@
|
|
|
6
6
|
* the part that decides whether a commit is blocked can actually be tested
|
|
7
7
|
* (#593).
|
|
8
8
|
*/
|
|
9
|
+
import { formatSarifJson } from "./sarif.js";
|
|
9
10
|
/** Console methods that put their arguments somewhere durable. */
|
|
10
|
-
const CONSOLE_METHODS = [
|
|
11
|
+
const CONSOLE_METHODS = ["log", "info", "warn", "error", "debug", "trace", "table", "dir"];
|
|
11
12
|
/**
|
|
12
13
|
* Start of a console call. Whitespace is permitted around the dot and the
|
|
13
14
|
* parenthesis because a formatter will put it there.
|
|
14
15
|
*/
|
|
15
|
-
const CONSOLE_CALL = new RegExp(`console\\s*\\.\\s*(?:${CONSOLE_METHODS.join(
|
|
16
|
+
const CONSOLE_CALL = new RegExp(`console\\s*\\.\\s*(?:${CONSOLE_METHODS.join("|")})\\s*\\(`, "g");
|
|
16
17
|
/**
|
|
17
18
|
* What makes an argument list suspicious.
|
|
18
19
|
*
|
|
@@ -21,26 +22,71 @@ const CONSOLE_CALL = new RegExp(`console\\s*\\.\\s*(?:${CONSOLE_METHODS.join('|'
|
|
|
21
22
|
* expression does.
|
|
22
23
|
*/
|
|
23
24
|
const INDICATORS = [
|
|
24
|
-
[
|
|
25
|
-
|
|
25
|
+
[
|
|
26
|
+
"environment variable",
|
|
27
|
+
/\b(?:process\s*\.\s*env|import\s*\.\s*meta\s*\.\s*env|Deno\s*\.\s*env|os\s*\.\s*environ)\b/,
|
|
28
|
+
],
|
|
29
|
+
[
|
|
30
|
+
"secret-named identifier",
|
|
31
|
+
/\b\w*(?:password|passwd|secret|token|credential|apikey|privatekey)\w*\b/i,
|
|
32
|
+
],
|
|
26
33
|
// `key` and `auth` on their own are common enough in ordinary code
|
|
27
34
|
// (`keyof`, `authorised`, `keys`) that they are only flagged when they read
|
|
28
35
|
// as a whole word or as an obvious compound.
|
|
29
|
-
[
|
|
36
|
+
[
|
|
37
|
+
"secret-named identifier",
|
|
38
|
+
/\b(?:api[_-]?key|access[_-]?key|secret[_-]?key|auth[_-]?token|authorization)\b/i,
|
|
39
|
+
],
|
|
30
40
|
];
|
|
31
41
|
/** Extensions never worth scanning as source text. */
|
|
32
42
|
const BINARY_EXTENSIONS = [
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
".png",
|
|
44
|
+
".jpg",
|
|
45
|
+
".jpeg",
|
|
46
|
+
".gif",
|
|
47
|
+
".ico",
|
|
48
|
+
".webp",
|
|
49
|
+
".avif",
|
|
50
|
+
".bmp",
|
|
51
|
+
".tiff",
|
|
52
|
+
".pdf",
|
|
53
|
+
".zip",
|
|
54
|
+
".gz",
|
|
55
|
+
".tar",
|
|
56
|
+
".bz2",
|
|
57
|
+
".7z",
|
|
58
|
+
".rar",
|
|
59
|
+
".woff",
|
|
60
|
+
".woff2",
|
|
61
|
+
".ttf",
|
|
62
|
+
".otf",
|
|
63
|
+
".eot",
|
|
64
|
+
".mp3",
|
|
65
|
+
".mp4",
|
|
66
|
+
".wav",
|
|
67
|
+
".mov",
|
|
68
|
+
".avi",
|
|
69
|
+
".webm",
|
|
70
|
+
".so",
|
|
71
|
+
".dylib",
|
|
72
|
+
".dll",
|
|
73
|
+
".exe",
|
|
74
|
+
".wasm",
|
|
75
|
+
".class",
|
|
76
|
+
".jar",
|
|
77
|
+
".sqlite",
|
|
78
|
+
".db",
|
|
39
79
|
];
|
|
40
80
|
/** Generated files that are large, uninteresting, and full of hashes. */
|
|
41
81
|
const GENERATED_FILES = [
|
|
42
|
-
|
|
43
|
-
|
|
82
|
+
"package-lock.json",
|
|
83
|
+
"yarn.lock",
|
|
84
|
+
"pnpm-lock.yaml",
|
|
85
|
+
"bun.lockb",
|
|
86
|
+
"composer.lock",
|
|
87
|
+
"Cargo.lock",
|
|
88
|
+
"Gemfile.lock",
|
|
89
|
+
"poetry.lock",
|
|
44
90
|
];
|
|
45
91
|
/**
|
|
46
92
|
* Bytes above which a staged blob is skipped.
|
|
@@ -52,12 +98,12 @@ export const MAX_SCANNED_BYTES = 512 * 1024;
|
|
|
52
98
|
/** Whether `path` should be scanned at all. */
|
|
53
99
|
export function shouldScanFile(path, byteLength) {
|
|
54
100
|
const lower = path.toLowerCase();
|
|
55
|
-
const basename = lower.split(
|
|
101
|
+
const basename = lower.split("/").pop() ?? lower;
|
|
56
102
|
if (GENERATED_FILES.some((name) => basename === name.toLowerCase()))
|
|
57
103
|
return false;
|
|
58
104
|
if (BINARY_EXTENSIONS.some((ext) => lower.endsWith(ext)))
|
|
59
105
|
return false;
|
|
60
|
-
if (typeof byteLength ===
|
|
106
|
+
if (typeof byteLength === "number" && byteLength > MAX_SCANNED_BYTES)
|
|
61
107
|
return false;
|
|
62
108
|
return true;
|
|
63
109
|
}
|
|
@@ -68,10 +114,10 @@ export function shouldScanFile(path, byteLength) {
|
|
|
68
114
|
* extension, since a staged blob can be anything.
|
|
69
115
|
*/
|
|
70
116
|
export function looksBinary(content) {
|
|
71
|
-
return content.includes(
|
|
117
|
+
return content.includes("\u0000");
|
|
72
118
|
}
|
|
73
119
|
/** Filler used where a string literal's contents were. */
|
|
74
|
-
const MASK_CHAR =
|
|
120
|
+
const MASK_CHAR = "·";
|
|
75
121
|
/**
|
|
76
122
|
* Blank out the *contents* of string literals, preserving length.
|
|
77
123
|
*
|
|
@@ -91,7 +137,7 @@ const MASK_CHAR = '·';
|
|
|
91
137
|
* the rest of the file.
|
|
92
138
|
*/
|
|
93
139
|
export function maskStringLiterals(source) {
|
|
94
|
-
const out = source.split(
|
|
140
|
+
const out = source.split("");
|
|
95
141
|
let index = 0;
|
|
96
142
|
while (index < out.length) {
|
|
97
143
|
const char = out[index];
|
|
@@ -99,24 +145,24 @@ export function maskStringLiterals(source) {
|
|
|
99
145
|
// cursor but leaves the text in the output, so the commented-out code was
|
|
100
146
|
// still scanned. The old detector only handled `//` at the very start of a
|
|
101
147
|
// trimmed line, so a trailing comment was scanned as code either way.
|
|
102
|
-
if (char ===
|
|
103
|
-
while (index < out.length && out[index] !==
|
|
148
|
+
if (char === "/" && out[index + 1] === "/") {
|
|
149
|
+
while (index < out.length && out[index] !== "\n") {
|
|
104
150
|
out[index] = MASK_CHAR;
|
|
105
151
|
index += 1;
|
|
106
152
|
}
|
|
107
153
|
continue;
|
|
108
154
|
}
|
|
109
|
-
if (char ===
|
|
155
|
+
if (char === "/" && out[index + 1] === "*") {
|
|
110
156
|
index += 2;
|
|
111
|
-
while (index < out.length && !(out[index] ===
|
|
112
|
-
if (out[index] !==
|
|
157
|
+
while (index < out.length && !(out[index] === "*" && out[index + 1] === "/")) {
|
|
158
|
+
if (out[index] !== "\n")
|
|
113
159
|
out[index] = MASK_CHAR;
|
|
114
160
|
index += 1;
|
|
115
161
|
}
|
|
116
162
|
index += 2;
|
|
117
163
|
continue;
|
|
118
164
|
}
|
|
119
|
-
if (char !== '"' && char !== "'" && char !==
|
|
165
|
+
if (char !== '"' && char !== "'" && char !== "`") {
|
|
120
166
|
index += 1;
|
|
121
167
|
continue;
|
|
122
168
|
}
|
|
@@ -124,11 +170,11 @@ export function maskStringLiterals(source) {
|
|
|
124
170
|
index += 1;
|
|
125
171
|
while (index < out.length) {
|
|
126
172
|
const current = out[index];
|
|
127
|
-
if (current ===
|
|
173
|
+
if (current === "\\") {
|
|
128
174
|
// Escaped character: mask both, and never let it terminate the literal.
|
|
129
|
-
if (out[index] !==
|
|
175
|
+
if (out[index] !== "\n")
|
|
130
176
|
out[index] = MASK_CHAR;
|
|
131
|
-
if (index + 1 < out.length && out[index + 1] !==
|
|
177
|
+
if (index + 1 < out.length && out[index + 1] !== "\n")
|
|
132
178
|
out[index + 1] = MASK_CHAR;
|
|
133
179
|
index += 2;
|
|
134
180
|
continue;
|
|
@@ -139,28 +185,28 @@ export function maskStringLiterals(source) {
|
|
|
139
185
|
}
|
|
140
186
|
// An unterminated quote must not swallow the rest of the file. Only a
|
|
141
187
|
// template literal legally spans lines.
|
|
142
|
-
if (current ===
|
|
188
|
+
if (current === "\n" && quote !== "`")
|
|
143
189
|
break;
|
|
144
190
|
// `${ … }` inside a template literal is code. Leave it visible, tracking
|
|
145
191
|
// brace depth so a nested object literal does not end it early.
|
|
146
|
-
if (quote ===
|
|
192
|
+
if (quote === "`" && current === "$" && out[index + 1] === "{") {
|
|
147
193
|
let depth = 1;
|
|
148
194
|
index += 2;
|
|
149
195
|
while (index < out.length && depth > 0) {
|
|
150
|
-
if (out[index] ===
|
|
196
|
+
if (out[index] === "{")
|
|
151
197
|
depth += 1;
|
|
152
|
-
else if (out[index] ===
|
|
198
|
+
else if (out[index] === "}")
|
|
153
199
|
depth -= 1;
|
|
154
200
|
index += 1;
|
|
155
201
|
}
|
|
156
202
|
continue;
|
|
157
203
|
}
|
|
158
|
-
if (current !==
|
|
204
|
+
if (current !== "\n")
|
|
159
205
|
out[index] = MASK_CHAR;
|
|
160
206
|
index += 1;
|
|
161
207
|
}
|
|
162
208
|
}
|
|
163
|
-
return out.join(
|
|
209
|
+
return out.join("");
|
|
164
210
|
}
|
|
165
211
|
/**
|
|
166
212
|
* Text of the balanced argument list starting at `openParen`, or null when the
|
|
@@ -180,9 +226,9 @@ export function maskStringLiterals(source) {
|
|
|
180
226
|
export function readArgumentList(source, openParen) {
|
|
181
227
|
let depth = 0;
|
|
182
228
|
for (let i = openParen; i < source.length; i += 1) {
|
|
183
|
-
if (source[i] ===
|
|
229
|
+
if (source[i] === "(")
|
|
184
230
|
depth += 1;
|
|
185
|
-
else if (source[i] ===
|
|
231
|
+
else if (source[i] === ")") {
|
|
186
232
|
depth -= 1;
|
|
187
233
|
if (depth === 0)
|
|
188
234
|
return source.slice(openParen + 1, i);
|
|
@@ -194,7 +240,7 @@ export function readArgumentList(source, openParen) {
|
|
|
194
240
|
export function lineOf(source, offset) {
|
|
195
241
|
let line = 1;
|
|
196
242
|
for (let i = 0; i < offset && i < source.length; i += 1) {
|
|
197
|
-
if (source[i] ===
|
|
243
|
+
if (source[i] === "\n")
|
|
198
244
|
line += 1;
|
|
199
245
|
}
|
|
200
246
|
return line;
|
|
@@ -225,7 +271,7 @@ export function findSecretLogging(source) {
|
|
|
225
271
|
const line = lineOf(masked, match.index);
|
|
226
272
|
violations.push({
|
|
227
273
|
line,
|
|
228
|
-
text: (lines[line - 1] ??
|
|
274
|
+
text: (lines[line - 1] ?? "").trim(),
|
|
229
275
|
reason: indicator[0],
|
|
230
276
|
});
|
|
231
277
|
}
|
|
@@ -233,12 +279,33 @@ export function findSecretLogging(source) {
|
|
|
233
279
|
}
|
|
234
280
|
/** Scan one staged blob. */
|
|
235
281
|
export function scanFile(path, content) {
|
|
236
|
-
if (!shouldScanFile(path, Buffer.byteLength(content,
|
|
237
|
-
return { path, violations: [], skipped:
|
|
282
|
+
if (!shouldScanFile(path, Buffer.byteLength(content, "utf-8"))) {
|
|
283
|
+
return { path, violations: [], skipped: "excluded by type or size" };
|
|
238
284
|
}
|
|
239
285
|
if (looksBinary(content)) {
|
|
240
|
-
return { path, violations: [], skipped:
|
|
286
|
+
return { path, violations: [], skipped: "binary content" };
|
|
241
287
|
}
|
|
242
288
|
return { path, violations: findSecretLogging(content) };
|
|
243
289
|
}
|
|
290
|
+
/**
|
|
291
|
+
* Format scan results based on the chosen output format ('text' | 'json' | 'sarif').
|
|
292
|
+
*/
|
|
293
|
+
export function formatScanResults(results, format = "text") {
|
|
294
|
+
if (format === "json") {
|
|
295
|
+
return JSON.stringify(results, null, 2);
|
|
296
|
+
}
|
|
297
|
+
if (format === "sarif") {
|
|
298
|
+
return formatSarifJson(results);
|
|
299
|
+
}
|
|
300
|
+
// Default text summary
|
|
301
|
+
let text = "";
|
|
302
|
+
for (const r of results) {
|
|
303
|
+
for (const v of r.violations) {
|
|
304
|
+
text += `🚨 [SecureFlow] Secret logging detected in ${r.path}:${v.line}\n`;
|
|
305
|
+
text += ` -> ${v.text}\n`;
|
|
306
|
+
text += ` why: ${v.reason} passed to a console call\n`;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return text;
|
|
310
|
+
}
|
|
244
311
|
//# sourceMappingURL=scanner.js.map
|
package/dist/scanner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "secureflow-cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Pre-commit secret logging detector for secure development workflows",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "tsc",
|
|
11
11
|
"prepare": "npm run build",
|
|
12
|
-
"publish": "npm publish --access public"
|
|
12
|
+
"publish": "npm publish --access public",
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"test:watch": "vitest"
|
|
13
15
|
},
|
|
14
16
|
"keywords": [
|
|
15
17
|
"security",
|
|
@@ -26,7 +28,8 @@
|
|
|
26
28
|
"dist"
|
|
27
29
|
],
|
|
28
30
|
"devDependencies": {
|
|
29
|
-
"@types/node": "^26.
|
|
30
|
-
"typescript": "^
|
|
31
|
+
"@types/node": "^26.4.1",
|
|
32
|
+
"typescript": "^6",
|
|
33
|
+
"vitest": "^5.0.0"
|
|
31
34
|
}
|
|
32
35
|
}
|