dsh-standards 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/40-checker/README.md +18 -1
- package/40-checker/scripts/build-offline-kit.mjs +284 -0
- package/40-checker/scripts/build-sidecar.mjs +484 -0
- package/40-checker/scripts/flow-collect.mjs +7 -1
- package/40-checker/scripts/level-audit.mjs +45 -7
- package/40-checker/scripts/run-tests.mjs +3 -7
- package/40-checker/src/author-cli.mjs +139 -0
- package/40-checker/src/author-report.mjs +260 -0
- package/40-checker/src/cli.mjs +73 -8
- package/40-checker/src/flow-evidence.mjs +106 -1
- package/40-checker/src/index.mjs +3 -3
- package/40-checker/src/report.mjs +125 -24
- package/CHANGELOG.md +21 -0
- package/README.en.md +7 -2
- package/README.es.md +7 -2
- package/README.hi.md +7 -2
- package/README.md +7 -2
- package/README.pt.md +7 -2
- package/bin/dsh-standards.mjs +13 -2
- package/docs/assets/dsh-standards-flow-v3-dark.svg +87 -0
- package/docs/assets/dsh-standards-flow-v3-light.svg +87 -0
- package/docs/assets/dsh-standards-flow-v4-dark.svg +79 -0
- package/docs/assets/dsh-standards-flow-v4-light.svg +79 -0
- package/docs/assets/dsh-standards-hero-en-dark.webp +0 -0
- package/docs/assets/dsh-standards-hero-en-light.webp +0 -0
- package/docs/assets/dsh-standards-hero-zh-dark.webp +0 -0
- package/docs/assets/dsh-standards-hero-zh-light.webp +0 -0
- package/package.json +1 -1
- package/spec/README.md +2 -0
- package/spec/flow-evidence.schema.json +6 -2
- package/spec/schemas/advice.schema.json +92 -0
package/40-checker/src/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/** Programmatic API for dsh-plugin-verify. */
|
|
2
|
-
export { main, parseArgs, usage, VERSION } from './cli.mjs'
|
|
2
|
+
export { main, parseArgs, usage, VERSION, detectHostRuntime, resolveRuntimeAnchor, declarationsOf } from './cli.mjs'
|
|
3
3
|
export { runManifestChecks, checkMarketEntry, DEFAULT_RUNTIME_VERSION, inferKind, resolveEntry } from './manifest.mjs'
|
|
4
4
|
export { checkCapabilities, checkCredentials, checkLabels, checkImportTimeAdvisory, CAPABILITIES, LABELS } from './declarations.mjs'
|
|
5
5
|
export { runRuntimeChecks } from './runtime.mjs'
|
|
6
|
-
export { buildReport, formatText, formatSarif, writeReport } from './report.mjs'
|
|
6
|
+
export { buildReport, formatText, formatSarif, writeReport, summarizeBlocking, DECLARATION_CHECKS } from './report.mjs'
|
|
7
7
|
export { joinAdvice, DOCS_BASE } from './remediation.mjs'
|
|
8
8
|
export { satisfies, validRange, parseVersion, compareVersions } from './semver.mjs'
|
|
9
9
|
export { parseInstallSpec } from './install-spec.mjs'
|
|
10
|
-
export { FLOW_IDS, FLOW_EVIDENCE_SCHEMA, evidenceHash, finalizeEvidence, journeyAggregate, isSandboxAttested, treeSha256, validateFlowEvidence } from './flow-evidence.mjs'
|
|
10
|
+
export { FLOW_IDS, FLOW_EVIDENCE_SCHEMA, evidenceHash, finalizeEvidence, journeyAggregate, isSandboxAttested, treeSha256, validateFlowEvidence, recomputeSubject, bindEvidenceSubject, ciAttribution } from './flow-evidence.mjs'
|
|
11
11
|
export { sanitizedEnv, scrubProcessEnv, isSecretEnvName, isAllowedEnvName, envPolicy } from './sandbox-env.mjs'
|
|
@@ -31,6 +31,46 @@ function adjustEntryCheck(item, entryClaim) {
|
|
|
31
31
|
return { ...item, result: 'WARN', reason: 'C-2.1 version declaration missing/invalid; entry treats it as WARN (C-2.2/C-2.3 remain blocking)' }
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
/** Declaration-class checks: a WARN here is hygiene, not a loadability defect. */
|
|
35
|
+
export const DECLARATION_CHECKS = new Set(['DSC-11', 'DSC-12', 'DSC-13'])
|
|
36
|
+
const DETAIL_RANK = { FAIL: 5, ERROR: 4, WARN: 3, PASS: 2, NA: 1 }
|
|
37
|
+
|
|
38
|
+
function bucketRow(item, extra = {}) {
|
|
39
|
+
return {
|
|
40
|
+
id: item.id,
|
|
41
|
+
clause: item.clause ?? null,
|
|
42
|
+
result: item.result,
|
|
43
|
+
coverage: item.coverage ?? null,
|
|
44
|
+
reason: item.reason ?? '',
|
|
45
|
+
...extra,
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* `entryBlocking` is only sound when every prerequisite was actually evaluated.
|
|
51
|
+
* C-1.x always produce details; C-3.1/3.2/3.5 only produce them when an install
|
|
52
|
+
* spec or market index was supplied. Missing details must therefore make
|
|
53
|
+
* `pass` false instead of silently claiming the gate held.
|
|
54
|
+
*/
|
|
55
|
+
export function summarizeBlocking(checks, blocking) {
|
|
56
|
+
const seen = new Map()
|
|
57
|
+
for (const item of checks) {
|
|
58
|
+
for (const detail of item.details ?? []) {
|
|
59
|
+
if (!blocking.includes(detail.item)) continue
|
|
60
|
+
const rank = DETAIL_RANK[detail.result] ?? 0
|
|
61
|
+
if (!seen.has(detail.item) || rank > (DETAIL_RANK[seen.get(detail.item)] ?? 0)) seen.set(detail.item, detail.result)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const checked = blocking.filter(id => seen.has(id))
|
|
65
|
+
// C-2.3 (workspace: range) has no detail when C-2.2 was evaluated against a
|
|
66
|
+
// normal range; evaluating C-2.2 rules the workspace form out, so count it.
|
|
67
|
+
if (seen.has('C-2.2') && blocking.includes('C-2.3') && !checked.includes('C-2.3')) checked.push('C-2.3')
|
|
68
|
+
checked.sort((a, b) => blocking.indexOf(a) - blocking.indexOf(b))
|
|
69
|
+
const failed = blocking.filter(id => seen.get(id) === 'FAIL')
|
|
70
|
+
const notChecked = blocking.filter(id => !checked.includes(id))
|
|
71
|
+
return { required: blocking.length, checked, notChecked, failed, pass: failed.length === 0 && notChecked.length === 0 }
|
|
72
|
+
}
|
|
73
|
+
|
|
34
74
|
function allowedNa(check, scope, profile, kind) {
|
|
35
75
|
if (check.result !== 'NA') return false
|
|
36
76
|
if (check.id === 'DSC-03' && scope !== 'mvp' && profile === 'package') return true
|
|
@@ -39,7 +79,7 @@ function allowedNa(check, scope, profile, kind) {
|
|
|
39
79
|
return false
|
|
40
80
|
}
|
|
41
81
|
|
|
42
|
-
export function buildReport({ subject, anchor, checks, g4 = null, scope, profile, kind, staticOnly = false, entryRuntime = 'require', entryClaim = 'runtime', advice = true }) {
|
|
82
|
+
export function buildReport({ subject, anchor, checks, g4 = null, scope, profile, kind, staticOnly = false, entryRuntime = 'require', entryClaim = 'runtime', advice = true, declarations = null }) {
|
|
43
83
|
const mandatory = mandatoryFor(scope, profile, staticOnly, entryClaim)
|
|
44
84
|
const adjustedChecks = scope === 'entry' ? checks.map(item => adjustEntryCheck(item, entryClaim)) : checks
|
|
45
85
|
const runtimeIds = new Set(['DSC-04', 'DSC-05', 'DSC-06', 'DSC-07', 'DSC-08', 'DSC-09'])
|
|
@@ -59,6 +99,17 @@ export function buildReport({ subject, anchor, checks, g4 = null, scope, profile
|
|
|
59
99
|
const warnings = selected.filter(item => item.result === 'WARN')
|
|
60
100
|
const pass = selected.filter(item => item.result === 'PASS')
|
|
61
101
|
|
|
102
|
+
// Coverage counts every check the report carries (14 in a full run), not just
|
|
103
|
+
// the mandatory subset. A report that only counts its mandatory five hides
|
|
104
|
+
// the runtime checks it never ran; that is the bug this block fixes.
|
|
105
|
+
const evaluatedAll = effectiveChecks.filter(item => ['PASS', 'WARN', 'FAIL'].includes(item.result))
|
|
106
|
+
const naAll = effectiveChecks.filter(item => item.result === 'NA')
|
|
107
|
+
const errorAll = effectiveChecks.filter(item => item.result === 'ERROR')
|
|
108
|
+
const notRunAll = effectiveChecks.filter(item => item.result !== 'NA' && (item.coverage === 'not-run' || item.coverage === 'partial'))
|
|
109
|
+
const notRunAllIds = [...new Set([...notRunAll.map(item => item.id), ...errorAll.map(item => item.id), ...missing])]
|
|
110
|
+
const entryBlockingIds = scope === 'entry' ? (entryClaim === 'static' ? ENTRY_BLOCKING : ENTRY_RUNTIME_BLOCKING) : null
|
|
111
|
+
const entryBlocking = entryBlockingIds ? summarizeBlocking(effectiveChecks, entryBlockingIds) : null
|
|
112
|
+
|
|
62
113
|
let status
|
|
63
114
|
let exitCode
|
|
64
115
|
if (failed.length > 0) { status = 'fail'; exitCode = 1 }
|
|
@@ -87,6 +138,31 @@ export function buildReport({ subject, anchor, checks, g4 = null, scope, profile
|
|
|
87
138
|
if (warnings.length > 0) reasonParts.push(`WARN: ${warnings.map(item => item.id).join(', ')}`)
|
|
88
139
|
if (advisoryWarnings.length > 0) reasonParts.push(`advisory: ${advisoryWarnings.map(item => item.id).join(', ')}`)
|
|
89
140
|
|
|
141
|
+
// Findings by reader-relevant bucket. A check appears in exactly one bucket:
|
|
142
|
+
// FAIL -> blocking, WARN -> hygiene, NA/ERROR/not-run -> notChecked.
|
|
143
|
+
const blockingBucket = effectiveChecks.filter(item => item.result === 'FAIL').map(item => bucketRow(item))
|
|
144
|
+
const hygieneBucket = effectiveChecks.filter(item => item.result === 'WARN').map(item => bucketRow(item, {
|
|
145
|
+
declaration: DECLARATION_CHECKS.has(item.id),
|
|
146
|
+
advisory: item.advisory === true,
|
|
147
|
+
}))
|
|
148
|
+
const notCheckedBucket = effectiveChecks
|
|
149
|
+
.filter(item => item.result === 'NA' || item.result === 'ERROR')
|
|
150
|
+
.map(item => bucketRow(item))
|
|
151
|
+
for (const id of missing) {
|
|
152
|
+
notCheckedBucket.push({ id, clause: null, result: 'NOT-RUN', coverage: 'not-run', reason: 'mandatory check missing' })
|
|
153
|
+
}
|
|
154
|
+
const buckets = { blocking: blockingBucket, hygiene: hygieneBucket, notChecked: notCheckedBucket }
|
|
155
|
+
const declaredFields = Array.isArray(declarations?.present) ? declarations.present.length : 0
|
|
156
|
+
const canMarkSuspect = anchor?.suspect === true
|
|
157
|
+
|
|
158
|
+
const metrics = {
|
|
159
|
+
checked: evaluatedAll.length,
|
|
160
|
+
total: effectiveChecks.length,
|
|
161
|
+
declaredFields,
|
|
162
|
+
}
|
|
163
|
+
const prereq = entryBlocking ? ` (prereq ${entryBlocking.checked.length}/${entryBlocking.required})` : ''
|
|
164
|
+
const headline = `${scope}/${profile} · runtime ${anchor?.dsh ?? 'unknown'}${anchor?.runtimeSource ? ` ${anchor.runtimeSource}` : ''}${canMarkSuspect ? ' SUSPECT' : ''} · blocking ${buckets.blocking.length}${prereq} · hygiene ${buckets.hygiene.length} · not checked ${buckets.notChecked.length}/${effectiveChecks.length}`
|
|
165
|
+
|
|
90
166
|
const report = {
|
|
91
167
|
schema: 'dsh-conformance/1',
|
|
92
168
|
standard: { id: 'DS-C', version: '1.0.0' },
|
|
@@ -95,21 +171,22 @@ export function buildReport({ subject, anchor, checks, g4 = null, scope, profile
|
|
|
95
171
|
g4,
|
|
96
172
|
checks: effectiveChecks,
|
|
97
173
|
coverage: {
|
|
174
|
+
total: effectiveChecks.length,
|
|
98
175
|
mandatory: mandatory.length,
|
|
99
|
-
|
|
100
|
-
|
|
176
|
+
checked: evaluatedAll.length,
|
|
177
|
+
pass: evaluatedAll.filter(item => item.result === 'PASS').length,
|
|
178
|
+
warn: evaluatedAll.filter(item => item.result === 'WARN').length,
|
|
101
179
|
advisory: advisoryWarnings.length,
|
|
102
|
-
fail:
|
|
103
|
-
na:
|
|
104
|
-
notRun:
|
|
105
|
-
error:
|
|
106
|
-
entryBlocking
|
|
107
|
-
const blocking = entryClaim === 'static' ? ENTRY_BLOCKING : ENTRY_RUNTIME_BLOCKING
|
|
108
|
-
const failedItems = effectiveChecks.flatMap(item => (item.details ?? []).filter(detail => detail.result === 'FAIL' && blocking.includes(detail.item)).map(detail => detail.item))
|
|
109
|
-
return { required: blocking.length, failed: failedItems, pass: failedItems.length === 0 }
|
|
110
|
-
})() : null,
|
|
180
|
+
fail: evaluatedAll.filter(item => item.result === 'FAIL').length,
|
|
181
|
+
na: naAll.length,
|
|
182
|
+
notRun: notRunAllIds.length,
|
|
183
|
+
error: errorAll.length,
|
|
184
|
+
entryBlocking,
|
|
111
185
|
},
|
|
186
|
+
buckets,
|
|
187
|
+
metrics,
|
|
112
188
|
conclusion: {
|
|
189
|
+
headline,
|
|
113
190
|
level,
|
|
114
191
|
status,
|
|
115
192
|
exitCode,
|
|
@@ -132,25 +209,49 @@ export function buildReport({ subject, anchor, checks, g4 = null, scope, profile
|
|
|
132
209
|
|
|
133
210
|
const ICON = { PASS: 'PASS', WARN: 'WARN', FAIL: 'FAIL', NA: 'NA ', ERROR: 'ERR ' }
|
|
134
211
|
|
|
212
|
+
function renderBucket(lines, title, rows, byId) {
|
|
213
|
+
lines.push(`${title} (${rows.length}):`)
|
|
214
|
+
if (rows.length === 0) {
|
|
215
|
+
lines.push(' none')
|
|
216
|
+
return
|
|
217
|
+
}
|
|
218
|
+
for (const row of rows) {
|
|
219
|
+
const tags = [row.declaration ? 'declaration' : null, row.advisory ? 'advisory' : null].filter(Boolean)
|
|
220
|
+
const reason = String(row.reason ?? '').replace(/\s+/g, ' ').slice(0, 150)
|
|
221
|
+
lines.push(` ${(ICON[row.result] ?? row.result).padEnd(4)} ${String(row.id).padEnd(7)} ${String(row.clause ?? '-').padEnd(6)} ${String(row.coverage ?? '-').padEnd(8)} ${reason}${tags.length > 0 ? ` [${tags.join(',')}]` : ''}`)
|
|
222
|
+
const item = byId.get(row.id)
|
|
223
|
+
if (!item || (row.result !== 'FAIL' && row.result !== 'WARN')) continue
|
|
224
|
+
for (const detail of item.details ?? []) {
|
|
225
|
+
if (detail.result === 'PASS') continue
|
|
226
|
+
lines.push(` - ${detail.item}: ${detail.result} ${String(detail.reason ?? '').replace(/\s+/g, ' ').slice(0, 140)}`)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
135
231
|
export function formatText(report) {
|
|
136
232
|
const lines = []
|
|
137
|
-
const { subject, anchor, conclusion, coverage } = report
|
|
233
|
+
const { subject, anchor = {}, conclusion, coverage } = report
|
|
234
|
+
const byId = new Map((report.checks ?? []).map(item => [item.id, item]))
|
|
138
235
|
lines.push(`DS-C check — ${subject.name ?? '<unknown>'}@${subject.version ?? '?'} (${subject.kind}${subject.kindInferred ? ', inferred' : ''})`)
|
|
139
|
-
lines.push(`anchor: repo=${anchor.repo} commit=${anchor.commit.slice(0, 12)} dsh=${anchor.dsh} cordis=${anchor.cordis}`)
|
|
236
|
+
lines.push(`anchor: repo=${anchor.repo ?? '?'} commit=${String(anchor.commit ?? '').slice(0, 12)} dsh=${anchor.dsh ?? '?'} cordis=${anchor.cordis ?? '?'}${anchor.runtimeSource ? ` source=${anchor.runtimeSource}` : ''}${anchor.suspect ? ' suspect=true' : ''}`)
|
|
140
237
|
lines.push(`scope=${conclusion.scope} profile=${conclusion.profile} status=${conclusion.status} exit=${conclusion.exitCode}${conclusion.subset ? ` subset=${conclusion.subset}` : ''}${conclusion.staticOnly ? ' static-only' : ''}`)
|
|
141
238
|
if (conclusion.level) lines.push(`level: ${conclusion.level}`)
|
|
239
|
+
if (conclusion.headline) lines.push(`headline: ${conclusion.headline}`)
|
|
142
240
|
lines.push('')
|
|
143
|
-
lines
|
|
144
|
-
for (const item of report.checks) {
|
|
145
|
-
const reason = (item.reason || '').replace(/\s+/g, ' ').slice(0, 120)
|
|
146
|
-
lines.push(`${item.id.padEnd(7)} ${ICON[item.result] ?? item.result} ${item.clause.padEnd(6)} ${item.layer.padEnd(6)} ${reason}`)
|
|
147
|
-
for (const detail of item.details ?? []) {
|
|
148
|
-
if (detail.result === 'PASS') continue
|
|
149
|
-
lines.push(` - ${detail.item}: ${detail.result} ${String(detail.reason).slice(0, 140)}`)
|
|
150
|
-
}
|
|
151
|
-
}
|
|
241
|
+
renderBucket(lines, 'Blocking (FAIL)', report.buckets?.blocking ?? [], byId)
|
|
152
242
|
lines.push('')
|
|
153
|
-
lines
|
|
243
|
+
renderBucket(lines, 'Hygiene (WARN)', report.buckets?.hygiene ?? [], byId)
|
|
244
|
+
lines.push('')
|
|
245
|
+
renderBucket(lines, 'Not checked (NA / not-run / ERROR)', report.buckets?.notChecked ?? [], byId)
|
|
246
|
+
lines.push('')
|
|
247
|
+
const passed = (report.checks ?? []).filter(item => item.result === 'PASS')
|
|
248
|
+
lines.push(`passed (${passed.length}): ${passed.map(item => item.id).join(', ') || 'none'}`)
|
|
249
|
+
lines.push(`coverage: total=${coverage.total ?? report.checks.length} checked=${coverage.checked ?? coverage.pass + coverage.warn + coverage.fail} pass=${coverage.pass} warn=${coverage.warn} fail=${coverage.fail} na=${coverage.na} notRun=${coverage.notRun} error=${coverage.error} advisory=${coverage.advisory}`)
|
|
250
|
+
if (coverage.entryBlocking) {
|
|
251
|
+
const blocking = coverage.entryBlocking
|
|
252
|
+
lines.push(`entryBlocking: checked=${blocking.checked?.length ?? '?'}/${blocking.required} failed=[${(blocking.failed ?? []).join(', ')}] notChecked=[${(blocking.notChecked ?? []).join(', ')}] pass=${blocking.pass}`)
|
|
253
|
+
}
|
|
254
|
+
lines.push(`metrics: checked=${report.metrics?.checked ?? '?'}/${report.metrics?.total ?? '?'} declaredFields=${report.metrics?.declaredFields ?? 0}/5`)
|
|
154
255
|
if (conclusion.reason) lines.push(`reason: ${conclusion.reason}`)
|
|
155
256
|
if (conclusion.status === 'incomplete') lines.push('NOT A DS-C PASS: at least one mandatory check did not run (see notRun).')
|
|
156
257
|
if (Array.isArray(report.advice) && report.advice.length > 0) {
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
## 1.3.0 — 2026-11-06
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
|
|
5
|
+
- `check [dir]` 子命令:默认 cwd、Markdown 报告(一句结论、≤3 条优先修复、分桶全表、advice、免责声明),`--json` 与 `--html`(单文件、内联 CSS、零依赖)。
|
|
6
|
+
- `spec/schemas/advice.schema.json`(`dsh-advice/1`),advice 逐条过 schema。
|
|
7
|
+
- 证据主体绑定:`--flow-evidence --dir` 校验 name/version/treeSha256/tarball 哈希,不一致即 `invalid`。
|
|
8
|
+
- CI 归属:collector 写 `producer.runUrl/repository/commit`,audit 标 `attestation: self-declared|ci`(不升级 claimLevel)。
|
|
9
|
+
- sidecar 生成(`publish/sidecar.json`,`dsh-market-sidecar/1`)、每日过期/撤回 workflow、离线包(~580 KB)。
|
|
10
|
+
- `SUPPORTED.md`(host × node × checker 矩阵 + 证据锚)与 `CHANGELOG-RULES.md`(规则级变更与影响面)。
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- coverage 按全部 14 项统计,`na/notRun/error` 不再为 0;未查 C-3.1/3.2/3.5 时 `entryBlocking.pass=false`,并输出 `checked/notChecked`。
|
|
15
|
+
- 默认 runtime 锚改为宿主实际版本(`anchor.runtimeSource=host`),与 `--runtime-version` 不一致时 `anchor.suspect=true`;不再固定 `0.2.2-alpha.2`。
|
|
16
|
+
- 报告分三桶:`blocking`/`hygiene`(DSC-11/12/13 声明类)/`notChecked`,text 分组渲染,JSON 加 `buckets/metrics/headline`。
|
|
17
|
+
|
|
18
|
+
### Verified
|
|
19
|
+
|
|
20
|
+
- `npm test` 193/193(含 root `test/`)。
|
|
21
|
+
|
|
1
22
|
## 1.2.2 — 2026-11-06
|
|
2
23
|
|
|
3
24
|
- FLOW 沙箱:`flow collect --sandbox` 在 `unshare -Urn` + `prlimit` 下运行,记录 `sandbox{}`;env 白名单硬断言 token 不在子进程;进程组超时击杀;`producer.kind=sandbox` 仅当隔离项全部生效,否则 `sandbox-degraded` 且 audit 永不 runtime。测试 152 → 159。
|
package/README.en.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
3
|
<picture>
|
|
4
|
-
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-
|
|
5
|
-
<img src="docs/assets/dsh-standards-
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-hero-en-dark.webp">
|
|
5
|
+
<img src="docs/assets/dsh-standards-hero-en-light.webp" width="100%" alt="dsh-standards: DSC-01…13 static checks + L1–L3 runtime, reproducible reports">
|
|
6
6
|
</picture>
|
|
7
7
|
|
|
8
8
|
# dsh-standards
|
|
@@ -76,6 +76,11 @@ The full 51-document index lives in the Chinese [README.md](README.md).
|
|
|
76
76
|
|
|
77
77
|
</details>
|
|
78
78
|
|
|
79
|
+
<picture>
|
|
80
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-flow-v4-dark.svg">
|
|
81
|
+
<img src="docs/assets/dsh-standards-flow-v4-light.svg" width="100%" alt="dsh-standards pipeline: plugin dir → DSC-01…13 → L1–L3 runtime → report → dsh-levels">
|
|
82
|
+
</picture>
|
|
83
|
+
|
|
79
84
|
## Three-step setup
|
|
80
85
|
|
|
81
86
|
**1. Install** (either way; Node `>=24.9.0`):
|
package/README.es.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
3
|
<picture>
|
|
4
|
-
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-
|
|
5
|
-
<img src="docs/assets/dsh-standards-
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-hero-en-dark.webp">
|
|
5
|
+
<img src="docs/assets/dsh-standards-hero-en-light.webp" width="100%" alt="dsh-standards: DSC-01…13 static checks + L1–L3 runtime, reproducible reports">
|
|
6
6
|
</picture>
|
|
7
7
|
|
|
8
8
|
# dsh-standards
|
|
@@ -76,6 +76,11 @@ El índice completo de 51 documentos está en el [README.md](README.md) en chino
|
|
|
76
76
|
|
|
77
77
|
</details>
|
|
78
78
|
|
|
79
|
+
<picture>
|
|
80
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-flow-v4-dark.svg">
|
|
81
|
+
<img src="docs/assets/dsh-standards-flow-v4-light.svg" width="100%" alt="dsh-standards pipeline: plugin dir → DSC-01…13 → L1–L3 runtime → report → dsh-levels">
|
|
82
|
+
</picture>
|
|
83
|
+
|
|
79
84
|
## Instalación en tres pasos
|
|
80
85
|
|
|
81
86
|
**1. Instalar** (dos opciones; Node `>=24.9.0`):
|
package/README.hi.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
3
|
<picture>
|
|
4
|
-
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-
|
|
5
|
-
<img src="docs/assets/dsh-standards-
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-hero-en-dark.webp">
|
|
5
|
+
<img src="docs/assets/dsh-standards-hero-en-light.webp" width="100%" alt="dsh-standards: DSC-01…13 static checks + L1–L3 runtime, reproducible reports">
|
|
6
6
|
</picture>
|
|
7
7
|
|
|
8
8
|
# dsh-standards
|
|
@@ -76,6 +76,11 @@ npm पर पुराना आरक्षण `dsh-standards@0.0.3` केव
|
|
|
76
76
|
|
|
77
77
|
</details>
|
|
78
78
|
|
|
79
|
+
<picture>
|
|
80
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-flow-v4-dark.svg">
|
|
81
|
+
<img src="docs/assets/dsh-standards-flow-v4-light.svg" width="100%" alt="dsh-standards pipeline: plugin dir → DSC-01…13 → L1–L3 runtime → report → dsh-levels">
|
|
82
|
+
</picture>
|
|
83
|
+
|
|
79
84
|
## तीन चरणों में शुरुआत
|
|
80
85
|
|
|
81
86
|
**1. इंस्टॉल** (दो में से कोई भी; Node `>=24.9.0`):
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
3
|
<picture>
|
|
4
|
-
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-
|
|
5
|
-
<img src="docs/assets/dsh-standards-
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-hero-zh-dark.webp">
|
|
5
|
+
<img src="docs/assets/dsh-standards-hero-zh-light.webp" width="100%" alt="dsh-standards:DSC-01…13 静态检查 + L1–L3 运行时,报告可复现">
|
|
6
6
|
</picture>
|
|
7
7
|
|
|
8
8
|
# dsh-standards
|
|
@@ -94,6 +94,11 @@ npm 上的旧占位 `dsh-standards@0.0.3` 只是名字预留,这条 1.x 线取
|
|
|
94
94
|
|
|
95
95
|
</details>
|
|
96
96
|
|
|
97
|
+
<picture>
|
|
98
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-flow-v4-dark.svg">
|
|
99
|
+
<img src="docs/assets/dsh-standards-flow-v4-light.svg" width="100%" alt="dsh-standards 流程:插件目录 → DSC-01…13 静态 → L1–L3 运行时 → 报告 → dsh-levels">
|
|
100
|
+
</picture>
|
|
101
|
+
|
|
97
102
|
## 三步上手
|
|
98
103
|
|
|
99
104
|
**1. 安装**(二选一,Node 需 `>=24.9.0`):
|
package/README.pt.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
3
|
<picture>
|
|
4
|
-
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-
|
|
5
|
-
<img src="docs/assets/dsh-standards-
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-hero-en-dark.webp">
|
|
5
|
+
<img src="docs/assets/dsh-standards-hero-en-light.webp" width="100%" alt="dsh-standards: DSC-01…13 static checks + L1–L3 runtime, reproducible reports">
|
|
6
6
|
</picture>
|
|
7
7
|
|
|
8
8
|
# dsh-standards
|
|
@@ -76,6 +76,11 @@ O índice completo de 51 documentos está no [README.md](README.md) em chinês.
|
|
|
76
76
|
|
|
77
77
|
</details>
|
|
78
78
|
|
|
79
|
+
<picture>
|
|
80
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-flow-v4-dark.svg">
|
|
81
|
+
<img src="docs/assets/dsh-standards-flow-v4-light.svg" width="100%" alt="dsh-standards pipeline: plugin dir → DSC-01…13 → L1–L3 runtime → report → dsh-levels">
|
|
82
|
+
</picture>
|
|
83
|
+
|
|
79
84
|
## Instalação em três passos
|
|
80
85
|
|
|
81
86
|
**1. Instalar** (duas opções; Node `>=24.9.0`):
|
package/bin/dsh-standards.mjs
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
// Public umbrella entry point for the DSH standards checker.
|
|
3
3
|
// The implementation lives in 40-checker/; this shim keeps the package-level
|
|
4
4
|
// command stable while the checker remains independently runnable from source.
|
|
5
|
+
//
|
|
6
|
+
// dsh-standards check [dir] author-facing Markdown / --json / --html report
|
|
7
|
+
// dsh-standards <dir> raw checker report (backwards compatible)
|
|
5
8
|
import { readFileSync } from 'node:fs'
|
|
6
9
|
import { fileURLToPath } from 'node:url'
|
|
7
10
|
import { dirname, join } from 'node:path'
|
|
@@ -15,5 +18,13 @@ if (argv.includes('--version') || argv.includes('-v')) {
|
|
|
15
18
|
process.exit(0)
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
await import('../40-checker/
|
|
21
|
+
if (argv[0] === 'check') {
|
|
22
|
+
const { runCheck } = await import('../40-checker/src/author-cli.mjs')
|
|
23
|
+
const code = await runCheck(argv.slice(1))
|
|
24
|
+
process.exitCode = code
|
|
25
|
+
// The checker may have imported a plugin; do not let stray handles hang us.
|
|
26
|
+
setTimeout(() => process.exit(code), 250)
|
|
27
|
+
} else {
|
|
28
|
+
process.env.DSH_CHECKER_BRAND = `dsh-standards ${pkg.version} (checker 0.1.0)`
|
|
29
|
+
await import('../40-checker/bin/dsh-plugin-verify.mjs')
|
|
30
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1100" height="660" viewBox="0 0 1100 660" role="img" aria-label="dsh-standards pipeline">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="lineGrad" x1="0" y1="0" x2="1" y2="0">
|
|
4
|
+
<stop offset="0" stop-color="#8496f5" stop-opacity=".15"/>
|
|
5
|
+
<stop offset=".5" stop-color="#8496f5" stop-opacity=".9"/>
|
|
6
|
+
<stop offset="1" stop-color="#8496f5" stop-opacity=".15"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
<filter id="soft" x="-40%" y="-40%" width="180%" height="200%">
|
|
9
|
+
<feDropShadow dx="0" dy="9" stdDeviation="12" flood-color="#1b1735" flood-opacity="0.55"/>
|
|
10
|
+
</filter>
|
|
11
|
+
<pattern id="dots" width="28" height="28" patternUnits="userSpaceOnUse">
|
|
12
|
+
<circle cx="1.3" cy="1.3" r="1.3" fill="#182031"/>
|
|
13
|
+
</pattern>
|
|
14
|
+
</defs>
|
|
15
|
+
<rect width="1100" height="660" fill="#0d1117"/>
|
|
16
|
+
<rect width="1100" height="660" fill="url(#dots)" opacity=".5"/>
|
|
17
|
+
<text x="50" y="62" fill="#f0f3f9" font-family="ui-monospace, SFMono-Regular, Menlo, monospace" font-size="32" font-weight="700">dsh-standards</text>
|
|
18
|
+
<text x="50" y="90" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="16">static checks, runtime measurement, one reproducible report</text>
|
|
19
|
+
<rect x="50" y="108" width="1000" height="1.6" rx="1" fill="#2a3142"/>
|
|
20
|
+
<g filter="url(#soft)">
|
|
21
|
+
<rect x="50" y="190" width="176" height="118" rx="17" fill="#161b26" stroke="#2a3142" stroke-width="1.3"/>
|
|
22
|
+
</g>
|
|
23
|
+
<circle cx="74" cy="214" r="12" fill="#242d42"/>
|
|
24
|
+
<text x="74" y="219" text-anchor="middle" fill="#8496f5" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">01</text>
|
|
25
|
+
<text x="138.0" y="256" text-anchor="middle" fill="#f0f3f9" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">plugin dir</text>
|
|
26
|
+
<text x="138.0" y="282" text-anchor="middle" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">the repository</text>
|
|
27
|
+
<path d="M231 249.0 C241.0 249.0, 241.0 249.0, 251 249.0" stroke="url(#lineGrad)" stroke-width="2.2" fill="none"/>
|
|
28
|
+
<path d="M241 244.0 l8 5 -8 5" fill="none" stroke="#8496f5" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
29
|
+
<g filter="url(#soft)">
|
|
30
|
+
<rect x="256" y="190" width="176" height="118" rx="17" fill="#161b26" stroke="#8496f5" stroke-width="2.6"/>
|
|
31
|
+
</g>
|
|
32
|
+
<circle cx="280" cy="214" r="12" fill="#242d42"/>
|
|
33
|
+
<text x="280" y="219" text-anchor="middle" fill="#8496f5" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">02</text>
|
|
34
|
+
<text x="344.0" y="256" text-anchor="middle" fill="#f0f3f9" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">DSC-01…13</text>
|
|
35
|
+
<text x="344.0" y="282" text-anchor="middle" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">static checks</text>
|
|
36
|
+
<path d="M437 249.0 C447.0 249.0, 447.0 249.0, 457 249.0" stroke="url(#lineGrad)" stroke-width="2.2" fill="none"/>
|
|
37
|
+
<path d="M447 244.0 l8 5 -8 5" fill="none" stroke="#8496f5" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
38
|
+
<g filter="url(#soft)">
|
|
39
|
+
<rect x="462" y="190" width="176" height="118" rx="17" fill="#161b26" stroke="#2a3142" stroke-width="1.3"/>
|
|
40
|
+
</g>
|
|
41
|
+
<circle cx="486" cy="214" r="12" fill="#242d42"/>
|
|
42
|
+
<text x="486" y="219" text-anchor="middle" fill="#8496f5" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">03</text>
|
|
43
|
+
<text x="550.0" y="256" text-anchor="middle" fill="#f0f3f9" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">L1–L3</text>
|
|
44
|
+
<text x="550.0" y="282" text-anchor="middle" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">runtime measurement</text>
|
|
45
|
+
<path d="M643 249.0 C653.0 249.0, 653.0 249.0, 663 249.0" stroke="url(#lineGrad)" stroke-width="2.2" fill="none"/>
|
|
46
|
+
<path d="M653 244.0 l8 5 -8 5" fill="none" stroke="#8496f5" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
47
|
+
<g filter="url(#soft)">
|
|
48
|
+
<rect x="668" y="190" width="176" height="118" rx="17" fill="#161b26" stroke="#2a3142" stroke-width="1.3"/>
|
|
49
|
+
</g>
|
|
50
|
+
<circle cx="692" cy="214" r="12" fill="#242d42"/>
|
|
51
|
+
<text x="692" y="219" text-anchor="middle" fill="#8496f5" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">04</text>
|
|
52
|
+
<text x="756.0" y="256" text-anchor="middle" fill="#f0f3f9" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">report</text>
|
|
53
|
+
<text x="756.0" y="282" text-anchor="middle" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">text / json / sarif</text>
|
|
54
|
+
<path d="M849 249.0 C859.0 249.0, 859.0 249.0, 869 249.0" stroke="url(#lineGrad)" stroke-width="2.2" fill="none"/>
|
|
55
|
+
<path d="M859 244.0 l8 5 -8 5" fill="none" stroke="#8496f5" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
56
|
+
<g filter="url(#soft)">
|
|
57
|
+
<rect x="874" y="190" width="176" height="118" rx="17" fill="#161b26" stroke="#3fb950" stroke-width="2.6"/>
|
|
58
|
+
</g>
|
|
59
|
+
<circle cx="898" cy="214" r="12" fill="#242d42"/>
|
|
60
|
+
<text x="898" y="219" text-anchor="middle" fill="#8496f5" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">05</text>
|
|
61
|
+
<text x="962.0" y="256" text-anchor="middle" fill="#f0f3f9" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">dsh-levels</text>
|
|
62
|
+
<text x="962.0" y="282" text-anchor="middle" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">ENTRY/FLOW/BOOST</text>
|
|
63
|
+
<g filter="url(#soft)">
|
|
64
|
+
<rect x="462" y="378" width="118" height="58" rx="14" fill="#161b26" stroke="#3fb950" stroke-width="1.2"/>
|
|
65
|
+
</g>
|
|
66
|
+
<text x="486" y="404" fill="#3fb950" font-family="ui-monospace, monospace" font-size="16" font-weight="700">0</text>
|
|
67
|
+
<text x="486" y="423" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">exit pass</text>
|
|
68
|
+
<g filter="url(#soft)">
|
|
69
|
+
<rect x="592" y="378" width="118" height="58" rx="14" fill="#161b26" stroke="#d29922" stroke-width="1.2"/>
|
|
70
|
+
</g>
|
|
71
|
+
<text x="616" y="404" fill="#d29922" font-family="ui-monospace, monospace" font-size="16" font-weight="700">1</text>
|
|
72
|
+
<text x="616" y="423" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">exit fail</text>
|
|
73
|
+
<g filter="url(#soft)">
|
|
74
|
+
<rect x="722" y="378" width="118" height="58" rx="14" fill="#161b26" stroke="#d29922" stroke-width="1.2"/>
|
|
75
|
+
</g>
|
|
76
|
+
<text x="746" y="404" fill="#d29922" font-family="ui-monospace, monospace" font-size="16" font-weight="700">2</text>
|
|
77
|
+
<text x="746" y="423" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">exit config</text>
|
|
78
|
+
<g filter="url(#soft)">
|
|
79
|
+
<rect x="852" y="378" width="118" height="58" rx="14" fill="#161b26" stroke="#d29922" stroke-width="1.2"/>
|
|
80
|
+
</g>
|
|
81
|
+
<text x="876" y="404" fill="#d29922" font-family="ui-monospace, monospace" font-size="16" font-weight="700">3</text>
|
|
82
|
+
<text x="876" y="423" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">exit runtime</text>
|
|
83
|
+
<rect x="50" y="476" width="1000" height="58" rx="15" fill="#161b26" stroke="#2a3142"/>
|
|
84
|
+
<text x="74" y="502" fill="#8496f5" font-family="ui-monospace, monospace" font-size="13" font-weight="700">static ≠ runtime</text>
|
|
85
|
+
<text x="74" y="522" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="13">a static pass never substitutes for a runtime run; not-run is reported, never upgraded to PASS</text>
|
|
86
|
+
<text x="50" y="596" fill="#a8b1c4" font-family="ui-monospace, monospace" font-size="13">Apache-2.0 + CC-BY-4.0 · PerryLink DSH plugin family</text>
|
|
87
|
+
</svg>
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1100" height="660" viewBox="0 0 1100 660" role="img" aria-label="dsh-standards pipeline">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="lineGrad" x1="0" y1="0" x2="1" y2="0">
|
|
4
|
+
<stop offset="0" stop-color="#4B67B4" stop-opacity=".15"/>
|
|
5
|
+
<stop offset=".5" stop-color="#4B67B4" stop-opacity=".9"/>
|
|
6
|
+
<stop offset="1" stop-color="#4B67B4" stop-opacity=".15"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
<filter id="soft" x="-40%" y="-40%" width="180%" height="200%">
|
|
9
|
+
<feDropShadow dx="0" dy="9" stdDeviation="12" flood-color="#1b1735" flood-opacity="0.10"/>
|
|
10
|
+
</filter>
|
|
11
|
+
<pattern id="dots" width="28" height="28" patternUnits="userSpaceOnUse">
|
|
12
|
+
<circle cx="1.3" cy="1.3" r="1.3" fill="#f2f0f8"/>
|
|
13
|
+
</pattern>
|
|
14
|
+
</defs>
|
|
15
|
+
<rect width="1100" height="660" fill="#ffffff"/>
|
|
16
|
+
<rect width="1100" height="660" fill="url(#dots)" opacity=".5"/>
|
|
17
|
+
<text x="50" y="62" fill="#141419" font-family="ui-monospace, SFMono-Regular, Menlo, monospace" font-size="32" font-weight="700">dsh-standards</text>
|
|
18
|
+
<text x="50" y="90" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="16">static checks, runtime measurement, one reproducible report</text>
|
|
19
|
+
<rect x="50" y="108" width="1000" height="1.6" rx="1" fill="#e6e3f0"/>
|
|
20
|
+
<g filter="url(#soft)">
|
|
21
|
+
<rect x="50" y="190" width="176" height="118" rx="17" fill="#fcfcfe" stroke="#e6e3f0" stroke-width="1.3"/>
|
|
22
|
+
</g>
|
|
23
|
+
<circle cx="74" cy="214" r="12" fill="#ece7f9"/>
|
|
24
|
+
<text x="74" y="219" text-anchor="middle" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">01</text>
|
|
25
|
+
<text x="138.0" y="256" text-anchor="middle" fill="#141419" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">plugin dir</text>
|
|
26
|
+
<text x="138.0" y="282" text-anchor="middle" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">the repository</text>
|
|
27
|
+
<path d="M231 249.0 C241.0 249.0, 241.0 249.0, 251 249.0" stroke="url(#lineGrad)" stroke-width="2.2" fill="none"/>
|
|
28
|
+
<path d="M241 244.0 l8 5 -8 5" fill="none" stroke="#4B67B4" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
29
|
+
<g filter="url(#soft)">
|
|
30
|
+
<rect x="256" y="190" width="176" height="118" rx="17" fill="#fcfcfe" stroke="#4B67B4" stroke-width="2.6"/>
|
|
31
|
+
</g>
|
|
32
|
+
<circle cx="280" cy="214" r="12" fill="#ece7f9"/>
|
|
33
|
+
<text x="280" y="219" text-anchor="middle" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">02</text>
|
|
34
|
+
<text x="344.0" y="256" text-anchor="middle" fill="#141419" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">DSC-01…13</text>
|
|
35
|
+
<text x="344.0" y="282" text-anchor="middle" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">static checks</text>
|
|
36
|
+
<path d="M437 249.0 C447.0 249.0, 447.0 249.0, 457 249.0" stroke="url(#lineGrad)" stroke-width="2.2" fill="none"/>
|
|
37
|
+
<path d="M447 244.0 l8 5 -8 5" fill="none" stroke="#4B67B4" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
38
|
+
<g filter="url(#soft)">
|
|
39
|
+
<rect x="462" y="190" width="176" height="118" rx="17" fill="#fcfcfe" stroke="#e6e3f0" stroke-width="1.3"/>
|
|
40
|
+
</g>
|
|
41
|
+
<circle cx="486" cy="214" r="12" fill="#ece7f9"/>
|
|
42
|
+
<text x="486" y="219" text-anchor="middle" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">03</text>
|
|
43
|
+
<text x="550.0" y="256" text-anchor="middle" fill="#141419" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">L1–L3</text>
|
|
44
|
+
<text x="550.0" y="282" text-anchor="middle" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">runtime measurement</text>
|
|
45
|
+
<path d="M643 249.0 C653.0 249.0, 653.0 249.0, 663 249.0" stroke="url(#lineGrad)" stroke-width="2.2" fill="none"/>
|
|
46
|
+
<path d="M653 244.0 l8 5 -8 5" fill="none" stroke="#4B67B4" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
47
|
+
<g filter="url(#soft)">
|
|
48
|
+
<rect x="668" y="190" width="176" height="118" rx="17" fill="#fcfcfe" stroke="#e6e3f0" stroke-width="1.3"/>
|
|
49
|
+
</g>
|
|
50
|
+
<circle cx="692" cy="214" r="12" fill="#ece7f9"/>
|
|
51
|
+
<text x="692" y="219" text-anchor="middle" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">04</text>
|
|
52
|
+
<text x="756.0" y="256" text-anchor="middle" fill="#141419" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">report</text>
|
|
53
|
+
<text x="756.0" y="282" text-anchor="middle" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">text / json / sarif</text>
|
|
54
|
+
<path d="M849 249.0 C859.0 249.0, 859.0 249.0, 869 249.0" stroke="url(#lineGrad)" stroke-width="2.2" fill="none"/>
|
|
55
|
+
<path d="M859 244.0 l8 5 -8 5" fill="none" stroke="#4B67B4" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
56
|
+
<g filter="url(#soft)">
|
|
57
|
+
<rect x="874" y="190" width="176" height="118" rx="17" fill="#fcfcfe" stroke="#16a34a" stroke-width="2.6"/>
|
|
58
|
+
</g>
|
|
59
|
+
<circle cx="898" cy="214" r="12" fill="#ece7f9"/>
|
|
60
|
+
<text x="898" y="219" text-anchor="middle" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">05</text>
|
|
61
|
+
<text x="962.0" y="256" text-anchor="middle" fill="#141419" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">dsh-levels</text>
|
|
62
|
+
<text x="962.0" y="282" text-anchor="middle" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">ENTRY/FLOW/BOOST</text>
|
|
63
|
+
<g filter="url(#soft)">
|
|
64
|
+
<rect x="462" y="378" width="118" height="58" rx="14" fill="#fcfcfe" stroke="#16a34a" stroke-width="1.2"/>
|
|
65
|
+
</g>
|
|
66
|
+
<text x="486" y="404" fill="#16a34a" font-family="ui-monospace, monospace" font-size="16" font-weight="700">0</text>
|
|
67
|
+
<text x="486" y="423" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">exit pass</text>
|
|
68
|
+
<g filter="url(#soft)">
|
|
69
|
+
<rect x="592" y="378" width="118" height="58" rx="14" fill="#fcfcfe" stroke="#bc4c00" stroke-width="1.2"/>
|
|
70
|
+
</g>
|
|
71
|
+
<text x="616" y="404" fill="#bc4c00" font-family="ui-monospace, monospace" font-size="16" font-weight="700">1</text>
|
|
72
|
+
<text x="616" y="423" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">exit fail</text>
|
|
73
|
+
<g filter="url(#soft)">
|
|
74
|
+
<rect x="722" y="378" width="118" height="58" rx="14" fill="#fcfcfe" stroke="#bc4c00" stroke-width="1.2"/>
|
|
75
|
+
</g>
|
|
76
|
+
<text x="746" y="404" fill="#bc4c00" font-family="ui-monospace, monospace" font-size="16" font-weight="700">2</text>
|
|
77
|
+
<text x="746" y="423" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">exit config</text>
|
|
78
|
+
<g filter="url(#soft)">
|
|
79
|
+
<rect x="852" y="378" width="118" height="58" rx="14" fill="#fcfcfe" stroke="#bc4c00" stroke-width="1.2"/>
|
|
80
|
+
</g>
|
|
81
|
+
<text x="876" y="404" fill="#bc4c00" font-family="ui-monospace, monospace" font-size="16" font-weight="700">3</text>
|
|
82
|
+
<text x="876" y="423" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">exit runtime</text>
|
|
83
|
+
<rect x="50" y="476" width="1000" height="58" rx="15" fill="#fcfcfe" stroke="#e6e3f0"/>
|
|
84
|
+
<text x="74" y="502" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="13" font-weight="700">static ≠ runtime</text>
|
|
85
|
+
<text x="74" y="522" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="13">a static pass never substitutes for a runtime run; not-run is reported, never upgraded to PASS</text>
|
|
86
|
+
<text x="50" y="596" fill="#4d5261" font-family="ui-monospace, monospace" font-size="13">Apache-2.0 + CC-BY-4.0 · PerryLink DSH plugin family</text>
|
|
87
|
+
</svg>
|