Compare commits

..
Author SHA1 Message Date
suyx 693fa117b7 chore(WP7-07): 冻结第一版发布记录
Dada P0-A isolated Windows CI / validate-and-package (push) Successful in 17m46s
Dada P0-A isolated Windows CI / validate-and-package (pull_request) Successful in 17m49s
2026-08-04 23:32:51 +08:00
suyx 08f3cccae4 fix(WP7-07): 排除发布扫描器路径误报
Dada P0-A isolated Windows CI / validate-and-package (push) Canceled after 0s
2026-08-04 23:20:40 +08:00
suyx a22b1f19e9 fix(WP7-07): 修正最终包浏览器门禁探测
Dada P0-A isolated Windows CI / validate-and-package (push) Canceled after 0s
2026-08-04 23:15:36 +08:00
suyx 194b59d4a5 fix(WP7-07): 统一构建全部工作区依赖
Dada P0-A isolated Windows CI / validate-and-package (push) Canceled after 0s
2026-08-04 23:07:01 +08:00
suyx 0f03b12f64 fix(WP7-07): 补齐便携包前置构建顺序
Dada P0-A isolated Windows CI / validate-and-package (push) Canceled after 0s
2026-08-04 23:05:38 +08:00
suyx ad86b4ddcc feat(WP7-07): 增加最终发布冻结与泄漏扫描
Dada P0-A isolated Windows CI / validate-and-package (push) Canceled after 0s
2026-08-04 23:03:11 +08:00
6 changed files with 378 additions and 15 deletions
+29
View File
@@ -0,0 +1,29 @@
{
"appVersion": "0.0.0",
"browsers": [
{
"brand": "Google Chrome",
"fullVersion": "150.0.7871.187"
},
{
"brand": "Microsoft Edge",
"fullVersion": "151.0.4129.59"
}
],
"buildCommit": "08f3cccae4a1e75e2f2292eef14611313523916d",
"deferredExternalTasks": [
"TASK-WP7-03",
"TASK-WP7-04"
],
"finalRelease": true,
"fixedPort": 43121,
"frozenFromCommit": "08e9c39e49d68f8642d5acfe22b0fdb40a3a08fa",
"recordedAt": "2026-08-04T15:20:54.271Z",
"releaseStatus": "first_version_internal",
"schemaVersion": "1.0",
"windows": {
"arch": "x64",
"build": "26200.8875",
"displayVersion": "25H2"
}
}
+3 -1
View File
@@ -116,7 +116,9 @@
"test:wp7-05": "node scripts/run-wp7-05-validation.mjs", "test:wp7-05": "node scripts/run-wp7-05-validation.mjs",
"test:wp7-05:unit": "node --test tests/package/wp7-05-ui-gate.test.mjs tests/package/wp7-05-coverage.test.mjs", "test:wp7-05:unit": "node --test tests/package/wp7-05-ui-gate.test.mjs tests/package/wp7-05-coverage.test.mjs",
"test:wp7-06": "node scripts/run-wp7-06-validation.mjs", "test:wp7-06": "node scripts/run-wp7-06-validation.mjs",
"test:wp7-06:unit": "node --test tests/package/wp7-06-prefreeze.test.mjs" "test:wp7-06:unit": "node --test tests/package/wp7-06-prefreeze.test.mjs",
"test:wp7-07": "node scripts/run-wp7-07-validation.mjs",
"test:wp7-07:unit": "node --test tests/package/wp7-07-final-release.test.mjs"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "1.62.0", "@playwright/test": "1.62.0",
+46 -14
View File
@@ -165,7 +165,7 @@ function copyApplication(source, destination, runtimeDependencies) {
function buildArtifacts(stagingRoot) { function buildArtifacts(stagingRoot) {
debug("build workspace artifacts"); debug("build workspace artifacts");
run("pnpm", ["--filter", "@dada/shared-contracts", "build"]); run("pnpm", ["build:workspace-packages"]);
run("pnpm", ["--filter", "@dada/web", "build"]); run("pnpm", ["--filter", "@dada/web", "build"]);
run("pnpm", ["--filter", "@dada/api", "build"]); run("pnpm", ["--filter", "@dada/api", "build"]);
run("pnpm", ["--filter", "@dada/worker", "build"]); run("pnpm", ["--filter", "@dada/worker", "build"]);
@@ -208,7 +208,7 @@ async function waitForHealth(child) {
throw new Error("Packaged API did not become healthy on fixed port 43121.", { cause: lastError }); throw new Error("Packaged API did not become healthy on fixed port 43121.", { cause: lastError });
} }
async function verifyExtractedPackage(zipPath, packageName) { export async function verifyExtractedPackage(zipPath, packageName, expectedSupport) {
const extractRoot = mkdtempSync(join(tmpdir(), "dada-wp0-09-")); const extractRoot = mkdtempSync(join(tmpdir(), "dada-wp0-09-"));
try { try {
const escapedZip = zipPath.replaceAll("'", "''"); const escapedZip = zipPath.replaceAll("'", "''");
@@ -235,21 +235,37 @@ async function verifyExtractedPackage(zipPath, packageName) {
}); });
try { try {
const health = await waitForHealth(api); const health = await waitForHealth(api);
const brands = [
{ brand: "Not_A Brand", version: "99" },
{ brand: "Chromium", version: String(expectedSupport.major) },
{ brand: expectedSupport.brand, version: String(expectedSupport.major) },
];
const fullVersionList = [
{ brand: "Not_A Brand", version: "99.0.0.0" },
{ brand: "Chromium", version: expectedSupport.fullVersion },
{ brand: expectedSupport.brand, version: expectedSupport.fullVersion },
];
const serializeBrands = (values) => values.map(({ brand, version }) => `"${brand}";v="${version}"`).join(", ");
const releaseGate = await fetch(`http://127.0.0.1:${fixedPort}/api/v1/support/check`, { const releaseGate = await fetch(`http://127.0.0.1:${fixedPort}/api/v1/support/check`, {
body: JSON.stringify({ body: JSON.stringify({
brands: [{ brand: "Google Chrome", version: "150" }], brands,
full_version_list: [{ brand: "Google Chrome", version: "150.0.0.0" }], full_version_list: fullVersionList,
platform: "Windows", platform: "Windows",
}), }),
headers: { headers: {
"content-type": "application/json", "content-type": "application/json",
"sec-ch-ua": '"Google Chrome";v="150"', host: `127.0.0.1:${fixedPort}`,
"sec-ch-ua-full-version-list": '"Google Chrome";v="150.0.0.0"', origin: `http://127.0.0.1:${fixedPort}`,
"sec-ch-ua": serializeBrands(brands),
"sec-ch-ua-full-version-list": serializeBrands(fullVersionList),
"sec-ch-ua-platform": '"Windows"', "sec-ch-ua-platform": '"Windows"',
}, },
method: "POST", method: "POST",
}); });
if (releaseGate.status !== 426) throw new Error(`Candidate RELEASE.json unexpectedly passed with ${releaseGate.status}.`); if (releaseGate.status !== expectedSupport.statusCode) {
const responseBody = await releaseGate.text();
throw new Error(`Packaged RELEASE.json support gate returned ${releaseGate.status}; expected ${expectedSupport.statusCode}: ${responseBody}`);
}
return { return {
api: { executable: "runtime/node.exe", health, pid: api.pid, release_gate: { status_code: releaseGate.status }, status: "passed" }, api: { executable: "runtime/node.exe", health, pid: api.pid, release_gate: { status_code: releaseGate.status }, status: "passed" },
native, native,
@@ -291,7 +307,7 @@ function scanPackage(packageDirectory) {
return { disallowed_matches: disallowedMatches, reparse_points: reparsePoints, scanned_files: files.length, status: disallowedMatches.length === 0 && reparsePoints.length === 0 ? "passed" : "failed" }; return { disallowed_matches: disallowedMatches, reparse_points: reparsePoints, scanned_files: files.length, status: disallowedMatches.length === 0 && reparsePoints.length === 0 ? "passed" : "failed" };
} }
export async function buildAndValidatePortablePackage({ evidenceDirectory, outputRoot }) { export async function buildAndValidatePortablePackage({ evidenceDirectory, outputRoot, releaseRecord }) {
if (process.platform !== frozenRuntime.os || process.arch !== frozenRuntime.arch || process.version.slice(1) !== frozenRuntime.node) { if (process.platform !== frozenRuntime.os || process.arch !== frozenRuntime.arch || process.version.slice(1) !== frozenRuntime.node) {
throw new Error("Portable package build requires frozen Node 24.13.0 on win-x64."); throw new Error("Portable package build requires frozen Node 24.13.0 on win-x64.");
} }
@@ -351,7 +367,8 @@ export async function buildAndValidatePortablePackage({ evidenceDirectory, outpu
writeJson(join(packageDirectory, "LICENSES", "third-party.json"), { api: apiDependencies, runtime: { node: frozenRuntime.node }, schema_version: "1.0", worker: workerDependencies }); writeJson(join(packageDirectory, "LICENSES", "third-party.json"), { api: apiDependencies, runtime: { node: frozenRuntime.node }, schema_version: "1.0", worker: workerDependencies });
const commit = run("git", ["rev-parse", "HEAD"]); const commit = run("git", ["rev-parse", "HEAD"]);
writeJson(join(packageDirectory, "RELEASE.json"), { const finalRelease = releaseRecord !== undefined;
writeJson(join(packageDirectory, "RELEASE.json"), releaseRecord ?? {
app_version: appVersion, app_version: appVersion,
browsers: [], browsers: [],
build_commit: commit, build_commit: commit,
@@ -360,16 +377,20 @@ export async function buildAndValidatePortablePackage({ evidenceDirectory, outpu
windows_build: null, windows_build: null,
}); });
writeFileSync(join(packageDirectory, "START-HERE.txt"), [ writeFileSync(join(packageDirectory, "START-HERE.txt"), [
"Dada P0-A candidate package", finalRelease ? "Dada P0-A first-version portable package" : "Dada P0-A candidate package",
"", "",
"This candidate is unsigned and is not a final P0-A release.", finalRelease
? "This unsigned first-version package passed the local P0-A release gates recorded in RELEASE.json."
: "This candidate is unsigned and is not a final P0-A release.",
"Verify the adjacent SHA-256 file before first launch.", "Verify the adjacent SHA-256 file before first launch.",
"Windows SmartScreen may warn on first launch because the executable is unsigned.", "Windows SmartScreen may warn on first launch because the executable is unsigned.",
"For an antivirus alert, compare the package hash with the Gitea build record.", "For an antivirus alert, compare the package hash with the Gitea build record.",
"Do not disable antivirus protection, add broad exclusions, or skip hash verification.", "Do not disable antivirus protection, add broad exclusions, or skip hash verification.",
"To update, exit Dada from the tray and replace the complete program directory.", "To update, exit Dada from the tray and replace the complete program directory.",
"Dada uses 127.0.0.1:43121 and does not support LAN or remote access.", "Dada uses 127.0.0.1:43121 and does not support LAN or remote access.",
"A final RELEASE.json is created only after WP-7 acceptance.", finalRelease
? "Resend and Amap real-provider validation remain explicitly deferred and are not recorded as passed."
: "A final RELEASE.json is created only after WP-7 acceptance.",
"", "",
].join("\r\n")); ].join("\r\n"));
@@ -381,7 +402,18 @@ export async function buildAndValidatePortablePackage({ evidenceDirectory, outpu
const zipSha256 = fileSha256(zipPath); const zipSha256 = fileSha256(zipPath);
const shaPath = `${zipPath}.sha256`; const shaPath = `${zipPath}.sha256`;
writeFileSync(shaPath, `${zipSha256} ${basename(zipPath)}\n`); writeFileSync(shaPath, `${zipSha256} ${basename(zipPath)}\n`);
const processTree = await verifyExtractedPackage(zipPath, packageName); const supportBrowser = finalRelease ? releaseRecord.browsers[0] : undefined;
const processTree = await verifyExtractedPackage(zipPath, packageName, finalRelease ? {
brand: supportBrowser.brand,
fullVersion: supportBrowser.fullVersion,
major: Number.parseInt(supportBrowser.fullVersion.split(".")[0], 10),
statusCode: 200,
} : {
brand: "Google Chrome",
fullVersion: "150.0.0.0",
major: 150,
statusCode: 426,
});
const fileEntries = listFiles(packageDirectory).files.map((path) => ({ const fileEntries = listFiles(packageDirectory).files.map((path) => ({
path: relative(packageDirectory, path).replaceAll("\\", "/"), path: relative(packageDirectory, path).replaceAll("\\", "/"),
sha256: fileSha256(path), sha256: fileSha256(path),
@@ -392,7 +424,7 @@ export async function buildAndValidatePortablePackage({ evidenceDirectory, outpu
files: fileEntries, files: fileEntries,
fixed_port: fixedPort, fixed_port: fixedPort,
package_name: packageName, package_name: packageName,
release_status: "candidate_unvalidated", release_status: finalRelease ? releaseRecord.releaseStatus : "candidate_unvalidated",
schema_version: "1.0", schema_version: "1.0",
zip_sha256: zipSha256, zip_sha256: zipSha256,
}; };
+96
View File
@@ -0,0 +1,96 @@
import { createHash } from "node:crypto";
import { readFileSync, readdirSync, statSync } from "node:fs";
import { extname, join, relative } from "node:path";
const SHA40 = /^[a-f0-9]{40}$/i;
const SHA64 = /^[a-f0-9]{64}$/i;
const VERSION = /^[1-9][0-9]*\.[0-9]+\.[0-9]+\.[0-9]+$/;
const ABSOLUTE_PATH = /(?:[A-Za-z]:[\\/](?:Users|Documents)[\\/][^\\/"'\s]+[\\/]|\/Users\/[^/"'\s]+\/|\/home\/[^/"'\s]+\/)/;
const CREDENTIAL = /\b(?:sk|key)-[A-Za-z0-9_-]{16,}\b|-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/i;
const TEXT_EXTENSIONS = new Set([".cjs", ".cs", ".css", ".html", ".js", ".json", ".mjs", ".ts", ".tsx", ".txt", ".xml", ".yaml", ".yml"]);
export const DEFERRED_EXTERNAL_TASKS = Object.freeze(["TASK-WP7-03", "TASK-WP7-04"]);
export function buildFinalReleaseRecord({ appVersion, browsers, buildCommit, frozenFromCommit, recordedAt, windows }) {
const record = {
appVersion,
browsers: browsers.map(({ brand, fullVersion }) => ({ brand, fullVersion })),
buildCommit: buildCommit.toLowerCase(),
deferredExternalTasks: [...DEFERRED_EXTERNAL_TASKS],
finalRelease: true,
fixedPort: 43121,
frozenFromCommit: frozenFromCommit.toLowerCase(),
recordedAt,
releaseStatus: "first_version_internal",
schemaVersion: "1.0",
windows: { arch: windows.arch, build: windows.build, displayVersion: windows.displayVersion },
};
return validateFinalReleaseRecord(record);
}
export function validateFinalReleaseRecord(record) {
const errors = [];
if (record?.schemaVersion !== "1.0") errors.push("schemaVersion");
if (record?.releaseStatus !== "first_version_internal") errors.push("releaseStatus");
if (record?.finalRelease !== true) errors.push("finalRelease");
if (record?.fixedPort !== 43121) errors.push("fixedPort");
if (!SHA40.test(record?.buildCommit ?? "")) errors.push("buildCommit");
if (!SHA40.test(record?.frozenFromCommit ?? "")) errors.push("frozenFromCommit");
if (!Number.isFinite(Date.parse(record?.recordedAt ?? ""))) errors.push("recordedAt");
if (!Array.isArray(record?.deferredExternalTasks) || record.deferredExternalTasks.join("|") !== DEFERRED_EXTERNAL_TASKS.join("|")) errors.push("deferredExternalTasks");
if (record?.windows?.arch !== "x64" || !/^\d+\.\d+$/.test(record?.windows?.build ?? "")) errors.push("windows");
if (!Array.isArray(record?.browsers) || record.browsers.length !== 2) {
errors.push("browsers");
} else {
const brands = record.browsers.map(({ brand }) => brand).sort();
if (brands.join("|") !== "Google Chrome|Microsoft Edge") errors.push("browserBrands");
for (const browser of record.browsers) {
if (!VERSION.test(browser.fullVersion ?? "")) errors.push(`${browser.brand}.fullVersion`);
if ("path" in browser || "executablePath" in browser || "executableSha256" in browser) errors.push(`${browser.brand}.privateMetadata`);
}
}
const serialized = JSON.stringify(record);
if (ABSOLUTE_PATH.test(serialized) || CREDENTIAL.test(serialized)) errors.push("sensitiveValue");
if (errors.length > 0) throw new Error(`WP7_07_RELEASE_INVALID:${[...new Set(errors)].join(",")}`);
return record;
}
export function sha256File(path) {
return createHash("sha256").update(readFileSync(path)).digest("hex").toUpperCase();
}
export function scanReleaseFiles({ roots, allowedFixturePaths = [] }) {
const allowed = new Set(allowedFixturePaths.map((value) => value.replaceAll("\\", "/")));
const findings = [];
let scannedFiles = 0;
function visit(root, current = root) {
for (const entry of readdirSync(current, { withFileTypes: true })) {
if ([".git", ".pnpm-store", "node_modules", "bin", "obj"].includes(entry.name)) continue;
const path = join(current, entry.name);
if (entry.isDirectory()) {
visit(root, path);
continue;
}
if (!entry.isFile()) continue;
scannedFiles += 1;
if (!TEXT_EXTENSIONS.has(extname(entry.name).toLowerCase())) continue;
const logicalPath = relative(root, path).replaceAll("\\", "/");
const content = readFileSync(path, "utf8");
if (!allowed.has(logicalPath) && ABSOLUTE_PATH.test(content)) findings.push({ path: logicalPath, rule: "absolute_user_path" });
if (!allowed.has(logicalPath) && CREDENTIAL.test(content)) findings.push({ path: logicalPath, rule: "credential_shape" });
}
}
for (const root of roots) {
if (!statSync(root).isDirectory()) throw new Error(`WP7_07_SCAN_ROOT_INVALID:${root}`);
visit(root);
}
return { findings, scanned_files: scannedFiles, status: findings.length === 0 ? "passed" : "failed" };
}
export function validateFinalEvidence({ packageManifest, release, releaseSha256, scan }) {
validateFinalReleaseRecord(release);
if (!SHA64.test(releaseSha256 ?? "")) throw new Error("WP7_07_RELEASE_HASH_INVALID");
if (packageManifest?.release_status !== release.releaseStatus || !SHA64.test(packageManifest?.zip_sha256 ?? "")) throw new Error("WP7_07_PACKAGE_MANIFEST_INVALID");
if (scan?.status !== "passed" || scan.findings?.length !== 0) throw new Error("WP7_07_LEAK_SCAN_FAILED");
return { release_sha256: releaseSha256.toUpperCase(), status: "passed", zip_sha256: packageManifest.zip_sha256.toUpperCase() };
}
+157
View File
@@ -0,0 +1,157 @@
import { spawnSync } from "node:child_process";
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { basename, join, resolve } from "node:path";
import { buildAndValidatePortablePackage } from "./lib/portable-package.mjs";
import { readCandidateEnvironment } from "./lib/release-candidate.mjs";
import {
buildFinalReleaseRecord,
scanReleaseFiles,
sha256File,
validateFinalEvidence,
} from "./lib/wp7-07-final-release.mjs";
const runId = process.env.DADA_TDD_RUN_ID ?? `wp7-07-final-${new Date().toISOString().replace(/[^0-9]/g, "")}`;
const runDirectory = resolve("artifacts", "tdd", runId);
const releaseCase = resolve(runDirectory, "cases", "TDD-WP7-REL-001-final-release-record");
const securityCase = resolve(runDirectory, "cases", "TDD-WP7-SEC-001-artifact-leak-scan");
const outputRoot = resolve(".build", "wp7-07-final-release");
if (existsSync(runDirectory)) throw new Error(`Evidence run already exists: ${runId}`);
mkdirSync(releaseCase, { recursive: true });
mkdirSync(securityCase, { recursive: true });
function writeJson(path, value) {
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`);
}
function git(args) {
const result = spawnSync("git", args, { encoding: "utf8", maxBuffer: 64 * 1024 * 1024, timeout: 120_000 });
if ((result.status ?? 1) !== 0) throw new Error(`WP7_07_GIT_FAILED:${args.join(" ")}`);
return result.stdout.trim();
}
function gitGrep(args) {
const result = spawnSync("git", ["grep", ...args], { encoding: "utf8", maxBuffer: 64 * 1024 * 1024, timeout: 120_000 });
if (![0, 1].includes(result.status ?? 2)) throw new Error("WP7_07_GIT_GREP_FAILED");
return result.status === 0 ? result.stdout.trim() : "";
}
function run(name, command) {
const startedAt = new Date().toISOString();
const result = spawnSync(process.env.ComSpec ?? "cmd.exe", ["/d", "/s", "/c", command], {
encoding: "utf8",
env: process.env,
maxBuffer: 64 * 1024 * 1024,
});
if (result.stdout) process.stdout.write(result.stdout);
if (result.stderr) process.stderr.write(result.stderr);
return { command, exit_code: result.status ?? 1, finished_at: new Date().toISOString(), name, started_at: startedAt };
}
const currentCommit = git(["rev-parse", "HEAD"]);
const prefreezeCommit = git(["ls-remote", "origin", "refs/heads/codex/wp7-06"]).split(/\s+/)[0];
if (!prefreezeCommit || spawnSync("git", ["merge-base", "--is-ancestor", prefreezeCommit, "HEAD"]).status !== 0) {
throw new Error("WP7_07_PREFREEZE_LINEAGE_INVALID");
}
const commands = [
run("unit", "node --test tests/package/wp7-07-final-release.test.mjs"),
run("security", "pnpm test:security"),
run("trace", "pnpm validate:tdd-trace"),
];
if (commands.some(({ exit_code }) => exit_code !== 0)) {
writeJson(join(releaseCase, "commands.json"), { commands, run_id: runId, schema_version: "1.0" });
process.exit(1);
}
const environment = readCandidateEnvironment();
const packageJson = JSON.parse(readFileSync("package.json", "utf8"));
const release = buildFinalReleaseRecord({
appVersion: packageJson.version,
browsers: environment.browsers.map(({ brand, full_version }) => ({ brand, fullVersion: full_version })),
buildCommit: currentCommit,
frozenFromCommit: prefreezeCommit,
recordedAt: new Date().toISOString(),
windows: {
arch: environment.windows.arch,
build: environment.windows.build,
displayVersion: environment.windows.display_version,
},
});
writeJson(resolve("RELEASE.json"), release);
const packageResult = await buildAndValidatePortablePackage({ evidenceDirectory: releaseCase, outputRoot, releaseRecord: release });
const packageDirectory = join(outputRoot, packageResult.packageManifest.package_name);
const zipPath = join(outputRoot, `${packageResult.packageManifest.package_name}.zip`);
const releaseSha256 = sha256File(resolve("RELEASE.json"));
const packageReleaseSha256 = sha256File(join(packageDirectory, "RELEASE.json"));
if (releaseSha256 !== packageReleaseSha256) throw new Error("WP7_07_PACKAGE_RELEASE_DRIFT");
const finalScan = scanReleaseFiles({ roots: [packageDirectory, releaseCase] });
const trackedSensitive = gitGrep(["-I", "-n", "-E", "C:\\\\Users\\\\[^\\\\]+|sk-[A-Za-z0-9_-]{24,}", "HEAD", "--", ":!tests", ":!scripts/lib/wp7-07-final-release.mjs"]);
const scan = {
...finalScan,
git_current_findings: trackedSensitive ? trackedSensitive.split(/\r?\n/).filter(Boolean) : [],
status: finalScan.status === "passed" && !trackedSensitive ? "passed" : "failed",
};
writeJson(join(securityCase, "scan-report.json"), scan);
writeJson(join(securityCase, "allowlist.json"), {
entries: ["tests/**:synthetic security traps", "scripts/lib/wp7-07-final-release.mjs:scanner patterns"],
real_values_allowed: false,
schema_version: "1.0",
});
if (scan.status !== "passed") throw new Error("WP7_07_LEAK_SCAN_FAILED");
const finalEvidence = validateFinalEvidence({ packageManifest: packageResult.packageManifest, release, releaseSha256, scan });
copyFileSync(resolve("RELEASE.json"), join(releaseCase, "RELEASE.json"));
copyFileSync(join(packageDirectory, "START-HERE.txt"), join(releaseCase, "START-HERE.txt"));
writeJson(join(releaseCase, "environment.json"), {
browsers: release.browsers,
fixed_port: release.fixedPort,
windows: release.windows,
schema_version: "1.0",
});
writeJson(join(releaseCase, "final-package.json"), {
file_name: basename(zipPath),
release_sha256: finalEvidence.release_sha256,
release_status: release.releaseStatus,
zip_sha256: finalEvidence.zip_sha256,
schema_version: "1.0",
});
writeJson(join(releaseCase, "commands.json"), { commands, run_id: runId, schema_version: "1.0" });
writeJson(join(releaseCase, "result.json"), {
acceptance_criteria: ["AC-24", "AC-41", "AC-48", "AC-56"],
deferred_external_tasks: release.deferredExternalTasks,
evidence_refs: ["RELEASE.json", "START-HERE.txt", "environment.json", "package-manifest.json", "final-package.json"],
release_gate: ["release:P0-A"],
requirements: ["NFR-01", "NFR-09", "PRIV-01", "PRIV-02"],
status: "passed",
task_id: "TASK-WP7-07",
test_id: "TDD-WP7-REL-001-final-release-record",
});
writeJson(join(securityCase, "result.json"), {
evidence_refs: ["scan-report.json", "allowlist.json"],
status: "passed",
task_id: "TASK-WP7-07",
test_id: "TDD-WP7-SEC-001-artifact-leak-scan",
});
writeJson(join(runDirectory, "evidence.json"), {
cases: [
{ missing_evidence: [], status: "passed", test_id: "TDD-WP7-REL-001-final-release-record" },
{ missing_evidence: [], status: "passed", test_id: "TDD-WP7-SEC-001-artifact-leak-scan" },
],
deferred_external_tasks: release.deferredExternalTasks,
release_sha256: finalEvidence.release_sha256,
run_id: runId,
status: "passed",
zip_sha256: finalEvidence.zip_sha256,
schema_version: "1.0",
});
console.log(JSON.stringify({
deferred_external_tasks: release.deferredExternalTasks,
release_sha256: finalEvidence.release_sha256,
run_id: runId,
status: "passed",
zip_sha256: finalEvidence.zip_sha256,
}, null, 2));
@@ -0,0 +1,47 @@
import assert from "node:assert/strict";
import { mkdtempSync, mkdirSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { test } from "node:test";
import { buildFinalReleaseRecord, scanReleaseFiles, validateFinalEvidence } from "../../scripts/lib/wp7-07-final-release.mjs";
function release() {
return buildFinalReleaseRecord({
appVersion: "0.0.0",
browsers: [
{ brand: "Google Chrome", fullVersion: "150.0.7871.187" },
{ brand: "Microsoft Edge", fullVersion: "151.0.4129.59" },
],
buildCommit: "a".repeat(40),
frozenFromCommit: "b".repeat(40),
recordedAt: "2026-08-04T06:00:00.000Z",
windows: { arch: "x64", build: "26200.8875", displayVersion: "25H2" },
});
}
test("TDD-WP7-REL-001 creates a browser-gate compatible first-version record", () => {
const record = release();
assert.equal(record.finalRelease, true);
assert.equal(record.fixedPort, 43121);
assert.deepEqual(record.deferredExternalTasks, ["TASK-WP7-03", "TASK-WP7-04"]);
assert.deepEqual(record.browsers.map(({ brand }) => brand).sort(), ["Google Chrome", "Microsoft Edge"]);
});
test("TDD-WP7-SEC-001 rejects credential shapes and absolute user paths", () => {
const root = mkdtempSync(join(tmpdir(), "dada-wp7-07-scan-"));
mkdirSync(join(root, "logs"));
writeFileSync(join(root, "logs", "diagnostic.txt"), "credential=key-abcdefghijklmnop C:\\Users\\person\\private.txt\n");
const scan = scanReleaseFiles({ roots: [root] });
assert.equal(scan.status, "failed");
assert.deepEqual(new Set(scan.findings.map(({ rule }) => rule)), new Set(["absolute_user_path", "credential_shape"]));
});
test("TDD-WP7-REL-001 binds release and package hashes only after a zero-finding scan", () => {
assert.deepEqual(validateFinalEvidence({
packageManifest: { release_status: "first_version_internal", zip_sha256: "C".repeat(64) },
release: release(),
releaseSha256: "D".repeat(64),
scan: { findings: [], status: "passed" },
}), { release_sha256: "D".repeat(64), status: "passed", zip_sha256: "C".repeat(64) });
});