Integrate diagnostic decorations and current-source Text input boundaries
This commit is contained in:
@@ -109,5 +109,68 @@ $bad.packages[0].notices[0].file = 'NOTICE.txt'
|
||||
$null = Write-Fixture 'supplements/NOTICE.txt' 'Synthetic supplemental notice'
|
||||
$null = Write-Fixture 'supplements/manifest.json' ($bad | ConvertTo-Json -Depth 8)
|
||||
Refuses 'Supplement changed'
|
||||
# A patched package is reachable through metadata but deliberately excluded
|
||||
# from workspace_members. Its registry supplement must not license local bytes.
|
||||
$registryNoticeHash = (Get-FileHash -LiteralPath (Join-Path $fixture 'supplements/NOTICE.txt') -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
$registrySupplement = [ordered]@{ name = 'normal'; version = '1.0.0'; checksum = $normalHash; notices = @([ordered]@{ file = 'NOTICE.txt'; sha256 = $registryNoticeHash; url = 'https://example.invalid/test' }) }
|
||||
$staleVendorSupplement = [ordered]@{ name = 'iced_widget'; version = '0.14.2'; checksum = ('b' * 64); notices = @([ordered]@{ file = 'registry-only-must-not-be-read.txt'; sha256 = ('c' * 64); url = 'https://example.invalid/registry-original' }) }
|
||||
$null = Write-Fixture 'supplements/manifest.json' ([ordered]@{ packages = @($registrySupplement, $staleVendorSupplement); assets = @(); runtimes = @() } | ConvertTo-Json -Depth 8)
|
||||
$registryBefore = ((Collect).Inventory.packages | Where-Object name -eq normal).local_source_sha256['src/lib.rs']
|
||||
$vendorManifest = Write-Fixture 'crates/vendor/iced_widget/Cargo.toml' "[package]`nname = `"iced_widget`"`nversion = `"0.14.2`"`nlicense = `"MIT`""
|
||||
$vendorSource = Write-Fixture 'crates/vendor/iced_widget/src/lib.rs' '// synthetic patched widget'
|
||||
$vendorLicense = Write-Fixture 'crates/vendor/iced_widget/LICENSE' 'Synthetic upstream widget license; not the workspace license'
|
||||
$vendorPatch = Write-Fixture 'crates/vendor/iced_widget/ARCADIA-PATCH.md' 'Synthetic exact upstream and patch provenance'
|
||||
$vendor = [pscustomobject]@{ id = 'local-iced-widget'; name = 'iced_widget'; version = '0.14.2'; source = $null; license = 'MIT'; license_file = $null; manifest_path = $vendorManifest; repository = 'https://example.invalid/widget' }
|
||||
$script:FixtureMetadata.packages += $vendor
|
||||
$script:FixtureMetadata.resolve.nodes += [pscustomobject]@{ id = $vendor.id; deps = @(); features = @('advanced') }
|
||||
$desktopNode = $script:FixtureMetadata.resolve.nodes | Where-Object id -eq 'oa-desktop'
|
||||
$desktopNode.deps += [pscustomobject]@{ pkg = $vendor.id; dep_kinds = @([pscustomobject]@{ kind = $null }) }
|
||||
Assert ($script:FixtureMetadata.workspace_members -notcontains $vendor.id) 'Vendor fixture accidentally became a workspace member'
|
||||
$withVendor = Collect
|
||||
$vendorRecord = $withVendor.Inventory.packages | Where-Object name -eq iced_widget
|
||||
Assert ($null -ne $vendorRecord) 'Excluded local dependency was omitted from the resolved notice graph'
|
||||
Assert ($vendorRecord.local_path -eq 'crates/vendor/iced_widget' -and $null -eq $vendorRecord.source -and $null -eq $vendorRecord.registry_checksum) 'Local package was attributed to registry bytes or the wrong repository path'
|
||||
Assert ($vendorRecord.notices.Count -eq 2) 'Vendor own license and patch evidence were not both retained'
|
||||
Assert (@($vendorRecord.notices | Where-Object { $_.path -match 'workspace-LICENSE|/supplement/' }).Count -eq 0) 'Vendor inherited the workspace license or stale registry supplement'
|
||||
foreach ($pair in @(@('Cargo.toml', $vendorManifest), @('src/lib.rs', $vendorSource), @('LICENSE', $vendorLicense), @('ARCADIA-PATCH.md', $vendorPatch))) {
|
||||
$expected = (Get-FileHash -LiteralPath $pair[1] -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
Assert ($vendorRecord.local_source_sha256[$pair[0]] -eq $expected) "Vendor source hash does not describe actual file: $($pair[0])"
|
||||
}
|
||||
foreach ($pair in @(@('LICENSE', $vendorLicense), @('ARCADIA-PATCH.md', $vendorPatch))) {
|
||||
$destination = "licenses/dependencies/iced_widget-0.14.2/$($pair[0])"
|
||||
Assert ($withVendor.Inputs[$destination] -eq $pair[1]) "Vendor notice did not retain actual source path: $($pair[0])"
|
||||
Assert ($withVendor.Hashes[$destination] -eq $vendorRecord.local_source_sha256[$pair[0]]) "Vendor notice hash differs from captured local source: $($pair[0])"
|
||||
}
|
||||
$workspaceRecord = $withVendor.Inventory.packages | Where-Object name -eq 'oa-desktop'
|
||||
Assert (@($workspaceRecord.notices | Where-Object path -match 'workspace-LICENSE$').Count -eq 1) 'Real workspace package lost its workspace license'
|
||||
$registryRecord = $withVendor.Inventory.packages | Where-Object name -eq normal
|
||||
Assert ($registryRecord.registry_checksum -eq $normalHash -and $null -eq $registryRecord.local_path) 'Registry identity changed while adding a local package'
|
||||
Assert (@($registryRecord.notices | Where-Object path -match '/supplement/NOTICE.txt$').Count -eq 1) 'Valid registry supplement was lost'
|
||||
$null = Write-Fixture 'crates/vendor/iced_widget/src/lib.rs' '// changed synthetic patched widget'
|
||||
$changedVendor = Collect
|
||||
$changedRecord = $changedVendor.Inventory.packages | Where-Object name -eq iced_widget
|
||||
Assert ($changedRecord.local_source_sha256['src/lib.rs'] -ne $vendorRecord.local_source_sha256['src/lib.rs']) 'Vendor source mutation was hidden by cached registry identity'
|
||||
Assert (($changedVendor.Inventory.packages | Where-Object name -eq normal).local_source_sha256['src/lib.rs'] -eq $registryBefore) 'Vendor mutation changed unrelated registry source inventory'
|
||||
$cliAfterVendor = Get-ArcadiaNotices $fixture x86_64-pc-windows-msvc $manifestPath '' @('oa-cli')
|
||||
Assert (@($cliAfterVendor.Inventory.packages | Where-Object name -eq iced_widget).Count -eq 0) 'Unreachable vendor leaked into CLI notices'
|
||||
$savedLicense = Join-Path $fixture 'saved-vendor-license'
|
||||
Move-Item -LiteralPath $vendorLicense -Destination $savedLicense
|
||||
try { Refuses 'Local dependency requires its own license text: iced_widget-0.14.2' }
|
||||
finally { Move-Item -LiteralPath $savedLicense -Destination $vendorLicense }
|
||||
# A sibling with the same textual prefix is outside the repository; it need
|
||||
# not exist, since containment must be checked before opening that manifest.
|
||||
$vendor.manifest_path = Join-Path ($fixture + '-outside') 'Cargo.toml'
|
||||
try { Refuses 'Local dependency is outside repository' }
|
||||
finally { $vendor.manifest_path = $vendorManifest }
|
||||
$registrySupplement.checksum = 'd' * 64
|
||||
$null = Write-Fixture 'supplements/manifest.json' ([ordered]@{ packages = @($registrySupplement, $staleVendorSupplement); assets = @(); runtimes = @() } | ConvertTo-Json -Depth 8)
|
||||
Refuses 'Supplement is for different registry source: normal-1.0.0'
|
||||
$registrySupplement.checksum = $normalHash
|
||||
$null = Write-Fixture 'supplements/manifest.json' ([ordered]@{ packages = @($registrySupplement, $staleVendorSupplement); assets = @(); runtimes = @() } | ConvertTo-Json -Depth 8)
|
||||
$null = Write-Fixture 'registry/cache/test/normal-1.0.0.crate' 'Tampered archive with local vendor present'
|
||||
Refuses 'Cached crate archive differs'
|
||||
$null = Write-Fixture 'registry/cache/test/normal-1.0.0.crate' 'Synthetic cached archive normal'
|
||||
$null = Collect
|
||||
Write-Output 'PASS: excluded local package identity, own license and patch evidence, changed source bytes, repository containment, missing vendor license refusal, registry isolation and unchanged archive/supplement checks.'
|
||||
Write-Output 'PASS: normal/build and server-only Linux selection, cross-target runtime refusal, Rust reports, actual source hashes, stale archive refusal, doc-comment font exclusion, production font path/mapping refusals, exact font mapping and changed-byte refusal, unsupported source refusal, traversal refusal, altered supplement refusal.'
|
||||
Write-Output "Synthetic fixture retained for inspection: $fixture"
|
||||
|
||||
@@ -83,7 +83,23 @@ function Get-ArcadiaNotices([string]$Repository, [string]$Target, [string]$Notic
|
||||
}
|
||||
foreach ($id in @($selected | Sort-Object)) {
|
||||
$package = $packages[$id]
|
||||
$root = Split-Path -Parent $package.manifest_path
|
||||
$root = [IO.Path]::GetFullPath((Split-Path -Parent $package.manifest_path)).TrimEnd('\', '/')
|
||||
$isWorkspace = $metadata.workspace_members -contains $id
|
||||
$localPath = $null
|
||||
if (-not $package.source) {
|
||||
$repositoryRoot = [IO.Path]::GetFullPath($Repository).TrimEnd('\', '/')
|
||||
$repositoryPrefix = $repositoryRoot + [IO.Path]::DirectorySeparatorChar
|
||||
$manifestPath = [IO.Path]::GetFullPath($package.manifest_path)
|
||||
$comparison = if ([IO.Path]::DirectorySeparatorChar -eq '/') { [StringComparison]::Ordinal } else { [StringComparison]::OrdinalIgnoreCase }
|
||||
if (-not $manifestPath.StartsWith($repositoryPrefix, $comparison)) { throw "Local dependency is outside repository: $id" }
|
||||
$current = Get-Item -LiteralPath $manifestPath
|
||||
while ($null -ne $current) {
|
||||
if (($current.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0) { throw "Local dependency has a reparse ancestor: $id" }
|
||||
if ($current.FullName.Equals($repositoryRoot, $comparison)) { break }
|
||||
$current = if ($current -is [IO.FileInfo]) { $current.Directory } else { $current.Parent }
|
||||
}
|
||||
$localPath = if ($root.Equals($repositoryRoot, $comparison)) { '.' } else { $root.Substring($repositoryPrefix.Length).Replace('\', '/') }
|
||||
}
|
||||
$key = "$($package.name)-$($package.version)"
|
||||
if ($key -notmatch '^[A-Za-z0-9._+-]+$') { throw "Unsafe package identity: $key" }
|
||||
$prefix = "licenses/dependencies/$key"
|
||||
@@ -101,6 +117,7 @@ function Get-ArcadiaNotices([string]$Repository, [string]$Target, [string]$Notic
|
||||
# Hash actual local files (not merely Cargo's cached checksum claims).
|
||||
$sourceHashes = [ordered]@{}
|
||||
$notices = New-Object 'Collections.Generic.List[object]'
|
||||
$hasLicenseText = $false
|
||||
$fontReferences = New-Object 'Collections.Generic.HashSet[string]'
|
||||
foreach ($file in @(Get-ChildItem -LiteralPath $root -Recurse -File | Sort-Object FullName)) {
|
||||
if (($file.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0) { throw "Source reparse point requires review: $($file.FullName)" }
|
||||
@@ -110,6 +127,10 @@ function Get-ArcadiaNotices([string]$Repository, [string]$Target, [string]$Notic
|
||||
if ($file.Name -match '(^|[-._])(LICENSE|LICENCE|COPYING|NOTICE|OFL|UNLICENSE|COPYRIGHT)([-._]|$)') {
|
||||
$notices.Add((Add-Notice $file.FullName "$prefix/$relative"))
|
||||
}
|
||||
if ($file.Name -match '(^|[-._])(LICENSE|LICENCE|COPYING|OFL|UNLICENSE)([-._]|$)') { $hasLicenseText = $true }
|
||||
if (-not $package.source -and -not $isWorkspace -and $relative -eq 'ARCADIA-PATCH.md') {
|
||||
$notices.Add((Add-Notice $file.FullName "$prefix/$relative"))
|
||||
}
|
||||
# Literal embedded fonts in production/build source, including feature-gated ones
|
||||
# conservatively. Tests/examples/benches are excluded, never all font files by glob.
|
||||
if ($file.Extension -eq '.rs' -and $relative -notmatch '^(tests|examples|benches)/') {
|
||||
@@ -127,8 +148,10 @@ function Get-ArcadiaNotices([string]$Repository, [string]$Target, [string]$Notic
|
||||
}
|
||||
}
|
||||
}
|
||||
if (-not $package.source) { $notices.Add((Add-Notice (Under-Root $Repository 'LICENSE') "$prefix/workspace-LICENSE")) }
|
||||
$matching = @($supplements.packages | Where-Object { $_.name -eq $package.name -and $_.version -eq $package.version })
|
||||
if ($isWorkspace) { $notices.Add((Add-Notice (Under-Root $Repository 'LICENSE') "$prefix/workspace-LICENSE")) }
|
||||
# Registry supplements bind original .crate bytes, not a local patch
|
||||
# with the same name/version and no registry checksum.
|
||||
$matching = @($supplements.packages | Where-Object { $registryChecksum -and $_.name -eq $package.name -and $_.version -eq $package.version })
|
||||
foreach ($supplement in $matching) {
|
||||
if ($supplement.checksum -ne $registryChecksum) { throw "Supplement is for different registry source: $key" }
|
||||
foreach ($notice in @(Add-Supplements $supplement.notices "$prefix/supplement")) { $notices.Add($notice) }
|
||||
@@ -137,7 +160,9 @@ function Get-ArcadiaNotices([string]$Repository, [string]$Target, [string]$Notic
|
||||
if ($package.license_file) {
|
||||
$licensePath = Under-Root $root $package.license_file
|
||||
$notices.Add((Add-Notice $licensePath "$prefix/declared-license/$([IO.Path]::GetFileName($licensePath))"))
|
||||
$hasLicenseText = $true
|
||||
}
|
||||
if (-not $package.source -and -not $isWorkspace -and -not $hasLicenseText) { $failures.Add("Local dependency requires its own license text: $key") }
|
||||
if ($notices.Count -eq 0) { $failures.Add("No notice text: $key ($($package.license)); provide checksum-bound supplement") }
|
||||
$assets = New-Object 'Collections.Generic.List[object]'
|
||||
foreach ($relative in @($fontReferences | Sort-Object)) {
|
||||
@@ -148,7 +173,7 @@ function Get-ArcadiaNotices([string]$Repository, [string]$Target, [string]$Notic
|
||||
if ($assetNotices.Count -eq 0) { $failures.Add("No embedded font notice: $key/$relative") }
|
||||
$assets.Add([ordered]@{ path = $relative; sha256 = $assetHash; license = $matches[0].license; notices = $assetNotices })
|
||||
}
|
||||
$records.Add([ordered]@{ id = $id; name = $package.name; version = $package.version; license = $package.license; license_file = $package.license_file; repository = $package.repository; source = $package.source; registry_checksum = $registryChecksum; features = $nodes[$id].features; local_source_sha256 = $sourceHashes; notices = @($notices.ToArray()); embedded_fonts = @($assets.ToArray()) })
|
||||
$records.Add([ordered]@{ id = $id; name = $package.name; version = $package.version; license = $package.license; license_file = $package.license_file; repository = $package.repository; source = $package.source; local_path = $localPath; registry_checksum = $registryChecksum; features = $nodes[$id].features; local_source_sha256 = $sourceHashes; notices = @($notices.ToArray()); embedded_fonts = @($assets.ToArray()) })
|
||||
}
|
||||
$rustVersion = @(& rustc -Vv)
|
||||
if ($LASTEXITCODE -ne 0) { throw 'Unable to identify the selected Rust compiler.' }
|
||||
|
||||
Reference in New Issue
Block a user