software-defence-factory 0.9.0 → 0.9.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/docs/npm.md +37 -0
- package/docs/recovery.md +10 -0
- package/factory/delivery.mjs +5 -6
- package/factory/execution-profile.mjs +11 -0
- package/package.json +1 -1
package/docs/npm.md
CHANGED
|
@@ -71,6 +71,43 @@ rebuilding the shared tag cannot remove an existing installation's pinned image.
|
|
|
71
71
|
These retained images are recovery data; remove them only after confirming no
|
|
72
72
|
installation or retained attempt needs them.
|
|
73
73
|
|
|
74
|
+
## Protected evidence compatibility
|
|
75
|
+
|
|
76
|
+
0.9.1 recognizes version-1 execution profiles emitted by native **0.8.0,
|
|
77
|
+
0.9.0 and 0.9.1**. This is an exact allowlist in
|
|
78
|
+
`factory/execution-profile.mjs`, independent of the installed package version;
|
|
79
|
+
it is not a semver range or an automatic promise for later releases. Unknown
|
|
80
|
+
runtime strings, unknown profile formats and incomplete legacy acceptance
|
|
81
|
+
remain unverified or blocked. New profiles still record the actual emitting
|
|
82
|
+
runtime version. Updates never relabel profiles, rewrite policy hashes,
|
|
83
|
+
re-approve jobs or retry old work.
|
|
84
|
+
|
|
85
|
+
Compatibility only permits interpreting the protected evidence. Publication
|
|
86
|
+
still requires exact protected-file/run-record equality, successful linked
|
|
87
|
+
phases with supported role/model provenance, the unchanged effective policy
|
|
88
|
+
hash, retained source and candidate/patch identity, passing checks, independent
|
|
89
|
+
review and bound approval. Synthetic or missing provenance cannot authorize
|
|
90
|
+
publication. Enabled browser verification still requires current tool, story,
|
|
91
|
+
candidate, attempt and retained artifact evidence; enabling or changing that
|
|
92
|
+
policy invalidates prior evidence. CLI, API and dashboard consume the same
|
|
93
|
+
delivery capability.
|
|
94
|
+
|
|
95
|
+
Before extending this list, inspect the released profile/evidence writers and
|
|
96
|
+
exercise both capability derivation and publication validation. Remove support
|
|
97
|
+
for an older writer whenever a trust-relevant format, isolation, provenance or
|
|
98
|
+
validation change makes its guarantees insufficient for current policy; bump
|
|
99
|
+
the evidence format when its meaning changes. A package version bump alone is
|
|
100
|
+
neither a reason to discard evidence nor proof of compatibility. Retain
|
|
101
|
+
unsupported records unchanged and obtain fresh applicable evidence through the
|
|
102
|
+
normal workflow; never repair them by editing private records or hashes.
|
|
103
|
+
|
|
104
|
+
After native Verify/Review, the lead must qualify the installed patch read-only
|
|
105
|
+
against actual retained production evidence. This source change and its
|
|
106
|
+
controlled fixtures do not supply that proof. An already delivered PR keeps
|
|
107
|
+
read-only receipt reconciliation. New publication still requires the current
|
|
108
|
+
remote target to equal the original accepted base; checkpoint continuation and
|
|
109
|
+
target mismatch remain the separate #42 limit.
|
|
110
|
+
|
|
74
111
|
## Release flow
|
|
75
112
|
|
|
76
113
|
`.github/workflows/ci.yml` tests pull requests and pushes on Node 22 and 24,
|
package/docs/recovery.md
CHANGED
|
@@ -126,6 +126,16 @@ proof of the model/provider that served inference. Changing the installation's
|
|
|
126
126
|
profile affects subsequent attempts, never the recorded history. A changed
|
|
127
127
|
check/review policy still invalidates acceptance of earlier proof.
|
|
128
128
|
|
|
129
|
+
Protected delivery evidence uses an explicit [runtime compatibility
|
|
130
|
+
contract](npm.md#protected-evidence-compatibility): version-1 profiles from
|
|
131
|
+
0.8.0, 0.9.0 and 0.9.1 can be interpreted by 0.9.1 while every existing trust
|
|
132
|
+
and current-policy guard still applies. An upgrade alone does not require
|
|
133
|
+
re-approval. Preserve original runtime versions, hashes and phase records;
|
|
134
|
+
do not normalize old configuration or manufacture provenance to unblock a job.
|
|
135
|
+
Unknown formats/versions remain blocked. Published PR receipt reconciliation
|
|
136
|
+
stays read-only, and compatibility does not relax the original-base/remote-target
|
|
137
|
+
guard or add checkpoint continuation.
|
|
138
|
+
|
|
129
139
|
Attempts made before this metadata existed display **Not recorded
|
|
130
140
|
(legacy/unknown)**. Do not copy today's profile onto them. For an investigation,
|
|
131
141
|
compare the retained per-attempt measurement, logs, candidate/check/review hashes
|
package/factory/delivery.mjs
CHANGED
|
@@ -3,9 +3,8 @@ import { existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, rmSync, wr
|
|
|
3
3
|
import { join, resolve } from 'node:path';
|
|
4
4
|
import { spawnSync } from 'node:child_process';
|
|
5
5
|
import { isDeepStrictEqual } from 'node:util';
|
|
6
|
-
import { VERSION } from './updates.mjs';
|
|
7
6
|
import { configAt, digest } from './lib.mjs';
|
|
8
|
-
import { effectiveExecutionConfig } from './execution-profile.mjs';
|
|
7
|
+
import { effectiveExecutionConfig, isSupportedExecutionProfile } from './execution-profile.mjs';
|
|
9
8
|
import { publicSourceAdmission } from './source-admission.mjs';
|
|
10
9
|
import { QueueError } from './queue.mjs';
|
|
11
10
|
import { readProjectLinks } from './project-links.mjs';
|
|
@@ -221,8 +220,8 @@ function trustedExecutionProfile(state, job, run, phase, expectedPolicy) {
|
|
|
221
220
|
&& ['explicit', 'provider_default'].includes(profile.modelSelection)
|
|
222
221
|
&& (profile.modelSelection === 'explicit' ? typeof profile.requestedModel === 'string' : profile.requestedModel === null)
|
|
223
222
|
: profile.executor === 'deterministic' && profile.modelSelection === 'not_applicable' && profile.requestedModel === null;
|
|
224
|
-
return profile
|
|
225
|
-
&& profile.
|
|
223
|
+
return isSupportedExecutionProfile(profile) && profile.phase === phase && executorMatches
|
|
224
|
+
&& profile.policyHash === expectedPolicy
|
|
226
225
|
&& isDeepStrictEqual(run.execution, profile);
|
|
227
226
|
} catch { return false; }
|
|
228
227
|
}
|
|
@@ -284,7 +283,7 @@ function acceptanceSummary(job, state, config, sourceAdmission) {
|
|
|
284
283
|
{ build: buildRun, verify: checkRun, review: reviewRun, handoff: handoffRun }, candidate, checks, review);
|
|
285
284
|
if (!phaseEvidence.trusted) return { state: 'blocked', reason: phaseEvidence.reason };
|
|
286
285
|
const validRun = (run, command) => run?.command === command && run.state === 'succeeded' && run.outcome === 'complete'
|
|
287
|
-
&& run.execution
|
|
286
|
+
&& isSupportedExecutionProfile(run.execution) && run.execution.policyHash === expectedPolicy;
|
|
288
287
|
const bound = handoff.id === accepted.handoff_run_id && validRun(buildRun, 'build')
|
|
289
288
|
&& validRun(checkRun, 'verify') && validRun(reviewRun, 'review') && validRun(handoff, 'handoff')
|
|
290
289
|
&& job.source_admission?.status === 'retained' && accepted.head !== accepted.base && candidate.parent === accepted.base
|
|
@@ -508,7 +507,7 @@ export class DeliveryService {
|
|
|
508
507
|
const byID = id => job.runs.find(run => run.id === id);
|
|
509
508
|
const buildRun = byID(accepted.build_run_id), checkRun = byID(accepted.checks_run_id), reviewRun = byID(accepted.review_run_id);
|
|
510
509
|
const validRun = (run, command) => run?.command === command && run.state === 'succeeded' && run.outcome === 'complete'
|
|
511
|
-
&& run.execution
|
|
510
|
+
&& isSupportedExecutionProfile(run.execution) && run.execution.policyHash === expectedPolicy;
|
|
512
511
|
if (!validRun(buildRun, 'build') || !validRun(checkRun, 'verify') || !validRun(reviewRun, 'review') || !validRun(handoff, 'handoff'))
|
|
513
512
|
throw new QueueError('Build, checks, review and approval do not match the current Factory policy.');
|
|
514
513
|
const phaseEvidence = trustedPhaseEvidence(this.state, job, expectedPolicy,
|
|
@@ -6,6 +6,17 @@ import { usageFields } from './usage.mjs';
|
|
|
6
6
|
import { effectiveInferenceProvider } from './model-environment.mjs';
|
|
7
7
|
import { expectedWebStories, webPolicyHash } from './web-verification.mjs';
|
|
8
8
|
|
|
9
|
+
// Audited v1 protected evidence writers: 0.8.0 (380f749), 0.9.0 (cdaadef),
|
|
10
|
+
// and this patch. Deliberately independent of VERSION: a release bump is not
|
|
11
|
+
// evidence compatibility. Re-audit this list for every trust-relevant writer,
|
|
12
|
+
// isolation or validation change; remove versions whose guarantees no longer
|
|
13
|
+
// satisfy current policy. See docs/npm.md. This predicate alone grants no trust.
|
|
14
|
+
const SUPPORTED_EXECUTION_RUNTIMES_V1 = new Set(['0.8.0', '0.9.0', '0.9.1']);
|
|
15
|
+
|
|
16
|
+
export function isSupportedExecutionProfile(profile) {
|
|
17
|
+
return profile?.version === 1 && SUPPORTED_EXECUTION_RUNTIMES_V1.has(profile.runtimeVersion);
|
|
18
|
+
}
|
|
19
|
+
|
|
9
20
|
export function withRequestedModel(configuration, requestedModel) {
|
|
10
21
|
const config = structuredClone(configuration);
|
|
11
22
|
if (requestedModel && requestedModel !== config.model) {
|