@shipsafe/cli 0.1.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.
Files changed (166) hide show
  1. package/README.md +167 -0
  2. package/dist/bin/shipsafe.d.ts +3 -0
  3. package/dist/bin/shipsafe.d.ts.map +1 -0
  4. package/dist/bin/shipsafe.js +33 -0
  5. package/dist/bin/shipsafe.js.map +1 -0
  6. package/dist/src/autofix/pr-generator.d.ts +48 -0
  7. package/dist/src/autofix/pr-generator.d.ts.map +1 -0
  8. package/dist/src/autofix/pr-generator.js +359 -0
  9. package/dist/src/autofix/pr-generator.js.map +1 -0
  10. package/dist/src/autofix/scaffolding.d.ts +26 -0
  11. package/dist/src/autofix/scaffolding.d.ts.map +1 -0
  12. package/dist/src/autofix/scaffolding.js +249 -0
  13. package/dist/src/autofix/scaffolding.js.map +1 -0
  14. package/dist/src/autofix/secret-fixer.d.ts +27 -0
  15. package/dist/src/autofix/secret-fixer.d.ts.map +1 -0
  16. package/dist/src/autofix/secret-fixer.js +138 -0
  17. package/dist/src/autofix/secret-fixer.js.map +1 -0
  18. package/dist/src/claude-md/manager.d.ts +17 -0
  19. package/dist/src/claude-md/manager.d.ts.map +1 -0
  20. package/dist/src/claude-md/manager.js +143 -0
  21. package/dist/src/claude-md/manager.js.map +1 -0
  22. package/dist/src/cli/activate.d.ts +4 -0
  23. package/dist/src/cli/activate.d.ts.map +1 -0
  24. package/dist/src/cli/activate.js +53 -0
  25. package/dist/src/cli/activate.js.map +1 -0
  26. package/dist/src/cli/config.d.ts +21 -0
  27. package/dist/src/cli/config.d.ts.map +1 -0
  28. package/dist/src/cli/config.js +128 -0
  29. package/dist/src/cli/config.js.map +1 -0
  30. package/dist/src/cli/connect.d.ts +36 -0
  31. package/dist/src/cli/connect.d.ts.map +1 -0
  32. package/dist/src/cli/connect.js +107 -0
  33. package/dist/src/cli/connect.js.map +1 -0
  34. package/dist/src/cli/init.d.ts +12 -0
  35. package/dist/src/cli/init.d.ts.map +1 -0
  36. package/dist/src/cli/init.js +45 -0
  37. package/dist/src/cli/init.js.map +1 -0
  38. package/dist/src/cli/license-check.d.ts +7 -0
  39. package/dist/src/cli/license-check.d.ts.map +1 -0
  40. package/dist/src/cli/license-check.js +69 -0
  41. package/dist/src/cli/license-check.js.map +1 -0
  42. package/dist/src/cli/license-gate.d.ts +9 -0
  43. package/dist/src/cli/license-gate.d.ts.map +1 -0
  44. package/dist/src/cli/license-gate.js +25 -0
  45. package/dist/src/cli/license-gate.js.map +1 -0
  46. package/dist/src/cli/scan.d.ts +9 -0
  47. package/dist/src/cli/scan.d.ts.map +1 -0
  48. package/dist/src/cli/scan.js +75 -0
  49. package/dist/src/cli/scan.js.map +1 -0
  50. package/dist/src/cli/setup.d.ts +27 -0
  51. package/dist/src/cli/setup.d.ts.map +1 -0
  52. package/dist/src/cli/setup.js +134 -0
  53. package/dist/src/cli/setup.js.map +1 -0
  54. package/dist/src/cli/status.d.ts +4 -0
  55. package/dist/src/cli/status.d.ts.map +1 -0
  56. package/dist/src/cli/status.js +52 -0
  57. package/dist/src/cli/status.js.map +1 -0
  58. package/dist/src/cli/upload-sourcemaps.d.ts +13 -0
  59. package/dist/src/cli/upload-sourcemaps.d.ts.map +1 -0
  60. package/dist/src/cli/upload-sourcemaps.js +157 -0
  61. package/dist/src/cli/upload-sourcemaps.js.map +1 -0
  62. package/dist/src/config/manager.d.ts +37 -0
  63. package/dist/src/config/manager.d.ts.map +1 -0
  64. package/dist/src/config/manager.js +131 -0
  65. package/dist/src/config/manager.js.map +1 -0
  66. package/dist/src/constants.d.ts +28 -0
  67. package/dist/src/constants.d.ts.map +1 -0
  68. package/dist/src/constants.js +34 -0
  69. package/dist/src/constants.js.map +1 -0
  70. package/dist/src/engines/graph/data-flow.d.ts +36 -0
  71. package/dist/src/engines/graph/data-flow.d.ts.map +1 -0
  72. package/dist/src/engines/graph/data-flow.js +189 -0
  73. package/dist/src/engines/graph/data-flow.js.map +1 -0
  74. package/dist/src/engines/graph/index.d.ts +20 -0
  75. package/dist/src/engines/graph/index.d.ts.map +1 -0
  76. package/dist/src/engines/graph/index.js +100 -0
  77. package/dist/src/engines/graph/index.js.map +1 -0
  78. package/dist/src/engines/graph/parser.d.ts +13 -0
  79. package/dist/src/engines/graph/parser.d.ts.map +1 -0
  80. package/dist/src/engines/graph/parser.js +620 -0
  81. package/dist/src/engines/graph/parser.js.map +1 -0
  82. package/dist/src/engines/graph/queries.d.ts +11 -0
  83. package/dist/src/engines/graph/queries.d.ts.map +1 -0
  84. package/dist/src/engines/graph/queries.js +196 -0
  85. package/dist/src/engines/graph/queries.js.map +1 -0
  86. package/dist/src/engines/graph/store.d.ts +35 -0
  87. package/dist/src/engines/graph/store.d.ts.map +1 -0
  88. package/dist/src/engines/graph/store.js +284 -0
  89. package/dist/src/engines/graph/store.js.map +1 -0
  90. package/dist/src/engines/pattern/gitleaks.d.ts +4 -0
  91. package/dist/src/engines/pattern/gitleaks.d.ts.map +1 -0
  92. package/dist/src/engines/pattern/gitleaks.js +78 -0
  93. package/dist/src/engines/pattern/gitleaks.js.map +1 -0
  94. package/dist/src/engines/pattern/index.d.ts +11 -0
  95. package/dist/src/engines/pattern/index.d.ts.map +1 -0
  96. package/dist/src/engines/pattern/index.js +111 -0
  97. package/dist/src/engines/pattern/index.js.map +1 -0
  98. package/dist/src/engines/pattern/semgrep.d.ts +4 -0
  99. package/dist/src/engines/pattern/semgrep.d.ts.map +1 -0
  100. package/dist/src/engines/pattern/semgrep.js +83 -0
  101. package/dist/src/engines/pattern/semgrep.js.map +1 -0
  102. package/dist/src/engines/pattern/trivy.d.ts +4 -0
  103. package/dist/src/engines/pattern/trivy.d.ts.map +1 -0
  104. package/dist/src/engines/pattern/trivy.js +90 -0
  105. package/dist/src/engines/pattern/trivy.js.map +1 -0
  106. package/dist/src/github/api.d.ts +19 -0
  107. package/dist/src/github/api.d.ts.map +1 -0
  108. package/dist/src/github/api.js +75 -0
  109. package/dist/src/github/api.js.map +1 -0
  110. package/dist/src/github/app-manifest.d.ts +28 -0
  111. package/dist/src/github/app-manifest.d.ts.map +1 -0
  112. package/dist/src/github/app-manifest.js +27 -0
  113. package/dist/src/github/app-manifest.js.map +1 -0
  114. package/dist/src/github/checks.d.ts +36 -0
  115. package/dist/src/github/checks.d.ts.map +1 -0
  116. package/dist/src/github/checks.js +90 -0
  117. package/dist/src/github/checks.js.map +1 -0
  118. package/dist/src/github/scanner.d.ts +20 -0
  119. package/dist/src/github/scanner.d.ts.map +1 -0
  120. package/dist/src/github/scanner.js +78 -0
  121. package/dist/src/github/scanner.js.map +1 -0
  122. package/dist/src/github/webhook.d.ts +39 -0
  123. package/dist/src/github/webhook.d.ts.map +1 -0
  124. package/dist/src/github/webhook.js +80 -0
  125. package/dist/src/github/webhook.js.map +1 -0
  126. package/dist/src/hooks/installer.d.ts +4 -0
  127. package/dist/src/hooks/installer.d.ts.map +1 -0
  128. package/dist/src/hooks/installer.js +146 -0
  129. package/dist/src/hooks/installer.js.map +1 -0
  130. package/dist/src/mcp/server.d.ts +2 -0
  131. package/dist/src/mcp/server.d.ts.map +1 -0
  132. package/dist/src/mcp/server.js +96 -0
  133. package/dist/src/mcp/server.js.map +1 -0
  134. package/dist/src/mcp/tools/check-package.d.ts +30 -0
  135. package/dist/src/mcp/tools/check-package.d.ts.map +1 -0
  136. package/dist/src/mcp/tools/check-package.js +196 -0
  137. package/dist/src/mcp/tools/check-package.js.map +1 -0
  138. package/dist/src/mcp/tools/fix.d.ts +41 -0
  139. package/dist/src/mcp/tools/fix.d.ts.map +1 -0
  140. package/dist/src/mcp/tools/fix.js +98 -0
  141. package/dist/src/mcp/tools/fix.js.map +1 -0
  142. package/dist/src/mcp/tools/graph-query.d.ts +7 -0
  143. package/dist/src/mcp/tools/graph-query.d.ts.map +1 -0
  144. package/dist/src/mcp/tools/graph-query.js +139 -0
  145. package/dist/src/mcp/tools/graph-query.js.map +1 -0
  146. package/dist/src/mcp/tools/production-errors.d.ts +23 -0
  147. package/dist/src/mcp/tools/production-errors.d.ts.map +1 -0
  148. package/dist/src/mcp/tools/production-errors.js +46 -0
  149. package/dist/src/mcp/tools/production-errors.js.map +1 -0
  150. package/dist/src/mcp/tools/scan.d.ts +7 -0
  151. package/dist/src/mcp/tools/scan.d.ts.map +1 -0
  152. package/dist/src/mcp/tools/scan.js +9 -0
  153. package/dist/src/mcp/tools/scan.js.map +1 -0
  154. package/dist/src/mcp/tools/status.d.ts +9 -0
  155. package/dist/src/mcp/tools/status.d.ts.map +1 -0
  156. package/dist/src/mcp/tools/status.js +18 -0
  157. package/dist/src/mcp/tools/status.js.map +1 -0
  158. package/dist/src/mcp/tools/verify-resolution.d.ts +12 -0
  159. package/dist/src/mcp/tools/verify-resolution.d.ts.map +1 -0
  160. package/dist/src/mcp/tools/verify-resolution.js +45 -0
  161. package/dist/src/mcp/tools/verify-resolution.js.map +1 -0
  162. package/dist/src/types.d.ts +136 -0
  163. package/dist/src/types.d.ts.map +1 -0
  164. package/dist/src/types.js +2 -0
  165. package/dist/src/types.js.map +1 -0
  166. package/package.json +53 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/engines/pattern/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAOzG,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,aAAa,CAW/D;AAED,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAQzE;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAgB1E;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,UAAU,CAAC,CAgFzF"}
