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,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Actions Integration
|
|
3
|
+
*
|
|
4
|
+
* Provides integration with GitHub Actions for automated security scanning
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface GitHubActionsConfig {
|
|
8
|
+
workflowName: string;
|
|
9
|
+
triggers: {
|
|
10
|
+
push?: { branches: string[] };
|
|
11
|
+
pullRequest?: { branches: string[] };
|
|
12
|
+
schedule?: { cron: string }[];
|
|
13
|
+
workflowDispatch?: boolean;
|
|
14
|
+
};
|
|
15
|
+
scanTypes: ('security' | 'secrets' | 'vulnerabilities' | 'compliance' | 'sbom')[];
|
|
16
|
+
failOnCritical: boolean;
|
|
17
|
+
failOnHigh: boolean;
|
|
18
|
+
uploadArtifacts: boolean;
|
|
19
|
+
createPRComments: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface WorkflowStep {
|
|
23
|
+
name: string;
|
|
24
|
+
uses?: string;
|
|
25
|
+
run?: string;
|
|
26
|
+
with?: Record<string, string | boolean | number>;
|
|
27
|
+
env?: Record<string, string>;
|
|
28
|
+
if?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface WorkflowJob {
|
|
32
|
+
name: string;
|
|
33
|
+
runsOn: string;
|
|
34
|
+
permissions?: Record<string, string>;
|
|
35
|
+
steps: WorkflowStep[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface GitHubWorkflow {
|
|
39
|
+
name: string;
|
|
40
|
+
on: Record<string, any>;
|
|
41
|
+
permissions?: Record<string, string>;
|
|
42
|
+
jobs: Record<string, WorkflowJob>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class GitHubActionsGenerator {
|
|
46
|
+
/**
|
|
47
|
+
* Generate a complete GitHub Actions workflow
|
|
48
|
+
*/
|
|
49
|
+
generateWorkflow(config: GitHubActionsConfig): string {
|
|
50
|
+
const workflow: GitHubWorkflow = {
|
|
51
|
+
name: config.workflowName,
|
|
52
|
+
on: this.buildTriggers(config.triggers),
|
|
53
|
+
permissions: {
|
|
54
|
+
contents: 'read',
|
|
55
|
+
'security-events': 'write',
|
|
56
|
+
'pull-requests': config.createPRComments ? 'write' : 'read',
|
|
57
|
+
},
|
|
58
|
+
jobs: {
|
|
59
|
+
'Guardrail-scan': this.buildScanJob(config),
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return this.toYAML(workflow);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Build workflow triggers
|
|
68
|
+
*/
|
|
69
|
+
private buildTriggers(triggers: GitHubActionsConfig['triggers']): Record<string, any> {
|
|
70
|
+
const on: Record<string, any> = {};
|
|
71
|
+
|
|
72
|
+
if (triggers.push) {
|
|
73
|
+
on['push'] = { branches: triggers.push.branches };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (triggers.pullRequest) {
|
|
77
|
+
on['pull_request'] = { branches: triggers.pullRequest.branches };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (triggers.schedule) {
|
|
81
|
+
on['schedule'] = triggers.schedule.map(s => ({ cron: s.cron }));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (triggers.workflowDispatch) {
|
|
85
|
+
on['workflow_dispatch'] = {};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return on;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Build the main scan job
|
|
93
|
+
*/
|
|
94
|
+
private buildScanJob(config: GitHubActionsConfig): WorkflowJob {
|
|
95
|
+
const steps: WorkflowStep[] = [
|
|
96
|
+
{
|
|
97
|
+
name: 'Checkout code',
|
|
98
|
+
uses: 'actions/checkout@v4',
|
|
99
|
+
with: { 'fetch-depth': 0 },
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'Setup Node.js',
|
|
103
|
+
uses: 'actions/setup-node@v4',
|
|
104
|
+
with: { 'node-version': '20' },
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'Install dependencies',
|
|
108
|
+
run: 'npm ci',
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
// Add scan steps based on config
|
|
113
|
+
if (config.scanTypes.includes('secrets')) {
|
|
114
|
+
steps.push({
|
|
115
|
+
name: 'Scan for secrets',
|
|
116
|
+
run: 'npx Guardrail scan:secrets --format sarif --output secrets-results.sarif',
|
|
117
|
+
env: { Guardrail_CI: 'true' },
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (config.scanTypes.includes('vulnerabilities')) {
|
|
122
|
+
steps.push({
|
|
123
|
+
name: 'Scan for vulnerabilities',
|
|
124
|
+
run: 'npx Guardrail scan:vulnerabilities --format sarif --output vuln-results.sarif',
|
|
125
|
+
env: { Guardrail_CI: 'true' },
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (config.scanTypes.includes('security')) {
|
|
130
|
+
steps.push({
|
|
131
|
+
name: 'Security scan',
|
|
132
|
+
run: 'npx Guardrail scan:security --format sarif --output security-results.sarif',
|
|
133
|
+
env: { Guardrail_CI: 'true' },
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (config.scanTypes.includes('compliance')) {
|
|
138
|
+
steps.push({
|
|
139
|
+
name: 'Compliance check',
|
|
140
|
+
run: 'npx Guardrail scan:compliance --format json --output compliance-results.json',
|
|
141
|
+
env: { Guardrail_CI: 'true' },
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (config.scanTypes.includes('sbom')) {
|
|
146
|
+
steps.push({
|
|
147
|
+
name: 'Generate SBOM',
|
|
148
|
+
run: 'npx Guardrail sbom:generate --format cyclonedx --output sbom.json',
|
|
149
|
+
env: { Guardrail_CI: 'true' },
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Upload SARIF results
|
|
154
|
+
steps.push({
|
|
155
|
+
name: 'Upload SARIF results',
|
|
156
|
+
uses: 'github/codeql-action/upload-sarif@v3',
|
|
157
|
+
with: {
|
|
158
|
+
'sarif_file': '.',
|
|
159
|
+
'category': 'Guardrail-security',
|
|
160
|
+
},
|
|
161
|
+
if: 'always()',
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Upload artifacts
|
|
165
|
+
if (config.uploadArtifacts) {
|
|
166
|
+
steps.push({
|
|
167
|
+
name: 'Upload scan artifacts',
|
|
168
|
+
uses: 'actions/upload-artifact@v4',
|
|
169
|
+
with: {
|
|
170
|
+
name: 'Guardrail-results',
|
|
171
|
+
path: '*.sarif\n*.json',
|
|
172
|
+
'retention-days': 30,
|
|
173
|
+
},
|
|
174
|
+
if: 'always()',
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Check results and fail if needed
|
|
179
|
+
steps.push({
|
|
180
|
+
name: 'Check scan results',
|
|
181
|
+
run: this.buildCheckScript(config),
|
|
182
|
+
env: {
|
|
183
|
+
FAIL_ON_CRITICAL: String(config.failOnCritical),
|
|
184
|
+
FAIL_ON_HIGH: String(config.failOnHigh),
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
name: 'Guardrail Security Scan',
|
|
190
|
+
runsOn: 'ubuntu-latest',
|
|
191
|
+
steps,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Build result check script
|
|
197
|
+
*/
|
|
198
|
+
private buildCheckScript(config: GitHubActionsConfig): string {
|
|
199
|
+
return `
|
|
200
|
+
npx Guardrail results:check \\
|
|
201
|
+
--fail-on-critical=${config.failOnCritical} \\
|
|
202
|
+
--fail-on-high=${config.failOnHigh}
|
|
203
|
+
`.trim();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Convert workflow object to YAML
|
|
208
|
+
*/
|
|
209
|
+
private toYAML(workflow: GitHubWorkflow): string {
|
|
210
|
+
const lines: string[] = [];
|
|
211
|
+
|
|
212
|
+
lines.push(`name: ${workflow.name}`);
|
|
213
|
+
lines.push('');
|
|
214
|
+
lines.push('on:');
|
|
215
|
+
lines.push(this.objectToYAML(workflow.on, 2));
|
|
216
|
+
lines.push('');
|
|
217
|
+
|
|
218
|
+
if (workflow.permissions) {
|
|
219
|
+
lines.push('permissions:');
|
|
220
|
+
for (const [key, value] of Object.entries(workflow.permissions)) {
|
|
221
|
+
lines.push(` ${key}: ${value}`);
|
|
222
|
+
}
|
|
223
|
+
lines.push('');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
lines.push('jobs:');
|
|
227
|
+
for (const [jobId, job] of Object.entries(workflow.jobs)) {
|
|
228
|
+
lines.push(` ${jobId}:`);
|
|
229
|
+
lines.push(` name: ${job.name}`);
|
|
230
|
+
lines.push(` runs-on: ${job.runsOn}`);
|
|
231
|
+
lines.push(' steps:');
|
|
232
|
+
|
|
233
|
+
for (const step of job.steps) {
|
|
234
|
+
lines.push(` - name: ${step.name}`);
|
|
235
|
+
if (step.uses) {
|
|
236
|
+
lines.push(` uses: ${step.uses}`);
|
|
237
|
+
}
|
|
238
|
+
if (step.run) {
|
|
239
|
+
if (step.run.includes('\n')) {
|
|
240
|
+
lines.push(' run: |');
|
|
241
|
+
for (const line of step.run.split('\n')) {
|
|
242
|
+
lines.push(` ${line}`);
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
lines.push(` run: ${step.run}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (step.with) {
|
|
249
|
+
lines.push(' with:');
|
|
250
|
+
for (const [key, value] of Object.entries(step.with)) {
|
|
251
|
+
if (typeof value === 'string' && value.includes('\n')) {
|
|
252
|
+
lines.push(` ${key}: |`);
|
|
253
|
+
for (const line of value.split('\n')) {
|
|
254
|
+
lines.push(` ${line}`);
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
lines.push(` ${key}: ${value}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (step.env) {
|
|
262
|
+
lines.push(' env:');
|
|
263
|
+
for (const [key, value] of Object.entries(step.env)) {
|
|
264
|
+
lines.push(` ${key}: ${value}`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (step.if) {
|
|
268
|
+
lines.push(` if: ${step.if}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return lines.join('\n');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Convert object to YAML with indentation
|
|
278
|
+
*/
|
|
279
|
+
private objectToYAML(obj: Record<string, any>, indent: number): string {
|
|
280
|
+
const lines: string[] = [];
|
|
281
|
+
const prefix = ' '.repeat(indent);
|
|
282
|
+
|
|
283
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
284
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
285
|
+
lines.push(`${prefix}${key}:`);
|
|
286
|
+
lines.push(this.objectToYAML(value, indent + 2));
|
|
287
|
+
} else if (Array.isArray(value)) {
|
|
288
|
+
lines.push(`${prefix}${key}:`);
|
|
289
|
+
for (const item of value) {
|
|
290
|
+
if (typeof item === 'object') {
|
|
291
|
+
const entries = Object.entries(item);
|
|
292
|
+
if (entries.length > 0) {
|
|
293
|
+
const firstEntry = entries[0];
|
|
294
|
+
if (!firstEntry) continue;
|
|
295
|
+
const [firstKey, firstValue] = firstEntry;
|
|
296
|
+
lines.push(`${prefix} - ${firstKey}: ${firstValue}`);
|
|
297
|
+
for (const [k, v] of entries.slice(1)) {
|
|
298
|
+
lines.push(`${prefix} ${k}: ${v}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
} else {
|
|
302
|
+
lines.push(`${prefix} - ${item}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
} else {
|
|
306
|
+
lines.push(`${prefix}${key}: ${value}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return lines.join('\n');
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Generate default workflow for quick setup
|
|
315
|
+
*/
|
|
316
|
+
generateDefaultWorkflow(): string {
|
|
317
|
+
return this.generateWorkflow({
|
|
318
|
+
workflowName: 'Guardrail Security Scan',
|
|
319
|
+
triggers: {
|
|
320
|
+
push: { branches: ['main', 'master'] },
|
|
321
|
+
pullRequest: { branches: ['main', 'master'] },
|
|
322
|
+
schedule: [{ cron: '0 0 * * 0' }], // Weekly on Sunday
|
|
323
|
+
workflowDispatch: true,
|
|
324
|
+
},
|
|
325
|
+
scanTypes: ['security', 'secrets', 'vulnerabilities', 'sbom'],
|
|
326
|
+
failOnCritical: true,
|
|
327
|
+
failOnHigh: false,
|
|
328
|
+
uploadArtifacts: true,
|
|
329
|
+
createPRComments: true,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Export singleton
|
|
335
|
+
export const githubActionsGenerator = new GitHubActionsGenerator();
|
package/src/ci/index.ts
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-commit Hooks Integration
|
|
3
|
+
*
|
|
4
|
+
* Generates pre-commit hook configurations for local validation
|
|
5
|
+
* before commits are pushed to the repository
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface PreCommitConfig {
|
|
9
|
+
scanSecrets: boolean;
|
|
10
|
+
scanVulnerabilities: boolean;
|
|
11
|
+
checkCompliance: boolean;
|
|
12
|
+
validateTypes: boolean;
|
|
13
|
+
runLint: boolean;
|
|
14
|
+
runTests: boolean;
|
|
15
|
+
blockOnCritical: boolean;
|
|
16
|
+
blockOnHigh: boolean;
|
|
17
|
+
maxFileSize: number; // in KB
|
|
18
|
+
excludePatterns: string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface HuskyConfig {
|
|
22
|
+
hooks: {
|
|
23
|
+
'pre-commit'?: string;
|
|
24
|
+
'pre-push'?: string;
|
|
25
|
+
'commit-msg'?: string;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class PreCommitGenerator {
|
|
30
|
+
/**
|
|
31
|
+
* Generate Husky pre-commit configuration
|
|
32
|
+
*/
|
|
33
|
+
generateHuskyConfig(config: PreCommitConfig): HuskyConfig {
|
|
34
|
+
const commands: string[] = [];
|
|
35
|
+
|
|
36
|
+
if (config.runLint) {
|
|
37
|
+
commands.push('npx lint-staged');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (config.scanSecrets) {
|
|
41
|
+
commands.push('npx Guardrail scan:secrets --staged --fail-on-detection');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (config.validateTypes) {
|
|
45
|
+
commands.push('npx tsc --noEmit');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
hooks: {
|
|
50
|
+
'pre-commit': commands.join(' && '),
|
|
51
|
+
'pre-push': this.generatePrePushCommands(config),
|
|
52
|
+
'commit-msg': 'npx commitlint --edit $1',
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Generate pre-push commands
|
|
59
|
+
*/
|
|
60
|
+
private generatePrePushCommands(config: PreCommitConfig): string {
|
|
61
|
+
const commands: string[] = [];
|
|
62
|
+
|
|
63
|
+
if (config.scanVulnerabilities) {
|
|
64
|
+
commands.push('npx Guardrail scan:vulnerabilities');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (config.checkCompliance) {
|
|
68
|
+
commands.push('npx Guardrail scan:compliance');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (config.runTests) {
|
|
72
|
+
commands.push('npm test');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return commands.join(' && ');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Generate .husky/pre-commit script
|
|
80
|
+
*/
|
|
81
|
+
generatePreCommitScript(config: PreCommitConfig): string {
|
|
82
|
+
const lines: string[] = [
|
|
83
|
+
'#!/usr/bin/env sh',
|
|
84
|
+
'. "$(dirname -- "$0")/_/husky.sh"',
|
|
85
|
+
'',
|
|
86
|
+
'# Guardrail Pre-commit Hook',
|
|
87
|
+
'# Generated by Guardrail AI',
|
|
88
|
+
'',
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
if (config.runLint) {
|
|
92
|
+
lines.push('echo "Running lint-staged..."');
|
|
93
|
+
lines.push('npx lint-staged || exit 1');
|
|
94
|
+
lines.push('');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (config.scanSecrets) {
|
|
98
|
+
lines.push('echo "Scanning for secrets..."');
|
|
99
|
+
lines.push('npx Guardrail scan:secrets --staged --fail-on-detection || {');
|
|
100
|
+
lines.push(' echo "ERROR: Secrets detected in staged files!"');
|
|
101
|
+
lines.push(' echo "Please remove secrets before committing."');
|
|
102
|
+
lines.push(' exit 1');
|
|
103
|
+
lines.push('}');
|
|
104
|
+
lines.push('');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (config.validateTypes) {
|
|
108
|
+
lines.push('echo "Checking TypeScript types..."');
|
|
109
|
+
lines.push('npx tsc --noEmit || exit 1');
|
|
110
|
+
lines.push('');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (config.maxFileSize > 0) {
|
|
114
|
+
lines.push(`echo "Checking file sizes (max ${config.maxFileSize}KB)..."`);
|
|
115
|
+
lines.push(`MAX_SIZE=${config.maxFileSize * 1024}`);
|
|
116
|
+
lines.push('for file in $(git diff --cached --name-only); do');
|
|
117
|
+
lines.push(' if [ -f "$file" ]; then');
|
|
118
|
+
lines.push(' size=$(wc -c < "$file")');
|
|
119
|
+
lines.push(' if [ $size -gt $MAX_SIZE ]; then');
|
|
120
|
+
lines.push(' echo "ERROR: $file exceeds maximum file size"');
|
|
121
|
+
lines.push(' exit 1');
|
|
122
|
+
lines.push(' fi');
|
|
123
|
+
lines.push(' fi');
|
|
124
|
+
lines.push('done');
|
|
125
|
+
lines.push('');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
lines.push('echo "Pre-commit checks passed!"');
|
|
129
|
+
|
|
130
|
+
return lines.join('\n');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Generate .husky/pre-push script
|
|
135
|
+
*/
|
|
136
|
+
generatePrePushScript(config: PreCommitConfig): string {
|
|
137
|
+
const lines: string[] = [
|
|
138
|
+
'#!/usr/bin/env sh',
|
|
139
|
+
'. "$(dirname -- "$0")/_/husky.sh"',
|
|
140
|
+
'',
|
|
141
|
+
'# Guardrail Pre-push Hook',
|
|
142
|
+
'# Generated by Guardrail AI',
|
|
143
|
+
'',
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
if (config.scanVulnerabilities) {
|
|
147
|
+
lines.push('echo "Scanning for vulnerabilities..."');
|
|
148
|
+
const failCondition = config.blockOnCritical
|
|
149
|
+
? (config.blockOnHigh ? '--fail-on-high' : '--fail-on-critical')
|
|
150
|
+
: '';
|
|
151
|
+
lines.push(`npx Guardrail scan:vulnerabilities ${failCondition} || {`);
|
|
152
|
+
lines.push(' echo "ERROR: Vulnerabilities detected!"');
|
|
153
|
+
lines.push(' exit 1');
|
|
154
|
+
lines.push('}');
|
|
155
|
+
lines.push('');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (config.checkCompliance) {
|
|
159
|
+
lines.push('echo "Checking compliance..."');
|
|
160
|
+
lines.push('npx Guardrail scan:compliance || {');
|
|
161
|
+
lines.push(' echo "WARNING: Compliance issues detected"');
|
|
162
|
+
lines.push('}');
|
|
163
|
+
lines.push('');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (config.runTests) {
|
|
167
|
+
lines.push('echo "Running tests..."');
|
|
168
|
+
lines.push('npm test || {');
|
|
169
|
+
lines.push(' echo "ERROR: Tests failed!"');
|
|
170
|
+
lines.push(' exit 1');
|
|
171
|
+
lines.push('}');
|
|
172
|
+
lines.push('');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
lines.push('echo "Pre-push checks passed!"');
|
|
176
|
+
|
|
177
|
+
return lines.join('\n');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Generate lint-staged configuration
|
|
182
|
+
*/
|
|
183
|
+
generateLintStagedConfig(config: PreCommitConfig): Record<string, string[]> {
|
|
184
|
+
const lintStaged: Record<string, string[]> = {};
|
|
185
|
+
|
|
186
|
+
// TypeScript/JavaScript files
|
|
187
|
+
lintStaged['*.{ts,tsx,js,jsx}'] = [
|
|
188
|
+
'eslint --fix',
|
|
189
|
+
'prettier --write',
|
|
190
|
+
];
|
|
191
|
+
|
|
192
|
+
if (config.scanSecrets) {
|
|
193
|
+
lintStaged['*.{ts,tsx,js,jsx}'].push('Guardrail scan:secrets --file');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// JSON files
|
|
197
|
+
lintStaged['*.json'] = ['prettier --write'];
|
|
198
|
+
|
|
199
|
+
// Markdown files
|
|
200
|
+
lintStaged['*.md'] = ['prettier --write'];
|
|
201
|
+
|
|
202
|
+
// CSS/SCSS files
|
|
203
|
+
lintStaged['*.{css,scss}'] = ['prettier --write'];
|
|
204
|
+
|
|
205
|
+
return lintStaged;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Generate package.json scripts for hooks
|
|
210
|
+
*/
|
|
211
|
+
generatePackageJsonScripts(): Record<string, string> {
|
|
212
|
+
return {
|
|
213
|
+
'prepare': 'husky install',
|
|
214
|
+
'pre-commit': 'lint-staged',
|
|
215
|
+
'Guardrail:secrets': 'Guardrail scan:secrets',
|
|
216
|
+
'Guardrail:vulnerabilities': 'Guardrail scan:vulnerabilities',
|
|
217
|
+
'Guardrail:compliance': 'Guardrail scan:compliance',
|
|
218
|
+
'Guardrail:full': 'Guardrail scan:all',
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Generate commitlint configuration
|
|
224
|
+
*/
|
|
225
|
+
generateCommitlintConfig(): Record<string, any> {
|
|
226
|
+
return {
|
|
227
|
+
extends: ['@commitlint/config-conventional'],
|
|
228
|
+
rules: {
|
|
229
|
+
'type-enum': [
|
|
230
|
+
2,
|
|
231
|
+
'always',
|
|
232
|
+
[
|
|
233
|
+
'feat',
|
|
234
|
+
'fix',
|
|
235
|
+
'docs',
|
|
236
|
+
'style',
|
|
237
|
+
'refactor',
|
|
238
|
+
'perf',
|
|
239
|
+
'test',
|
|
240
|
+
'build',
|
|
241
|
+
'ci',
|
|
242
|
+
'chore',
|
|
243
|
+
'revert',
|
|
244
|
+
'security',
|
|
245
|
+
],
|
|
246
|
+
],
|
|
247
|
+
'subject-case': [2, 'always', 'lower-case'],
|
|
248
|
+
'subject-max-length': [2, 'always', 72],
|
|
249
|
+
'body-max-line-length': [2, 'always', 100],
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Generate default configuration
|
|
256
|
+
*/
|
|
257
|
+
generateDefaultConfig(): PreCommitConfig {
|
|
258
|
+
return {
|
|
259
|
+
scanSecrets: true,
|
|
260
|
+
scanVulnerabilities: true,
|
|
261
|
+
checkCompliance: false,
|
|
262
|
+
validateTypes: true,
|
|
263
|
+
runLint: true,
|
|
264
|
+
runTests: false,
|
|
265
|
+
blockOnCritical: true,
|
|
266
|
+
blockOnHigh: false,
|
|
267
|
+
maxFileSize: 500, // 500KB
|
|
268
|
+
excludePatterns: [
|
|
269
|
+
'node_modules/**',
|
|
270
|
+
'dist/**',
|
|
271
|
+
'build/**',
|
|
272
|
+
'*.min.js',
|
|
273
|
+
'*.bundle.js',
|
|
274
|
+
],
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Generate setup instructions
|
|
280
|
+
*/
|
|
281
|
+
generateSetupInstructions(): string {
|
|
282
|
+
return `
|
|
283
|
+
# Guardrail Pre-commit Hooks Setup
|
|
284
|
+
|
|
285
|
+
## Installation
|
|
286
|
+
|
|
287
|
+
1. Install dependencies:
|
|
288
|
+
\`\`\`bash
|
|
289
|
+
npm install -D husky lint-staged @commitlint/cli @commitlint/config-conventional
|
|
290
|
+
\`\`\`
|
|
291
|
+
|
|
292
|
+
2. Initialize Husky:
|
|
293
|
+
\`\`\`bash
|
|
294
|
+
npx husky install
|
|
295
|
+
\`\`\`
|
|
296
|
+
|
|
297
|
+
3. Add prepare script to package.json:
|
|
298
|
+
\`\`\`json
|
|
299
|
+
{
|
|
300
|
+
"scripts": {
|
|
301
|
+
"prepare": "husky install"
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
\`\`\`
|
|
305
|
+
|
|
306
|
+
4. Create pre-commit hook:
|
|
307
|
+
\`\`\`bash
|
|
308
|
+
npx husky add .husky/pre-commit "npx lint-staged"
|
|
309
|
+
\`\`\`
|
|
310
|
+
|
|
311
|
+
5. Create pre-push hook:
|
|
312
|
+
\`\`\`bash
|
|
313
|
+
npx husky add .husky/pre-push "npx Guardrail scan:vulnerabilities"
|
|
314
|
+
\`\`\`
|
|
315
|
+
|
|
316
|
+
## Configuration
|
|
317
|
+
|
|
318
|
+
Add lint-staged config to package.json:
|
|
319
|
+
\`\`\`json
|
|
320
|
+
{
|
|
321
|
+
"lint-staged": {
|
|
322
|
+
"*.{ts,tsx,js,jsx}": ["eslint --fix", "prettier --write"]
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
\`\`\`
|
|
326
|
+
|
|
327
|
+
## Usage
|
|
328
|
+
|
|
329
|
+
Hooks will run automatically on:
|
|
330
|
+
- **Pre-commit**: Lint, format, and secret scanning
|
|
331
|
+
- **Pre-push**: Vulnerability and compliance scanning
|
|
332
|
+
- **Commit-msg**: Conventional commit validation
|
|
333
|
+
`.trim();
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Export singleton
|
|
338
|
+
export const preCommitGenerator = new PreCommitGenerator();
|