dsh-standards 1.2.0 → 1.2.1
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/40-checker/README.md +2 -2
- package/40-checker/scripts/level-audit.mjs +33 -5
- package/40-checker/src/flow-evidence.mjs +24 -0
- package/40-checker/src/index.mjs +1 -1
- package/40-checker/src/remediation.mjs +14 -9
- package/CHANGELOG.md +3 -1
- package/package.json +1 -2
- package/spec/flow/README.md +4 -0
- package/spec/flow-evidence.schema.json +7 -3
- package/40-checker/test/attestation.test.mjs +0 -59
- package/40-checker/test/checks.test.mjs +0 -414
- package/40-checker/test/claim.test.mjs +0 -28
- package/40-checker/test/client-shape.test.mjs +0 -35
- package/40-checker/test/dco.test.mjs +0 -51
- package/40-checker/test/entry-validate.test.mjs +0 -172
- package/40-checker/test/flow-evidence.test.mjs +0 -295
- package/40-checker/test/governance.test.mjs +0 -76
- package/40-checker/test/label.test.mjs +0 -97
- package/40-checker/test/level-audit.test.mjs +0 -99
- package/40-checker/test/levels.test.mjs +0 -31
- package/40-checker/test/ops.test.mjs +0 -117
- package/40-checker/test/permissions.test.mjs +0 -40
- package/40-checker/test/remediation.test.mjs +0 -300
- package/40-checker/test/semver.test.mjs +0 -32
- package/40-checker/test/verify.test.mjs +0 -57
|
@@ -1,414 +0,0 @@
|
|
|
1
|
-
import test from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
import path from 'node:path'
|
|
4
|
-
import { fileURLToPath } from 'node:url'
|
|
5
|
-
import { runManifestChecks } from '../src/manifest.mjs'
|
|
6
|
-
import { checkCapabilities, checkCredentials, checkImportTimeAdvisory, checkLabels } from '../src/declarations.mjs'
|
|
7
|
-
import { readSources } from '../src/util.mjs'
|
|
8
|
-
import { buildReport } from '../src/report.mjs'
|
|
9
|
-
import { parseInstallSpec } from '../src/install-spec.mjs'
|
|
10
|
-
|
|
11
|
-
const here = path.dirname(fileURLToPath(import.meta.url))
|
|
12
|
-
const good = path.join(here, '..', 'fixtures', 'good')
|
|
13
|
-
const bad = path.join(here, '..', 'fixtures', 'bad')
|
|
14
|
-
|
|
15
|
-
test('good fixture passes manifest and version checks', () => {
|
|
16
|
-
const result = runManifestChecks(good)
|
|
17
|
-
assert.equal(result.kind, 'plugin')
|
|
18
|
-
assert.equal(result.checks[0].id, 'DSC-01')
|
|
19
|
-
assert.equal(result.checks[0].result, 'PASS')
|
|
20
|
-
assert.equal(result.checks[1].result, 'PASS')
|
|
21
|
-
assert.equal(result.checks[2].result, 'NA')
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
test('patch-only bundle reports C-1.2 as NA, not WARN', () => {
|
|
25
|
-
const result = runManifestChecks(path.join(here, '..', 'fixtures', 'bundle-patch-only'))
|
|
26
|
-
const dsc01 = result.checks.find((check) => check.id === 'DSC-01')
|
|
27
|
-
const entry = dsc01.details.find((detail) => detail.item === 'C-1.2')
|
|
28
|
-
assert.equal(entry.result, 'NA')
|
|
29
|
-
assert.match(entry.reason, /patch-only bundle/)
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
test('bad fixture fails version and warns licence', () => {
|
|
33
|
-
const result = runManifestChecks(bad)
|
|
34
|
-
assert.equal(result.checks[0].result, 'WARN')
|
|
35
|
-
assert.equal(result.checks[1].result, 'FAIL')
|
|
36
|
-
assert.match(result.checks[1].reason, /engines\.dsh|peer/)
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
test('good fixture declarations are consistent', () => {
|
|
40
|
-
const result = runManifestChecks(good)
|
|
41
|
-
const sources = readSources(good)
|
|
42
|
-
const caps = checkCapabilities(result.manifest, sources, result.patch)
|
|
43
|
-
const creds = checkCredentials(result.manifest, sources)
|
|
44
|
-
const labels = checkLabels(result.manifest, sources)
|
|
45
|
-
assert.equal(caps.result, 'PASS', caps.reason)
|
|
46
|
-
assert.equal(creds.result, 'PASS', creds.reason)
|
|
47
|
-
assert.equal(labels.result, 'PASS', labels.reason)
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
test('bad fixture rejects official label and unknown capability', () => {
|
|
51
|
-
const result = runManifestChecks(bad)
|
|
52
|
-
const sources = readSources(bad)
|
|
53
|
-
const caps = checkCapabilities(result.manifest, sources, result.patch)
|
|
54
|
-
const labels = checkLabels(result.manifest, sources)
|
|
55
|
-
assert.equal(labels.result, 'FAIL')
|
|
56
|
-
assert.match(labels.reason, /official|forbidden/)
|
|
57
|
-
assert.equal(caps.result, 'WARN')
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
test('parseInstallSpec pins git and rejects floating git', () => {
|
|
61
|
-
const pinned = parseInstallSpec('github:acme/dsh-greet#0123456789abcdef0123456789abcdef01234567')
|
|
62
|
-
assert.equal(pinned.kind, 'git')
|
|
63
|
-
assert.equal(pinned.pinned, true)
|
|
64
|
-
assert.throws(() => parseInstallSpec('github:acme/dsh-greet'), /pin/)
|
|
65
|
-
assert.equal(parseInstallSpec('@acme/dsh-greet@1.2.3').pinned, true)
|
|
66
|
-
assert.equal(parseInstallSpec('@acme/dsh-greet').pinned, false)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
test('report marks missing mandatory checks as incomplete', () => {
|
|
70
|
-
const checks = [
|
|
71
|
-
{ id: 'DSC-01', clause: 'C-1', layer: 'L0', result: 'PASS', coverage: 'full', reason: '', details: [], evidence: [] },
|
|
72
|
-
{ id: 'DSC-02', clause: 'C-2', layer: 'L0', result: 'PASS', coverage: 'full', reason: '', details: [], evidence: [] },
|
|
73
|
-
]
|
|
74
|
-
const report = buildReport({
|
|
75
|
-
subject: { mode: 'package', profile: 'package', kind: 'plugin', kindInferred: false, name: 'x', version: '1.0.0', path: here },
|
|
76
|
-
anchor: { repo: 'x', commit: '0'.repeat(40), scanner: 'x', dsh: '0.2.2-alpha.2', cordis: '4.0.6-alpha.1', runtime: null },
|
|
77
|
-
checks,
|
|
78
|
-
scope: 'ds-c',
|
|
79
|
-
profile: 'package',
|
|
80
|
-
kind: 'plugin',
|
|
81
|
-
})
|
|
82
|
-
assert.equal(report.conclusion.status, 'incomplete')
|
|
83
|
-
assert.equal(report.conclusion.exitCode, 2)
|
|
84
|
-
assert.equal(report.conclusion.level, null)
|
|
85
|
-
assert.ok(report.coverage.notRun > 0)
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
test('report passes when all mandatory checks are full and clean', () => {
|
|
89
|
-
const checks = ['DSC-01', 'DSC-02', 'DSC-04', 'DSC-05', 'DSC-06', 'DSC-07', 'DSC-08', 'DSC-09', 'DSC-11', 'DSC-12', 'DSC-13']
|
|
90
|
-
.map(id => ({ id, clause: id.replace('DSC', 'C'), layer: 'L0', result: 'PASS', coverage: 'full', reason: '', details: [], evidence: [] }))
|
|
91
|
-
const report = buildReport({
|
|
92
|
-
subject: { mode: 'package', profile: 'package', kind: 'plugin', kindInferred: false, name: 'x', version: '1.0.0', path: here },
|
|
93
|
-
anchor: { repo: 'x', commit: '0'.repeat(40), scanner: 'x', dsh: '0.2.2-alpha.2', cordis: '4.0.6-alpha.1', runtime: null },
|
|
94
|
-
checks,
|
|
95
|
-
scope: 'ds-c',
|
|
96
|
-
profile: 'package',
|
|
97
|
-
kind: 'plugin',
|
|
98
|
-
})
|
|
99
|
-
assert.equal(report.conclusion.status, 'pass')
|
|
100
|
-
assert.equal(report.conclusion.exitCode, 0)
|
|
101
|
-
assert.equal(report.conclusion.level, 'DS-C (package)')
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
test('static-only M1 report maps L1-L3 to NA and issues DS-C static', () => {
|
|
105
|
-
const checks = ['DSC-01', 'DSC-02', 'DSC-03', 'DSC-11', 'DSC-12', 'DSC-13']
|
|
106
|
-
.map(id => ({ id, clause: id.replace('DSC', 'C'), layer: 'L0', result: 'PASS', coverage: 'full', reason: '', details: [], evidence: [] }))
|
|
107
|
-
.concat(['DSC-04', 'DSC-05', 'DSC-06', 'DSC-07', 'DSC-08', 'DSC-09'].map(id => ({ id, clause: id.replace('DSC', 'C'), layer: 'L1', result: 'ERROR', coverage: 'not-run', reason: 'no runtime', details: [], evidence: [] })))
|
|
108
|
-
const report = buildReport({
|
|
109
|
-
subject: { mode: 'listing', profile: 'listing', kind: 'plugin', kindInferred: false, name: 'x', version: '1.0.0', path: here },
|
|
110
|
-
anchor: { repo: 'x', commit: '0'.repeat(40), scanner: 'x', dsh: '0.2.2-alpha.2', cordis: '4.0.6-alpha.1', runtime: null },
|
|
111
|
-
checks,
|
|
112
|
-
scope: 'ds-c',
|
|
113
|
-
profile: 'listing',
|
|
114
|
-
kind: 'plugin',
|
|
115
|
-
staticOnly: true,
|
|
116
|
-
})
|
|
117
|
-
assert.equal(report.conclusion.status, 'pass')
|
|
118
|
-
assert.equal(report.conclusion.level, 'DS-C static')
|
|
119
|
-
assert.equal(report.conclusion.staticOnly, true)
|
|
120
|
-
assert.equal(report.checks.find(item => item.id === 'DSC-04').result, 'NA')
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
function entryChecks(overrides = {}) {
|
|
124
|
-
const make = (id, clause, result = 'PASS', details = []) => ({ id, clause, layer: 'L0', result, coverage: result === 'ERROR' ? 'not-run' : 'full', reason: '', details, evidence: [] })
|
|
125
|
-
return [
|
|
126
|
-
make('DSC-01', 'C-1'),
|
|
127
|
-
make('DSC-02', 'C-2', overrides.c2Result ?? 'PASS', overrides.c2Details ?? []),
|
|
128
|
-
make('DSC-11', 'C-11'),
|
|
129
|
-
make('DSC-12', 'C-12'),
|
|
130
|
-
make('DSC-13', 'C-13'),
|
|
131
|
-
make('DSC-14', 'C-14', overrides.runResult ?? 'PASS'),
|
|
132
|
-
]
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
test('entry: C-2.1 only is WARN, runtime level', () => {
|
|
136
|
-
const report = buildReport({
|
|
137
|
-
subject: { mode: 'package', profile: 'package', kind: 'plugin', kindInferred: false, name: 'x', version: '1.0.0', path: here },
|
|
138
|
-
anchor: { repo: 'x', commit: '0'.repeat(40), scanner: 'x', dsh: '0.2.2-alpha.2', cordis: '4.0.6-alpha.1', runtime: null },
|
|
139
|
-
checks: entryChecks({ c2Result: 'FAIL', c2Details: [{ item: 'C-2.1', result: 'FAIL', reason: 'no engines' }] }),
|
|
140
|
-
scope: 'entry', profile: 'package', kind: 'plugin',
|
|
141
|
-
})
|
|
142
|
-
assert.equal(report.conclusion.status, 'warn')
|
|
143
|
-
assert.equal(report.conclusion.level, 'DSH-ENTRY runtime (warnings)')
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
test('entry: C-2.2 remains blocking', () => {
|
|
147
|
-
const report = buildReport({
|
|
148
|
-
subject: { mode: 'package', profile: 'package', kind: 'plugin', kindInferred: false, name: 'x', version: '1.0.0', path: here },
|
|
149
|
-
anchor: { repo: 'x', commit: '0'.repeat(40), scanner: 'x', dsh: '0.2.2-alpha.2', cordis: '4.0.6-alpha.1', runtime: null },
|
|
150
|
-
checks: entryChecks({ c2Result: 'FAIL', c2Details: [{ item: 'C-2.2', result: 'FAIL', reason: 'peer mismatch' }] }),
|
|
151
|
-
scope: 'entry', profile: 'package', kind: 'plugin',
|
|
152
|
-
})
|
|
153
|
-
assert.equal(report.conclusion.status, 'fail')
|
|
154
|
-
assert.ok(report.coverage.entryBlocking.failed.includes('C-2.2'))
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
test('entry: no runtime is incomplete, not static pass', () => {
|
|
158
|
-
const report = buildReport({
|
|
159
|
-
subject: { mode: 'package', profile: 'package', kind: 'plugin', kindInferred: false, name: 'x', version: '1.0.0', path: here },
|
|
160
|
-
anchor: { repo: 'x', commit: '0'.repeat(40), scanner: 'x', dsh: '0.2.2-alpha.2', cordis: '4.0.6-alpha.1', runtime: null },
|
|
161
|
-
checks: entryChecks({ runResult: 'ERROR' }),
|
|
162
|
-
scope: 'entry', profile: 'package', kind: 'plugin',
|
|
163
|
-
})
|
|
164
|
-
assert.equal(report.conclusion.status, 'incomplete')
|
|
165
|
-
assert.equal(report.conclusion.exitCode, 2)
|
|
166
|
-
assert.equal(report.conclusion.level, null)
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
test('entry: static claim recognizes without runtime', () => {
|
|
170
|
-
const checks = entryChecks().filter(c => c.id !== 'DSC-14')
|
|
171
|
-
const report = buildReport({
|
|
172
|
-
subject: { mode: 'package', profile: 'package', kind: 'plugin', kindInferred: false, name: 'x', version: '1.0.0', path: here },
|
|
173
|
-
anchor: { repo: 'x', commit: '0'.repeat(40), scanner: 'x', dsh: '0.2.2-alpha.2', cordis: '4.0.6-alpha.1', runtime: null },
|
|
174
|
-
checks,
|
|
175
|
-
scope: 'entry', profile: 'package', kind: 'plugin', entryClaim: 'static',
|
|
176
|
-
})
|
|
177
|
-
assert.equal(report.conclusion.status, 'pass')
|
|
178
|
-
assert.equal(report.conclusion.level, 'DSH-ENTRY recognized')
|
|
179
|
-
assert.equal(report.conclusion.exitCode, 0)
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
test('DSC-10.4: flags a module-scope fs mutation in the entry module', () => {
|
|
183
|
-
const dir = path.join(here, '..', 'fixtures', 'import-side-effect')
|
|
184
|
-
const result = runManifestChecks(dir)
|
|
185
|
-
const check = checkImportTimeAdvisory(readSources(dir), result.entry?.relative)
|
|
186
|
-
assert.equal(check.result, 'WARN', check.reason)
|
|
187
|
-
assert.ok(check.details.some((detail) => /top-level fs mutation/.test(detail.reason)), JSON.stringify(check.details))
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
test('DSC-10.4: ignores a module-scope fs mutation outside the entry module', () => {
|
|
191
|
-
const dir = path.join(here, '..', 'fixtures', 'import-side-effect-nonentry')
|
|
192
|
-
const result = runManifestChecks(dir)
|
|
193
|
-
const check = checkImportTimeAdvisory(readSources(dir), result.entry?.relative)
|
|
194
|
-
assert.equal(check.result, 'PASS', check.reason)
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
test('DSC-10.4: a clean entry module still passes', () => {
|
|
198
|
-
const result = runManifestChecks(good)
|
|
199
|
-
const check = checkImportTimeAdvisory(readSources(good), result.entry?.relative)
|
|
200
|
-
assert.equal(check.result, 'PASS', check.reason)
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
test('DSC-10.1/2: the existing patterns are entry-scoped too', () => {
|
|
204
|
-
const dir = path.join(here, '..', 'fixtures', 'import-side-effect-nonentry')
|
|
205
|
-
const result = runManifestChecks(dir)
|
|
206
|
-
const check = checkImportTimeAdvisory(readSources(dir), result.entry?.relative)
|
|
207
|
-
assert.equal(check.result, 'PASS', check.reason)
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
test('DSC-10.4: a read-only openSync is not a write mutation', () => {
|
|
211
|
-
const dir = path.join(here, '..', 'fixtures', 'import-open-readonly')
|
|
212
|
-
const result = runManifestChecks(dir)
|
|
213
|
-
const check = checkImportTimeAdvisory(readSources(dir), result.entry?.relative)
|
|
214
|
-
assert.equal(check.result, 'PASS', check.reason)
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
test('DSC-10: no source files is NA, not WARN', () => {
|
|
218
|
-
const dir = path.join(here, '..', 'fixtures', 'no-source')
|
|
219
|
-
const check = checkImportTimeAdvisory(readSources(dir), 'index.mjs')
|
|
220
|
-
assert.equal(check.result, 'NA', check.reason)
|
|
221
|
-
assert.match(check.reason, /no source/)
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
test('DSC-10.2: flags an immediate module-scope void main()', () => {
|
|
225
|
-
const dir = path.join(here, '..', 'fixtures', 'import-void-main')
|
|
226
|
-
const result = runManifestChecks(dir)
|
|
227
|
-
const check = checkImportTimeAdvisory(readSources(dir), result.entry?.relative)
|
|
228
|
-
assert.equal(check.result, 'WARN', check.reason)
|
|
229
|
-
assert.ok(check.details.some((detail) => /immediate main/.test(detail.reason)), JSON.stringify(check.details))
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
test('DSC-10.1/2: AST catches top-level await, timer, process hooks and globals', () => {
|
|
233
|
-
const text = [
|
|
234
|
-
"await Promise.resolve()",
|
|
235
|
-
'for await (const item of []) {}',
|
|
236
|
-
"setInterval(() => {}, 1000)",
|
|
237
|
-
"process.on('SIGINT', () => {})",
|
|
238
|
-
"process.env.DSH_FIXTURE = '1'",
|
|
239
|
-
'globalThis.__dshFixture = true',
|
|
240
|
-
].join('\n')
|
|
241
|
-
const check = checkImportTimeAdvisory([{ relative: 'index.mjs', text }], 'index.mjs')
|
|
242
|
-
assert.equal(check.result, 'WARN', check.reason)
|
|
243
|
-
assert.equal(check.details.filter((detail) => /top-level await/.test(detail.reason)).length, 2)
|
|
244
|
-
for (const pattern of ['top-level setInterval', 'top-level process.on', 'top-level process.env assignment', 'top-level globalThis assignment']) {
|
|
245
|
-
assert.ok(check.details.some((detail) => detail.reason.includes(pattern)), `${pattern}: ${JSON.stringify(check.details)}`)
|
|
246
|
-
}
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
test('DSC-10.4: member fs writes and module-scope control flow count', () => {
|
|
250
|
-
const text = [
|
|
251
|
-
"import * as fs from 'node:fs'",
|
|
252
|
-
'if (process.env.DSH_FIXTURE) {',
|
|
253
|
-
" fs.writeFileSync('/tmp/dsh-fixture-member-write.txt', 'x')",
|
|
254
|
-
'}',
|
|
255
|
-
].join('\n')
|
|
256
|
-
const check = checkImportTimeAdvisory([{ relative: 'index.mjs', text }], 'index.mjs')
|
|
257
|
-
assert.equal(check.result, 'WARN', check.reason)
|
|
258
|
-
assert.ok(check.details.some((detail) => /top-level fs mutation/.test(detail.reason)), JSON.stringify(check.details))
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
test('DSC-10: function, arrow and class-method bodies are not module-scope side effects', () => {
|
|
262
|
-
const text = [
|
|
263
|
-
"function a() { writeFileSync('/tmp/a', 'x') }",
|
|
264
|
-
"const b = () => spawnSync('cmd')",
|
|
265
|
-
"class C { m() { process.on('x', () => {}) } }",
|
|
266
|
-
'const d = { n() { setInterval(() => {}, 1) } }',
|
|
267
|
-
].join('\n')
|
|
268
|
-
const check = checkImportTimeAdvisory([{ relative: 'index.mjs', text }], 'index.mjs')
|
|
269
|
-
assert.equal(check.result, 'PASS', check.reason)
|
|
270
|
-
})
|
|
271
|
-
|
|
272
|
-
test('DSC-12 C-12.4: ignores comment-only literals', () => {
|
|
273
|
-
const dir = path.join(here, '..', 'fixtures', 'c12-comment')
|
|
274
|
-
const check = checkCredentials({}, readSources(dir))
|
|
275
|
-
assert.equal(check.result, 'PASS', check.reason)
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
test('DSC-12 C-12.4: ignores equality comparisons', () => {
|
|
279
|
-
const dir = path.join(here, '..', 'fixtures', 'c12-compare')
|
|
280
|
-
const check = checkCredentials({}, readSources(dir))
|
|
281
|
-
assert.equal(check.result, 'PASS', check.reason)
|
|
282
|
-
})
|
|
283
|
-
|
|
284
|
-
test('DSC-12 C-12.4: still flags an actual set() grant', () => {
|
|
285
|
-
const dir = path.join(here, '..', 'fixtures', 'c12-grant')
|
|
286
|
-
const check = checkCredentials({}, readSources(dir))
|
|
287
|
-
assert.equal(check.result, 'WARN', check.reason)
|
|
288
|
-
assert.ok(check.details.some((detail) => detail.item === 'C-12.4'), JSON.stringify(check.details))
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
test('DSC-12 C-12.4: console-only documentation string is not a grant', () => {
|
|
292
|
-
const dir = path.join(here, '..', 'fixtures', 'c12-string')
|
|
293
|
-
const check = checkCredentials({}, readSources(dir))
|
|
294
|
-
assert.equal(check.result, 'PASS', check.reason)
|
|
295
|
-
assert.ok(!check.details.some((detail) => detail.item === 'C-12.4'), JSON.stringify(check.details))
|
|
296
|
-
})
|
|
297
|
-
|
|
298
|
-
test('DSC-12 C-12.4: console/desc documentation strings are not grants', () => {
|
|
299
|
-
const dir = path.join(here, '..', 'fixtures', 'c12-doc-string')
|
|
300
|
-
const check = checkCredentials({}, readSources(dir))
|
|
301
|
-
assert.equal(check.result, 'PASS', check.reason)
|
|
302
|
-
assert.ok(!check.details.some((detail) => detail.item === 'C-12.4'), JSON.stringify(check.details))
|
|
303
|
-
})
|
|
304
|
-
|
|
305
|
-
test('DSC-11 C-11.4: console/desc documentation strings are not danger literals', () => {
|
|
306
|
-
const dir = path.join(here, '..', 'fixtures', 'c12-doc-string')
|
|
307
|
-
const check = checkCapabilities({}, readSources(dir))
|
|
308
|
-
assert.ok(!check.details.some((detail) => detail.item === 'C-11.4'), JSON.stringify(check.details))
|
|
309
|
-
})
|
|
310
|
-
|
|
311
|
-
test('DSC-11 C-11.4: --add-dir alone is a C-12.4 concern, not C-11.4', () => {
|
|
312
|
-
const sources = [{ relative: 'index.mjs', text: "spawn('dsh', ['--add-dir', './docs'])\n" }]
|
|
313
|
-
const check = checkCapabilities({}, sources)
|
|
314
|
-
assert.ok(!check.details.some((detail) => detail.item === 'C-11.4'), JSON.stringify(check.details))
|
|
315
|
-
})
|
|
316
|
-
|
|
317
|
-
test('DSC-12 C-12.4: spawn args with --add-dir are a real command string', () => {
|
|
318
|
-
const dir = path.join(here, '..', 'fixtures', 'c12-command-arg')
|
|
319
|
-
const check = checkCredentials({}, readSources(dir))
|
|
320
|
-
assert.equal(check.result, 'WARN', check.reason)
|
|
321
|
-
assert.ok(check.details.some((detail) => detail.item === 'C-12.4'), JSON.stringify(check.details))
|
|
322
|
-
})
|
|
323
|
-
|
|
324
|
-
test('DSC-12 C-12.4: set/resolve/mode/switch contexts are real grants', () => {
|
|
325
|
-
const text = [
|
|
326
|
-
"svc.set(session, 'danger-full-access')",
|
|
327
|
-
"svc.resolve('danger-full-access')",
|
|
328
|
-
"const policy = { sandboxPolicy: { mode: 'danger-full-access' } }",
|
|
329
|
-
"switch (mode) { case 'danger-full-access': break }",
|
|
330
|
-
].join('\n')
|
|
331
|
-
const check = checkCredentials({}, [{ relative: 'index.mjs', text }])
|
|
332
|
-
assert.equal(check.result, 'WARN', check.reason)
|
|
333
|
-
const detail = check.details.find((item) => item.item === 'C-12.4')
|
|
334
|
-
assert.ok(detail, JSON.stringify(check.details))
|
|
335
|
-
assert.match(detail.reason, /\b4\b/)
|
|
336
|
-
})
|
|
337
|
-
|
|
338
|
-
test('DSC-12 C-12.4: nested command properties outrank documentation keys', () => {
|
|
339
|
-
const check = checkCredentials({}, [{ relative: 'index.mjs', text: "const policy = { sandboxPolicy: { description: 'danger-full-access' } }\n" }])
|
|
340
|
-
assert.equal(check.result, 'WARN', check.reason)
|
|
341
|
-
})
|
|
342
|
-
|
|
343
|
-
test('DSC-12 C-12.4: an unknown context stays a conservative warning', () => {
|
|
344
|
-
const check = checkCredentials({}, [{ relative: 'index.mjs', text: "const mode = 'danger-full-access'\n" }])
|
|
345
|
-
assert.equal(check.result, 'WARN', check.reason)
|
|
346
|
-
})
|
|
347
|
-
|
|
348
|
-
test('DSC-12 C-12.4: an unparsable source is not-run, not PASS', () => {
|
|
349
|
-
const check = checkCredentials({}, [{ relative: 'index.mjs', text: 'const broken = {\n' }])
|
|
350
|
-
assert.equal(check.result, 'ERROR', check.reason)
|
|
351
|
-
assert.equal(check.coverage, 'not-run')
|
|
352
|
-
assert.ok(check.details.some((detail) => detail.item === 'C-12.4' && /not-run/.test(detail.reason)), JSON.stringify(check.details))
|
|
353
|
-
})
|
|
354
|
-
|
|
355
|
-
test('DSC-10.3: follows a local relative import and flags its top-level fs mutation', () => {
|
|
356
|
-
const dir = path.join(here, '..', 'fixtures', 'import-local-side-effect')
|
|
357
|
-
const result = runManifestChecks(dir)
|
|
358
|
-
const check = checkImportTimeAdvisory(readSources(dir), result.entry?.relative)
|
|
359
|
-
assert.equal(check.result, 'WARN', check.reason)
|
|
360
|
-
assert.ok(check.details.some((detail) => /top-level fs mutation/.test(detail.reason) && /side-effectful\.mjs/.test(detail.reason)), JSON.stringify(check.details))
|
|
361
|
-
})
|
|
362
|
-
|
|
363
|
-
test('DSC-10.3: a package-only entry is PASS with a C-10.3 not-run coverage note', () => {
|
|
364
|
-
const dir = path.join(here, '..', 'fixtures', 'import-package-only')
|
|
365
|
-
const result = runManifestChecks(dir)
|
|
366
|
-
const check = checkImportTimeAdvisory(readSources(dir), result.entry?.relative)
|
|
367
|
-
assert.equal(check.result, 'PASS', check.reason)
|
|
368
|
-
const note = check.details.find((detail) => detail.item === 'C-10.3')
|
|
369
|
-
assert.ok(note, JSON.stringify(check.details))
|
|
370
|
-
assert.match(note.reason, /not-run/)
|
|
371
|
-
assert.match(note.reason, /node:path/)
|
|
372
|
-
})
|
|
373
|
-
|
|
374
|
-
test('DSC-10.3: an unresolved local import records not-run without becoming a WARN', () => {
|
|
375
|
-
const sources = [{ relative: 'index.mjs', text: "import './missing-module.mjs'\n" }]
|
|
376
|
-
const check = checkImportTimeAdvisory(sources, 'index.mjs')
|
|
377
|
-
assert.equal(check.result, 'PASS', check.reason)
|
|
378
|
-
assert.ok(check.details.some((detail) => detail.item === 'C-10.3' && /not-run/.test(detail.reason)), JSON.stringify(check.details))
|
|
379
|
-
})
|
|
380
|
-
|
|
381
|
-
test('DSC-10.3: a require inside a function is not an import-time edge', () => {
|
|
382
|
-
const sources = [
|
|
383
|
-
{ relative: 'index.mjs', text: "function load() { return require('./side-effectful.mjs') }\n" },
|
|
384
|
-
{ relative: 'side-effectful.mjs', text: "writeFileSync('/tmp/x', 'x')\n" },
|
|
385
|
-
]
|
|
386
|
-
const check = checkImportTimeAdvisory(sources, 'index.mjs')
|
|
387
|
-
assert.equal(check.result, 'PASS', check.reason)
|
|
388
|
-
assert.ok(!check.details.some((detail) => /side-effectful/.test(detail.reason)), JSON.stringify(check.details))
|
|
389
|
-
})
|
|
390
|
-
|
|
391
|
-
test('DSC-10: an unparsable entry is ERROR/not-run, not PASS', () => {
|
|
392
|
-
const dir = path.join(here, '..', 'fixtures', 'import-parse-error')
|
|
393
|
-
const result = runManifestChecks(dir)
|
|
394
|
-
const check = checkImportTimeAdvisory(readSources(dir), result.entry?.relative)
|
|
395
|
-
assert.equal(check.result, 'ERROR', check.reason)
|
|
396
|
-
assert.equal(check.coverage, 'not-run')
|
|
397
|
-
})
|
|
398
|
-
|
|
399
|
-
test('DSC-10/DSC-12: a broken .d.ts is skipped, not ERROR', () => {
|
|
400
|
-
const dir = path.join(here, '..', 'fixtures', 'declaration-file')
|
|
401
|
-
const result = runManifestChecks(dir)
|
|
402
|
-
const c10 = checkImportTimeAdvisory(readSources(dir), result.entry?.relative)
|
|
403
|
-
const c12 = checkCredentials({}, readSources(dir))
|
|
404
|
-
assert.equal(c10.result, 'PASS', c10.reason)
|
|
405
|
-
assert.equal(c12.result, 'PASS', c12.reason)
|
|
406
|
-
})
|
|
407
|
-
|
|
408
|
-
test('DSC-12 C-12.4: one unparsable non-entry file is partial, not ERROR', () => {
|
|
409
|
-
const dir = path.join(here, '..', 'fixtures', 'parse-error-nonentry')
|
|
410
|
-
const check = checkCredentials({}, readSources(dir))
|
|
411
|
-
assert.notEqual(check.result, 'ERROR', check.reason)
|
|
412
|
-
assert.equal(check.coverage, 'partial')
|
|
413
|
-
assert.ok(check.details.some((detail) => detail.item === 'C-12.4' && /not-run/.test(detail.reason)), JSON.stringify(check.details))
|
|
414
|
-
})
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import test from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
import { scanText } from '../scripts/claim-check.mjs'
|
|
4
|
-
|
|
5
|
-
test('claim: an official claim fails', () => {
|
|
6
|
-
const r = scanText('DSH-ENTRY is an official certification.', 'x.md')
|
|
7
|
-
assert.equal(r.errors.length, 1)
|
|
8
|
-
})
|
|
9
|
-
|
|
10
|
-
test('claim: a negated claim passes', () => {
|
|
11
|
-
const r = scanText('DSH-ENTRY is not an official certification.', 'x.md')
|
|
12
|
-
assert.deepEqual(r.errors, [])
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
test('claim: Chinese negation passes', () => {
|
|
16
|
-
const r = scanText('DSH-ENTRY 不是官方认证,也不做安全审计。', 'x.md')
|
|
17
|
-
assert.deepEqual(r.errors, [])
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
test('claim: a mark without a disclaimer warns', () => {
|
|
21
|
-
const r = scanText('DSH-FLOW manual', 'x.md')
|
|
22
|
-
assert.equal(r.warnings.length, 1)
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
test('claim: a mark with a disclaimer does not warn', () => {
|
|
26
|
-
const r = scanText('DSH-FLOW manual\n非官方 · 非认证 · 非安全审计', 'x.md')
|
|
27
|
-
assert.deepEqual(r.warnings, [])
|
|
28
|
-
})
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import test from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
import fs from 'node:fs'
|
|
4
|
-
import path from 'node:path'
|
|
5
|
-
import { fileURLToPath } from 'node:url'
|
|
6
|
-
import { runManifestChecks } from '../src/manifest.mjs'
|
|
7
|
-
|
|
8
|
-
const HERE = path.dirname(fileURLToPath(import.meta.url))
|
|
9
|
-
const ROOT = path.resolve(HERE, '..')
|
|
10
|
-
|
|
11
|
-
test('G4: no dsh.client is not-applicable, not failed', () => {
|
|
12
|
-
const { g4 } = runManifestChecks(path.join(ROOT, 'fixtures/good'))
|
|
13
|
-
assert.equal(g4.hasClient, false)
|
|
14
|
-
assert.equal(g4.ok, true)
|
|
15
|
-
assert.equal(g4.result, 'NA')
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
test('G4: dsh.client without exports["./client"] fails statically', () => {
|
|
19
|
-
const { g4 } = runManifestChecks(path.join(ROOT, 'fixtures/client-missing'))
|
|
20
|
-
assert.equal(g4.hasClient, true)
|
|
21
|
-
assert.equal(g4.ok, false)
|
|
22
|
-
assert.equal(g4.result, 'FAIL')
|
|
23
|
-
assert.match(g4.reason, /exports\["\.\/client"\]/)
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
test('G4: a client package with the export passes statically', () => {
|
|
27
|
-
const manifest = JSON.parse(fs.readFileSync(path.join(ROOT, 'fixtures/client-missing/package.json'), 'utf8'))
|
|
28
|
-
manifest.exports['./client'] = './client.js'
|
|
29
|
-
const dir = fs.mkdtempSync(path.join(ROOT, 'fixtures', 'client-ok-'))
|
|
30
|
-
fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify(manifest))
|
|
31
|
-
const { g4 } = runManifestChecks(dir)
|
|
32
|
-
assert.equal(g4.ok, true)
|
|
33
|
-
assert.equal(g4.result, 'PASS')
|
|
34
|
-
fs.rmSync(dir, { recursive: true, force: true })
|
|
35
|
-
})
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import test from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
import { parseLog, checkCommit, checkTerms } from '../scripts/dco-check.mjs'
|
|
4
|
-
|
|
5
|
-
function commit(body, email = 'a@example.com') {
|
|
6
|
-
return { sha: 'abcdef1234567890', authorName: 'Alice', authorEmail: email, body }
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
test('dco: a signed commit passes', () => {
|
|
10
|
-
const r = checkCommit(commit('feat: x\n\nSigned-off-by: Alice <a@example.com>'))
|
|
11
|
-
assert.deepEqual(r.errors, [])
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
test('dco: missing sign-off fails', () => {
|
|
15
|
-
const r = checkCommit(commit('feat: x'))
|
|
16
|
-
assert.ok(r.errors.some((e) => /missing Signed-off-by/.test(e)))
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
test('dco: author email must be among sign-offs', () => {
|
|
20
|
-
const r = checkCommit(commit('feat: x\n\nSigned-off-by: Alice <other@example.com>'))
|
|
21
|
-
assert.ok(r.errors.some((e) => /not among the sign-offs/.test(e)))
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
test('dco: AI-assisted requires a tool and a human sign-off', () => {
|
|
25
|
-
const r = checkCommit(commit('feat: x\n\nAI-Assisted: yes\nSigned-off-by: [bot] <bot@example.com>'))
|
|
26
|
-
assert.ok(r.errors.some((e) => /AI-Tool/.test(e)))
|
|
27
|
-
assert.ok(r.errors.some((e) => /human sign-off/.test(e)))
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
test('dco: terms checkbox counts', () => {
|
|
31
|
-
const r = checkTerms({
|
|
32
|
-
commits: [{ body: 'feat: x' }],
|
|
33
|
-
prBody: '- [x] I agree to the DSH Contribution Terms v1 (dsh-ct/1)',
|
|
34
|
-
requireTerms: true,
|
|
35
|
-
})
|
|
36
|
-
assert.deepEqual(r.errors, [])
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
test('dco: missing terms fails only when required', () => {
|
|
40
|
-
const r = checkTerms({ commits: [{ body: 'feat: x' }], prBody: '', requireTerms: true })
|
|
41
|
-
assert.ok(r.errors.length === 1)
|
|
42
|
-
assert.deepEqual(checkTerms({ commits: [{ body: 'feat: x' }], prBody: '', requireTerms: false }).errors, [])
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
test('dco: parseLog keeps author and body', () => {
|
|
46
|
-
const raw = 'abc\x1fAlice\x1fa@example.com\x1ffeat: x\n\nSigned-off-by: Alice <a@example.com>\x1e'
|
|
47
|
-
const [c] = parseLog(raw)
|
|
48
|
-
assert.equal(c.sha, 'abc')
|
|
49
|
-
assert.equal(c.authorEmail, 'a@example.com')
|
|
50
|
-
assert.match(c.body, /Signed-off-by/)
|
|
51
|
-
})
|