summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2021-03-08 15:38:43 +0100
committerJo-Philipp Wich <jo@mein.io>2021-03-08 19:18:58 +0100
commite196ee6689d1bf082531319185feea134bf888af (patch)
tree6b20bddd7eb4586492c64b5a842b88bd0da97bb1
parent330f7fec6858de97fe762c272ca499764bc6b02b (diff)
lib: retain prototype when passing scopes to include()
Change uc_include() to retain the prototype of the given scope object when processing includes. Also change the default behaviour to register the current VM scope as prototype on the passed scope object so that included code has access to functions such as length(), print() etc. by default. To actually sandbox the included code, the new `proto()` function can be used to create a scope object with an empty prototype: `include(..., proto({ ... }, {}))` Signed-off-by: Jo-Philipp Wich <jo@mein.io>
-rw-r--r--README.md24
-rw-r--r--lib.c11
2 files changed, 26 insertions, 9 deletions
diff --git a/README.md b/README.md
index d6e6b25..86d4e7d 100644
--- a/README.md
+++ b/README.md
@@ -987,9 +987,12 @@ execution scope with the given scope object.
By default, the file is executed within the same scope as the calling
`include()` but by passing an object as second argument, it is possible to
-override the scope available to the included file. This is useful to sandbox the
-included code and only grant it access to explicitely passed values and
-functions.
+extend the scope available to the included file. This is useful to supply
+additional properties as global variables to the included code.
+
+To sandbox included code, that is giving it only access to explicitely
+provided properties, the `proto()` function can be used to create a scope
+object with an empty prototype. See the examples below for details.
If the given path argument is not absolute, it is interpreted relative to the
directory of the current template file, that is the file that is invoking the
@@ -1002,13 +1005,22 @@ interpreted relative to the current working directory of the process.
// Load and execute "foo.uc" immediately
include("./foo.uc")
+// Execute the "supplemental.ucode" in an extended scope and make the "foo" and
+// "bar" properties available as global variables
+include("./supplemental.uc", {
+ foo: true,
+ bar: 123
+})
+
// Execute the "untrusted.ucode" in a sandboxed scope and make the "foo" and
-// "bar" variables as well as the "print" function available to it
-include("./untrusted.uc", {
+// "bar" variables as well as the "print" function available to it. By assigning
+// an empty prototype object to the scope, included code has no access to
+// other global values anymore
+include("./untrusted.uc", proto({
foo: true,
bar: 123,
print: print
-})
+}, {}))
```
#### 6.51. `warn(x, ...)`
diff --git a/lib.c b/lib.c
index 5e4c0a0..536fcd7 100644
--- a/lib.c
+++ b/lib.c
@@ -2125,6 +2125,7 @@ uc_include(uc_vm *vm, size_t nargs)
json_object *rv = NULL;
uc_closure *closure = NULL;
uc_prototype *sc;
+ bool put = false;
size_t i;
char *p;
@@ -2162,8 +2163,12 @@ uc_include(uc_vm *vm, size_t nargs)
return NULL;
}
- if (scope) {
- sc = uc_prototype_new(NULL);
+ if (uc_object_is_type(scope, UC_OBJ_PROTOTYPE)) {
+ sc = uc_object_as_prototype(scope);
+ }
+ else if (scope) {
+ sc = uc_prototype_new(vm->globals);
+ put = true;
json_object_object_foreach(scope, key, val)
json_object_object_add(sc->header.jso, key, uc_value_get(val));
@@ -2177,7 +2182,7 @@ uc_include(uc_vm *vm, size_t nargs)
free(p);
- if (scope)
+ if (put)
uc_value_put(sc->header.jso);
return NULL;