Do some work on redbean

- Rewrite Slurp() API to be like string.sub()
- Introduce a new Barf() API for creating files
- Update Redbean `-S` sandbox flag to do unveiling
This commit is contained in:
Justine Tunney 2022-07-22 20:44:24 -07:00
parent 742251dd92
commit 48ce3ad7cc
9 changed files with 296 additions and 33 deletions

View file

@ -39,24 +39,32 @@ local function main()
WriteForm("https://www.cloudflare.com/robots.txt")
elseif GetMethod() == 'POST' then
status, headers, payload = Fetch(GetParam('url'))
WriteForm(GetParam('url'))
Write('<dl>\r\n')
Write('<dt>Status\r\n')
Write('<dd><p>%d %s\r\n' % {status, GetHttpReason(status)})
Write('<dt>Headers\r\n')
Write('<dd>\r\n')
for k,v in pairs(headers) do
Write('<div class="hdr"><strong>')
Write(EscapeHtml(k))
Write('</strong>: ')
Write(EscapeHtml(v))
Write('</div>\r\n')
if status then
WriteForm(GetParam('url'))
Write('<dl>\r\n')
Write('<dt>Status\r\n')
Write('<dd><p>%d %s\r\n' % {status, GetHttpReason(status)})
Write('<dt>Headers\r\n')
Write('<dd>\r\n')
for k,v in pairs(headers) do
Write('<div class="hdr"><strong>')
Write(EscapeHtml(k))
Write('</strong>: ')
Write(EscapeHtml(v))
Write('</div>\r\n')
end
Write('<dt>Payload\r\n')
Write('<dd><pre>')
Write(EscapeHtml(VisualizeControlCodes(payload)))
Write('</pre>\r\n')
Write('</dl>\r\n')
else
err = headers
WriteForm(GetParam('url'))
Write('<h3>Error</h3>\n')
Write('<p>')
Write(EscapeHtml(VisualizeControlCodes(err)))
end
Write('<dt>Payload\r\n')
Write('<dd><pre>')
Write(EscapeHtml(VisualizeControlCodes(payload)))
Write('</pre>\r\n')
Write('</dl>\r\n')
else
ServeError(405)
SetHeader('Allow', 'GET, HEAD, POST')

View file

@ -1499,8 +1499,46 @@ FUNCTIONS
Same as the -u flag if called from .init.lua. Can be used to
configure the uniprocess mode. The current value is returned.
Slurp(filename:str) → str
Reads file data from local file system.
Slurp(filename:str[, i:int[, j:int]])
├─→ data:str
└─→ nil, unix.Errno
Writes all data from file the easy way.
This function reads file data from local file system. Zip file
assets can be accessed using the `/zip/...` prefix.
`i` and `j` may be used to slice a substring in `filename`.
These parameters are 1-indexed and behave consistently with
Lua's string.sub() API. For example:
assert(Barf('x.txt', 'abc123'))
assert(assert(Slurp('x.txt', 2, 3)) == 'bc')
This function is uninterruptible so `unix.EINTR` errors will
be ignored. This should only be a concern if you've installed
signal handlers. Use the UNIX API if you need to react to it.
Barf(filename:str, data:str[, mode:int[, flags:int[, offset:int]]])
├─→ true
└─→ nil, unix.Errno
Writes all data to file the easy way.
This function writes to the local file system.
`mode` defaults to `0644`. This parameter is ignored when
`flags` doesn't have `unix.O_CREAT`.
`flags` defaults to `unix.O_TRUNC | unix.O_CREAT`.
`offset` is 1-indexed and may be used to overwrite arbitrary
slices within a file when used in conjunction with `flags=0`.
For example:
assert(Barf('x.txt', 'abc123'))
assert(Barf('x.txt', 'XX', 0, 0, 3))
assert(assert(Slurp('x.txt', 1, 6)) == 'abXX23')
Sleep(seconds:number)
Sleeps the specified number of seconds (can be fractional). The
@ -3069,7 +3107,6 @@ UNIX MODULE
Sets user id for file system ops.
unix.setgid(gid:int)
├─→ true
└─→ nil, unix.Errno

View file

