git-lfs/pointer/pointer.go
rubyist 46841cc996 Stricter parsing of OID in old alpha pointer formats
The old pointer format was pretty loose, this can cause false positives for the
scanner. An example can be found in this repository, docs/man/index.txt will be
considered a git media file. Validing that the line that should be an OID is an
OID should reduce these false positives.
2014-10-04 12:28:12 -04:00

166 lines
3.4 KiB
Go

package pointer
import (
"bufio"
"bytes"
"errors"
"fmt"
"github.com/github/git-media/gitmedia"
"io"
"regexp"
"strconv"
"strings"
)
var (
MediaWarning = []byte("# git-media\n")
alpha = "http://git-media.io/v/1"
latest = "http://git-media.io/v/2"
oidType = "sha256"
alphaHeaderRE = regexp.MustCompile(`\A# (.*git-media|external)`)
oidRE = regexp.MustCompile(`\A[0-9a-fA-F]{64}`)
template = `version %s
oid sha256:%s
size %d
`
matcher = []byte("git-media")
pointerKeys = []string{"version", "oid", "size"}
)
type Pointer struct {
Version string
Oid string
Size int64
OidType string
}
func NewPointer(oid string, size int64) *Pointer {
return &Pointer{latest, oid, size, oidType}
}
func (p *Pointer) Smudge(writer io.Writer, cb gitmedia.CopyCallback) error {
return Smudge(writer, p, cb)
}
func (p *Pointer) Encode(writer io.Writer) (int, error) {
return Encode(writer, p)
}
func (p *Pointer) Encoded() string {
return fmt.Sprintf(template, latest, p.Oid, p.Size)
}
func Encode(writer io.Writer, pointer *Pointer) (int, error) {
return writer.Write([]byte(pointer.Encoded()))
}
func Decode(reader io.Reader) (*Pointer, error) {
buf := make([]byte, 200)
written, err := reader.Read(buf)
if err != nil {
return nil, err
}
data := bytes.TrimSpace(buf[0:written])
if alphaHeaderRE.Match(data) {
return decodeAlpha(data)
} else {
return decodeKV(data)
}
}
func decodeKV(data []byte) (*Pointer, error) {
parsed, err := decodeKVData(data)
if err != nil {
return nil, err
}
v, ok := parsed["version"]
if !ok || v != latest {
if len(v) == 0 {
v = "--"
}
return nil, errors.New("Invalid version: " + v)
}
oidValue, ok := parsed["oid"]
if !ok {
return nil, errors.New("Invalid Oid")
}
oidParts := strings.SplitN(oidValue, ":", 2)
if len(oidParts) != 2 {
return nil, errors.New("Invalid Oid type in" + oidValue)
}
if oidParts[0] != oidType {
return nil, errors.New("Invalid Oid type: " + oidParts[0])
}
oid := oidParts[1]
var size int64
sizeStr, ok := parsed["size"]
if !ok {
return nil, errors.New("Invalid Oid")
} else {
size, err = strconv.ParseInt(sizeStr, 10, 0)
if err != nil {
return nil, errors.New("Invalid size: " + sizeStr)
}
}
return NewPointer(oid, size), nil
}
func decodeKVData(data []byte) (map[string]string, error) {
m := make(map[string]string)
if !bytes.Contains(data, matcher) {
return m, fmt.Errorf("Not a valid Git Media pointer file.")
}
scanner := bufio.NewScanner(bytes.NewBuffer(data))
line := 0
numKeys := len(pointerKeys)
for scanner.Scan() {
text := scanner.Text()
if len(text) == 0 {
continue
}
parts := strings.SplitN(text, " ", 2)
key := parts[0]
if numKeys <= line {
return m, fmt.Errorf("Extra line: %s", text)
}
if expected := pointerKeys[line]; key != expected {
return m, fmt.Errorf("Expected key %s, got %s", expected, key)
}
line += 1
if len(parts) < 2 {
return m, fmt.Errorf("Error reading line %d: %s", line, text)
}
m[key] = parts[1]
}
return m, scanner.Err()
}
func decodeAlpha(data []byte) (*Pointer, error) {
lines := bytes.Split(data, []byte("\n"))
last := len(lines) - 1
if last == 0 {
return nil, errors.New("No OID in pointer file")
}
if !oidRE.Match(lines[last]) {
return nil, errors.New("Invalid OID in pointer file")
}
return &Pointer{alpha, string(lines[last]), 0, oidType}, nil
}