pub fn apply_global_blocks(lua: &Lua) -> Result<()>Expand description
Clear every name ASSAY_BLOCK_GLOBALS and the policy’s globals.block
name. One application point, and it is deliberately the last thing the
constructor does.
Running any of it earlier is what made blocking a name weaken the VM.
The mode gates skip a table that is not on _G — a feature-gated build
legitimately has none — so an earlier pass that deleted io meant the
io.popen stub and the io.open write guard were never installed, while
the real, ungated table sat in package.loaded for require to hand
back.
Two things keep a block list monotonic, and the ordering is the lesser
of them. clear_package_entry is what removes the survivor: a bare name
goes from package.loaded and package.preload as well as _G, so no
ungated handle is left for the skipped gate to have mattered. Running
last then makes the skip unreachable rather than merely harmless. Should
the cache clearing ever regress, this ordering would not save you — so
treat them as one mechanism, and do not move either half on the
assumption that the other covers it. Two tests pin it:
blocking_io_under_readonly_leaves_no_handle_at_all and
blocking_a_table_never_leaves_an_ungated_one_behind.
Call it again after installing a global of your own (the CLI’s arg),
or that global outlives the list that named it.