@ -21,7 +21,9 @@
#include "libc/bits/popcnt.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/rusage.h"
#include "libc/calls/struct/stat.h"
#include "libc/dns/dns.h"
#include "libc/errno.h"
#include "libc/fmt/itoa.h"
#include "libc/fmt/leb128.h"
#include "libc/intrin/kprintf.h"
@ -43,6 +45,7 @@
#include "libc/sock/sock.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/rusage.h"
#include "libc/sysv/consts/sock.h"
#include "libc/time/time.h"
@ -54,6 +57,8 @@
#include "third_party/lua/cosmo.h"
#include "third_party/lua/lauxlib.h"
#include "third_party/lua/lua.h"
#include "third_party/lua/luaconf.h"
#include "third_party/lua/lunix.h"
#include "third_party/mbedtls/md.h"
#include "third_party/mbedtls/md5.h"
#include "third_party/mbedtls/platform.h"
@ -368,19 +373,128 @@ int LuaGetMonospaceWidth(lua_State *L) {
return 1;
}
// Slurp(path:str[, i:int[, j:int]])
// ├─→ data:str
// └─→ nil, unix.Errno
int LuaSlurp(lua_State *L) {
char *p, *f;
size_t n;
f = luaL_checkstring(L, 1);
if ((p = xslurp(f, &n))) {
lua_pushlstring(L, p, n);
free(p);
return 1;
ssize_t rc;
char tb[2048];
luaL_Buffer b;
struct stat st;
int fd, olderr;
bool shouldpread;
lua_Integer i, j, got;
olderr = errno;
if (lua_isnoneornil(L, 2)) {
i = 1;
} else {
lua_pushnil(L);
lua_pushstring(L, gc(xasprintf("Can't slurp file %`'s: %m", f)));
return 2;
i = luaL_checkinteger(L, 2);
}
if (lua_isnoneornil(L, 3)) {
j = LUA_MAXINTEGER;
} else {
j = luaL_checkinteger(L, 3);
}
luaL_buffinit(L, &b);
if ((fd = open(luaL_checkstring(L, 1), O_RDONLY | O_SEQUENTIAL)) == -1) {
return LuaUnixSysretErrno(L, "open", olderr);
}
if (i < 0 || j < 0) {
if (fstat(fd, &st) == -1) {
close(fd);
return LuaUnixSysretErrno(L, "fstat", olderr);
}
if (i < 0) {
i = st.st_size + (i + 1);
}
if (j < 0) {
j = st.st_size + (j + 1);
}
}
if (i < 1) {
i = 1;
}
shouldpread = i > 1;
for (; i <= j; i += got) {
if (shouldpread) {
rc = pread(fd, tb, MIN(j - i + 1, sizeof(tb)), i - 1);
} else {
rc = read(fd, tb, MIN(j - i + 1, sizeof(tb)));
}
if (rc != -1) {
got = rc;
if (!got) break;
luaL_addlstring(&b, tb, got);
} else if (errno == EINTR) {
errno = olderr;
got = 0;
} else {
close(fd);
return LuaUnixSysretErrno(L, "read", olderr);
}
}
if (close(fd) == -1) {
return LuaUnixSysretErrno(L, "close", olderr);
}
luaL_pushresult(&b);
return 1;
}
// Barf(path:str, data:str[, mode:int[, flags:int[, offset:int]]])
// ├─→ true
// └─→ nil, unix.Errno
int LuaBarf(lua_State *L) {
char *data;
ssize_t rc;
lua_Number offset;
size_t i, n, wrote;
int fd, mode, flags, olderr;
olderr = errno;
data = luaL_checklstring(L, 2, &n);
if (lua_isnoneornil(L, 5)) {
offset = 0;
} else {
offset = luaL_checkinteger(L, 5);
if (offset < 1) {
luaL_error(L, "offset must be >= 1");
unreachable;
}
--offset;
}
mode = luaL_optinteger(L, 3, 0644);
flags = O_WRONLY | O_SEQUENTIAL | luaL_optinteger(L, 4, O_TRUNC | O_CREAT);
if (flags & O_NONBLOCK) {
luaL_error(L, "O_NONBLOCK not allowed");
unreachable;
}
if ((flags & O_APPEND) && offset) {
luaL_error(L, "O_APPEND with offset not possible");
unreachable;
}
if ((fd = open(luaL_checkstring(L, 1), flags, mode)) == -1) {
return LuaUnixSysretErrno(L, "open", olderr);
}
for (i = 0; i < n; i += wrote) {
if (offset) {
rc = pwrite(fd, data + i, n - i, offset + i);
} else {
rc = write(fd, data + i, n - i);
}
if (rc != -1) {
wrote = rc;
} else if (errno == EINTR) {
errno = olderr;
wrote = 0;
} else {
close(fd);
return LuaUnixSysretErrno(L, "write", olderr);
}
}
if (close(fd) == -1) {
return LuaUnixSysretErrno(L, "close", olderr);
}
lua_pushboolean(L, true);
return 1;
}
int LuaResolveIp(lua_State *L) {

View file

@ -10,6 +10,7 @@ int LuaRe(lua_State *);
int luaopen_argon2(lua_State *);
int luaopen_lsqlite3(lua_State *);
int LuaBarf(lua_State *);
int LuaBenchmark(lua_State *);
int LuaBin(lua_State *);
int LuaBsf(lua_State *);

View file

@ -5126,6 +5126,7 @@ static const char *const kDontAutoComplete[] = {
// </SORTED>
static const luaL_Reg kLuaFuncs[] = {
{"Barf", LuaBarf}, //
{"Benchmark", LuaBenchmark}, //
{"Bsf", LuaBsf}, //
{"Bsr", LuaBsr}, //
@ -6543,18 +6544,29 @@ static int ExitWorker(void) {
_Exit(0);
}
static void UnveilRedbean(void) {
size_t i;
for (i = 0; i < stagedirs.n; ++i) {
unveil(stagedirs.p[i].s, "r");
}
unveil(0, 0);
}
static int EnableSandbox(void) {
switch (sandboxed) {
case 0:
return 0;
case 1: // -S
DEBUGF("(stat) applying '%s' sandbox policy", "online");
UnveilRedbean();
return pledge("stdio rpath inet dns", 0);
case 2: // -SS
DEBUGF("(stat) applying '%s' sandbox policy", "offline");
UnveilRedbean();
return pledge("stdio rpath", 0);
default: // -SSS
DEBUGF("(stat) applying '%s' sandbox policy", "contained");
UnveilRedbean();
return pledge("stdio", 0);
}
}