chore: establish YCode replication baseline and TODO
This commit is contained in:
commit
6dc59f89ba
1118 files changed
+196002
No files matched your search
@@ -0,0 +1,225 @@
|
||||
import { createHash } from 'node:crypto'
|
||||
import path from 'node:path'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
|
||||
const MAX_SELECTED_FILES = 16
|
||||
const MAX_FILE_BYTES = 8 * 1024 * 1024
|
||||
const MAX_TOTAL_BYTES = 16 * 1024 * 1024
|
||||
const SHA256_PATTERN = /^[a-f0-9]{64}$/u
|
||||
|
||||
function assertCanonicalSelectedPath(value, binary = false) {
|
||||
if (typeof value !== 'string' || value.length === 0) {
|
||||
throw new TypeError('Selected paths must be non-empty relative strings')
|
||||
}
|
||||
if (
|
||||
value.startsWith('/') ||
|
||||
value.endsWith('/') ||
|
||||
value.includes('\\') ||
|
||||
value.includes('\0') ||
|
||||
/^[a-zA-Z]:/u.test(value) ||
|
||||
value.split('/').some((part) => part === '' || part === '.' || part === '..') ||
|
||||
path.posix.normalize(value) !== value
|
||||
) {
|
||||
throw new TypeError(`Selected path is not canonical: ${value}`)
|
||||
}
|
||||
if (binary ? !/\.(?:png|jpe?g|gif|webp|svg|woff2?|ttf)$/u.test(value) : !/\.(?:js|css|json|md|txt|yaml|yml|py)$/u.test(value)) {
|
||||
if (binary) throw new TypeError(`Selected binary path must name a supported image or font: ${value}`)
|
||||
throw new TypeError(`Selected path must name a supported UTF-8 text file (.js/.css/.json/.md/.txt/.yaml/.yml/.py): ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
function validateSelection(expectedRootSha256, selectedPaths, binary = false) {
|
||||
if (typeof expectedRootSha256 !== 'string' || !SHA256_PATTERN.test(expectedRootSha256)) {
|
||||
throw new TypeError('Expected ASAR root SHA-256 must be 64 lowercase hexadecimal characters')
|
||||
}
|
||||
if (!Array.isArray(selectedPaths) || selectedPaths.length === 0 || selectedPaths.length > MAX_SELECTED_FILES) {
|
||||
throw new RangeError(`Select between 1 and ${MAX_SELECTED_FILES} exact files`)
|
||||
}
|
||||
|
||||
const paths = [...selectedPaths]
|
||||
const seen = new Set()
|
||||
for (const selectedPath of paths) {
|
||||
assertCanonicalSelectedPath(selectedPath, binary)
|
||||
if (seen.has(selectedPath)) throw new TypeError(`Duplicate selected path: ${selectedPath}`)
|
||||
seen.add(selectedPath)
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
async function loadReaRuntime() {
|
||||
const runtimeRoot = path.resolve(process.env.REA_RUNTIME_ROOT || '.tmp/rea-css-runtime')
|
||||
const runtimeUrl = (relativePath) => pathToFileURL(path.join(runtimeRoot, relativePath)).href
|
||||
const [readerModule, evidenceModule] = await Promise.all([
|
||||
import(runtimeUrl('dist/artifacts/AsarArtifactReader.js')),
|
||||
import(runtimeUrl('dist/domain/evidence.js')),
|
||||
])
|
||||
const { createRequire } = await import('node:module')
|
||||
const requireFromRuntime = createRequire(path.join(runtimeRoot, 'package.json'))
|
||||
const packageMetadata = requireFromRuntime('./package.json')
|
||||
return {
|
||||
AsarArtifactReader: readerModule.AsarArtifactReader,
|
||||
createEvidence: evidenceModule.createEvidence,
|
||||
version: packageMetadata.version,
|
||||
}
|
||||
}
|
||||
|
||||
async function readSelectedEntry(reader, entry, totalBytes, binary = false) {
|
||||
if (entry.kind !== 'file') throw new TypeError(`Selected path is not a regular file: ${entry.path}`)
|
||||
if (!Number.isSafeInteger(entry.declaredSize) || entry.declaredSize < 0) {
|
||||
throw new Error(`Selected file has no valid declared size: ${entry.path}`)
|
||||
}
|
||||
if (entry.declaredSize > MAX_FILE_BYTES) {
|
||||
throw new RangeError(`Selected file exceeds the ${MAX_FILE_BYTES}-byte per-file budget: ${entry.path}`)
|
||||
}
|
||||
if (typeof entry.declaredSha256 !== 'string' || !SHA256_PATTERN.test(entry.declaredSha256)) {
|
||||
throw new Error(`Selected file has no valid declared SHA-256: ${entry.path}`)
|
||||
}
|
||||
|
||||
const chunks = []
|
||||
const digest = createHash('sha256')
|
||||
let bytes = 0
|
||||
const stream = await reader.open(entry)
|
||||
for await (const value of stream) {
|
||||
if (!(Buffer.isBuffer(value) || value instanceof Uint8Array)) {
|
||||
throw new TypeError(`Selected file stream returned non-byte data: ${entry.path}`)
|
||||
}
|
||||
const chunk = Buffer.from(value)
|
||||
bytes += chunk.length
|
||||
if (bytes > MAX_FILE_BYTES || bytes > entry.declaredSize || totalBytes + bytes > MAX_TOTAL_BYTES) {
|
||||
throw new RangeError(`Selected text exceeds its declared size or aggregate budget: ${entry.path}`)
|
||||
}
|
||||
digest.update(chunk)
|
||||
chunks.push(chunk)
|
||||
}
|
||||
|
||||
if (bytes !== entry.declaredSize) {
|
||||
throw new Error(`Selected file size mismatch for ${entry.path}: declared ${entry.declaredSize}, read ${bytes}`)
|
||||
}
|
||||
const actualSha256 = digest.digest('hex')
|
||||
if (actualSha256 !== entry.declaredSha256) {
|
||||
throw new Error(`Selected file SHA-256 mismatch for ${entry.path}`)
|
||||
}
|
||||
|
||||
if (binary) return { path: entry.path, size_bytes: bytes, sha256: actualSha256, unpacked: entry.unpacked,
|
||||
encoding: 'base64', data_base64: Buffer.concat(chunks, bytes).toString('base64') }
|
||||
let text
|
||||
try {
|
||||
text = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(Buffer.concat(chunks, bytes))
|
||||
} catch (cause) {
|
||||
throw new Error(`Selected file is not strict UTF-8: ${entry.path}`, { cause })
|
||||
}
|
||||
|
||||
return {
|
||||
path: entry.path,
|
||||
size_bytes: bytes,
|
||||
sha256: actualSha256,
|
||||
unpacked: entry.unpacked,
|
||||
text,
|
||||
}
|
||||
}
|
||||
|
||||
/** Export bounded UTF-8 text from exact selected files in a root-pinned ASAR. */
|
||||
export async function exportSelectedArtifactText(inputPath, expectedRootSha256, selectedPaths, binary = false) {
|
||||
if (typeof inputPath !== 'string' || inputPath.length === 0) {
|
||||
throw new TypeError('ASAR input path must be a non-empty string')
|
||||
}
|
||||
const paths = validateSelection(expectedRootSha256, selectedPaths, binary)
|
||||
const { AsarArtifactReader, createEvidence, version } = await loadReaRuntime()
|
||||
const reader = new AsarArtifactReader(inputPath)
|
||||
let primaryFailure
|
||||
let primaryFailed = false
|
||||
let evidence
|
||||
|
||||
try {
|
||||
await reader.prepareContainer(expectedRootSha256)
|
||||
|
||||
const wanted = new Set(paths)
|
||||
const entries = new Map()
|
||||
for await (const entry of reader.entries()) {
|
||||
if (!wanted.has(entry.path)) continue
|
||||
if (entries.has(entry.path)) throw new Error(`ASAR inventory contains a duplicate path: ${entry.path}`)
|
||||
entries.set(entry.path, entry)
|
||||
}
|
||||
|
||||
for (const selectedPath of paths) {
|
||||
if (!entries.has(selectedPath)) throw new Error(`Selected path is absent from the ASAR inventory: ${selectedPath}`)
|
||||
}
|
||||
|
||||
let declaredTotalBytes = 0
|
||||
for (const selectedPath of paths) {
|
||||
const entry = entries.get(selectedPath)
|
||||
if (!Number.isSafeInteger(entry.declaredSize) || entry.declaredSize < 0) {
|
||||
throw new Error(`Selected file has no valid declared size: ${selectedPath}`)
|
||||
}
|
||||
if (entry.declaredSize > MAX_FILE_BYTES) {
|
||||
throw new RangeError(`Selected file exceeds the ${MAX_FILE_BYTES}-byte per-file budget: ${selectedPath}`)
|
||||
}
|
||||
declaredTotalBytes += entry.declaredSize
|
||||
if (declaredTotalBytes > MAX_TOTAL_BYTES) {
|
||||
throw new RangeError(`Selected files exceed the ${MAX_TOTAL_BYTES}-byte aggregate budget`)
|
||||
}
|
||||
}
|
||||
|
||||
const files = []
|
||||
let actualTotalBytes = 0
|
||||
for (const selectedPath of paths) {
|
||||
const file = await readSelectedEntry(reader, entries.get(selectedPath), actualTotalBytes, binary)
|
||||
actualTotalBytes += file.size_bytes
|
||||
files.push(file)
|
||||
}
|
||||
|
||||
const unselectedScope = 'Not read; unpacked payload content and availability are unknown.'
|
||||
evidence = createEvidence(
|
||||
{ path: inputPath, sha256: expectedRootSha256, format: 'asar' },
|
||||
{
|
||||
id: binary ? 'rea-selected-asar-binary' : 'rea-selected-asar-text',
|
||||
name: binary ? 'REA selected ASAR binary adapter' : 'REA selected ASAR text adapter',
|
||||
version,
|
||||
},
|
||||
{
|
||||
predicateType: binary ? 'rea.artifact.selected-binary' : 'rea.artifact.selected-text',
|
||||
operation: binary ? 'export_selected_asar_artifact_binary' : 'export_selected_asar_artifact_text',
|
||||
parameters: {
|
||||
expected_root_sha256: expectedRootSha256,
|
||||
selected_paths: paths,
|
||||
max_file_bytes: MAX_FILE_BYTES,
|
||||
max_total_bytes: MAX_TOTAL_BYTES,
|
||||
},
|
||||
result: {
|
||||
root_sha256: expectedRootSha256,
|
||||
scope: {
|
||||
kind: 'selected-files-only',
|
||||
selected_paths: paths,
|
||||
unselected_members: unselectedScope,
|
||||
},
|
||||
files,
|
||||
},
|
||||
confidence: 'observed',
|
||||
authority: 'shipped-artifact',
|
||||
limitations: [
|
||||
'Only selected file streams were read and validated.',
|
||||
`Unselected ASAR members were not opened; ${unselectedScope}`,
|
||||
],
|
||||
locations: paths.map((selectedPath) => ({ kind: 'artifact-path', path: selectedPath })),
|
||||
},
|
||||
)
|
||||
} catch (error) {
|
||||
primaryFailure = error
|
||||
primaryFailed = true
|
||||
}
|
||||
|
||||
try {
|
||||
await reader.close()
|
||||
} catch (cleanupFailure) {
|
||||
if (primaryFailed) {
|
||||
throw new AggregateError(
|
||||
[primaryFailure, cleanupFailure],
|
||||
'Selected ASAR text export failed and reader cleanup also failed',
|
||||
{ cause: primaryFailure },
|
||||
)
|
||||
}
|
||||
throw cleanupFailure
|
||||
}
|
||||
if (primaryFailed) throw primaryFailure
|
||||
return evidence
|
||||
}
|
||||
Reference in new issue
Block a user