@@ -0,0 +1,111 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { SEVERITY_ORDER } from '../../constants.js';
3
+ import { checkSemgrepInstalled, runSemgrep } from './semgrep.js';
4
+ import { checkGitleaksInstalled, runGitleaks } from './gitleaks.js';
5
+ import { checkTrivyInstalled, runTrivy } from './trivy.js';
6
+ import { runGraphEngine, isGraphEngineAvailable } from '../graph/index.js';
7
+ export function computeScore(findings) {
8
+ if (findings.length === 0)
9
+ return 'A';
10
+ const severities = new Set(findings.map((f) => f.severity));
11
+ if (severities.has('critical'))
12
+ return 'F';
13
+ if (severities.has('high'))
14
+ return 'D';
15
+ if (severities.has('medium'))
16
+ return 'C';
17
+ // Only info and/or low remain
18
+ return 'B';
19
+ }
20
+ export async function getAvailableScanners() {
21
+ const [semgrep, gitleaks, trivy] = await Promise.all([
22
+ checkSemgrepInstalled(),
23
+ checkGitleaksInstalled(),
24
+ checkTrivyInstalled(),
25
+ ]);
26
+ return { semgrep, gitleaks, trivy };
27
+ }
28
+ export async function getStagedFiles(projectDir) {
29
+ return new Promise((resolve) => {
30
+ execFile('git', ['diff', '--cached', '--name-only'], { cwd: projectDir }, (error, stdout) => {
31
+ if (error) {
32
+ resolve([]);
33
+ return;
34
+ }
35
+ const files = (typeof stdout === 'string' ? stdout : '')
36
+ .split('\n')
37
+ .map((line) => line.trim())
38
+ .filter((line) => line.length > 0);
39
+ resolve(files);
40
+ });
41
+ });
42
+ }
43
+ export async function runPatternEngine(options) {
44
+ const startTime = Date.now();
45
+ const { targetPath, scope, stagedFiles: providedStagedFiles } = options;
46
+ // 1. Check which scanners are installed
47
+ const availability = await getAvailableScanners();
48
+ // 2. If scope is 'staged', get staged files
49
+ let stagedFiles = providedStagedFiles;
50
+ if (scope === 'staged' && !stagedFiles) {
51
+ stagedFiles = await getStagedFiles(targetPath);
52
+ }
53
+ // 3. If scope is 'staged' and no staged files, return clean result immediately
54
+ if (scope === 'staged' && (!stagedFiles || stagedFiles.length === 0)) {
55
+ return {
56
+ status: 'pass',
57
+ score: 'A',
58
+ findings: [],
59
+ scan_duration_ms: Date.now() - startTime,
60
+ };
61
+ }
62
+ // 4. Run all available scanners in parallel with Promise.allSettled()
63
+ const scannerPromises = [];
64
+ if (availability.semgrep) {
65
+ scannerPromises.push(runSemgrep(targetPath, stagedFiles));
66
+ }
67
+ if (availability.gitleaks) {
68
+ scannerPromises.push(runGitleaks(targetPath, stagedFiles));
69
+ }
70
+ if (availability.trivy) {
71
+ scannerPromises.push(runTrivy(targetPath));
72
+ }
73
+ const results = await Promise.allSettled(scannerPromises);
74
+ // 5. Merge all findings into single array
75
+ const findings = [];
76
+ for (const result of results) {
77
+ if (result.status === 'fulfilled') {
78
+ findings.push(...result.value);
79
+ }
80
+ // Rejected promises are silently skipped (scanner failure is non-fatal)
81
+ }
82
+ // 5b. Run graph engine (optional — failures are non-fatal)
83
+ if (isGraphEngineAvailable()) {
84
+ try {
85
+ const graphResult = await runGraphEngine({ targetPath, scope });
86
+ findings.push(...graphResult.findings);
87
+ }
88
+ catch {
89
+ // Graph engine failure is non-fatal — pattern results are still returned
90
+ }
91
+ }
92
+ // 6. Sort findings by severity (critical first)
93
+ findings.sort((a, b) => {
94
+ const orderA = SEVERITY_ORDER[a.severity] ?? 999;
95
+ const orderB = SEVERITY_ORDER[b.severity] ?? 999;
96
+ return orderA - orderB;
97
+ });
98
+ // 7. Compute score
99
+ const score = computeScore(findings);
100
+ // 8. Determine status
101
+ const hasCriticalOrHigh = findings.some((f) => f.severity === 'critical' || f.severity === 'high');
102
+ const status = hasCriticalOrHigh ? 'fail' : 'pass';
103
+ // 9. Return ScanResult with timing info
104
+ return {
105
+ status,
106
+ score,
107
+ findings,
108
+ scan_duration_ms: Date.now() - startTime,
109
+ };
110
+ }
111
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/engines/pattern/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Finding } from '../../types.js';
2
+ export declare function checkSemgrepInstalled(): Promise<boolean>;
3
+ export declare function runSemgrep(targetPath: string, stagedFiles?: string[]): Promise<Finding[]>;
4
+ //# sourceMappingURL=semgrep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semgrep.d.ts","sourceRoot":"","sources":["../../../../src/engines/pattern/semgrep.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAY,MAAM,gBAAgB,CAAC;AAsExD,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO9D;AAED,wBAAsB,UAAU,CAC9B,UAAU,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,OAAO,EAAE,CAAC,CAgCpB"}
@@ -0,0 +1,83 @@
1
+ import { execFile } from 'node:child_process';
2
+ function execFilePromise(cmd, args) {
3
+ return new Promise((resolve, reject) => {
4
+ execFile(cmd, args, (error, stdout, stderr) => {
5
+ if (error) {
6
+ // Attach stdout/stderr to the error so callers can still read output
7
+ const enrichedError = error;
8
+ enrichedError.stdout = typeof stdout === 'string' ? stdout : '';
9
+ enrichedError.stderr = typeof stderr === 'string' ? stderr : '';
10
+ reject(enrichedError);
11
+ return;
12
+ }
13
+ resolve({
14
+ stdout: typeof stdout === 'string' ? stdout : '',
15
+ stderr: typeof stderr === 'string' ? stderr : '',
16
+ });
17
+ });
18
+ });
19
+ }
20
+ const SEVERITY_MAP = {
21
+ ERROR: 'critical',
22
+ WARNING: 'high',
23
+ INFO: 'medium',
24
+ };
25
+ function mapSeverity(semgrepSeverity) {
26
+ return SEVERITY_MAP[semgrepSeverity] ?? 'low';
27
+ }
28
+ function parseSemgrepOutput(jsonString) {
29
+ const output = JSON.parse(jsonString);
30
+ return output.results.map((result) => ({
31
+ id: `semgrep_${result.check_id}_${result.start.line}`,
32
+ engine: 'pattern',
33
+ severity: mapSeverity(result.extra.severity),
34
+ type: result.check_id,
35
+ file: result.path,
36
+ line: result.start.line,
37
+ description: result.extra.message,
38
+ fix_suggestion: result.extra.fix ?? '',
39
+ auto_fixable: result.extra.fix != null,
40
+ }));
41
+ }
42
+ export async function checkSemgrepInstalled() {
43
+ try {
44
+ await execFilePromise('which', ['semgrep']);
45
+ return true;
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ }
51
+ export async function runSemgrep(targetPath, stagedFiles) {
52
+ const installed = await checkSemgrepInstalled();
53
+ if (!installed) {
54
+ console.warn('ShipSafe: semgrep is not installed, skipping pattern scan');
55
+ return [];
56
+ }
57
+ try {
58
+ const args = ['scan', '--json', '--quiet'];
59
+ if (stagedFiles && stagedFiles.length > 0) {
60
+ args.push(...stagedFiles);
61
+ }
62
+ else {
63
+ args.push(targetPath);
64
+ }
65
+ const { stdout } = await execFilePromise('semgrep', args);
66
+ return parseSemgrepOutput(stdout);
67
+ }
68
+ catch (error) {
69
+ // If semgrep exits non-zero but produced output, still try to parse it
70
+ const execError = error;
71
+ if (execError.stdout) {
72
+ try {
73
+ return parseSemgrepOutput(execError.stdout);
74
+ }
75
+ catch {
76
+ // JSON parse failed on the output — fall through to warn
77
+ }
78
+ }
79
+ console.warn('ShipSafe: semgrep scan failed', execError.message);
80
+ return [];
81
+ }
82
+ }
83
+ //# sourceMappingURL=semgrep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semgrep.js","sourceRoot":"","sources":["../../../../src/engines/pattern/semgrep.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Finding } from '../../types.js';
2
+ export declare function checkTrivyInstalled(): Promise<boolean>;
3
+ export declare function runTrivy(targetPath: string): Promise<Finding[]>;
4
+ //# sourceMappingURL=trivy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trivy.d.ts","sourceRoot":"","sources":["../../../../src/engines/pattern/trivy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAY,MAAM,gBAAgB,CAAC;AA2FxD,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO5D;AAED,wBAAsB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CA0BrE"}
@@ -0,0 +1,90 @@
1
+ import { execFile } from 'node:child_process';
2
+ function execFilePromise(cmd, args) {
3
+ return new Promise((resolve, reject) => {
4
+ execFile(cmd, args, (error, stdout, stderr) => {
5
+ if (error) {
6
+ // Attach stdout/stderr to the error so callers can still read output
7
+ const enrichedError = error;
8
+ enrichedError.stdout = typeof stdout === 'string' ? stdout : '';
9
+ enrichedError.stderr = typeof stderr === 'string' ? stderr : '';
10
+ reject(enrichedError);
11
+ return;
12
+ }
13
+ resolve({
14
+ stdout: typeof stdout === 'string' ? stdout : '',
15
+ stderr: typeof stderr === 'string' ? stderr : '',
16
+ });
17
+ });
18
+ });
19
+ }
20
+ const SEVERITY_MAP = {
21
+ CRITICAL: 'critical',
22
+ HIGH: 'high',
23
+ MEDIUM: 'medium',
24
+ LOW: 'low',
25
+ };
26
+ function mapSeverity(trivySeverity) {
27
+ return SEVERITY_MAP[trivySeverity] ?? 'low';
28
+ }
29
+ function parseTrivyOutput(jsonString) {
30
+ const output = JSON.parse(jsonString);
31
+ const findings = [];
32
+ for (const result of output.Results) {
33
+ if (!result.Vulnerabilities) {
34
+ continue;
35
+ }
36
+ for (const vuln of result.Vulnerabilities) {
37
+ const hasFixedVersion = vuln.FixedVersion !== '' && vuln.FixedVersion != null;
38
+ findings.push({
39
+ id: `trivy_${vuln.VulnerabilityID}_${vuln.PkgName}`,
40
+ engine: 'pattern',
41
+ severity: mapSeverity(vuln.Severity),
42
+ type: 'dependency_vulnerability',
43
+ file: result.Target,
44
+ line: 0,
45
+ description: `${vuln.VulnerabilityID}: ${vuln.Title} (${vuln.PkgName}@${vuln.InstalledVersion})`,
46
+ fix_suggestion: hasFixedVersion
47
+ ? `Upgrade ${vuln.PkgName} to ${vuln.FixedVersion}`
48
+ : 'No fix available yet',
49
+ auto_fixable: hasFixedVersion,
50
+ });
51
+ }
52
+ }
53
+ return findings;
54
+ }
55
+ export async function checkTrivyInstalled() {
56
+ try {
57
+ await execFilePromise('which', ['trivy']);
58
+ return true;
59
+ }
60
+ catch {
61
+ return false;
62
+ }
63
+ }
64
+ export async function runTrivy(targetPath) {
65
+ const installed = await checkTrivyInstalled();
66
+ if (!installed) {
67
+ console.warn('ShipSafe: trivy is not installed, skipping dependency vulnerability scan');
68
+ return [];
69
+ }
70
+ try {
71
+ const args = ['fs', '--format', 'json', '--quiet', targetPath];
72
+ const { stdout } = await execFilePromise('trivy', args);
73
+ return parseTrivyOutput(stdout);
74
+ }
75
+ catch (error) {
76
+ // If trivy exits non-zero but produced output, still try to parse it
77
+ const execError = error;
78
+ if (execError.stdout) {
79
+ try {
80
+ return parseTrivyOutput(execError.stdout);
81
+ }
82
+ catch {
83
+ // JSON parse failed on the output — fall through to warn
84
+ }
85
+ }
86
+ console.warn('ShipSafe: trivy scan failed', execError.message);
87
+ return [];
88
+ }
89
+ }
90
+ //# sourceMappingURL=trivy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trivy.js","sourceRoot":"","sources":["../../../../src/engines/pattern/trivy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Generate a JWT for GitHub App authentication.
3
+ * Uses RS256 signing with the App's private key.
4
+ */
5
+ export declare function generateJwt(appId: string, privateKey: string): string;
6
+ /**
7
+ * Get an installation access token for GitHub API calls.
8
+ * Requires SHIPSAFE_GITHUB_APP_ID and SHIPSAFE_GITHUB_PRIVATE_KEY env vars.
9
+ */
10
+ export declare function getInstallationToken(installationId: number): Promise<string>;
11
+ /**
12
+ * Make an authenticated GitHub API request.
13
+ */
14
+ export declare function githubApi(path: string, options: {
15
+ method?: string;
16
+ body?: unknown;
17
+ token: string;
18
+ }): Promise<unknown>;
19
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/github/api.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAiBrE;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA+BlF;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IACP,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf,GACA,OAAO,CAAC,OAAO,CAAC,CAgClB"}
@@ -0,0 +1,75 @@
1
+ import { createSign } from 'node:crypto';
2
+ /**
3
+ * Generate a JWT for GitHub App authentication.
4
+ * Uses RS256 signing with the App's private key.
5
+ */
6
+ export function generateJwt(appId, privateKey) {
7
+ const now = Math.floor(Date.now() / 1000);
8
+ const header = Buffer.from(JSON.stringify({ alg: 'RS256', typ: 'JWT' })).toString('base64url');
9
+ const payload = Buffer.from(JSON.stringify({
10
+ iat: now - 60, // issued 60s ago to account for clock drift
11
+ exp: now + 600, // expires in 10 minutes
12
+ iss: appId,
13
+ })).toString('base64url');
14
+ const unsigned = `${header}.${payload}`;
15
+ const signer = createSign('RSA-SHA256');
16
+ signer.update(unsigned);
17
+ const signature = signer.sign(privateKey, 'base64url');
18
+ return `${unsigned}.${signature}`;
19
+ }
20
+ /**
21
+ * Get an installation access token for GitHub API calls.
22
+ * Requires SHIPSAFE_GITHUB_APP_ID and SHIPSAFE_GITHUB_PRIVATE_KEY env vars.
23
+ */
24
+ export async function getInstallationToken(installationId) {
25
+ const appId = process.env.SHIPSAFE_GITHUB_APP_ID;
26
+ const privateKey = process.env.SHIPSAFE_GITHUB_PRIVATE_KEY;
27
+ if (!appId || !privateKey) {
28
+ throw new Error('Missing SHIPSAFE_GITHUB_APP_ID or SHIPSAFE_GITHUB_PRIVATE_KEY environment variables');
29
+ }
30
+ const jwt = generateJwt(appId, privateKey);
31
+ const response = await fetch(`https://api.github.com/app/installations/${installationId}/access_tokens`, {
32
+ method: 'POST',
33
+ headers: {
34
+ Authorization: `Bearer ${jwt}`,
35
+ Accept: 'application/vnd.github+json',
36
+ 'X-GitHub-Api-Version': '2022-11-28',
37
+ },
38
+ });
39
+ if (!response.ok) {
40
+ const body = await response.text();
41
+ throw new Error(`Failed to get installation token: ${response.status} ${body}`);
42
+ }
43
+ const data = (await response.json());
44
+ return data.token;
45
+ }
46
+ /**
47
+ * Make an authenticated GitHub API request.
48
+ */
49
+ export async function githubApi(path, options) {
50
+ const url = path.startsWith('https://') ? path : `https://api.github.com${path}`;
51
+ const headers = {
52
+ Authorization: `token ${options.token}`,
53
+ Accept: 'application/vnd.github+json',
54
+ 'X-GitHub-Api-Version': '2022-11-28',
55
+ };
56
+ const fetchOptions = {
57
+ method: options.method ?? 'GET',
58
+ headers,
59
+ };
60
+ if (options.body !== undefined) {
61
+ headers['Content-Type'] = 'application/json';
62
+ fetchOptions.body = JSON.stringify(options.body);
63
+ }
64
+ const response = await fetch(url, fetchOptions);
65
+ if (!response.ok) {
66
+ const body = await response.text();
67
+ throw new Error(`GitHub API error: ${response.status} ${body}`);
68
+ }
69
+ const contentType = response.headers.get('content-type') ?? '';
70
+ if (contentType.includes('application/json')) {
71
+ return response.json();
72
+ }
73
+ return response.text();
74
+ }
75
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/github/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * GitHub App manifest for ShipSafe registration.
3
+ *
4
+ * This manifest defines the permissions and events the ShipSafe GitHub App
5
+ * requires. The actual App registration is done manually on GitHub; this
6
+ * manifest serves as the canonical source of truth for the configuration.
7
+ *
8
+ * See: https://docs.github.com/en/apps/sharing-github-apps/registering-a-github-app-from-a-manifest
9
+ */
10
+ export declare const APP_MANIFEST: {
11
+ name: string;
12
+ url: string;
13
+ hook_attributes: {
14
+ url: string;
15
+ };
16
+ redirect_url: string;
17
+ setup_url: string;
18
+ public: boolean;
19
+ default_permissions: {
20
+ checks: "write";
21
+ contents: "read";
22
+ pull_requests: "write";
23
+ statuses: "write";
24
+ };
25
+ default_events: readonly ["pull_request", "push"];
26
+ };
27
+ export type AppManifest = typeof APP_MANIFEST;
28
+ //# sourceMappingURL=app-manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-manifest.d.ts","sourceRoot":"","sources":["../../../src/github/app-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;CAgBxB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,OAAO,YAAY,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * GitHub App manifest for ShipSafe registration.
3
+ *
4
+ * This manifest defines the permissions and events the ShipSafe GitHub App
5
+ * requires. The actual App registration is done manually on GitHub; this
6
+ * manifest serves as the canonical source of truth for the configuration.
7
+ *
8
+ * See: https://docs.github.com/en/apps/sharing-github-apps/registering-a-github-app-from-a-manifest
9
+ */
10
+ export const APP_MANIFEST = {
11
+ name: 'ShipSafe',
12
+ url: 'https://shipsafe.org',
13
+ hook_attributes: {
14
+ url: '', // filled in during setup with the webhook endpoint URL
15
+ },
16
+ redirect_url: '',
17
+ setup_url: '',
18
+ public: true,
19
+ default_permissions: {
20
+ checks: 'write',
21
+ contents: 'read',
22
+ pull_requests: 'write',
23
+ statuses: 'write',
24
+ },
25
+ default_events: ['pull_request', 'push'],
26
+ };
27
+ //# sourceMappingURL=app-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-manifest.js","sourceRoot":"","sources":["../../../src/github/app-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,UAAU;IAChB,GAAG,EAAE,sBAAsB;IAC3B,eAAe,EAAE;QACf,GAAG,EAAE,EAAE,EAAE,uDAAuD;KACjE;IACD,YAAY,EAAE,EAAE;IAChB,SAAS,EAAE,EAAE;IACb,MAAM,EAAE,IAAI;IACZ,mBAAmB,EAAE;QACnB,MAAM,EAAE,OAAgB;QACxB,QAAQ,EAAE,MAAe;QACzB,aAAa,EAAE,OAAgB;QAC/B,QAAQ,EAAE,OAAgB;KAC3B;IACD,cAAc,EAAE,CAAC,cAAc,EAAE,MAAM,CAAU;CAClD,CAAC"}
@@ -0,0 +1,36 @@
1
+ import type { Finding } from '../types.js';
2
+ /**
3
+ * Create a check run in 'in_progress' state.
4
+ * Returns the check run ID.
5
+ */
6
+ export declare function createCheckRun(options: {
7
+ repoFullName: string;
8
+ headSha: string;
9
+ installationId: number;
10
+ }): Promise<number>;
11
+ /**
12
+ * Format findings as GitHub check run annotations.
13
+ */
14
+ export declare function formatAnnotations(findings: Finding[]): Array<{
15
+ path: string;
16
+ start_line: number;
17
+ end_line: number;
18
+ annotation_level: 'failure' | 'warning' | 'notice';
19
+ message: string;
20
+ title: string;
21
+ }>;
22
+ /**
23
+ * Build a summary string for the check run output.
24
+ */
25
+ export declare function buildSummary(findings: Finding[]): string;
26
+ /**
27
+ * Complete a check run with results.
28
+ */
29
+ export declare function completeCheckRun(options: {
30
+ repoFullName: string;
31
+ checkRunId: number;
32
+ installationId: number;
33
+ conclusion: 'success' | 'failure' | 'neutral';
34
+ findings: Finding[];
35
+ }): Promise<void>;
36
+ //# sourceMappingURL=checks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../../../src/github/checks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAG3C;;;GAGG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;CACxB,GAAG,OAAO,CAAC,MAAM,CAAC,CAelB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,OAAO,EAAE,GAClB,KAAK,CAAC;IACP,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CASD;AAgBD;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAiBxD;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IAC9C,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBhB"}
@@ -0,0 +1,90 @@
1
+ import { getInstallationToken, githubApi } from './api.js';
2
+ /**
3
+ * Create a check run in 'in_progress' state.
4
+ * Returns the check run ID.
5
+ */
6
+ export async function createCheckRun(options) {
7
+ const token = await getInstallationToken(options.installationId);
8
+ const result = (await githubApi(`/repos/${options.repoFullName}/check-runs`, {
9
+ method: 'POST',
10
+ token,
11
+ body: {
12
+ name: 'ShipSafe Security Scan',
13
+ head_sha: options.headSha,
14
+ status: 'in_progress',
15
+ started_at: new Date().toISOString(),
16
+ },
17
+ }));
18
+ return result.id;
19
+ }
20
+ /**
21
+ * Format findings as GitHub check run annotations.
22
+ */
23
+ export function formatAnnotations(findings) {
24
+ return findings.map((finding) => ({
25
+ path: finding.file,
26
+ start_line: finding.line,
27
+ end_line: finding.line,
28
+ annotation_level: severityToAnnotationLevel(finding.severity),
29
+ message: `${finding.description}\n\nFix: ${finding.fix_suggestion}`,
30
+ title: `[${finding.severity.toUpperCase()}] ${finding.type}`,
31
+ }));
32
+ }
33
+ function severityToAnnotationLevel(severity) {
34
+ switch (severity) {
35
+ case 'critical':
36
+ case 'high':
37
+ return 'failure';
38
+ case 'medium':
39
+ return 'warning';
40
+ default:
41
+ return 'notice';
42
+ }
43
+ }
44
+ /**
45
+ * Build a summary string for the check run output.
46
+ */
47
+ export function buildSummary(findings) {
48
+ if (findings.length === 0) {
49
+ return 'ShipSafe found no security issues. Ship it!';
50
+ }
51
+ const critical = findings.filter((f) => f.severity === 'critical').length;
52
+ const high = findings.filter((f) => f.severity === 'high').length;
53
+ const medium = findings.filter((f) => f.severity === 'medium').length;
54
+ const low = findings.filter((f) => f.severity === 'low').length;
55
+ const parts = [];
56
+ if (critical > 0)
57
+ parts.push(`${critical} critical`);
58
+ if (high > 0)
59
+ parts.push(`${high} high`);
60
+ if (medium > 0)
61
+ parts.push(`${medium} medium`);
62
+ if (low > 0)
63
+ parts.push(`${low} low`);
64
+ return `ShipSafe found ${findings.length} issues (${parts.join(', ')})`;
65
+ }
66
+ /**
67
+ * Complete a check run with results.
68
+ */
69
+ export async function completeCheckRun(options) {
70
+ const token = await getInstallationToken(options.installationId);
71
+ const annotations = formatAnnotations(options.findings);
72
+ const summary = buildSummary(options.findings);
73
+ // GitHub API limits annotations to 50 per request
74
+ const annotationBatch = annotations.slice(0, 50);
75
+ await githubApi(`/repos/${options.repoFullName}/check-runs/${options.checkRunId}`, {
76
+ method: 'PATCH',
77
+ token,
78
+ body: {
79
+ status: 'completed',
80
+ conclusion: options.conclusion,
81
+ completed_at: new Date().toISOString(),
82
+ output: {
83
+ title: 'ShipSafe Security Scan',
84
+ summary,
85
+ annotations: annotationBatch,
86
+ },
87
+ },
88
+ });
89
+ }
90
+ //# sourceMappingURL=checks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checks.js","sourceRoot":"","sources":["../../../src/github/checks.ts"],"names":[],"mappings":"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"}