66 "debug/elf"
77 "debug/macho"
88 "debug/pe"
9+ "encoding/binary"
910 "encoding/json"
1011 "errors"
1112 "fmt"
6061 machoHeader = []byte ("\xFE \xED \xFA " )
6162 machoHeaderLittleEndian = []byte ("\xFA \xED \xFE " )
6263 machoUniversalHeader = []byte ("\xCA \xFE \xBA \xBE " )
64+ // https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-magic
65+ wasmHeader = []byte ("\x00 asm\x01 \x00 \x00 \x00 " )
66+
67+ cargoAuditableSectionName = ".dep-v0"
68+ cargoAuditableLegacySectionName = ".rust-deps-v0"
6369)
6470
6571func GetDependencyInfo (r io.ReaderAt ) (VersionInfo , error ) {
@@ -90,6 +96,8 @@ func GetDependencyInfo(r io.ReaderAt) (VersionInfo, error) {
9096 return VersionInfo {}, ErrUnknownFileFormat
9197 }
9298 x = & machoExe {f }
99+ case bytes .HasPrefix (header , wasmHeader ):
100+ x = & wasmReader {r }
93101 default :
94102 return VersionInfo {}, ErrUnknownFileFormat
95103 }
@@ -135,13 +143,13 @@ type elfExe struct {
135143func (x * elfExe ) ReadRustDepSection () ([]byte , error ) {
136144 // Try .dep-v0 first, falling back to .rust-deps-v0 as used in
137145 // in rust-audit 0.1.0
138- depInfo := x .f .Section (".dep-v0" )
146+ depInfo := x .f .Section (cargoAuditableSectionName )
139147
140148 if depInfo != nil {
141149 return depInfo .Data ()
142150 }
143151
144- depInfo = x .f .Section (".rust-deps-v0" )
152+ depInfo = x .f .Section (cargoAuditableLegacySectionName )
145153
146154 if depInfo == nil {
147155 return nil , ErrNoRustDepInfo
@@ -157,7 +165,7 @@ type peExe struct {
157165func (x * peExe ) ReadRustDepSection () ([]byte , error ) {
158166 // Try .dep-v0 first, falling back to rdep-v0 as used in
159167 // in rust-audit 0.1.0
160- depInfo := x .f .Section (".dep-v0" )
168+ depInfo := x .f .Section (cargoAuditableSectionName )
161169
162170 if depInfo != nil {
163171 return depInfo .Data ()
@@ -179,7 +187,7 @@ type machoExe struct {
179187func (x * machoExe ) ReadRustDepSection () ([]byte , error ) {
180188 // Try .dep-v0 first, falling back to rust-deps-v0 as used in
181189 // in rust-audit 0.1.0
182- depInfo := x .f .Section (".dep-v0" )
190+ depInfo := x .f .Section (cargoAuditableSectionName )
183191
184192 if depInfo != nil {
185193 return depInfo .Data ()
@@ -193,3 +201,98 @@ func (x *machoExe) ReadRustDepSection() ([]byte, error) {
193201
194202 return depInfo .Data ()
195203}
204+
205+ type wasmReader struct {
206+ r io.ReaderAt
207+ }
208+
209+ func (x * wasmReader ) ReadRustDepSection () ([]byte , error ) {
210+ r := x .r
211+ var offset int64 = 0
212+
213+ // Check the preamble (magic number and version)
214+ buf := make ([]byte , 8 )
215+ _ , err := r .ReadAt (buf , offset )
216+ offset += 8
217+ if err != nil || ! bytes .Equal (buf , wasmHeader ) {
218+ return nil , ErrUnknownFileFormat
219+ }
220+
221+ // https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#custom-section%E2%91%A0
222+ // Look through the sections until we find a custom .dep-v0 section or EOF
223+ for {
224+ // Read single byte section ID
225+ sectionId := make ([]byte , 1 )
226+ _ , err = r .ReadAt (sectionId , offset )
227+ offset += 1
228+ if err == io .EOF {
229+ return nil , ErrNoRustDepInfo
230+ } else if err != nil {
231+ return nil , ErrUnknownFileFormat
232+ }
233+
234+ // Read section size
235+ buf = make ([]byte , 4 )
236+ _ , err = r .ReadAt (buf , offset )
237+ if err != nil {
238+ return nil , ErrUnknownFileFormat
239+ }
240+ sectionSize , n , err := readUint32 (buf )
241+ if err != nil {
242+ return nil , ErrUnknownFileFormat
243+ }
244+ offset += n
245+ nextSection := offset + int64 (sectionSize )
246+
247+ // Custom sections have a zero section ID
248+ if sectionId [0 ] != 0 {
249+ offset = nextSection
250+ continue
251+ }
252+
253+ // The custom section has a variable length name
254+ // followed by the data
255+ _ , err = r .ReadAt (buf , offset )
256+ if err != nil {
257+ return nil , ErrUnknownFileFormat
258+ }
259+ nameSize , n , err := readUint32 (buf )
260+ if err != nil {
261+ return nil , ErrUnknownFileFormat
262+ }
263+ offset += n
264+
265+ // Read section name
266+ name := make ([]byte , nameSize )
267+ _ , err = r .ReadAt (name , offset )
268+ if err != nil {
269+ return nil , ErrUnknownFileFormat
270+ }
271+ offset += int64 (nameSize )
272+
273+ // Is this our custom section?
274+ if string (name ) != cargoAuditableSectionName {
275+ offset = nextSection
276+ continue
277+ }
278+
279+ // Read audit data
280+ data := make ([]byte , nextSection - offset )
281+ _ , err = r .ReadAt (data , offset )
282+ if err != nil {
283+ return nil , ErrUnknownFileFormat
284+ }
285+ return data , nil
286+
287+ }
288+ }
289+
290+ // wrap binary.Uvarint to return uint32, checking for overflow
291+ // https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#integers%E2%91%A4
292+ func readUint32 (buf []byte ) (uint32 , int64 , error ) {
293+ v , n := binary .Uvarint (buf )
294+ if n <= 0 || v > uint64 (^ uint32 (0 )) {
295+ return 0 , 0 , fmt .Errorf ("overflow decoding uint32" )
296+ }
297+ return uint32 (v ), int64 (n ), nil
298+ }
0 commit comments