@secfn/core 0.0.1 → 0.0.3
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 +30 -322
- package/dist/encryption.d.ts +8 -0
- package/dist/encryption.d.ts.map +1 -0
- package/dist/encryption.js +83 -0
- package/dist/encryption.js.map +1 -0
- package/dist/errors.d.ts +24 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +47 -0
- package/dist/errors.js.map +1 -0
- package/dist/id.d.ts +3 -0
- package/dist/id.d.ts.map +1 -0
- package/dist/id.js +11 -0
- package/dist/id.js.map +1 -0
- package/dist/index.d.ts +9 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -11
- package/dist/index.js.map +1 -1
- package/dist/scanner/reporters.d.ts +14 -0
- package/dist/scanner/reporters.d.ts.map +1 -0
- package/dist/scanner/reporters.js +85 -0
- package/dist/scanner/reporters.js.map +1 -0
- package/dist/scanner/rules.d.ts +24 -0
- package/dist/scanner/rules.d.ts.map +1 -0
- package/dist/scanner/rules.js +141 -0
- package/dist/scanner/rules.js.map +1 -0
- package/dist/scanner/scanner.d.ts +28 -0
- package/dist/scanner/scanner.d.ts.map +1 -0
- package/dist/scanner/scanner.js +104 -0
- package/dist/scanner/scanner.js.map +1 -0
- package/dist/scanner/utils.d.ts +15 -0
- package/dist/scanner/utils.d.ts.map +1 -0
- package/dist/scanner/utils.js +41 -0
- package/dist/scanner/utils.js.map +1 -0
- package/dist/schema.d.ts +9 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +249 -0
- package/dist/schema.js.map +1 -0
- package/dist/types.d.ts +261 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +32 -27
- package/dist/access-control/rbac.d.ts +0 -48
- package/dist/access-control/rbac.d.ts.map +0 -1
- package/dist/access-control/rbac.js +0 -203
- package/dist/access-control/rbac.js.map +0 -1
- package/dist/core/secfn.d.ts +0 -28
- package/dist/core/secfn.d.ts.map +0 -1
- package/dist/core/secfn.js +0 -73
- package/dist/core/secfn.js.map +0 -1
- package/dist/monitoring/security-monitor.d.ts +0 -46
- package/dist/monitoring/security-monitor.d.ts.map +0 -1
- package/dist/monitoring/security-monitor.js +0 -172
- package/dist/monitoring/security-monitor.js.map +0 -1
- package/dist/rate-limiting/rate-limiter.d.ts +0 -31
- package/dist/rate-limiting/rate-limiter.d.ts.map +0 -1
- package/dist/rate-limiting/rate-limiter.js +0 -140
- package/dist/rate-limiting/rate-limiter.js.map +0 -1
- package/dist/scanning/patterns.d.ts +0 -3
- package/dist/scanning/patterns.d.ts.map +0 -1
- package/dist/scanning/patterns.js +0 -139
- package/dist/scanning/patterns.js.map +0 -1
- package/dist/scanning/scanner.d.ts +0 -36
- package/dist/scanning/scanner.d.ts.map +0 -1
- package/dist/scanning/scanner.js +0 -152
- package/dist/scanning/scanner.js.map +0 -1
- package/dist/secrets/encryption.d.ts +0 -17
- package/dist/secrets/encryption.d.ts.map +0 -1
- package/dist/secrets/encryption.js +0 -47
- package/dist/secrets/encryption.js.map +0 -1
- package/dist/secrets/vault.d.ts +0 -43
- package/dist/secrets/vault.d.ts.map +0 -1
- package/dist/secrets/vault.js +0 -126
- package/dist/secrets/vault.js.map +0 -1
- package/dist/storage/indexeddb-storage.d.ts +0 -37
- package/dist/storage/indexeddb-storage.d.ts.map +0 -1
- package/dist/storage/indexeddb-storage.js +0 -284
- package/dist/storage/indexeddb-storage.js.map +0 -1
- package/dist/types/index.d.ts +0 -189
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -2
- package/dist/types/index.js.map +0 -1
- package/dist/utils/helpers.d.ts +0 -8
- package/dist/utils/helpers.d.ts.map +0 -1
- package/dist/utils/helpers.js +0 -73
- package/dist/utils/helpers.js.map +0 -1
- package/examples/basic-usage.ts +0 -143
- package/src/access-control/rbac.ts +0 -271
- package/src/core/secfn.ts +0 -90
- package/src/index.ts +0 -27
- package/src/monitoring/security-monitor.ts +0 -245
- package/src/rate-limiting/rate-limiter.ts +0 -181
- package/src/scanning/patterns.ts +0 -140
- package/src/scanning/scanner.ts +0 -199
- package/src/secrets/encryption.ts +0 -74
- package/src/secrets/vault.ts +0 -187
- package/src/storage/indexeddb-storage.ts +0 -355
- package/src/types/index.ts +0 -213
- package/src/utils/helpers.ts +0 -84
- package/tsconfig.json +0 -26
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const SEVERITY_ORDER = {
|
|
2
|
+
info: 0,
|
|
3
|
+
low: 1,
|
|
4
|
+
medium: 2,
|
|
5
|
+
high: 3,
|
|
6
|
+
critical: 4,
|
|
7
|
+
};
|
|
8
|
+
export function findingMeetsSeverity(finding, minimum) {
|
|
9
|
+
return SEVERITY_ORDER[finding.severity] >= SEVERITY_ORDER[minimum];
|
|
10
|
+
}
|
|
11
|
+
export function formatFindingsTable(findings) {
|
|
12
|
+
if (findings.length === 0) {
|
|
13
|
+
return "No security findings found.";
|
|
14
|
+
}
|
|
15
|
+
return findings
|
|
16
|
+
.map((finding) => [
|
|
17
|
+
finding.severity.toUpperCase().padEnd(8),
|
|
18
|
+
finding.ruleName,
|
|
19
|
+
`${finding.file}:${finding.line}:${finding.column}`,
|
|
20
|
+
finding.redactedMatch ?? "",
|
|
21
|
+
].filter(Boolean).join(" "))
|
|
22
|
+
.join("\n");
|
|
23
|
+
}
|
|
24
|
+
export function formatFindingsJson(findings) {
|
|
25
|
+
return JSON.stringify({ findings: findings.map(finding => { const safe = { ...finding }; delete safe.match; delete safe.context; return safe; }) }, null, 2);
|
|
26
|
+
}
|
|
27
|
+
export function formatFindingsSarif(findings) {
|
|
28
|
+
const rules = new Map();
|
|
29
|
+
for (const finding of findings) {
|
|
30
|
+
if (!rules.has(finding.ruleId))
|
|
31
|
+
rules.set(finding.ruleId, finding);
|
|
32
|
+
}
|
|
33
|
+
return JSON.stringify({
|
|
34
|
+
version: "2.1.0",
|
|
35
|
+
$schema: "https://json.schemastore.org/sarif-2.1.0.json",
|
|
36
|
+
runs: [
|
|
37
|
+
{
|
|
38
|
+
tool: {
|
|
39
|
+
driver: {
|
|
40
|
+
name: "SecFn",
|
|
41
|
+
informationUri: "https://github.com/21nCo/super-functions/tree/dev/secfn",
|
|
42
|
+
rules: Array.from(rules.values()).map((finding) => ({
|
|
43
|
+
id: finding.ruleId,
|
|
44
|
+
name: finding.ruleName,
|
|
45
|
+
shortDescription: { text: finding.ruleName },
|
|
46
|
+
fullDescription: { text: finding.message },
|
|
47
|
+
defaultConfiguration: {
|
|
48
|
+
level: sarifLevel(finding.severity),
|
|
49
|
+
},
|
|
50
|
+
})),
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
results: findings.map((finding) => ({
|
|
54
|
+
ruleId: finding.ruleId,
|
|
55
|
+
level: sarifLevel(finding.severity),
|
|
56
|
+
message: {
|
|
57
|
+
text: `${finding.message}${finding.redactedMatch ? ` (${finding.redactedMatch})` : ""}`,
|
|
58
|
+
},
|
|
59
|
+
partialFingerprints: {
|
|
60
|
+
primaryLocationLineHash: finding.fingerprint,
|
|
61
|
+
},
|
|
62
|
+
locations: [
|
|
63
|
+
{
|
|
64
|
+
physicalLocation: {
|
|
65
|
+
artifactLocation: { uri: finding.file },
|
|
66
|
+
region: {
|
|
67
|
+
startLine: finding.line,
|
|
68
|
+
startColumn: finding.column,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
})),
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
}, null, 2);
|
|
77
|
+
}
|
|
78
|
+
function sarifLevel(severity) {
|
|
79
|
+
if (severity === "critical" || severity === "high")
|
|
80
|
+
return "error";
|
|
81
|
+
if (severity === "medium" || severity === "low")
|
|
82
|
+
return "warning";
|
|
83
|
+
return "note";
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=reporters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporters.js","sourceRoot":"","sources":["../../src/scanner/reporters.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { RulePack, ScanTarget, SecurityFinding, SecurityRule, SecFnSeverity } from "../types.js";
|
|
2
|
+
export interface PatternSecurityRuleConfig {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
severity: SecFnSeverity;
|
|
7
|
+
pattern: RegExp;
|
|
8
|
+
entropy?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class PatternSecurityRule implements SecurityRule {
|
|
11
|
+
readonly id: string;
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly description: string;
|
|
14
|
+
readonly severity: SecFnSeverity;
|
|
15
|
+
private readonly pattern;
|
|
16
|
+
private readonly entropy?;
|
|
17
|
+
constructor(config: PatternSecurityRuleConfig);
|
|
18
|
+
evaluate({ target, content }: {
|
|
19
|
+
target: ScanTarget;
|
|
20
|
+
content: string;
|
|
21
|
+
}): SecurityFinding[];
|
|
22
|
+
}
|
|
23
|
+
export declare const defaultSecretRulePack: RulePack;
|
|
24
|
+
//# sourceMappingURL=rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/scanner/rules.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAStG,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,aAAa,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,mBAAoB,YAAW,YAAY;IACtD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAS;gBAEtB,MAAM,EAAE,yBAAyB;IAS7C,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,eAAe,EAAE;CAiC1F;AAED,eAAO,MAAM,qBAAqB,EAAE,QAmFnC,CAAC"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { generateId } from "../id.js";
|
|
2
|
+
import { calculateEntropy, contextForLine, fingerprintFinding, lineColumn, redactSecret, } from "./utils.js";
|
|
3
|
+
export class PatternSecurityRule {
|
|
4
|
+
id;
|
|
5
|
+
name;
|
|
6
|
+
description;
|
|
7
|
+
severity;
|
|
8
|
+
pattern;
|
|
9
|
+
entropy;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.id = config.id;
|
|
12
|
+
this.name = config.name;
|
|
13
|
+
this.description = config.description;
|
|
14
|
+
this.severity = config.severity;
|
|
15
|
+
this.pattern = ensureGlobal(config.pattern);
|
|
16
|
+
this.entropy = config.entropy;
|
|
17
|
+
}
|
|
18
|
+
evaluate({ target, content }) {
|
|
19
|
+
const findings = [];
|
|
20
|
+
for (const match of content.matchAll(this.pattern)) {
|
|
21
|
+
if (match.index === undefined || !match[0])
|
|
22
|
+
continue;
|
|
23
|
+
const value = match[1] || match[0];
|
|
24
|
+
if (this.entropy !== undefined && calculateEntropy(value) < this.entropy) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const location = lineColumn(content, match.index);
|
|
28
|
+
const file = target.displayPath ?? target.path;
|
|
29
|
+
findings.push({
|
|
30
|
+
id: generateId("finding"),
|
|
31
|
+
ruleId: this.id,
|
|
32
|
+
ruleName: this.name,
|
|
33
|
+
severity: this.severity,
|
|
34
|
+
message: this.description,
|
|
35
|
+
file,
|
|
36
|
+
line: location.line,
|
|
37
|
+
column: location.column,
|
|
38
|
+
match: value,
|
|
39
|
+
redactedMatch: redactSecret(value),
|
|
40
|
+
fingerprint: fingerprintFinding({
|
|
41
|
+
ruleId: this.id,
|
|
42
|
+
file,
|
|
43
|
+
line: location.line,
|
|
44
|
+
column: location.column,
|
|
45
|
+
value,
|
|
46
|
+
}),
|
|
47
|
+
context: contextForLine(content, location.line),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return findings;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export const defaultSecretRulePack = {
|
|
54
|
+
id: "secfn.secrets",
|
|
55
|
+
name: "SecFn Secret Detection",
|
|
56
|
+
rules: [
|
|
57
|
+
new PatternSecurityRule({
|
|
58
|
+
id: "secret.aws_access_key",
|
|
59
|
+
name: "AWS Access Key",
|
|
60
|
+
description: "AWS access key detected",
|
|
61
|
+
severity: "critical",
|
|
62
|
+
pattern: /AKIA[0-9A-Z]{16}/g,
|
|
63
|
+
entropy: 3.5,
|
|
64
|
+
}),
|
|
65
|
+
new PatternSecurityRule({
|
|
66
|
+
id: "secret.aws_secret_key",
|
|
67
|
+
name: "AWS Secret Key",
|
|
68
|
+
description: "AWS secret access key assignment detected",
|
|
69
|
+
severity: "critical",
|
|
70
|
+
pattern: /aws[_-]?secret[_-]?access[_-]?key\s*[=:]\s*['"]?([A-Za-z0-9/+=]{40})['"]?/gi,
|
|
71
|
+
entropy: 3.5,
|
|
72
|
+
}),
|
|
73
|
+
new PatternSecurityRule({
|
|
74
|
+
id: "secret.github_token",
|
|
75
|
+
name: "GitHub Token",
|
|
76
|
+
description: "GitHub token detected",
|
|
77
|
+
severity: "critical",
|
|
78
|
+
pattern: /\b(gh[pousr]_[A-Za-z0-9_]{36,255})\b/g,
|
|
79
|
+
entropy: 3.5,
|
|
80
|
+
}),
|
|
81
|
+
new PatternSecurityRule({
|
|
82
|
+
id: "secret.openai_key",
|
|
83
|
+
name: "OpenAI API Key",
|
|
84
|
+
description: "OpenAI API key detected",
|
|
85
|
+
severity: "critical",
|
|
86
|
+
pattern: /\b(sk-[A-Za-z0-9_-]{20,})\b/g,
|
|
87
|
+
entropy: 3.0,
|
|
88
|
+
}),
|
|
89
|
+
new PatternSecurityRule({
|
|
90
|
+
id: "secret.stripe_key",
|
|
91
|
+
name: "Stripe API Key",
|
|
92
|
+
description: "Stripe API key detected",
|
|
93
|
+
severity: "critical",
|
|
94
|
+
pattern: /\b((?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,})\b/g,
|
|
95
|
+
entropy: 3.0,
|
|
96
|
+
}),
|
|
97
|
+
new PatternSecurityRule({
|
|
98
|
+
id: "secret.slack_token",
|
|
99
|
+
name: "Slack Token",
|
|
100
|
+
description: "Slack token detected",
|
|
101
|
+
severity: "high",
|
|
102
|
+
pattern: /\b(xox[baprs]-[A-Za-z0-9-]{10,})\b/g,
|
|
103
|
+
entropy: 3.0,
|
|
104
|
+
}),
|
|
105
|
+
new PatternSecurityRule({
|
|
106
|
+
id: "secret.private_key",
|
|
107
|
+
name: "Private Key",
|
|
108
|
+
description: "Private key block detected",
|
|
109
|
+
severity: "critical",
|
|
110
|
+
pattern: /-----BEGIN (?:RSA |OPENSSH |DSA |EC |PGP )?PRIVATE KEY-----/g,
|
|
111
|
+
}),
|
|
112
|
+
new PatternSecurityRule({
|
|
113
|
+
id: "secret.jwt",
|
|
114
|
+
name: "JWT Token",
|
|
115
|
+
description: "JWT token detected",
|
|
116
|
+
severity: "high",
|
|
117
|
+
pattern: /\b(eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)\b/g,
|
|
118
|
+
entropy: 3.0,
|
|
119
|
+
}),
|
|
120
|
+
new PatternSecurityRule({
|
|
121
|
+
id: "secret.database_url",
|
|
122
|
+
name: "Database URL With Credentials",
|
|
123
|
+
description: "Database URL containing credentials detected",
|
|
124
|
+
severity: "high",
|
|
125
|
+
pattern: /\b((?:postgres|postgresql|mysql|mongodb|redis):\/\/[^:\s]+:[^@\s]+@[^\s'"]+)/gi,
|
|
126
|
+
}),
|
|
127
|
+
new PatternSecurityRule({
|
|
128
|
+
id: "secret.generic_api_key",
|
|
129
|
+
name: "Generic API Key",
|
|
130
|
+
description: "Generic API key assignment detected",
|
|
131
|
+
severity: "medium",
|
|
132
|
+
pattern: /(?:api[_-]?key|secret|token)\s*[=:]\s*['"]?([A-Za-z0-9_\-./+=]{24,})['"]?/gi,
|
|
133
|
+
entropy: 3.5,
|
|
134
|
+
}),
|
|
135
|
+
],
|
|
136
|
+
};
|
|
137
|
+
function ensureGlobal(pattern) {
|
|
138
|
+
const flags = pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`;
|
|
139
|
+
return new RegExp(pattern.source, flags);
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/scanner/rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { RulePack, ScanTarget, SecurityFinding } from "../types.js";
|
|
2
|
+
export interface SecurityScannerOptions {
|
|
3
|
+
rulePacks?: RulePack[];
|
|
4
|
+
excludePaths?: string[];
|
|
5
|
+
maxFileSize?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface ScanDirectoryOptions {
|
|
8
|
+
saveContent?: false;
|
|
9
|
+
}
|
|
10
|
+
export interface GitHistoryScanOptions {
|
|
11
|
+
commits?: number;
|
|
12
|
+
branch?: string;
|
|
13
|
+
/** Fail explicitly if one commit exceeds this many bytes; default 32 MiB. */
|
|
14
|
+
maxCommitBytes?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare class SecurityScanner {
|
|
17
|
+
private readonly rulePacks;
|
|
18
|
+
private readonly excludePaths;
|
|
19
|
+
private readonly maxFileSize;
|
|
20
|
+
constructor(options?: SecurityScannerOptions);
|
|
21
|
+
scanContent(content: string, target: ScanTarget): SecurityFinding[];
|
|
22
|
+
scanFile(filePath: string): Promise<SecurityFinding[]>;
|
|
23
|
+
private readFileContent;
|
|
24
|
+
scanDirectory(dir: string): Promise<SecurityFinding[]>;
|
|
25
|
+
scanGitHistory(options?: GitHistoryScanOptions): Promise<SecurityFinding[]>;
|
|
26
|
+
}
|
|
27
|
+
export declare function createSecurityScanner(options?: SecurityScannerOptions): SecurityScanner;
|
|
28
|
+
//# sourceMappingURL=scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/scanner/scanner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAIzE,MAAM,WAAW,sBAAsB;IACrC,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,CAAC,EAAE,KAAK,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAa;IACvC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAW;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAEzB,OAAO,GAAE,sBAA2B;IAgBhD,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,eAAe,EAAE;IAM7D,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;YAK9C,eAAe;IAoBvB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAuBtD,cAAc,CAAC,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;CAwBtF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,eAAe,CAEvF"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { defaultSecretRulePack } from "./rules.js";
|
|
2
|
+
export class SecurityScanner {
|
|
3
|
+
rulePacks;
|
|
4
|
+
excludePaths;
|
|
5
|
+
maxFileSize;
|
|
6
|
+
constructor(options = {}) {
|
|
7
|
+
this.rulePacks = options.rulePacks ?? [defaultSecretRulePack];
|
|
8
|
+
this.excludePaths = options.excludePaths ?? [
|
|
9
|
+
"node_modules/**",
|
|
10
|
+
"dist/**",
|
|
11
|
+
"build/**",
|
|
12
|
+
".git/**",
|
|
13
|
+
".next/**",
|
|
14
|
+
"coverage/**",
|
|
15
|
+
];
|
|
16
|
+
this.maxFileSize = options.maxFileSize ?? 1024 * 1024;
|
|
17
|
+
if (!Number.isSafeInteger(this.maxFileSize) || this.maxFileSize < 0 || this.maxFileSize >= 2 ** 32) {
|
|
18
|
+
throw new RangeError("maxFileSize must be a nonnegative integer below 2^32");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
scanContent(content, target) {
|
|
22
|
+
return this.rulePacks.flatMap((pack) => pack.rules.flatMap((rule) => rule.evaluate({ content, target })));
|
|
23
|
+
}
|
|
24
|
+
async scanFile(filePath) {
|
|
25
|
+
const content = await this.readFileContent(filePath);
|
|
26
|
+
return content === undefined ? [] : this.scanContent(content, { path: filePath });
|
|
27
|
+
}
|
|
28
|
+
async readFileContent(filePath) {
|
|
29
|
+
const { open } = await import("node:fs/promises");
|
|
30
|
+
const file = await open(filePath, "r");
|
|
31
|
+
try {
|
|
32
|
+
const chunks = [];
|
|
33
|
+
let size = 0;
|
|
34
|
+
while (size <= this.maxFileSize) {
|
|
35
|
+
const buffer = Buffer.alloc(Math.min(64 * 1024, this.maxFileSize + 1 - size));
|
|
36
|
+
const { bytesRead } = await file.read(buffer, 0, buffer.length, null);
|
|
37
|
+
if (!bytesRead)
|
|
38
|
+
break;
|
|
39
|
+
size += bytesRead;
|
|
40
|
+
if (size > this.maxFileSize)
|
|
41
|
+
return undefined;
|
|
42
|
+
chunks.push(buffer.subarray(0, bytesRead));
|
|
43
|
+
}
|
|
44
|
+
return Buffer.concat(chunks, size).toString("utf8");
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
await file.close();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async scanDirectory(dir) {
|
|
51
|
+
const { default: glob } = await import("fast-glob");
|
|
52
|
+
const files = await glob("**/*", {
|
|
53
|
+
cwd: dir,
|
|
54
|
+
absolute: true,
|
|
55
|
+
onlyFiles: true,
|
|
56
|
+
followSymbolicLinks: false,
|
|
57
|
+
ignore: this.excludePaths,
|
|
58
|
+
});
|
|
59
|
+
const findings = [];
|
|
60
|
+
for (const file of files) {
|
|
61
|
+
let content;
|
|
62
|
+
try {
|
|
63
|
+
content = await this.readFileContent(file);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Unreadable files are ignored by design; rule failures run outside this boundary.
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (content !== undefined)
|
|
70
|
+
findings.push(...this.scanContent(content, { path: file }));
|
|
71
|
+
}
|
|
72
|
+
return findings;
|
|
73
|
+
}
|
|
74
|
+
async scanGitHistory(options = {}) {
|
|
75
|
+
const { execFile } = await import("node:child_process");
|
|
76
|
+
const { promisify } = await import("node:util");
|
|
77
|
+
const execFileAsync = promisify(execFile);
|
|
78
|
+
const maxBuffer = options.maxCommitBytes ?? 32 * 1024 * 1024;
|
|
79
|
+
if (!Number.isSafeInteger(maxBuffer) || maxBuffer < 1)
|
|
80
|
+
throw new RangeError("maxCommitBytes must be a positive safe integer");
|
|
81
|
+
const commits = String(options.commits ?? 100);
|
|
82
|
+
const branch = options.branch ?? "HEAD";
|
|
83
|
+
if (branch.startsWith("-"))
|
|
84
|
+
throw new RangeError("branch must not begin with '-'");
|
|
85
|
+
const { stdout } = await execFileAsync("git", ["log", branch, `-n${commits}`, "--format=%H"]);
|
|
86
|
+
const findings = [];
|
|
87
|
+
for (const hash of stdout.split("\n").filter(Boolean)) {
|
|
88
|
+
const shown = await execFileAsync("git", ["show", "--format=medium", "--no-ext-diff", hash], { maxBuffer });
|
|
89
|
+
const commitFindings = this.scanContent(shown.stdout, {
|
|
90
|
+
path: `commit:${hash}`,
|
|
91
|
+
displayPath: `commit:${hash.slice(0, 12)}`,
|
|
92
|
+
});
|
|
93
|
+
for (const finding of commitFindings) {
|
|
94
|
+
finding.metadata = { ...(finding.metadata ?? {}), commit: hash };
|
|
95
|
+
}
|
|
96
|
+
findings.push(...commitFindings);
|
|
97
|
+
}
|
|
98
|
+
return findings;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export function createSecurityScanner(options) {
|
|
102
|
+
return new SecurityScanner(options);
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/scanner/scanner.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAoBnD,MAAM,OAAO,eAAe;IACT,SAAS,CAAa;IACtB,YAAY,CAAW;IACvB,WAAW,CAAS;IAErC,YAAY,UAAkC,EAAE;QAC9C,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,CAAC,qBAAqB,CAAC,CAAC;QAC9D,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI;YAC1C,iBAAiB;YACjB,SAAS;YACT,UAAU;YACV,SAAS;YACT,UAAU;YACV,aAAa;SACd,CAAC;QACF,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,IAAI,GAAG,IAAI,CAAC;QACtD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,WAAW,GAAG,CAAC,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YACnG,MAAM,IAAI,UAAU,CAAC,sDAAsD,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IAED,WAAW,CAAC,OAAe,EAAE,MAAkB;QAC7C,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CACrC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,CACjE,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,QAAgB;QAC7B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QACrD,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;IACpF,CAAC;IAEO,KAAK,CAAC,eAAe,CAAC,QAAgB;QAC5C,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;QAClD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACvC,IAAI,CAAC;YACH,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,IAAI,IAAI,GAAG,CAAC,CAAC;YACb,OAAO,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBAChC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,CAAC,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;gBAC9E,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBACtE,IAAI,CAAC,SAAS;oBAAE,MAAM;gBACtB,IAAI,IAAI,SAAS,CAAC;gBAClB,IAAI,IAAI,GAAG,IAAI,CAAC,WAAW;oBAAE,OAAO,SAAS,CAAC;gBAC9C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;YAC7C,CAAC;YACD,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACtD,CAAC;gBAAS,CAAC;YACT,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,GAAW;QAC7B,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;QACpD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE;YAC/B,GAAG,EAAE,GAAG;YACR,QAAQ,EAAE,IAAI;YACd,SAAS,EAAE,IAAI;YACf,mBAAmB,EAAE,KAAK;YAC1B,MAAM,EAAE,IAAI,CAAC,YAAY;SAC1B,CAAC,CAAC;QACH,MAAM,QAAQ,GAAsB,EAAE,CAAC;QACvC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,OAA2B,CAAC;YAChC,IAAI,CAAC;gBACH,OAAO,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;YAC7C,CAAC;YAAC,MAAM,CAAC;gBACP,mFAAmF;gBACnF,SAAS;YACX,CAAC;YACD,IAAI,OAAO,KAAK,SAAS;gBAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACzF,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,UAAiC,EAAE;QACtD,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACxD,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;QAChD,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC1C,MAAM,SAAS,GAAG,OAAO,CAAC,cAAc,IAAI,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;QAC7D,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,gDAAgD,CAAC,CAAC;QAC9H,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,IAAI,GAAG,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC;QACxC,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,gCAAgC,CAAC,CAAC;QACnF,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,OAAO,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC;QAC9F,MAAM,QAAQ,GAAsB,EAAE,CAAC;QACvC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YACtD,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,iBAAiB,EAAE,eAAe,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;YAC5G,MAAM,cAAc,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE;gBACpD,IAAI,EAAE,UAAU,IAAI,EAAE;gBACtB,WAAW,EAAE,UAAU,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE;aAC3C,CAAC,CAAC;YACH,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;gBACrC,OAAO,CAAC,QAAQ,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;YACnE,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF;AAED,MAAM,UAAU,qBAAqB,CAAC,OAAgC;IACpE,OAAO,IAAI,eAAe,CAAC,OAAO,CAAC,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare function calculateEntropy(value: string): number;
|
|
2
|
+
export declare function redactSecret(value: string): string;
|
|
3
|
+
export declare function fingerprintFinding(input: {
|
|
4
|
+
ruleId: string;
|
|
5
|
+
file: string;
|
|
6
|
+
line: number;
|
|
7
|
+
column: number;
|
|
8
|
+
value: string;
|
|
9
|
+
}): string;
|
|
10
|
+
export declare function lineColumn(content: string, index: number): {
|
|
11
|
+
line: number;
|
|
12
|
+
column: number;
|
|
13
|
+
};
|
|
14
|
+
export declare function contextForLine(content: string, line: number, span?: number): string;
|
|
15
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/scanner/utils.ts"],"names":[],"mappings":"AAEA,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAatD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGlD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,MAAM,CAIT;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAO3F;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,SAAI,GAAG,MAAM,CAK9E"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
export function calculateEntropy(value) {
|
|
3
|
+
if (!value)
|
|
4
|
+
return 0;
|
|
5
|
+
const symbols = [...value];
|
|
6
|
+
const counts = new Map();
|
|
7
|
+
for (const char of symbols) {
|
|
8
|
+
counts.set(char, (counts.get(char) ?? 0) + 1);
|
|
9
|
+
}
|
|
10
|
+
let entropy = 0;
|
|
11
|
+
for (const count of counts.values()) {
|
|
12
|
+
const probability = count / symbols.length;
|
|
13
|
+
entropy -= probability * Math.log2(probability);
|
|
14
|
+
}
|
|
15
|
+
return entropy;
|
|
16
|
+
}
|
|
17
|
+
export function redactSecret(value) {
|
|
18
|
+
if (value.length <= 8)
|
|
19
|
+
return "[redacted]";
|
|
20
|
+
return `${value.slice(0, 4)}...${value.slice(-4)}`;
|
|
21
|
+
}
|
|
22
|
+
export function fingerprintFinding(input) {
|
|
23
|
+
return createHash("sha256")
|
|
24
|
+
.update(`${input.ruleId}:${input.file}:${input.line}:${input.column}:${input.value}`)
|
|
25
|
+
.digest("hex");
|
|
26
|
+
}
|
|
27
|
+
export function lineColumn(content, index) {
|
|
28
|
+
const before = content.slice(0, index);
|
|
29
|
+
const lines = before.split("\n");
|
|
30
|
+
return {
|
|
31
|
+
line: lines.length,
|
|
32
|
+
column: lines[lines.length - 1].length + 1,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function contextForLine(content, line, span = 2) {
|
|
36
|
+
const lines = content.split("\n");
|
|
37
|
+
const start = Math.max(0, line - span - 1);
|
|
38
|
+
const end = Math.min(lines.length, line + span);
|
|
39
|
+
return lines.slice(start, end).join("\n");
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/scanner/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,CAAC;IACrB,MAAM,OAAO,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;IAC3B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;QACpC,MAAM,WAAW,GAAG,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC;QAC3C,OAAO,IAAI,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,YAAY,CAAC;IAC3C,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAMlC;IACC,OAAO,UAAU,CAAC,QAAQ,CAAC;SACxB,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;SACpF,MAAM,CAAC,KAAK,CAAC,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAe,EAAE,KAAa;IACvD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,MAAM;QAClB,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC;KAC3C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,IAAY,EAAE,IAAI,GAAG,CAAC;IACpE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,CAAC,CAAC;IAChD,OAAO,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5C,CAAC"}
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TableSchema } from "@superfunctions/db";
|
|
2
|
+
export declare const SECFN_SCHEMA_VERSION = 4;
|
|
3
|
+
export declare function getSecFnSchema(): TableSchema[];
|
|
4
|
+
export interface SecFnSchemaDefinition {
|
|
5
|
+
version: number;
|
|
6
|
+
schemas: TableSchema[];
|
|
7
|
+
}
|
|
8
|
+
export declare function getSchema(): SecFnSchemaDefinition;
|
|
9
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,wBAAgB,cAAc,IAAI,WAAW,EAAE,CAiO9C;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AAED,wBAAgB,SAAS,IAAI,qBAAqB,CAKjD"}
|