ai-project-maintainer 0.3.0 → 0.4.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/README.md +44 -15
- package/ai-project-maintainer/scripts/init-project.mjs +3 -3
- package/docs/DEMO.md +69 -81
- package/docs/DEMO.zh-CN.md +69 -81
- package/docs/SECURITY-WORKFLOW.md +61 -0
- package/docs/UPGRADE-ROADMAP.zh-CN.md +58 -47
- package/docs/demo-output/security-report.md +64 -57
- package/examples/demo-ai-app/.ai-maintainer/business-flows.yml +14 -0
- package/examples/demo-ai-app/.ai-maintainer/db-migration-policy.yml +6 -0
- package/examples/demo-ai-app/.ai-maintainer/evidence-sources.yml +18 -0
- package/examples/demo-ai-app/.ai-maintainer/exceptions.yml +1 -0
- package/examples/demo-ai-app/.ai-maintainer/incident-runbook.md +11 -0
- package/examples/demo-ai-app/.ai-maintainer/observability-checklist.yml +7 -0
- package/examples/demo-ai-app/.ai-maintainer/policy.yml +27 -0
- package/examples/demo-ai-app/.ai-maintainer/project-profile.yml +15 -0
- package/examples/demo-ai-app/.ai-maintainer/release-checklist.yml +7 -0
- package/examples/demo-ai-app/.ai-maintainer/risk-policy.yml +5 -0
- package/examples/demo-ai-app/.ai-maintainer/threat-model.md +18 -0
- package/examples/demo-ai-app/README.md +38 -0
- package/examples/demo-ai-app/package-lock.json +15 -0
- package/examples/demo-ai-app/package.json +16 -0
- package/examples/demo-ai-app/scripts/build.mjs +18 -0
- package/examples/demo-ai-app/scripts/create-before-state.mjs +86 -0
- package/examples/demo-ai-app/scripts/run-demo-gate.mjs +95 -0
- package/examples/demo-ai-app/src/order-risk.js +28 -0
- package/examples/demo-ai-app/test/order-risk.test.mjs +24 -0
- package/package.json +40 -9
package/README.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# AI Project Maintainer
|
|
2
2
|
|
|
3
|
-

|
|
4
|
-

|
|
5
|
-

