diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 7f065c87b..652f414ff 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -17,10 +17,12 @@ The bundled font files are redistributed unmodified. ARSAS does not rename the I ## External intellectual-property boundary -No source code, binary, header, generated binding, wrapper, example, test, API layer, executable, manual, brochure, help file, screenshot, icon, logo, product photo, report template, UI resource, database, capture, or extracted asset from an unrelated external implementation or proprietary engineering product is included or directly required by this application repository. +Project policy prohibits including source code, binary, header, generated binding, wrapper, example, test, API layer, executable, manual, brochure, help file, screenshot, icon, logo, product photo, report template, UI resource, database, capture, or extracted asset copied from an unrelated external implementation or proprietary engineering product. Interoperability testing with separately licensed tools does not make those tools application dependencies and does not authorize copying their software, documentation, visual design, reports, resources, or confidential data. +Tracked visual and font assets are inventoried in [docs/ASSET_PROVENANCE_REGISTER.md](docs/ASSET_PROVENANCE_REGISTER.md) and its machine-readable manifest. An inventory entry or passing source-clean check is not a certification of authorship, license clearance, or visual independence; unresolved origin/rights review remains explicitly recorded there. + ## Assets and releases All application icons, screenshots, illustrations, UI resources, and marketing images included in a release must be project-owned or separately licensed for that use. Screenshots must be generated from ARSAS itself using synthetic or sanitized data. diff --git a/docs/ASSET_PROVENANCE_REGISTER.md b/docs/ASSET_PROVENANCE_REGISTER.md index 8ed09f5f8..1cbe3d636 100644 --- a/docs/ASSET_PROVENANCE_REGISTER.md +++ b/docs/ASSET_PROVENANCE_REGISTER.md @@ -6,10 +6,12 @@ This register supports the [independent implementation and provenance policy](IN ## Audited snapshot -- Repository: `masarray/arsas`; tracked recursive Git tree at `d656aa4faf141ede084f2063bca3c091ca1021e5` (2026-09-25). -- Full recursive tree response: not truncated; 1,026 tracked blobs. +- Repository: `masarray/arsas`; tracked recursive Git tree at `61ad333c2f61784ee49fda04b47f036206265cbc` (2026-09-25). +- Full recursive tree response: not truncated; 1,028 tracked blobs. - Extension inventory: 67 `.png`, `.jpg`/`.jpeg`, `.webp`, `.ico`, `.svg`, `.ttf`, `.otf`, `.woff2` or `.gif` blobs. - This is a *path and Git-blob metadata inventory*, not an image-content, source-license, hidden-metadata, or legal review. The separately maintained source-clean gate scans tracked paths and supported text contents; it does not establish binary-image provenance. +- The machine-readable [per-file asset manifest](asset-provenance-manifest.json) records all 67 tracked asset paths, their Git blob SHA, byte size, exact-duplicate relationship and explicit review status. There are 44 unique asset blobs; 23 duplicate groups are byte-for-byte deployments of the same Git blob at two paths. +- CI validates that every tracked asset in this extension scope is represented and that its blob SHA still matches the manifest. Changing or adding an asset therefore requires an explicit provenance-manifest update rather than silently entering the tree. | Category | Count | Scope | Origin/rights disposition | | --- | ---: | --- | --- | diff --git a/docs/asset-provenance-manifest.json b/docs/asset-provenance-manifest.json new file mode 100644 index 000000000..cdc9c83f5 --- /dev/null +++ b/docs/asset-provenance-manifest.json @@ -0,0 +1,481 @@ +{ + "schema_version": 1, + "audited_repository": "masarray/arsas", + "audited_commit": "61ad333c2f61784ee49fda04b47f036206265cbc", + "audited_date": "2026-09-25", + "scope": "Git-tracked binary visual/font assets with extensions png,jpg,jpeg,webp,ico,svg,ttf,otf,woff2,gif", + "disclaimer": "Inventory and Git identity only. This manifest does not certify originality, license clearance, or freedom from resemblance. Manual origin/rights/content review remains required where disposition says so.", + "tracked_asset_count": 67, + "unique_blob_count": 44, + "files": [ + { + "path": "Assets/Fonts/Inter-Bold.ttf", + "blob_sha": "9fb9b751e5b2441054f6eef670da7b3f53a3505a", + "size_bytes": 420428, + "duplicate_of": null, + "review_status": "third-party-font-notice-present; exact-upstream-package-review-required" + }, + { + "path": "Assets/Fonts/Inter-Medium.ttf", + "blob_sha": "458cd0601d28013d3817481aa2839c9bbb5ded70", + "size_bytes": 417300, + "duplicate_of": null, + "review_status": "third-party-font-notice-present; exact-upstream-package-review-required" + }, + { + "path": "Assets/Fonts/Inter-Regular.ttf", + "blob_sha": "b7aaca8de1fe458399e17311d35d543a1fa2f984", + "size_bytes": 411640, + "duplicate_of": null, + "review_status": "third-party-font-notice-present; exact-upstream-package-review-required" + }, + { + "path": "Assets/Fonts/Inter-SemiBold.ttf", + "blob_sha": "47f8ab1d68144fc004b310d65d95180d502b449b", + "size_bytes": 419744, + "duplicate_of": null, + "review_status": "third-party-font-notice-present; exact-upstream-package-review-required" + }, + { + "path": "Assets/RelayFascia.svg", + "blob_sha": "9a69c173b9efdbf8f622f630e4cbeb1f5318699a", + "size_bytes": 11506, + "duplicate_of": null, + "review_status": "origin-rights-and-visual-similarity-review-required" + }, + { + "path": "Assets/app-icon-256.png", + "blob_sha": "fd8f941baa44811e97be408663c7e54b57ad85a1", + "size_bytes": 12891, + "duplicate_of": null, + "review_status": "origin-rights-and-visual-similarity-review-required" + }, + { + "path": "Assets/app-icon.ico", + "blob_sha": "cc9036ccfb9b387891d9887b4367262b7d154bca", + "size_bytes": 208630, + "duplicate_of": null, + "review_status": "origin-rights-and-visual-similarity-review-required" + }, + { + "path": "Assets/app-icon.png", + "blob_sha": "7b69ac247a7214d15437a69814f4036cedebc86e", + "size_bytes": 153165, + "duplicate_of": null, + "review_status": "origin-rights-and-visual-similarity-review-required" + }, + { + "path": "Assets/black-fascia-ied.svg", + "blob_sha": "0c4594a762fe5f13df6ff7b005def3bc7b5cffd7", + "size_bytes": 14334, + "duplicate_of": null, + "review_status": "origin-rights-and-visual-similarity-review-required" + }, + { + "path": "Assets/gateway-hero.png", + "blob_sha": "cb1141207830159642a8f73f981bccbce94e840e", + "size_bytes": 1658537, + "duplicate_of": null, + "review_status": "origin-rights-and-visual-similarity-review-required" + }, + { + "path": "Assets/ied-protection-relay-fascia.png", + "blob_sha": "8d47aee5707eadd373489bc7670da9d99aa24238", + "size_bytes": 1131989, + "duplicate_of": null, + "review_status": "origin-rights-and-visual-similarity-review-required" + }, + { + "path": "Assets/screenshot/arsas (1).webp", + "blob_sha": "4ff8ea73132e1616e585f7020068dfcae29dcf77", + "size_bytes": 116714, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas (2).webp", + "blob_sha": "275e7b3e64478c48a517b0665a3642cc78da4e37", + "size_bytes": 134920, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas (3).webp", + "blob_sha": "8a873301cd7b7dfc1eb2502bf1f4371bc0d7e897", + "size_bytes": 126090, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas (4).webp", + "blob_sha": "3f242ce2c1dd945395cf70c54d92e0eedeaf9fbe", + "size_bytes": 140392, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas (5).webp", + "blob_sha": "dde93be42222734bbccb9f724119e12dcc08b7df", + "size_bytes": 138672, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas (6).webp", + "blob_sha": "5bed62792b9c383b99886df2024524b6d5d69db5", + "size_bytes": 142298, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas-alarm-annunciator-v1.6.40.webp", + "blob_sha": "29b9b3866ce8b29b401df8ffde0cd9239b623b5d", + "size_bytes": 133632, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas-comtrade-harmonics-v1.6.40.webp", + "blob_sha": "7d8484b8ed7e613ff283ad6fdcfdadb04a56d977", + "size_bytes": 85884, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas-comtrade-locus-v1.6.40.webp", + "blob_sha": "b1a6bcb05222d491164b42fb8710d227e9de4e17", + "size_bytes": 64714, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas-comtrade-phasor-v1.6.40.webp", + "blob_sha": "b820739847f984aff27d656387084efa2a90b53b", + "size_bytes": 77708, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas-comtrade-protection-timeline-v1.6.40.webp", + "blob_sha": "5a5c5aef148d5bb4bb0fae4d04ab742b93e4f036", + "size_bytes": 75964, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas-comtrade-time-signals-v1.6.40.webp", + "blob_sha": "d9f4f7c4b7eeb1b18b059772592032ac4342021f", + "size_bytes": 87612, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas-fat-report-preview-v1.6.40.webp", + "blob_sha": "5d2a566f4b2854a8469ec2fd4821d86fe5438c54", + "size_bytes": 49254, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas-fault-records-v1.6.40.webp", + "blob_sha": "882b626dbc260b2846f946257c32a1aeea8c288f", + "size_bytes": 55328, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas-ied-explorer-command-v1.6.40.webp", + "blob_sha": "3f1878f290454f278ab97a56f5683a8efbbb0a4d", + "size_bytes": 137946, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas-landing-home-desktop-v1.6.19.png", + "blob_sha": "1c7c76eb8df9de60c5be0fe84977a9ea926d0e3b", + "size_bytes": 104657, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas-landing-home-mobile-v1.6.19.png", + "blob_sha": "9fefd8a0503fbe17abbb7bcba2b0c013ddd48b8a", + "size_bytes": 38921, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas-native-fat-v1.6.40.webp", + "blob_sha": "f7e47cfe02decd87cb9e53d699611b2d421963dd", + "size_bytes": 166496, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas-overview-v1.6.19.webp", + "blob_sha": "ec0ba89c4f03dc6b29129b92f8032b54cb9be2b9", + "size_bytes": 111848, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas-quick-start-choose-source-v1.6.40.webp", + "blob_sha": "303fbb149a639a3c14511cbe259017788fe51f5a", + "size_bytes": 136494, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas-quick-start-discover-ip-v1.6.40.webp", + "blob_sha": "f63829ff8f49f3badee38dd20431d24671496325", + "size_bytes": 134446, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas-quick-start-monitor-control-v1.6.40.webp", + "blob_sha": "b977d8bb592522678778d146f27b8c12424c260d", + "size_bytes": 98562, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas-rcb-selection-v1.6.40.webp", + "blob_sha": "b1179f8acbc924c95f94d4d7b0c963fdd5db129d", + "size_bytes": 36876, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas-scl-edition-export-v1.6.40.webp", + "blob_sha": "a23c3576823e719a85e8f94e398a82e57fe40755", + "size_bytes": 29244, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/screenshot/arsas-sequence-of-events-v1.6.40.webp", + "blob_sha": "0bb53fa0f39320c57e6e3a812fbe0574b2fe18d9", + "size_bytes": 150598, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "Assets/social/arsas-og-industrial-background.webp", + "blob_sha": "9af8f6e4919fda46bc5ab1496eb6b30c86943335", + "size_bytes": 62230, + "duplicate_of": null, + "review_status": "origin-rights-and-visual-similarity-review-required" + }, + { + "path": "landing/assets/arsas-substation-context.webp", + "blob_sha": "9af8f6e4919fda46bc5ab1496eb6b30c86943335", + "size_bytes": 62230, + "duplicate_of": "Assets/social/arsas-og-industrial-background.webp", + "review_status": "origin-rights-and-visual-similarity-review-required" + }, + { + "path": "landing/assets/favicon.svg", + "blob_sha": "836e0b588a09245f969e6f2e9b9996d433d309f8", + "size_bytes": 547, + "duplicate_of": null, + "review_status": "origin-rights-and-visual-similarity-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-alarm-annunciator-v1.6.40.webp", + "blob_sha": "29b9b3866ce8b29b401df8ffde0cd9239b623b5d", + "size_bytes": 133632, + "duplicate_of": "Assets/screenshot/arsas-alarm-annunciator-v1.6.40.webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-comtrade-harmonics-v1.6.40.webp", + "blob_sha": "7d8484b8ed7e613ff283ad6fdcfdadb04a56d977", + "size_bytes": 85884, + "duplicate_of": "Assets/screenshot/arsas-comtrade-harmonics-v1.6.40.webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-comtrade-locus-v1.6.40.webp", + "blob_sha": "b1a6bcb05222d491164b42fb8710d227e9de4e17", + "size_bytes": 64714, + "duplicate_of": "Assets/screenshot/arsas-comtrade-locus-v1.6.40.webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-comtrade-phasor-v1.6.40.webp", + "blob_sha": "b820739847f984aff27d656387084efa2a90b53b", + "size_bytes": 77708, + "duplicate_of": "Assets/screenshot/arsas-comtrade-phasor-v1.6.40.webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-comtrade-protection-timeline-v1.6.40.webp", + "blob_sha": "5a5c5aef148d5bb4bb0fae4d04ab742b93e4f036", + "size_bytes": 75964, + "duplicate_of": "Assets/screenshot/arsas-comtrade-protection-timeline-v1.6.40.webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-comtrade-time-signals-v1.6.40.webp", + "blob_sha": "d9f4f7c4b7eeb1b18b059772592032ac4342021f", + "size_bytes": 87612, + "duplicate_of": "Assets/screenshot/arsas-comtrade-time-signals-v1.6.40.webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-diagnostics.webp", + "blob_sha": "5bed62792b9c383b99886df2024524b6d5d69db5", + "size_bytes": 142298, + "duplicate_of": "Assets/screenshot/arsas (6).webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-event-log.webp", + "blob_sha": "3f242ce2c1dd945395cf70c54d92e0eedeaf9fbe", + "size_bytes": 140392, + "duplicate_of": "Assets/screenshot/arsas (4).webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-fat-report-preview-v1.6.40.webp", + "blob_sha": "5d2a566f4b2854a8469ec2fd4821d86fe5438c54", + "size_bytes": 49254, + "duplicate_of": "Assets/screenshot/arsas-fat-report-preview-v1.6.40.webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-fault-records-v1.6.40.webp", + "blob_sha": "882b626dbc260b2846f946257c32a1aeea8c288f", + "size_bytes": 55328, + "duplicate_of": "Assets/screenshot/arsas-fault-records-v1.6.40.webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-first-launch.webp", + "blob_sha": "8a873301cd7b7dfc1eb2502bf1f4371bc0d7e897", + "size_bytes": 126090, + "duplicate_of": "Assets/screenshot/arsas (3).webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-goose.webp", + "blob_sha": "dde93be42222734bbccb9f724119e12dcc08b7df", + "size_bytes": 138672, + "duplicate_of": "Assets/screenshot/arsas (5).webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-ied-explorer-command-v1.6.40.webp", + "blob_sha": "3f1878f290454f278ab97a56f5683a8efbbb0a4d", + "size_bytes": 137946, + "duplicate_of": "Assets/screenshot/arsas-ied-explorer-command-v1.6.40.webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-live-values.webp", + "blob_sha": "275e7b3e64478c48a517b0665a3642cc78da4e37", + "size_bytes": 134920, + "duplicate_of": "Assets/screenshot/arsas (2).webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-multi-ied.webp", + "blob_sha": "4ff8ea73132e1616e585f7020068dfcae29dcf77", + "size_bytes": 116714, + "duplicate_of": "Assets/screenshot/arsas (1).webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-native-fat-v1.6.40.webp", + "blob_sha": "f7e47cfe02decd87cb9e53d699611b2d421963dd", + "size_bytes": 166496, + "duplicate_of": "Assets/screenshot/arsas-native-fat-v1.6.40.webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-overview-v1.6.19.webp", + "blob_sha": "dfebe207b911f47e688da65893a83c5cb4105905", + "size_bytes": 101468, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-quick-start-choose-source-v1.6.40.webp", + "blob_sha": "303fbb149a639a3c14511cbe259017788fe51f5a", + "size_bytes": 136494, + "duplicate_of": "Assets/screenshot/arsas-quick-start-choose-source-v1.6.40.webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-quick-start-discover-ip-v1.6.40.webp", + "blob_sha": "f63829ff8f49f3badee38dd20431d24671496325", + "size_bytes": 134446, + "duplicate_of": "Assets/screenshot/arsas-quick-start-discover-ip-v1.6.40.webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-quick-start-monitor-control-v1.6.40.webp", + "blob_sha": "b977d8bb592522678778d146f27b8c12424c260d", + "size_bytes": 98562, + "duplicate_of": "Assets/screenshot/arsas-quick-start-monitor-control-v1.6.40.webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-rcb-scl-export.webp", + "blob_sha": "33625676e8f2adcb3d0c93f56a6b2760a430de4d", + "size_bytes": 130058, + "duplicate_of": null, + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-rcb-selection-v1.6.40.webp", + "blob_sha": "b1179f8acbc924c95f94d4d7b0c963fdd5db129d", + "size_bytes": 36876, + "duplicate_of": "Assets/screenshot/arsas-rcb-selection-v1.6.40.webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-scl-edition-export-v1.6.40.webp", + "blob_sha": "a23c3576823e719a85e8f94e398a82e57fe40755", + "size_bytes": 29244, + "duplicate_of": "Assets/screenshot/arsas-scl-edition-export-v1.6.40.webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/screenshots/arsas-sequence-of-events-v1.6.40.webp", + "blob_sha": "0bb53fa0f39320c57e6e3a812fbe0574b2fe18d9", + "size_bytes": 150598, + "duplicate_of": "Assets/screenshot/arsas-sequence-of-events-v1.6.40.webp", + "review_status": "capture-origin-and-sanitization-review-required" + }, + { + "path": "landing/assets/social-card.png", + "blob_sha": "8708a22b5c0c8f96a9825ad6b39be249e9ddd075", + "size_bytes": 389388, + "duplicate_of": null, + "review_status": "origin-rights-and-visual-similarity-review-required" + }, + { + "path": "landing/assets/social-card.svg", + "blob_sha": "11fc6b3b24db27ab5c231a31920763761cf3bb29", + "size_bytes": 3181, + "duplicate_of": null, + "review_status": "origin-rights-and-visual-similarity-review-required" + }, + { + "path": "output/scl-signal-selection-comparison.png", + "blob_sha": "c3a6ff981b40eb6878da5349fe80d1fbe9eab09f", + "size_bytes": 598548, + "duplicate_of": null, + "review_status": "legacy-output-origin-and-visual-similarity-review-required" + }, + { + "path": "output/scl-signal-selection-modern.png", + "blob_sha": "4f0acfe082833972f84de58c74280ad86991cbd3", + "size_bytes": 65657, + "duplicate_of": null, + "review_status": "legacy-output-origin-and-visual-similarity-review-required" + } + ] +} diff --git a/scripts/test-source-clean-guard.ps1 b/scripts/test-source-clean-guard.ps1 index 9b7448b31..345bbdc5e 100644 --- a/scripts/test-source-clean-guard.ps1 +++ b/scripts/test-source-clean-guard.ps1 @@ -16,6 +16,8 @@ $cases = @( @{ Path = "docs/reference.md"; Text = "# $identifier"; Expected = "text" }, @{ Path = "evidence/fixture.json"; Text = "{`"reference`": `"$identifier`"}"; Expected = "text" }, @{ Path = ".github/workflows/smart-discovery-post-merge-production.yml"; Text = "name: $identifier"; Expected = "text" }, + @{ Path = "NOTICE"; Text = "Policy marker: $identifier"; Expected = "text" }, + @{ Path = ".gitignore"; Text = "# $identifier"; Expected = "text" }, @{ Path = "tests/ARSAS.Tests/SyntheticFixture.cs"; Text = "// $identifier"; Expected = "text" }, @{ Path = "docs/${identifier}-fixture.md"; Text = "# independently generated fixture"; Expected = "path" }, @{ Path = "docs/split-name.md"; Text = $identifier.Substring(0, 3) + " " + $identifier.Substring(3); Expected = "text" }, diff --git a/scripts/verify-asset-provenance-manifest.ps1 b/scripts/verify-asset-provenance-manifest.ps1 new file mode 100644 index 000000000..d1d721623 --- /dev/null +++ b/scripts/verify-asset-provenance-manifest.ps1 @@ -0,0 +1,95 @@ +param( + [string]$RepositoryRoot = (Split-Path -Parent $PSScriptRoot) +) + +$ErrorActionPreference = "Stop" +$RepoRoot = (Resolve-Path -LiteralPath $RepositoryRoot).Path +$ManifestPath = Join-Path $RepoRoot "docs\asset-provenance-manifest.json" + +if (-not (Test-Path -LiteralPath $ManifestPath -PathType Leaf)) { + throw "Asset provenance manifest was not found: $ManifestPath" +} + +$manifest = Get-Content -LiteralPath $ManifestPath -Raw | ConvertFrom-Json +if ($manifest.schema_version -ne 1) { + throw "Unsupported asset provenance manifest schema version: $($manifest.schema_version)" +} + +$extensions = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) +@(".png", ".jpg", ".jpeg", ".webp", ".ico", ".svg", ".ttf", ".otf", ".woff2", ".gif") | + ForEach-Object { [void]$extensions.Add($_) } + +$trackedAssets = @( + & git -C $RepoRoot ls-files | + ForEach-Object { $_.Replace("\", "/") } | + Where-Object { $extensions.Contains([IO.Path]::GetExtension($_)) } | + Sort-Object +) + +if ($LASTEXITCODE -ne 0) { + throw "git ls-files failed while validating asset provenance." +} + +$manifestFiles = @($manifest.files) +if ($manifestFiles.Count -ne $trackedAssets.Count) { + throw "Asset provenance manifest count mismatch. Manifest=$($manifestFiles.Count), tracked=$($trackedAssets.Count)." +} + +$byPath = [System.Collections.Generic.Dictionary[string,object]]::new([System.StringComparer]::Ordinal) +foreach ($entry in $manifestFiles) { + $path = [string]$entry.path + if ([string]::IsNullOrWhiteSpace($path)) { + throw "Asset provenance manifest contains an entry without a path." + } + if ($byPath.ContainsKey($path)) { + throw "Asset provenance manifest contains duplicate path: $path" + } + if ([string]::IsNullOrWhiteSpace([string]$entry.review_status)) { + throw "Asset provenance manifest entry has no review status: $path" + } + $byPath.Add($path, $entry) +} + +foreach ($relative in $trackedAssets) { + if (-not $byPath.ContainsKey($relative)) { + throw "Tracked asset is missing from provenance manifest: $relative" + } + + $entry = $byPath[$relative] + $actualSha = (& git -C $RepoRoot hash-object -- $relative).Trim() + if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($actualSha)) { + throw "Unable to calculate Git blob SHA for tracked asset: $relative" + } + if (-not $actualSha.Equals([string]$entry.blob_sha, [StringComparison]::OrdinalIgnoreCase)) { + throw "Asset provenance blob mismatch for $relative. Manifest=$($entry.blob_sha), actual=$actualSha" + } + + $actualSizeText = (& git -C $RepoRoot cat-file -s $actualSha).Trim() + if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($actualSizeText)) { + throw "Unable to read Git blob size for tracked asset: $relative" + } + $actualSize = [long]$actualSizeText + if ([long]$entry.size_bytes -ne $actualSize) { + throw "Asset provenance size mismatch for $relative. Manifest=$($entry.size_bytes), GitBlob=$actualSize" + } + + $duplicateOf = [string]$entry.duplicate_of + if (-not [string]::IsNullOrWhiteSpace($duplicateOf)) { + if (-not $byPath.ContainsKey($duplicateOf)) { + throw "Asset provenance duplicate_of target does not exist: $relative -> $duplicateOf" + } + if (-not ([string]$byPath[$duplicateOf].blob_sha).Equals([string]$entry.blob_sha, [StringComparison]::OrdinalIgnoreCase)) { + throw "Asset provenance duplicate_of target has a different blob: $relative -> $duplicateOf" + } + } +} + +$uniqueBlobCount = @($manifestFiles | ForEach-Object { [string]$_.blob_sha } | Sort-Object -Unique).Count +if ([int]$manifest.tracked_asset_count -ne $trackedAssets.Count) { + throw "tracked_asset_count does not match manifest entries." +} +if ([int]$manifest.unique_blob_count -ne $uniqueBlobCount) { + throw "unique_blob_count does not match manifest entries." +} + +Write-Host "Asset provenance manifest PASS: $($trackedAssets.Count) tracked paths, $uniqueBlobCount unique Git blobs." -ForegroundColor Green diff --git a/scripts/verify-source-clean.ps1 b/scripts/verify-source-clean.ps1 index ace318e6b..db4d9cb8b 100644 --- a/scripts/verify-source-clean.ps1 +++ b/scripts/verify-source-clean.ps1 @@ -60,6 +60,13 @@ $TextExtensions = @( ".props", ".targets", ".sln", ".slnx", ".txt" ) +$TextFileNames = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) +@(".editorconfig", ".gitattributes", ".gitignore", "CODEOWNERS", "LICENSE", "NOTICE", "VERSION") | + ForEach-Object { [void]$TextFileNames.Add($_) } + +$InternalPatternPolicyFiles = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) +@(".gitignore") | ForEach-Object { [void]$InternalPatternPolicyFiles.Add($_) } + # No tracked path receives a whole-file external-identifier exemption. Historical # comparison evidence is linked by immutable commit rather than copied into active files. $Problems = New-Object System.Collections.Generic.List[string] @@ -179,16 +186,20 @@ foreach ($relative in (Get-TrackedRelativePaths)) { } if ($relative -eq "scripts/verify-source-clean.ps1") { continue } - if ($TextExtensions -notcontains [IO.Path]::GetExtension($relative).ToLowerInvariant()) { continue } + $extension = [IO.Path]::GetExtension($relative).ToLowerInvariant() + $leafName = [IO.Path]::GetFileName($relative) + if ($TextExtensions -notcontains $extension -and -not $TextFileNames.Contains($leafName)) { continue } $content = Get-Content -LiteralPath $fullPath -Raw -ErrorAction SilentlyContinue if (Test-ContainsForbiddenIdentifier $content) { $Problems.Add("Forbidden external identifier in text: $relative") } - foreach ($pattern in $ForbiddenTextPatterns) { - if ($content -match [regex]::Escape($pattern)) { - $Problems.Add("Forbidden internal-release text: $relative") + if (-not $InternalPatternPolicyFiles.Contains($leafName)) { + foreach ($pattern in $ForbiddenTextPatterns) { + if ($content -match [regex]::Escape($pattern)) { + $Problems.Add("Forbidden internal-release text: $relative") + } } } } @@ -201,6 +212,7 @@ if ($Problems.Count -gt 0) { } if (-not $ScanOnly) { + & (Join-Path $PSScriptRoot "verify-asset-provenance-manifest.ps1") -RepositoryRoot $RepoRoot & (Join-Path $PSScriptRoot "verify-fault-record-bindings.ps1") & (Join-Path $PSScriptRoot "verify-auto-update.ps1") }