From b0f0f0e149b0367d63a8a49d011d057baccc4c53 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 26 Oct 2023 07:41:06 -0700 Subject: [PATCH] Update parsing of integrity metadata --- crates/wasmparser/src/validator/names.rs | 45 ++++++++++++++++++- tests/local/component-model/export.wast | 2 +- tests/local/component-model/import.wast | 42 ++++++++++++++--- .../component-model/import.wast/44.print | 4 +- .../component-model/import.wast/56.print | 2 +- .../component-model/import.wast/59.print | 20 ++++++++- 6 files changed, 102 insertions(+), 13 deletions(-) diff --git a/crates/wasmparser/src/validator/names.rs b/crates/wasmparser/src/validator/names.rs index 1dcc3ceaf1..fa203ab9ed 100644 --- a/crates/wasmparser/src/validator/names.rs +++ b/crates/wasmparser/src/validator/names.rs @@ -643,8 +643,34 @@ impl<'a> ComponentNameParser<'a> { } fn parse_hash(&mut self) -> Result<&'a str> { - let hash = self.take_up_to('>')?; - Ok(hash) + let integrity = self.take_up_to('>')?; + let mut any = false; + for hash in integrity.split_whitespace() { + any = true; + let rest = hash + .strip_prefix("sha256") + .or_else(|| hash.strip_prefix("sha384")) + .or_else(|| hash.strip_prefix("sha512")); + let rest = match rest { + Some(s) => s, + None => bail!(self.offset, "unrecognized hash algorithm: `{hash}`"), + }; + let rest = match rest.strip_prefix('-') { + Some(s) => s, + None => bail!(self.offset, "expected `-` after hash algorithm: {hash}"), + }; + let (base64, _options) = match rest.find('?') { + Some(i) => (&rest[..i], Some(&rest[i + 1..])), + None => (rest, None), + }; + if !is_base64(base64) { + bail!(self.offset, "not valid base64: `{base64}`"); + } + } + if !any { + bail!(self.offset, "integrity hash cannot be empty"); + } + Ok(integrity) } fn eat_optional_hash(&mut self) -> Result> { @@ -730,6 +756,21 @@ impl<'a> ComponentNameParser<'a> { } } +fn is_base64(s: &str) -> bool { + if s.is_empty() { + return false; + } + let mut equals = 0; + for (i, byte) in s.as_bytes().iter().enumerate() { + match byte { + b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z' | b'+' | b'/' if equals == 0 => {} + b'=' if i > 0 && equals < 2 => equals += 1, + _ => return false, + } + } + true +} + #[cfg(test)] mod tests { use super::*; diff --git a/tests/local/component-model/export.wast b/tests/local/component-model/export.wast index 0e2d5159fc..581ecd7576 100644 --- a/tests/local/component-model/export.wast +++ b/tests/local/component-model/export.wast @@ -54,7 +54,7 @@ ;; cannot export some types of strings (assert_invalid - (component (type (component (export "integrity=" (func))))) + (component (type (component (export "integrity=" (func))))) "not of an allowed kind to use in an export") (assert_invalid (component (type (component (export "url=" (func))))) diff --git a/tests/local/component-model/import.wast b/tests/local/component-model/import.wast index 16702a90e0..485a9a73b5 100644 --- a/tests/local/component-model/import.wast +++ b/tests/local/component-model/import.wast @@ -228,8 +228,8 @@ (component (import "locked-dep=" (func)) (import "locked-dep=" (func)) - (import "locked-dep=,integrity=" (func)) - (import "locked-dep=,integrity=" (func)) + (import "locked-dep=,integrity=" (func)) + (import "locked-dep=,integrity=" (func)) ) (assert_invalid @@ -269,7 +269,7 @@ (component (import "url=<>" (func)) (import "url=" (func)) - (import "url=,integrity=" (func)) + (import "url=,integrity=" (func)) ) (assert_invalid @@ -280,6 +280,38 @@ "failed to find `>`") (component - (import "integrity=<>" (func)) - (import "integrity=" (func)) + (import "integrity=" (func)) + (import "integrity=" (func)) + (import "integrity=" (func)) + (import "integrity=" (func)) + (import "integrity=< sha512-a sha256-b >" (func)) + (import "integrity=< sha512-a?abcd >" (func)) + (import "integrity=" (func)) + (import "integrity=" (func)) + (import "integrity=" (func)) + (import "integrity=" (func)) ) +(assert_invalid + (component (import "integrity=<>" (func))) + "integrity hash cannot be empty") +(assert_invalid + (component (import "integrity=" (func))) + "expected `-` after hash algorithm") +(assert_invalid + (component (import "integrity=" (func))) + "not valid base64") +(assert_invalid + (component (import "integrity=" (func))) + "not valid base64") +(assert_invalid + (component (import "integrity=" (func))) + "not valid base64") +(assert_invalid + (component (import "integrity=" (func))) + "not valid base64") +(assert_invalid + (component (import "integrity=" (func))) + "not valid base64") +(assert_invalid + (component (import "integrity=" (func))) + "unrecognized hash algorithm") diff --git a/tests/snapshots/local/component-model/import.wast/44.print b/tests/snapshots/local/component-model/import.wast/44.print index ac6e2184fe..de8d39be71 100644 --- a/tests/snapshots/local/component-model/import.wast/44.print +++ b/tests/snapshots/local/component-model/import.wast/44.print @@ -4,7 +4,7 @@ (type (;1;) (func)) (import "locked-dep=" (func (;1;) (type 1))) (type (;2;) (func)) - (import "locked-dep=,integrity=" (func (;2;) (type 2))) + (import "locked-dep=,integrity=" (func (;2;) (type 2))) (type (;3;) (func)) - (import "locked-dep=,integrity=" (func (;3;) (type 3))) + (import "locked-dep=,integrity=" (func (;3;) (type 3))) ) \ No newline at end of file diff --git a/tests/snapshots/local/component-model/import.wast/56.print b/tests/snapshots/local/component-model/import.wast/56.print index 2735765da3..e15ec7f5f4 100644 --- a/tests/snapshots/local/component-model/import.wast/56.print +++ b/tests/snapshots/local/component-model/import.wast/56.print @@ -4,5 +4,5 @@ (type (;1;) (func)) (import "url=" (func (;1;) (type 1))) (type (;2;) (func)) - (import "url=,integrity=" (func (;2;) (type 2))) + (import "url=,integrity=" (func (;2;) (type 2))) ) \ No newline at end of file diff --git a/tests/snapshots/local/component-model/import.wast/59.print b/tests/snapshots/local/component-model/import.wast/59.print index cd6ffef57d..a812c45c00 100644 --- a/tests/snapshots/local/component-model/import.wast/59.print +++ b/tests/snapshots/local/component-model/import.wast/59.print @@ -1,6 +1,22 @@ (component (type (;0;) (func)) - (import "integrity=<>" (func (;0;) (type 0))) + (import "integrity=" (func (;0;) (type 0))) (type (;1;) (func)) - (import "integrity=" (func (;1;) (type 1))) + (import "integrity=" (func (;1;) (type 1))) + (type (;2;) (func)) + (import "integrity=" (func (;2;) (type 2))) + (type (;3;) (func)) + (import "integrity=" (func (;3;) (type 3))) + (type (;4;) (func)) + (import "integrity=< sha512-a sha256-b >" (func (;4;) (type 4))) + (type (;5;) (func)) + (import "integrity=< sha512-a?abcd >" (func (;5;) (type 5))) + (type (;6;) (func)) + (import "integrity=" (func (;6;) (type 6))) + (type (;7;) (func)) + (import "integrity=" (func (;7;) (type 7))) + (type (;8;) (func)) + (import "integrity=" (func (;8;) (type 8))) + (type (;9;) (func)) + (import "integrity=" (func (;9;) (type 9))) ) \ No newline at end of file