package runs import ( "errors" "strings" "time" "unicode" "unicode/utf8" ) var ( ErrBadApp = errors.New("runs: not an account and app") ErrBadID = errors.New("runs: not a run id") ErrNoArgs = errors.New("runs: no arguments") ErrTooManyArgs = errors.New("runs: more than 256 arguments") ErrArgvTooLarge = errors.New("runs: arguments over 64 KB") ErrArgNUL = errors.New("runs: an argument holds a NUL byte") ErrTimeout = errors.New("runs: timeout outside 1s to 6h") ErrFilesTooLarge = errors.New("runs: files over 64 MB") ErrFileName = errors.New("runs: not a plain file name") ErrFileTwice = errors.New("runs: two files share a name") ) // FileNameError names the file a refusal is about. type FileNameError struct { Name string Twice bool } func (e *FileNameError) Error() string { if e.Twice { return ErrFileTwice.Error() + ": " + e.Name } return ErrFileName.Error() + ": " + e.Name } func (e *FileNameError) Is(target error) bool { return (e.Twice && target == ErrFileTwice) || (!e.Twice && target == ErrFileName) } // CheckArgv is spec §5.1's argv rule. func CheckArgv(argv []string) error { switch { case len(argv) == 0: return ErrNoArgs case len(argv) > MaxArgs: return ErrTooManyArgs } total := 0 for _, a := range argv { if strings.IndexByte(a, 0) >= 0 { return ErrArgNUL } total += len(a) } if total > MaxArgvBytes { return ErrArgvTooLarge } return nil } // CheckTimeout is spec §5.1's timeout rule. func CheckTimeout(d time.Duration) error { if d < MinTimeout || d > MaxTimeout { return ErrTimeout } return nil } // PlainName reports whether name is a plain base name: not empty, at most // 255 bytes, valid UTF-8, no slash, backslash or control character, and // not "." or "..". The console checks it, and the box again (spec §7). func PlainName(name string) bool { if name == "" || name == "." || name == ".." || len(name) > 255 || !utf8.ValidString(name) { return false } for _, r := range name { if r == '/' || r == '\\' || unicode.IsControl(r) { return false } } return true } // CheckFiles is spec §5.1's file rule: plain names, each once, sizes not // negative, 64 MB in total. func CheckFiles(files []MetaFile) error { seen := map[string]bool{} var total int64 for _, f := range files { if !PlainName(f.Name) { return &FileNameError{Name: f.Name} } if seen[f.Name] { return &FileNameError{Name: f.Name, Twice: true} } seen[f.Name] = true if f.Size < 0 { return ErrFilesTooLarge } total += f.Size if total > MaxFileBytes { return ErrFilesTooLarge } } return nil }