Sitelet https://github.com/luarocks/luarocks-site/commit/master
Skip to content

Commit bec0fda

Browse files
committed
brwap parser on ci??
1 parent 94d5cef commit bec0fda

3 files changed

Lines changed: 98 additions & 25 deletions

File tree

‎.github/workflows/spec.yml‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,6 @@ jobs:
1515
1616
- name: test
1717
run: |
18-
docker run luarocks-test
18+
# bubblewrap needs to create namespaces, REQUIRE_SANDBOX fails the
19+
# sandbox specs instead of skipping them when it can't
20+
docker run --cap-add SYS_ADMIN --security-opt seccomp=unconfined --security-opt apparmor=unconfined -e REQUIRE_SANDBOX=1 luarocks-test

‎Dockerfile.test‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
FROM ghcr.io/leafo/lapis-archlinux-itchio:2026-04-23
22
MAINTAINER leaf corcoran <leafot@gmail.com>
33

4+
# TODO: this will break once the base image is too out of date from the archlinux repo
5+
RUN pacman -Sy --noconfirm bubblewrap
6+
47
WORKDIR /site/luarocks.org
58
ADD . .
69
ENTRYPOINT ./ci.sh

‎spec/helpers/rockspec_eval_spec.moon‎

Lines changed: 92 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,45 @@
11
config = require("lapis.config").get!
22

33
import eval_rockspec, run_sandboxed, get_sandbox_source, decode_result from require "helpers.rockspec_eval"
4+
import shell_escape from require "lapis.cmd.path"
5+
6+
RESTY = "/usr/local/openresty/bin/resty"
47

58
-- busted may run on PUC Lua, but the sandbox always runs on LuaJIT
69
bytecode = string.dump -> 1
710

11+
local bwrap_available
812
can_bwrap = ->
9-
return false unless io.open "/usr/bin/bwrap"
10-
status = os.execute "unshare -Ur true > /dev/null 2>&1"
11-
status == 0 or status == true
13+
if bwrap_available == nil
14+
status = io.open("/usr/bin/bwrap") and os.execute "unshare -Ur true > /dev/null 2>&1"
15+
bwrap_available = status == 0 or status == true
16+
bwrap_available
17+
18+
-- CI sets REQUIRE_SANDBOX so a missing dependency fails the build instead of
19+
-- quietly skipping the specs that cover production's configuration
20+
skip = (reason) ->
21+
assert not os.getenv("REQUIRE_SANDBOX"), reason
22+
pending reason
23+
24+
bwrap_it = (name, fn) ->
25+
it name, ->
26+
return skip "bubblewrap is unavailable" unless can_bwrap!
27+
fn!
28+
29+
-- Runs lua with resty, where the child is run with ngx.pipe like it is in
30+
-- the web server. Returns everything it printed
31+
run_resty = (lua) ->
32+
f = assert io.popen "LAPIS_ENVIRONMENT=test #{RESTY} -I . -e '#{shell_escape lua}' 2>&1"
33+
with f\read "*a"
34+
f\close!
35+
36+
resty_eval = (rockspec) ->
37+
run_resty string.format [[
38+
local config = require("lapis.config").get()
39+
config.rockspec_sandbox = { bwrap = true }
40+
local spec, err = require("helpers.rockspec_eval").eval_rockspec(%q)
41+
io.write("result: ", tostring(spec and spec.package), " ", tostring(err))
42+
]], rockspec
1243

1344
describe "helpers.rockspec_eval", ->
1445
local original
@@ -80,18 +111,7 @@ describe "helpers.rockspec_eval", ->
80111
assert.falsy spec
81112
assert.same "Failed to eval rockspec", err
82113

83-
describe "in process", ->
84-
before_each ->
85-
config.rockspec_sandbox = nil
86-
87-
data_tests!
88-
89-
describe "child process", ->
90-
before_each ->
91-
config.rockspec_sandbox = {}
92-
93-
data_tests!
94-
114+
limit_tests = (it) ->
95115
it "kills a backtracking pattern", ->
96116
spec, err = eval_rockspec [[x = ("a"):rep(30000):find(".-.-.-.-b")]]
97117
assert.falsy spec
@@ -107,6 +127,19 @@ describe "helpers.rockspec_eval", ->
107127
assert.falsy spec
108128
assert.same "Failed to eval rockspec", err
109129

