Skip to content

Commit ad01bff

Browse files
Ash258r15ch13
authored andcommitted
Add bin\checkhashes.ps1 (#2766)
- Checks if all URLs inside manifest have correct hashes
1 parent 5d14db6 commit ad01bff

3 files changed

Lines changed: 198 additions & 9 deletions

File tree

bin/checkhashes.ps1

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
<#
2+
.SYNOPSIS
3+
Check if ALL urls inside manifest have correct hashes.
4+
.PARAMETER App
5+
Manifest to be checked.
6+
Wildcard is supported.
7+
.PARAMETER Dir
8+
Where to search for manifest(s).
9+
.PARAMETER Update
10+
When there are mismatched hashes, manifest will be updated.
11+
.PARAMETER ForceUpdate
12+
Manifest will be updated all the time. Not only when there are mismatched hashes.
13+
.PARAMETER SkipCorrect
14+
Manifests without mismatch will not be shown.
15+
.PARAMETER UseCache
16+
Downloaded files will not be deleted after script finish.
17+
Should not be used, because check should be used for downloading actual version of file (as normal user, not finding in some document from vendors, which could be damaged / wrong (Example: Slack@3.3.1 lukesampson/scoop-extras#1192)), not some previously downloaded.
18+
.EXAMPLE
19+
PS BUCKETDIR> .\bin\checkhashes.ps1
20+
Check all manifests for hash mismatch.
21+
.EXAMPLE
22+
PS BUCKETDIR> .\bin\checkhashes.ps1 MANIFEST -Update
23+
Check MANIFEST and Update if there are some wrong hashes.
24+
#>
25+
param(
26+
[String] $App = '*',
27+
[ValidateScript( {
28+
if (!(Test-Path $_ -Type Container)) {
29+
throw "$_ is not a directory!"
30+
} else {
31+
$true
32+
}
33+
})]
34+
[String] $Dir = "$PSScriptRoot\..\bucket",
35+
[Switch] $Update,
36+
[Switch] $ForceUpdate,
37+
[Switch] $SkipCorrect,
38+
[Alias('k')]
39+
[Switch] $UseCache
40+
)
41+
42+
. "$PSScriptRoot\..\lib\core.ps1"
43+
. "$PSScriptRoot\..\lib\manifest.ps1"
44+
. "$PSScriptRoot\..\lib\config.ps1"
45+
. "$PSScriptRoot\..\lib\buckets.ps1"
46+
. "$PSScriptRoot\..\lib\autoupdate.ps1"
47+
. "$PSScriptRoot\..\lib\json.ps1"
48+
. "$PSScriptRoot\..\lib\versions.ps1"
49+
. "$PSScriptRoot\..\lib\install.ps1"
50+
. "$PSScriptRoot\..\lib\unix.ps1"
51+
52+
$Dir = Resolve-Path $Dir
53+
if ($ForceUpdate) { $Update = $true }
54+
# Cleanup
55+
if (!$UseCache) { scoop cache rm '*HASH_CHECK*' }
56+
57+
function err ([String] $name, [String[]] $message) {
58+
Write-Host "$name`: " -ForegroundColor Red -NoNewline
59+
Write-Host ($message -join "`r`n") -ForegroundColor Red
60+
}
61+
62+
$MANIFESTS = @()
63+
foreach ($single in Get-ChildItem $Dir "$App.json") {
64+
$name = (strip_ext $single.Name)
65+
$manifest = parse_json "$Dir\$($single.Name)"
66+
67+
# Skip nighly manifests, since their hash validation is skipped
68+
if ($manifest.version -eq 'nightly') { continue }
69+
70+
$urls = @()
71+
$hashes = @()
72+
73+
if ($manifest.architecture) {
74+
# First handle 64bit
75+
url $manifest '64bit' | ForEach-Object { $urls += $_ }
76+
hash $manifest '64bit' | ForEach-Object { $hashes += $_ }
77+
url $manifest '32bit' | ForEach-Object { $urls += $_ }
78+
hash $manifest '32bit' | ForEach-Object { $hashes += $_ }
79+
} elseif ($manifest.url) {
80+
$manifest.url | ForEach-Object { $urls += $_ }
81+
$manifest.hash | ForEach-Object { $hashes += $_ }
82+
} else {
83+
err $name 'Manifest does not contain URL property.'
84+
continue
85+
}
86+
87+
# Number of URLS and Hashes is different
88+
if ($urls.Length -ne $hashes.Length) {
89+
err $name 'URLS and hashes count mismatch.'
90+
continue
91+
}
92+
93+
$MANIFESTS += @{
94+
app = $name
95+
manifest = $manifest
96+
urls = $urls
97+
hashes = $hashes
98+
}
99+
}
100+
101+
# clear any existing events
102+
Get-Event | ForEach-Object { Remove-Event $_.SourceIdentifier }
103+
104+
foreach ($current in $MANIFESTS) {
105+
$count = 0
106+
# Array of indexes mismatched hashes.
107+
$mismatched = @()
108+
# Array of computed hashes
109+
$actuals = @()
110+
111+
$current.urls | ForEach-Object {
112+
$algorithm, $expected = get_hash $current.hashes[$count]
113+
$version = 'HASH_CHECK'
114+
$tmp = $expected_hash -split ':'
115+
116+
dl_with_cache $current.app $version $_ $null $null -use_cache:$UseCache
117+
118+
$to_check = fullpath (cache_path $current.app $version $_)
119+
$actual_hash = compute_hash $to_check $algorithm
120+
121+
# Append type of algorithm to both expected and actual if it's not sha256
122+
if ($algorithm -ne 'sha256') {
123+
$actual_hash = "$algorithm`:$actual_hash"
124+
$expected = "$algorithm`:$expected"
125+
}
126+
127+
$actuals += $actual_hash
128+
if ($actual_hash -ne $expected) {
129+
$mismatched += $count
130+
}
131+
$count++
132+
}
133+
134+
if ($mismatched.Length -eq 0 ) {
135+
if (!$SkipCorrect) {
136+
Write-Host "$($current.app): " -NoNewline
137+
Write-Host 'OK' -ForegroundColor Green
138+
}
139+
} else {
140+
Write-Host "$($current.app): " -NoNewline
141+
Write-Host 'Mismatch found ' -ForegroundColor Red
142+
$mismatched | ForEach-Object {
143+
$file = fullpath (cache_path $current.app $version $current.urls[$_])
144+
Write-Host "`tURL:`t`t$($current.urls[$_])"
145+
if (Test-Path $file) {
146+
Write-Host "`tFirst bytes:`t$((get_magic_bytes_pretty $file ' ').ToUpper())"
147+
}
148+
Write-Host "`tExpected:`t$($current.urls[$_])" -ForegroundColor Green
149+
Write-Host "`tActual:`t`t$($actuals[$_])" -ForegroundColor Red
150+
}
151+
}
152+
153+
if ($Update) {
154+
if ($current.manifest.url -and $current.manifest.hash) {
155+
$current.manifest.hash = $actuals
156+
} else {
157+
$platforms = ($current.manifest.architecture | Get-Member -MemberType NoteProperty).Name
158+
# Defaults to zero, don't know, which architecture is available
159+
$64bit_count = 0
160+
$32bit_count = 0
161+
162+
if ($platforms.Contains('64bit')) {
163+
$64bit_count = $current.manifest.architecture.'64bit'.hash.Count
164+
# 64bit is get, donwloaded and added first
165+
$current.manifest.architecture.'64bit'.hash = $actuals[0..($64bit_count - 1)]
166+
}
167+
if ($platforms.Contains('32bit')) {
168+
$32bit_count = $current.manifest.architecture.'32bit'.hash.Count
169+
$max = $64bit_count + $32bit_count - 1 # Edge case if manifest contains 64bit and 32bit.
170+
$current.manifest.architecture.'32bit'.hash = $actuals[($64bit_count)..$max]
171+
}
172+
}
173+
174+
Write-Host "Writing updated $($current.app) manifest" -ForegroundColor DarkGreen
175+
176+
$current.manifest = $current.manifest | ConvertToPrettyJson
177+
$path = Resolve-Path "$Dir\$($current.app).json"
178+
[System.IO.File]::WriteAllLines($path, $current.manifest)
179+
}
180+
}

lib/core.ps1

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,20 @@ function format_hash_aria2([String] $hash) {
650650
return $hash
651651
}
652652

653+
function get_hash([String] $multihash) {
654+
$type, $hash = $multihash -split ':'
655+
if(!$hash) {
656+
# no type specified, assume sha256
657+
$type, $hash = 'sha256', $multihash
658+
}
659+
660+
if(@('md5','sha1','sha256', 'sha512') -notcontains $type) {
661+
return $null, "Hash type '$type' isn't supported."
662+
}
663+
664+
return $type, $hash.ToLower()
665+
}
666+
653667
function handle_special_urls($url)
654668
{
655669
# FossHub.com

lib/install.ps1

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -639,17 +639,12 @@ function check_hash($file, $hash, $app_name) {
639639
Write-Host "Checking hash of " -NoNewline
640640
Write-Host $(url_remote_filename $url) -f Cyan -NoNewline
641641
Write-Host " ... " -nonewline
642-
$type, $expected = $hash.split(':')
643-
if(!$expected) {
644-
# no type specified, assume sha256
645-
$type, $expected = 'sha256', $type
642+
$algorithm, $expected = get_hash $hash
643+
if ($null -eq $algorithm) {
644+
return $false, "Hash type '$algorithm' isn't supported."
646645
}
647646

648-
if(@('md5','sha1','sha256', 'sha512') -notcontains $type) {
649-
return $false, "Hash type '$type' isn't supported."
650-
}
651-
652-
$actual = (compute_hash $file $type).ToLower()
647+
$actual = compute_hash $file $algorithm
653648
$expected = $expected.ToLower()
654649

655650
if($actual -ne $expected) {

0 commit comments

Comments
 (0)