|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
[](https://www.npmjs.com/package/ai-project-maintainer)
|
|
6
7
|
[](https://github.com/xixifusi1213-gif/ai-project-maintainer/actions/workflows/ci.yml)
|
|
8
|
+
[](https://github.com/xixifusi1213-gif/ai-project-maintainer/actions/workflows/security.yml)
|
|
7
9
|
|
|
8
10
|
**A production-readiness gate for AI-coded projects.**
|
|
9
11
|
|
|
@@ -30,11 +32,13 @@ AI coding makes it easy to ship code that looks complete but quietly misses prod
|
|
|
30
32
|
|
|
31
33
|
`ai-project-maintainer` makes those gaps visible before they become production surprises.
|
|
32
34
|
|
|
33
|
-
## The 3-Minute Flow
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
## The 3-Minute Flow
|
|
36
|
+
|
|
37
|
+
Requires Node.js 20+.
|
|
38
|
+
|
|
39
|
+
```powershell
|
|
40
|
+
# 1. Add local and CI guardrails
|
|
41
|
+
npx ai-project-maintainer init "E:\my-project" --profile oss --ci github
|
|
38
42
|
|
|
39
43
|
# 2. Create the production audit intake templates
|
|
40
44
|
npx ai-project-maintainer init-audit "E:\my-project"
|
|
@@ -43,10 +47,34 @@ npx ai-project-maintainer init-audit "E:\my-project"
|
|
|
43
47
|
npx ai-project-maintainer audit-plan "E:\my-project" --output reports/audit-plan.json
|
|
44
48
|
|
|
45
49
|
# 4. Run the production gate
|
|
46
|
-
npx ai-project-maintainer gate "E:\my-project" --production --strict --release --output reports/security-report.json
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
npx ai-project-maintainer gate "E:\my-project" --production --strict --release --output reports/security-report.json
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
GitHub Actions templates can either use the npm package or clone this repository directly.
|
|
54
|
+
|
|
55
|
+
## Real Demo
|
|
56
|
+
|
|
57
|
+
This repository includes a runnable sample project at `examples/demo-ai-app`.
|
|
58
|
+
|
|
59
|
+
```powershell
|
|
60
|
+
npm test --prefix .\examples\demo-ai-app
|
|
61
|
+
npm run build --prefix .\examples\demo-ai-app
|
|
62
|
+
node .\examples\demo-ai-app\scripts\run-demo-gate.mjs
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The demo shows the intended workflow:
|
|
66
|
+
|
|
67
|
+
- healthy business tests and release build pass
|
|
68
|
+
- Gitleaks, Trivy, Semgrep, OSV, Syft, Grype, Scorecard, and CI checks are represented in the report
|
|
69
|
+
- production-readiness gaps remain visible for release approval, monitoring, logs, metrics, and alerts
|
|
70
|
+
|
|
71
|
+
To see the "before" state without committing unsafe fixtures:
|
|
72
|
+
|
|
73
|
+
```powershell
|
|
74
|
+
node .\examples\demo-ai-app\scripts\create-before-state.mjs
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
It writes a broken copy under the OS temp directory, where the business tests fail.
|
|
50
78
|
|
|
51
79
|
## What It Checks
|
|
52
80
|
|
|
@@ -156,9 +184,10 @@ It is designed for the practical middle ground: a personal developer or small te
|
|
|
156
184
|
|
|
157
185
|
## Documentation
|
|
158
186
|
|
|
159
|
-
- [Demo](docs/DEMO.md)
|
|
160
|
-
- [中文演示](docs/DEMO.zh-CN.md)
|
|
161
|
-
- [
|
|
187
|
+
- [Demo](docs/DEMO.md)
|
|
188
|
+
- [中文演示](docs/DEMO.zh-CN.md)
|
|
189
|
+
- [Security workflow](docs/SECURITY-WORKFLOW.md)
|
|
190
|
+
- [Production audit workflow](docs/PRODUCTION-AUDIT.zh-CN.md)
|
|
162
191
|
- [Intake schema](docs/INTAKE-SCHEMA.zh-CN.md)
|
|
163
192
|
- [Install guide](docs/INSTALL.zh-CN.md)
|
|
164
193
|
- [GitHub Actions guide](docs/CI-GITHUB-ACTIONS.zh-CN.md)
|
|
@@ -132,9 +132,9 @@ jobs:
|
|
|
132
132
|
cat reports/security-report.md >> "$GITHUB_STEP_SUMMARY"
|
|
133
133
|
fi
|
|
134
134
|
|
|
135
|
-
- name: Upload SARIF to code scanning
|
|
136
|
-
if: always()
|
|
137
|
-
uses: github/codeql-action/upload-sarif@
|
|
135
|
+
- name: Upload SARIF to code scanning
|
|
136
|
+
if: always()
|
|
137
|
+
uses: github/codeql-action/upload-sarif@v4
|
|
138
138
|
with:
|
|
139
139
|
sarif_file: reports/security-report.sarif
|
|
140
140
|
continue-on-error: true
|
package/docs/DEMO.md
CHANGED
|
@@ -1,81 +1,69 @@
|
|
|
1
|
-
# Demo: From AI-Coded Repo to Production Audit Report
|
|
2
|
-
|
|
3
|
-
This demo
|
|
4
|
-
|
|
5
|
-
## 1.
|
|
6
|
-
|
|
7
|
-
```powershell
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
.
|
|
27
|
-
|
|
28
|
-
.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
See [sample report](demo-output/security-report.md).
|
|
72
|
-
|
|
73
|
-
## 5. Let Codex Fix Blockers
|
|
74
|
-
|
|
75
|
-
Ask Codex:
|
|
76
|
-
|
|
77
|
-
```text
|
|
78
|
-
$ai-project-maintainer run the production gate for this project, fix blockers, and rerun until it passes.
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
Codex can handle deterministic blockers. The maintainer still owns business decisions such as critical flows, accepted risks, and production evidence.
|
|
1
|
+
# Demo: From AI-Coded Repo to Production Audit Report
|
|
2
|
+
|
|
3
|
+
This demo uses the runnable project in `examples/demo-ai-app`. It does not require any paid account or external API.
|
|
4
|
+
|
|
5
|
+
## 1. Run The Healthy Demo App
|
|
6
|
+
|
|
7
|
+
```powershell
|
|
8
|
+
npm test --prefix .\examples\demo-ai-app
|
|
9
|
+
npm run build --prefix .\examples\demo-ai-app
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The app is intentionally small: it quotes orders and decides whether paid orders can be released. The tests protect the business behavior that must not break.
|
|
13
|
+
|
|
14
|
+
## 2. Generate A Broken Before State
|
|
15
|
+
|
|
16
|
+
```powershell
|
|
17
|
+
node .\examples\demo-ai-app\scripts\create-before-state.mjs
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The command prints a temporary directory. Run this in that copied project:
|
|
21
|
+
|
|
22
|
+
```powershell
|
|
23
|
+
npm test
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
You should see the business tests fail. This is the "AI-coded project looks complete, but behavior is broken" stage. The broken copy is created under the OS temp directory, so the repository does not commit fake secrets or intentionally bad source files.
|
|
27
|
+
|
|
28
|
+
## 3. Run The Reproducible Demo Gate
|
|
29
|
+
|
|
30
|
+
```powershell
|
|
31
|
+
node .\examples\demo-ai-app\scripts\run-demo-gate.mjs
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
This script runs the real AI Project Maintainer gate with temporary scanner shims, so the sample report is stable even on a machine that has not installed Gitleaks, Trivy, Semgrep, OSV-Scanner, Syft, Grype, actionlint, zizmor, or Scorecard yet.
|
|
35
|
+
|
|
36
|
+
Expected result:
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
Local Security Gate: PASS
|
|
40
|
+
Blocking Checks: None
|
|
41
|
+
Coverage Gaps:
|
|
42
|
+
- Production release approval
|
|
43
|
+
- Error monitoring
|
|
44
|
+
- Production logs
|
|
45
|
+
- Production metrics
|
|
46
|
+
- Production alerts
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
See the generated-style [sample report](demo-output/security-report.md).
|
|
50
|
+
|
|
51
|
+
## 4. Run The Real Gate
|
|
52
|
+
|
|
53
|
+
After installing scanner CLIs, use the same command a real project would use:
|
|
54
|
+
|
|
55
|
+
```powershell
|
|
56
|
+
npx ai-project-maintainer gate .\examples\demo-ai-app --production --strict --release --output reports/security-report.json
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The point is not to pretend the project is perfect. The point is to make the checked failures and missing production evidence explicit before release.
|
|
60
|
+
|
|
61
|
+
## 5. Let Codex Fix Blockers
|
|
62
|
+
|
|
63
|
+
Ask Codex:
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
$ai-project-maintainer run the production gate for this project, fix blockers, and rerun until it passes.
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Codex can handle deterministic blockers. The maintainer still owns business decisions such as critical flows, accepted risks, and production evidence.
|
package/docs/DEMO.zh-CN.md
CHANGED
|
@@ -1,81 +1,69 @@
|
|
|
1
|
-
# 演示:从 AI
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## 1.
|
|
6
|
-
|
|
7
|
-
```powershell
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
查看 [示例报告](demo-output/security-report.md)。
|
|
72
|
-
|
|
73
|
-
## 5. 让 Codex 修阻断项
|
|
74
|
-
|
|
75
|
-
对 Codex 说:
|
|
76
|
-
|
|
77
|
-
```text
|
|
78
|
-
$ai-project-maintainer 对这个项目运行生产级门禁,修复阻断项,然后重新运行直到通过。
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
Codex 可以处理确定性的阻断项。项目负责人仍然负责核心业务流程、风险接受和生产证据判断。
|
|
1
|
+
# 演示:从 AI 写出的项目到生产审查报告
|
|
2
|
+
|
|
3
|
+
这个演示使用仓库里的真实项目:`examples/demo-ai-app`。不需要付费账号,也不需要外部 API。
|
|
4
|
+
|
|
5
|
+
## 1. 跑健康态项目
|
|
6
|
+
|
|
7
|
+
```powershell
|
|
8
|
+
npm test --prefix .\examples\demo-ai-app
|
|
9
|
+
npm run build --prefix .\examples\demo-ai-app
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
这个项目很小:它负责订单报价和订单释放判断。测试覆盖的是不能被 AI 修改坏的核心业务行为。
|
|
13
|
+
|
|
14
|
+
## 2. 生成坏掉的 before 状态
|
|
15
|
+
|
|
16
|
+
```powershell
|
|
17
|
+
node .\examples\demo-ai-app\scripts\create-before-state.mjs
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
命令会输出一个临时目录。进入那个复制出来的项目后运行:
|
|
21
|
+
|
|
22
|
+
```powershell
|
|
23
|
+
npm test
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
你会看到业务测试失败。这代表“AI 写出来看起来完整,但关键行为已经坏了”的阶段。坏代码只存在于系统临时目录,仓库不会提交假 secret 或故意脆弱的源码。
|
|
27
|
+
|
|
28
|
+
## 3. 跑可复现的 demo gate
|
|
29
|
+
|
|
30
|
+
```powershell
|
|
31
|
+
node .\examples\demo-ai-app\scripts\run-demo-gate.mjs
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
这个脚本会运行真实的 AI Project Maintainer gate,但会临时创建扫描器 mock,所以即使本机还没安装 Gitleaks、Trivy、Semgrep、OSV-Scanner、Syft、Grype、actionlint、zizmor、Scorecard,也能稳定生成示例报告。
|
|
35
|
+
|
|
36
|
+
预期结果:
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
Local Security Gate: PASS
|
|
40
|
+
Blocking Checks: None
|
|
41
|
+
Coverage Gaps:
|
|
42
|
+
- Production release approval
|
|
43
|
+
- Error monitoring
|
|
44
|
+
- Production logs
|
|
45
|
+
- Production metrics
|
|
46
|
+
- Production alerts
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
示例报告见:[sample report](demo-output/security-report.md)。
|
|
50
|
+
|
|
51
|
+
## 4. 跑真实 gate
|
|
52
|
+
|
|
53
|
+
安装扫描器 CLI 后,可以用真实项目同款命令:
|
|
54
|
+
|
|
55
|
+
```powershell
|
|
56
|
+
npx ai-project-maintainer gate .\examples\demo-ai-app --production --strict --release --output reports/security-report.json
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
这个工具不是为了假装项目完美,而是把已经检查失败的项和缺少的生产证据在发布前明确展示出来。
|
|
60
|
+
|
|
61
|
+
## 5. 让 Codex 修阻断项
|
|
62
|
+
|
|
63
|
+
可以这样要求 Codex:
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
$ai-project-maintainer run the production gate for this project, fix blockers, and rerun until it passes.
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Codex 适合处理确定性的阻断项。维护者仍然负责业务决策、风险接受和生产证据确认。
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Heavy Security Workflow
|
|
2
|
+
|
|
3
|
+
`v0.4.0` adds `.github/workflows/security.yml` so the repository dogfoods a heavier security gate.
|
|
4
|
+
|
|
5
|
+
## When It Runs
|
|
6
|
+
|
|
7
|
+
- pull requests to `main`
|
|
8
|
+
- pushes to `main`
|
|
9
|
+
- weekly scheduled run
|
|
10
|
+
- manual `workflow_dispatch`
|
|
11
|
+
|
|
12
|
+
## Blocking Checks
|
|
13
|
+
|
|
14
|
+
The first version blocks on high-confidence failures:
|
|
15
|
+
|
|
16
|
+
- `npm test`
|
|
17
|
+
- `npm run check`
|
|
18
|
+
- `npm pack --dry-run`
|
|
19
|
+
- AI Project Maintainer production gate
|
|
20
|
+
- Gitleaks findings through the production gate
|
|
21
|
+
- Trivy high/critical findings through the production gate
|
|
22
|
+
- Semgrep blocking findings through the production gate
|
|
23
|
+
- actionlint failures through the production gate
|
|
24
|
+
|
|
25
|
+
## Advisory Evidence
|
|
26
|
+
|
|
27
|
+
The workflow also captures extra evidence and uploads it as an artifact:
|
|
28
|
+
|
|
29
|
+
- raw Gitleaks JSON
|
|
30
|
+
- raw Trivy JSON
|
|
31
|
+
- Semgrep SARIF
|
|
32
|
+
- OSV-Scanner text output
|
|
33
|
+
- Syft CycloneDX SBOM
|
|
34
|
+
- Grype JSON
|
|
35
|
+
- actionlint output
|
|
36
|
+
- zizmor output
|
|
37
|
+
- OpenSSF Scorecard JSON
|
|
38
|
+
- AI Project Maintainer JSON, Markdown, SARIF, and SBOM reports
|
|
39
|
+
|
|
40
|
+
Some advisory steps use `continue-on-error: true` because early public projects often need one or two runs to tune scanner baselines. The production gate remains the release decision.
|
|
41
|
+
|
|
42
|
+
## Why Not Block Everything Immediately
|
|
43
|
+
|
|
44
|
+
This project is a public tool, so a permanently red workflow hurts trust as much as missing checks. The v0.4.0 policy is:
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
high-confidence release risk -> block
|
|
48
|
+
optional ecosystem signal -> report first
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Once the workflow has a stable baseline, optional checks can be promoted from warning to blocking in `.ai-maintainer/policy.yml`.
|
|
52
|
+
|
|
53
|
+
## Local Equivalent
|
|
54
|
+
|
|
55
|
+
The closest local command is:
|
|
56
|
+
|
|
57
|
+
```powershell
|
|
58
|
+
node .\ai-project-maintainer\scripts\run-local-gate.mjs . --production --strict --release --output reports/security-report.json
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Install scanner CLIs first, or use `node .\ai-project-maintainer\scripts\doctor.mjs` to see what is missing.
|
|
@@ -1,47 +1,58 @@
|
|
|
1
|
-
# 升级路线图
|
|
2
|
-
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
##
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
|
|
1
|
+
# 升级路线图
|
|
2
|
+
|
|
3
|
+
## v0.4.0:真实 demo 项目和重型安全 workflow
|
|
4
|
+
|
|
5
|
+
本阶段目标是把项目可信度从“有说明、有轻量 CI”提升到“陌生人能直接跑 demo,并且仓库自己跑重型安全证据链”。
|
|
6
|
+
|
|
7
|
+
已完成目标:
|
|
8
|
+
|
|
9
|
+
- 新增可复现的 `examples/demo-ai-app`,展示健康项目、坏掉的 before 状态、生产审查报告。
|
|
10
|
+
- 新增独立 `.github/workflows/security.yml`,安装并运行 Gitleaks、Trivy、Semgrep、OSV-Scanner、Syft、Grype、actionlint、zizmor、OpenSSF Scorecard 等安全工具。
|
|
11
|
+
- README 增加 Security badge 和 Real Demo 入口。
|
|
12
|
+
- Demo 文档改成基于真实项目,而不是纯文本示例。
|
|
13
|
+
|
|
14
|
+
阻断策略:
|
|
15
|
+
|
|
16
|
+
- 高置信风险阻断:测试、build、secret、Trivy high/critical、Semgrep blocking、actionlint、production gate。
|
|
17
|
+
- 可选生态信号先记录证据:OSV、Syft、Grype、zizmor、Scorecard。
|
|
18
|
+
|
|
19
|
+
后续还可以补:
|
|
20
|
+
|
|
21
|
+
- 90 秒 GIF/视频,演示 `before -> gate -> fix -> pass`。
|
|
22
|
+
- GitHub PR comment,把报告摘要直接写到 PR。
|
|
23
|
+
- 把稳定的可选检查逐步提升为阻断项。
|
|
24
|
+
|
|
25
|
+
## V2:开源维护者专业半自动平台
|
|
26
|
+
|
|
27
|
+
当前目标:
|
|
28
|
+
|
|
29
|
+
- 提供 npm/npx CLI。
|
|
30
|
+
- 保留 Codex skill 和旧 Node 脚本兼容。
|
|
31
|
+
- 使用插件化检查注册表。
|
|
32
|
+
- 使用 `yaml@2.9.0` 解析真实 YAML 配置。
|
|
33
|
+
- 生成 GitHub Actions、Dependabot、pre-commit 起步配置。
|
|
34
|
+
- 生成 JSON、Markdown、SARIF、SBOM 报告。
|
|
35
|
+
- 提供开源维护分。
|
|
36
|
+
|
|
37
|
+
V2 仍然免账号优先,不依赖 Bytebase、云、K8s、Sentry 或 Grafana。
|
|
38
|
+
|
|
39
|
+
## V3:平台证据接入
|
|
40
|
+
|
|
41
|
+
可选接入:
|
|
42
|
+
|
|
43
|
+
- Bytebase:SQL Review、审批流、发布状态。
|
|
44
|
+
- Sentry/Grafana/Loki/Datadog:发布后错误率、日志、trace 和事故时间线。
|
|
45
|
+
- 云平台:IAM、网络、安全组、公开入口。
|
|
46
|
+
- Kubernetes:RBAC、NetworkPolicy、PodSecurity、镜像和运行时风险。
|
|
47
|
+
|
|
48
|
+
这些能力需要账号或只读 token,所以不作为默认硬依赖。
|
|
49
|
+
|
|
50
|
+
## 不做
|
|
51
|
+
|
|
52
|
+
默认不做:
|
|
53
|
+
|
|
54
|
+
- 自动修改生产数据库。
|
|
55
|
+
- 主动攻击公网目标。
|
|
56
|
+
- 自动承担发布责任。
|
|
57
|
+
- 把扫描器二进制提交进仓库。
|
|
58
|
+
- 用 AI 替代维护者的最终判断。
|
|
@@ -1,57 +1,64 @@
|
|
|
1
|
-
# Local Security Gate: PASS
|
|
2
|
-
|
|
3
|
-
Root: `
|
|
4
|
-
Mode: strict=true, release=true, production=true
|
|
5
|
-
Open Source Maintenance Score:
|
|
6
|
-
|
|
7
|
-
## Blocking Checks
|
|
8
|
-
|
|
9
|
-
- None
|
|
10
|
-
|
|
11
|
-
## Warnings
|
|
12
|
-
|
|
13
|
-
- production audit:
|
|
14
|
-
- production audit: Error monitoring: GAP. Error monitoring evidence is missing.
|
|
15
|
-
- production audit: Production
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- Production
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
-
|
|
36
|
-
- PASS
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
1
|
+
# Local Security Gate: PASS
|
|
2
|
+
|
|
3
|
+
Root: `examples/demo-ai-app`
|
|
4
|
+
Mode: strict=true, release=true, production=true
|
|
5
|
+
Open Source Maintenance Score: 75/100 (B)
|
|
6
|
+
|
|
7
|
+
## Blocking Checks
|
|
8
|
+
|
|
9
|
+
- None
|
|
10
|
+
|
|
11
|
+
## Warnings
|
|
12
|
+
|
|
13
|
+
- production audit: Production release approval: GAP. Production deployment exists without approval evidence.
|
|
14
|
+
- production audit: Error monitoring: GAP. Error monitoring evidence is missing.
|
|
15
|
+
- production audit: Production logs: GAP. Production logs evidence is missing.
|
|
16
|
+
- production audit: Production metrics: GAP. Production metrics evidence is missing.
|
|
17
|
+
- production audit: Production alerts: GAP. Production alerts evidence is missing.
|
|
18
|
+
|
|
19
|
+
## Coverage Gaps
|
|
20
|
+
|
|
21
|
+
- Production release approval: use GitHub Environments or document the approval gate.
|
|
22
|
+
- Error monitoring: declare Sentry, OpenTelemetry, or another error source.
|
|
23
|
+
- Production logs: declare log evidence before relying on production recovery.
|
|
24
|
+
- Production metrics: declare release and service health metrics.
|
|
25
|
+
- Production alerts: declare alert routing before release.
|
|
26
|
+
|
|
27
|
+
## Production Audit
|
|
28
|
+
|
|
29
|
+
Project Type: node
|
|
30
|
+
Database: false
|
|
31
|
+
CI: true
|
|
32
|
+
|
|
33
|
+
### Plan
|
|
34
|
+
|
|
35
|
+
- PASS Production audit intake: project profile and evidence templates are present.
|
|
36
|
+
- PASS Critical business flows: 2 critical flows declared.
|
|
37
|
+
- PASS Business flow tests: 2 test references declared.
|
|
38
|
+
- N/A Electron security review: no Electron surface detected.
|
|
39
|
+
- PASS CI security review: CI workflow evidence detected.
|
|
40
|
+
- GAP Production release approval: production deployment exists without approval evidence.
|
|
41
|
+
- GAP Error monitoring: error monitoring evidence is missing.
|
|
42
|
+
- GAP Production logs: production logs evidence is missing.
|
|
43
|
+
- GAP Production metrics: production metrics evidence is missing.
|
|
44
|
+
- GAP Production alerts: production alerts evidence is missing.
|
|
45
|
+
- N/A Database migration review: no database surface detected or declared.
|
|
46
|
+
|
|
47
|
+
## Checks Run
|
|
48
|
+
|
|
49
|
+
- package test: pass
|
|
50
|
+
- release build: pass
|
|
51
|
+
- npm production audit: pass
|
|
52
|
+
- gitleaks secret scan: pass
|
|
53
|
+
- trivy filesystem scan: pass
|
|
54
|
+
- osv-scanner dependency scan: pass
|
|
55
|
+
- semgrep static scan: pass
|
|
56
|
+
- syft SBOM: pass
|
|
57
|
+
- grype vulnerability scan: pass
|
|
58
|
+
- OpenSSF Scorecard: pass
|
|
59
|
+
- production audit evidence checks: GAP items reported but not blocking by default
|
|
60
|
+
|
|
61
|
+
## Next Step
|
|
62
|
+
|
|
63
|
+
- Add real release approval, monitoring, logs, metrics, and alerts evidence.
|
|
64
|
+
- Rerun `gate --production --strict --release`.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
schema_version: 1
|
|
2
|
+
business_flows:
|
|
3
|
+
- id: "checkout-quote"
|
|
4
|
+
name: "Customer checkout quote"
|
|
5
|
+
criticality: "high"
|
|
6
|
+
expected_behavior: "A customer-visible total must include the selected shipping cost exactly once."
|
|
7
|
+
tests:
|
|
8
|
+
- "test/order-risk.test.mjs"
|
|
9
|
+
- id: "order-release"
|
|
10
|
+
name: "Paid order release"
|
|
11
|
+
criticality: "high"
|
|
12
|
+
expected_behavior: "An order can be released only when payment, stock, and risk checks all pass."
|
|
13
|
+
tests:
|
|
14
|
+
- "test/order-risk.test.mjs"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
schema_version: 1
|
|
2
|
+
evidence:
|
|
3
|
+
github_actions: "present"
|
|
4
|
+
deployment:
|
|
5
|
+
provider: "demo"
|
|
6
|
+
has_staging: true
|
|
7
|
+
has_production: true
|
|
8
|
+
production_requires_approval: false
|
|
9
|
+
observability:
|
|
10
|
+
errors: "none"
|
|
11
|
+
logs: "none"
|
|
12
|
+
metrics: "none"
|
|
13
|
+
alerts: "none"
|
|
14
|
+
database:
|
|
15
|
+
migrations: "none"
|
|
16
|
+
review_tool: "none"
|
|
17
|
+
backup_policy: "none"
|
|
18
|
+
rollback_plan: "none"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
exceptions: []
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Incident Runbook
|
|
2
|
+
|
|
3
|
+
## First Response
|
|
4
|
+
|
|
5
|
+
- Stop new releases.
|
|
6
|
+
- Check checkout quote and order release tests.
|
|
7
|
+
- Decide whether to rollback the latest release.
|
|
8
|
+
|
|
9
|
+
## Missing Evidence
|
|
10
|
+
|
|
11
|
+
- Production monitoring is intentionally missing in the demo so the audit report shows GAP items.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
profile: oss
|
|
2
|
+
mode: strict
|
|
3
|
+
checks:
|
|
4
|
+
gitleaks: block
|
|
5
|
+
trivy: block
|
|
6
|
+
semgrep: block
|
|
7
|
+
osv-scanner: warn
|
|
8
|
+
syft: warn
|
|
9
|
+
grype: warn
|
|
10
|
+
actionlint: block
|
|
11
|
+
zizmor: warn
|
|
12
|
+
checkov: warn
|
|
13
|
+
trivy-config: warn
|
|
14
|
+
scorecard: warn
|
|
15
|
+
megalinter: warn
|
|
16
|
+
pre-commit: warn
|
|
17
|
+
package-audit: warn
|
|
18
|
+
fail_on:
|
|
19
|
+
tests: true
|
|
20
|
+
secrets: true
|
|
21
|
+
dependency_high_or_critical: true
|
|
22
|
+
semgrep_blocking: true
|
|
23
|
+
trivy_unavailable: true
|
|
24
|
+
electron_dangerous_settings: true
|
|
25
|
+
ci_security_high: true
|
|
26
|
+
warn_on:
|
|
27
|
+
missing_optional_tools: true
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
schema_version: 1
|
|
2
|
+
project:
|
|
3
|
+
name: demo-ai-app
|
|
4
|
+
type: node
|
|
5
|
+
lifecycle: production-candidate
|
|
6
|
+
production: true
|
|
7
|
+
risk:
|
|
8
|
+
handles_auth: false
|
|
9
|
+
handles_sensitive_data: false
|
|
10
|
+
handles_payments: false
|
|
11
|
+
handles_financial_data: false
|
|
12
|
+
handles_health_data: false
|
|
13
|
+
has_database: false
|
|
14
|
+
has_deployment: true
|
|
15
|
+
has_user_generated_content: false
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Threat Model
|
|
2
|
+
|
|
3
|
+
## Assets
|
|
4
|
+
|
|
5
|
+
- Order totals
|
|
6
|
+
- Payment status
|
|
7
|
+
- Release decisions
|
|
8
|
+
|
|
9
|
+
## Trust Boundaries
|
|
10
|
+
|
|
11
|
+
- Browser or API client input
|
|
12
|
+
- Order risk calculation
|
|
13
|
+
- Release approval process
|
|
14
|
+
|
|
15
|
+
## User Decisions
|
|
16
|
+
|
|
17
|
+
- Confirm whether manual review threshold matches real business risk.
|
|
18
|
+
- Confirm who can override a release hold.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Demo AI App
|
|
2
|
+
|
|
3
|
+
This is a small healthy Node.js project used by AI Project Maintainer demos.
|
|
4
|
+
|
|
5
|
+
It has:
|
|
6
|
+
|
|
7
|
+
- business-critical tests
|
|
8
|
+
- a build script
|
|
9
|
+
- production audit intake files
|
|
10
|
+
- intentional production evidence gaps for monitoring, alerts, approval, and rollback
|
|
11
|
+
|
|
12
|
+
## Run The Healthy Project
|
|
13
|
+
|
|
14
|
+
```powershell
|
|
15
|
+
npm test --prefix .\examples\demo-ai-app
|
|
16
|
+
npm run build --prefix .\examples\demo-ai-app
|
|
17
|
+
node .\examples\demo-ai-app\scripts\run-demo-gate.mjs
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The demo gate uses temporary scanner shims so the sample report is reproducible on machines that do not have Gitleaks, Trivy, Semgrep, and other scanners installed yet.
|
|
21
|
+
|
|
22
|
+
## Generate The Broken Before State
|
|
23
|
+
|
|
24
|
+
```powershell
|
|
25
|
+
node .\examples\demo-ai-app\scripts\create-before-state.mjs
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The command prints a temporary directory. Run `npm test` in that copied project to see the business tests fail. Nothing bad is committed into this repository; the failing state exists only under the OS temp directory.
|
|
29
|
+
|
|
30
|
+
## Run The Real Gate
|
|
31
|
+
|
|
32
|
+
When scanner CLIs are installed, run the same command a real project would use:
|
|
33
|
+
|
|
34
|
+
```powershell
|
|
35
|
+
npx ai-project-maintainer gate .\examples\demo-ai-app --production --strict --release --output reports/security-report.json
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The expected result is PASS with visible GAP items for missing production evidence.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "demo-ai-app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Runnable demo project for AI Project Maintainer.",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "node --test",
|
|
9
|
+
"build": "node scripts/build.mjs",
|
|
10
|
+
"demo:before": "node scripts/create-before-state.mjs",
|
|
11
|
+
"demo:gate": "node scripts/run-demo-gate.mjs"
|
|
12
|
+
},
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=20"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
6
|
+
const outDir = path.join(root, "dist");
|
|
7
|
+
|
|
8
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
9
|
+
fs.writeFileSync(
|
|
10
|
+
path.join(outDir, "build-manifest.json"),
|
|
11
|
+
`${JSON.stringify({
|
|
12
|
+
app: "demo-ai-app",
|
|
13
|
+
builtAt: new Date().toISOString(),
|
|
14
|
+
entrypoints: ["src/order-risk.js"],
|
|
15
|
+
}, null, 2)}\n`,
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
console.log(`Demo build manifest written to ${path.relative(root, outDir)}`);
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const ignored = new Set(["node_modules", "dist", "reports"]);
|
|
8
|
+
|
|
9
|
+
function normalizeForCompare(value) {
|
|
10
|
+
return path.resolve(value).toLowerCase();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function assertInsideTemp(destination) {
|
|
14
|
+
const tempRoot = normalizeForCompare(fs.realpathSync(os.tmpdir()));
|
|
15
|
+
const resolved = normalizeForCompare(destination);
|
|
16
|
+
if (resolved !== tempRoot && !resolved.startsWith(`${tempRoot}${path.sep}`)) {
|
|
17
|
+
throw new Error(`Refusing to write demo before-state outside the OS temp directory: ${destination}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function copyDirectory(source, destination) {
|
|
22
|
+
fs.mkdirSync(destination, { recursive: true });
|
|
23
|
+
for (const entry of fs.readdirSync(source, { withFileTypes: true })) {
|
|
24
|
+
if (ignored.has(entry.name)) continue;
|
|
25
|
+
const from = path.join(source, entry.name);
|
|
26
|
+
const to = path.join(destination, entry.name);
|
|
27
|
+
if (entry.isDirectory()) copyDirectory(from, to);
|
|
28
|
+
else if (entry.isFile()) fs.copyFileSync(from, to);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function readOutputArg(args) {
|
|
33
|
+
const index = args.indexOf("--output");
|
|
34
|
+
if (index !== -1) return args[index + 1];
|
|
35
|
+
const inline = args.find((arg) => arg.startsWith("--output="));
|
|
36
|
+
return inline ? inline.slice("--output=".length) : null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function createBeforeState({ outputPath = null } = {}) {
|
|
40
|
+
const demoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
41
|
+
const destination = path.resolve(outputPath || path.join(os.tmpdir(), `apm-demo-before-${Date.now()}`));
|
|
42
|
+
assertInsideTemp(destination);
|
|
43
|
+
|
|
44
|
+
fs.rmSync(destination, { recursive: true, force: true });
|
|
45
|
+
copyDirectory(demoRoot, destination);
|
|
46
|
+
|
|
47
|
+
fs.writeFileSync(
|
|
48
|
+
path.join(destination, "src", "order-risk.js"),
|
|
49
|
+
`const shippingRates = {
|
|
50
|
+
standard: 499,
|
|
51
|
+
expedited: 1299,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export function quoteOrder({ subtotalCents, shippingTier }) {
|
|
55
|
+
if (!Number.isInteger(subtotalCents) || subtotalCents < 0) {
|
|
56
|
+
throw new TypeError("subtotalCents must be a non-negative integer");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!Object.hasOwn(shippingRates, shippingTier)) {
|
|
60
|
+
throw new RangeError(\`unsupported shipping tier: \${shippingTier}\`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const shippingCents = shippingRates[shippingTier];
|
|
64
|
+
const totalCents = subtotalCents + shippingCents;
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
subtotalCents,
|
|
68
|
+
shippingCents,
|
|
69
|
+
totalCents,
|
|
70
|
+
needsManualReview: false,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function canReleaseOrder({ paid, flagged }) {
|
|
75
|
+
return Boolean(paid && !flagged);
|
|
76
|
+
}
|
|
77
|
+
`,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
return { destination };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
84
|
+
const result = createBeforeState({ outputPath: readOutputArg(process.argv.slice(2)) });
|
|
85
|
+
console.log(JSON.stringify(result, null, 2));
|
|
86
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
import { runLocalGate } from "../../../ai-project-maintainer/scripts/run-local-gate.mjs";
|
|
8
|
+
import { toMarkdown } from "../../../ai-project-maintainer/scripts/lib/report.mjs";
|
|
9
|
+
|
|
10
|
+
function writeMockTool(toolsDir) {
|
|
11
|
+
const scriptPath = path.join(toolsDir, "mock-tool.mjs");
|
|
12
|
+
fs.writeFileSync(
|
|
13
|
+
scriptPath,
|
|
14
|
+
`#!/usr/bin/env node
|
|
15
|
+
import fs from "node:fs";
|
|
16
|
+
|
|
17
|
+
const [, , tool, ...args] = process.argv;
|
|
18
|
+
if (args.includes("--version")) {
|
|
19
|
+
console.log(\`\${tool} demo 0.0.0\`);
|
|
20
|
+
process.exit(0);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (tool === "syft") {
|
|
24
|
+
const outputArg = args.find((arg) => arg.startsWith("cyclonedx-json="));
|
|
25
|
+
if (outputArg) {
|
|
26
|
+
fs.writeFileSync(outputArg.slice("cyclonedx-json=".length), JSON.stringify({
|
|
27
|
+
bomFormat: "CycloneDX",
|
|
28
|
+
specVersion: "1.5",
|
|
29
|
+
version: 1,
|
|
30
|
+
metadata: { component: { type: "application", name: "demo-ai-app" } },
|
|
31
|
+
components: [],
|
|
32
|
+
}, null, 2));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (tool === "scorecard") {
|
|
37
|
+
console.log(JSON.stringify({ score: 8.5, checks: [] }));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
process.exit(0);
|
|
41
|
+
`,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const tools = [
|
|
45
|
+
"actionlint",
|
|
46
|
+
"checkov",
|
|
47
|
+
"gitleaks",
|
|
48
|
+
"grype",
|
|
49
|
+
"mega-linter-runner",
|
|
50
|
+
"osv-scanner",
|
|
51
|
+
"pre-commit",
|
|
52
|
+
"scorecard",
|
|
53
|
+
"semgrep",
|
|
54
|
+
"squawk",
|
|
55
|
+
"syft",
|
|
56
|
+
"trivy",
|
|
57
|
+
"zizmor",
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
for (const tool of tools) {
|
|
61
|
+
if (process.platform === "win32") {
|
|
62
|
+
fs.writeFileSync(path.join(toolsDir, `${tool}.cmd`), `@echo off\r\nnode "%~dp0mock-tool.mjs" ${tool} %*\r\n`);
|
|
63
|
+
} else {
|
|
64
|
+
const shim = path.join(toolsDir, tool);
|
|
65
|
+
fs.writeFileSync(shim, `#!/usr/bin/env sh\nnode "$(dirname "$0")/mock-tool.mjs" ${tool} "$@"\n`);
|
|
66
|
+
fs.chmodSync(shim, 0o755);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function runDemoGate({ outputPath = null } = {}) {
|
|
72
|
+
const demoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
73
|
+
const reportsDir = path.join(demoRoot, "reports");
|
|
74
|
+
const toolsDir = fs.mkdtempSync(path.join(os.tmpdir(), "apm-demo-tools-"));
|
|
75
|
+
writeMockTool(toolsDir);
|
|
76
|
+
|
|
77
|
+
const report = runLocalGate(demoRoot, {
|
|
78
|
+
strict: true,
|
|
79
|
+
release: true,
|
|
80
|
+
production: true,
|
|
81
|
+
outputPath: outputPath || path.join(reportsDir, "security-report.json"),
|
|
82
|
+
writeReports: true,
|
|
83
|
+
runnerOptions: {
|
|
84
|
+
envPath: `${toolsDir}${path.delimiter}${process.env.PATH || ""}`,
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return { report, toolsDir };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
92
|
+
const { report } = runDemoGate();
|
|
93
|
+
console.log(toMarkdown(report));
|
|
94
|
+
process.exit(report.passed ? 0 : 1);
|
|
95
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const shippingRates = {
|
|
2
|
+
standard: 499,
|
|
3
|
+
expedited: 1299,
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export function quoteOrder({ subtotalCents, shippingTier }) {
|
|
7
|
+
if (!Number.isInteger(subtotalCents) || subtotalCents < 0) {
|
|
8
|
+
throw new TypeError("subtotalCents must be a non-negative integer");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (!Object.hasOwn(shippingRates, shippingTier)) {
|
|
12
|
+
throw new RangeError(`unsupported shipping tier: ${shippingTier}`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const shippingCents = shippingRates[shippingTier];
|
|
16
|
+
const totalCents = subtotalCents + shippingCents;
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
subtotalCents,
|
|
20
|
+
shippingCents,
|
|
21
|
+
totalCents,
|
|
22
|
+
needsManualReview: totalCents >= 100000,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function canReleaseOrder({ paid, flagged, stockAvailable }) {
|
|
27
|
+
return Boolean(paid && stockAvailable && !flagged);
|
|
28
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
import { canReleaseOrder, quoteOrder } from "../src/order-risk.js";
|
|
5
|
+
|
|
6
|
+
test("checkout quote preserves the customer-visible total", () => {
|
|
7
|
+
assert.deepEqual(quoteOrder({ subtotalCents: 2500, shippingTier: "standard" }), {
|
|
8
|
+
subtotalCents: 2500,
|
|
9
|
+
shippingCents: 499,
|
|
10
|
+
totalCents: 2999,
|
|
11
|
+
needsManualReview: false,
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("expensive orders require manual review before release", () => {
|
|
16
|
+
assert.equal(quoteOrder({ subtotalCents: 99600, shippingTier: "standard" }).needsManualReview, true);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("orders are released only when payment, stock, and risk checks all pass", () => {
|
|
20
|
+
assert.equal(canReleaseOrder({ paid: true, stockAvailable: true, flagged: false }), true);
|
|
21
|
+
assert.equal(canReleaseOrder({ paid: false, stockAvailable: true, flagged: false }), false);
|
|
22
|
+
assert.equal(canReleaseOrder({ paid: true, stockAvailable: false, flagged: false }), false);
|
|
23
|
+
assert.equal(canReleaseOrder({ paid: true, stockAvailable: true, flagged: true }), false);
|
|
24
|
+
});
|
package/package.json
CHANGED
|
@@ -1,13 +1,44 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "ai-project-maintainer",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Production-readiness audit and CI gate for AI-coded projects.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
|
|
2
|
+
"name": "ai-project-maintainer",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Production-readiness audit and CI gate for AI-coded projects.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/xixifusi1213-gif/ai-project-maintainer.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/xixifusi1213-gif/ai-project-maintainer/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/xixifusi1213-gif/ai-project-maintainer#readme",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ai-coding",
|
|
17
|
+
"devsecops",
|
|
18
|
+
"security",
|
|
19
|
+
"production-readiness",
|
|
20
|
+
"github-actions",
|
|
21
|
+
"semgrep",
|
|
22
|
+
"trivy",
|
|
23
|
+
"gitleaks",
|
|
24
|
+
"codex",
|
|
25
|
+
"ai-agents"
|
|
26
|
+
],
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=20"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"ai-project-maintainer/",
|
|
32
|
+
"examples/demo-ai-app/.ai-maintainer/",
|
|
33
|
+
"examples/demo-ai-app/package.json",
|
|
34
|
+
"examples/demo-ai-app/package-lock.json",
|
|
35
|
+
"examples/demo-ai-app/README.md",
|
|
36
|
+
"examples/demo-ai-app/scripts/",
|
|
37
|
+
"examples/demo-ai-app/src/",
|
|
38
|
+
"examples/demo-ai-app/test/",
|
|
39
|
+
"docs/",
|
|
40
|
+
"README.md"
|
|
41
|
+
],
|
|
11
42
|
"bin": {
|
|
12
43
|
"ai-project-maintainer": "ai-project-maintainer/scripts/cli.mjs"
|
|
13
44
|
},
|