130+
describe "in process", ->
131+
before_each ->
132+
config.rockspec_sandbox = nil
133+
134+
data_tests!
135+
136+
describe "child process", ->
137+
before_each ->
138+
config.rockspec_sandbox = {}
139+
140+
data_tests!
141+
limit_tests it
142+
110143
it "reports a sandbox that can't start", ->
111144
config.rockspec_sandbox = { luajit: "/nonexistent" }
112145
spec, err = eval_rockspec [[package = "my-module"]]
@@ -156,23 +189,58 @@ describe "helpers.rockspec_eval", ->
156189
before_each ->
157190
config.rockspec_sandbox = { bwrap: true }
158191

159-
it "evaluates a rockspec", ->
160-
unless can_bwrap!
161-
pending "bubblewrap is unavailable"
162-
return
163-
192+
bwrap_it "evaluates a rockspec", ->
164193
spec = assert eval_rockspec [[package = "my-module"]]
165194
assert.same { package: "my-module" }, spec
166195

167-
it "hides the site's files", ->
168-
unless can_bwrap!
169-
pending "bubblewrap is unavailable"
170-
return
196+
limit_tests bwrap_it
171197

198+
bwrap_it "hides the site's files", ->
172199
site_config = "#{io.popen("pwd")\read "*l"}/config.moon"
173200
assert io.open site_config
174201

175202
out = run_sandboxed string.format([[
176203
io.write(tostring(io.open(%q) ~= nil), " ", tostring(io.open("/etc/passwd") ~= nil))
177204
]], site_config), ""
178205
assert.same "false false", out
206+
207+
bwrap_it "has no network", ->
208+
-- a udp connect to 1.1.1.1:53 sends nothing, it only needs a route
209+
out = run_sandboxed [[
210+
local ffi = require("ffi")
211+
ffi.cdef("int socket(int domain, int type, int protocol); int connect(int fd, const void *addr, unsigned int len);")
212+
local addr = ffi.new("uint8_t[16]", {2, 0, 0, 53, 1, 1, 1, 1})
213+
io.write(tostring(ffi.C.connect(ffi.C.socket(2, 2, 0), addr, 16)))
214+
]], ""
215+
assert.same "-1", out
216+
217+
describe "inside nginx", ->
218+
resty_it = (name, fn) ->
219+
it name, ->
220+
return skip "resty is unavailable" unless io.open RESTY
221+
fn!
222+
223+
resty_it "evaluates a rockspec under bubblewrap", ->
224+
return skip "bubblewrap is unavailable" unless can_bwrap!
225+
out = resty_eval [[package = "my-module"]]
226+
assert.truthy out\find("result: my-module nil", 1, true), out
227+
228+
resty_it "kills a backtracking pattern under bubblewrap", ->
229+
return skip "bubblewrap is unavailable" unless can_bwrap!
230+
out = resty_eval [[x = ("a"):rep(30000):find(".-.-.-.-b")]]
231+
assert.truthy out\find("result: nil Failed to eval rockspec", 1, true), out
232+
233+
resty_it "applies the timeout to the whole run", ->
234+
-- each write restarts a per operation timeout, but not the deadline
235+
out = run_resty [[
236+
local config = require("lapis.config").get()
237+
local out, err = require("helpers.rockspec_eval").run_sandboxed([=[
238+
for i = 1, 20 do
239+
os.execute("/usr/bin/sleep 0.3")
240+
io.write("a")
241+
io.stdout:flush()
242+
end
243+
]=], "", { timeout = 1 })
244+
io.write("result: ", tostring(err))
245+
]]
246+
assert.truthy out\find("result: timeout", 1, true), out

0 commit comments

Comments
 (0)