ai-project-maintainer 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +175 -0
- package/ai-project-maintainer/SKILL.md +62 -0
- package/ai-project-maintainer/agents/openai.yaml +6 -0
- package/ai-project-maintainer/references/ci-guardrails.md +55 -0
- package/ai-project-maintainer/references/database.md +60 -0
- package/ai-project-maintainer/references/electron-desktop.md +43 -0
- package/ai-project-maintainer/references/incident-response.md +52 -0
- package/ai-project-maintainer/references/local-gate.md +117 -0
- package/ai-project-maintainer/references/security.md +48 -0
- package/ai-project-maintainer/references/tool-router.md +53 -0
- package/ai-project-maintainer/scripts/audit-plan.mjs +155 -0
- package/ai-project-maintainer/scripts/bootstrap-local-tools.ps1 +109 -0
- package/ai-project-maintainer/scripts/check-syntax.mjs +41 -0
- package/ai-project-maintainer/scripts/ci-smoke-gate.mjs +26 -0
- package/ai-project-maintainer/scripts/cli.mjs +165 -0
- package/ai-project-maintainer/scripts/doctor.mjs +80 -0
- package/ai-project-maintainer/scripts/init-audit.mjs +105 -0
- package/ai-project-maintainer/scripts/init-project.mjs +229 -0
- package/ai-project-maintainer/scripts/lib/check-registry.mjs +68 -0
- package/ai-project-maintainer/scripts/lib/checks.mjs +337 -0
- package/ai-project-maintainer/scripts/lib/command-runner.mjs +130 -0
- package/ai-project-maintainer/scripts/lib/intake.mjs +172 -0
- package/ai-project-maintainer/scripts/lib/policy.mjs +150 -0
- package/ai-project-maintainer/scripts/lib/project-detect.mjs +111 -0
- package/ai-project-maintainer/scripts/lib/report.mjs +227 -0
- package/ai-project-maintainer/scripts/probe-project.mjs +218 -0
- package/ai-project-maintainer/scripts/report-summary.mjs +25 -0
- package/ai-project-maintainer/scripts/run-local-gate.mjs +147 -0
- package/docs/CI-GITHUB-ACTIONS.zh-CN.md +83 -0
- package/docs/DEMO.md +81 -0
- package/docs/DEMO.zh-CN.md +81 -0
- package/docs/GITHUB-LAUNCH-CHECKLIST.md +77 -0
- package/docs/INSTALL.zh-CN.md +112 -0
- package/docs/INTAKE-SCHEMA.zh-CN.md +105 -0
- package/docs/POLICY-AND-EXCEPTIONS.zh-CN.md +96 -0
- package/docs/PRODUCTION-AUDIT.zh-CN.md +89 -0
- package/docs/PROMOTION.md +116 -0
- package/docs/UPGRADE-ROADMAP.zh-CN.md +47 -0
- package/docs/demo-output/security-report.md +57 -0
- package/docs/superpowers/plans/2026-06-29-ci-dogfooding.md +200 -0
- package/package.json +21 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import YAML from "yaml";
|
|
6
|
+
import {
|
|
7
|
+
defaultBusinessFlows,
|
|
8
|
+
defaultEvidenceSources,
|
|
9
|
+
defaultProjectProfile,
|
|
10
|
+
defaultRiskPolicy,
|
|
11
|
+
} from "./lib/intake.mjs";
|
|
12
|
+
|
|
13
|
+
const markdownTemplates = {
|
|
14
|
+
".ai-maintainer/threat-model.md": `# Threat Model
|
|
15
|
+
|
|
16
|
+
## Assets
|
|
17
|
+
|
|
18
|
+
- Source code
|
|
19
|
+
- User data
|
|
20
|
+
- Credentials and local secrets
|
|
21
|
+
|
|
22
|
+
## Trust Boundaries
|
|
23
|
+
|
|
24
|
+
- Browser or renderer process
|
|
25
|
+
- API or main process
|
|
26
|
+
- Database and file system
|
|
27
|
+
- CI/CD and release artifacts
|
|
28
|
+
|
|
29
|
+
## User Decisions
|
|
30
|
+
|
|
31
|
+
- Confirm the real roles, permissions, and sensitive data before production review.
|
|
32
|
+
`,
|
|
33
|
+
".ai-maintainer/release-checklist.yml": `schema_version: 1
|
|
34
|
+
release:
|
|
35
|
+
has_staging: false
|
|
36
|
+
has_smoke_tests: false
|
|
37
|
+
has_manual_approval: false
|
|
38
|
+
has_rollback_plan: false
|
|
39
|
+
has_versioned_artifacts: false
|
|
40
|
+
`,
|
|
41
|
+
".ai-maintainer/incident-runbook.md": `# Incident Runbook
|
|
42
|
+
|
|
43
|
+
## First Response
|
|
44
|
+
|
|
45
|
+
- Identify current version and latest deploy.
|
|
46
|
+
- Check error monitoring, logs, and recent migrations.
|
|
47
|
+
- Decide whether rollback is safe.
|
|
48
|
+
|
|
49
|
+
## User Decisions
|
|
50
|
+
|
|
51
|
+
- Define who can approve rollback and user communication.
|
|
52
|
+
`,
|
|
53
|
+
".ai-maintainer/db-migration-policy.yml": `schema_version: 1
|
|
54
|
+
database:
|
|
55
|
+
changes_use_migrations: true
|
|
56
|
+
destructive_changes_require_review: true
|
|
57
|
+
backup_before_production_migration: false
|
|
58
|
+
rollback_or_forward_fix_required: false
|
|
59
|
+
`,
|
|
60
|
+
".ai-maintainer/observability-checklist.yml": `schema_version: 1
|
|
61
|
+
observability:
|
|
62
|
+
error_monitoring: false
|
|
63
|
+
structured_logs: false
|
|
64
|
+
metrics: false
|
|
65
|
+
alerts: false
|
|
66
|
+
release_tracking: false
|
|
67
|
+
`,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
function yamlTemplate(value) {
|
|
71
|
+
return YAML.stringify(value);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function safeWrite(root, relativePath, content, result) {
|
|
75
|
+
const full = path.join(root, ...relativePath.split("/"));
|
|
76
|
+
fs.mkdirSync(path.dirname(full), { recursive: true });
|
|
77
|
+
if (fs.existsSync(full)) {
|
|
78
|
+
result.skipped.push(relativePath);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
fs.writeFileSync(full, content);
|
|
82
|
+
result.created.push(relativePath);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function initAudit(projectRoot) {
|
|
86
|
+
const root = path.resolve(projectRoot || process.cwd());
|
|
87
|
+
const result = { root, created: [], skipped: [] };
|
|
88
|
+
|
|
89
|
+
safeWrite(root, ".ai-maintainer/project-profile.yml", yamlTemplate(defaultProjectProfile), result);
|
|
90
|
+
safeWrite(root, ".ai-maintainer/evidence-sources.yml", yamlTemplate(defaultEvidenceSources), result);
|
|
91
|
+
safeWrite(root, ".ai-maintainer/business-flows.yml", yamlTemplate(defaultBusinessFlows), result);
|
|
92
|
+
safeWrite(root, ".ai-maintainer/risk-policy.yml", yamlTemplate(defaultRiskPolicy), result);
|
|
93
|
+
for (const [relativePath, content] of Object.entries(markdownTemplates)) safeWrite(root, relativePath, content, result);
|
|
94
|
+
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function main() {
|
|
99
|
+
const projectRoot = process.argv.slice(2).find((arg) => !arg.startsWith("--")) || process.cwd();
|
|
100
|
+
console.log(JSON.stringify(initAudit(projectRoot), null, 2));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
104
|
+
main();
|
|
105
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const policyTemplate = `profile: oss
|
|
7
|
+
mode: strict
|
|
8
|
+
checks:
|
|
9
|
+
gitleaks: block
|
|
10
|
+
trivy: block
|
|
11
|
+
semgrep: block
|
|
12
|
+
osv-scanner: warn
|
|
13
|
+
syft: warn
|
|
14
|
+
grype: warn
|
|
15
|
+
actionlint: block
|
|
16
|
+
zizmor: warn
|
|
17
|
+
checkov: warn
|
|
18
|
+
trivy-config: warn
|
|
19
|
+
scorecard: warn
|
|
20
|
+
megalinter: warn
|
|
21
|
+
pre-commit: warn
|
|
22
|
+
fail_on:
|
|
23
|
+
tests: true
|
|
24
|
+
secrets: true
|
|
25
|
+
dependency_high_or_critical: true
|
|
26
|
+
semgrep_blocking: true
|
|
27
|
+
trivy_unavailable: true
|
|
28
|
+
electron_dangerous_settings: true
|
|
29
|
+
ci_security_high: true
|
|
30
|
+
warn_on:
|
|
31
|
+
dev_dependency_vulnerabilities: true
|
|
32
|
+
missing_optional_tools: true
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
const exceptionsTemplate = `exceptions:
|
|
36
|
+
- id: "example-dev-only-vuln"
|
|
37
|
+
check: "npm audit"
|
|
38
|
+
reason: "dev-only transitive dependency, not shipped"
|
|
39
|
+
expires: "2026-09-01"
|
|
40
|
+
owner: "repo-owner"
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
const workflowTemplate = `name: Security Gate
|
|
44
|
+
|
|
45
|
+
on:
|
|
46
|
+
pull_request:
|
|
47
|
+
push:
|
|
48
|
+
branches:
|
|
49
|
+
- main
|
|
50
|
+
- master
|
|
51
|
+
|
|
52
|
+
permissions:
|
|
53
|
+
contents: read
|
|
54
|
+
security-events: write
|
|
55
|
+
actions: read
|
|
56
|
+
|
|
57
|
+
jobs:
|
|
58
|
+
security-gate:
|
|
59
|
+
runs-on: ubuntu-latest
|
|
60
|
+
steps:
|
|
61
|
+
- name: Checkout project
|
|
62
|
+
uses: actions/checkout@v4
|
|
63
|
+
|
|
64
|
+
- name: Setup Node
|
|
65
|
+
uses: actions/setup-node@v4
|
|
66
|
+
with:
|
|
67
|
+
node-version: "22"
|
|
68
|
+
|
|
69
|
+
- name: Setup Go for scanner CLIs
|
|
70
|
+
uses: actions/setup-go@v5
|
|
71
|
+
with:
|
|
72
|
+
go-version: "1.23"
|
|
73
|
+
|
|
74
|
+
- name: Install project dependencies
|
|
75
|
+
shell: bash
|
|
76
|
+
run: |
|
|
77
|
+
set -euo pipefail
|
|
78
|
+
if [ -f pnpm-lock.yaml ]; then
|
|
79
|
+
corepack enable
|
|
80
|
+
pnpm install --frozen-lockfile
|
|
81
|
+
elif [ -f yarn.lock ]; then
|
|
82
|
+
corepack enable
|
|
83
|
+
yarn install --immutable || yarn install --frozen-lockfile
|
|
84
|
+
elif [ -f bun.lock ] || [ -f bun.lockb ]; then
|
|
85
|
+
curl -fsSL https://bun.sh/install | bash
|
|
86
|
+
echo "$HOME/.bun/bin" >> "$GITHUB_PATH"
|
|
87
|
+
"$HOME/.bun/bin/bun" install --frozen-lockfile
|
|
88
|
+
elif [ -f package-lock.json ]; then
|
|
89
|
+
npm ci
|
|
90
|
+
elif [ -f package.json ]; then
|
|
91
|
+
npm install
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
- name: Install security scanners
|
|
95
|
+
shell: bash
|
|
96
|
+
run: |
|
|
97
|
+
set -euo pipefail
|
|
98
|
+
mkdir -p "$HOME/.local/bin"
|
|
99
|
+
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
100
|
+
echo "$HOME/go/bin" >> "$GITHUB_PATH"
|
|
101
|
+
python -m pip install --user semgrep zizmor checkov
|
|
102
|
+
go install github.com/gitleaks/gitleaks/v8@latest
|
|
103
|
+
go install github.com/google/osv-scanner/cmd/osv-scanner@latest
|
|
104
|
+
go install github.com/rhysd/actionlint/cmd/actionlint@latest
|
|
105
|
+
go install github.com/anchore/syft/cmd/syft@latest
|
|
106
|
+
go install github.com/anchore/grype/cmd/grype@latest
|
|
107
|
+
curl -sSfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b "$HOME/.local/bin"
|
|
108
|
+
|
|
109
|
+
- name: Checkout AI Project Maintainer
|
|
110
|
+
shell: bash
|
|
111
|
+
run: |
|
|
112
|
+
set -euo pipefail
|
|
113
|
+
git clone --depth 1 https://github.com/xixifusi1213-gif/ai-project-maintainer.git "$RUNNER_TEMP/ai-project-maintainer"
|
|
114
|
+
cd "$RUNNER_TEMP/ai-project-maintainer"
|
|
115
|
+
npm ci --omit=dev || npm install --omit=dev
|
|
116
|
+
|
|
117
|
+
- name: Run security gate
|
|
118
|
+
shell: bash
|
|
119
|
+
run: |
|
|
120
|
+
set -euo pipefail
|
|
121
|
+
EXTRA_FLAGS=""
|
|
122
|
+
if [ -f ".ai-maintainer/project-profile.yml" ]; then
|
|
123
|
+
EXTRA_FLAGS="$EXTRA_FLAGS --production"
|
|
124
|
+
fi
|
|
125
|
+
node "$RUNNER_TEMP/ai-project-maintainer/ai-project-maintainer/scripts/run-local-gate.mjs" "$GITHUB_WORKSPACE" --strict --release $EXTRA_FLAGS --output reports/security-report.json
|
|
126
|
+
|
|
127
|
+
- name: Write gate summary
|
|
128
|
+
if: always()
|
|
129
|
+
shell: bash
|
|
130
|
+
run: |
|
|
131
|
+
if [ -f reports/security-report.md ]; then
|
|
132
|
+
cat reports/security-report.md >> "$GITHUB_STEP_SUMMARY"
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
- name: Upload SARIF to code scanning
|
|
136
|
+
if: always()
|
|
137
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
138
|
+
with:
|
|
139
|
+
sarif_file: reports/security-report.sarif
|
|
140
|
+
continue-on-error: true
|
|
141
|
+
|
|
142
|
+
- name: Upload security reports
|
|
143
|
+
if: always()
|
|
144
|
+
uses: actions/upload-artifact@v4
|
|
145
|
+
with:
|
|
146
|
+
name: security-reports
|
|
147
|
+
path: |
|
|
148
|
+
reports/security-report.json
|
|
149
|
+
reports/security-report.md
|
|
150
|
+
reports/security-report.sarif
|
|
151
|
+
reports/sbom.cdx.json
|
|
152
|
+
if-no-files-found: ignore
|
|
153
|
+
`;
|
|
154
|
+
|
|
155
|
+
const dependabotTemplate = `version: 2
|
|
156
|
+
updates:
|
|
157
|
+
- package-ecosystem: "github-actions"
|
|
158
|
+
directory: "/"
|
|
159
|
+
schedule:
|
|
160
|
+
interval: "weekly"
|
|
161
|
+
- package-ecosystem: "npm"
|
|
162
|
+
directory: "/"
|
|
163
|
+
schedule:
|
|
164
|
+
interval: "weekly"
|
|
165
|
+
`;
|
|
166
|
+
|
|
167
|
+
const preCommitTemplate = `repos:
|
|
168
|
+
- repo: local
|
|
169
|
+
hooks:
|
|
170
|
+
- id: ai-project-maintainer-local-gate
|
|
171
|
+
name: AI Project Maintainer local gate
|
|
172
|
+
entry: npx ai-project-maintainer gate .
|
|
173
|
+
language: system
|
|
174
|
+
pass_filenames: false
|
|
175
|
+
`;
|
|
176
|
+
|
|
177
|
+
function safeWrite(root, relativePath, content, result) {
|
|
178
|
+
const full = path.join(root, ...relativePath.split("/"));
|
|
179
|
+
fs.mkdirSync(path.dirname(full), { recursive: true });
|
|
180
|
+
if (fs.existsSync(full)) {
|
|
181
|
+
result.skipped.push(relativePath);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
fs.writeFileSync(full, content);
|
|
185
|
+
result.created.push(relativePath);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function initProject(projectRoot, options = {}) {
|
|
189
|
+
const root = path.resolve(projectRoot || process.cwd());
|
|
190
|
+
const result = { root, created: [], skipped: [] };
|
|
191
|
+
|
|
192
|
+
safeWrite(root, ".ai-maintainer/policy.yml", policyTemplate, result);
|
|
193
|
+
safeWrite(root, ".ai-maintainer/exceptions.yml", exceptionsTemplate, result);
|
|
194
|
+
if ((options.ci || "github") === "github") {
|
|
195
|
+
safeWrite(root, ".github/workflows/security-gate.yml", workflowTemplate, result);
|
|
196
|
+
safeWrite(root, ".github/dependabot.yml", dependabotTemplate, result);
|
|
197
|
+
}
|
|
198
|
+
safeWrite(root, "reports/.gitkeep", "", result);
|
|
199
|
+
if (options.preCommit) {
|
|
200
|
+
safeWrite(root, ".pre-commit-config.yaml", preCommitTemplate, result);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function parseArgs(args) {
|
|
207
|
+
const readOption = (name, fallback) => {
|
|
208
|
+
const index = args.indexOf(name);
|
|
209
|
+
if (index !== -1) return args[index + 1] || fallback;
|
|
210
|
+
const inline = args.find((arg) => arg.startsWith(`${name}=`));
|
|
211
|
+
return inline ? inline.slice(name.length + 1) : fallback;
|
|
212
|
+
};
|
|
213
|
+
return {
|
|
214
|
+
projectRoot: args.find((arg) => !arg.startsWith("--")) || process.cwd(),
|
|
215
|
+
profile: readOption("--profile", "oss"),
|
|
216
|
+
ci: readOption("--ci", "github"),
|
|
217
|
+
preCommit: args.includes("--pre-commit"),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function main() {
|
|
222
|
+
const args = parseArgs(process.argv.slice(2));
|
|
223
|
+
const result = initProject(args.projectRoot, { profile: args.profile, ci: args.ci, preCommit: args.preCommit });
|
|
224
|
+
console.log(JSON.stringify(result, null, 2));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
228
|
+
main();
|
|
229
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runCiSecurityChecks,
|
|
3
|
+
runDatabaseChecks,
|
|
4
|
+
runElectronChecks,
|
|
5
|
+
runGrypeChecks,
|
|
6
|
+
runIacChecks,
|
|
7
|
+
runMegaLinterChecks,
|
|
8
|
+
runOsvScannerChecks,
|
|
9
|
+
runPackageAuditChecks,
|
|
10
|
+
runPreCommitChecks,
|
|
11
|
+
runSastChecks,
|
|
12
|
+
runScorecardChecks,
|
|
13
|
+
runSecretChecks,
|
|
14
|
+
runSyftChecks,
|
|
15
|
+
runTestChecks,
|
|
16
|
+
runTrivyFilesystemChecks,
|
|
17
|
+
} from "./checks.mjs";
|
|
18
|
+
|
|
19
|
+
const builtinCheckRegistry = [
|
|
20
|
+
{ id: "tests", group: "tests", title: "Project tests and release scripts", requiredTools: ["npm", "pnpm", "yarn", "bun"], detect: (project) => Boolean(project.packageJson), run: ({ project, options }) => runTestChecks(project, options), defaultLevel: "block" },
|
|
21
|
+
{ id: "package-audit", group: "dependencies", title: "Package manager production audit", requiredTools: ["npm", "pnpm", "yarn"], detect: (project) => Boolean(project.packageJson), run: ({ project, options }) => runPackageAuditChecks(project, options), defaultLevel: "block" },
|
|
22
|
+
{ id: "gitleaks", group: "secrets", title: "Gitleaks secret scan", requiredTools: ["gitleaks"], detect: () => true, run: ({ project, options }) => runSecretChecks(project, options), defaultLevel: "block" },
|
|
23
|
+
{ id: "trivy", group: "dependencies", title: "Trivy filesystem scan", requiredTools: ["trivy"], detect: () => true, run: ({ project, options }) => runTrivyFilesystemChecks(project, options), defaultLevel: "block" },
|
|
24
|
+
{ id: "osv-scanner", group: "dependencies", title: "OSV-Scanner dependency scan", requiredTools: ["osv-scanner"], detect: (project) => (project.files || []).some((file) => /(^|\/)(package-lock\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb?|go\.sum|requirements.*\.txt|poetry\.lock|Cargo\.lock|Gemfile\.lock)$/i.test(file)), run: ({ project, options }) => runOsvScannerChecks(project, options), defaultLevel: "warn" },
|
|
25
|
+
{ id: "semgrep", group: "sast", title: "Semgrep static scan", requiredTools: ["semgrep"], detect: () => true, run: ({ project, options }) => runSastChecks(project, options), defaultLevel: "block" },
|
|
26
|
+
{ id: "syft", group: "supply-chain", title: "Syft SBOM", requiredTools: ["syft"], detect: () => true, run: ({ project, options }) => runSyftChecks(project, options), defaultLevel: "warn" },
|
|
27
|
+
{ id: "grype", group: "supply-chain", title: "Grype vulnerability scan", requiredTools: ["grype"], detect: () => true, run: ({ project, options }) => runGrypeChecks(project, options), defaultLevel: "warn" },
|
|
28
|
+
{ id: "actionlint", group: "ci-security", title: "actionlint workflow lint", requiredTools: ["actionlint"], detect: (project) => (project.riskSurfaces?.ci || []).length > 0, run: ({ project, options }) => runCiSecurityChecks(project, options).filter((check) => check.checkId === "actionlint"), defaultLevel: "block" },
|
|
29
|
+
{ id: "zizmor", group: "ci-security", title: "zizmor workflow security", requiredTools: ["zizmor"], detect: (project) => (project.riskSurfaces?.ci || []).length > 0, run: ({ project, options }) => runCiSecurityChecks(project, options).filter((check) => check.checkId === "zizmor"), defaultLevel: "warn" },
|
|
30
|
+
{ id: "checkov", group: "iac", title: "Checkov IaC scan", requiredTools: ["checkov"], detect: (project) => (project.riskSurfaces?.infra || []).length > 0, run: ({ project, options }) => runIacChecks(project, options).filter((check) => check.checkId === "checkov" || check.checkId === "trivy-config"), defaultLevel: "warn" },
|
|
31
|
+
{ id: "electron", group: "electron", title: "Electron baseline", requiredTools: [], detect: (project) => Boolean(project.electron?.detected), run: ({ project }) => runElectronChecks(project), defaultLevel: "block" },
|
|
32
|
+
{ id: "database", group: "database", title: "Database migration review", requiredTools: ["squawk"], detect: (project) => (project.riskSurfaces?.database || []).length > 0, run: ({ project, options }) => runDatabaseChecks(project, options), defaultLevel: "block" },
|
|
33
|
+
{ id: "scorecard", group: "oss-hygiene", title: "OpenSSF Scorecard", requiredTools: ["scorecard"], detect: () => true, run: ({ project, options }) => runScorecardChecks(project, options), defaultLevel: "warn" },
|
|
34
|
+
{ id: "pre-commit", group: "oss-hygiene", title: "pre-commit hooks", requiredTools: ["pre-commit"], detect: () => true, run: ({ project, options }) => runPreCommitChecks(project, options), defaultLevel: "warn" },
|
|
35
|
+
{ id: "megalinter", group: "oss-hygiene", title: "MegaLinter security profile", requiredTools: ["mega-linter-runner"], detect: () => true, run: ({ project, options }) => runMegaLinterChecks(project, options), defaultLevel: "warn" },
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
export function getBuiltinCheckRegistry() {
|
|
39
|
+
return builtinCheckRegistry.map((check) => ({ ...check }));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function resolveEnabledChecks(registry, policy = {}) {
|
|
43
|
+
const configuredLevels = policy.checks || {};
|
|
44
|
+
return registry
|
|
45
|
+
.map((check) => ({ ...check, level: configuredLevels[check.id] || check.defaultLevel }))
|
|
46
|
+
.filter((check) => check.level !== "off");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function runRegisteredChecks(project, options = {}) {
|
|
50
|
+
const policy = options.policy || {};
|
|
51
|
+
const registry = options.registry || getBuiltinCheckRegistry();
|
|
52
|
+
const enabledChecks = resolveEnabledChecks(registry, policy);
|
|
53
|
+
const results = [];
|
|
54
|
+
|
|
55
|
+
for (const definition of enabledChecks) {
|
|
56
|
+
if (!definition.detect(project, options)) continue;
|
|
57
|
+
const checks = definition.run({ project, options, definition });
|
|
58
|
+
for (const check of checks) {
|
|
59
|
+
results.push({
|
|
60
|
+
checkId: check.checkId || definition.id,
|
|
61
|
+
policyLevel: definition.level,
|
|
62
|
+
...check,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return results;
|
|
68
|
+
}
|