cleartoship 0.4.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/LICENSE +21 -0
- package/README.md +264 -0
- package/action.yml +154 -0
- package/dist/banner.d.ts +1 -0
- package/dist/banner.js +14 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +103 -0
- package/dist/data/popular.d.ts +17 -0
- package/dist/data/popular.js +103 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/report.d.ts +15 -0
- package/dist/report.js +317 -0
- package/dist/scan.d.ts +24 -0
- package/dist/scan.js +81 -0
- package/dist/scanners/community.d.ts +2 -0
- package/dist/scanners/community.js +155 -0
- package/dist/scanners/dependencies.d.ts +21 -0
- package/dist/scanners/dependencies.js +549 -0
- package/dist/scanners/index.d.ts +8 -0
- package/dist/scanners/index.js +13 -0
- package/dist/scanners/rls.d.ts +7 -0
- package/dist/scanners/rls.js +531 -0
- package/dist/scanners/secrets.d.ts +2 -0
- package/dist/scanners/secrets.js +439 -0
- package/dist/scanners/server-actions.d.ts +2 -0
- package/dist/scanners/server-actions.js +466 -0
- package/dist/types.d.ts +65 -0
- package/dist/types.js +10 -0
- package/dist/utils/ast.d.ts +13 -0
- package/dist/utils/ast.js +107 -0
- package/dist/utils/detect.d.ts +2 -0
- package/dist/utils/detect.js +77 -0
- package/dist/utils/entropy.d.ts +7 -0
- package/dist/utils/entropy.js +19 -0
- package/dist/utils/files.d.ts +13 -0
- package/dist/utils/files.js +154 -0
- package/dist/utils/osv.d.ts +26 -0
- package/dist/utils/osv.js +123 -0
- package/dist/utils/registry.d.ts +44 -0
- package/dist/utils/registry.js +195 -0
- package/dist/utils/sql.d.ts +20 -0
- package/dist/utils/sql.js +157 -0
- package/dist/utils/suppress.d.ts +19 -0
- package/dist/utils/suppress.js +56 -0
- package/dist/utils/traverse.d.ts +2 -0
- package/dist/utils/traverse.js +14 -0
- package/dist/vendor/gitleaks/rules.d.ts +13 -0
- package/dist/vendor/gitleaks/rules.js +1571 -0
- package/dist/vendor/guardvibe/index.d.ts +11 -0
- package/dist/vendor/guardvibe/index.js +60 -0
- package/dist/vendor/guardvibe/rules/advanced-security.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/advanced-security.js +418 -0
- package/dist/vendor/guardvibe/rules/ai-host-security.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/ai-host-security.js +190 -0
- package/dist/vendor/guardvibe/rules/ai-security.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/ai-security.js +413 -0
- package/dist/vendor/guardvibe/rules/ai-tool-runtime.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/ai-tool-runtime.js +194 -0
- package/dist/vendor/guardvibe/rules/api-security.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/api-security.js +154 -0
- package/dist/vendor/guardvibe/rules/auth.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/auth.js +222 -0
- package/dist/vendor/guardvibe/rules/cicd.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/cicd.js +108 -0
- package/dist/vendor/guardvibe/rules/core.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/core.js +484 -0
- package/dist/vendor/guardvibe/rules/cve-versions.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/cve-versions.js +1130 -0
- package/dist/vendor/guardvibe/rules/database.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/database.js +173 -0
- package/dist/vendor/guardvibe/rules/deployment.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/deployment.js +269 -0
- package/dist/vendor/guardvibe/rules/dockerfile.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/dockerfile.js +99 -0
- package/dist/vendor/guardvibe/rules/firebase.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/firebase.js +96 -0
- package/dist/vendor/guardvibe/rules/go.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/go.js +85 -0
- package/dist/vendor/guardvibe/rules/modern-stack.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/modern-stack.js +631 -0
- package/dist/vendor/guardvibe/rules/nextjs.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/nextjs.js +233 -0
- package/dist/vendor/guardvibe/rules/other-services.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/other-services.js +72 -0
- package/dist/vendor/guardvibe/rules/payments.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/payments.js +123 -0
- package/dist/vendor/guardvibe/rules/react-native.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/react-native.js +132 -0
- package/dist/vendor/guardvibe/rules/services.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/services.js +149 -0
- package/dist/vendor/guardvibe/rules/shell.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/shell.js +72 -0
- package/dist/vendor/guardvibe/rules/sql.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/sql.js +60 -0
- package/dist/vendor/guardvibe/rules/supply-chain.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/supply-chain.js +243 -0
- package/dist/vendor/guardvibe/rules/terraform.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/terraform.js +84 -0
- package/dist/vendor/guardvibe/rules/types.d.ts +14 -0
- package/dist/vendor/guardvibe/rules/types.js +11 -0
- package/dist/vendor/guardvibe/rules/web-security.d.ts +2 -0
- package/dist/vendor/guardvibe/rules/web-security.js +258 -0
- package/examples/security.yml +23 -0
- package/package.json +70 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
export function splitStatements(source) {
|
|
2
|
+
const out = [];
|
|
3
|
+
let buf = '';
|
|
4
|
+
let line = 1;
|
|
5
|
+
let startLine = 1;
|
|
6
|
+
let pendingStart = true;
|
|
7
|
+
let i = 0;
|
|
8
|
+
// Statements are separated by blank lines and comments, so the recorded line
|
|
9
|
+
// has to be the first line carrying an actual token.
|
|
10
|
+
const emit = (text) => {
|
|
11
|
+
if (pendingStart && text.trim()) {
|
|
12
|
+
startLine = line;
|
|
13
|
+
pendingStart = false;
|
|
14
|
+
}
|
|
15
|
+
buf += text;
|
|
16
|
+
};
|
|
17
|
+
const flush = () => {
|
|
18
|
+
if (buf.trim())
|
|
19
|
+
out.push({ text: buf.trim(), line: startLine });
|
|
20
|
+
buf = '';
|
|
21
|
+
pendingStart = true;
|
|
22
|
+
};
|
|
23
|
+
while (i < source.length) {
|
|
24
|
+
const ch = source[i];
|
|
25
|
+
const next = source[i + 1];
|
|
26
|
+
if (ch === '-' && next === '-') {
|
|
27
|
+
while (i < source.length && source[i] !== '\n')
|
|
28
|
+
i++;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (ch === '/' && next === '*') {
|
|
32
|
+
i += 2;
|
|
33
|
+
while (i < source.length && !(source[i] === '*' && source[i + 1] === '/')) {
|
|
34
|
+
if (source[i] === '\n')
|
|
35
|
+
line++;
|
|
36
|
+
i++;
|
|
37
|
+
}
|
|
38
|
+
i += 2;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (ch === "'" || ch === '"') {
|
|
42
|
+
const quote = ch;
|
|
43
|
+
emit(ch);
|
|
44
|
+
i++;
|
|
45
|
+
while (i < source.length) {
|
|
46
|
+
if (source[i] === '\n')
|
|
47
|
+
line++;
|
|
48
|
+
if (source[i] === quote) {
|
|
49
|
+
if (source[i + 1] === quote) {
|
|
50
|
+
emit(quote + quote);
|
|
51
|
+
i += 2;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
emit(quote);
|
|
55
|
+
i++;
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
emit(source[i]);
|
|
59
|
+
i++;
|
|
60
|
+
}
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (ch === '$') {
|
|
64
|
+
const tag = /^\$[A-Za-z_][A-Za-z0-9_]*\$|^\$\$/.exec(source.slice(i));
|
|
65
|
+
if (tag) {
|
|
66
|
+
const marker = tag[0];
|
|
67
|
+
const end = source.indexOf(marker, i + marker.length);
|
|
68
|
+
const chunk = end === -1 ? source.slice(i) : source.slice(i, end + marker.length);
|
|
69
|
+
emit(chunk);
|
|
70
|
+
for (const c of chunk)
|
|
71
|
+
if (c === '\n')
|
|
72
|
+
line++;
|
|
73
|
+
i += chunk.length;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (ch === ';') {
|
|
78
|
+
flush();
|
|
79
|
+
i++;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (ch === '\n') {
|
|
83
|
+
emit(ch);
|
|
84
|
+
line++;
|
|
85
|
+
i++;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
emit(ch);
|
|
89
|
+
i++;
|
|
90
|
+
}
|
|
91
|
+
flush();
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
/** Reads a parenthesised group starting at `open` (index of the `(`). */
|
|
95
|
+
export function readBalanced(text, open) {
|
|
96
|
+
if (text[open] !== '(')
|
|
97
|
+
return null;
|
|
98
|
+
let depth = 0;
|
|
99
|
+
let inString = null;
|
|
100
|
+
for (let i = open; i < text.length; i++) {
|
|
101
|
+
const ch = text[i];
|
|
102
|
+
if (inString) {
|
|
103
|
+
if (ch === inString) {
|
|
104
|
+
if (text[i + 1] === inString) {
|
|
105
|
+
i++;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
inString = null;
|
|
109
|
+
}
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (ch === "'" || ch === '"') {
|
|
113
|
+
inString = ch;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (ch === '(')
|
|
117
|
+
depth++;
|
|
118
|
+
else if (ch === ')') {
|
|
119
|
+
depth--;
|
|
120
|
+
if (depth === 0)
|
|
121
|
+
return text.slice(open + 1, i);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
/** Grabs the expression after a keyword such as USING or WITH CHECK. */
|
|
127
|
+
export function clauseAfter(text, keyword) {
|
|
128
|
+
const m = keyword.exec(text);
|
|
129
|
+
if (!m)
|
|
130
|
+
return null;
|
|
131
|
+
let i = m.index + m[0].length;
|
|
132
|
+
while (i < text.length && /\s/.test(text[i]))
|
|
133
|
+
i++;
|
|
134
|
+
return readBalanced(text, i);
|
|
135
|
+
}
|
|
136
|
+
/** Normalises `"public"."posts"` / `public.posts` / `posts` to `public.posts`. */
|
|
137
|
+
export function normaliseTable(raw) {
|
|
138
|
+
const parts = raw
|
|
139
|
+
.trim()
|
|
140
|
+
.split('.')
|
|
141
|
+
.map((p) => p.replace(/^"(.*)"$/, '$1').trim())
|
|
142
|
+
.filter(Boolean);
|
|
143
|
+
if (parts.length === 0)
|
|
144
|
+
return '';
|
|
145
|
+
if (parts.length === 1)
|
|
146
|
+
return `public.${parts[0]}`;
|
|
147
|
+
return `${parts[parts.length - 2]}.${parts[parts.length - 1]}`;
|
|
148
|
+
}
|
|
149
|
+
const IDENT = `(?:"[^"]+"|[A-Za-z_][A-Za-z0-9_$]*)`;
|
|
150
|
+
export const QUALIFIED_NAME = `${IDENT}(?:\\s*\\.\\s*${IDENT})*`;
|
|
151
|
+
/** True when a policy predicate lets everything through. */
|
|
152
|
+
export function isAlwaysTrue(expr) {
|
|
153
|
+
if (expr === null)
|
|
154
|
+
return false;
|
|
155
|
+
const cleaned = expr.replace(/\s+/g, ' ').replace(/[()]/g, ' ').trim().toLowerCase();
|
|
156
|
+
return cleaned === 'true' || cleaned === '1' || cleaned === 'true::boolean';
|
|
157
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Honours inline suppressions:
|
|
3
|
+
* // cleartoship-ignore CTS001 (same line, or anywhere in the comment block
|
|
4
|
+
* directly above)
|
|
5
|
+
* // cts-ignore (suppresses every rule at that location)
|
|
6
|
+
*
|
|
7
|
+
* The directive is looked for on the finding's own line and then upward through
|
|
8
|
+
* the contiguous comment lines above it, so a directive can sit at the top of a
|
|
9
|
+
* multi-line explanation — which is where anyone writing a real justification
|
|
10
|
+
* naturally puts it.
|
|
11
|
+
*/
|
|
12
|
+
export declare class Suppressions {
|
|
13
|
+
private readonly lines;
|
|
14
|
+
/** How far above a finding a comment block may start. */
|
|
15
|
+
private static readonly MAX_LOOKBACK;
|
|
16
|
+
constructor(source: string);
|
|
17
|
+
suppressed(line: number, ruleId: string): boolean;
|
|
18
|
+
private check;
|
|
19
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Honours inline suppressions:
|
|
3
|
+
* // cleartoship-ignore CTS001 (same line, or anywhere in the comment block
|
|
4
|
+
* directly above)
|
|
5
|
+
* // cts-ignore (suppresses every rule at that location)
|
|
6
|
+
*
|
|
7
|
+
* The directive is looked for on the finding's own line and then upward through
|
|
8
|
+
* the contiguous comment lines above it, so a directive can sit at the top of a
|
|
9
|
+
* multi-line explanation — which is where anyone writing a real justification
|
|
10
|
+
* naturally puts it.
|
|
11
|
+
*/
|
|
12
|
+
export class Suppressions {
|
|
13
|
+
lines;
|
|
14
|
+
/** How far above a finding a comment block may start. */
|
|
15
|
+
static MAX_LOOKBACK = 6;
|
|
16
|
+
constructor(source) {
|
|
17
|
+
this.lines = source.split('\n');
|
|
18
|
+
}
|
|
19
|
+
suppressed(line, ruleId) {
|
|
20
|
+
if (this.check(line, ruleId))
|
|
21
|
+
return true;
|
|
22
|
+
for (let i = 1; i <= Suppressions.MAX_LOOKBACK; i++) {
|
|
23
|
+
const candidate = line - i;
|
|
24
|
+
if (candidate < 1)
|
|
25
|
+
break;
|
|
26
|
+
const text = this.lines[candidate - 1];
|
|
27
|
+
if (text === undefined)
|
|
28
|
+
break;
|
|
29
|
+
const trimmed = text.trim();
|
|
30
|
+
// Only walk upward through comments; any real code ends the block.
|
|
31
|
+
if (!/^(\/\/|\/\*|\*|#|--)/.test(trimmed) && trimmed !== '')
|
|
32
|
+
break;
|
|
33
|
+
if (this.check(candidate, ruleId))
|
|
34
|
+
return true;
|
|
35
|
+
if (trimmed === '')
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
check(line, ruleId) {
|
|
41
|
+
const text = this.lines[line - 1];
|
|
42
|
+
if (!text)
|
|
43
|
+
return false;
|
|
44
|
+
const m = /(?:cleartoship|cts)-ignore(?:\s+([A-Z0-9,\s]+))?/i.exec(text);
|
|
45
|
+
if (!m)
|
|
46
|
+
return false;
|
|
47
|
+
const ids = m[1];
|
|
48
|
+
if (!ids || !ids.trim())
|
|
49
|
+
return true;
|
|
50
|
+
return ids
|
|
51
|
+
.split(/[,\s]+/)
|
|
52
|
+
.map((s) => s.trim().toUpperCase())
|
|
53
|
+
.filter(Boolean)
|
|
54
|
+
.includes(ruleId.toUpperCase());
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as babelTraverse from '@babel/traverse';
|
|
2
|
+
// @babel/traverse ships CommonJS compiled with Babel's own esModule interop, so
|
|
3
|
+
// under Node16 ESM resolution the callable can sit one or two `.default` hops
|
|
4
|
+
// deep depending on how the CJS named exports were detected.
|
|
5
|
+
const ns = babelTraverse;
|
|
6
|
+
const resolved = typeof ns === 'function'
|
|
7
|
+
? ns
|
|
8
|
+
: typeof ns.default === 'function'
|
|
9
|
+
? ns.default
|
|
10
|
+
: ns.default?.default;
|
|
11
|
+
if (typeof resolved !== 'function') {
|
|
12
|
+
throw new Error('could not resolve @babel/traverse to a callable');
|
|
13
|
+
}
|
|
14
|
+
export const traverse = resolved;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface GitleaksRule {
|
|
2
|
+
id: string;
|
|
3
|
+
description: string;
|
|
4
|
+
pattern: RegExp;
|
|
5
|
+
/** Minimum Shannon entropy of the captured secret, when upstream sets one. */
|
|
6
|
+
entropy: number | null;
|
|
7
|
+
/** Cheap substring prefilter: skip the regex unless one of these appears. */
|
|
8
|
+
keywords: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare const GITLEAKS_ATTRIBUTION = "gitleaks (github.com/gitleaks/gitleaks), Copyright (c) 2019 Zachary Rice, MIT";
|
|
11
|
+
/** Words that mark a match as a documentation placeholder rather than a secret. */
|
|
12
|
+
export declare const GITLEAKS_STOPWORDS: string[];
|
|
13
|
+
export declare const GITLEAKS_RULES: GitleaksRule[];
|