guardrail-core 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/dist/__tests__/autopilot.test.d.ts +7 -0
- package/dist/__tests__/autopilot.test.d.ts.map +1 -0
- package/dist/__tests__/autopilot.test.js +156 -0
- package/dist/__tests__/tier-config.test.d.ts +9 -0
- package/dist/__tests__/tier-config.test.d.ts.map +1 -0
- package/dist/__tests__/tier-config.test.js +230 -0
- package/dist/__tests__/utils/hash-inline.test.d.ts +2 -0
- package/dist/__tests__/utils/hash-inline.test.d.ts.map +1 -0
- package/dist/__tests__/utils/hash-inline.test.js +62 -0
- package/dist/__tests__/utils/hash.test.d.ts +3 -0
- package/dist/__tests__/utils/hash.test.d.ts.map +1 -0
- package/dist/__tests__/utils/hash.test.js +95 -0
- package/dist/__tests__/utils/simple.test.d.ts +1 -0
- package/dist/__tests__/utils/simple.test.d.ts.map +1 -0
- package/dist/__tests__/utils/simple.test.js +10 -0
- package/dist/__tests__/utils/utils-simple.test.d.ts +1 -0
- package/dist/__tests__/utils/utils-simple.test.d.ts.map +1 -0
- package/dist/__tests__/utils/utils-simple.test.js +6 -0
- package/dist/__tests__/utils/utils.test.d.ts +15 -0
- package/dist/__tests__/utils/utils.test.d.ts.map +1 -0
- package/dist/__tests__/utils/utils.test.js +172 -0
- package/dist/autopilot/autopilot-runner.d.ts +33 -0
- package/dist/autopilot/autopilot-runner.d.ts.map +1 -0
- package/dist/autopilot/autopilot-runner.js +479 -0
- package/dist/autopilot/index.d.ts +6 -0
- package/dist/autopilot/index.d.ts.map +1 -0
- package/dist/autopilot/index.js +25 -0
- package/dist/autopilot/types.d.ts +102 -0
- package/dist/autopilot/types.d.ts.map +1 -0
- package/dist/autopilot/types.js +18 -0
- package/dist/cache/index.d.ts +7 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +22 -0
- package/dist/cache/redis-cache.d.ts +145 -0
- package/dist/cache/redis-cache.d.ts.map +1 -0
- package/dist/cache/redis-cache.js +459 -0
- package/dist/ci/github-actions.d.ts +77 -0
- package/dist/ci/github-actions.d.ts.map +1 -0
- package/dist/ci/github-actions.js +277 -0
- package/dist/ci/index.d.ts +12 -0
- package/dist/ci/index.d.ts.map +1 -0
- package/dist/ci/index.js +27 -0
- package/dist/ci/pre-commit.d.ts +65 -0
- package/dist/ci/pre-commit.d.ts.map +1 -0
- package/dist/ci/pre-commit.js +286 -0
- package/dist/entitlements.d.ts +149 -0
- package/dist/entitlements.d.ts.map +1 -0
- package/dist/entitlements.js +464 -0
- package/dist/env.d.ts +113 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +204 -0
- package/dist/fix-packs/__tests__/generate-fix-packs.test.d.ts +7 -0
- package/dist/fix-packs/__tests__/generate-fix-packs.test.d.ts.map +1 -0
- package/dist/fix-packs/__tests__/generate-fix-packs.test.js +250 -0
- package/dist/fix-packs/generate-fix-packs.d.ts +15 -0
- package/dist/fix-packs/generate-fix-packs.d.ts.map +1 -0
- package/dist/fix-packs/generate-fix-packs.js +505 -0
- package/dist/fix-packs/index.d.ts +8 -0
- package/dist/fix-packs/index.d.ts.map +1 -0
- package/dist/fix-packs/index.js +23 -0
- package/dist/fix-packs/types.d.ts +113 -0
- package/dist/fix-packs/types.d.ts.map +1 -0
- package/dist/fix-packs/types.js +71 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/metrics/prometheus.d.ts +99 -0
- package/dist/metrics/prometheus.d.ts.map +1 -0
- package/dist/metrics/prometheus.js +306 -0
- package/dist/quota-ledger.d.ts +119 -0
- package/dist/quota-ledger.d.ts.map +1 -0
- package/dist/quota-ledger.js +462 -0
- package/dist/rbac/__tests__/permissions.test.d.ts +8 -0
- package/dist/rbac/__tests__/permissions.test.d.ts.map +1 -0
- package/dist/rbac/__tests__/permissions.test.js +350 -0
- package/dist/rbac/index.d.ts +9 -0
- package/dist/rbac/index.d.ts.map +1 -0
- package/dist/rbac/index.js +32 -0
- package/dist/rbac/permissions.d.ts +71 -0
- package/dist/rbac/permissions.d.ts.map +1 -0
- package/dist/rbac/permissions.js +247 -0
- package/dist/rbac/types.d.ts +69 -0
- package/dist/rbac/types.d.ts.map +1 -0
- package/dist/rbac/types.js +213 -0
- package/dist/tier-config.d.ts +203 -0
- package/dist/tier-config.d.ts.map +1 -0
- package/dist/tier-config.js +675 -0
- package/dist/types.d.ts +365 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/utils.d.ts +36 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +127 -0
- package/dist/verified-autofix/__tests__/format-validator.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/format-validator.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/format-validator.test.js +285 -0
- package/dist/verified-autofix/__tests__/pipeline.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/pipeline.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/pipeline.test.js +389 -0
- package/dist/verified-autofix/__tests__/repo-fingerprint.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/repo-fingerprint.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/repo-fingerprint.test.js +236 -0
- package/dist/verified-autofix/__tests__/workspace.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/workspace.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/workspace.test.js +314 -0
- package/dist/verified-autofix/format-validator.d.ts +101 -0
- package/dist/verified-autofix/format-validator.d.ts.map +1 -0
- package/dist/verified-autofix/format-validator.js +446 -0
- package/dist/verified-autofix/index.d.ts +14 -0
- package/dist/verified-autofix/index.d.ts.map +1 -0
- package/dist/verified-autofix/index.js +39 -0
- package/dist/verified-autofix/pipeline.d.ts +68 -0
- package/dist/verified-autofix/pipeline.d.ts.map +1 -0
- package/dist/verified-autofix/pipeline.js +330 -0
- package/dist/verified-autofix/repo-fingerprint.d.ts +56 -0
- package/dist/verified-autofix/repo-fingerprint.d.ts.map +1 -0
- package/dist/verified-autofix/repo-fingerprint.js +396 -0
- package/dist/verified-autofix/workspace.d.ts +83 -0
- package/dist/verified-autofix/workspace.d.ts.map +1 -0
- package/dist/verified-autofix/workspace.js +454 -0
- package/dist/verified-autofix.d.ts +182 -0
- package/dist/verified-autofix.d.ts.map +1 -0
- package/dist/verified-autofix.js +1021 -0
- package/dist/visualization/dependency-graph.d.ts +79 -0
- package/dist/visualization/dependency-graph.d.ts.map +1 -0
- package/dist/visualization/dependency-graph.js +399 -0
- package/dist/visualization/index.d.ts +5 -0
- package/dist/visualization/index.d.ts.map +1 -0
- package/dist/visualization/index.js +20 -0
- package/package.json +29 -0
- package/src/__tests__/autopilot.test.ts +196 -0
- package/src/__tests__/tier-config.test.ts +289 -0
- package/src/__tests__/utils/hash-inline.test.ts +76 -0
- package/src/__tests__/utils/hash.test.ts +119 -0
- package/src/__tests__/utils/simple.test.ts +10 -0
- package/src/__tests__/utils/utils-simple.test.ts +5 -0
- package/src/__tests__/utils/utils.test.ts +203 -0
- package/src/autopilot/autopilot-runner.ts +503 -0
- package/src/autopilot/index.ts +6 -0
- package/src/autopilot/types.ts +119 -0
- package/src/cache/index.ts +7 -0
- package/src/cache/redis-cache.d.ts +155 -0
- package/src/cache/redis-cache.d.ts.map +1 -0
- package/src/cache/redis-cache.ts +517 -0
- package/src/ci/github-actions.ts +335 -0
- package/src/ci/index.ts +12 -0
- package/src/ci/pre-commit.ts +338 -0
- package/src/db/usage-schema.prisma +114 -0
- package/src/entitlements.ts +570 -0
- package/src/env.d.ts +68 -0
- package/src/env.d.ts.map +1 -0
- package/src/env.ts +247 -0
- package/src/fix-packs/__tests__/generate-fix-packs.test.ts +317 -0
- package/src/fix-packs/generate-fix-packs.ts +577 -0
- package/src/fix-packs/index.ts +8 -0
- package/src/fix-packs/types.ts +206 -0
- package/src/index.d.ts +7 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.ts +12 -0
- package/src/metrics/prometheus.d.ts +104 -0
- package/src/metrics/prometheus.d.ts.map +1 -0
- package/src/metrics/prometheus.ts +446 -0
- package/src/quota-ledger.ts +548 -0
- package/src/rbac/__tests__/permissions.test.ts +446 -0
- package/src/rbac/index.ts +46 -0
- package/src/rbac/permissions.ts +301 -0
- package/src/rbac/types.ts +298 -0
- package/src/tier-config.json +157 -0
- package/src/tier-config.ts +815 -0
- package/src/types.d.ts +365 -0
- package/src/types.d.ts.map +1 -0
- package/src/types.ts +441 -0
- package/src/utils.d.ts +36 -0
- package/src/utils.d.ts.map +1 -0
- package/src/utils.ts +140 -0
- package/src/verified-autofix/__tests__/format-validator.test.ts +335 -0
- package/src/verified-autofix/__tests__/pipeline.test.ts +419 -0
- package/src/verified-autofix/__tests__/repo-fingerprint.test.ts +241 -0
- package/src/verified-autofix/__tests__/workspace.test.ts +373 -0
- package/src/verified-autofix/format-validator.ts +517 -0
- package/src/verified-autofix/index.ts +63 -0
- package/src/verified-autofix/pipeline.ts +403 -0
- package/src/verified-autofix/repo-fingerprint.ts +459 -0
- package/src/verified-autofix/workspace.ts +531 -0
- package/src/verified-autofix.ts +1187 -0
- package/src/visualization/dependency-graph.d.ts +85 -0
- package/src/visualization/dependency-graph.d.ts.map +1 -0
- package/src/visualization/dependency-graph.ts +495 -0
- package/src/visualization/index.ts +5 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visual Dependency Graph Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates interactive dependency graphs showing:
|
|
5
|
+
* - Package dependencies and their relationships
|
|
6
|
+
* - Vulnerability status of each package
|
|
7
|
+
* - License compatibility
|
|
8
|
+
* - Security risk levels
|
|
9
|
+
*/
|
|
10
|
+
export interface DependencyNode {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
version: string;
|
|
14
|
+
type: "root" | "direct" | "transitive";
|
|
15
|
+
vulnerabilities: VulnerabilityInfo[];
|
|
16
|
+
license: string;
|
|
17
|
+
riskLevel: "none" | "low" | "medium" | "high" | "critical";
|
|
18
|
+
depth: number;
|
|
19
|
+
size?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface DependencyEdge {
|
|
22
|
+
source: string;
|
|
23
|
+
target: string;
|
|
24
|
+
type: "dependency" | "devDependency" | "peerDependency";
|
|
25
|
+
}
|
|
26
|
+
export interface VulnerabilityInfo {
|
|
27
|
+
id: string;
|
|
28
|
+
severity: "low" | "medium" | "high" | "critical";
|
|
29
|
+
title: string;
|
|
30
|
+
}
|
|
31
|
+
export interface DependencyGraph {
|
|
32
|
+
nodes: DependencyNode[];
|
|
33
|
+
edges: DependencyEdge[];
|
|
34
|
+
metadata: {
|
|
35
|
+
projectName: string;
|
|
36
|
+
totalPackages: number;
|
|
37
|
+
vulnerablePackages: number;
|
|
38
|
+
riskDistribution: Record<string, number>;
|
|
39
|
+
generatedAt: string;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export interface GraphRenderOptions {
|
|
43
|
+
format: "svg" | "html" | "json" | "d3" | "mermaid";
|
|
44
|
+
width?: number;
|
|
45
|
+
height?: number;
|
|
46
|
+
showVulnerabilities?: boolean;
|
|
47
|
+
showLicenses?: boolean;
|
|
48
|
+
highlightVulnerable?: boolean;
|
|
49
|
+
maxDepth?: number;
|
|
50
|
+
colorScheme?: "default" | "colorblind" | "dark";
|
|
51
|
+
}
|
|
52
|
+
export declare class DependencyGraphGenerator {
|
|
53
|
+
/**
|
|
54
|
+
* Generate dependency graph from package.json
|
|
55
|
+
*/
|
|
56
|
+
generateFromPackageJson(
|
|
57
|
+
packageJsonPath: string,
|
|
58
|
+
options?: Partial<GraphRenderOptions>,
|
|
59
|
+
): Promise<DependencyGraph>;
|
|
60
|
+
/**
|
|
61
|
+
* Create a dependency node
|
|
62
|
+
*/
|
|
63
|
+
private createNode;
|
|
64
|
+
/**
|
|
65
|
+
* Get transitive dependencies
|
|
66
|
+
*/
|
|
67
|
+
private getTransitiveDeps;
|
|
68
|
+
/**
|
|
69
|
+
* Render graph to Mermaid format
|
|
70
|
+
*/
|
|
71
|
+
renderToMermaid(graph: DependencyGraph): string;
|
|
72
|
+
/**
|
|
73
|
+
* Render graph to D3.js compatible JSON
|
|
74
|
+
*/
|
|
75
|
+
renderToD3(graph: DependencyGraph): string;
|
|
76
|
+
/**
|
|
77
|
+
* Render graph to HTML with embedded visualization
|
|
78
|
+
*/
|
|
79
|
+
renderToHTML(
|
|
80
|
+
graph: DependencyGraph,
|
|
81
|
+
options?: Partial<GraphRenderOptions>,
|
|
82
|
+
): string;
|
|
83
|
+
}
|
|
84
|
+
export declare const dependencyGraphGenerator: DependencyGraphGenerator;
|
|
85
|
+
//# sourceMappingURL=dependency-graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependency-graph.d.ts","sourceRoot":"","sources":["dependency-graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,YAAY,CAAC;IACvC,eAAe,EAAE,iBAAiB,EAAE,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,YAAY,GAAG,eAAe,GAAG,gBAAgB,CAAC;CACzD;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;IACjD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,QAAQ,EAAE;QACR,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACzC,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,SAAS,GAAG,YAAY,GAAG,MAAM,CAAC;CACjD;AAED,qBAAa,wBAAwB;IACnC;;OAEG;IACG,uBAAuB,CAC3B,eAAe,EAAE,MAAM,EACvB,OAAO,GAAE,OAAO,CAAC,kBAAkB,CAAM,GACxC,OAAO,CAAC,eAAe,CAAC;IAyF3B;;OAEG;YACW,UAAU;IAoDxB;;OAEG;YACW,iBAAiB;IAgD/B;;OAEG;IACH,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM;IAsC/C;;OAEG;IACH,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM;IAqB1C;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,GAAE,OAAO,CAAC,kBAAkB,CAAM,GAAG,MAAM;CA4HxF;AAGD,eAAO,MAAM,wBAAwB,0BAAiC,CAAC"}
|
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visual Dependency Graph Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates interactive dependency graphs showing:
|
|
5
|
+
* - Package dependencies and their relationships
|
|
6
|
+
* - Vulnerability status of each package
|
|
7
|
+
* - License compatibility
|
|
8
|
+
* - Security risk levels
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export interface DependencyNode {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
version: string;
|
|
15
|
+
type: "root" | "direct" | "transitive";
|
|
16
|
+
vulnerabilities: VulnerabilityInfo[];
|
|
17
|
+
license: string;
|
|
18
|
+
riskLevel: "none" | "low" | "medium" | "high" | "critical";
|
|
19
|
+
depth: number;
|
|
20
|
+
size?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface DependencyEdge {
|
|
24
|
+
source: string;
|
|
25
|
+
target: string;
|
|
26
|
+
type: "dependency" | "devDependency" | "peerDependency";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface VulnerabilityInfo {
|
|
30
|
+
id: string;
|
|
31
|
+
severity: "low" | "medium" | "high" | "critical";
|
|
32
|
+
title: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface DependencyGraph {
|
|
36
|
+
nodes: DependencyNode[];
|
|
37
|
+
edges: DependencyEdge[];
|
|
38
|
+
metadata: {
|
|
39
|
+
projectName: string;
|
|
40
|
+
totalPackages: number;
|
|
41
|
+
vulnerablePackages: number;
|
|
42
|
+
riskDistribution: Record<string, number>;
|
|
43
|
+
generatedAt: string;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface GraphRenderOptions {
|
|
48
|
+
format: "svg" | "html" | "json" | "d3" | "mermaid";
|
|
49
|
+
width?: number;
|
|
50
|
+
height?: number;
|
|
51
|
+
showVulnerabilities?: boolean;
|
|
52
|
+
showLicenses?: boolean;
|
|
53
|
+
highlightVulnerable?: boolean;
|
|
54
|
+
maxDepth?: number;
|
|
55
|
+
colorScheme?: "default" | "colorblind" | "dark";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class DependencyGraphGenerator {
|
|
59
|
+
/**
|
|
60
|
+
* Generate dependency graph from package.json
|
|
61
|
+
*/
|
|
62
|
+
async generateFromPackageJson(
|
|
63
|
+
packageJsonPath: string,
|
|
64
|
+
options: Partial<GraphRenderOptions> = {},
|
|
65
|
+
): Promise<DependencyGraph> {
|
|
66
|
+
const { readFileSync } = await import("fs");
|
|
67
|
+
const { dirname } = await import("path");
|
|
68
|
+
|
|
69
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
70
|
+
const projectDir = dirname(packageJsonPath);
|
|
71
|
+
|
|
72
|
+
const nodes: DependencyNode[] = [];
|
|
73
|
+
const edges: DependencyEdge[] = [];
|
|
74
|
+
const visited = new Set<string>();
|
|
75
|
+
|
|
76
|
+
// Add root node
|
|
77
|
+
const rootId = `${packageJson.name}@${packageJson.version}`;
|
|
78
|
+
nodes.push({
|
|
79
|
+
id: rootId,
|
|
80
|
+
name: packageJson.name || "root",
|
|
81
|
+
version: packageJson.version || "0.0.0",
|
|
82
|
+
type: "root",
|
|
83
|
+
vulnerabilities: [],
|
|
84
|
+
license: packageJson.license || "UNKNOWN",
|
|
85
|
+
riskLevel: "none",
|
|
86
|
+
depth: 0,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Process direct dependencies
|
|
90
|
+
const deps = packageJson.dependencies || {};
|
|
91
|
+
const devDeps = packageJson.devDependencies || {};
|
|
92
|
+
|
|
93
|
+
for (const [name, version] of Object.entries(deps)) {
|
|
94
|
+
const nodeId = `${name}@${version}`;
|
|
95
|
+
if (!visited.has(nodeId)) {
|
|
96
|
+
visited.add(nodeId);
|
|
97
|
+
const node = await this.createNode(
|
|
98
|
+
name,
|
|
99
|
+
String(version),
|
|
100
|
+
"direct",
|
|
101
|
+
1,
|
|
102
|
+
projectDir,
|
|
103
|
+
);
|
|
104
|
+
nodes.push(node);
|
|
105
|
+
edges.push({ source: rootId, target: nodeId, type: "dependency" });
|
|
106
|
+
|
|
107
|
+
// Check for transitive dependencies
|
|
108
|
+
if (options.maxDepth !== 1) {
|
|
109
|
+
const transitives = await this.getTransitiveDeps(
|
|
110
|
+
name,
|
|
111
|
+
projectDir,
|
|
112
|
+
visited,
|
|
113
|
+
2,
|
|
114
|
+
options.maxDepth || 3,
|
|
115
|
+
);
|
|
116
|
+
for (const trans of transitives.nodes) {
|
|
117
|
+
nodes.push(trans);
|
|
118
|
+
}
|
|
119
|
+
for (const edge of transitives.edges) {
|
|
120
|
+
edges.push(edge);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
for (const [name, version] of Object.entries(devDeps)) {
|
|
127
|
+
const nodeId = `${name}@${version}`;
|
|
128
|
+
if (!visited.has(nodeId)) {
|
|
129
|
+
visited.add(nodeId);
|
|
130
|
+
const node = await this.createNode(
|
|
131
|
+
name,
|
|
132
|
+
String(version),
|
|
133
|
+
"direct",
|
|
134
|
+
1,
|
|
135
|
+
projectDir,
|
|
136
|
+
);
|
|
137
|
+
nodes.push(node);
|
|
138
|
+
edges.push({ source: rootId, target: nodeId, type: "devDependency" });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Calculate risk distribution
|
|
143
|
+
const riskDistribution: Record<string, number> = {
|
|
144
|
+
none: 0,
|
|
145
|
+
low: 0,
|
|
146
|
+
medium: 0,
|
|
147
|
+
high: 0,
|
|
148
|
+
critical: 0,
|
|
149
|
+
};
|
|
150
|
+
for (const node of nodes) {
|
|
151
|
+
const level = node.riskLevel;
|
|
152
|
+
if (typeof riskDistribution[level] === "number") {
|
|
153
|
+
riskDistribution[level]++;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const vulnerablePackages = nodes.filter(
|
|
158
|
+
(n) => n.vulnerabilities.length > 0,
|
|
159
|
+
).length;
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
nodes,
|
|
163
|
+
edges,
|
|
164
|
+
metadata: {
|
|
165
|
+
projectName: packageJson.name || "unknown",
|
|
166
|
+
totalPackages: nodes.length,
|
|
167
|
+
vulnerablePackages,
|
|
168
|
+
riskDistribution,
|
|
169
|
+
generatedAt: new Date().toISOString(),
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Create a dependency node
|
|
176
|
+
*/
|
|
177
|
+
private async createNode(
|
|
178
|
+
name: string,
|
|
179
|
+
version: string,
|
|
180
|
+
type: "root" | "direct" | "transitive",
|
|
181
|
+
depth: number,
|
|
182
|
+
projectDir: string,
|
|
183
|
+
): Promise<DependencyNode> {
|
|
184
|
+
const { existsSync, readFileSync } = await import("fs");
|
|
185
|
+
const { join } = await import("path");
|
|
186
|
+
|
|
187
|
+
const versionStr = String(version).replace(/^[\^~]/, "");
|
|
188
|
+
const nodeId = `${name}@${version}`;
|
|
189
|
+
|
|
190
|
+
// Try to get license from node_modules
|
|
191
|
+
let license = "UNKNOWN";
|
|
192
|
+
const pkgPath = join(projectDir, "node_modules", name, "package.json");
|
|
193
|
+
if (existsSync(pkgPath)) {
|
|
194
|
+
try {
|
|
195
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
196
|
+
license = pkg.license || "UNKNOWN";
|
|
197
|
+
} catch {
|
|
198
|
+
// Skip
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Placeholder for vulnerability check - would integrate with vulnerability DB
|
|
203
|
+
const vulnerabilities: VulnerabilityInfo[] = [];
|
|
204
|
+
|
|
205
|
+
// Calculate risk level based on vulnerabilities
|
|
206
|
+
let riskLevel: "none" | "low" | "medium" | "high" | "critical" = "none";
|
|
207
|
+
if (vulnerabilities.some((v) => v.severity === "critical")) {
|
|
208
|
+
riskLevel = "critical";
|
|
209
|
+
} else if (vulnerabilities.some((v) => v.severity === "high")) {
|
|
210
|
+
riskLevel = "high";
|
|
211
|
+
} else if (vulnerabilities.some((v) => v.severity === "medium")) {
|
|
212
|
+
riskLevel = "medium";
|
|
213
|
+
} else if (vulnerabilities.length > 0) {
|
|
214
|
+
riskLevel = "low";
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
id: nodeId,
|
|
219
|
+
name,
|
|
220
|
+
version: versionStr,
|
|
221
|
+
type,
|
|
222
|
+
vulnerabilities,
|
|
223
|
+
license,
|
|
224
|
+
riskLevel,
|
|
225
|
+
depth,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Get transitive dependencies
|
|
231
|
+
*/
|
|
232
|
+
private async getTransitiveDeps(
|
|
233
|
+
packageName: string,
|
|
234
|
+
projectDir: string,
|
|
235
|
+
visited: Set<string>,
|
|
236
|
+
currentDepth: number,
|
|
237
|
+
maxDepth: number,
|
|
238
|
+
): Promise<{ nodes: DependencyNode[]; edges: DependencyEdge[] }> {
|
|
239
|
+
const { existsSync, readFileSync } = await import("fs");
|
|
240
|
+
const { join } = await import("path");
|
|
241
|
+
|
|
242
|
+
const nodes: DependencyNode[] = [];
|
|
243
|
+
const edges: DependencyEdge[] = [];
|
|
244
|
+
|
|
245
|
+
if (currentDepth > maxDepth) {
|
|
246
|
+
return { nodes, edges };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const pkgPath = join(
|
|
250
|
+
projectDir,
|
|
251
|
+
"node_modules",
|
|
252
|
+
packageName,
|
|
253
|
+
"package.json",
|
|
254
|
+
);
|
|
255
|
+
if (!existsSync(pkgPath)) {
|
|
256
|
+
return { nodes, edges };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
261
|
+
const deps = pkg.dependencies || {};
|
|
262
|
+
const parentId = `${packageName}@${pkg.version}`;
|
|
263
|
+
|
|
264
|
+
for (const [name, version] of Object.entries(deps)) {
|
|
265
|
+
const nodeId = `${name}@${version}`;
|
|
266
|
+
if (!visited.has(nodeId)) {
|
|
267
|
+
visited.add(nodeId);
|
|
268
|
+
const node = await this.createNode(
|
|
269
|
+
name,
|
|
270
|
+
String(version),
|
|
271
|
+
"transitive",
|
|
272
|
+
currentDepth,
|
|
273
|
+
projectDir,
|
|
274
|
+
);
|
|
275
|
+
nodes.push(node);
|
|
276
|
+
edges.push({ source: parentId, target: nodeId, type: "dependency" });
|
|
277
|
+
|
|
278
|
+
// Recurse for deeper transitive deps
|
|
279
|
+
const deeper = await this.getTransitiveDeps(
|
|
280
|
+
name,
|
|
281
|
+
projectDir,
|
|
282
|
+
visited,
|
|
283
|
+
currentDepth + 1,
|
|
284
|
+
maxDepth,
|
|
285
|
+
);
|
|
286
|
+
nodes.push(...deeper.nodes);
|
|
287
|
+
edges.push(...deeper.edges);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
} catch {
|
|
291
|
+
// Skip packages we can't read
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return { nodes, edges };
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Render graph to Mermaid format
|
|
299
|
+
*/
|
|
300
|
+
renderToMermaid(graph: DependencyGraph): string {
|
|
301
|
+
const lines: string[] = ["graph TD"];
|
|
302
|
+
|
|
303
|
+
// Define node styles based on risk level
|
|
304
|
+
const riskStyles: Record<string, string> = {
|
|
305
|
+
none: "fill:#90EE90",
|
|
306
|
+
low: "fill:#FFFF99",
|
|
307
|
+
medium: "fill:#FFB347",
|
|
308
|
+
high: "fill:#FF6B6B",
|
|
309
|
+
critical: "fill:#FF0000,color:#fff",
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// Add nodes
|
|
313
|
+
for (const node of graph.nodes) {
|
|
314
|
+
const label = `${node.name}@${node.version}`;
|
|
315
|
+
const safeId = node.id.replace(/[@./]/g, "_");
|
|
316
|
+
lines.push(` ${safeId}["${label}"]`);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Add edges
|
|
320
|
+
for (const edge of graph.edges) {
|
|
321
|
+
const sourceId = edge.source.replace(/[@./]/g, "_");
|
|
322
|
+
const targetId = edge.target.replace(/[@./]/g, "_");
|
|
323
|
+
const edgeStyle = edge.type === "devDependency" ? "-->" : "-->";
|
|
324
|
+
lines.push(` ${sourceId} ${edgeStyle} ${targetId}`);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Add styles
|
|
328
|
+
lines.push("");
|
|
329
|
+
for (const node of graph.nodes) {
|
|
330
|
+
const safeId = node.id.replace(/[@./]/g, "_");
|
|
331
|
+
const style = riskStyles[node.riskLevel];
|
|
332
|
+
lines.push(` style ${safeId} ${style}`);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return lines.join("\n");
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Render graph to D3.js compatible JSON
|
|
340
|
+
*/
|
|
341
|
+
renderToD3(graph: DependencyGraph): string {
|
|
342
|
+
const d3Data = {
|
|
343
|
+
nodes: graph.nodes.map((node) => ({
|
|
344
|
+
id: node.id,
|
|
345
|
+
name: node.name,
|
|
346
|
+
version: node.version,
|
|
347
|
+
group: node.type === "root" ? 1 : node.type === "direct" ? 2 : 3,
|
|
348
|
+
riskLevel: node.riskLevel,
|
|
349
|
+
vulnerabilities: node.vulnerabilities.length,
|
|
350
|
+
license: node.license,
|
|
351
|
+
})),
|
|
352
|
+
links: graph.edges.map((edge) => ({
|
|
353
|
+
source: edge.source,
|
|
354
|
+
target: edge.target,
|
|
355
|
+
type: edge.type,
|
|
356
|
+
})),
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
return JSON.stringify(d3Data, null, 2);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Render graph to HTML with embedded visualization
|
|
364
|
+
*/
|
|
365
|
+
renderToHTML(
|
|
366
|
+
graph: DependencyGraph,
|
|
367
|
+
options: Partial<GraphRenderOptions> = {},
|
|
368
|
+
): string {
|
|
369
|
+
const width = options.width || 1200;
|
|
370
|
+
const height = options.height || 800;
|
|
371
|
+
const d3Data = this.renderToD3(graph);
|
|
372
|
+
|
|
373
|
+
return `<!DOCTYPE html>
|
|
374
|
+
<html lang="en">
|
|
375
|
+
<head>
|
|
376
|
+
<meta charset="UTF-8">
|
|
377
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
378
|
+
<title>Dependency Graph - ${graph.metadata.projectName}</title>
|
|
379
|
+
<script src="https://d3js.org/d3.v7.min.js"></script>
|
|
380
|
+
<style>
|
|
381
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
|
|
382
|
+
.container { background: white; border-radius: 8px; padding: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
|
383
|
+
h1 { margin: 0 0 10px 0; color: #333; }
|
|
384
|
+
.stats { display: flex; gap: 20px; margin-bottom: 20px; }
|
|
385
|
+
.stat { background: #f0f0f0; padding: 10px 15px; border-radius: 4px; }
|
|
386
|
+
.stat-label { font-size: 12px; color: #666; }
|
|
387
|
+
.stat-value { font-size: 24px; font-weight: bold; }
|
|
388
|
+
.critical { color: #ff0000; }
|
|
389
|
+
.high { color: #ff6b6b; }
|
|
390
|
+
.medium { color: #ffb347; }
|
|
391
|
+
.low { color: #ffd700; }
|
|
392
|
+
svg { display: block; margin: 0 auto; }
|
|
393
|
+
.node circle { stroke: #fff; stroke-width: 2px; }
|
|
394
|
+
.node text { font-size: 10px; pointer-events: none; }
|
|
395
|
+
.link { stroke: #999; stroke-opacity: 0.6; }
|
|
396
|
+
.tooltip { position: absolute; background: #333; color: white; padding: 8px 12px; border-radius: 4px; font-size: 12px; pointer-events: none; }
|
|
397
|
+
</style>
|
|
398
|
+
</head>
|
|
399
|
+
<body>
|
|
400
|
+
<div class="container">
|
|
401
|
+
<h1>Dependency Graph: ${graph.metadata.projectName}</h1>
|
|
402
|
+
<div class="stats">
|
|
403
|
+
<div class="stat">
|
|
404
|
+
<div class="stat-label">Total Packages</div>
|
|
405
|
+
<div class="stat-value">${graph.metadata.totalPackages}</div>
|
|
406
|
+
</div>
|
|
407
|
+
<div class="stat">
|
|
408
|
+
<div class="stat-label">Vulnerable</div>
|
|
409
|
+
<div class="stat-value critical">${graph.metadata.vulnerablePackages}</div>
|
|
410
|
+
</div>
|
|
411
|
+
<div class="stat">
|
|
412
|
+
<div class="stat-label">Critical</div>
|
|
413
|
+
<div class="stat-value critical">${graph.metadata.riskDistribution["critical"]}</div>
|
|
414
|
+
</div>
|
|
415
|
+
<div class="stat">
|
|
416
|
+
<div class="stat-label">High</div>
|
|
417
|
+
<div class="stat-value high">${graph.metadata.riskDistribution["high"]}</div>
|
|
418
|
+
</div>
|
|
419
|
+
</div>
|
|
420
|
+
<svg width="${width}" height="${height}"></svg>
|
|
421
|
+
</div>
|
|
422
|
+
<script>
|
|
423
|
+
const data = ${d3Data};
|
|
424
|
+
const width = ${width};
|
|
425
|
+
const height = ${height};
|
|
426
|
+
|
|
427
|
+
const color = d3.scaleOrdinal()
|
|
428
|
+
.domain(['none', 'low', 'medium', 'high', 'critical'])
|
|
429
|
+
.range(['#90EE90', '#FFFF99', '#FFB347', '#FF6B6B', '#FF0000']);
|
|
430
|
+
|
|
431
|
+
const simulation = d3.forceSimulation(data.nodes)
|
|
432
|
+
.force('link', d3.forceLink(data.links).id(d => d.id).distance(100))
|
|
433
|
+
.force('charge', d3.forceManyBody().strength(-200))
|
|
434
|
+
.force('center', d3.forceCenter(width / 2, height / 2));
|
|
435
|
+
|
|
436
|
+
const svg = d3.select('svg');
|
|
437
|
+
|
|
438
|
+
const link = svg.append('g')
|
|
439
|
+
.selectAll('line')
|
|
440
|
+
.data(data.links)
|
|
441
|
+
.join('line')
|
|
442
|
+
.attr('class', 'link');
|
|
443
|
+
|
|
444
|
+
const node = svg.append('g')
|
|
445
|
+
.selectAll('g')
|
|
446
|
+
.data(data.nodes)
|
|
447
|
+
.join('g')
|
|
448
|
+
.attr('class', 'node')
|
|
449
|
+
.call(d3.drag()
|
|
450
|
+
.on('start', dragstarted)
|
|
451
|
+
.on('drag', dragged)
|
|
452
|
+
.on('end', dragended));
|
|
453
|
+
|
|
454
|
+
node.append('circle')
|
|
455
|
+
.attr('r', d => d.group === 1 ? 20 : d.group === 2 ? 12 : 8)
|
|
456
|
+
.attr('fill', d => color(d.riskLevel));
|
|
457
|
+
|
|
458
|
+
node.append('text')
|
|
459
|
+
.attr('dx', 15)
|
|
460
|
+
.attr('dy', 4)
|
|
461
|
+
.text(d => d.name);
|
|
462
|
+
|
|
463
|
+
simulation.on('tick', () => {
|
|
464
|
+
link
|
|
465
|
+
.attr('x1', d => d.source.x)
|
|
466
|
+
.attr('y1', d => d.source.y)
|
|
467
|
+
.attr('x2', d => d.target.x)
|
|
468
|
+
.attr('y2', d => d.target.y);
|
|
469
|
+
node.attr('transform', d => \`translate(\${d.x},\${d.y})\`);
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
function dragstarted(event) {
|
|
473
|
+
if (!event.active) simulation.alphaTarget(0.3).restart();
|
|
474
|
+
event.subject.fx = event.subject.x;
|
|
475
|
+
event.subject.fy = event.subject.y;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function dragged(event) {
|
|
479
|
+
event.subject.fx = event.x;
|
|
480
|
+
event.subject.fy = event.y;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function dragended(event) {
|
|
484
|
+
if (!event.active) simulation.alphaTarget(0);
|
|
485
|
+
event.subject.fx = null;
|
|
486
|
+
event.subject.fy = null;
|
|
487
|
+
}
|
|
488
|
+
</script>
|
|
489
|
+
</body>
|
|
490
|
+
</html>`;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Export singleton
|
|
495
|
+
export const dependencyGraphGenerator = new DependencyGraphGenerator();
|