| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import assert from 'node:assert/strict'; |
| import { execFileSync } from 'node:child_process'; |
| import { createHash } from 'node:crypto'; |
| import { |
| chmodSync, |
| copyFileSync, |
| existsSync, |
| mkdtempSync, |
| mkdirSync, |
| readFileSync, |
| rmSync, |
| writeFileSync, |
| } from 'node:fs'; |
| import { tmpdir } from 'node:os'; |
| import { basename, dirname, join } from 'node:path'; |
| import { describe, test } from 'node:test'; |
| import { |
| createSourceCandidate, |
| controlledProcessEnvironment, |
| parseSha512File, |
| reproduceSourceCandidate, |
| signSourceCandidate, |
| sourceCandidateIdentity, |
| validateArchiveEntries, |
| validateGpgVerificationStatus, |
| validatePackageVersions, |
| verifySourceCandidate, |
| } from './asf-source-release.mjs'; |
| |
| describe('ASF source release identity', () => { |
| test('uses the incubating source distribution name for plain versions only', () => { |
| assert.deepEqual(sourceCandidateIdentity('0.1.12'), { |
| archiveName: 'apache-maka-0.1.12-incubating-src.tar.gz', |
| rootDirectory: 'apache-maka-0.1.12-incubating', |
| version: '0.1.12', |
| }); |
| assert.throws(() => sourceCandidateIdentity('0.1.12-incubating'), /Invalid release version/); |
| }); |
| }); |
| |
| describe('ASF source release verification', () => { |
| test('validates checksum identity and archive boundaries', () => { |
| const name = 'apache-maka-0.1.12-incubating-src.tar.gz'; |
| assert.equal(parseSha512File(`${'a'.repeat(128)} ${name}\n`, name), 'a'.repeat(128)); |
| assert.throws(() => parseSha512File(`${'a'.repeat(128)} another.tar.gz\n`, name), /expected/); |
| |
| const root = 'apache-maka-0.1.12-incubating'; |
| assert.doesNotThrow(() => |
| validateArchiveEntries( |
| [ |
| `${root}/`, |
| `${root}/DISCLAIMER-WIP`, |
| `${root}/LICENSE`, |
| `${root}/NOTICE`, |
| `${root}/package-lock.json`, |
| `${root}/package.json`, |
| `${root}/src/index.ts`, |
| ], |
| root, |
| ), |
| ); |
| assert.throws( |
| () => |
| validateArchiveEntries( |
| [ |
| `${root}/DISCLAIMER-WIP`, |
| `${root}/LICENSE`, |
| `${root}/NOTICE`, |
| `${root}/package-lock.json`, |
| `${root}/package.json`, |
| `${root}/.maka-shots/review.png`, |
| `${root}/node_modules/dependency/index.js`, |
| ], |
| root, |
| ), |
| /Forbidden archive entry/, |
| ); |
| assert.throws( |
| () => |
| validateArchiveEntries( |
| [ |
| `${root}/DISCLAIMER-WIP`, |
| `${root}/LICENSE`, |
| `${root}/NOTICE`, |
| `${root}/package-lock.json`, |
| `${root}/package.json`, |
| `${root}/fixtures/evil.bin`, |
| `${root}/fixtures/EVIL.BIN`, |
| ], |
| root, |
| ), |
| /Cross-platform archive entry collision/, |
| ); |
| }); |
| |
| test('requires one current valid GPG signature status', () => { |
| const fingerprint = 'A'.repeat(40); |
| assert.deepEqual( |
| validateGpgVerificationStatus( |
| `[GNUPG:] GOODSIG ABCDEF0123456789 Release Test\n[GNUPG:] VALIDSIG ${fingerprint} 2026-08-20 1787193600 0 4 0 1 10 00 ${fingerprint}\n`, |
| ), |
| { fingerprint, hashAlgorithm: 10 }, |
| ); |
| for (const status of ['EXPKEYSIG', 'EXPSIG', 'KEYEXPIRED', 'KEYREVOKED', 'REVKEYSIG']) { |
| assert.throws( |
| () => |
| validateGpgVerificationStatus( |
| `[GNUPG:] ${status} ABCDEF0123456789 Release Test\n[GNUPG:] GOODSIG ABCDEF0123456789 Release Test\n[GNUPG:] VALIDSIG ${fingerprint} 2026-08-20 1787193600 0 4 0 1 10 00 ${fingerprint}\n`, |
| ), |
| new RegExp(status), |
| ); |
| } |
| }); |
| |
| test('requires package and lockfile versions to share one identity', () => { |
| const packageJson = { version: '0.1.12' }; |
| const packageLock = { packages: { '': { version: '0.1.12' } }, version: '0.1.12' }; |
| assert.doesNotThrow(() => |
| validatePackageVersions({ packageJson, packageLock, source: 'fixture', version: '0.1.12' }), |
| ); |
| assert.throws( |
| () => |
| validatePackageVersions({ |
| packageJson, |
| packageLock: { ...packageLock, version: '9.9.9' }, |
| source: 'fixture', |
| version: '0.1.12', |
| }), |
| /package-lock\.json.*9\.9\.9/, |
| ); |
| }); |
| |
| test('rejects Category X dependencies from a nested package lockfile', async () => { |
| const fixture = createFixtureCandidate({ |
| 'tools/runtime/package.json': `${JSON.stringify({ name: 'runtime', private: true, license: 'Apache-2.0' })}\n`, |
| 'tools/runtime/package-lock.json': `${JSON.stringify({ |
| lockfileVersion: 3, |
| name: 'runtime', |
| packages: { |
| '': { name: 'runtime', license: 'Apache-2.0' }, |
| 'node_modules/category-x-runtime': { |
| version: '1.0.0', |
| license: 'LGPL-3.0-or-later', |
| }, |
| }, |
| })}\n`, |
| }); |
| try { |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath: fixture.archivePath }), |
| /Category X.*category-x-runtime.*LGPL-3\.0-or-later/, |
| ); |
| } finally { |
| fixture.cleanup(); |
| } |
| }); |
| |
| test('accepts a classified dependency from a nested package lockfile', async () => { |
| const fixture = createFixtureCandidate({ |
| 'tools/source/package.json': `${JSON.stringify({ |
| dependencies: { 'source-helper': '1.0.0' }, |
| name: 'source', |
| private: true, |
| })}\n`, |
| 'tools/source/package-lock.json': `${JSON.stringify({ |
| lockfileVersion: 3, |
| name: 'source', |
| packages: { |
| '': { name: 'source', license: 'Apache-2.0' }, |
| 'node_modules/source-helper': { version: '1.0.0', license: 'MIT' }, |
| }, |
| })}\n`, |
| }); |
| try { |
| await assert.doesNotReject(() => verifySourceCandidate({ archivePath: fixture.archivePath })); |
| } finally { |
| fixture.cleanup(); |
| } |
| }); |
| |
| test('rejects an unclassified dependency from a nested package lockfile', async () => { |
| const fixture = createFixtureCandidate({ |
| 'tools/unknown/package.json': `${JSON.stringify({ name: 'unknown', private: true, license: 'Apache-2.0' })}\n`, |
| 'tools/unknown/package-lock.json': `${JSON.stringify({ |
| lockfileVersion: 3, |
| name: 'unknown', |
| packages: { |
| '': { name: 'unknown', license: 'Apache-2.0' }, |
| 'node_modules/unknown-runtime': { version: '1.0.0' }, |
| }, |
| })}\n`, |
| }); |
| try { |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath: fixture.archivePath }), |
| /Cannot safely classify unknown-runtime@1\.0\.0.*tools\/unknown\/package-lock\.json/, |
| ); |
| } finally { |
| fixture.cleanup(); |
| } |
| }); |
| |
| test('accepts an unlicensed private manifest without dependencies', async () => { |
| const fixture = createFixtureCandidate({ |
| 'tools/profile/package.json': `${JSON.stringify({ name: 'profile', private: true })}\n`, |
| }); |
| try { |
| await assert.doesNotReject(() => verifySourceCandidate({ archivePath: fixture.archivePath })); |
| } finally { |
| fixture.cleanup(); |
| } |
| }); |
| |
| test('rejects unlicensed private manifests whose dependencies lack lock provenance', async () => { |
| const dependencyDeclarations = { |
| bundleDependencies: ['runtime-helper'], |
| bundledDependencies: ['runtime-helper'], |
| dependencies: { 'runtime-helper': '1.0.0' }, |
| devDependencies: { 'runtime-helper': '1.0.0' }, |
| optionalDependencies: { 'runtime-helper': '1.0.0' }, |
| peerDependencies: { 'runtime-helper': '1.0.0' }, |
| }; |
| for (const [field, declaration] of Object.entries(dependencyDeclarations)) { |
| const fixture = createFixtureCandidate({ |
| [`tools/${field}/package.json`]: `${JSON.stringify({ |
| [field]: declaration, |
| name: field, |
| private: true, |
| })}\n`, |
| }); |
| try { |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath: fixture.archivePath }), |
| new RegExp( |
| `Cannot safely classify.*${field}.*without (?:matching )?lock provenance`, |
| 'u', |
| ), |
| ); |
| } finally { |
| fixture.cleanup(); |
| } |
| } |
| }); |
| |
| test('does not accept arbitrary notice files as dependency license authority', async () => { |
| const fixture = createFixtureCandidate({ |
| 'tools/runtime/THIRD_PARTY_NOTICES.txt': |
| 'Package: unknown-runtime@1.0.0\nSelected license: MIT\n', |
| 'tools/runtime/package-lock.json': `${JSON.stringify({ |
| lockfileVersion: 3, |
| packages: { |
| '': { name: 'runtime' }, |
| 'node_modules/unknown-runtime': { version: '1.0.0' }, |
| }, |
| })}\n`, |
| 'tools/runtime/package.json': `${JSON.stringify({ |
| dependencies: { 'unknown-runtime': '1.0.0' }, |
| license: 'Apache-2.0', |
| name: 'runtime', |
| private: true, |
| })}\n`, |
| }); |
| try { |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath: fixture.archivePath }), |
| /Cannot safely classify unknown-runtime@1\.0\.0/, |
| ); |
| } finally { |
| fixture.cleanup(); |
| } |
| }); |
| |
| test('uses the generated npm notice as the dependency license authority', async () => { |
| const fixture = createFixtureCandidate({ |
| 'apps/desktop/resources/licenses/npm/THIRD_PARTY_NOTICES.txt': |
| 'Package: source-helper@1.0.0\nSelected license: MIT\n', |
| 'tools/source/package-lock.json': `${JSON.stringify({ |
| lockfileVersion: 3, |
| packages: { |
| '': { name: 'source' }, |
| 'node_modules/source-helper': { version: '1.0.0' }, |
| }, |
| })}\n`, |
| 'tools/source/package.json': `${JSON.stringify({ |
| dependencies: { 'source-helper': '1.0.0' }, |
| name: 'source', |
| private: true, |
| })}\n`, |
| }); |
| try { |
| await assert.doesNotReject(() => verifySourceCandidate({ archivePath: fixture.archivePath })); |
| } finally { |
| fixture.cleanup(); |
| } |
| }); |
| |
| test('does not let lock metadata override a Category X generated notice', async () => { |
| const fixture = createFixtureCandidate({ |
| 'apps/desktop/resources/licenses/npm/THIRD_PARTY_NOTICES.txt': |
| 'Package: disguised-runtime@1.0.0\nSelected license: GPL-3.0-only\n', |
| 'tools/runtime/package-lock.json': `${JSON.stringify({ |
| lockfileVersion: 3, |
| packages: { |
| '': { name: 'runtime' }, |
| 'node_modules/disguised-runtime': { license: 'MIT', version: '1.0.0' }, |
| }, |
| })}\n`, |
| 'tools/runtime/package.json': `${JSON.stringify({ |
| dependencies: { 'disguised-runtime': '1.0.0' }, |
| license: 'Apache-2.0', |
| name: 'runtime', |
| private: true, |
| })}\n`, |
| }); |
| try { |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath: fixture.archivePath }), |
| /Category X.*disguised-runtime.*GPL-3\.0-only/, |
| ); |
| } finally { |
| fixture.cleanup(); |
| } |
| }); |
| |
| test('rejects lock links without a candidate-owned workspace target', async () => { |
| const fixture = createFixtureCandidate({ |
| 'tools/runtime/package-lock.json': `${JSON.stringify({ |
| lockfileVersion: 3, |
| packages: { |
| '': { name: 'runtime' }, |
| 'node_modules/evil-runtime': { link: true, resolved: '../../evil-runtime' }, |
| }, |
| })}\n`, |
| 'tools/runtime/package.json': `${JSON.stringify({ license: 'Apache-2.0', name: 'runtime' })}\n`, |
| }); |
| try { |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath: fixture.archivePath }), |
| /Cannot safely classify workspace link.*evil-runtime/, |
| ); |
| } finally { |
| fixture.cleanup(); |
| } |
| }); |
| |
| test('rejects Category X metadata on workspace package lock entries', async () => { |
| const fixture = createFixtureCandidate({ |
| 'packages/evil/package.json': `${JSON.stringify({ |
| license: 'Apache-2.0', |
| name: 'evil', |
| version: '1.0.0', |
| })}\n`, |
| 'package-lock.json': `${JSON.stringify({ |
| lockfileVersion: 3, |
| name: 'maka', |
| packages: { |
| '': { name: 'maka', version: '0.1.12' }, |
| 'packages/evil': { license: 'GPL-3.0-only', name: 'evil', version: '1.0.0' }, |
| }, |
| version: '0.1.12', |
| })}\n`, |
| }); |
| try { |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath: fixture.archivePath }), |
| /Category X.*packages\/evil.*GPL-3\.0-only/, |
| ); |
| } finally { |
| fixture.cleanup(); |
| } |
| }); |
| |
| test('binds licensed manifest dependencies to a package lock closure', async () => { |
| const fixture = createFixtureCandidate({ |
| 'tools/runtime/package.json': `${JSON.stringify({ |
| dependencies: { 'unlocked-runtime': '1.0.0' }, |
| license: 'Apache-2.0', |
| name: 'runtime', |
| private: true, |
| })}\n`, |
| }); |
| try { |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath: fixture.archivePath }), |
| /Cannot safely classify.*tools\/runtime\/package\.json.*without (?:matching )?lock provenance/, |
| ); |
| } finally { |
| fixture.cleanup(); |
| } |
| }); |
| |
| test('rejects unsupported npm lockfile versions', async () => { |
| const fixture = createFixtureCandidate({ |
| 'tools/runtime/package-lock.json': `${JSON.stringify({ |
| lockfileVersion: 99, |
| packages: { '': { name: 'runtime' } }, |
| })}\n`, |
| 'tools/runtime/package.json': `${JSON.stringify({ license: 'Apache-2.0', name: 'runtime' })}\n`, |
| }); |
| try { |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath: fixture.archivePath }), |
| /Unsupported npm lockfile version 99/, |
| ); |
| } finally { |
| fixture.cleanup(); |
| } |
| }); |
| |
| test('classifies dependencies from a nested npm shrinkwrap', async () => { |
| const fixture = createFixtureCandidate({ |
| 'tools/source/npm-shrinkwrap.json': `${JSON.stringify({ |
| lockfileVersion: 3, |
| packages: { |
| '': { name: 'source' }, |
| 'node_modules/source-helper': { license: 'MIT', version: '1.0.0' }, |
| }, |
| })}\n`, |
| 'tools/source/package.json': `${JSON.stringify({ |
| dependencies: { 'source-helper': '1.0.0' }, |
| name: 'source', |
| private: true, |
| })}\n`, |
| }); |
| try { |
| await assert.doesNotReject(() => verifySourceCandidate({ archivePath: fixture.archivePath })); |
| } finally { |
| fixture.cleanup(); |
| } |
| }); |
| |
| test('rejects unsupported nested package manager lockfiles', async () => { |
| for (const [name, contents] of [ |
| ['pnpm-lock.yaml', 'lockfileVersion: 9\n'], |
| ['yarn.lock', '# yarn lockfile v1\n'], |
| ]) { |
| const fixture = createFixtureCandidate({ [`tools/source/${name}`]: contents }); |
| try { |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath: fixture.archivePath }), |
| /Cannot safely classify unsupported package lockfile/, |
| ); |
| } finally { |
| fixture.cleanup(); |
| } |
| } |
| }); |
| |
| test('rejects Category X and unknown nested package manifest licenses', async () => { |
| for (const [license, expected] of [ |
| ['AGPL-3.0-only', /Category X/], |
| ['LicenseRef-Unknown', /Cannot safely classify license/], |
| ]) { |
| const fixture = createFixtureCandidate({ |
| 'tools/source/package.json': `${JSON.stringify({ license, name: 'source' })}\n`, |
| }); |
| try { |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath: fixture.archivePath }), |
| expected, |
| ); |
| } finally { |
| fixture.cleanup(); |
| } |
| } |
| }); |
| |
| test('rejects compiled artifacts regardless of their file extension', async () => { |
| const formats = new Map([ |
| ['ELF', Buffer.from([0x7f, 0x45, 0x4c, 0x46])], |
| ['Mach-O', Buffer.from([0xfe, 0xed, 0xfa, 0xcf])], |
| ['Mach-O fat little-endian', Buffer.from([0xbe, 0xba, 0xfe, 0xca])], |
| ['Mach-O fat64 little-endian', Buffer.from([0xbf, 0xba, 0xfe, 0xca])], |
| ['PE', Buffer.from('MZ')], |
| ['WASM', Buffer.from([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00])], |
| ['ZIP/JAR', Buffer.from([0x50, 0x4b, 0x03, 0x04])], |
| ['thin ar archive', Buffer.from('!<thin>\n')], |
| ]); |
| for (const [format, bytes] of formats) { |
| const fixture = createFixtureCandidate({ |
| 'docs/code-origin-audit.md': |
| '### Source archive non-text inventory\n\n- `fixtures/runtime.dat`: a claimed fixture.\n', |
| 'fixtures/runtime.dat': bytes, |
| }); |
| try { |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath: fixture.archivePath }), |
| new RegExp(`Compiled artifact.*fixtures/runtime\\.dat.*${format.replace('/', '\\/')}`), |
| ); |
| } finally { |
| fixture.cleanup(); |
| } |
| } |
| }); |
| |
| test('rejects ZIP payloads hidden behind an allowed source prefix', async () => { |
| for (const bytes of [ |
| Buffer.concat([Buffer.from('source preface\n'), Buffer.from([0x50, 0x4b, 0x03, 0x04])]), |
| Buffer.concat([ |
| Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]), |
| Buffer.from([0x50, 0x4b, 0x03, 0x04]), |
| ]), |
| ]) { |
| const fixture = createFixtureCandidate({ |
| 'docs/code-origin-audit.md': |
| '### Source archive non-text inventory\n\n- `fixtures/runtime.dat`: a claimed fixture.\n', |
| 'fixtures/runtime.dat': bytes, |
| }); |
| try { |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath: fixture.archivePath }), |
| /forbidden ZIP\/JAR content/, |
| ); |
| } finally { |
| fixture.cleanup(); |
| } |
| } |
| }); |
| |
| test('accepts an inventoried source image', async () => { |
| const fixture = createFixtureCandidate({ |
| 'docs/code-origin-audit.md': |
| '### Source archive non-text inventory\n\n- `fixtures/*.png`: test-generated source images.\n', |
| 'fixtures/source.png': Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]), |
| }); |
| try { |
| await assert.doesNotReject(() => verifySourceCandidate({ archivePath: fixture.archivePath })); |
| } finally { |
| fixture.cleanup(); |
| } |
| }); |
| |
| test('accepts an inventoried non-text source fixture', async () => { |
| const fixture = createFixtureCandidate({ |
| 'docs/code-origin-audit.md': |
| '### Source archive non-text inventory\n\n- `fixtures/control.ts`: a raw control-character fixture.\n', |
| 'fixtures/control.ts': Buffer.from([0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x78, 0x00]), |
| }); |
| try { |
| await assert.doesNotReject(() => verifySourceCandidate({ archivePath: fixture.archivePath })); |
| } finally { |
| fixture.cleanup(); |
| } |
| }); |
| |
| test('rejects unknown non-text release inputs', async () => { |
| const fixture = createFixtureCandidate({ 'fixtures/unknown.bin': Buffer.from([0x01, 0x02]) }); |
| try { |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath: fixture.archivePath }), |
| /Cannot safely classify non-text release input.*fixtures\/unknown\.bin/, |
| ); |
| } finally { |
| fixture.cleanup(); |
| } |
| }); |
| |
| test('rejects unknown binary paths even when their bytes are valid UTF-8', async () => { |
| const fixture = createFixtureCandidate({ |
| 'fixtures/unknown.bin': Buffer.from('printable but still an unknown binary input'), |
| }); |
| try { |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath: fixture.archivePath }), |
| /Cannot safely classify non-text release input.*fixtures\/unknown\.bin/, |
| ); |
| } finally { |
| fixture.cleanup(); |
| } |
| }); |
| |
| test('keeps provenance inventory entries inside their Markdown section', async () => { |
| const fixture = createFixtureCandidate({ |
| 'docs/code-origin-audit.md': |
| '### Source archive non-text inventory\n\n## Another section\n\n- `fixtures/unknown.bin`: outside the inventory.\n', |
| 'fixtures/unknown.bin': Buffer.from([0x01, 0x02]), |
| }); |
| try { |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath: fixture.archivePath }), |
| /Cannot safely classify non-text release input.*fixtures\/unknown\.bin/, |
| ); |
| } finally { |
| fixture.cleanup(); |
| } |
| }); |
| |
| test('rejects provenance patterns without a fixed path prefix', async () => { |
| const fixture = createFixtureCandidate({ |
| 'docs/code-origin-audit.md': |
| '### Source archive non-text inventory\n\n- `**`: overbroad inventory.\n', |
| 'fixtures/unknown.bin': Buffer.from([0x01, 0x02]), |
| }); |
| try { |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath: fixture.archivePath }), |
| /Unsafe source archive inventory pattern/, |
| ); |
| } finally { |
| fixture.cleanup(); |
| } |
| }); |
| |
| test('allows a double-star inventory to match zero nested directories', async () => { |
| const fixture = createFixtureCandidate({ |
| 'docs/code-origin-audit.md': |
| '### Source archive non-text inventory\n\n- `docs/images/**/*.png`: reviewed screenshots.\n', |
| 'docs/images/root.png': Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]), |
| }); |
| try { |
| await assert.doesNotReject(() => verifySourceCandidate({ archivePath: fixture.archivePath })); |
| } finally { |
| fixture.cleanup(); |
| } |
| }); |
| |
| test('filters controlled environment names case-insensitively', async () => { |
| await withEnvironmentVariables( |
| { gIt_Prefix_Probe: 'prefix', mAkA_Exact_Probe: 'exact' }, |
| () => { |
| const environment = controlledProcessEnvironment({ |
| excludedNames: ['MAKA_EXACT_PROBE'], |
| excludedPrefixes: ['GIT_'], |
| overrides: { GIT_CONFIG_NOSYSTEM: '1' }, |
| }); |
| const names = Object.keys(environment).map((name) => name.toUpperCase()); |
| assert.equal(names.includes('MAKA_EXACT_PROBE'), false); |
| assert.equal(names.includes('GIT_PREFIX_PROBE'), false); |
| assert.equal(environment.GIT_CONFIG_NOSYSTEM, '1'); |
| }, |
| ); |
| }); |
| |
| test('requires a signature before reading unauthenticated candidate metadata', async () => { |
| const temporaryRoot = mkdtempSync(join(tmpdir(), 'maka-asf-auth-order-test-')); |
| const identity = sourceCandidateIdentity('0.1.12'); |
| const archivePath = join(temporaryRoot, identity.archiveName); |
| try { |
| writeFileSync(archivePath, 'not a tar archive\n'); |
| const keysPath = join(temporaryRoot, 'KEYS'); |
| writeFileSync(keysPath, ''); |
| |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath, keysPath }), |
| /Detached signature does not exist/, |
| ); |
| } finally { |
| rmSync(temporaryRoot, { force: true, recursive: true }); |
| } |
| }); |
| |
| test('documents the checkout-only DeepSeek Harness toolchain build in the candidate', async () => { |
| const temporaryRoot = mkdtempSync(join(tmpdir(), 'maka-asf-dsh-contract-test-')); |
| const repositoryRoot = join(temporaryRoot, 'repository'); |
| const outputDirectory = join(temporaryRoot, 'release'); |
| const identity = sourceCandidateIdentity('0.1.12'); |
| try { |
| writeReleaseContents(repositoryRoot, { includeAttributes: true }); |
| mkdirSync(join(repositoryRoot, 'packages/eval/harbor/deepseek-harness-toolchain'), { |
| recursive: true, |
| }); |
| mkdirSync(join(repositoryRoot, 'packages/eval/harbor/deepseek-harness-profile'), { |
| recursive: true, |
| }); |
| mkdirSync(join(repositoryRoot, 'scripts'), { recursive: true }); |
| copyFileSync( |
| join(import.meta.dirname, '../packages/eval/README.md'), |
| join(repositoryRoot, 'packages/eval/README.md'), |
| ); |
| writeFileSync( |
| join(repositoryRoot, 'packages/eval/harbor/deepseek-harness-toolchain/package.json'), |
| '{}\n', |
| ); |
| writeFileSync( |
| join(repositoryRoot, 'packages/eval/harbor/deepseek-harness-toolchain/package-lock.json'), |
| '{}\n', |
| ); |
| writeFileSync( |
| join(repositoryRoot, 'scripts/prepare-deepseek-harness-toolchain.mjs'), |
| 'export {};\n', |
| ); |
| for (const name of ['package.json', 'cordis.patch.yml']) { |
| copyFileSync( |
| join(import.meta.dirname, `../packages/eval/harbor/deepseek-harness-profile/${name}`), |
| join(repositoryRoot, `packages/eval/harbor/deepseek-harness-profile/${name}`), |
| ); |
| } |
| |
| git(repositoryRoot, ['init']); |
| git(repositoryRoot, ['add', '.']); |
| commitFixture(repositoryRoot, 'test DSH source archive contract'); |
| |
| const candidate = await createSourceCandidate({ |
| outputDirectory, |
| repositoryRoot, |
| version: '0.1.12', |
| }); |
| const entries = execFileSync('tar', ['-tzf', candidate.archivePath], { encoding: 'utf8' }); |
| const archivedReadme = execFileSync( |
| 'tar', |
| ['-xOzf', candidate.archivePath, `${identity.rootDirectory}/packages/eval/README.md`], |
| { encoding: 'utf8' }, |
| ); |
| |
| assert.doesNotMatch(entries, /deepseek-harness-toolchain\/package(?:-lock)?\.json/); |
| assert.match(entries, /scripts\/prepare-deepseek-harness-toolchain\.mjs/); |
| for (const name of ['package.json', 'cordis.patch.yml']) { |
| const relativePath = `packages/eval/harbor/deepseek-harness-profile/${name}`; |
| assert.deepEqual( |
| execFileSync('tar', [ |
| '-xOzf', |
| candidate.archivePath, |
| `${identity.rootDirectory}/${relativePath}`, |
| ]), |
| readFileSync(join(repositoryRoot, relativePath)), |
| ); |
| } |
| assert.match(archivedReadme, /only from a complete Git checkout/u); |
| assert.match(archivedReadme, /intentionally excluded from ASF source archives/u); |
| } finally { |
| rmSync(temporaryRoot, { force: true, recursive: true }); |
| } |
| }); |
| |
| test('creates reproducible candidates from committed files only', async () => { |
| const temporaryRoot = mkdtempSync(join(tmpdir(), 'maka-asf-source-test-')); |
| const repositoryRoot = join(temporaryRoot, 'repository'); |
| const firstOutput = join(temporaryRoot, 'first'); |
| const hostileOutput = join(temporaryRoot, 'hostile'); |
| mkdirSync(repositoryRoot, { recursive: true }); |
| try { |
| writeReleaseContents(repositoryRoot, { includeAttributes: true }); |
| writeFileSync(join(repositoryRoot, '.gitignore'), 'untracked.txt\n'); |
| writeFileSync(join(repositoryRoot, 'README.md'), 'release fixture\n'.repeat(4096)); |
| writeFileSync(join(repositoryRoot, 'untracked.txt'), 'must not be released\n'); |
| mkdirSync(join(repositoryRoot, '.claude')); |
| mkdirSync(join(repositoryRoot, '.maka-shots')); |
| writeFileSync(join(repositoryRoot, '.claude/launch.json'), '{}\n'); |
| writeFileSync(join(repositoryRoot, '.maka-shots/review.png'), 'review evidence\n'); |
| writeFileSync(join(repositoryRoot, 'maka-proposal-zh-review.txt'), 'working notes\n'); |
| mkdirSync(join(repositoryRoot, 'packages/eval/harbor/deepseek-harness-toolchain'), { |
| recursive: true, |
| }); |
| mkdirSync(join(repositoryRoot, 'packages/eval/harbor/deepseek-harness-profile'), { |
| recursive: true, |
| }); |
| writeFileSync( |
| join(repositoryRoot, 'packages/eval/harbor/deepseek-harness-toolchain/package-lock.json'), |
| '{}\n', |
| ); |
| writeFileSync( |
| join(repositoryRoot, 'packages/eval/harbor/deepseek-harness-toolchain/package.json'), |
| '{}\n', |
| ); |
| writeFileSync( |
| join( |
| repositoryRoot, |
| 'packages/eval/harbor/deepseek-harness-toolchain/patch-subprocess-local.mjs', |
| ), |
| 'export {};\n', |
| ); |
| writeFileSync( |
| join(repositoryRoot, 'packages/eval/harbor/deepseek-harness-profile/cordis.yml'), |
| 'profile: maka-eval\n', |
| ); |
| |
| git(repositoryRoot, ['init']); |
| git(repositoryRoot, [ |
| 'add', |
| 'package.json', |
| 'package-lock.json', |
| 'DISCLAIMER-WIP', |
| 'LICENSE', |
| 'NOTICE', |
| '.gitignore', |
| '.gitattributes', |
| 'README.md', |
| '.claude/launch.json', |
| '.maka-shots/review.png', |
| 'maka-proposal-zh-review.txt', |
| 'packages/eval/harbor/deepseek-harness-toolchain/package-lock.json', |
| 'packages/eval/harbor/deepseek-harness-toolchain/package.json', |
| 'packages/eval/harbor/deepseek-harness-toolchain/patch-subprocess-local.mjs', |
| 'packages/eval/harbor/deepseek-harness-profile/cordis.yml', |
| ]); |
| git(repositoryRoot, [ |
| '-c', |
| 'user.name=ASF Release Test', |
| '-c', |
| 'user.email=release-test@example.invalid', |
| 'commit', |
| '-m', |
| 'test fixture', |
| ]); |
| |
| const first = await createSourceCandidate({ |
| outputDirectory: firstOutput, |
| repositoryRoot, |
| version: '0.1.12', |
| }); |
| await assert.rejects( |
| () => |
| createSourceCandidate({ |
| outputDirectory: firstOutput, |
| repositoryRoot, |
| version: '0.1.12', |
| }), |
| /Refusing to overwrite existing release output/, |
| ); |
| git(repositoryRoot, ['config', 'tar.umask', '0077']); |
| writeFileSync(join(repositoryRoot, '.git/info/attributes'), 'README.md export-ignore\n'); |
| |
| const ambientTemplate = join(temporaryRoot, 'ambient-template'); |
| mkdirSync(join(ambientTemplate, 'info'), { recursive: true }); |
| writeFileSync(join(ambientTemplate, 'info/attributes'), 'README.md export-ignore\n'); |
| |
| const ambientRepository = join(temporaryRoot, 'ambient-repository'); |
| git(temporaryRoot, ['clone', '--quiet', repositoryRoot, ambientRepository]); |
| writeFileSync(join(ambientRepository, 'README.md'), 'ambient repository\n'); |
| git(ambientRepository, ['add', 'README.md']); |
| git(ambientRepository, [ |
| '-c', |
| 'user.name=ASF Release Test', |
| '-c', |
| 'user.email=release-test@example.invalid', |
| 'commit', |
| '-m', |
| 'change ambient repository', |
| ]); |
| const hostile = await withEnvironmentVariables( |
| { |
| GIT_DIR: join(ambientRepository, '.git'), |
| GIT_TEMPLATE_DIR: ambientTemplate, |
| GZIP: '-l', |
| }, |
| () => |
| createSourceCandidate({ |
| outputDirectory: hostileOutput, |
| repositoryRoot, |
| version: '0.1.12', |
| }), |
| ); |
| assert.deepEqual(readFileSync(first.archivePath), readFileSync(hostile.archivePath)); |
| assert.equal(hostile.commit, first.commit); |
| assert.deepEqual( |
| readFileSync(`${first.archivePath}.sha512`), |
| readFileSync(`${hostile.archivePath}.sha512`), |
| ); |
| await assert.doesNotReject(() => verifySourceCandidate({ archivePath: first.archivePath })); |
| |
| const entries = execFileSync('tar', ['-tzf', basename(first.archivePath)], { |
| cwd: dirname(first.archivePath), |
| encoding: 'utf8', |
| }); |
| assert.doesNotMatch(entries, /untracked\.txt/); |
| assert.doesNotMatch(entries, /\.claude|\.maka-shots|maka-proposal-zh-review/); |
| assert.doesNotMatch(entries, /deepseek-harness-toolchain\/package(?:-lock)?\.json/); |
| assert.match(entries, /deepseek-harness-toolchain\/patch-subprocess-local\.mjs/); |
| assert.match(entries, /deepseek-harness-profile\/cordis\.yml/); |
| assert.match(entries, /README\.md/); |
| |
| const originalCompressedBytes = readFileSync(first.archivePath); |
| rewriteArchiveCompression(first.archivePath, 1); |
| assert.notDeepEqual(readFileSync(first.archivePath), originalCompressedBytes); |
| await assert.doesNotReject(() => |
| reproduceSourceCandidate({ |
| archivePath: first.archivePath, |
| repositoryRoot, |
| revision: first.commit, |
| }), |
| ); |
| writeFileSync(join(repositoryRoot, 'README.md'), 'different committed payload\n'); |
| git(repositoryRoot, ['add', 'README.md']); |
| commitFixture(repositoryRoot, 'change candidate payload'); |
| await assert.rejects( |
| () => |
| reproduceSourceCandidate({ |
| archivePath: first.archivePath, |
| repositoryRoot, |
| revision: 'HEAD', |
| }), |
| /Candidate source payload does not match/, |
| ); |
| |
| const gpgHome = join(temporaryRoot, 'gnupg'); |
| const keysPath = join(temporaryRoot, 'KEYS'); |
| const fingerprint = generateSigningKey({ |
| algorithm: 'rsa2048', |
| gpgHome, |
| identity: 'ASF Release Test <release-test@example.invalid>', |
| signingSubkeyAlgorithm: 'rsa2048', |
| usage: 'cert', |
| }); |
| writeFileSync(join(gpgHome, 'gpg.conf'), 'digest-algo SHA1\n'); |
| await assert.doesNotReject(() => |
| signSourceCandidate({ |
| archivePath: first.archivePath, |
| gpgHome, |
| keyFingerprint: fingerprint, |
| repositoryRoot, |
| revision: first.commit, |
| }), |
| ); |
| exportPublicKey({ fingerprint, gpgHome, keysPath }); |
| await assert.doesNotReject(() => |
| verifySourceCandidate({ archivePath: first.archivePath, keysPath }), |
| ); |
| |
| rmSync(`${first.archivePath}.asc`); |
| execFileSync( |
| 'gpg', |
| [ |
| '--batch', |
| '--homedir', |
| gpgHome, |
| '--digest-algo', |
| 'SHA512', |
| '--detach-sign', |
| '--output', |
| `${first.archivePath}.asc`, |
| first.archivePath, |
| ], |
| { stdio: 'ignore' }, |
| ); |
| const checksum = readFileSync(`${first.archivePath}.sha512`); |
| writeFileSync(`${first.archivePath}.sha512`, 'not a checksum\n'); |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath: first.archivePath, keysPath }), |
| /ASCII-armored/, |
| ); |
| writeFileSync(`${first.archivePath}.sha512`, checksum); |
| |
| rmSync(`${first.archivePath}.asc`); |
| execFileSync( |
| 'gpg', |
| [ |
| '--batch', |
| '--homedir', |
| gpgHome, |
| '--armor', |
| '--digest-algo', |
| 'SHA1', |
| '--detach-sign', |
| '--output', |
| `${first.archivePath}.asc`, |
| first.archivePath, |
| ], |
| { stdio: 'ignore' }, |
| ); |
| await assert.rejects( |
| () => verifySourceCandidate({ archivePath: first.archivePath, keysPath }), |
| /must use SHA-256, SHA-384, or SHA-512/, |
| ); |
| |
| rmSync(`${first.archivePath}.asc`); |
| // Keep the homedir short enough for GPG agent socket paths on macOS. |
| const ed25519Home = join(temporaryRoot, 'ed'); |
| const ed25519Fingerprint = generateSigningKey({ |
| algorithm: 'ed25519', |
| gpgHome: ed25519Home, |
| identity: 'ASF Ed25519 Release Test <release-test@example.invalid>', |
| }); |
| await assert.rejects( |
| () => |
| signSourceCandidate({ |
| archivePath: first.archivePath, |
| gpgHome: ed25519Home, |
| keyFingerprint: ed25519Fingerprint, |
| repositoryRoot, |
| revision: first.commit, |
| }), |
| /must be RSA with at least 2048 bits/, |
| ); |
| assert.equal(existsSync(`${first.archivePath}.asc`), false); |
| |
| const rsa1024Home = join(temporaryRoot, 'r'); |
| const rsa1024Fingerprint = generateSigningKey({ |
| algorithm: 'rsa2048', |
| gpgHome: rsa1024Home, |
| identity: 'ASF RSA-1024 Subkey Test <release-test@example.invalid>', |
| signingSubkeyAlgorithm: 'rsa1024', |
| usage: 'cert', |
| }); |
| await assert.rejects( |
| () => |
| signSourceCandidate({ |
| archivePath: first.archivePath, |
| gpgHome: rsa1024Home, |
| keyFingerprint: rsa1024Fingerprint, |
| repositoryRoot, |
| revision: first.commit, |
| }), |
| /must be RSA with at least 2048 bits/, |
| ); |
| assert.equal(existsSync(`${first.archivePath}.asc`), false); |
| } finally { |
| rmSync(temporaryRoot, { force: true, recursive: true }); |
| } |
| }); |
| |
| test('does not let ambient tar options control archive verification', async () => { |
| const temporaryRoot = mkdtempSync(join(tmpdir(), 'maka-asf-tar-options-test-')); |
| const identity = sourceCandidateIdentity('0.1.12'); |
| const sourceRoot = join(temporaryRoot, identity.rootDirectory); |
| const archivePath = join(temporaryRoot, identity.archiveName); |
| try { |
| writeReleaseContents(sourceRoot); |
| mkdirSync(join(sourceRoot, '.agents'), { recursive: true }); |
| writeFileSync(join(sourceRoot, '.agents/secret.txt'), 'must not be released\n'); |
| execFileSync('tar', ['-czf', archivePath, identity.rootDirectory], { |
| cwd: temporaryRoot, |
| stdio: 'ignore', |
| }); |
| const digest = createHash('sha512').update(readFileSync(archivePath)).digest('hex'); |
| writeFileSync(`${archivePath}.sha512`, `${digest} ${identity.archiveName}\n`); |
| |
| for (const [name, value] of Object.entries({ |
| GZIP: '-l', |
| TAR_OPTIONS: `--exclude=${identity.rootDirectory}/.agents`, |
| TAR_READER_OPTIONS: 'tar:hdrcharset=BOGUS', |
| })) { |
| await withEnvironmentVariables({ [name]: value }, () => |
| assert.rejects(() => verifySourceCandidate({ archivePath }), /Forbidden archive entry/), |
| ); |
| } |
| } finally { |
| rmSync(temporaryRoot, { force: true, recursive: true }); |
| } |
| }); |
| }); |
| |
| function git(repositoryRoot, arguments_) { |
| execFileSync('git', arguments_, { cwd: repositoryRoot, stdio: 'ignore' }); |
| } |
| |
| function writeReleaseContents(root, { includeAttributes = false } = {}) { |
| mkdirSync(root, { recursive: true }); |
| writeFileSync( |
| join(root, 'package.json'), |
| `${JSON.stringify({ license: 'Apache-2.0', name: 'maka', private: true, version: '0.1.12' }, null, 2)}\n`, |
| ); |
| writeFileSync( |
| join(root, 'package-lock.json'), |
| `${JSON.stringify({ lockfileVersion: 3, name: 'maka', packages: { '': { name: 'maka', version: '0.1.12' } }, version: '0.1.12' }, null, 2)}\n`, |
| ); |
| writeFileSync( |
| join(root, 'DISCLAIMER-WIP'), |
| 'Apache Maka is undergoing incubation at The Apache Software Foundation.\n', |
| ); |
| writeFileSync(join(root, 'LICENSE'), 'Apache License, Version 2.0\n'); |
| writeFileSync(join(root, 'NOTICE'), 'Apache Maka\n'); |
| if (includeAttributes) { |
| copyFileSync(join(import.meta.dirname, '../.gitattributes'), join(root, '.gitattributes')); |
| } |
| } |
| |
| function createFixtureCandidate(files = {}) { |
| const temporaryRoot = mkdtempSync(join(tmpdir(), 'maka-asf-candidate-test-')); |
| const identity = sourceCandidateIdentity('0.1.12'); |
| const sourceRoot = join(temporaryRoot, identity.rootDirectory); |
| const archivePath = join(temporaryRoot, identity.archiveName); |
| writeReleaseContents(sourceRoot); |
| for (const [relativePath, contents] of Object.entries(files)) { |
| const path = join(sourceRoot, relativePath); |
| mkdirSync(dirname(path), { recursive: true }); |
| writeFileSync(path, contents); |
| } |
| execFileSync('tar', ['-czf', archivePath, identity.rootDirectory], { |
| cwd: temporaryRoot, |
| stdio: 'ignore', |
| }); |
| const digest = createHash('sha512').update(readFileSync(archivePath)).digest('hex'); |
| writeFileSync(`${archivePath}.sha512`, `${digest} ${identity.archiveName}\n`); |
| return { |
| archivePath, |
| cleanup: () => rmSync(temporaryRoot, { force: true, recursive: true }), |
| }; |
| } |
| |
| function commitFixture(repositoryRoot, message) { |
| git(repositoryRoot, [ |
| '-c', |
| 'user.name=ASF Release Test', |
| '-c', |
| 'user.email=release-test@example.invalid', |
| 'commit', |
| '-m', |
| message, |
| ]); |
| } |
| |
| async function withEnvironmentVariables(values, callback) { |
| const previous = new Map(Object.keys(values).map((name) => [name, process.env[name]])); |
| Object.assign(process.env, values); |
| try { |
| return await callback(); |
| } finally { |
| for (const [name, value] of previous) { |
| if (value === undefined) delete process.env[name]; |
| else process.env[name] = value; |
| } |
| } |
| } |
| |
| function rewriteArchiveCompression(archivePath, level) { |
| const tarPath = archivePath.slice(0, -3); |
| execFileSync('gzip', ['-d', archivePath], { stdio: 'ignore' }); |
| execFileSync('gzip', ['-n', `-${level}`, tarPath], { stdio: 'ignore' }); |
| const digest = createHash('sha512').update(readFileSync(archivePath)).digest('hex'); |
| writeFileSync(`${archivePath}.sha512`, `${digest} ${basename(archivePath)}\n`); |
| } |
| |
| function generateSigningKey({ |
| algorithm, |
| gpgHome, |
| identity, |
| signingSubkeyAlgorithm, |
| usage = 'sign', |
| }) { |
| mkdirSync(gpgHome, { mode: 0o700 }); |
| chmodSync(gpgHome, 0o700); |
| execFileSync( |
| 'gpg', |
| [ |
| '--batch', |
| '--homedir', |
| gpgHome, |
| '--pinentry-mode', |
| 'loopback', |
| '--passphrase', |
| '', |
| '--quick-generate-key', |
| identity, |
| algorithm, |
| usage, |
| '1d', |
| ], |
| { stdio: 'ignore' }, |
| ); |
| const fingerprint = execFileSync( |
| 'gpg', |
| ['--batch', '--homedir', gpgHome, '--with-colons', '--fingerprint', '--list-secret-keys'], |
| { encoding: 'utf8' }, |
| ) |
| .split(/\r?\n/) |
| .find((line) => line.startsWith('fpr:')) |
| ?.split(':')[9]; |
| assert.match(fingerprint, /^[0-9A-F]{40}$/); |
| if (signingSubkeyAlgorithm) { |
| execFileSync( |
| 'gpg', |
| [ |
| '--batch', |
| '--homedir', |
| gpgHome, |
| '--pinentry-mode', |
| 'loopback', |
| '--passphrase', |
| '', |
| '--quick-add-key', |
| fingerprint, |
| signingSubkeyAlgorithm, |
| 'sign', |
| '1d', |
| ], |
| { stdio: 'ignore' }, |
| ); |
| } |
| return fingerprint; |
| } |
| |
| function exportPublicKey({ fingerprint, gpgHome, keysPath }) { |
| execFileSync( |
| 'gpg', |
| ['--batch', '--homedir', gpgHome, '--armor', '--output', keysPath, '--export', fingerprint], |
| { stdio: 'ignore' }, |
| ); |
| } |