@t275005746/gse 0.1.0 → 0.1.2
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/.github/ISSUE_TEMPLATE/bug_report.yml +42 -42
- package/.github/ISSUE_TEMPLATE/change_request.yml +50 -50
- package/.github/ISSUE_TEMPLATE/config.yml +5 -5
- package/.github/PULL_REQUEST_TEMPLATE.md +38 -38
- package/.github/workflows/validate-gse.yml +33 -33
- package/.gse/README.md +18 -18
- package/.gse/gse-development-protocol.md +50 -50
- package/.gse/project-profile.md +29 -29
- package/.gse/quality-gates.md +25 -25
- package/.gse/releases/public-registry-publication-npm.md +49 -0
- package/.gse/releases/public-release-owner-required.md +65 -65
- package/.gse/releases/public-security-contact-owner-required.md +45 -45
- package/.gse/state.json +147 -27
- package/CHANGELOG.md +31 -15
- package/CONTRIBUTING.md +64 -64
- package/LICENSE +21 -21
- package/README.md +14 -7
- package/README.zh-CN.md +14 -7
- package/SECURITY.md +41 -41
- package/SKILL.md +32 -12
- package/SUPPORT.md +38 -38
- package/assets/marketplace/README.md +7 -7
- package/assets/marketplace/gse-listing.json +75 -75
- package/assets/templates/acceptance-execution-packet.md +73 -73
- package/assets/templates/adr.md +14 -14
- package/assets/templates/change-brief.md +16 -16
- package/assets/templates/design.md +18 -18
- package/assets/templates/dispatch-packet.md +100 -92
- package/assets/templates/evidence.md +15 -9
- package/assets/templates/execution-quality-pack.md +65 -65
- package/assets/templates/goal-map.md +21 -21
- package/assets/templates/host-adapter.md +48 -48
- package/assets/templates/host-ui-invocation-record.md +42 -42
- package/assets/templates/incident-review.md +60 -60
- package/assets/templates/public-channel-publication-record.md +49 -49
- package/assets/templates/public-ci-run-record.md +53 -53
- package/assets/templates/public-release-record.md +65 -65
- package/assets/templates/public-repository-settings-record.md +59 -59
- package/assets/templates/public-security-contact-record.md +45 -45
- package/assets/templates/release-trust-record.md +32 -32
- package/assets/templates/review.md +18 -18
- package/assets/templates/role-fallback-packet.md +49 -0
- package/assets/templates/spec.md +16 -16
- package/assets/templates/target-adoption-evidence.md +29 -29
- package/assets/templates/tasks.md +17 -17
- package/assets/templates/update-release-acceptance-record.md +58 -58
- package/examples/README.md +22 -22
- package/examples/agent-runtime-host/.claude/gse-adapter.md +6 -6
- package/examples/agent-runtime-host/.codex/gse-adapter.md +7 -7
- package/examples/agent-runtime-host/.gse/goal-map.md +7 -7
- package/examples/agent-runtime-host/.gse/project-profile.md +27 -27
- package/examples/agent-runtime-host/.gse/tooling.md +5 -5
- package/examples/agent-runtime-host/.mcp.json +9 -9
- package/examples/agent-runtime-host/AGENTS.md +5 -5
- package/examples/agent-runtime-host/README.md +10 -10
- package/examples/agent-runtime-host/docs/model-routing.md +5 -5
- package/examples/cli-tool/.gse/project-profile.md +21 -21
- package/examples/cli-tool/AGENTS.md +5 -5
- package/examples/cli-tool/README.md +12 -12
- package/examples/cli-tool/package.json +21 -21
- package/examples/small-app/.env.example +2 -2
- package/examples/small-app/.github/workflows/ci.yml +8 -8
- package/examples/small-app/AGENTS.md +5 -5
- package/examples/small-app/README.md +12 -12
- package/examples/small-app/package.json +26 -26
- package/examples/small-app/playwright.config.ts +4 -4
- package/package.json +52 -44
- package/references/adoption-recipes.md +171 -171
- package/references/agent-roles.md +42 -21
- package/references/architecture-health.md +101 -101
- package/references/benchmark-audit.md +73 -73
- package/references/commands.md +74 -45
- package/references/community-channels.md +46 -46
- package/references/compatibility.md +70 -70
- package/references/design-basis.md +38 -38
- package/references/domain-model.md +129 -129
- package/references/domain-quality-gates.md +75 -75
- package/references/drift-audit.md +81 -80
- package/references/evidence-taxonomy.md +145 -108
- package/references/file-ownership.md +97 -93
- package/references/final-form-roadmap.md +201 -0
- package/references/final-readiness.md +84 -84
- package/references/forward-test.md +133 -133
- package/references/goal-map.md +36 -36
- package/references/host-adapters.md +129 -101
- package/references/learning-system.md +42 -26
- package/references/maintenance-cadence.md +51 -0
- package/references/marketplace-discovery.md +46 -46
- package/references/model-routing.md +103 -103
- package/references/open-source-defaults.md +39 -39
- package/references/operating-model.md +52 -52
- package/references/packaging.md +277 -277
- package/references/project-agent-workspace.md +89 -89
- package/references/project-bootstrap.md +122 -122
- package/references/project-guards.md +52 -0
- package/references/project-profile.md +57 -57
- package/references/public-release.md +174 -174
- package/references/quality-gates.md +96 -89
- package/references/recovery.md +176 -176
- package/references/release-trust.md +43 -43
- package/references/release.md +126 -126
- package/references/review.md +141 -141
- package/references/role-dispatch-fallback.md +54 -0
- package/references/router.md +90 -90
- package/references/spec-workflow.md +66 -66
- package/references/task-levels.md +81 -81
- package/references/tool-adapters.md +80 -70
- package/scripts/audit-acceptance-execution-packet.mjs +133 -133
- package/scripts/audit-adoption-recipes.mjs +99 -99
- package/scripts/audit-change-lifecycle.mjs +77 -77
- package/scripts/audit-change-system.mjs +134 -134
- package/scripts/audit-ci-readiness.mjs +107 -107
- package/scripts/audit-close-gate-hardening.mjs +188 -0
- package/scripts/audit-close-gate.mjs +448 -262
- package/scripts/audit-command-adapters.mjs +91 -91
- package/scripts/audit-command-execution.mjs +212 -199
- package/scripts/audit-commands.mjs +7 -5
- package/scripts/audit-compatibility.mjs +149 -149
- package/scripts/audit-completion-plan-drill.mjs +230 -0
- package/scripts/audit-completion-readiness.mjs +290 -287
- package/scripts/audit-continue-preflight.mjs +234 -0
- package/scripts/audit-distribution.mjs +251 -251
- package/scripts/audit-domain-quality-gates.mjs +108 -108
- package/scripts/audit-evidence-levels.mjs +217 -0
- package/scripts/audit-evidence-placeholders.mjs +126 -126
- package/scripts/audit-evidence-review-queue.mjs +206 -0
- package/scripts/audit-final-acceptance-packet.mjs +9 -9
- package/scripts/audit-final-form-progress-report.mjs +19 -18
- package/scripts/audit-final-form-roadmap.mjs +157 -0
- package/scripts/audit-final-form-stale-copy.mjs +2 -2
- package/scripts/audit-final-readiness-promotion.mjs +255 -255
- package/scripts/audit-final-readiness.mjs +226 -226
- package/scripts/audit-fixtures.mjs +154 -154
- package/scripts/audit-fresh-session-readiness.mjs +177 -177
- package/scripts/audit-host-capabilities.mjs +237 -0
- package/scripts/audit-host-runtime-evidence-handoff.mjs +159 -159
- package/scripts/audit-host-runtime-invocation-drill.mjs +240 -240
- package/scripts/audit-host-runtime-invocations.mjs +254 -254
- package/scripts/audit-host-ui-invocation.mjs +132 -132
- package/scripts/audit-installed-sync.mjs +203 -0
- package/scripts/audit-learning-drift.mjs +292 -0
- package/scripts/audit-learning-promotion.mjs +351 -0
- package/scripts/audit-learning-system.mjs +24 -14
- package/scripts/audit-local-final-form-completion.mjs +11 -10
- package/scripts/audit-maintenance-cadence.mjs +139 -0
- package/scripts/audit-maintenance-snapshot.mjs +181 -0
- package/scripts/audit-marketplace-discovery.mjs +122 -122
- package/scripts/audit-npm-package-metadata.mjs +160 -154
- package/scripts/audit-npm-publish-dry-run.mjs +194 -166
- package/scripts/audit-npm-tarball-install.mjs +195 -189
- package/scripts/audit-open-source-defaults.mjs +92 -92
- package/scripts/audit-open-source-readiness.mjs +97 -97
- package/scripts/audit-owner-external-gate-kit.mjs +12 -11
- package/scripts/audit-project-guards.mjs +189 -0
- package/scripts/audit-project.mjs +144 -141
- package/scripts/audit-public-acceptance-command-dry-run-drill.mjs +203 -203
- package/scripts/audit-public-acceptance-handoff.mjs +10 -10
- package/scripts/audit-public-acceptance-readiness.mjs +222 -222
- package/scripts/audit-public-channel-publication.mjs +248 -248
- package/scripts/audit-public-ci-run.mjs +184 -184
- package/scripts/audit-public-collaboration-templates.mjs +98 -98
- package/scripts/audit-public-external-gate-probe.mjs +206 -206
- package/scripts/audit-public-release-checklist.mjs +17 -15
- package/scripts/audit-public-release-decision.mjs +201 -201
- package/scripts/audit-public-release-metadata.mjs +176 -176
- package/scripts/audit-public-repository-settings.mjs +237 -237
- package/scripts/audit-public-security-contact.mjs +171 -171
- package/scripts/audit-readme-docs.mjs +6 -4
- package/scripts/audit-recovery-readiness.mjs +98 -98
- package/scripts/audit-release-bundle.mjs +13 -11
- package/scripts/audit-release-owner-action-plan-drill.mjs +5 -4
- package/scripts/audit-release-owner-action-plan.mjs +10 -10
- package/scripts/audit-release-readiness.mjs +106 -106
- package/scripts/audit-release-status-manifest.mjs +4 -4
- package/scripts/audit-release-trust.mjs +62 -62
- package/scripts/audit-remote-distribution.mjs +266 -266
- package/scripts/audit-roadmap-consistency.mjs +234 -230
- package/scripts/audit-role-dispatch-fallback.mjs +212 -0
- package/scripts/audit-session-sync.mjs +127 -0
- package/scripts/audit-signing.mjs +147 -147
- package/scripts/audit-state-freshness.mjs +20 -14
- package/scripts/audit-state-repair.mjs +360 -0
- package/scripts/audit-target-adoption-evidence.mjs +117 -117
- package/scripts/audit-target-hardening-drills.mjs +267 -0
- package/scripts/audit-target-project.mjs +507 -507
- package/scripts/audit-tool-fallback-policy.mjs +180 -0
- package/scripts/audit-ui-browser-evidence-policy.mjs +191 -0
- package/scripts/audit-update-release-acceptance.mjs +136 -136
- package/scripts/audit-v1-target-validation.mjs +269 -269
- package/scripts/audit-validation-profiles.mjs +125 -123
- package/scripts/backfill-evidence-levels.mjs +98 -0
- package/scripts/check-encoding.mjs +72 -0
- package/scripts/close-change.mjs +116 -116
- package/scripts/discover-project-profile.mjs +307 -307
- package/scripts/generate-command-adapter.mjs +231 -231
- package/scripts/generate-continue-packet.mjs +1214 -0
- package/scripts/generate-final-acceptance-packet.mjs +188 -173
- package/scripts/generate-final-form-progress-report.mjs +191 -189
- package/scripts/generate-host-runtime-evidence-handoff.mjs +198 -191
- package/scripts/generate-maintenance-snapshot.mjs +216 -0
- package/scripts/generate-owner-external-gate-kit.mjs +295 -295
- package/scripts/generate-public-acceptance-handoff.mjs +168 -156
- package/scripts/generate-public-release-checklist.mjs +207 -207
- package/scripts/generate-release-bundle.mjs +505 -505
- package/scripts/generate-release-owner-action-plan.mjs +172 -171
- package/scripts/generate-release-status-manifest.mjs +199 -198
- package/scripts/generate-session-prompt.mjs +188 -188
- package/scripts/gse.mjs +67 -67
- package/scripts/init-change.mjs +265 -265
- package/scripts/init-project.mjs +793 -712
- package/scripts/install-gse.mjs +234 -234
- package/scripts/lib/evidence-placeholders.mjs +28 -28
- package/scripts/package-gse.mjs +174 -174
- package/scripts/probe-public-external-gates.mjs +167 -167
- package/scripts/record-host-invocation.mjs +151 -151
- package/scripts/record-learning.mjs +37 -8
- package/scripts/record-public-channel-publication.mjs +178 -178
- package/scripts/record-public-ci-run.mjs +180 -180
- package/scripts/record-public-release.mjs +175 -175
- package/scripts/record-public-repository-settings.mjs +209 -209
- package/scripts/record-public-security-contact.mjs +157 -157
- package/scripts/record-session-sync.mjs +87 -0
- package/scripts/run-gse-command.mjs +79 -47
- package/scripts/run-validation-profile.mjs +24 -5
- package/scripts/sign-gse-package.mjs +83 -83
- package/scripts/update-project-state.mjs +223 -223
- package/scripts/validate-gse.mjs +216 -0
- package/scripts/verify-gse-package.mjs +85 -85
|
@@ -0,0 +1,212 @@
|
|
|
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
|
+
import { spawnSync } from 'node:child_process'
|
|
7
|
+
|
|
8
|
+
const args = process.argv.slice(2)
|
|
9
|
+
|
|
10
|
+
function readArg(name, fallback = null) {
|
|
11
|
+
const index = args.indexOf(name)
|
|
12
|
+
if (index === -1) return fallback
|
|
13
|
+
return args[index + 1] ?? fallback
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const root = path.resolve(readArg('--root', path.join(import.meta.dirname, '..')))
|
|
17
|
+
const targetArg = readArg('--target')
|
|
18
|
+
const jsonOnly = args.includes('--json')
|
|
19
|
+
|
|
20
|
+
const requiredRoles = ['Planner', 'Locator', 'Implementer', 'Verifier', 'Reviewer', 'Docs/Evidence', 'Release']
|
|
21
|
+
const allowedModes = new Set(['real-subagent', 'sequential-role', 'handoff-session'])
|
|
22
|
+
const allowedDelegation = new Set(['yes', 'no'])
|
|
23
|
+
const allowedToolStatuses = new Set(['verified', 'documented', 'unknown', 'unavailable'])
|
|
24
|
+
|
|
25
|
+
function readText(filePath) {
|
|
26
|
+
return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8').replace(/^\uFEFF/, '') : ''
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function parseRoleTable(text) {
|
|
30
|
+
const rows = []
|
|
31
|
+
for (const line of text.split(/\r?\n/)) {
|
|
32
|
+
const trimmed = line.trim()
|
|
33
|
+
if (!trimmed.startsWith('|')) continue
|
|
34
|
+
if (/^\|\s*-+/.test(trimmed)) continue
|
|
35
|
+
if (/^\|\s*Role\s*\|/i.test(trimmed)) continue
|
|
36
|
+
const cells = trimmed
|
|
37
|
+
.slice(1, trimmed.endsWith('|') ? -1 : undefined)
|
|
38
|
+
.split('|')
|
|
39
|
+
.map((cell) => cell.trim())
|
|
40
|
+
if (cells.length < 8) continue
|
|
41
|
+
rows.push({
|
|
42
|
+
role: cells[0],
|
|
43
|
+
mode: cells[1],
|
|
44
|
+
realDelegationUsed: cells[2],
|
|
45
|
+
toolStatus: cells[3],
|
|
46
|
+
fallbackOutput: cells[4],
|
|
47
|
+
evidence: cells[5],
|
|
48
|
+
stopCondition: cells[6],
|
|
49
|
+
writeAccess: cells[7],
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
return rows
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function readRoleDispatchFallback(target) {
|
|
56
|
+
const filePath = path.join(target, '.gse', 'agents', 'role-fallback-packets.md')
|
|
57
|
+
const exists = fs.existsSync(filePath)
|
|
58
|
+
const text = exists ? readText(filePath) : ''
|
|
59
|
+
const packets = parseRoleTable(text)
|
|
60
|
+
const roleSet = new Set(packets.map((packet) => packet.role))
|
|
61
|
+
const missingRoles = requiredRoles.filter((role) => !roleSet.has(role))
|
|
62
|
+
const invalidMode = packets.filter((packet) => !allowedModes.has(packet.mode))
|
|
63
|
+
const invalidDelegation = packets.filter((packet) => !allowedDelegation.has(packet.realDelegationUsed))
|
|
64
|
+
const invalidToolStatus = packets.filter((packet) => !allowedToolStatuses.has(packet.toolStatus))
|
|
65
|
+
const missingEvidence = packets.filter((packet) => !packet.fallbackOutput || !packet.evidence || !packet.stopCondition || !packet.writeAccess)
|
|
66
|
+
const fakeDelegationRisk = packets.filter((packet) => packet.realDelegationUsed === 'yes' && packet.toolStatus !== 'verified')
|
|
67
|
+
const status = !exists
|
|
68
|
+
? 'warning'
|
|
69
|
+
: missingRoles.length > 0 || invalidMode.length > 0 || invalidDelegation.length > 0 || invalidToolStatus.length > 0 || missingEvidence.length > 0 || fakeDelegationRisk.length > 0
|
|
70
|
+
? 'failed'
|
|
71
|
+
: 'passed'
|
|
72
|
+
return {
|
|
73
|
+
path: '.gse/agents/role-fallback-packets.md',
|
|
74
|
+
exists,
|
|
75
|
+
status,
|
|
76
|
+
packets,
|
|
77
|
+
summary: {
|
|
78
|
+
requiredRoles,
|
|
79
|
+
total: packets.length,
|
|
80
|
+
missingRoles,
|
|
81
|
+
invalidMode: invalidMode.map((packet) => packet.role),
|
|
82
|
+
invalidDelegation: invalidDelegation.map((packet) => packet.role),
|
|
83
|
+
invalidToolStatus: invalidToolStatus.map((packet) => packet.role),
|
|
84
|
+
missingEvidence: missingEvidence.map((packet) => packet.role),
|
|
85
|
+
fakeDelegationRisk: fakeDelegationRisk.map((packet) => packet.role),
|
|
86
|
+
sequentialFallbackRoles: packets.filter((packet) => packet.mode === 'sequential-role').map((packet) => packet.role),
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function run(script, commandArgs) {
|
|
92
|
+
const result = spawnSync(process.execPath, [path.join(root, 'scripts', script), ...commandArgs], {
|
|
93
|
+
cwd: root,
|
|
94
|
+
encoding: 'utf8',
|
|
95
|
+
windowsHide: true,
|
|
96
|
+
})
|
|
97
|
+
return {
|
|
98
|
+
command: [process.execPath, path.join(root, 'scripts', script), ...commandArgs].join(' '),
|
|
99
|
+
status: result.status ?? 1,
|
|
100
|
+
stdout: (result.stdout ?? '').trim(),
|
|
101
|
+
stderr: (result.stderr ?? '').trim(),
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function check(id, label, ok, evidence, risk = '') {
|
|
106
|
+
return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function createFixture() {
|
|
110
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gse-role-fallback-'))
|
|
111
|
+
const init = run('init-project.mjs', ['--target', dir, '--mode', 'standard', '--json'])
|
|
112
|
+
return { dir, init }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function audit(target) {
|
|
116
|
+
const resolvedTarget = path.resolve(target)
|
|
117
|
+
const reference = readText(path.join(root, 'references', 'role-dispatch-fallback.md'))
|
|
118
|
+
const agentRoles = readText(path.join(root, 'references', 'agent-roles.md'))
|
|
119
|
+
const dispatchTemplate = readText(path.join(root, 'assets', 'templates', 'dispatch-packet.md'))
|
|
120
|
+
const fallbackTemplate = readText(path.join(root, 'assets', 'templates', 'role-fallback-packet.md'))
|
|
121
|
+
const initProject = readText(path.join(root, 'scripts', 'init-project.mjs'))
|
|
122
|
+
const continuePacket = readText(path.join(root, 'scripts', 'generate-continue-packet.mjs'))
|
|
123
|
+
const validationProfile = readText(path.join(root, 'scripts', 'run-validation-profile.mjs'))
|
|
124
|
+
const validator = readText(path.join(root, 'scripts', 'validate-gse.mjs'))
|
|
125
|
+
const fallback = readRoleDispatchFallback(resolvedTarget)
|
|
126
|
+
const roleDocsCoverRequired = requiredRoles.every((role) => reference.includes(role)) && agentRoles.includes('Planner') && agentRoles.includes('Release')
|
|
127
|
+
const templatesCoverEvidence =
|
|
128
|
+
dispatchTemplate.includes('Real delegation used') &&
|
|
129
|
+
dispatchTemplate.includes('Role output evidence') &&
|
|
130
|
+
dispatchTemplate.includes('Claim boundary') &&
|
|
131
|
+
fallbackTemplate.includes('Execution mode') &&
|
|
132
|
+
fallbackTemplate.includes('Evidence level')
|
|
133
|
+
const checks = [
|
|
134
|
+
check('RDF01', 'role dispatch fallback reference defines required roles and claim boundary', roleDocsCoverRequired && reference.includes('real-subagent') && reference.includes('sequential-role') && reference.includes('handoff-session'), 'references/role-dispatch-fallback.md and references/agent-roles.md'),
|
|
135
|
+
check('RDF02', 'dispatch templates expose auditable fallback fields', templatesCoverEvidence, 'assets/templates/dispatch-packet.md and assets/templates/role-fallback-packet.md'),
|
|
136
|
+
check('RDF03', 'init-project scaffolds role fallback packets for standard or enterprise projects', initProject.includes('agents/role-fallback-packets.md') && initProject.includes('Planner') && initProject.includes('Release'), 'scripts/init-project.mjs'),
|
|
137
|
+
check('RDF04', 'target role fallback packet is present or reported as warning', fallback.exists || fallback.status === 'warning', fallback.exists ? fallback.path : 'missing role fallback packet warning'),
|
|
138
|
+
check('RDF05', 'target role fallback packets cover all required roles when present', !fallback.exists || fallback.summary.missingRoles.length === 0, fallback.summary.missingRoles.join(', ') || 'required roles present'),
|
|
139
|
+
check('RDF06', 'target role fallback packets use valid mode/delegation/tool status vocabulary', !fallback.exists || (fallback.summary.invalidMode.length === 0 && fallback.summary.invalidDelegation.length === 0 && fallback.summary.invalidToolStatus.length === 0), 'mode/delegation/tool status vocabulary'),
|
|
140
|
+
check('RDF07', 'target role fallback packets include output evidence and stop conditions', !fallback.exists || fallback.summary.missingEvidence.length === 0, fallback.summary.missingEvidence.join(', ') || 'fallback evidence fields present'),
|
|
141
|
+
check('RDF08', 'target role fallback packets do not claim real delegation without verified tool status', !fallback.exists || fallback.summary.fakeDelegationRisk.length === 0, fallback.summary.fakeDelegationRisk.join(', ') || 'no fake delegation risk'),
|
|
142
|
+
check('RDF09', 'continue packet surfaces role fallback readiness', continuePacket.includes('readRoleDispatchFallback') && continuePacket.includes('roleFallback'), 'scripts/generate-continue-packet.mjs'),
|
|
143
|
+
check('RDF10', 'validation routes include role dispatch fallback audit', validationProfile.includes('audit-role-dispatch-fallback.mjs') && validator.includes('audit-role-dispatch-fallback.mjs'), 'validation profile and validate-gse'),
|
|
144
|
+
]
|
|
145
|
+
const passed = checks.filter((item) => item.status === 'passed').length
|
|
146
|
+
const failed = checks.length - passed
|
|
147
|
+
return {
|
|
148
|
+
target: resolvedTarget,
|
|
149
|
+
generatedAt: new Date().toISOString(),
|
|
150
|
+
summary: { status: failed === 0 ? fallback.status : 'failed', passed, failed, total: checks.length },
|
|
151
|
+
workflows: {
|
|
152
|
+
roleDispatchFallback: failed === 0 && fallback.status !== 'failed' ? 'verified' : 'failed',
|
|
153
|
+
requiredRoles,
|
|
154
|
+
packets: fallback.summary.total,
|
|
155
|
+
sequentialFallbackRoles: fallback.summary.sequentialFallbackRoles.length,
|
|
156
|
+
},
|
|
157
|
+
roleFallback: fallback,
|
|
158
|
+
checks,
|
|
159
|
+
limits: [
|
|
160
|
+
'Role fallback packets prove auditable role boundaries, not real subagent execution.',
|
|
161
|
+
'Real subagent support still requires current host dispatch evidence.',
|
|
162
|
+
'Missing role fallback packets are a warning for target projects until the scaffold is adopted.',
|
|
163
|
+
],
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function selfTestReport() {
|
|
168
|
+
const fixture = createFixture()
|
|
169
|
+
const fixtureReport = audit(fixture.dir)
|
|
170
|
+
const missingDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gse-role-fallback-missing-'))
|
|
171
|
+
fs.mkdirSync(path.join(missingDir, '.gse'), { recursive: true })
|
|
172
|
+
const missingReport = audit(missingDir)
|
|
173
|
+
fs.rmSync(fixture.dir, { recursive: true, force: true })
|
|
174
|
+
fs.rmSync(missingDir, { recursive: true, force: true })
|
|
175
|
+
const checks = [
|
|
176
|
+
check('RDFA01', 'init-project creates role fallback packet scaffold', fixture.init.status === 0 && fixtureReport.roleFallback.exists, 'scripts/init-project.mjs'),
|
|
177
|
+
check('RDFA02', 'scaffold covers every required role', fixtureReport.roleFallback.summary.missingRoles.length === 0, fixtureReport.roleFallback.packets.map((packet) => packet.role).join(', ')),
|
|
178
|
+
check('RDFA03', 'scaffold uses sequential fallback without fake real delegation', fixtureReport.roleFallback.summary.fakeDelegationRisk.length === 0 && fixtureReport.roleFallback.packets.every((packet) => packet.realDelegationUsed === 'no'), 'sequential fallback packets'),
|
|
179
|
+
check('RDFA04', 'missing role fallback file is warning, not hard failure', missingReport.roleFallback.status === 'warning', 'missing fixture'),
|
|
180
|
+
check('RDFA05', 'audit source is wired to continue packet and validation scripts', fixtureReport.checks.find((item) => item.id === 'RDF09')?.status === 'passed' && fixtureReport.checks.find((item) => item.id === 'RDF10')?.status === 'passed', 'continue/validation wiring'),
|
|
181
|
+
]
|
|
182
|
+
const passed = checks.filter((item) => item.status === 'passed').length
|
|
183
|
+
const failed = checks.length - passed
|
|
184
|
+
return {
|
|
185
|
+
root,
|
|
186
|
+
generatedAt: new Date().toISOString(),
|
|
187
|
+
summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, total: checks.length },
|
|
188
|
+
workflows: {
|
|
189
|
+
roleDispatchFallback: failed === 0 ? 'verified' : 'failed',
|
|
190
|
+
initProjectRoleFallbackScaffold: failed === 0 ? 'verified' : 'failed',
|
|
191
|
+
},
|
|
192
|
+
fixture: {
|
|
193
|
+
scaffoldStatus: fixtureReport.roleFallback.status,
|
|
194
|
+
missingStatus: missingReport.roleFallback.status,
|
|
195
|
+
roles: fixtureReport.roleFallback.packets.map((packet) => packet.role),
|
|
196
|
+
},
|
|
197
|
+
checks,
|
|
198
|
+
limits: [
|
|
199
|
+
'This self-test verifies scaffold and audit mechanics.',
|
|
200
|
+
'It does not prove a real host spawned subagents.',
|
|
201
|
+
],
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const isCli = process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1])
|
|
206
|
+
|
|
207
|
+
if (isCli) {
|
|
208
|
+
const report = targetArg ? audit(targetArg) : selfTestReport()
|
|
209
|
+
if (jsonOnly) console.log(JSON.stringify(report, null, 2))
|
|
210
|
+
else console.log(JSON.stringify(report, null, 2))
|
|
211
|
+
if (report.summary.status === 'failed') process.exit(1)
|
|
212
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
|
|
5
|
+
const args = process.argv.slice(2)
|
|
6
|
+
|
|
7
|
+
function readArg(name, fallback = null) {
|
|
8
|
+
const index = args.indexOf(name)
|
|
9
|
+
if (index === -1) return fallback
|
|
10
|
+
return args[index + 1] ?? fallback
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function readArgs(name) {
|
|
14
|
+
const values = []
|
|
15
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
16
|
+
if (args[index] === name && args[index + 1]) values.push(args[index + 1])
|
|
17
|
+
}
|
|
18
|
+
return values
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const root = path.resolve(readArg('--root', path.join(import.meta.dirname, '..')))
|
|
22
|
+
const jsonOnly = args.includes('--json')
|
|
23
|
+
const requireInstalled = args.includes('--require-installed')
|
|
24
|
+
const requiredThreads = readArgs('--require-thread')
|
|
25
|
+
const recordPath = path.join(root, '.gse', 'session-sync.jsonl')
|
|
26
|
+
|
|
27
|
+
function check(id, label, ok, evidence, risk = '') {
|
|
28
|
+
return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function parseJsonl(filePath) {
|
|
32
|
+
if (!fs.existsSync(filePath)) return { records: [], errors: [] }
|
|
33
|
+
const lines = fs.readFileSync(filePath, 'utf8').replace(/^\uFEFF/, '').split(/\r?\n/)
|
|
34
|
+
const records = []
|
|
35
|
+
const errors = []
|
|
36
|
+
lines.forEach((line, index) => {
|
|
37
|
+
if (!line.trim()) return
|
|
38
|
+
try {
|
|
39
|
+
records.push(JSON.parse(line))
|
|
40
|
+
} catch (error) {
|
|
41
|
+
errors.push({ line: index + 1, error: error.message })
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
return { records, errors }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function validRecord(record) {
|
|
48
|
+
const validStatuses = new Set(['sent', 'installed-sync', 'skipped', 'failed', 'archived', 'unavailable'])
|
|
49
|
+
return record?.schemaVersion === 1 &&
|
|
50
|
+
typeof record.recordedAt === 'string' &&
|
|
51
|
+
validStatuses.has(record.status) &&
|
|
52
|
+
typeof record.method === 'string' &&
|
|
53
|
+
record.method.length > 0 &&
|
|
54
|
+
typeof record.evidence === 'string' &&
|
|
55
|
+
record.evidence.length > 0
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const { records, errors } = parseJsonl(recordPath)
|
|
59
|
+
const invalidRecords = records.filter((record) => !validRecord(record))
|
|
60
|
+
const installedRecords = records.filter((record) => record.status === 'installed-sync')
|
|
61
|
+
const threadResults = requiredThreads.map((threadId) => {
|
|
62
|
+
const matches = records.filter((record) => record.threadId === threadId)
|
|
63
|
+
const latest = matches.at(-1) ?? null
|
|
64
|
+
const accepted = latest && ['sent', 'archived', 'unavailable', 'failed', 'skipped'].includes(latest.status)
|
|
65
|
+
return { threadId, latest, accepted }
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const checks = [
|
|
69
|
+
check(
|
|
70
|
+
'SS01',
|
|
71
|
+
'session sync record file is optional but parseable when present',
|
|
72
|
+
errors.length === 0,
|
|
73
|
+
fs.existsSync(recordPath) ? recordPath : 'no .gse/session-sync.jsonl yet',
|
|
74
|
+
errors.map((item) => `line ${item.line}: ${item.error}`).join('; '),
|
|
75
|
+
),
|
|
76
|
+
check(
|
|
77
|
+
'SS02',
|
|
78
|
+
'all session sync records follow schema',
|
|
79
|
+
invalidRecords.length === 0,
|
|
80
|
+
`records=${records.length}, invalid=${invalidRecords.length}`,
|
|
81
|
+
),
|
|
82
|
+
requireInstalled
|
|
83
|
+
? check(
|
|
84
|
+
'SS03',
|
|
85
|
+
'installed-copy sync/parity evidence is recorded',
|
|
86
|
+
installedRecords.length > 0,
|
|
87
|
+
installedRecords.at(-1)?.evidence ?? 'missing installed-sync record',
|
|
88
|
+
)
|
|
89
|
+
: check('SS03', 'installed-copy sync record is optional unless required', true, 'no --require-installed supplied'),
|
|
90
|
+
check(
|
|
91
|
+
'SS04',
|
|
92
|
+
'required thread sync outcomes are recorded honestly',
|
|
93
|
+
threadResults.every((item) => item.accepted),
|
|
94
|
+
threadResults.map((item) => `${item.threadId}:${item.latest?.status ?? 'missing'}`).join(', ') || 'no --require-thread supplied',
|
|
95
|
+
'Allowed outcomes are sent, archived, unavailable, failed, or skipped; adoption must not be inferred from the record alone.',
|
|
96
|
+
),
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
const passed = checks.filter((item) => item.status === 'passed').length
|
|
100
|
+
const failed = checks.length - passed
|
|
101
|
+
const report = {
|
|
102
|
+
root,
|
|
103
|
+
recordPath,
|
|
104
|
+
generatedAt: new Date().toISOString(),
|
|
105
|
+
summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, total: checks.length },
|
|
106
|
+
workflows: {
|
|
107
|
+
sessionSyncRecords: failed === 0 ? 'verified' : 'failed',
|
|
108
|
+
installedSyncRecorded: installedRecords.length > 0 ? 'recorded' : 'not-recorded',
|
|
109
|
+
requiredThreadCount: requiredThreads.length,
|
|
110
|
+
},
|
|
111
|
+
records: {
|
|
112
|
+
total: records.length,
|
|
113
|
+
installedSync: installedRecords.length,
|
|
114
|
+
requiredThreads: threadResults,
|
|
115
|
+
},
|
|
116
|
+
limits: [
|
|
117
|
+
'This audit verifies sync records and honest outcomes; it does not prove a target session adopted the new capability.',
|
|
118
|
+
'Use --require-thread for owner-requested active sessions and --require-installed for installed-copy capability sync closure.',
|
|
119
|
+
'Archived or unavailable sessions should be recorded as such instead of treated as successful syncs.',
|
|
120
|
+
],
|
|
121
|
+
checks,
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (jsonOnly) console.log(JSON.stringify(report, null, 2))
|
|
125
|
+
else console.log(JSON.stringify(report, null, 2))
|
|
126
|
+
|
|
127
|
+
if (failed > 0) process.exit(1)
|
|
@@ -1,147 +1,147 @@
|
|
|
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 { spawnSync } from 'node:child_process'
|
|
6
|
-
|
|
7
|
-
const args = process.argv.slice(2)
|
|
8
|
-
|
|
9
|
-
function readArg(name, fallback = null) {
|
|
10
|
-
const index = args.indexOf(name)
|
|
11
|
-
if (index === -1) return fallback
|
|
12
|
-
return args[index + 1] ?? fallback
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const root = path.resolve(readArg('--root', path.join(import.meta.dirname, '..')))
|
|
16
|
-
const jsonOnly = args.includes('--json')
|
|
17
|
-
|
|
18
|
-
function run(command, commandArgs, cwd = root) {
|
|
19
|
-
const result = spawnSync(command, commandArgs, { cwd, encoding: 'utf8', windowsHide: true })
|
|
20
|
-
return {
|
|
21
|
-
command: [command, ...commandArgs].join(' '),
|
|
22
|
-
status: result.status ?? 1,
|
|
23
|
-
stdout: (result.stdout ?? '').trim(),
|
|
24
|
-
stderr: (result.stderr ?? '').trim(),
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function parseJson(text) {
|
|
29
|
-
try { return JSON.parse(text) } catch { return null }
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function check(id, label, ok, evidence, risk = '') {
|
|
33
|
-
return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gse-signing-'))
|
|
37
|
-
const packageOut = path.join(tempRoot, 'package')
|
|
38
|
-
const installTarget = path.join(tempRoot, 'installed-gse')
|
|
39
|
-
const tamperedPackage = path.join(tempRoot, 'tampered-package')
|
|
40
|
-
const tamperedTarget = path.join(tempRoot, 'tampered-installed')
|
|
41
|
-
const privateKey = path.join(tempRoot, 'gse-private.pem')
|
|
42
|
-
const publicKey = path.join(tempRoot, 'gse-public.pem')
|
|
43
|
-
|
|
44
|
-
const packageRun = run(process.execPath, [
|
|
45
|
-
path.join(root, 'scripts', 'package-gse.mjs'),
|
|
46
|
-
'--root',
|
|
47
|
-
root,
|
|
48
|
-
'--out',
|
|
49
|
-
packageOut,
|
|
50
|
-
'--label',
|
|
51
|
-
'gse-signing-audit',
|
|
52
|
-
'--json',
|
|
53
|
-
])
|
|
54
|
-
const signRun = run(process.execPath, [
|
|
55
|
-
path.join(root, 'scripts', 'sign-gse-package.mjs'),
|
|
56
|
-
'--package',
|
|
57
|
-
packageOut,
|
|
58
|
-
'--private-key',
|
|
59
|
-
privateKey,
|
|
60
|
-
'--public-key',
|
|
61
|
-
publicKey,
|
|
62
|
-
'--generate-key',
|
|
63
|
-
'--json',
|
|
64
|
-
])
|
|
65
|
-
const verifyRun = run(process.execPath, [
|
|
66
|
-
path.join(root, 'scripts', 'verify-gse-package.mjs'),
|
|
67
|
-
'--package',
|
|
68
|
-
packageOut,
|
|
69
|
-
'--public-key',
|
|
70
|
-
publicKey,
|
|
71
|
-
'--json',
|
|
72
|
-
])
|
|
73
|
-
const installRun = run(process.execPath, [
|
|
74
|
-
path.join(root, 'scripts', 'install-gse.mjs'),
|
|
75
|
-
'--source',
|
|
76
|
-
packageOut,
|
|
77
|
-
'--target',
|
|
78
|
-
installTarget,
|
|
79
|
-
'--public-key',
|
|
80
|
-
publicKey,
|
|
81
|
-
'--json',
|
|
82
|
-
])
|
|
83
|
-
|
|
84
|
-
fs.cpSync(packageOut, tamperedPackage, { recursive: true })
|
|
85
|
-
fs.appendFileSync(path.join(tamperedPackage, 'SKILL.md'), '\nTampered for signature audit.\n', 'utf8')
|
|
86
|
-
const tamperedVerify = run(process.execPath, [
|
|
87
|
-
path.join(root, 'scripts', 'verify-gse-package.mjs'),
|
|
88
|
-
'--package',
|
|
89
|
-
tamperedPackage,
|
|
90
|
-
'--public-key',
|
|
91
|
-
publicKey,
|
|
92
|
-
'--json',
|
|
93
|
-
])
|
|
94
|
-
const tamperedInstall = run(process.execPath, [
|
|
95
|
-
path.join(root, 'scripts', 'install-gse.mjs'),
|
|
96
|
-
'--source',
|
|
97
|
-
tamperedPackage,
|
|
98
|
-
'--target',
|
|
99
|
-
tamperedTarget,
|
|
100
|
-
'--public-key',
|
|
101
|
-
publicKey,
|
|
102
|
-
'--json',
|
|
103
|
-
])
|
|
104
|
-
|
|
105
|
-
const signData = parseJson(signRun.stdout)
|
|
106
|
-
const verifyData = parseJson(verifyRun.stdout)
|
|
107
|
-
const installData = parseJson(installRun.stdout)
|
|
108
|
-
const tamperedVerifyData = parseJson(tamperedVerify.stdout)
|
|
109
|
-
const tamperedInstallData = parseJson(tamperedInstall.stdout)
|
|
110
|
-
|
|
111
|
-
const checks = [
|
|
112
|
-
check('SIGN01', 'package command succeeds', packageRun.status === 0, packageRun.command),
|
|
113
|
-
check('SIGN02', 'sign command creates signature and keys', signRun.status === 0 && fs.existsSync(path.join(packageOut, 'gse-package-signature.json')) && fs.existsSync(publicKey), signRun.command),
|
|
114
|
-
check('SIGN03', 'verify command accepts signed package', verifyRun.status === 0 && verifyData?.summary?.failed === 0, verifyRun.command),
|
|
115
|
-
check('SIGN04', 'install command accepts signed package with public key', installRun.status === 0 && installData?.signature?.status === 'verified', installRun.command),
|
|
116
|
-
check('SIGN05', 'tampered package fails signature/hash verification', tamperedVerify.status === 1 && tamperedVerifyData?.summary?.failed > 0, tamperedVerify.command),
|
|
117
|
-
check('SIGN06', 'install command rejects tampered signed package', tamperedInstall.status === 1 && (tamperedInstallData?.signature?.status === 'failed' || tamperedInstallData?.summary?.integrityFailed > 0), tamperedInstall.command),
|
|
118
|
-
check('SIGN07', 'signature uses Ed25519', signData?.algorithm === 'ed25519', 'sign-gse-package output'),
|
|
119
|
-
]
|
|
120
|
-
|
|
121
|
-
const passed = checks.filter((item) => item.status === 'passed').length
|
|
122
|
-
const failed = checks.length - passed
|
|
123
|
-
const report = {
|
|
124
|
-
root,
|
|
125
|
-
generatedAt: new Date().toISOString(),
|
|
126
|
-
tempRoot,
|
|
127
|
-
summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, total: checks.length },
|
|
128
|
-
workflows: {
|
|
129
|
-
packageSigning: signRun.status === 0 ? 'verified' : 'failed',
|
|
130
|
-
signatureVerification: verifyRun.status === 0 ? 'verified' : 'failed',
|
|
131
|
-
signedInstall: installRun.status === 0 ? 'verified' : 'failed',
|
|
132
|
-
tamperRejection: tamperedVerify.status === 1 && tamperedInstall.status === 1 ? 'verified' : 'failed',
|
|
133
|
-
},
|
|
134
|
-
commands: [packageRun.command, signRun.command, verifyRun.command, installRun.command, tamperedVerify.command, tamperedInstall.command],
|
|
135
|
-
limits: [
|
|
136
|
-
'This audit verifies local Ed25519 signing and verification mechanics.',
|
|
137
|
-
'It does not prove public key custody, maintainer identity, transparency logs, or marketplace trust.',
|
|
138
|
-
],
|
|
139
|
-
checks,
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
fs.rmSync(tempRoot, { recursive: true, force: true })
|
|
143
|
-
|
|
144
|
-
if (jsonOnly) console.log(JSON.stringify(report, null, 2))
|
|
145
|
-
else console.log(JSON.stringify(report, null, 2))
|
|
146
|
-
|
|
147
|
-
if (failed > 0) process.exit(1)
|
|
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 { spawnSync } from 'node:child_process'
|
|
6
|
+
|
|
7
|
+
const args = process.argv.slice(2)
|
|
8
|
+
|
|
9
|
+
function readArg(name, fallback = null) {
|
|
10
|
+
const index = args.indexOf(name)
|
|
11
|
+
if (index === -1) return fallback
|
|
12
|
+
return args[index + 1] ?? fallback
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const root = path.resolve(readArg('--root', path.join(import.meta.dirname, '..')))
|
|
16
|
+
const jsonOnly = args.includes('--json')
|
|
17
|
+
|
|
18
|
+
function run(command, commandArgs, cwd = root) {
|
|
19
|
+
const result = spawnSync(command, commandArgs, { cwd, encoding: 'utf8', windowsHide: true })
|
|
20
|
+
return {
|
|
21
|
+
command: [command, ...commandArgs].join(' '),
|
|
22
|
+
status: result.status ?? 1,
|
|
23
|
+
stdout: (result.stdout ?? '').trim(),
|
|
24
|
+
stderr: (result.stderr ?? '').trim(),
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function parseJson(text) {
|
|
29
|
+
try { return JSON.parse(text) } catch { return null }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function check(id, label, ok, evidence, risk = '') {
|
|
33
|
+
return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gse-signing-'))
|
|
37
|
+
const packageOut = path.join(tempRoot, 'package')
|
|
38
|
+
const installTarget = path.join(tempRoot, 'installed-gse')
|
|
39
|
+
const tamperedPackage = path.join(tempRoot, 'tampered-package')
|
|
40
|
+
const tamperedTarget = path.join(tempRoot, 'tampered-installed')
|
|
41
|
+
const privateKey = path.join(tempRoot, 'gse-private.pem')
|
|
42
|
+
const publicKey = path.join(tempRoot, 'gse-public.pem')
|
|
43
|
+
|
|
44
|
+
const packageRun = run(process.execPath, [
|
|
45
|
+
path.join(root, 'scripts', 'package-gse.mjs'),
|
|
46
|
+
'--root',
|
|
47
|
+
root,
|
|
48
|
+
'--out',
|
|
49
|
+
packageOut,
|
|
50
|
+
'--label',
|
|
51
|
+
'gse-signing-audit',
|
|
52
|
+
'--json',
|
|
53
|
+
])
|
|
54
|
+
const signRun = run(process.execPath, [
|
|
55
|
+
path.join(root, 'scripts', 'sign-gse-package.mjs'),
|
|
56
|
+
'--package',
|
|
57
|
+
packageOut,
|
|
58
|
+
'--private-key',
|
|
59
|
+
privateKey,
|
|
60
|
+
'--public-key',
|
|
61
|
+
publicKey,
|
|
62
|
+
'--generate-key',
|
|
63
|
+
'--json',
|
|
64
|
+
])
|
|
65
|
+
const verifyRun = run(process.execPath, [
|
|
66
|
+
path.join(root, 'scripts', 'verify-gse-package.mjs'),
|
|
67
|
+
'--package',
|
|
68
|
+
packageOut,
|
|
69
|
+
'--public-key',
|
|
70
|
+
publicKey,
|
|
71
|
+
'--json',
|
|
72
|
+
])
|
|
73
|
+
const installRun = run(process.execPath, [
|
|
74
|
+
path.join(root, 'scripts', 'install-gse.mjs'),
|
|
75
|
+
'--source',
|
|
76
|
+
packageOut,
|
|
77
|
+
'--target',
|
|
78
|
+
installTarget,
|
|
79
|
+
'--public-key',
|
|
80
|
+
publicKey,
|
|
81
|
+
'--json',
|
|
82
|
+
])
|
|
83
|
+
|
|
84
|
+
fs.cpSync(packageOut, tamperedPackage, { recursive: true })
|
|
85
|
+
fs.appendFileSync(path.join(tamperedPackage, 'SKILL.md'), '\nTampered for signature audit.\n', 'utf8')
|
|
86
|
+
const tamperedVerify = run(process.execPath, [
|
|
87
|
+
path.join(root, 'scripts', 'verify-gse-package.mjs'),
|
|
88
|
+
'--package',
|
|
89
|
+
tamperedPackage,
|
|
90
|
+
'--public-key',
|
|
91
|
+
publicKey,
|
|
92
|
+
'--json',
|
|
93
|
+
])
|
|
94
|
+
const tamperedInstall = run(process.execPath, [
|
|
95
|
+
path.join(root, 'scripts', 'install-gse.mjs'),
|
|
96
|
+
'--source',
|
|
97
|
+
tamperedPackage,
|
|
98
|
+
'--target',
|
|
99
|
+
tamperedTarget,
|
|
100
|
+
'--public-key',
|
|
101
|
+
publicKey,
|
|
102
|
+
'--json',
|
|
103
|
+
])
|
|
104
|
+
|
|
105
|
+
const signData = parseJson(signRun.stdout)
|
|
106
|
+
const verifyData = parseJson(verifyRun.stdout)
|
|
107
|
+
const installData = parseJson(installRun.stdout)
|
|
108
|
+
const tamperedVerifyData = parseJson(tamperedVerify.stdout)
|
|
109
|
+
const tamperedInstallData = parseJson(tamperedInstall.stdout)
|
|
110
|
+
|
|
111
|
+
const checks = [
|
|
112
|
+
check('SIGN01', 'package command succeeds', packageRun.status === 0, packageRun.command),
|
|
113
|
+
check('SIGN02', 'sign command creates signature and keys', signRun.status === 0 && fs.existsSync(path.join(packageOut, 'gse-package-signature.json')) && fs.existsSync(publicKey), signRun.command),
|
|
114
|
+
check('SIGN03', 'verify command accepts signed package', verifyRun.status === 0 && verifyData?.summary?.failed === 0, verifyRun.command),
|
|
115
|
+
check('SIGN04', 'install command accepts signed package with public key', installRun.status === 0 && installData?.signature?.status === 'verified', installRun.command),
|
|
116
|
+
check('SIGN05', 'tampered package fails signature/hash verification', tamperedVerify.status === 1 && tamperedVerifyData?.summary?.failed > 0, tamperedVerify.command),
|
|
117
|
+
check('SIGN06', 'install command rejects tampered signed package', tamperedInstall.status === 1 && (tamperedInstallData?.signature?.status === 'failed' || tamperedInstallData?.summary?.integrityFailed > 0), tamperedInstall.command),
|
|
118
|
+
check('SIGN07', 'signature uses Ed25519', signData?.algorithm === 'ed25519', 'sign-gse-package output'),
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
const passed = checks.filter((item) => item.status === 'passed').length
|
|
122
|
+
const failed = checks.length - passed
|
|
123
|
+
const report = {
|
|
124
|
+
root,
|
|
125
|
+
generatedAt: new Date().toISOString(),
|
|
126
|
+
tempRoot,
|
|
127
|
+
summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, total: checks.length },
|
|
128
|
+
workflows: {
|
|
129
|
+
packageSigning: signRun.status === 0 ? 'verified' : 'failed',
|
|
130
|
+
signatureVerification: verifyRun.status === 0 ? 'verified' : 'failed',
|
|
131
|
+
signedInstall: installRun.status === 0 ? 'verified' : 'failed',
|
|
132
|
+
tamperRejection: tamperedVerify.status === 1 && tamperedInstall.status === 1 ? 'verified' : 'failed',
|
|
133
|
+
},
|
|
134
|
+
commands: [packageRun.command, signRun.command, verifyRun.command, installRun.command, tamperedVerify.command, tamperedInstall.command],
|
|
135
|
+
limits: [
|
|
136
|
+
'This audit verifies local Ed25519 signing and verification mechanics.',
|
|
137
|
+
'It does not prove public key custody, maintainer identity, transparency logs, or marketplace trust.',
|
|
138
|
+
],
|
|
139
|
+
checks,
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
fs.rmSync(tempRoot, { recursive: true, force: true })
|
|
143
|
+
|
|
144
|
+
if (jsonOnly) console.log(JSON.stringify(report, null, 2))
|
|
145
|
+
else console.log(JSON.stringify(report, null, 2))
|
|
146
|
+
|
|
147
|
+
if (failed > 0) process.exit(1)
|