mirror of https://github.com/ory/hydra
105 lines
2.8 KiB
Go
105 lines
2.8 KiB
Go
// Copyright © 2023 Ory Corp
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package jsonnetsecure
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
const (
|
|
GiB uint64 = 1024 * 1024 * 1024
|
|
// Generous limit on virtual memory including the peak memory allocated by the Go runtime, the Jsonnet VM,
|
|
// and the Jsonnet script.
|
|
// This number was acquired by running:
|
|
// Found by trial and error with:
|
|
// `ulimit -Sv 1048576 && echo '{"Snippet": "{user_id: std.repeat(\'a\', 1000)}"}' | kratos jsonnet -0`
|
|
// NOTE: Ideally we'd like to limit RSS but that is not possible on Linux with `ulimit/setrlimit(2)` - only with cgroups.
|
|
virtualMemoryLimitBytes = 2 * GiB
|
|
)
|
|
|
|
func NewJsonnetCmd() *cobra.Command {
|
|
var null bool
|
|
cmd := &cobra.Command{
|
|
Use: "jsonnet",
|
|
Short: "Run Jsonnet as a CLI command",
|
|
Hidden: true,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
|
|
// This could fail because current limits are lower than what we tried to set,
|
|
// so we still continue in this case.
|
|
SetVirtualMemoryLimit(virtualMemoryLimitBytes)
|
|
|
|
if null {
|
|
return scan(cmd.OutOrStdout(), cmd.InOrStdin())
|
|
}
|
|
|
|
input, err := io.ReadAll(cmd.InOrStdin())
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to read from stdin")
|
|
}
|
|
|
|
json, err := eval(input)
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to evaluate jsonnet")
|
|
}
|
|
|
|
if _, err := io.WriteString(cmd.OutOrStdout(), json); err != nil {
|
|
return errors.Wrap(err, "failed to write json output")
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
cmd.Flags().BoolVarP(&null, "null", "0", false,
|
|
`Read multiple snippets and parameters from stdin separated by null bytes.
|
|
Output will be in the same order as inputs, separated by null bytes.
|
|
Evaluation errors will also be reported to stdout, separated by null bytes.
|
|
Non-recoverable errors are written to stderr and the program will terminate with a non-zero exit code.`)
|
|
|
|
return cmd
|
|
}
|
|
|
|
func scan(w io.Writer, r io.Reader) error {
|
|
scanner := bufio.NewScanner(r)
|
|
scanner.Split(splitNull)
|
|
for scanner.Scan() {
|
|
json, err := eval(scanner.Bytes())
|
|
if err != nil {
|
|
json = fmt.Sprintf("ERROR: %s", err)
|
|
}
|
|
if _, err := fmt.Fprintf(w, "%s%c", json, 0); err != nil {
|
|
return errors.Wrap(err, "failed to write json output")
|
|
}
|
|
}
|
|
return errors.Wrap(scanner.Err(), "failed to read from stdin")
|
|
}
|
|
|
|
func eval(input []byte) (json string, err error) {
|
|
var params processParameters
|
|
if err := params.Decode(input); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
vm := MakeSecureVM()
|
|
|
|
for _, it := range params.ExtCodes {
|
|
vm.ExtCode(it.Key, it.Value)
|
|
}
|
|
for _, it := range params.ExtVars {
|
|
vm.ExtVar(it.Key, it.Value)
|
|
}
|
|
for _, it := range params.TLACodes {
|
|
vm.TLACode(it.Key, it.Value)
|
|
}
|
|
for _, it := range params.TLAVars {
|
|
vm.TLAVar(it.Key, it.Value)
|
|
}
|
|
|
|
return vm.EvaluateAnonymousSnippet(params.Filename, params.Snippet)
|
|
}
|