toolip 1.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/LICENSE +20 -0
- package/dist/src/commands/alternatives.d.ts +2 -0
- package/dist/src/commands/alternatives.js +17 -0
- package/dist/src/commands/alternatives.js.map +1 -0
- package/dist/src/commands/compare.d.ts +2 -0
- package/dist/src/commands/compare.js +19 -0
- package/dist/src/commands/compare.js.map +1 -0
- package/dist/src/commands/doctor.d.ts +2 -0
- package/dist/src/commands/doctor.js +42 -0
- package/dist/src/commands/doctor.js.map +1 -0
- package/dist/src/commands/git-audit.d.ts +2 -0
- package/dist/src/commands/git-audit.js +37 -0
- package/dist/src/commands/git-audit.js.map +1 -0
- package/dist/src/commands/hook.d.ts +2 -0
- package/dist/src/commands/hook.js +16 -0
- package/dist/src/commands/hook.js.map +1 -0
- package/dist/src/commands/inspect.d.ts +2 -0
- package/dist/src/commands/inspect.js +24 -0
- package/dist/src/commands/inspect.js.map +1 -0
- package/dist/src/commands/learn.d.ts +2 -0
- package/dist/src/commands/learn.js +42 -0
- package/dist/src/commands/learn.js.map +1 -0
- package/dist/src/commands/licenses.d.ts +2 -0
- package/dist/src/commands/licenses.js +40 -0
- package/dist/src/commands/licenses.js.map +1 -0
- package/dist/src/commands/pre-commit.d.ts +2 -0
- package/dist/src/commands/pre-commit.js +37 -0
- package/dist/src/commands/pre-commit.js.map +1 -0
- package/dist/src/commands/profile.d.ts +2 -0
- package/dist/src/commands/profile.js +18 -0
- package/dist/src/commands/profile.js.map +1 -0
- package/dist/src/commands/scan.d.ts +2 -0
- package/dist/src/commands/scan.js +48 -0
- package/dist/src/commands/scan.js.map +1 -0
- package/dist/src/commands/score.d.ts +2 -0
- package/dist/src/commands/score.js +24 -0
- package/dist/src/commands/score.js.map +1 -0
- package/dist/src/commands/self-test.d.ts +2 -0
- package/dist/src/commands/self-test.js +63 -0
- package/dist/src/commands/self-test.js.map +1 -0
- package/dist/src/commands/tree.d.ts +2 -0
- package/dist/src/commands/tree.js +21 -0
- package/dist/src/commands/tree.js.map +1 -0
- package/dist/src/commands/vault.d.ts +2 -0
- package/dist/src/commands/vault.js +86 -0
- package/dist/src/commands/vault.js.map +1 -0
- package/dist/src/config/version.d.ts +6 -0
- package/dist/src/config/version.js +7 -0
- package/dist/src/config/version.js.map +1 -0
- package/dist/src/core/alternatives.d.ts +8 -0
- package/dist/src/core/alternatives.js +35 -0
- package/dist/src/core/alternatives.js.map +1 -0
- package/dist/src/core/analyze-package.d.ts +2 -0
- package/dist/src/core/analyze-package.js +49 -0
- package/dist/src/core/analyze-package.js.map +1 -0
- package/dist/src/core/compare-packages.d.ts +7 -0
- package/dist/src/core/compare-packages.js +19 -0
- package/dist/src/core/compare-packages.js.map +1 -0
- package/dist/src/core/dependency-scan.d.ts +17 -0
- package/dist/src/core/dependency-scan.js +70 -0
- package/dist/src/core/dependency-scan.js.map +1 -0
- package/dist/src/core/dependency-tree.d.ts +16 -0
- package/dist/src/core/dependency-tree.js +19 -0
- package/dist/src/core/dependency-tree.js.map +1 -0
- package/dist/src/core/dependency-types.d.ts +17 -0
- package/dist/src/core/dependency-types.js +2 -0
- package/dist/src/core/dependency-types.js.map +1 -0
- package/dist/src/core/file-walker.d.ts +6 -0
- package/dist/src/core/file-walker.js +27 -0
- package/dist/src/core/file-walker.js.map +1 -0
- package/dist/src/core/git-audit.d.ts +12 -0
- package/dist/src/core/git-audit.js +111 -0
- package/dist/src/core/git-audit.js.map +1 -0
- package/dist/src/core/hooks.d.ts +1 -0
- package/dist/src/core/hooks.js +14 -0
- package/dist/src/core/hooks.js.map +1 -0
- package/dist/src/core/learn.d.ts +11 -0
- package/dist/src/core/learn.js +163 -0
- package/dist/src/core/learn.js.map +1 -0
- package/dist/src/core/license-analysis.d.ts +18 -0
- package/dist/src/core/license-analysis.js +98 -0
- package/dist/src/core/license-analysis.js.map +1 -0
- package/dist/src/core/load-toolip-ignore.d.ts +5 -0
- package/dist/src/core/load-toolip-ignore.js +35 -0
- package/dist/src/core/load-toolip-ignore.js.map +1 -0
- package/dist/src/core/npm-registry.d.ts +3 -0
- package/dist/src/core/npm-registry.js +5 -0
- package/dist/src/core/npm-registry.js.map +1 -0
- package/dist/src/core/pre-commit.d.ts +11 -0
- package/dist/src/core/pre-commit.js +22 -0
- package/dist/src/core/pre-commit.js.map +1 -0
- package/dist/src/core/profile-project.d.ts +23 -0
- package/dist/src/core/profile-project.js +119 -0
- package/dist/src/core/profile-project.js.map +1 -0
- package/dist/src/core/read-dependencies.d.ts +2 -0
- package/dist/src/core/read-dependencies.js +24 -0
- package/dist/src/core/read-dependencies.js.map +1 -0
- package/dist/src/core/report-writer.d.ts +5 -0
- package/dist/src/core/report-writer.js +61 -0
- package/dist/src/core/report-writer.js.map +1 -0
- package/dist/src/core/report.d.ts +34 -0
- package/dist/src/core/report.js +26 -0
- package/dist/src/core/report.js.map +1 -0
- package/dist/src/core/risk-score.d.ts +5 -0
- package/dist/src/core/risk-score.js +14 -0
- package/dist/src/core/risk-score.js.map +1 -0
- package/dist/src/core/scanner-context.d.ts +12 -0
- package/dist/src/core/scanner-context.js +27 -0
- package/dist/src/core/scanner-context.js.map +1 -0
- package/dist/src/core/score.d.ts +13 -0
- package/dist/src/core/score.js +44 -0
- package/dist/src/core/score.js.map +1 -0
- package/dist/src/core/security-doctor.d.ts +12 -0
- package/dist/src/core/security-doctor.js +106 -0
- package/dist/src/core/security-doctor.js.map +1 -0
- package/dist/src/core/security-patterns.d.ts +14 -0
- package/dist/src/core/security-patterns.js +130 -0
- package/dist/src/core/security-patterns.js.map +1 -0
- package/dist/src/core/typosquat.d.ts +6 -0
- package/dist/src/core/typosquat.js +50 -0
- package/dist/src/core/typosquat.js.map +1 -0
- package/dist/src/core/vault.d.ts +48 -0
- package/dist/src/core/vault.js +127 -0
- package/dist/src/core/vault.js.map +1 -0
- package/dist/src/errors/toolip-error.d.ts +8 -0
- package/dist/src/errors/toolip-error.js +11 -0
- package/dist/src/errors/toolip-error.js.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +51 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/utils/error-handler.d.ts +1 -0
- package/dist/src/utils/error-handler.js +16 -0
- package/dist/src/utils/error-handler.js.map +1 -0
- package/dist/src/utils/output.d.ts +6 -0
- package/dist/src/utils/output.js +72 -0
- package/dist/src/utils/output.js.map +1 -0
- package/dist/src/utils/package-output.d.ts +4 -0
- package/dist/src/utils/package-output.js +40 -0
- package/dist/src/utils/package-output.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export function detectReportFormat(outputPath) {
|
|
4
|
+
const extension = path.extname(outputPath).toLowerCase();
|
|
5
|
+
if (extension === '.md' || extension === '.markdown') {
|
|
6
|
+
return 'md';
|
|
7
|
+
}
|
|
8
|
+
return 'json';
|
|
9
|
+
}
|
|
10
|
+
export async function writeReport(outputPath, report) {
|
|
11
|
+
const format = detectReportFormat(outputPath);
|
|
12
|
+
if (format === 'md') {
|
|
13
|
+
await writeFile(outputPath, renderMarkdownReport(report), 'utf8');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
await writeFile(outputPath, `${JSON.stringify(report, null, 2)}\n`, 'utf8');
|
|
17
|
+
}
|
|
18
|
+
export function renderMarkdownReport(report) {
|
|
19
|
+
const findings = report.findings.length
|
|
20
|
+
? report.findings
|
|
21
|
+
.map((finding) => {
|
|
22
|
+
const fileLine = finding.file ? `\n- File: \`${finding.file}\`` : '';
|
|
23
|
+
const evidenceLine = finding.evidence ? `\n- Evidence: \`${finding.evidence}\`` : '';
|
|
24
|
+
return `### ${finding.title}
|
|
25
|
+
|
|
26
|
+
- ID: \`${finding.id}\`
|
|
27
|
+
- Severity: **${finding.severity.toUpperCase()}**
|
|
28
|
+
- Category: ${finding.category}${fileLine}${evidenceLine}
|
|
29
|
+
|
|
30
|
+
${finding.message}
|
|
31
|
+
|
|
32
|
+
**Recommendation:** ${finding.recommendation}
|
|
33
|
+
`;
|
|
34
|
+
})
|
|
35
|
+
.join('\n')
|
|
36
|
+
: 'No findings were detected.';
|
|
37
|
+
return `# Toolip Report
|
|
38
|
+
|
|
39
|
+
Generated: ${report.generatedAt}
|
|
40
|
+
|
|
41
|
+
Root: \`${report.root}\`
|
|
42
|
+
|
|
43
|
+
Command: \`${report.command}\`
|
|
44
|
+
|
|
45
|
+
## Summary
|
|
46
|
+
|
|
47
|
+
| Severity | Count |
|
|
48
|
+
|---|---:|
|
|
49
|
+
| Critical | ${report.summary.critical} |
|
|
50
|
+
| High | ${report.summary.high} |
|
|
51
|
+
| Medium | ${report.summary.medium} |
|
|
52
|
+
| Low | ${report.summary.low} |
|
|
53
|
+
| Info | ${report.summary.info} |
|
|
54
|
+
| Total | ${report.summary.totalFindings} |
|
|
55
|
+
|
|
56
|
+
## Findings
|
|
57
|
+
|
|
58
|
+
${findings}
|
|
59
|
+
`;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=report-writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-writer.js","sourceRoot":"","sources":["../../../src/core/report-writer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,IAAI,MAAM,WAAW,CAAC;AAK7B,MAAM,UAAU,kBAAkB,CAAC,UAAkB;IACnD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;IAEzD,IAAI,SAAS,KAAK,KAAK,IAAI,SAAS,KAAK,WAAW,EAAE,CAAC;QACrD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,UAAkB,EAAE,MAAoB;IACxE,MAAM,MAAM,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAE9C,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,SAAS,CAAC,UAAU,EAAE,oBAAoB,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;QAClE,OAAO;IACT,CAAC;IAED,MAAM,SAAS,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAoB;IACvD,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM;QACrC,CAAC,CAAC,MAAM,CAAC,QAAQ;aACZ,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;YACf,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACrE,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,mBAAmB,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAErF,OAAO,OAAO,OAAO,CAAC,KAAK;;UAE3B,OAAO,CAAC,EAAE;gBACJ,OAAO,CAAC,QAAQ,CAAC,WAAW,EAAE;cAChC,OAAO,CAAC,QAAQ,GAAG,QAAQ,GAAG,YAAY;;EAEtD,OAAO,CAAC,OAAO;;sBAEK,OAAO,CAAC,cAAc;CAC3C,CAAC;QACM,CAAC,CAAC;aACD,IAAI,CAAC,IAAI,CAAC;QACf,CAAC,CAAC,4BAA4B,CAAC;IAEjC,OAAO;;aAEI,MAAM,CAAC,WAAW;;UAErB,MAAM,CAAC,IAAI;;aAER,MAAM,CAAC,OAAO;;;;;;eAMZ,MAAM,CAAC,OAAO,CAAC,QAAQ;WAC3B,MAAM,CAAC,OAAO,CAAC,IAAI;aACjB,MAAM,CAAC,OAAO,CAAC,MAAM;UACxB,MAAM,CAAC,OAAO,CAAC,GAAG;WACjB,MAAM,CAAC,OAAO,CAAC,IAAI;YAClB,MAAM,CAAC,OAAO,CAAC,aAAa;;;;EAItC,QAAQ;CACT,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type FindingSeverity = 'info' | 'low' | 'medium' | 'high' | 'critical';
|
|
2
|
+
export type ToolipFinding = {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
severity: FindingSeverity;
|
|
6
|
+
category: string;
|
|
7
|
+
message: string;
|
|
8
|
+
recommendation: string;
|
|
9
|
+
file?: string;
|
|
10
|
+
evidence?: string;
|
|
11
|
+
};
|
|
12
|
+
export type ToolipReport = {
|
|
13
|
+
tool: 'toolip';
|
|
14
|
+
version: string;
|
|
15
|
+
command: string;
|
|
16
|
+
generatedAt: string;
|
|
17
|
+
root: string;
|
|
18
|
+
summary: {
|
|
19
|
+
totalFindings: number;
|
|
20
|
+
critical: number;
|
|
21
|
+
high: number;
|
|
22
|
+
medium: number;
|
|
23
|
+
low: number;
|
|
24
|
+
info: number;
|
|
25
|
+
};
|
|
26
|
+
findings: ToolipFinding[];
|
|
27
|
+
};
|
|
28
|
+
export declare function createReport(input: {
|
|
29
|
+
version: string;
|
|
30
|
+
command: string;
|
|
31
|
+
root: string;
|
|
32
|
+
findings: ToolipFinding[];
|
|
33
|
+
}): ToolipReport;
|
|
34
|
+
export declare function summarizeFindings(findings: ToolipFinding[]): ToolipReport['summary'];
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function createReport(input) {
|
|
2
|
+
return {
|
|
3
|
+
tool: 'toolip',
|
|
4
|
+
version: input.version,
|
|
5
|
+
command: input.command,
|
|
6
|
+
generatedAt: new Date().toISOString(),
|
|
7
|
+
root: input.root,
|
|
8
|
+
summary: summarizeFindings(input.findings),
|
|
9
|
+
findings: input.findings
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function summarizeFindings(findings) {
|
|
13
|
+
return findings.reduce((summary, finding) => {
|
|
14
|
+
summary.totalFindings += 1;
|
|
15
|
+
summary[finding.severity] += 1;
|
|
16
|
+
return summary;
|
|
17
|
+
}, {
|
|
18
|
+
totalFindings: 0,
|
|
19
|
+
critical: 0,
|
|
20
|
+
high: 0,
|
|
21
|
+
medium: 0,
|
|
22
|
+
low: 0,
|
|
23
|
+
info: 0
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/core/report.ts"],"names":[],"mappings":"AA8BA,MAAM,UAAU,YAAY,CAAC,KAK5B;IACC,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACrC,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,OAAO,EAAE,iBAAiB,CAAC,KAAK,CAAC,QAAQ,CAAC;QAC1C,QAAQ,EAAE,KAAK,CAAC,QAAQ;KACzB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAyB;IACzD,OAAO,QAAQ,CAAC,MAAM,CACpB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE;QACnB,OAAO,CAAC,aAAa,IAAI,CAAC,CAAC;QAC3B,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC/B,OAAO,OAAO,CAAC;IACjB,CAAC,EACD;QACE,aAAa,EAAE,CAAC;QAChB,QAAQ,EAAE,CAAC;QACX,IAAI,EAAE,CAAC;QACP,MAAM,EAAE,CAAC;QACT,GAAG,EAAE,CAAC;QACN,IAAI,EAAE,CAAC;KACR,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function calculateRiskScore(input) {
|
|
2
|
+
let score = 0;
|
|
3
|
+
if (input.deprecated)
|
|
4
|
+
score += 40;
|
|
5
|
+
if (input.maintainers === 0)
|
|
6
|
+
score += 30;
|
|
7
|
+
else if (input.maintainers === 1)
|
|
8
|
+
score += 15;
|
|
9
|
+
if (input.ageInDays !== null && input.ageInDays > 730) {
|
|
10
|
+
score += 20;
|
|
11
|
+
}
|
|
12
|
+
return Math.min(score, 100);
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=risk-score.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"risk-score.js","sourceRoot":"","sources":["../../../src/core/risk-score.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,kBAAkB,CAAC,KAIlC;IACC,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,IAAI,KAAK,CAAC,UAAU;QAAE,KAAK,IAAI,EAAE,CAAC;IAElC,IAAI,KAAK,CAAC,WAAW,KAAK,CAAC;QAAE,KAAK,IAAI,EAAE,CAAC;SACpC,IAAI,KAAK,CAAC,WAAW,KAAK,CAAC;QAAE,KAAK,IAAI,EAAE,CAAC;IAE9C,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI,IAAI,KAAK,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC;QACtD,KAAK,IAAI,EAAE,CAAC;IACd,CAAC;IAED,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ProjectFile } from './file-walker.js';
|
|
2
|
+
import { type ProjectProfile } from './profile-project.js';
|
|
3
|
+
export type ScannerContext = {
|
|
4
|
+
root: string;
|
|
5
|
+
profile: ProjectProfile;
|
|
6
|
+
files: ProjectFile[];
|
|
7
|
+
summary: {
|
|
8
|
+
totalFiles: number;
|
|
9
|
+
extensions: Record<string, number>;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export declare function createScannerContext(root: string): Promise<ScannerContext>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { walkProjectFiles } from './file-walker.js';
|
|
3
|
+
import { profileProject } from './profile-project.js';
|
|
4
|
+
function summarizeExtensions(files) {
|
|
5
|
+
return files.reduce((summary, file) => {
|
|
6
|
+
const key = file.extension || 'none';
|
|
7
|
+
summary[key] = (summary[key] ?? 0) + 1;
|
|
8
|
+
return summary;
|
|
9
|
+
}, {});
|
|
10
|
+
}
|
|
11
|
+
export async function createScannerContext(root) {
|
|
12
|
+
const absoluteRoot = path.resolve(root);
|
|
13
|
+
const [profile, files] = await Promise.all([
|
|
14
|
+
profileProject(absoluteRoot),
|
|
15
|
+
walkProjectFiles(absoluteRoot)
|
|
16
|
+
]);
|
|
17
|
+
return {
|
|
18
|
+
root: absoluteRoot,
|
|
19
|
+
profile,
|
|
20
|
+
files,
|
|
21
|
+
summary: {
|
|
22
|
+
totalFiles: files.length,
|
|
23
|
+
extensions: summarizeExtensions(files)
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=scanner-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner-context.js","sourceRoot":"","sources":["../../../src/core/scanner-context.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAoB,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,cAAc,EAAuB,MAAM,sBAAsB,CAAC;AAY3E,SAAS,mBAAmB,CAAC,KAAoB;IAC/C,OAAO,KAAK,CAAC,MAAM,CAAyB,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE;QAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACvC,OAAO,OAAO,CAAC;IACjB,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,IAAY;IACrD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACzC,cAAc,CAAC,YAAY,CAAC;QAC5B,gBAAgB,CAAC,YAAY,CAAC;KAC/B,CAAC,CAAC;IAEH,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,OAAO;QACP,KAAK;QACL,OAAO,EAAE;YACP,UAAU,EAAE,KAAK,CAAC,MAAM;YACxB,UAAU,EAAE,mBAAmB,CAAC,KAAK,CAAC;SACvC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ToolipFinding } from './report.js';
|
|
2
|
+
export type ScoreGrade = 'A' | 'B' | 'C' | 'D' | 'F';
|
|
3
|
+
export type ToolipScore = {
|
|
4
|
+
dependencyHealth: number;
|
|
5
|
+
secretHygiene: number;
|
|
6
|
+
configurationSecurity: number;
|
|
7
|
+
gitSafety: number;
|
|
8
|
+
overall: number;
|
|
9
|
+
grade: ScoreGrade;
|
|
10
|
+
};
|
|
11
|
+
export declare function calculateScore(input?: Partial<Omit<ToolipScore, 'overall' | 'grade'>>): ToolipScore;
|
|
12
|
+
export declare function calculateDependencyHealth(findings: ToolipFinding[]): number;
|
|
13
|
+
export declare function gradeScore(score: number): ScoreGrade;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export function calculateScore(input) {
|
|
2
|
+
const dependencyHealth = clamp(input?.dependencyHealth ?? 100);
|
|
3
|
+
const secretHygiene = clamp(input?.secretHygiene ?? 100);
|
|
4
|
+
const configurationSecurity = clamp(input?.configurationSecurity ?? 100);
|
|
5
|
+
const gitSafety = clamp(input?.gitSafety ?? 100);
|
|
6
|
+
const overall = Math.round((dependencyHealth + secretHygiene + configurationSecurity + gitSafety) / 4);
|
|
7
|
+
return {
|
|
8
|
+
dependencyHealth,
|
|
9
|
+
secretHygiene,
|
|
10
|
+
configurationSecurity,
|
|
11
|
+
gitSafety,
|
|
12
|
+
overall,
|
|
13
|
+
grade: gradeScore(overall)
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function calculateDependencyHealth(findings) {
|
|
17
|
+
const penalty = findings.reduce((total, finding) => {
|
|
18
|
+
if (finding.severity === 'critical')
|
|
19
|
+
return total + 35;
|
|
20
|
+
if (finding.severity === 'high')
|
|
21
|
+
return total + 25;
|
|
22
|
+
if (finding.severity === 'medium')
|
|
23
|
+
return total + 12;
|
|
24
|
+
if (finding.severity === 'low')
|
|
25
|
+
return total + 5;
|
|
26
|
+
return total + 0;
|
|
27
|
+
}, 0);
|
|
28
|
+
return clamp(100 - penalty);
|
|
29
|
+
}
|
|
30
|
+
function clamp(value) {
|
|
31
|
+
return Math.max(0, Math.min(100, Math.round(value)));
|
|
32
|
+
}
|
|
33
|
+
export function gradeScore(score) {
|
|
34
|
+
if (score >= 90)
|
|
35
|
+
return 'A';
|
|
36
|
+
if (score >= 80)
|
|
37
|
+
return 'B';
|
|
38
|
+
if (score >= 70)
|
|
39
|
+
return 'C';
|
|
40
|
+
if (score >= 60)
|
|
41
|
+
return 'D';
|
|
42
|
+
return 'F';
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=score.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score.js","sourceRoot":"","sources":["../../../src/core/score.ts"],"names":[],"mappings":"AAaA,MAAM,UAAU,cAAc,CAAC,KAAuD;IACpF,MAAM,gBAAgB,GAAG,KAAK,CAAC,KAAK,EAAE,gBAAgB,IAAI,GAAG,CAAC,CAAC;IAC/D,MAAM,aAAa,GAAG,KAAK,CAAC,KAAK,EAAE,aAAa,IAAI,GAAG,CAAC,CAAC;IACzD,MAAM,qBAAqB,GAAG,KAAK,CAAC,KAAK,EAAE,qBAAqB,IAAI,GAAG,CAAC,CAAC;IACzE,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,EAAE,SAAS,IAAI,GAAG,CAAC,CAAC;IAEjD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CACxB,CAAC,gBAAgB,GAAG,aAAa,GAAG,qBAAqB,GAAG,SAAS,CAAC,GAAG,CAAC,CAC3E,CAAC;IAEF,OAAO;QACL,gBAAgB;QAChB,aAAa;QACb,qBAAqB;QACrB,SAAS;QACT,OAAO;QACP,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC;KAC3B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,QAAyB;IACjE,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QACjD,IAAI,OAAO,CAAC,QAAQ,KAAK,UAAU;YAAE,OAAO,KAAK,GAAG,EAAE,CAAC;QACvD,IAAI,OAAO,CAAC,QAAQ,KAAK,MAAM;YAAE,OAAO,KAAK,GAAG,EAAE,CAAC;QACnD,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ;YAAE,OAAO,KAAK,GAAG,EAAE,CAAC;QACrD,IAAI,OAAO,CAAC,QAAQ,KAAK,KAAK;YAAE,OAAO,KAAK,GAAG,CAAC,CAAC;QACjD,OAAO,KAAK,GAAG,CAAC,CAAC;IACnB,CAAC,EAAE,CAAC,CAAC,CAAC;IAEN,OAAO,KAAK,CAAC,GAAG,GAAG,OAAO,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,KAAK,CAAC,KAAa;IAC1B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,GAAG,CAAC;IAC5B,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,GAAG,CAAC;IAC5B,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,GAAG,CAAC;IAC5B,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,GAAG,CAAC;IAC5B,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ToolipFinding } from './report.js';
|
|
2
|
+
export type SecurityDoctorResult = {
|
|
3
|
+
findings: ToolipFinding[];
|
|
4
|
+
summary: {
|
|
5
|
+
filesScanned: number;
|
|
6
|
+
secrets: number;
|
|
7
|
+
dangerousCode: number;
|
|
8
|
+
configuration: number;
|
|
9
|
+
headers: number;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export declare function runSecurityDoctor(root: string): Promise<SecurityDoctorResult>;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { createScannerContext } from './scanner-context.js';
|
|
3
|
+
import { configSecurityPatterns, dangerousCodePatterns, secretPatterns, securityHeaderNames } from './security-patterns.js';
|
|
4
|
+
const scanExtensions = new Set([
|
|
5
|
+
'js',
|
|
6
|
+
'jsx',
|
|
7
|
+
'ts',
|
|
8
|
+
'tsx',
|
|
9
|
+
'json',
|
|
10
|
+
'yml',
|
|
11
|
+
'yaml',
|
|
12
|
+
'env',
|
|
13
|
+
'txt',
|
|
14
|
+
'pem',
|
|
15
|
+
'key'
|
|
16
|
+
]);
|
|
17
|
+
export async function runSecurityDoctor(root) {
|
|
18
|
+
const context = await createScannerContext(root);
|
|
19
|
+
const findings = [];
|
|
20
|
+
for (const file of context.files) {
|
|
21
|
+
if (!shouldScan(file.relativePath, file.extension))
|
|
22
|
+
continue;
|
|
23
|
+
let content = '';
|
|
24
|
+
try {
|
|
25
|
+
content = await readFile(file.absolutePath, 'utf8');
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
findings.push(...scanContent(file.relativePath, content, secretPatterns));
|
|
31
|
+
findings.push(...scanContent(file.relativePath, content, dangerousCodePatterns));
|
|
32
|
+
findings.push(...scanContent(file.relativePath, content, configSecurityPatterns));
|
|
33
|
+
}
|
|
34
|
+
findings.push(...detectMissingSecurityHeaders(context.files.map((file) => file.relativePath)));
|
|
35
|
+
return {
|
|
36
|
+
findings,
|
|
37
|
+
summary: {
|
|
38
|
+
filesScanned: context.files.length,
|
|
39
|
+
secrets: findings.filter((finding) => finding.category === 'secrets').length,
|
|
40
|
+
dangerousCode: findings.filter((finding) => finding.category === 'dangerous-code').length,
|
|
41
|
+
configuration: findings.filter((finding) => finding.category === 'configuration').length,
|
|
42
|
+
headers: findings.filter((finding) => finding.category === 'security-headers').length
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function shouldScan(relativePath, extension) {
|
|
47
|
+
if (relativePath.endsWith('.d.ts'))
|
|
48
|
+
return false;
|
|
49
|
+
if (relativePath.endsWith('.map'))
|
|
50
|
+
return false;
|
|
51
|
+
if (relativePath.startsWith('dist/'))
|
|
52
|
+
return false;
|
|
53
|
+
if (relativePath.endsWith('.env'))
|
|
54
|
+
return true;
|
|
55
|
+
if (relativePath.includes('.env.'))
|
|
56
|
+
return true;
|
|
57
|
+
if (scanExtensions.has(extension))
|
|
58
|
+
return true;
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
function scanContent(relativePath, content, patterns) {
|
|
62
|
+
const findings = [];
|
|
63
|
+
for (const pattern of patterns) {
|
|
64
|
+
pattern.regex.lastIndex = 0;
|
|
65
|
+
const matches = content.match(pattern.regex);
|
|
66
|
+
if (!matches)
|
|
67
|
+
continue;
|
|
68
|
+
findings.push({
|
|
69
|
+
id: `${pattern.id}-${relativePath.toUpperCase().replaceAll(/[^A-Z0-9]/g, '-')}`,
|
|
70
|
+
title: pattern.title,
|
|
71
|
+
severity: pattern.severity,
|
|
72
|
+
category: pattern.category,
|
|
73
|
+
message: pattern.message,
|
|
74
|
+
recommendation: pattern.recommendation,
|
|
75
|
+
file: relativePath,
|
|
76
|
+
evidence: redactEvidence(matches[0])
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return findings;
|
|
80
|
+
}
|
|
81
|
+
function redactEvidence(value) {
|
|
82
|
+
if (value.length <= 16)
|
|
83
|
+
return value;
|
|
84
|
+
return `${value.slice(0, 8)}...[redacted]...${value.slice(-4)}`;
|
|
85
|
+
}
|
|
86
|
+
function detectMissingSecurityHeaders(relativePaths) {
|
|
87
|
+
const possibleServerFiles = relativePaths.filter((file) => !file.startsWith('dist/') &&
|
|
88
|
+
/server|app|main|index|middleware/i.test(file) &&
|
|
89
|
+
/\.(js|ts|jsx|tsx)$/.test(file));
|
|
90
|
+
if (possibleServerFiles.length === 0) {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
const findings = [];
|
|
94
|
+
for (const header of securityHeaderNames) {
|
|
95
|
+
findings.push({
|
|
96
|
+
id: `TOOLIP-HEADER-VERIFY-${header.toUpperCase().replaceAll('-', '_')}`,
|
|
97
|
+
title: `Verify security header: ${header}`,
|
|
98
|
+
severity: 'info',
|
|
99
|
+
category: 'security-headers',
|
|
100
|
+
message: `Toolip found server-like files. Confirm that ${header} is configured in production responses.`,
|
|
101
|
+
recommendation: 'Use Helmet or equivalent framework middleware to set secure HTTP response headers.'
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return findings;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=security-doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-doctor.js","sourceRoot":"","sources":["../../../src/core/security-doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,cAAc,EACd,mBAAmB,EAEpB,MAAM,wBAAwB,CAAC;AAahC,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IAC7B,IAAI;IACJ,KAAK;IACL,IAAI;IACJ,KAAK;IACL,MAAM;IACN,KAAK;IACL,MAAM;IACN,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;CACN,CAAC,CAAC;AAEH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY;IAClD,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAoB,EAAE,CAAC;IAErC,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QACjC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC;YAAE,SAAS;QAE7D,IAAI,OAAO,GAAG,EAAE,CAAC;QAEjB,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,QAAQ,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC;QAC1E,QAAQ,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,qBAAqB,CAAC,CAAC,CAAC;QACjF,QAAQ,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,sBAAsB,CAAC,CAAC,CAAC;IACpF,CAAC;IAED,QAAQ,CAAC,IAAI,CAAC,GAAG,4BAA4B,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAE/F,OAAO;QACL,QAAQ;QACR,OAAO,EAAE;YACP,YAAY,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM;YAClC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,MAAM;YAC5E,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,KAAK,gBAAgB,CAAC,CAAC,MAAM;YACzF,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,KAAK,eAAe,CAAC,CAAC,MAAM;YACxF,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,KAAK,kBAAkB,CAAC,CAAC,MAAM;SACtF;KACF,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,YAAoB,EAAE,SAAiB;IACzD,IAAI,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IACjD,IAAI,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IAChD,IAAI,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IACnD,IAAI,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,IAAI,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAChD,IAAI,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,WAAW,CAClB,YAAoB,EACpB,OAAe,EACf,QAA2B;IAE3B,MAAM,QAAQ,GAAoB,EAAE,CAAC;IAErC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,OAAO,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC;QAC5B,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAE7C,IAAI,CAAC,OAAO;YAAE,SAAS;QAEvB,QAAQ,CAAC,IAAI,CAAC;YACZ,EAAE,EAAE,GAAG,OAAO,CAAC,EAAE,IAAI,YAAY,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,YAAY,EAAE,GAAG,CAAC,EAAE;YAC/E,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,cAAc,EAAE,OAAO,CAAC,cAAc;YACtC,IAAI,EAAE,YAAY;YAClB,QAAQ,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;SACrC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,cAAc,CAAC,KAAa;IACnC,IAAI,KAAK,CAAC,MAAM,IAAI,EAAE;QAAE,OAAO,KAAK,CAAC;IACrC,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,mBAAmB,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAClE,CAAC;AAED,SAAS,4BAA4B,CAAC,aAAuB;IAC3D,MAAM,mBAAmB,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CACxD,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QACzB,mCAAmC,CAAC,IAAI,CAAC,IAAI,CAAC;QAC9C,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CAChC,CAAC;IAEF,IAAI,mBAAmB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,QAAQ,GAAoB,EAAE,CAAC;IAErC,KAAK,MAAM,MAAM,IAAI,mBAAmB,EAAE,CAAC;QACzC,QAAQ,CAAC,IAAI,CAAC;YACZ,EAAE,EAAE,wBAAwB,MAAM,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE;YACvE,KAAK,EAAE,2BAA2B,MAAM,EAAE;YAC1C,QAAQ,EAAE,MAAM;YAChB,QAAQ,EAAE,kBAAkB;YAC5B,OAAO,EAAE,gDAAgD,MAAM,yCAAyC;YACxG,cAAc,EAAE,oFAAoF;SACrG,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ToolipFinding } from './report.js';
|
|
2
|
+
export type SecurityPattern = {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
category: string;
|
|
6
|
+
severity: ToolipFinding['severity'];
|
|
7
|
+
regex: RegExp;
|
|
8
|
+
message: string;
|
|
9
|
+
recommendation: string;
|
|
10
|
+
};
|
|
11
|
+
export declare const secretPatterns: SecurityPattern[];
|
|
12
|
+
export declare const dangerousCodePatterns: SecurityPattern[];
|
|
13
|
+
export declare const configSecurityPatterns: SecurityPattern[];
|
|
14
|
+
export declare const securityHeaderNames: string[];
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
export const secretPatterns = [
|
|
2
|
+
{
|
|
3
|
+
id: 'TOOLIP-SECRET-GITHUB-TOKEN',
|
|
4
|
+
title: 'GitHub token detected',
|
|
5
|
+
category: 'secrets',
|
|
6
|
+
severity: 'critical',
|
|
7
|
+
regex: /\bgh[pousr]_[A-Za-z0-9_]{20,}\b/g,
|
|
8
|
+
message: 'A GitHub token-like secret was found in source files.',
|
|
9
|
+
recommendation: 'Revoke the token immediately, remove it from history, and move it into a secure secret store.'
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
id: 'TOOLIP-SECRET-AWS-ACCESS-KEY',
|
|
13
|
+
title: 'AWS access key detected',
|
|
14
|
+
category: 'secrets',
|
|
15
|
+
severity: 'critical',
|
|
16
|
+
regex: /\bAKIA[0-9A-Z]{16}\b/g,
|
|
17
|
+
message: 'An AWS access key-like value was found in source files.',
|
|
18
|
+
recommendation: 'Rotate the key immediately and store credentials outside source control.'
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: 'TOOLIP-SECRET-PRIVATE-KEY',
|
|
22
|
+
title: 'Private key detected',
|
|
23
|
+
category: 'secrets',
|
|
24
|
+
severity: 'critical',
|
|
25
|
+
regex: /-----BEGIN (RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----/g,
|
|
26
|
+
message: 'Private key material was found.',
|
|
27
|
+
recommendation: 'Remove key material and rotate affected credentials.'
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 'TOOLIP-SECRET-NPM-TOKEN',
|
|
31
|
+
title: 'npm token detected',
|
|
32
|
+
category: 'secrets',
|
|
33
|
+
severity: 'critical',
|
|
34
|
+
regex: /\bnpm_[A-Za-z0-9]{20,}\b/g,
|
|
35
|
+
message: 'An npm token-like value was detected.',
|
|
36
|
+
recommendation: 'Rotate the token and move it into a secure secret store.'
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 'TOOLIP-SECRET-HARDCODED-PASSWORD',
|
|
40
|
+
title: 'Hardcoded password detected',
|
|
41
|
+
category: 'secrets',
|
|
42
|
+
severity: 'high',
|
|
43
|
+
regex: /\b(password|passwd|pwd)\s*[:=]\s*['"][^'"]{8,}['"]/gi,
|
|
44
|
+
message: 'A hardcoded password-like assignment was found.',
|
|
45
|
+
recommendation: 'Move passwords into environment variables or Toolip Vault.'
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: 'TOOLIP-SECRET-JWT-SECRET',
|
|
49
|
+
title: 'Hardcoded JWT secret detected',
|
|
50
|
+
category: 'secrets',
|
|
51
|
+
severity: 'high',
|
|
52
|
+
regex: /\b(jwtSecret|JWT_SECRET|jwt_secret)\s*[:=]\s*['"][^'"]{8,}['"]/g,
|
|
53
|
+
message: 'A hardcoded JWT secret was found.',
|
|
54
|
+
recommendation: 'Use a strong secret from an environment variable or secret manager.'
|
|
55
|
+
}
|
|
56
|
+
];
|
|
57
|
+
export const dangerousCodePatterns = [
|
|
58
|
+
{
|
|
59
|
+
id: 'TOOLIP-DANGEROUS-EVAL',
|
|
60
|
+
title: 'Dangerous eval usage',
|
|
61
|
+
category: 'dangerous-code',
|
|
62
|
+
severity: 'high',
|
|
63
|
+
regex: /\beval\s*\(/g,
|
|
64
|
+
message: 'eval() can execute arbitrary code.',
|
|
65
|
+
recommendation: 'Replace eval() with safer alternatives.'
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'TOOLIP-DANGEROUS-NEW-FUNCTION',
|
|
69
|
+
title: 'Dangerous Function constructor usage',
|
|
70
|
+
category: 'dangerous-code',
|
|
71
|
+
severity: 'high',
|
|
72
|
+
regex: /\bnew\s+Function\s*\(/g,
|
|
73
|
+
message: 'new Function() executes dynamic code.',
|
|
74
|
+
recommendation: 'Avoid dynamic code execution.'
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: 'TOOLIP-DANGEROUS-CHILD-PROCESS-EXEC',
|
|
78
|
+
title: 'Unsafe child_process exec usage',
|
|
79
|
+
category: 'dangerous-code',
|
|
80
|
+
severity: 'high',
|
|
81
|
+
regex: /\bexec\s*\(/g,
|
|
82
|
+
message: 'exec() can expose shell injection risks.',
|
|
83
|
+
recommendation: 'Use execFile/spawn with validated arguments.'
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: 'TOOLIP-DANGEROUS-EXECSYNC',
|
|
87
|
+
title: 'Unsafe execSync usage',
|
|
88
|
+
category: 'dangerous-code',
|
|
89
|
+
severity: 'high',
|
|
90
|
+
regex: /\bexecSync\s*\(/g,
|
|
91
|
+
message: 'execSync() can expose shell injection risks.',
|
|
92
|
+
recommendation: 'Avoid shell execution where possible.'
|
|
93
|
+
}
|
|
94
|
+
];
|
|
95
|
+
export const configSecurityPatterns = [
|
|
96
|
+
{
|
|
97
|
+
id: 'TOOLIP-CONFIG-OPEN-CORS',
|
|
98
|
+
title: 'Open CORS policy detected',
|
|
99
|
+
category: 'configuration',
|
|
100
|
+
severity: 'medium',
|
|
101
|
+
regex: /\borigin\s*:\s*['"]\*['"]/g,
|
|
102
|
+
message: 'CORS origin is configured as "*".',
|
|
103
|
+
recommendation: 'Restrict origins to trusted domains.'
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: 'TOOLIP-CONFIG-WEAK-JWT-SECRET',
|
|
107
|
+
title: 'Weak JWT secret detected',
|
|
108
|
+
category: 'configuration',
|
|
109
|
+
severity: 'high',
|
|
110
|
+
regex: /\b(jwtSecret|JWT_SECRET|jwt_secret)\s*[:=]\s*['"](secret|changeme|password|123456|devsecret)['"]/gi,
|
|
111
|
+
message: 'A weak JWT secret value was detected.',
|
|
112
|
+
recommendation: 'Use a long, random secret.'
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: 'TOOLIP-CONFIG-LONG-JWT_EXPIRY',
|
|
116
|
+
title: 'Long-lived JWT expiry detected',
|
|
117
|
+
category: 'configuration',
|
|
118
|
+
severity: 'medium',
|
|
119
|
+
regex: /\b(expiresIn|JWT_EXPIRES_IN)\s*[:=]\s*['"](?:365d|999d|1000d|never|10y)['"]/gi,
|
|
120
|
+
message: 'A long-lived JWT expiry was detected.',
|
|
121
|
+
recommendation: 'Use shorter access-token lifetimes.'
|
|
122
|
+
}
|
|
123
|
+
];
|
|
124
|
+
export const securityHeaderNames = [
|
|
125
|
+
'content-security-policy',
|
|
126
|
+
'strict-transport-security',
|
|
127
|
+
'x-frame-options',
|
|
128
|
+
'x-content-type-options'
|
|
129
|
+
];
|
|
130
|
+
//# sourceMappingURL=security-patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-patterns.js","sourceRoot":"","sources":["../../../src/core/security-patterns.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const popularPackages = [
|
|
2
|
+
'express',
|
|
3
|
+
'fastify',
|
|
4
|
+
'react',
|
|
5
|
+
'vue',
|
|
6
|
+
'axios',
|
|
7
|
+
'got',
|
|
8
|
+
'lodash',
|
|
9
|
+
'typescript',
|
|
10
|
+
'commander',
|
|
11
|
+
'chalk',
|
|
12
|
+
'zod',
|
|
13
|
+
'prisma',
|
|
14
|
+
'vite',
|
|
15
|
+
'next',
|
|
16
|
+
'nestjs'
|
|
17
|
+
];
|
|
18
|
+
export function detectTyposquat(input) {
|
|
19
|
+
const normalized = input.trim().toLowerCase();
|
|
20
|
+
const suggestions = popularPackages
|
|
21
|
+
.map((candidate) => ({
|
|
22
|
+
candidate,
|
|
23
|
+
distance: levenshtein(normalized, candidate)
|
|
24
|
+
}))
|
|
25
|
+
.filter((item) => item.distance > 0 && item.distance <= 2)
|
|
26
|
+
.sort((a, b) => a.distance - b.distance)
|
|
27
|
+
.map((item) => item.candidate);
|
|
28
|
+
return {
|
|
29
|
+
input,
|
|
30
|
+
suspected: suggestions.length > 0,
|
|
31
|
+
suggestions
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function levenshtein(a, b) {
|
|
35
|
+
const matrix = Array.from({ length: a.length + 1 }, (_, row) => Array.from({ length: b.length + 1 }, (_, col) => {
|
|
36
|
+
if (row === 0)
|
|
37
|
+
return col;
|
|
38
|
+
if (col === 0)
|
|
39
|
+
return row;
|
|
40
|
+
return 0;
|
|
41
|
+
}));
|
|
42
|
+
for (let row = 1; row <= a.length; row += 1) {
|
|
43
|
+
for (let col = 1; col <= b.length; col += 1) {
|
|
44
|
+
const cost = a[row - 1] === b[col - 1] ? 0 : 1;
|
|
45
|
+
matrix[row][col] = Math.min(matrix[row - 1][col] + 1, matrix[row][col - 1] + 1, matrix[row - 1][col - 1] + cost);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return matrix[a.length][b.length];
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=typosquat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typosquat.js","sourceRoot":"","sources":["../../../src/core/typosquat.ts"],"names":[],"mappings":"AAMA,MAAM,eAAe,GAAG;IACtB,SAAS;IACT,SAAS;IACT,OAAO;IACP,KAAK;IACL,OAAO;IACP,KAAK;IACL,QAAQ;IACR,YAAY;IACZ,WAAW;IACX,OAAO;IACP,KAAK;IACL,QAAQ;IACR,MAAM;IACN,MAAM;IACN,QAAQ;CACT,CAAC;AAEF,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAE9C,MAAM,WAAW,GAAG,eAAe;SAChC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QACnB,SAAS;QACT,QAAQ,EAAE,WAAW,CAAC,UAAU,EAAE,SAAS,CAAC;KAC7C,CAAC,CAAC;SACF,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;SACzD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC;SACvC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAEjC,OAAO;QACL,KAAK;QACL,SAAS,EAAE,WAAW,CAAC,MAAM,GAAG,CAAC;QACjC,WAAW;KACZ,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,CAAS,EAAE,CAAS;IACvC,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAC7D,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE;QAC9C,IAAI,GAAG,KAAK,CAAC;YAAE,OAAO,GAAG,CAAC;QAC1B,IAAI,GAAG,KAAK,CAAC;YAAE,OAAO,GAAG,CAAC;QAC1B,OAAO,CAAC,CAAC;IACX,CAAC,CAAC,CACH,CAAC;IAEF,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QAC5C,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;YAC5C,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAE/C,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CACzB,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EACxB,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,EACxB,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,IAAI,CAChC,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AACpC,CAAC"}
|