diff --git a/.gitea/workflows/verify.yml b/.gitea/workflows/verify.yml new file mode 100644 index 0000000..b84a7cb --- /dev/null +++ b/.gitea/workflows/verify.yml @@ -0,0 +1,39 @@ +name: Verify + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +concurrency: + group: verify-${{ gitea.repository }}-${{ gitea.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + verify: + runs-on: ubuntu-latest + timeout-minutes: 45 + env: + CI: "true" + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "22" + cache: npm + - name: Select declared npm version + run: npm install --global npm@11.17.0 + - name: Install dependencies + run: npm ci + - name: Audit runtime dependencies + run: npm audit --omit=dev --audit-level=moderate + - name: Check, test, and build + run: npm run check + - name: Install browser engines + run: npx playwright install --with-deps chromium firefox webkit + - name: Browser tests + run: npm run test:browser diff --git a/CHANGELOG.md b/CHANGELOG.md index 67732a2..15fac0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 0.2.0 - 2026-09-02 + +- Replaced CRC/size equality with bounded on-demand decompressed SHA-256 and + safe semantic text, JSON, XML and manifest diffs. +- Added bounded dependency and licence declarations, signature-companion and + declared-digest diagnostics, plus verified-entry Web Share handoff. +- Declared accurate package input, report/extracted-file output and optional + browser capability profiles for Toolbox handoff. + ## 0.1.0 - 2026-09-01 - Add bounded ZIP/ZIP64 inventory, safe package tree and verified entry reads. diff --git a/README.md b/README.md index dbca3bd..8559599 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ bounded, format-aware interpretation for EPUB, OOXML (`.docx`, `.xlsx`, `.pptx`), OpenDocument (`.odt`, `.ods`, `.odp`), JAR, APK, and Chrome/Firefox extension packages. -Version 0.1.0 can: +Version 0.2.0 can: - inventory paths, sizes, compression, CRC-32, ZIP64 and encryption state; - reject traversal, ambiguous, colliding and special-entry paths; @@ -14,12 +14,21 @@ Version 0.1.0 can: - interpret OOXML content types and internal/external relationships; - inspect EPUB container/package metadata and manifest/spine consistency; - inspect ODF manifests, JAR manifests and WebExtension manifests; -- inventory known signature-material files without asserting validity or trust; +- inventory signature-material pairs and declared digest algorithms without + asserting validity or trust; +- extract bounded dependency declarations from package.json, Python requirements, + Maven metadata and JAR Class-Path, plus conventional licence-file evidence; - flag missing relationship/manifest targets and unreferenced embedded media; - preview bounded text, raster images, audio and video, and download a strictly re-read entry; -- compare two package inventories by path, kind, size, compressed size and CRC; +- compare two packages using bounded, on-demand SHA-256 of verified + decompressed entries; matching metadata/CRC alone remains explicitly + unverified; +- summarize safe bounded JSON, XML, manifest and text differences without + rendering or executing package content; - export an inert JSON inventory report. +- explicitly hand a verified entry to the browser/OS share surface where + supported, with verified download-and-reopen as the local-only fallback. All processing happens in the browser. The app has no telemetry, remote parser, CDN assets or automatic network lookups. @@ -42,11 +51,14 @@ for the complete verification suite. Package Tools is an inspection aid, not a renderer for office/ebook content, an APK resource decoder, a Java class analyser, an extension sandbox, or a signature verifier. It never establishes authenticity or publisher trust. +Dependency declarations are not resolved or vulnerability-checked, and licence +evidence is not a legal interpretation. A chosen OS share target can upload a +file even though Package Tools itself never does. Encrypted entries, split archives, unsafe paths, unsupported compression, and entries beyond the configured byte budgets remain inventory-only. APK v2/v3/v4 signing blocks live outside normal ZIP entries and are not decoded -in v0.1. Chrome CRX wrapper headers are not a guaranteed input format; use an +in v0.2. Chrome CRX wrapper headers are not a guaranteed input format; use an unpacked ZIP-form extension package when needed. ## Licence diff --git a/SOURCE.md b/SOURCE.md index cb9b7a4..2431d29 100644 --- a/SOURCE.md +++ b/SOURCE.md @@ -1,7 +1,7 @@ # Source identity - Project: Package Tools -- Version: 0.1.0 +- Version: 0.2.0 - Canonical repository: https://git.add-ideas.de/lotobo/package-tools - Licence: GPL-3.0-or-later - Toolbox app id: `de.add-ideas.package-tools` diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 0daaf9b..10ca39e 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -5,7 +5,8 @@ never loaded from a CDN. Their licence texts are copied into the production artifact at build time. - React and React DOM — MIT -- add·ideas Toolbox Contract, Shell React and Helpers — GPL-3.0-or-later +- add·ideas Toolbox Contract and Shell React 0.3.0 — Apache-2.0 +- add·ideas Toolbox Helpers 0.2.0 — GPL-3.0-or-later - zip.js — BSD-3-Clause - @xmldom/xmldom — MIT diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 8bb7ebc..af957c7 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -7,12 +7,23 @@ The React workbench is a thin UI over four bounded layers: 2. `src/package/analyze.ts` selects at most 512 metadata files / 16 MiB, determines a package adapter, parses static XML or bounded JSON, and builds references and diagnostics. -3. Tree and comparison modules create deterministic projections without - reading entry payloads. +3. Tree comparison first reports metadata-only candidates as unverified. On an + explicit user action it reopens up to 512 comparable entries within a 128 MiB + expanded-byte budget, verifies container integrity and hashes decompressed + bytes with SHA-256. Differing small JSON, static XML, manifest and text files + receive bounded semantic summaries; DTDs/entities remain rejected. 4. The preview path reopens the package and performs strict CRC/overlap checks for one bounded entry. Markup is text-only; only browser-supported raster image/audio/video blobs receive local object URLs. +Supply-chain inspection is declaration-only: bounded package.json, +requirements, Maven/JAR and conventional licence paths produce evidence rows +without registry access, resolution, vulnerability lookup or legal inference. +Signature inventory correlates companion `.SF`/`.RSA`/`.DSA`/`.EC` entries and +reports declared digest algorithm names, but never verifies signed bytes or a +certificate chain. Explicit entry handoff uses the browser's user-mediated Web +Share surface; verified download is retained as the predictable fallback. + The UI lazy-loads the workbench beneath the shared toolbox shell. The app uses relative URLs so it works under nested portal paths, and a service worker caches same-origin resources after first use. diff --git a/docs/PRIVACY-SECURITY.md b/docs/PRIVACY-SECURITY.md index 34ee809..b10fa6b 100644 --- a/docs/PRIVACY-SECURITY.md +++ b/docs/PRIVACY-SECURITY.md @@ -4,7 +4,7 @@ Files remain in browser memory and are never uploaded. Package Tools performs no telemetry, DNS lookup, external relationship fetch, certificate lookup or signature validation. Export occurs only after an explicit button press. -Limits in v0.1 include 512 MiB source and aggregate declared expansion, 20,000 +Limits in v0.2 include 512 MiB source and aggregate declared expansion, 20,000 entries, 256 MiB per entry, 200:1 compression ratio, 2 MiB per metadata entry, 16 MiB total inspected metadata, and 512 metadata files. UI rendering is capped at 5,000 tree/comparison rows while diagnostics cover the bounded inventory. diff --git a/package-lock.json b/package-lock.json index fe09134..3f9fb48 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,24 +1,24 @@ { "name": "package-tools", - "version": "0.1.0", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "package-tools", - "version": "0.1.0", + "version": "0.2.0", "license": "GPL-3.0-or-later", "dependencies": { - "@add-ideas/toolbox-contract": "0.2.3", - "@add-ideas/toolbox-helpers": "0.1.0", - "@add-ideas/toolbox-shell-react": "0.2.3", + "@add-ideas/toolbox-contract": "0.3.0", + "@add-ideas/toolbox-helpers": "0.2.0", + "@add-ideas/toolbox-shell-react": "0.3.0", "@xmldom/xmldom": "0.9.12", "@zip.js/zip.js": "2.9.0", "react": "19.2.8", "react-dom": "19.2.8" }, "devDependencies": { - "@add-ideas/toolbox-testkit": "0.2.3", + "@add-ideas/toolbox-testkit": "0.3.0", "@eslint/js": "10.0.1", "@playwright/test": "1.62.1", "@testing-library/jest-dom": "6.9.1", @@ -45,24 +45,24 @@ } }, "node_modules/@add-ideas/toolbox-contract": { - "version": "0.2.3", - "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-contract/-/0.2.3/toolbox-contract-0.2.3.tgz", - "integrity": "sha512-T0PVSuMT40GjTDfQJhEEY3ZawQq8zz1/ry95JdKI6W39CdLacaRXdGnEpDCMHt+jUbf1Jz7Nat/M5dFCgKVM9A==", + "version": "0.3.0", + "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-contract/-/0.3.0/toolbox-contract-0.3.0.tgz", + "integrity": "sha512-dKrK7BjOFwqJaBfJuhKxZKIld4sH0AKjEn6a0yLnbdMUFY+fFv4VSLGV2tNSBD016gumc2iNqOjUj/ld7x4rtA==", "license": "Apache-2.0" }, "node_modules/@add-ideas/toolbox-helpers": { - "version": "0.1.0", - "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-helpers/-/0.1.0/toolbox-helpers-0.1.0.tgz", - "integrity": "sha512-UKl1Oxekedf8D2df86VrnVA53AcMhrnh6iUPXY+k8frirBXotb0yd8SGT+IF/3hcqYwcYe/v9WVFuSgKtIYVnw==", + "version": "0.2.0", + "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-helpers/-/0.2.0/toolbox-helpers-0.2.0.tgz", + "integrity": "sha512-SdOqkw+P+3J3fa5iVkzb5P15rVepB001GNV21Oh8w0CZcVL+YRltgD/s+MVcTyrNijWQf3E5vtQON/3N2LLyKg==", "license": "GPL-3.0-or-later" }, "node_modules/@add-ideas/toolbox-shell-react": { - "version": "0.2.3", - "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-shell-react/-/0.2.3/toolbox-shell-react-0.2.3.tgz", - "integrity": "sha512-DT5lQDH48BFkFcmFLZnQh7+Cm73JzBPcmp5WzUXypfkUXpEyDYHzaXgmW4kZ0edSwh4RK4sPmx+JPtK0X4aKCQ==", + "version": "0.3.0", + "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-shell-react/-/0.3.0/toolbox-shell-react-0.3.0.tgz", + "integrity": "sha512-74p6JzAOG0YCAKdlc1hLofV4ZIko7vb448S75cIiM88PKm93EHl5VD7g8YVyfM56Ui97UY9dmy+Whiq4sGzpsg==", "license": "Apache-2.0", "dependencies": { - "@add-ideas/toolbox-contract": "0.2.3" + "@add-ideas/toolbox-contract": "0.3.0" }, "peerDependencies": { "react": ">=18 <20", @@ -70,13 +70,13 @@ } }, "node_modules/@add-ideas/toolbox-testkit": { - "version": "0.2.3", - "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-testkit/-/0.2.3/toolbox-testkit-0.2.3.tgz", - "integrity": "sha512-sq1MwhKWfFKen+N+124hl74qQimRSvmQ9sOU7jdcI+2qCKZ67+2B8rWyezeV80uTFu4Jv6deHksfYQ/tKNV6XQ==", + "version": "0.3.0", + "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-testkit/-/0.3.0/toolbox-testkit-0.3.0.tgz", + "integrity": "sha512-4Fk+oSvZFspOMIXr8Xy040nhAaBsIQAzsGyXWSpjn3+k3yBKq7nB1r5zCHhsXzfdLzvPDAx2KcmSNOhM330D9w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@add-ideas/toolbox-contract": "0.2.3" + "@add-ideas/toolbox-contract": "0.3.0" }, "bin": { "toolbox-check": "dist/cli.js" diff --git a/package.json b/package.json index 0509bfa..3ab44b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "package-tools", - "version": "0.1.0", + "version": "0.2.0", "description": "Inspect and compare compound ZIP packages locally in the browser.", "license": "GPL-3.0-or-later", "author": "Albrecht Degering", @@ -39,16 +39,16 @@ "release:artifact": "npm run check && npm run test:browser && npm run package:release -- --force" }, "dependencies": { - "@add-ideas/toolbox-contract": "0.2.3", - "@add-ideas/toolbox-helpers": "0.1.0", - "@add-ideas/toolbox-shell-react": "0.2.3", + "@add-ideas/toolbox-contract": "0.3.0", + "@add-ideas/toolbox-helpers": "0.2.0", + "@add-ideas/toolbox-shell-react": "0.3.0", "@xmldom/xmldom": "0.9.12", "@zip.js/zip.js": "2.9.0", "react": "19.2.8", "react-dom": "19.2.8" }, "devDependencies": { - "@add-ideas/toolbox-testkit": "0.2.3", + "@add-ideas/toolbox-testkit": "0.3.0", "@eslint/js": "10.0.1", "@playwright/test": "1.62.1", "@testing-library/jest-dom": "6.9.1", diff --git a/playwright.config.ts b/playwright.config.ts index 1997174..f388aab 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -15,7 +15,25 @@ export default defineConfig({ timeout: 180_000, }, projects: [ - { name: "chromium", use: { ...devices["Desktop Chrome"] } }, - { name: "firefox", use: { ...devices["Desktop Firefox"] } }, + { + name: "chromium", + testIgnore: /responsive\.spec\.ts/, + use: { ...devices["Desktop Chrome"] }, + }, + { + name: "firefox", + testIgnore: /responsive\.spec\.ts/, + use: { ...devices["Desktop Firefox"] }, + }, + { + name: "webkit", + testIgnore: /responsive\.spec\.ts/, + use: { ...devices["Desktop Safari"] }, + }, + { + name: "mobile-chromium", + testMatch: /responsive\.spec\.ts/, + use: { ...devices["Pixel 5"] }, + }, ], }); diff --git a/public/CHANGELOG.md b/public/CHANGELOG.md index 67732a2..15fac0a 100644 --- a/public/CHANGELOG.md +++ b/public/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 0.2.0 - 2026-09-02 + +- Replaced CRC/size equality with bounded on-demand decompressed SHA-256 and + safe semantic text, JSON, XML and manifest diffs. +- Added bounded dependency and licence declarations, signature-companion and + declared-digest diagnostics, plus verified-entry Web Share handoff. +- Declared accurate package input, report/extracted-file output and optional + browser capability profiles for Toolbox handoff. + ## 0.1.0 - 2026-09-01 - Add bounded ZIP/ZIP64 inventory, safe package tree and verified entry reads. diff --git a/public/LICENSES/npm-runtime-licenses.txt b/public/LICENSES/npm-runtime-licenses.txt index 718294b..c774062 100644 --- a/public/LICENSES/npm-runtime-licenses.txt +++ b/public/LICENSES/npm-runtime-licenses.txt @@ -1,5 +1,5 @@ ============================================================================== -@add-ideas/toolbox-contract@0.2.3 +@add-ideas/toolbox-contract@0.3.0 Declared licence: Apache-2.0 ============================================================================== --- LICENSE --- @@ -198,7 +198,7 @@ Declared licence: Apache-2.0 ============================================================================== -@add-ideas/toolbox-helpers@0.1.0 +@add-ideas/toolbox-helpers@0.2.0 Declared licence: GPL-3.0-or-later ============================================================================== --- LICENSE --- @@ -879,7 +879,7 @@ Public License instead of this License. But first, please read ============================================================================== -@add-ideas/toolbox-shell-react@0.2.3 +@add-ideas/toolbox-shell-react@0.3.0 Declared licence: Apache-2.0 ============================================================================== --- LICENSE --- diff --git a/public/README.md b/public/README.md index dbca3bd..8559599 100644 --- a/public/README.md +++ b/public/README.md @@ -6,7 +6,7 @@ bounded, format-aware interpretation for EPUB, OOXML (`.docx`, `.xlsx`, `.pptx`), OpenDocument (`.odt`, `.ods`, `.odp`), JAR, APK, and Chrome/Firefox extension packages. -Version 0.1.0 can: +Version 0.2.0 can: - inventory paths, sizes, compression, CRC-32, ZIP64 and encryption state; - reject traversal, ambiguous, colliding and special-entry paths; @@ -14,12 +14,21 @@ Version 0.1.0 can: - interpret OOXML content types and internal/external relationships; - inspect EPUB container/package metadata and manifest/spine consistency; - inspect ODF manifests, JAR manifests and WebExtension manifests; -- inventory known signature-material files without asserting validity or trust; +- inventory signature-material pairs and declared digest algorithms without + asserting validity or trust; +- extract bounded dependency declarations from package.json, Python requirements, + Maven metadata and JAR Class-Path, plus conventional licence-file evidence; - flag missing relationship/manifest targets and unreferenced embedded media; - preview bounded text, raster images, audio and video, and download a strictly re-read entry; -- compare two package inventories by path, kind, size, compressed size and CRC; +- compare two packages using bounded, on-demand SHA-256 of verified + decompressed entries; matching metadata/CRC alone remains explicitly + unverified; +- summarize safe bounded JSON, XML, manifest and text differences without + rendering or executing package content; - export an inert JSON inventory report. +- explicitly hand a verified entry to the browser/OS share surface where + supported, with verified download-and-reopen as the local-only fallback. All processing happens in the browser. The app has no telemetry, remote parser, CDN assets or automatic network lookups. @@ -42,11 +51,14 @@ for the complete verification suite. Package Tools is an inspection aid, not a renderer for office/ebook content, an APK resource decoder, a Java class analyser, an extension sandbox, or a signature verifier. It never establishes authenticity or publisher trust. +Dependency declarations are not resolved or vulnerability-checked, and licence +evidence is not a legal interpretation. A chosen OS share target can upload a +file even though Package Tools itself never does. Encrypted entries, split archives, unsafe paths, unsupported compression, and entries beyond the configured byte budgets remain inventory-only. APK v2/v3/v4 signing blocks live outside normal ZIP entries and are not decoded -in v0.1. Chrome CRX wrapper headers are not a guaranteed input format; use an +in v0.2. Chrome CRX wrapper headers are not a guaranteed input format; use an unpacked ZIP-form extension package when needed. ## Licence diff --git a/public/SOURCE.md b/public/SOURCE.md index cb9b7a4..2431d29 100644 --- a/public/SOURCE.md +++ b/public/SOURCE.md @@ -1,7 +1,7 @@ # Source identity - Project: Package Tools -- Version: 0.1.0 +- Version: 0.2.0 - Canonical repository: https://git.add-ideas.de/lotobo/package-tools - Licence: GPL-3.0-or-later - Toolbox app id: `de.add-ideas.package-tools` diff --git a/public/THIRD_PARTY_NOTICES.md b/public/THIRD_PARTY_NOTICES.md index 0daaf9b..10ca39e 100644 --- a/public/THIRD_PARTY_NOTICES.md +++ b/public/THIRD_PARTY_NOTICES.md @@ -5,7 +5,8 @@ never loaded from a CDN. Their licence texts are copied into the production artifact at build time. - React and React DOM — MIT -- add·ideas Toolbox Contract, Shell React and Helpers — GPL-3.0-or-later +- add·ideas Toolbox Contract and Shell React 0.3.0 — Apache-2.0 +- add·ideas Toolbox Helpers 0.2.0 — GPL-3.0-or-later - zip.js — BSD-3-Clause - @xmldom/xmldom — MIT diff --git a/public/docs/ARCHITECTURE.md b/public/docs/ARCHITECTURE.md index 8bb7ebc..af957c7 100644 --- a/public/docs/ARCHITECTURE.md +++ b/public/docs/ARCHITECTURE.md @@ -7,12 +7,23 @@ The React workbench is a thin UI over four bounded layers: 2. `src/package/analyze.ts` selects at most 512 metadata files / 16 MiB, determines a package adapter, parses static XML or bounded JSON, and builds references and diagnostics. -3. Tree and comparison modules create deterministic projections without - reading entry payloads. +3. Tree comparison first reports metadata-only candidates as unverified. On an + explicit user action it reopens up to 512 comparable entries within a 128 MiB + expanded-byte budget, verifies container integrity and hashes decompressed + bytes with SHA-256. Differing small JSON, static XML, manifest and text files + receive bounded semantic summaries; DTDs/entities remain rejected. 4. The preview path reopens the package and performs strict CRC/overlap checks for one bounded entry. Markup is text-only; only browser-supported raster image/audio/video blobs receive local object URLs. +Supply-chain inspection is declaration-only: bounded package.json, +requirements, Maven/JAR and conventional licence paths produce evidence rows +without registry access, resolution, vulnerability lookup or legal inference. +Signature inventory correlates companion `.SF`/`.RSA`/`.DSA`/`.EC` entries and +reports declared digest algorithm names, but never verifies signed bytes or a +certificate chain. Explicit entry handoff uses the browser's user-mediated Web +Share surface; verified download is retained as the predictable fallback. + The UI lazy-loads the workbench beneath the shared toolbox shell. The app uses relative URLs so it works under nested portal paths, and a service worker caches same-origin resources after first use. diff --git a/public/docs/PRIVACY-SECURITY.md b/public/docs/PRIVACY-SECURITY.md index 34ee809..b10fa6b 100644 --- a/public/docs/PRIVACY-SECURITY.md +++ b/public/docs/PRIVACY-SECURITY.md @@ -4,7 +4,7 @@ Files remain in browser memory and are never uploaded. Package Tools performs no telemetry, DNS lookup, external relationship fetch, certificate lookup or signature validation. Export occurs only after an explicit button press. -Limits in v0.1 include 512 MiB source and aggregate declared expansion, 20,000 +Limits in v0.2 include 512 MiB source and aggregate declared expansion, 20,000 entries, 256 MiB per entry, 200:1 compression ratio, 2 MiB per metadata entry, 16 MiB total inspected metadata, and 512 metadata files. UI rendering is capped at 5,000 tree/comparison rows while diagnostics cover the bounded inventory. diff --git a/public/sw.js b/public/sw.js index 8a8724d..5af6955 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1,4 +1,4 @@ -const CACHE = "package-tools-v0.1.0"; +const CACHE = "package-tools-v0.2.0"; const APP = [ "./", "./index.html", diff --git a/public/toolbox-app.json b/public/toolbox-app.json index cb09d31..6f3a10b 100644 --- a/public/toolbox-app.json +++ b/public/toolbox-app.json @@ -3,12 +3,21 @@ "schemaVersion": 1, "id": "de.add-ideas.package-tools", "name": "Package Tools", - "version": "0.1.0", - "description": "Inspect compound packages locally.", + "version": "0.2.0", + "description": "Inspect, compare and extract compound packages locally.", "entry": "./", "icon": "./favicon.svg", "categories": ["documents", "developer", "forensics"], - "tags": ["zip", "epub", "ooxml", "odf", "jar", "apk", "extension"], + "tags": [ + "zip", + "epub", + "ooxml", + "odf", + "jar", + "apk", + "dependencies", + "signatures" + ], "integration": { "contextVersion": 1, "launchModes": ["navigate", "new-tab"], @@ -21,6 +30,44 @@ "crossOriginIsolated": false, "topLevelContext": false }, + "io": { + "accepts": [ + { + "mediaType": "application/zip", + "extensions": [".zip", ".jar", ".apk", ".xpi"] + }, + { "mediaType": "application/epub+zip", "extensions": [".epub"] }, + { + "mediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "extensions": [".docx"] + }, + { + "mediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "extensions": [".xlsx"] + }, + { + "mediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "extensions": [".pptx"] + }, + { + "mediaType": "application/vnd.oasis.opendocument.text", + "extensions": [".odt"] + }, + { + "mediaType": "application/vnd.oasis.opendocument.spreadsheet", + "extensions": [".ods"] + }, + { + "mediaType": "application/vnd.oasis.opendocument.presentation", + "extensions": [".odp"] + } + ], + "produces": [ + { "mediaType": "application/json", "extensions": [".json"] }, + { "mediaType": "application/octet-stream", "extensions": [] } + ] + }, + "capabilities": { "required": [], "optional": ["web-crypto", "web-share"] }, "privacy": { "processing": "local", "fileUploads": true, diff --git a/src/components/Workbench.tsx b/src/components/Workbench.tsx index 6d49d21..98cdbdc 100644 --- a/src/components/Workbench.tsx +++ b/src/components/Workbench.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { stableStringify, triggerBlobDownload, @@ -7,8 +7,14 @@ import { formatBytes } from "../archive/limits"; import type { ArchiveEntryRecord } from "../archive/types"; import { analyzePackage, entryBlob, previewEntry } from "../package/analyze"; import { compareInventories } from "../package/compare"; +import { verifyPackageContents } from "../package/content-compare"; import { buildPackageTree } from "../package/tree"; -import type { EntryPreview, PackageDocument, TreeNode } from "../package/types"; +import type { + ContentComparisonEvidenceMap, + EntryPreview, + PackageDocument, + TreeNode, +} from "../package/types"; type Tab = | "overview" @@ -17,6 +23,7 @@ type Tab = | "relationships" | "diagnostics" | "signatures" + | "supply-chain" | "compare"; export function Workbench() { @@ -85,10 +92,39 @@ export function Workbench() { } } + async function shareEntry(entry: ArchiveEntryRecord) { + if (!active) return; + try { + const blob = await entryBlob(active, entry); + const file = new File( + [blob], + entry.path.split("/").at(-1) || "package-entry.bin", + { type: blob.type }, + ); + if (!navigator.share || !navigator.canShare?.({ files: [file] })) + throw new Error( + "This browser has no file handoff surface. Download the verified entry and open it explicitly in the target tool.", + ); + await navigator.share({ + files: [file], + title: `Open ${file.name} with another local application`, + }); + setStatus( + `Handed ${entry.path} to the browser's explicit share/open-with surface.`, + ); + } catch (error) { + if (error instanceof DOMException && error.name === "AbortError") { + setStatus("Open-with handoff cancelled."); + return; + } + setStatus(error instanceof Error ? error.message : String(error)); + } + } + function exportReport() { if (!active) return; const report = { - application: "Package Tools 0.1.0", + application: "Package Tools 0.2.0", generatedAt: new Date().toISOString(), package: { name: active.name, @@ -110,6 +146,8 @@ export function Workbench() { diagnostics: active.diagnostics, relationships: active.relationships, signatures: active.signatures, + dependencies: active.dependencies, + licenses: active.licenses, boundary: "Signature files are inventoried without cryptographic validation or trust claims.", }; @@ -195,6 +233,7 @@ export function Workbench() { ["relationships", "Relationships"], ["diagnostics", "Diagnostics"], ["signatures", "Signatures"], + ["supply-chain", "Dependencies & licences"], ...(documents.length > 1 ? [["compare", "Compare"]] : []), ] as [Tab, string][] ).map(([id, label]) => ( @@ -253,6 +292,7 @@ export function Workbench() { preview={preview} status={previewStatus} onDownload={downloadEntry} + onShare={shareEntry} /> )} @@ -266,8 +306,15 @@ export function Workbench() { {active && tab === "signatures" && ( )} + {active && tab === "supply-chain" && ( + + )} {tab === "compare" && documents.length > 1 && ( - + )} )} @@ -422,12 +469,14 @@ function EntryInspector({ preview, status, onDownload, + onShare, }: { document: PackageDocument; entry?: ArchiveEntryRecord; preview?: EntryPreview; status: string; onDownload: (entry: ArchiveEntryRecord) => void; + onShare: (entry: ArchiveEntryRecord) => void; }) { return (