mirror of
https://github.com/aykhans/sarin.git
synced 2026-10-17 06:53:52 +00:00
fix(script): enforce script body limits without streaming
Streaming accepted a chunked body cut at a chunk boundary as complete, so the limit now comes from the client that reads the body, picked per requested size. Script requests keep their own default, maxBodySize accepts sizes like 10MiB, and files sarin downloads for itself are capped at 64MiB.
This commit is contained in:
16 files changed
+222
-173
No files matched your search
+20
-3
@@ -67,8 +67,8 @@ const httpDefaultMethod = http.MethodGet
|
||||
// httpMaxRedirectsLimit is the highest maxRedirects a script may ask for.
|
||||
const httpMaxRedirectsLimit = 100
|
||||
|
||||
// httpMaxBodySizeLimit is the highest maxBodySize a script may ask for, 1 TiB.
|
||||
const httpMaxBodySizeLimit int64 = 1 << 40
|
||||
// httpMaxBodySizeLimit is the highest maxBodySize a script may ask for.
|
||||
const httpMaxBodySizeLimit int64 = types.ByteSizeLimit
|
||||
|
||||
// Option keys accepted by the http function.
|
||||
const (
|
||||
@@ -132,13 +132,30 @@ func parseHTTPMaxBodySize(size float64) (int64, error) {
|
||||
httpOptionMaxBodySize,
|
||||
types.NewScriptTypeError(
|
||||
"a whole number of bytes between 0 and "+strconv.FormatInt(httpMaxBodySizeLimit, 10),
|
||||
strconv.FormatFloat(size, 'g', -1, 64),
|
||||
strconv.FormatFloat(size, 'f', -1, 64),
|
||||
),
|
||||
)
|
||||
}
|
||||
return int64(size), nil
|
||||
}
|
||||
|
||||
// parseHTTPMaxBodySizeString reads a body limit written with a unit, such as "10MiB".
|
||||
// It can return the following errors:
|
||||
// - types.ScriptHTTPOptionError
|
||||
func parseHTTPMaxBodySizeString(value string) (int64, error) {
|
||||
size, err := types.ParseByteSize(value)
|
||||
if err != nil {
|
||||
return 0, types.NewScriptHTTPOptionError(httpOptionMaxBodySize, err)
|
||||
}
|
||||
if size > uint64(httpMaxBodySizeLimit) {
|
||||
return 0, types.NewScriptHTTPOptionError(
|
||||
httpOptionMaxBodySize,
|
||||
types.NewScriptTypeError("a size up to "+types.FormatByteSize(uint64(httpMaxBodySizeLimit)), value),
|
||||
)
|
||||
}
|
||||
return int64(size), nil
|
||||
}
|
||||
|
||||
// parseHTTPTimeout parses a Go duration string such as "500ms" or "2s".
|
||||
// It can return the following errors:
|
||||
// - types.ScriptHTTPOptionError
|
||||
|
||||
@@ -118,13 +118,18 @@ func (e *JsEngine) parseHTTPOptions(opts *goja.Object, req *HTTPRequest) error {
|
||||
return err
|
||||
}
|
||||
case httpOptionMaxBodySize:
|
||||
size, ok := numberValue(value)
|
||||
if !ok {
|
||||
return types.NewScriptHTTPOptionError(name, types.NewScriptTypeError("number", jsTypeName(value)))
|
||||
}
|
||||
|
||||
var err error
|
||||
if req.MaxBodySize, err = parseHTTPMaxBodySize(size); err != nil {
|
||||
switch size := value.Export().(type) {
|
||||
case string:
|
||||
req.MaxBodySize, err = parseHTTPMaxBodySizeString(size)
|
||||
default:
|
||||
number, ok := numberValue(value)
|
||||
if !ok {
|
||||
return types.NewScriptHTTPOptionError(name, types.NewScriptTypeError("number or size string", jsTypeName(value)))
|
||||
}
|
||||
req.MaxBodySize, err = parseHTTPMaxBodySize(number)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -107,12 +107,14 @@ func (e *LuaEngine) parseHTTPOptions(opts *lua.LTable, req *HTTPRequest) error {
|
||||
}
|
||||
req.MaxRedirects, err = parseHTTPMaxRedirects(float64(count))
|
||||
case httpOptionMaxBodySize:
|
||||
size, ok := v.(lua.LNumber)
|
||||
if !ok {
|
||||
err = types.NewScriptHTTPOptionError(name, types.NewScriptTypeError("number", v.Type().String()))
|
||||
return
|
||||
switch size := v.(type) {
|
||||
case lua.LNumber:
|
||||
req.MaxBodySize, err = parseHTTPMaxBodySize(float64(size))
|
||||
case lua.LString:
|
||||
req.MaxBodySize, err = parseHTTPMaxBodySizeString(string(size))
|
||||
default:
|
||||
err = types.NewScriptHTTPOptionError(name, types.NewScriptTypeError("number or size string", v.Type().String()))
|
||||
}
|
||||
req.MaxBodySize, err = parseHTTPMaxBodySize(float64(size))
|
||||
default:
|
||||
err = types.NewScriptHTTPOptionError(name, types.ErrScriptHTTPUnknownOption)
|
||||
}
|
||||
|
||||
@@ -171,10 +171,13 @@ func fetchURL(ctx context.Context, url string) (string, error) {
|
||||
return "", types.NewHTTPStatusError(url, resp.StatusCode, resp.Status)
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(io.LimitReader(resp.Body, types.RemoteFetchLimit+1))
|
||||
if err != nil {
|
||||
return "", types.NewHTTPFetchError(url, err)
|
||||
}
|
||||
if len(data) > types.RemoteFetchLimit {
|
||||
return "", types.NewHTTPFetchError(url, types.ErrRemoteFileTooLarge)
|
||||
}
|
||||
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user