From cd6bfbea57c2437c00f808317f896aacb2ad2440 Mon Sep 17 00:00:00 2001
From: Cai <cai@nbcai.cc>
Date: Thu, 10 Sep 2026 18:16:33 +0800
Subject: [PATCH] chore: 更新2026-09-10股票估值每日台账
---
dev/project-dev/bili_authenticated_extension/install_native_host.ps1 | 552 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 500 insertions(+), 52 deletions(-)
diff --git a/dev/project-dev/bili_authenticated_extension/install_native_host.ps1 b/dev/project-dev/bili_authenticated_extension/install_native_host.ps1
index b491b9f..04d4936 100644
--- a/dev/project-dev/bili_authenticated_extension/install_native_host.ps1
+++ b/dev/project-dev/bili_authenticated_extension/install_native_host.ps1
@@ -22,7 +22,11 @@
[Parameter(DontShow = $true)]
[string]$TestRegistryRoot,
[Parameter(DontShow = $true)]
- [ValidateSet('none', 'after-root', 'after-payload', 'after-config', 'after-manifest', 'after-registry-key', 'after-registry-value')]
+ [string]$TestPreviousInstallRoot,
+ [Parameter(DontShow = $true)]
+ [string]$TestPreviousArtifactReceipt,
+ [Parameter(DontShow = $true)]
+ [ValidateSet('none', 'after-root', 'after-payload', 'after-config', 'after-manifest', 'after-registry-key', 'after-registry-value', 'after-registry-value-mixed')]
[string]$InjectFailure = 'none'
)
@@ -31,9 +35,17 @@
$expectedOrigin = 'chrome-extension://oidmclckpdmpabbfedplkbdplmfcenbb/'
$expectedHostName = 'com.project_info.bili_auth_ingress'
$expectedPublicDerHash = 'E83C2B2AF3CF011543FBA13FBC524D1122EEA68548F9F27B9F7A82B5D594666C'
-$expectedExtensionBuild = 'project-info-bili-auth-ingress/1.0.0+20260805.v002'
-$expectedHostBuild = 'project-info-bili-auth-native-host/1.0.0+20260805.v002'
+$expectedExtensionBuild = 'project-info-bili-auth-ingress/1.2.25+20260829.generic.v027'
+$expectedHostBuild = 'project-info-bili-auth-native-host/1.2.25+20260829.generic.v027'
$expectedHostExecutable = 'project-info-bili-auth-native-host.exe'
+$expectedPreviousExtensionBuild = 'project-info-bili-auth-ingress/1.2.24+20260829.generic.v026'
+$expectedPreviousVersion = '1.2.24+20260829.generic.v026'
+$expectedPreviousManifestBytes = 381
+$expectedPreviousManifestSha256 = '9FFAC5073A5839F030ED321840C5715ECBE2CC7276DD057A7236B3CF78AA5C38'
+$expectedPreviousHostBytes = 21452778
+$expectedPreviousHostSha256 = '24E0C15B9E8269F55F25E55A0614DD96BEF58CB0F7EE069C41F86AF95CD19762'
+$expectedPreviousConfigBytes = 1870
+$expectedPreviousConfigSha256 = 'ACA285C700CD970A846A824BD32E6826195B0204640F8445C288C7476EC475E3'
$publicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt2dT1HGYaI0DXM7zZwOTNBWXTKlMBJMpyDVjRUc+v6bUotmLyoraC+ay2scy9UQluSZVYq0tS8qvQNNvuZOlc5w2bOExm4TH2IIKvaVO8nVthHBnNz2kXdiM8ItN0vPZEmS+8gpTCI1+6wPTuUglMoXpqYBYhii8fJ5RkENRF3PRJBBigGt8soqdBFRY1QZUmpQv9dYw4dRq4L2C4QtBgClUg4bQpuCppiVZ9LHbePi9IAjc9r9R93KLzpaBuXdJpfVRE5w/6YHnxP8ovXxBdl7XktmrdH3xj7mWT7Q7ZBxkDNwn2RkruD45XgDD3yuNxOYSkLFMkseN+Ua69gSS4wIDAQAB'
function Stop-Injected([string]$Point) {
@@ -86,6 +98,26 @@
}
}
+function Assert-ExactSourceSnapshot([string]$Root, [string]$ManifestPath, [string[]]$ExpectedFiles, [object]$Manifest) {
+ Assert-ExactSourceTree $Root $ManifestPath $ExpectedFiles
+ foreach ($entry in $Manifest.files) {
+ if ($entry.path -notmatch '^[A-Za-z0-9._/-]+$' -or $entry.path.Contains('..') -or
+ $entry.sha256 -notmatch '^[A-F0-9]{64}$' -or $entry.bytes -lt 1) {
+ throw 'Source artifact manifest contains an invalid entry.'
+ }
+ $candidate = [System.IO.Path]::GetFullPath((Join-Path $Root $entry.path))
+ if (-not $candidate.StartsWith($Root + [System.IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) {
+ throw 'Source artifact path escaped its root.'
+ }
+ $item = Get-Item -LiteralPath $candidate
+ if (($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
+ $item.Length -ne $entry.bytes -or
+ (Get-FileHash -Algorithm SHA256 -LiteralPath $candidate).Hash -cne $entry.sha256) {
+ throw 'Source artifact hash mismatch.'
+ }
+ }
+}
+
function Copy-CreateNew([string]$Source, [string]$Destination) {
$input = [System.IO.File]::Open($Source, [IO.FileMode]::Open, [IO.FileAccess]::Read, [IO.FileShare]::Read)
try {
@@ -110,6 +142,339 @@
} finally {
$stream.Dispose()
}
+}
+
+function Get-FileIdentity([string]$Path) {
+ $item = Get-Item -LiteralPath $Path -Force
+ if ($item.PSIsContainer -or ($item.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
+ throw 'Expected a regular non-reparse file.'
+ }
+ return [pscustomobject]@{
+ Bytes = $item.Length
+ Sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $Path).Hash
+ }
+}
+
+function ConvertTo-StrictPositiveInt64 {
+ [CmdletBinding(PositionalBinding = $false)]
+ param(
+ [Parameter(Mandatory = $true)]
+ [AllowNull()]
+ [object]$Value,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateSet(
+ 'previous native host manifest bytes',
+ 'previous Host executable bytes',
+ 'previous config bytes'
+ )]
+ [string]$IdentityName
+ )
+ $isIntegralScalar =
+ $Value -is [byte] -or $Value -is [sbyte] -or
+ $Value -is [int16] -or $Value -is [uint16] -or
+ $Value -is [int32] -or $Value -is [uint32] -or
+ $Value -is [int64]
+ if (-not $isIntegralScalar -or [long]$Value -lt 1) {
+ throw "Pinned $IdentityName must be a positive integer scalar."
+ }
+ return [long]$Value
+}
+
+function ConvertTo-StrictSha256 {
+ [CmdletBinding(PositionalBinding = $false)]
+ param(
+ [Parameter(Mandatory = $true)]
+ [AllowNull()]
+ [object]$Value,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateSet(
+ 'previous native host manifest SHA-256',
+ 'previous Host executable SHA-256',
+ 'previous config SHA-256'
+ )]
+ [string]$IdentityName
+ )
+ if ($Value -isnot [string] -or $Value -cnotmatch '^[A-F0-9]{64}$') {
+ throw "Pinned $IdentityName must be an uppercase 64-character hexadecimal string."
+ }
+ return [string]$Value
+}
+
+function Assert-ExactPreviousInstall {
+ [CmdletBinding(PositionalBinding = $false)]
+ param(
+ [Parameter(Mandatory = $true)]
+ [string]$PreviousRoot,
+
+ [Parameter(Mandatory = $true)]
+ [AllowNull()]
+ [object]$ManifestBytes,
+
+ [Parameter(Mandatory = $true)]
+ [AllowNull()]
+ [object]$ManifestSha256,
+
+ [Parameter(Mandatory = $true)]
+ [AllowNull()]
+ [object]$HostBytes,
+
+ [Parameter(Mandatory = $true)]
+ [AllowNull()]
+ [object]$HostSha256,
+
+ [Parameter(Mandatory = $true)]
+ [AllowNull()]
+ [object]$ConfigBytes,
+
+ [Parameter(Mandatory = $true)]
+ [AllowNull()]
+ [object]$ConfigSha256
+ )
+ $pinnedManifestBytes = ConvertTo-StrictPositiveInt64 -Value $ManifestBytes -IdentityName 'previous native host manifest bytes'
+ $pinnedManifestSha256 = ConvertTo-StrictSha256 -Value $ManifestSha256 -IdentityName 'previous native host manifest SHA-256'
+ $pinnedHostBytes = ConvertTo-StrictPositiveInt64 -Value $HostBytes -IdentityName 'previous Host executable bytes'
+ $pinnedHostSha256 = ConvertTo-StrictSha256 -Value $HostSha256 -IdentityName 'previous Host executable SHA-256'
+ $pinnedConfigBytes = ConvertTo-StrictPositiveInt64 -Value $ConfigBytes -IdentityName 'previous config bytes'
+ $pinnedConfigSha256 = ConvertTo-StrictSha256 -Value $ConfigSha256 -IdentityName 'previous config SHA-256'
+
+ $rootItem = Get-Item -LiteralPath $PreviousRoot -Force
+ if (-not $rootItem.PSIsContainer -or ($rootItem.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
+ throw 'Previous install root must be an ordinary non-reparse directory.'
+ }
+ $children = @(Get-ChildItem -LiteralPath $PreviousRoot -Force)
+ if ($children.Count -ne 3 -or @($children | Where-Object { $_.PSIsContainer }).Count -ne 0 -or
+ (Compare-Object -CaseSensitive @('config.json', 'native-host-manifest.json', $expectedHostExecutable) @($children.Name | Sort-Object))) {
+ throw 'Previous install root does not contain the exact three-file preimage.'
+ }
+ foreach ($child in $children) {
+ if ($child.Attributes -band [IO.FileAttributes]::ReparsePoint) {
+ throw 'Previous install contains a reparse path.'
+ }
+ }
+ $manifestPath = Join-Path $PreviousRoot 'native-host-manifest.json'
+ $hostPath = Join-Path $PreviousRoot $expectedHostExecutable
+ $configPath = Join-Path $PreviousRoot 'config.json'
+ $manifestIdentity = Get-FileIdentity $manifestPath
+ $hostIdentity = Get-FileIdentity $hostPath
+ $configIdentity = Get-FileIdentity $configPath
+ if ($manifestIdentity.Bytes -ne $pinnedManifestBytes) {
+ throw 'Previous native host manifest bytes mismatch.'
+ }
+ if ($manifestIdentity.Sha256 -cne $pinnedManifestSha256) {
+ throw 'Previous native host manifest SHA-256 mismatch.'
+ }
+ if ($hostIdentity.Bytes -ne $pinnedHostBytes) {
+ throw 'Previous Host executable bytes mismatch.'
+ }
+ if ($hostIdentity.Sha256 -cne $pinnedHostSha256) {
+ throw 'Previous Host executable SHA-256 mismatch.'
+ }
+ if ($configIdentity.Bytes -ne $pinnedConfigBytes) {
+ throw 'Previous config bytes mismatch.'
+ }
+ if ($configIdentity.Sha256 -cne $pinnedConfigSha256) {
+ throw 'Previous config SHA-256 mismatch.'
+ }
+ $previousManifest = Get-StrictJson $manifestPath
+ if ((Compare-Object @('allowed_origins', 'description', 'name', 'path', 'type') @($previousManifest.PSObject.Properties.Name | Sort-Object)) -or
+ $previousManifest.name -cne $expectedHostName -or
+ $previousManifest.path -cne $hostPath -or
+ $previousManifest.type -cne 'stdio' -or
+ @($previousManifest.allowed_origins).Count -ne 1 -or
+ $previousManifest.allowed_origins[0] -cne $expectedOrigin) {
+ throw 'Previous native host manifest identity mismatch.'
+ }
+ $previousConfig = Get-StrictJson $configPath
+ if ($previousConfig.schema -ne 2 -or
+ $previousConfig.required_extension_build -cne $expectedPreviousExtensionBuild -or
+ @($previousConfig.creator_allowlist).Count -ne 1 -or
+ $previousConfig.creator_allowlist[0] -cne '1420210197') {
+ throw 'Previous host config identity mismatch.'
+ }
+ return [pscustomobject]@{
+ Root = $PreviousRoot
+ ManifestPath = $manifestPath
+ HostPath = $hostPath
+ ConfigPath = $configPath
+ }
+}
+
+function Get-RegistrySnapshot([string]$Path, [bool]$UseFileProvider) {
+ if (-not (Test-Path -LiteralPath $Path)) {
+ return [pscustomobject]@{
+ Exists = $false
+ ValueNames = @()
+ SubKeyNames = @()
+ DefaultKind = $null
+ DefaultValue = $null
+ }
+ }
+ if ($UseFileProvider) {
+ $item = Get-Item -LiteralPath $Path -Force
+ if (-not $item.PSIsContainer -or ($item.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
+ throw 'Test registry key must be an ordinary non-reparse directory.'
+ }
+ $children = @(Get-ChildItem -LiteralPath $Path -Force)
+ if (@($children | Where-Object { $_.Attributes -band [IO.FileAttributes]::ReparsePoint }).Count -ne 0) {
+ throw 'Test registry contains a reparse path.'
+ }
+ $files = @($children | Where-Object { -not $_.PSIsContainer })
+ $subkeys = @($children | Where-Object { $_.PSIsContainer } | ForEach-Object { $_.Name })
+ $valueNames = @($files | ForEach-Object { if ($_.Name -ceq 'default.value') { '' } else { $_.Name } })
+ $defaultPath = Join-Path $Path 'default.value'
+ $defaultValue = $null
+ $defaultKind = $null
+ if (Test-Path -LiteralPath $defaultPath) {
+ $defaultValue = [System.IO.File]::ReadAllText($defaultPath, [System.Text.UTF8Encoding]::new($false, $true))
+ $defaultKind = 'String'
+ }
+ return [pscustomobject]@{
+ Exists = $true
+ ValueNames = $valueNames
+ SubKeyNames = $subkeys
+ DefaultKind = $defaultKind
+ DefaultValue = $defaultValue
+ }
+ }
+ $key = Get-Item -LiteralPath $Path -Force
+ $valueNames = @($key.GetValueNames())
+ $subKeyNames = @($key.GetSubKeyNames())
+ $defaultValue = $null
+ $defaultKind = $null
+ if ($valueNames -contains '') {
+ $defaultValue = $key.GetValue('', $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
+ $defaultKind = $key.GetValueKind('').ToString()
+ }
+ return [pscustomobject]@{
+ Exists = $true
+ ValueNames = $valueNames
+ SubKeyNames = $subKeyNames
+ DefaultKind = $defaultKind
+ DefaultValue = $defaultValue
+ }
+}
+
+function Test-RegistrySnapshotExact([object]$Snapshot, [string]$ExpectedDefault) {
+ return $Snapshot.Exists -and
+ @($Snapshot.ValueNames).Count -eq 1 -and $Snapshot.ValueNames[0] -ceq '' -and
+ @($Snapshot.SubKeyNames).Count -eq 0 -and
+ $Snapshot.DefaultKind -ceq 'String' -and
+ $Snapshot.DefaultValue -ceq $ExpectedDefault
+}
+
+function Test-RegistrySnapshotEmpty([object]$Snapshot) {
+ return $Snapshot.Exists -and @($Snapshot.ValueNames).Count -eq 0 -and @($Snapshot.SubKeyNames).Count -eq 0
+}
+
+function Set-RegistryDefaultAtomic([string]$Path, [string]$Value, [bool]$UseFileProvider) {
+ if ($UseFileProvider) {
+ $defaultPath = Join-Path $Path 'default.value'
+ if (-not (Test-Path -LiteralPath $defaultPath)) {
+ Write-Utf8CreateNew $defaultPath $Value
+ return
+ }
+ $pending = Join-Path $Path ('.default.pending.' + [Guid]::NewGuid().ToString('N'))
+ $backup = Join-Path $Path ('.default.backup.' + [Guid]::NewGuid().ToString('N'))
+ try {
+ Write-Utf8CreateNew $pending $Value
+ [System.IO.File]::Replace($pending, $defaultPath, $backup, $true)
+ } finally {
+ if (Test-Path -LiteralPath $pending) {
+ Remove-Item -LiteralPath $pending -Force
+ }
+ if (Test-Path -LiteralPath $backup) {
+ Remove-Item -LiteralPath $backup -Force
+ }
+ }
+ return
+ }
+ Set-Item -LiteralPath $Path -Value $Value -ErrorAction Stop
+}
+
+function Assert-NewInstallRoot(
+ [string]$InstallRootPath,
+ [string]$InstalledHostPath,
+ [string]$InstalledConfigPath,
+ [string]$InstalledManifestPath,
+ [object]$ExpectedBuildEntry,
+ [object]$ExpectedConfigIdentity
+) {
+ $rootItem = Get-Item -LiteralPath $InstallRootPath -Force
+ if (-not $rootItem.PSIsContainer -or ($rootItem.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
+ throw 'Installed root must be an ordinary non-reparse directory.'
+ }
+ $children = @(Get-ChildItem -LiteralPath $InstallRootPath -Force)
+ if ($children.Count -ne 3 -or @($children | Where-Object { $_.PSIsContainer }).Count -ne 0 -or
+ (Compare-Object -CaseSensitive @('config.json', 'native-host-manifest.json', $expectedHostExecutable) @($children.Name | Sort-Object))) {
+ throw 'Installed root does not contain the exact three-file set.'
+ }
+ foreach ($child in $children) {
+ if ($child.Attributes -band [IO.FileAttributes]::ReparsePoint) {
+ throw 'Installed root contains a reparse path.'
+ }
+ }
+ $hostIdentity = Get-FileIdentity $InstalledHostPath
+ $configIdentity = Get-FileIdentity $InstalledConfigPath
+ if ($hostIdentity.Bytes -ne $ExpectedBuildEntry.bytes -or $hostIdentity.Sha256 -cne $ExpectedBuildEntry.sha256 -or
+ $configIdentity.Bytes -ne $ExpectedConfigIdentity.Bytes -or $configIdentity.Sha256 -cne $ExpectedConfigIdentity.Sha256) {
+ throw 'Installed payload identity mismatch.'
+ }
+ $persistedManifest = Get-StrictJson $InstalledManifestPath
+ if ((Compare-Object @('allowed_origins', 'description', 'name', 'path', 'type') @($persistedManifest.PSObject.Properties.Name | Sort-Object)) -or
+ $persistedManifest.name -cne $expectedHostName -or
+ $persistedManifest.path -cne $InstalledHostPath -or
+ $persistedManifest.type -cne 'stdio' -or
+ @($persistedManifest.allowed_origins).Count -ne 1 -or
+ $persistedManifest.allowed_origins[0] -cne $expectedOrigin) {
+ throw 'Installed native host manifest identity mismatch.'
+ }
+}
+
+function Remove-ProvenOwnedInstallRoot(
+ [string]$InstallRootPath,
+ [string]$InstalledHostPath,
+ [string]$InstalledConfigPath,
+ [string]$InstalledManifestPath,
+ [object]$ExpectedBuildEntry,
+ [object]$ExpectedConfigIdentity
+) {
+ if (-not (Test-Path -LiteralPath $InstallRootPath)) {
+ return
+ }
+ $rootItem = Get-Item -LiteralPath $InstallRootPath -Force
+ if (-not $rootItem.PSIsContainer -or ($rootItem.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
+ throw 'RECOVERY_REQUIRED: target root is not a proven owned ordinary directory.'
+ }
+ $children = @(Get-ChildItem -LiteralPath $InstallRootPath -Force)
+ if (@($children | Where-Object { $_.PSIsContainer -or ($_.Attributes -band [IO.FileAttributes]::ReparsePoint) }).Count -ne 0 -or
+ @($children | Where-Object { $_.Name -cnotin @('config.json', 'native-host-manifest.json', $expectedHostExecutable) }).Count -ne 0) {
+ throw 'RECOVERY_REQUIRED: target root contains an unknown or reparse object.'
+ }
+ if (Test-Path -LiteralPath $InstalledHostPath) {
+ $identity = Get-FileIdentity $InstalledHostPath
+ if ($identity.Bytes -ne $ExpectedBuildEntry.bytes -or $identity.Sha256 -cne $ExpectedBuildEntry.sha256) {
+ throw 'RECOVERY_REQUIRED: target host identity is ambiguous.'
+ }
+ }
+ if (Test-Path -LiteralPath $InstalledConfigPath) {
+ $identity = Get-FileIdentity $InstalledConfigPath
+ if ($identity.Bytes -ne $ExpectedConfigIdentity.Bytes -or $identity.Sha256 -cne $ExpectedConfigIdentity.Sha256) {
+ throw 'RECOVERY_REQUIRED: target config identity is ambiguous.'
+ }
+ }
+ if (Test-Path -LiteralPath $InstalledManifestPath) {
+ $persistedManifest = Get-StrictJson $InstalledManifestPath
+ if ($persistedManifest.name -cne $expectedHostName -or
+ $persistedManifest.path -cne $InstalledHostPath -or
+ $persistedManifest.type -cne 'stdio' -or
+ @($persistedManifest.allowed_origins).Count -ne 1 -or
+ $persistedManifest.allowed_origins[0] -cne $expectedOrigin) {
+ throw 'RECOVERY_REQUIRED: target manifest identity is ambiguous.'
+ }
+ }
+ Remove-Item -LiteralPath $InstallRootPath -Recurse -Force
}
if ($ObservedExtensionId -cne $expectedId) {
@@ -195,14 +560,15 @@
'__init__.py', 'background.js', 'build_host.ps1', 'config.example.json',
'constants.py', 'dependencies/dependency-artifact-manifest.json',
'dependencies/yt_dlp-2026.7.4-py3-none-any.whl',
- 'install_native_host.ps1', 'job.py', 'manifest.json',
+ 'formal_legacy_identity_manifest.py', 'install_native_host.ps1', 'job.py', 'manifest.json',
'native-host-manifest.template.json', 'native_host.py', 'protocol.py',
+ 'queue-producer.example.json', 'queue_producer.py', 'queue_state.py',
'sidepanel.css', 'sidepanel.html', 'sidepanel.js', 'worker.py'
)
-Assert-ExactSourceTree $sourceRoot $sourceManifestPath $expectedSourceFiles
+Assert-ExactSourceSnapshot $sourceRoot $sourceManifestPath $expectedSourceFiles $sourceManifest
$dependencyManifestPath = Join-Path $sourceRoot 'dependencies/dependency-artifact-manifest.json'
$dependencyManifestItem = Get-Item -LiteralPath $dependencyManifestPath
-if ($sourceManifest.schema -ne 1 -or $sourceManifest.target -cne 'BV1HA3o6oEJJ' -or
+if ($sourceManifest.schema -ne 1 -or $sourceManifest.scope -cne 'generic-bilibili-queue' -or
$sourceManifest.extension_id -cne $expectedId -or
$sourceManifest.extension_build -cne $expectedExtensionBuild -or
$sourceManifest.host_build -cne $expectedHostBuild -or
@@ -253,7 +619,7 @@
$buildManifest = Get-StrictJson $buildManifestPath
$buildScript = Join-Path $sourceRoot 'build_host.ps1'
-if ($buildManifest.schema -ne 2 -or $buildManifest.target -cne 'BV1HA3o6oEJJ' -or
+if ($buildManifest.schema -ne 2 -or $buildManifest.scope -cne 'generic-bilibili-queue' -or
$buildManifest.extension_id -cne $expectedId -or
$buildManifest.extension_build -cne $expectedExtensionBuild -or
$buildManifest.host_build -cne $expectedHostBuild -or
@@ -315,21 +681,26 @@
if (($configItem.Attributes -band [IO.FileAttributes]::ReparsePoint) -or $configItem.PSIsContainer) {
throw 'Config file must be a regular non-reparse file.'
}
+$configIdentity = Get-FileIdentity $config
$configObject = Get-StrictJson $config
$expectedConfigKeys = @(
- 'schema', 'target', 'canonical_url', 'ffmpeg', 'ffmpeg_sha256', 'ffprobe',
- 'ffprobe_sha256', 'bridge_python', 'bridge_python_sha256', 'bridge_script',
- 'bridge_script_sha256', 'batch_json', 'batch_json_sha256', 'yt_dlp_executable',
- 'yt_dlp_executable_sha256', 'destination'
+ 'schema', 'creator_allowlist', 'queue_path', 'queue_state_path', 'queue_lock_path',
+ 'reload_state_path', 'reload_generation', 'required_extension_build',
+ 'ffmpeg', 'ffmpeg_sha256', 'ffprobe', 'ffprobe_sha256', 'bridge_python',
+ 'bridge_python_sha256', 'bridge_script', 'bridge_script_sha256',
+ 'yt_dlp_executable', 'yt_dlp_executable_sha256', 'destination',
+ 'creator_name', 'formal_manifest_path', 'processing_handoff_path'
)
$actualConfigKeys = @($configObject.PSObject.Properties.Name | Sort-Object)
if ((Compare-Object ($expectedConfigKeys | Sort-Object) $actualConfigKeys) -or
- $configObject.schema -ne 1 -or $configObject.target -cne 'BV1HA3o6oEJJ' -or
- $configObject.canonical_url -cne 'https://www.bilibili.com/video/BV1HA3o6oEJJ' -or
- $configObject.bridge_script_sha256 -cne '749FC486B0F42315BD463F11771FE2A7C71CAB53DD9AC2E411CE82E1175DFF13') {
+ $configObject.schema -ne 2 -or
+ $configObject.required_extension_build -cne $expectedExtensionBuild -or
+ $null -eq $configObject.creator_allowlist -or @($configObject.creator_allowlist).Count -lt 1 -or
+ $configObject.reload_generation -cne 'bili-auth-generic-v027' -or
+ $configObject.bridge_script_sha256 -cne '00F11DAF8387160DB863C89F0B33AB8480422233FF42199189222C989C7ED07E') {
throw 'Host config identity mismatch.'
}
-foreach ($name in @('ffmpeg', 'ffprobe', 'bridge_python', 'bridge_script', 'batch_json', 'yt_dlp_executable')) {
+foreach ($name in @('ffmpeg', 'ffprobe', 'bridge_python', 'bridge_script', 'yt_dlp_executable')) {
$value = $configObject.$name
$expectedHash = $configObject."${name}_sha256"
if (-not [System.IO.Path]::IsPathRooted($value) -or $value.StartsWith('\\') -or
@@ -351,30 +722,100 @@
if (-not $destinationItem.PSIsContainer -or ($destinationItem.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
throw 'Host config destination must be an existing non-reparse directory.'
}
-if (Get-ChildItem -LiteralPath $resolvedDestination -File | Where-Object { $_.Name.StartsWith('BV1HA3o6oEJJ.', [StringComparison]::OrdinalIgnoreCase) }) {
- throw 'Formal output already exists; overwrite is forbidden.'
+if ([string]::IsNullOrWhiteSpace([string]$configObject.creator_name) -or
+ [Text.Encoding]::UTF8.GetByteCount([string]$configObject.creator_name) -gt 240) {
+ throw 'Host config creator_name is invalid.'
+}
+foreach ($name in @('formal_manifest_path', 'processing_handoff_path')) {
+ $value = [string]$configObject.$name
+ if (-not [System.IO.Path]::IsPathRooted($value) -or $value.StartsWith('\\')) {
+ throw 'Host governed output path must be absolute and local.'
+ }
+ $parent = [System.IO.Path]::GetDirectoryName($value)
+ $parentItem = Get-Item -LiteralPath $parent
+ if (-not $parentItem.PSIsContainer -or ($parentItem.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
+ throw 'Host governed output parent must be an ordinary directory.'
+ }
+ if (Test-Path -LiteralPath $value) {
+ $item = Get-Item -LiteralPath $value
+ if ($item.PSIsContainer -or ($item.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
+ throw 'Host governed output must be an ordinary file.'
+ }
+ } elseif ($name -ceq 'formal_manifest_path') {
+ throw 'Host formal manifest must already exist.'
+ }
+}
+if ($configObject.formal_manifest_path -ceq $configObject.processing_handoff_path) {
+ throw 'Host governed output paths must be distinct.'
}
if (Test-Path -LiteralPath $root) {
throw 'InstallRoot already exists; overwrite is forbidden.'
}
$registryPath = "HKCU:\Software\Google\Chrome\NativeMessagingHosts\$expectedHostName"
+$previousRoot = [System.IO.Path]::GetFullPath((Join-Path $env:LOCALAPPDATA "project-info\bili-auth-native-host\$expectedPreviousVersion"))
if ($TestFileRegistryProvider) {
- if (-not $TestRegistryRoot) {
- throw 'TestRegistryRoot is required for the test file registry provider.'
+ if (-not $TestRegistryRoot -or -not $TestPreviousInstallRoot -or -not $TestPreviousArtifactReceipt) {
+ throw 'The test file registry provider requires its registry root and previous-install fixture inputs.'
}
$testRegistryBase = [System.IO.Path]::GetFullPath($TestRegistryRoot)
$tempBase = [System.IO.Path]::GetFullPath([System.IO.Path]::GetTempPath())
- if (-not $testRegistryBase.StartsWith($tempBase, [StringComparison]::OrdinalIgnoreCase)) {
- throw 'The test registry provider must be under the current temporary directory.'
+ $previousRoot = [System.IO.Path]::GetFullPath($TestPreviousInstallRoot)
+ $previousReceiptPath = (Resolve-Path -LiteralPath $TestPreviousArtifactReceipt).Path
+ if (-not $testRegistryBase.StartsWith($tempBase, [StringComparison]::OrdinalIgnoreCase) -or
+ -not $previousRoot.StartsWith($tempBase, [StringComparison]::OrdinalIgnoreCase) -or
+ -not $previousReceiptPath.StartsWith($tempBase, [StringComparison]::OrdinalIgnoreCase) -or
+ ($root -notlike ($tempBase.TrimEnd('\') + '\*'))) {
+ throw 'Test-only installer paths must be under the current temporary directory.'
}
+ $previousReceiptItem = Get-Item -LiteralPath $previousReceiptPath -Force
+ if ($previousReceiptItem.PSIsContainer -or ($previousReceiptItem.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
+ throw 'Test previous-artifact receipt must be a regular non-reparse file.'
+ }
+ $previousReceipt = Get-StrictJson $previousReceiptPath
+ $expectedPreviousReceiptKeys = @(
+ 'schema', 'manifest_bytes', 'manifest_sha256', 'host_executable_bytes',
+ 'host_executable_sha256', 'config_bytes', 'config_sha256'
+ )
+ if ((Compare-Object ($expectedPreviousReceiptKeys | Sort-Object) @($previousReceipt.PSObject.Properties.Name | Sort-Object)) -or
+ $previousReceipt.schema -ne 1) {
+ throw 'Test previous-artifact receipt identity mismatch.'
+ }
+ $expectedPreviousManifestBytes = $previousReceipt.manifest_bytes
+ $expectedPreviousManifestSha256 = $previousReceipt.manifest_sha256
+ $expectedPreviousHostBytes = $previousReceipt.host_executable_bytes
+ $expectedPreviousHostSha256 = $previousReceipt.host_executable_sha256
+ $expectedPreviousConfigBytes = $previousReceipt.config_bytes
+ $expectedPreviousConfigSha256 = $previousReceipt.config_sha256
$registryPath = Join-Path $testRegistryBase $expectedHostName
-} elseif ($TestRegistryRoot -or $InjectFailure -cne 'none') {
+} elseif ($TestRegistryRoot -or $TestPreviousInstallRoot -or $TestPreviousArtifactReceipt -or $InjectFailure -cne 'none') {
throw 'Test-only controls require TestFileRegistryProvider.'
+} else {
+ $expectedInstallRoot = [System.IO.Path]::GetFullPath((Join-Path $env:LOCALAPPDATA 'project-info\bili-auth-native-host\1.2.25+20260829.generic.v027'))
+ if ($root -cne $expectedInstallRoot) {
+ throw 'Production InstallRoot does not match the pinned v027 location.'
+ }
}
-if (Test-Path -LiteralPath $registryPath) {
- throw 'Native Messaging registration already exists; overwrite is forbidden.'
+
+$previousIdentityParameters = @{
+ PreviousRoot = $previousRoot
+ ManifestBytes = $expectedPreviousManifestBytes
+ ManifestSha256 = $expectedPreviousManifestSha256
+ HostBytes = $expectedPreviousHostBytes
+ HostSha256 = $expectedPreviousHostSha256
+ ConfigBytes = $expectedPreviousConfigBytes
+ ConfigSha256 = $expectedPreviousConfigSha256
}
+
+$registryPreimage = Get-RegistrySnapshot $registryPath $TestFileRegistryProvider.IsPresent
+if (-not $registryPreimage.Exists) {
+ throw 'Exact v026 Native Messaging registration preimage is required.'
+}
+$previousInstall = Assert-ExactPreviousInstall @previousIdentityParameters
+if (-not (Test-RegistrySnapshotExact $registryPreimage $previousInstall.ManifestPath)) {
+ throw 'Existing Native Messaging registration does not match the pinned v026 preimage.'
+}
+$installMode = 'EXACT_V026_TO_V027_SWITCH'
if (-not $Install) {
[pscustomobject]@{
@@ -383,16 +824,18 @@
origin = $expectedOrigin
host_name = $expectedHostName
packaging = 'pyinstaller-onefile'
+ install_mode = $installMode
} | ConvertTo-Json -Compress
return
}
if ($PSCmdlet.ShouldProcess($root, 'Install exact-BVID Native Messaging host for current user')) {
+ $installedHost = Join-Path $root $expectedHostExecutable
+ $installedConfig = Join-Path $root 'config.json'
+ $manifestPath = Join-Path $root 'native-host-manifest.json'
try {
[System.IO.Directory]::CreateDirectory($root) | Out-Null
Stop-Injected 'after-root'
- $installedHost = Join-Path $root $expectedHostExecutable
- $installedConfig = Join-Path $root 'config.json'
Copy-CreateNew $resolvedHost $installedHost
if ((Get-Item -LiteralPath $installedHost).Length -ne $buildEntry.bytes -or
(Get-FileHash -Algorithm SHA256 -LiteralPath $installedHost).Hash -cne $buildEntry.sha256) {
@@ -405,7 +848,6 @@
throw 'Installed config reread verification failed.'
}
Stop-Injected 'after-config'
- $manifestPath = Join-Path $root 'native-host-manifest.json'
$manifest = [ordered]@{
name = $expectedHostName
description = 'project-info exact-BVID authenticated ingress'
@@ -414,38 +856,44 @@
allowed_origins = @($expectedOrigin)
}
Write-Utf8CreateNew $manifestPath ($manifest | ConvertTo-Json -Depth 3)
- $persistedManifest = Get-StrictJson $manifestPath
- if ($persistedManifest.name -cne $expectedHostName -or
- $persistedManifest.path -cne $installedHost -or
- $persistedManifest.type -cne 'stdio' -or
- $persistedManifest.allowed_origins.Count -ne 1 -or
- $persistedManifest.allowed_origins[0] -cne $expectedOrigin) {
- throw 'Native host manifest reread verification failed.'
- }
+ Assert-NewInstallRoot $root $installedHost $installedConfig $manifestPath $buildEntry $configIdentity
Stop-Injected 'after-manifest'
- if ($TestFileRegistryProvider) {
- [System.IO.Directory]::CreateDirectory($registryPath) | Out-Null
- } else {
- New-Item -Path $registryPath -ErrorAction Stop | Out-Null
+ $lastRegistryPreimage = Get-RegistrySnapshot $registryPath $TestFileRegistryProvider.IsPresent
+ $null = Assert-ExactPreviousInstall @previousIdentityParameters
+ if (-not (Test-RegistrySnapshotExact $lastRegistryPreimage $previousInstall.ManifestPath)) {
+ throw 'Native Messaging v026 preimage changed before the registry switch.'
}
Stop-Injected 'after-registry-key'
- if ($TestFileRegistryProvider) {
- Write-Utf8CreateNew (Join-Path $registryPath 'default.value') $manifestPath
- } else {
- Set-Item -LiteralPath $registryPath -Value $manifestPath -ErrorAction Stop
+ Set-RegistryDefaultAtomic $registryPath $manifestPath $TestFileRegistryProvider.IsPresent
+ if ($InjectFailure -ceq 'after-registry-value-mixed') {
+ Write-Utf8CreateNew (Join-Path $registryPath 'unexpected.value') 'ambiguous'
+ throw 'Injected ambiguous registry state after v026 switch.'
}
Stop-Injected 'after-registry-value'
+ $committedRegistry = Get-RegistrySnapshot $registryPath $TestFileRegistryProvider.IsPresent
+ if (-not (Test-RegistrySnapshotExact $committedRegistry $manifestPath)) {
+ throw 'Native Messaging v026 registry commit reread verification failed.'
+ }
+ Assert-NewInstallRoot $root $installedHost $installedConfig $manifestPath $buildEntry $configIdentity
} catch {
- # Both targets were proven absent before the transaction, so any
- # surviving object belongs to this attempt even if a provider threw
- # after partially creating it.
- if (Test-Path -LiteralPath $registryPath) {
- Remove-Item -LiteralPath $registryPath -Recurse -Force
+ $originalFailure = $_.Exception
+ try {
+ $rollbackRegistry = Get-RegistrySnapshot $registryPath $TestFileRegistryProvider.IsPresent
+ if (Test-RegistrySnapshotExact $rollbackRegistry $manifestPath) {
+ Set-RegistryDefaultAtomic $registryPath $previousInstall.ManifestPath $TestFileRegistryProvider.IsPresent
+ } elseif (-not (Test-RegistrySnapshotExact $rollbackRegistry $previousInstall.ManifestPath)) {
+ throw 'RECOVERY_REQUIRED: Native Messaging registration is neither the pinned v026 preimage nor this v027 attempt.'
+ }
+ $restoredRegistry = Get-RegistrySnapshot $registryPath $TestFileRegistryProvider.IsPresent
+ if (-not (Test-RegistrySnapshotExact $restoredRegistry $previousInstall.ManifestPath)) {
+ throw 'RECOVERY_REQUIRED: Native Messaging v026 registry preimage was not restored.'
+ }
+ $null = Assert-ExactPreviousInstall @previousIdentityParameters
+ Remove-ProvenOwnedInstallRoot $root $installedHost $installedConfig $manifestPath $buildEntry $configIdentity
+ } catch {
+ throw "RECOVERY_REQUIRED: $($_.Exception.Message) Original failure: $($originalFailure.Message)"
}
- if (Test-Path -LiteralPath $root) {
- Remove-Item -LiteralPath $root -Recurse -Force
- }
- throw
+ throw $originalFailure
}
}
--
Gitblit v1.9.3