Commit 5dcbdbef authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner

sandbox: Read only flags support

parent 8b8469fb
......@@ -626,14 +626,16 @@ absolute. Referencing self as an empty string works. The table is
generated on the fly, therefore the script may not find out what other
scripts ever run.
The subtable is wholly instantiated, therefore the script can list the
keys it left itself from previous run.
In case of the own flags, the table is fully instantiated, so they can
be written and iterated (using `next` and `pairs`). If it is flags
belonging to other script, the table is read only and doesn't allow
iterating (eg. it is possible to query a flag value, but not possible
to discover which flags exist).
Flags may be only strings.
Any modification to the own flags table is stored on successful
updater termination or when the script explicitly request it.
Modifications to other scripts' flags tables are not stored.
......@@ -279,6 +279,9 @@ for _, name in pairs({'model', 'board_name', 'turris_version', 'serial', 'archit
value = name
funcs.Restricted["flags"] = {
mode = "flags"
for name, val in pairs(G) do
funcs.Full[name] = {
mode = "inject",
......@@ -324,6 +327,61 @@ function level(l)
local function ro_proxy(flags)
return setmetatable({}, {
__index = function (_, name)
local result = flags[name]
if result and type(result) == 'string' then
return result
elseif result then
WARN("Type of flag " .. name .. " is " .. type(result) .. ", foreign access prevented")
return nil
__newindex = function ()
error(utils.exception("access violation", "Writing of foreign flags not allowed"))
local function flags_new(context)
return setmetatable({}, {
__index = function (_, path)
-- Make sure the hierarchy is there ‒ it was created by run_sandboxed, not standalone
local requested
if path == "" then
-- It's us
requested = context
elseif path.match('^/') then
-- It's an absolute path
requested = context.root_parent.hierarchy[path]
-- It's relative path ‒ construct the absolute one first
requested = context.root_parent.hierarchy[context.full_name + '/' + path]
if requested == context then
-- It's us. Provide full access to the table (eg. let it be written there as well)
return context.flags
TODO: We actually want to load the RO proxy from the flags storage.
The script's context might not exist yet (it hasn't run yet), but there
might be flags from previous run.
But as the storage doesn't exist yet, we shall do that later. For now,
we just construct a RO proxy on the fly.
if requested then
return ro_proxy(requested.flags)
return nil
Create a new context. The context inherits everything
from its parent (if the parent is not nil). The security
......@@ -349,6 +407,8 @@ function new(sec_level, parent)
result.sec_level = sec_level
-- Construct a new environment
result.env = {}
-- TODO: Load them somewhere
result.flags = {}
local inject = utils.clone
if sec_level >= level("Full") then
inject = function (...) return ... end
......@@ -358,6 +418,8 @@ function new(sec_level, parent)
result.env[n] = inject(v.value)
elseif v.mode == "state" then
result.env[n] = utils.clone(state_vars[v.value])
elseif v.mode == "flags" then
result.env[n] = flags_new(result)
elseif v.mode == "wrap" then
result.env[n] = function(...)
return v.value(result, ...)
......@@ -64,7 +64,7 @@ function test_context_new()
assert_equal(sandbox.state_vars.architectures[1], 'all')
context.env = nil
context.level_check = nil
assert_table_equal({sec_level = sandbox.level(level), tp = "context"}, context)
assert_table_equal({sec_level = sandbox.level(level), tp = "context", flags = {}}, context)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment