diff --git a/src/artifacts/extraction/ArtifactExtraction.ts b/src/artifacts/extraction/ArtifactExtraction.ts index a309d35f1d..4cd2268319 100644 --- a/src/artifacts/extraction/ArtifactExtraction.ts +++ b/src/artifacts/extraction/ArtifactExtraction.ts @@ -67,15 +67,22 @@ export const extractArtifact = async ( nodes, }; const selected = selectedOccurrences.map((occurrence) => { + // REA never decrypts archive entries, so an encrypted entry makes the + // complete extraction unsupported rather than the archive invalid. + if (occurrence.encrypted) + throw new AnalysisUnsupportedTargetError( + "extract_artifact", + sourcePath, + `Archive entry ${occurrence.logical_path} is encrypted; REA does not decrypt archive entries, so the archive cannot be extracted completely`, + ); if ( (occurrence.entry_kind !== "file" && occurrence.entry_kind !== "slice") || occurrence.artifact_id === null || - occurrence.encrypted || occurrence.logical_path === "." ) throw new ArtifactReaderFailure( "format", - `Selected occurrence is not an extractable regular child file: ${occurrence.occurrence_id}`, + `Selected occurrence is not an extractable regular child file: ${occurrence.logical_path} (${occurrence.occurrence_id})`, ); const node = inventory.nodes.get(occurrence.artifact_id); if (node === undefined) diff --git a/tests/boundary/filesystem/artifactZipIntegrity.test.ts b/tests/boundary/filesystem/artifactZipIntegrity.test.ts index 61b6b47c48..1796f1bc77 100644 --- a/tests/boundary/filesystem/artifactZipIntegrity.test.ts +++ b/tests/boundary/filesystem/artifactZipIntegrity.test.ts @@ -89,6 +89,22 @@ it("preserves encrypted-member and corrupt-CRC observations", async () => { hash_status: "unavailable", }), ); + // REA does not decrypt entries, so complete extraction is unsupported. + const extractionRoot = join(root, "encrypted-output"); + expect( + await new ArtifactProvider() + .createClient(encryptedTarget.value) + .execute("extract_artifact", { output_root: extractionRoot }), + ).toMatchObject({ + ok: false, + error: { + _tag: "AnalysisUnsupportedTargetError", + reason: expect.stringContaining("Archive entry secret.txt is encrypted"), + }, + }); + await expect(readFile(extractionRoot)).rejects.toMatchObject({ + code: "ENOENT", + }); const corruptPath = join(root, "corrupt-crc.zip"); const corruptWriter = new ZipWriter(new Uint8ArrayWriter());