diff --git a/docs/content/manual/manual.yml b/docs/content/manual/manual.yml index 75580de2f5..60d8cb77a3 100644 --- a/docs/content/manual/manual.yml +++ b/docs/content/manual/manual.yml @@ -297,6 +297,11 @@ sections: operations that would allow the filter code to access data other than the input data that is explicitly specified in the invocation. + This flag also hides all environment variables from the enviroment + where jq was run by setting `$ENV` and `env` to be an empty object. + If you need to pass named arguments to a sandboxed jq filter, use the + `--arg` and/or `--argjson` options to pass them explicitly. + * `--binary` / `-b`: Windows users using WSL, MSYS2, or Cygwin, should use this option @@ -2019,6 +2024,9 @@ sections: `env` outputs an object representing jq's current environment. + `$ENV` and `env` will be an empty object if jq was run with the + `--sandbox` flag. + At the moment there is no builtin for setting environment variables. diff --git a/jq.1.prebuilt b/jq.1.prebuilt index d5c34baaef..825949870c 100644 --- a/jq.1.prebuilt +++ b/jq.1.prebuilt @@ -228,6 +228,9 @@ Another way to set the exit status is with the \fBhalt_error\fR builtin function .IP Prevent the use of modules (\fBimport\fR/\fBinclude\fR) or any other file operations that would allow the filter code to access data other than the input data that is explicitly specified in the invocation\. . +.IP +This flag also hides all environment variables from the enviroment where jq was run by setting \fB$ENV\fR and \fBenv\fR to be an empty object\. If you need to pass named arguments to a sandboxed jq filter, use the \fB\-\-arg\fR and/or \fB\-\-argjson\fR options to pass them explicitly\. +. .TP \fB\-\-binary\fR / \fB\-b\fR: . @@ -2195,6 +2198,9 @@ Note that this can be overriden in the command\-line with \fB\-\-arg\fR and rela \fBenv\fR outputs an object representing jq\'s current environment\. . .P +\fB$ENV\fR and \fBenv\fR will be an empty object if jq was run with the \fB\-\-sandbox\fR flag\. +. +.P At the moment there is no builtin for setting environment variables\. . .IP "" 4 diff --git a/src/builtin.c b/src/builtin.c index ebc1863d47..0485186609 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -1137,6 +1137,11 @@ extern char **environ; static jv f_env(jq_state *jq, jv input) { jv_free(input); jv env = jv_object(); + + // A sandboxed filter doesn't have access to environment variables, + // so in such a case we return the empty object without using environ. + if (jq_is_sandbox(jq)) return env; + const char *var, *val; for (char **e = environ; *e != NULL; e++) { var = e[0]; diff --git a/src/compile.c b/src/compile.c index e5e65f2014..8d77b1d4dc 100644 --- a/src/compile.c +++ b/src/compile.c @@ -1367,7 +1367,7 @@ static int compile(struct bytecode* bc, block b, struct locfile* lf, jv args, jv return errors; } -int block_compile(block b, struct bytecode** out, struct locfile* lf, jv args) { +int block_compile(block b, struct bytecode** out, struct locfile* lf, jv args, int is_sandbox) { struct bytecode* bc = jv_mem_alloc(sizeof(struct bytecode)); bc->parent = 0; bc->nclosures = 0; @@ -1377,7 +1377,13 @@ int block_compile(block b, struct bytecode** out, struct locfile* lf, jv args) { bc->globals->cfunctions = jv_mem_calloc(ncfunc, sizeof(struct cfunction)); bc->globals->cfunc_names = jv_array(); bc->debuginfo = jv_object_set(jv_object(), jv_string("name"), jv_null()); - jv env = jv_invalid(); + + // When sandboxed, we don't want to expose environment vars to the program, + // so we create an empty object which is already valid. This prevents a + // later step from creating a populated `$ENV` object, because that step + // only does so if the current value for `env` is invalid. + jv env = is_sandbox ? jv_object() : jv_invalid(); + int nerrors = compile(bc, b, lf, args, &env); jv_free(args); jv_free(env); diff --git a/src/compile.h b/src/compile.h index c1512e6b87..db3fa28b71 100644 --- a/src/compile.h +++ b/src/compile.h @@ -79,7 +79,7 @@ block block_drop_unreferenced(block body); jv block_take_imports(block* body); jv block_list_funcs(block body, int omit_underscores); -int block_compile(block, struct bytecode**, struct locfile*, jv); +int block_compile(block, struct bytecode**, struct locfile*, jv, int is_sandbox); void block_free(block); diff --git a/src/execute.c b/src/execute.c index 40f18394f4..f107084d23 100644 --- a/src/execute.c +++ b/src/execute.c @@ -1246,7 +1246,7 @@ int jq_compile_args(jq_state *jq, const char* str, jv args) { if (nerrors == 0) { nerrors = builtins_bind(jq, &program); if (nerrors == 0) { - nerrors = block_compile(program, &jq->bc, locations, args2obj(args)); + nerrors = block_compile(program, &jq->bc, locations, args2obj(args), jq_is_sandbox(jq)); } } else jv_free(args); diff --git a/tests/shtest b/tests/shtest index 4e062963aa..b5cb2faa2c 100755 --- a/tests/shtest +++ b/tests/shtest @@ -396,6 +396,28 @@ if $VALGRIND $Q $JQ -L ./tests/modules --sandbox -n 'import "a" as a; empty'; th exit 1 fi +## Test environment variable access + +if [ "$(FOO=foo $VALGRIND $Q $JQ -nr '$ENV.FOO')" != foo ]; then + echo "couldn't read an environment variable via \$ENV" 1>&2 + exit 1 +fi + +if [ "$(FOO=foo $VALGRIND $Q $JQ --sandbox -nr '$ENV.FOO')" != null ]; then + echo "\$ENV should have been empty due to the sandbox flag" 1>&2 + exit 1 +fi + +if [ "$(FOO=foo $VALGRIND $Q $JQ -nr 'env.FOO')" != foo ]; then + echo "couldn't read an environment variable via \env" 1>&2 + exit 1 +fi + +if [ "$(FOO=foo $VALGRIND $Q $JQ --sandbox -nr 'env.FOO')" != null ]; then + echo "\env should have been empty due to the sandbox flag" 1>&2 + exit 1 +fi + ## Halt if ! $VALGRIND $Q $JQ -n halt; then