package asar // import "b612.me/asar" import ( "encoding/json" "errors" "io" ) var ( errHeader = errors.New("asar: invalid file header") ) type jsonReader struct { ASAR io.ReaderAt BaseOffset int64 D *json.Decoder Token json.Token Options DecodeOptions // 新增字段 } func (j *jsonReader) Peek() json.Token { if j.Token != nil { return j.Token } tkn, err := j.D.Token() if err != nil { if err == io.EOF { return nil } panic(err) } j.Token = tkn return tkn } func (j *jsonReader) HasDelimRune(r rune) bool { peek := j.Peek() ru, ok := peek.(json.Delim) if !ok { return false } if rune(ru) != r { return false } return true } func (j *jsonReader) Next() json.Token { if j.Token != nil { t := j.Token j.Token = nil return t } tkn, err := j.D.Token() if err != nil { if err == io.EOF { return nil } panic(err) } return tkn } func (j *jsonReader) NextDelimRune() rune { tkn := j.Next() r, ok := tkn.(json.Delim) if !ok { panic(errHeader) } return rune(r) } func (j *jsonReader) ExpectDelim(r rune) { next := j.NextDelimRune() if next != r { panic(errHeader) } } func (j *jsonReader) ExpectBool() bool { tkn := j.Next() b, ok := tkn.(bool) if !ok { panic(errHeader) } return b } func (j *jsonReader) ExpectString() string { next := j.Next() str, ok := next.(string) if !ok { panic(errHeader) } return str } func (j *jsonReader) ExpectStringVal(val string) { str := j.ExpectString() if str != val { panic(errHeader) } } func (j *jsonReader) ExpectInt64() int64 { var number json.Number switch j.Peek().(type) { case string: number = json.Number(j.ExpectString()) case json.Number: number = j.Next().(json.Number) default: panic(errHeader) } val, err := number.Int64() if err != nil { panic(errHeader) } return val } func parseRoot(r *jsonReader) *Entry { entry := &Entry{ Flags: FlagDir, } r.ExpectDelim('{') r.ExpectStringVal("files") parseFiles(r, entry) r.ExpectDelim('}') // 关键修改:根据选项决定是否检查剩余内容 if r.Options.StrictValidation { // 严格模式:不允许有剩余数据 if r.Next() != nil { panic(errHeader) } } else { // 宽松模式:消费并忽略剩余的 token // 这些可能是 NUL 填充或其他无关数据 for { token := r.Next() if token == nil { break } // 可选:如果需要,可以在这里记录日志 // log.Printf("Ignoring trailing token in ASAR header: %v", token) } } return entry } func parseFiles(r *jsonReader, parent *Entry) { r.ExpectDelim('{') for !r.HasDelimRune('}') { parseEntry(r, parent) } r.ExpectDelim('}') } func parseEntry(r *jsonReader, parent *Entry) { name := r.ExpectString() if name == "" { panic(errHeader) } if !validFilename(name) { panic(errHeader) } r.ExpectDelim('{') child := &Entry{ Name: name, Parent: parent, } for !r.HasDelimRune('}') { key := r.ExpectString() switch key { case "files": child.Flags |= FlagDir parseFiles(r, child) case "size": child.Size = r.ExpectInt64() case "offset": child.Offset = r.ExpectInt64() case "unpacked": if r.ExpectBool() { child.Flags |= FlagUnpacked } case "executable": if r.ExpectBool() { child.Flags |= FlagExecutable } default: if r.Options.StrictValidation { // 严格模式:未知字段导致错误 panic(errHeader) } else { // 宽松模式:跳过未知字段 // 需要消费这个未知字段的值 if token := r.Next(); token == nil { panic(errHeader) } // 可选:记录日志 // log.Printf("Ignoring unknown field '%s' in ASAR entry", key) } } } if child.Flags&FlagDir == 0 { child.r = r.ASAR child.baseOffset = r.BaseOffset } parent.Children = append(parent.Children, child) r.ExpectDelim('}') } // 保留原有的 decodeHeader func decodeHeader(asar io.ReaderAt, header *io.SectionReader, offset int64) (entry *Entry, err error) { return decodeHeaderWithOptions(asar, header, offset, DefaultDecodeOptions) } // 新增带选项的版本 func decodeHeaderWithOptions(asar io.ReaderAt, header *io.SectionReader, offset int64, opts DecodeOptions) (entry *Entry, err error) { decoder := json.NewDecoder(header) decoder.UseNumber() reader := jsonReader{ ASAR: asar, BaseOffset: offset, D: decoder, Options: opts, // 传递选项 } defer func() { if r := recover(); r != nil { if e := r.(error); e != nil { err = e } else { panic(r) } } }() entry = parseRoot(&reader) return } // findJSONEnd 找到 JSON 的实际结束位置(最后一个有意义的字符) func findJSONEnd(data []byte) int { // 从后往前找,跳过所有的 NUL 和空白字符 for i := len(data) - 1; i >= 0; i-- { b := data[i] // 跳过 NUL 和空白字符 if b != 0 && b != ' ' && b != '\n' && b != '\r' && b != '\t' { // 找到了非空白字符,这应该是 JSON 的结束 return i + 1 } } return len(data) }