software-defence-factory 0.15.0 → 0.15.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 +31 -4
- package/factory/execution-profile.mjs +5 -4
- package/factory/updates.mjs +21 -5
- package/package.json +1 -1
package/docs/npm.md
CHANGED
|
@@ -94,6 +94,18 @@ jobs and history are preserved. Existing processes keep their original code.
|
|
|
94
94
|
Set `SDF_AUTO_UPDATE=0` to skip automatic network checks for a command.
|
|
95
95
|
Source checkouts remain managed by Git and do not update themselves.
|
|
96
96
|
|
|
97
|
+
The exact selected release is downloaded with npm's `--prefer-online` metadata
|
|
98
|
+
refresh and `--prefer-offline=false`; this does not change account settings.
|
|
99
|
+
An `ETARGET` visibility failure
|
|
100
|
+
gets at most three attempts, separated by one second, within the same total
|
|
101
|
+
120-second download budget (including waits). Offline, authentication and package
|
|
102
|
+
identity errors are not retried by Factory. A successful download must match the
|
|
103
|
+
requested package name, version and CLI entry before immutable adoption. Exhaustion
|
|
104
|
+
cleans only that download's staging directory and retains the selected runtime,
|
|
105
|
+
configuration and job history. Managed updates release their idle reservations on
|
|
106
|
+
download failure; normal reservation/recovery rules in [services](services.md)
|
|
107
|
+
still apply. The retries add no extra download maintenance budget.
|
|
108
|
+
|
|
97
109
|
CLI updates do not rebuild or update Docker images automatically. Stop the
|
|
98
110
|
installation and run `install --state PATH` when intentionally adopting a new
|
|
99
111
|
runtime image; repeat the relevant qualification before resuming jobs. Install retains the old and new
|
|
@@ -104,8 +116,8 @@ installation or retained attempt needs them.
|
|
|
104
116
|
|
|
105
117
|
## Protected evidence compatibility
|
|
106
118
|
|
|
107
|
-
Factory 0.15.
|
|
108
|
-
**0.8.0, 0.9.0, 0.9.1, 0.10.0, 0.11.0, 0.11.1, 0.11.2, 0.12.0, 0.13.0, 0.13.1, 0.14.0 and 0.15.
|
|
119
|
+
Factory 0.15.1 recognizes version-1 execution profiles emitted by native
|
|
120
|
+
**0.8.0, 0.9.0, 0.9.1, 0.10.0, 0.11.0, 0.11.1, 0.11.2, 0.12.0, 0.13.0, 0.13.1, 0.14.0, 0.15.0 and 0.15.1**. This is an exact allowlist in
|
|
109
121
|
`factory/execution-profile.mjs`, independent of the installed package version;
|
|
110
122
|
it is not a semver range or an automatic promise for later releases. Unknown
|
|
111
123
|
runtime strings, unknown profile formats and incomplete legacy acceptance
|
|
@@ -172,7 +184,7 @@ Verify evidence and cannot authorize acceptance or publication.
|
|
|
172
184
|
|
|
173
185
|
Version 0.14.0 preserves the v1 writer only for unchanged inherited installations:
|
|
174
186
|
policy bytes, candidate/check/review/acceptance bindings, mounts and credential
|
|
175
|
-
rules stay compatible. Adopted role overrides use **v2 from 0.14.0 and 0.15.
|
|
187
|
+
rules stay compatible. Adopted role overrides use **v2 from 0.14.0, 0.15.0 and 0.15.1**, recording
|
|
176
188
|
role/provider/effort and an exact selection digest. The executor verifies the
|
|
177
189
|
frozen common configuration before phase selection; continuation and both delivery
|
|
178
190
|
validation paths require matching protected v2 evidence and private frozen config.
|
|
@@ -199,6 +211,15 @@ publishes an increased `package.json` version. Existing versions are skipped;
|
|
|
199
211
|
registry errors fail the release instead of masquerading as a missing version.
|
|
200
212
|
The workflow can also be started manually from `main`.
|
|
201
213
|
|
|
214
|
+
An accepted npm publication may still be processing. It is not yet a verified,
|
|
215
|
+
downloadable release: exact-version and `latest` metadata can appear at different
|
|
216
|
+
times, and an npm install can briefly report `ETARGET` even after both appear.
|
|
217
|
+
Verify an exact package download and compare its contents with the reviewed
|
|
218
|
+
artifact before reporting release availability. If the bounded updater reports
|
|
219
|
+
that the release is not yet downloadable, retain the prior runtime and retry the
|
|
220
|
+
update later. This does not establish whether a particular delay came from a
|
|
221
|
+
local cache or registry propagation.
|
|
222
|
+
|
|
202
223
|
For a release, update both manifests with `npm version patch --no-git-tag-version`,
|
|
203
224
|
review the change, and push through the project's normal review flow. A code
|
|
204
225
|
push without a version bump is tested but does not overwrite a published package.
|
|
@@ -233,7 +254,7 @@ The 0.15.0 compatibility audit preserves unchanged v1/v2 policy construction,
|
|
|
233
254
|
protected checks, candidate reconstruction, read-only review and acceptance guards.
|
|
234
255
|
Historical 0.14.0 v2 validation compares its actual writer version rather than
|
|
235
256
|
relabeling it as the installed version. Local registry selection is a new execution
|
|
236
|
-
contract: every phase of a local/hybrid attempt requires **v3 from 0.15.0** and its
|
|
257
|
+
contract: every phase of a local/hybrid attempt requires **v3 from 0.15.0 or 0.15.1** and its
|
|
237
258
|
exact protected frozen configuration. v1/v2 cannot attest local binding fields,
|
|
238
259
|
providers or a common policy containing a selected local role. A selected endpoint,
|
|
239
260
|
model, limit or compatibility change invalidates the current policy; explicit
|
|
@@ -241,3 +262,9 @@ rollback can restore it without editing historical evidence. Controlled regressi
|
|
|
241
262
|
exercise legacy v2 readback and local v3 rejection/tamper cases. Installed-package
|
|
242
263
|
Docker and independent Review remain delivery qualification, not inferred from this
|
|
243
264
|
compatibility audit.
|
|
265
|
+
|
|
266
|
+
The 0.15.1 patch changes release downloads and preserves the 0.15.0 v1/v2/v3
|
|
267
|
+
writers, frozen configuration and policy bytes. The explicit allowlists include
|
|
268
|
+
both patch versions; supported v3 readback retains the original writer version
|
|
269
|
+
while still comparing every execution selection, phase and policy field. It does
|
|
270
|
+
not rewrite historical evidence or allow v1/v2 to attest local bindings.
|
|
@@ -21,16 +21,17 @@ import { expectedWebStories, webPolicyHash } from './web-verification.mjs';
|
|
|
21
21
|
// selection digest. V1 can never attest an adopted role override.
|
|
22
22
|
// 0.15.0 keeps unchanged v1/v2 policies and writers; local selections require
|
|
23
23
|
// v3 for every phase. Legacy evidence cannot attest the new registry/launcher.
|
|
24
|
+
// 0.15.1 changes release downloads only; v1/v2/v3 writers and policy are unchanged.
|
|
24
25
|
// Older writers still require exact patch/tree reconstruction before publication.
|
|
25
26
|
// Deliberately independent of VERSION: a release bump is not
|
|
26
27
|
// evidence compatibility. Re-audit this list for every trust-relevant writer,
|
|
27
28
|
// isolation or validation change; remove versions whose guarantees no longer
|
|
28
29
|
// satisfy current policy. See docs/npm.md. This predicate alone grants no trust.
|
|
29
|
-
const SUPPORTED_EXECUTION_RUNTIMES_V1 = new Set(['0.8.0', '0.9.0', '0.9.1', '0.10.0', '0.11.0', '0.11.1', '0.11.2', '0.12.0', '0.13.0', '0.13.1', '0.14.0', '0.15.0']);
|
|
30
|
+
const SUPPORTED_EXECUTION_RUNTIMES_V1 = new Set(['0.8.0', '0.9.0', '0.9.1', '0.10.0', '0.11.0', '0.11.1', '0.11.2', '0.12.0', '0.13.0', '0.13.1', '0.14.0', '0.15.0', '0.15.1']);
|
|
30
31
|
|
|
31
32
|
export function isSupportedExecutionProfile(profile) {
|
|
32
|
-
if ([2, 3].includes(profile?.version)) return (profile.version === 3 ?
|
|
33
|
-
: ['0.14.0', '0.15.0'].includes(profile.runtimeVersion) && !Object.hasOwn(profile, 'localBinding') && profile.inferenceProvider !== 'factory-local')
|
|
33
|
+
if ([2, 3].includes(profile?.version)) return (profile.version === 3 ? ['0.15.0', '0.15.1'].includes(profile.runtimeVersion) && Object.hasOwn(profile, 'localBinding')
|
|
34
|
+
: ['0.14.0', '0.15.0', '0.15.1'].includes(profile.runtimeVersion) && !Object.hasOwn(profile, 'localBinding') && profile.inferenceProvider !== 'factory-local')
|
|
34
35
|
&& profile.role === (Object.keys(ROLE_PHASES).find(role => ROLE_PHASES[role] === profile.phase) || null)
|
|
35
36
|
&& (profile.reasoningEffort === null || ['low', 'medium', 'high'].includes(profile.reasoningEffort))
|
|
36
37
|
&& /^[a-f0-9]{64}$/.test(profile.selectionHash || '');
|
|
@@ -132,7 +133,7 @@ export function assertFrozenExecution(config, execution, phase) {
|
|
|
132
133
|
delete expected.hostName;
|
|
133
134
|
// Audited legacy writers retain their real version. Never let v1/v2 attest
|
|
134
135
|
// a local binding, including a deterministic phase of a hybrid attempt.
|
|
135
|
-
if (
|
|
136
|
+
if (isSupportedExecutionProfile(execution) && execution.version === expected.version)
|
|
136
137
|
expected.runtimeVersion = execution.runtimeVersion;
|
|
137
138
|
const actual = { ...execution }; delete actual.hostName; delete actual.workerName;
|
|
138
139
|
if (!isDeepStrictEqual(expected, actual)) throw new Error('Admitted execution profile does not match this attempt');
|
package/factory/updates.mjs
CHANGED
|
@@ -65,17 +65,33 @@ function installedEntry(dataHome, version) {
|
|
|
65
65
|
return pkg.name === PACKAGE && pkg.version === version && existsSync(entry) ? entry : null;
|
|
66
66
|
} catch { return null; }
|
|
67
67
|
}
|
|
68
|
-
export function installRelease(version, dataHome = DATA_HOME, runner = spawnSync
|
|
68
|
+
export function installRelease(version, dataHome = DATA_HOME, runner = spawnSync, {
|
|
69
|
+
now = () => performance.now(),
|
|
70
|
+
sleep = ms => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms),
|
|
71
|
+
} = {}) {
|
|
69
72
|
if (!/^\d+\.\d+\.\d+$/.test(version)) throw new Error('Invalid release version');
|
|
70
73
|
const existing = installedEntry(dataHome, version);
|
|
71
74
|
if (existing) return existing;
|
|
72
75
|
const releases = join(dataHome, 'releases'); privateDir(releases);
|
|
73
76
|
const staging = mkdtempSync(join(releases, '.download-'));
|
|
74
77
|
try {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
// Metadata refresh and visibility retries share the original download budget.
|
|
79
|
+
// Only ETARGET is retried; offline/auth/identity failures keep their own errors.
|
|
80
|
+
const deadline = now() + 120000;
|
|
81
|
+
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
82
|
+
const remaining = Math.floor(deadline - now());
|
|
83
|
+
if (remaining <= 0) throw new Error(`npm update download failed for ${PACKAGE}@${version}: 120-second download budget exhausted`);
|
|
84
|
+
const result = runner('npm', ['install', '--prefix', staging, '--no-save', '--package-lock=false',
|
|
85
|
+
'--ignore-scripts', '--omit=dev', '--no-audit', '--no-fund', '--prefer-online', '--prefer-offline=false', '--registry', REGISTRY, `${PACKAGE}@${version}`],
|
|
86
|
+
{ encoding: 'utf8', timeout: remaining, killSignal: 'SIGKILL', maxBuffer: 1024 * 1024 });
|
|
87
|
+
if (!result.error && result.status === 0) break;
|
|
88
|
+
const detail = result.error?.message || result.stderr || result.status;
|
|
89
|
+
const visibilityDelay = !result.error && /^npm (?:ERR!|error) code ETARGET\s*$/m.test(result.stderr || '');
|
|
90
|
+
if (!visibilityDelay) throw new Error(`npm update download failed for ${PACKAGE}@${version}: ${detail}`);
|
|
91
|
+
if (attempt === 3 || deadline - now() <= 1000)
|
|
92
|
+
throw new Error(`npm release ${PACKAGE}@${version} is not yet downloadable; publication may still be processing. Retry the update later. ${detail}`);
|
|
93
|
+
sleep(1000);
|
|
94
|
+
}
|
|
79
95
|
const pkg = read(join(staging, 'node_modules', PACKAGE, 'package.json'));
|
|
80
96
|
if (pkg.name !== PACKAGE || pkg.version !== version || !existsSync(join(staging, 'node_modules', PACKAGE, 'bin/software-defence-factory.mjs')))
|
|
81
97
|
throw new Error('Downloaded package identity does not match the requested release');
|
package/package.json
CHANGED