mirror of
https://github.com/pocketpy/pocketpy
synced 2026-08-05 05:37:10 +08:00
Add py_Callbacks.open_file hook for embedder file-access policy
Adds an optional open_file callback to py_Callbacks, consulted before script-reachable file operations: - io.FileIO(path, mode): called with the fopen mode string before the open - os.remove(path): called with the literal "delete" Returning false rejects the operation and raises OSError; returning true (or leaving the callback NULL, which is the default) allows it. Existing embedders are unaffected -- the default is NULL and behavior is unchanged. This lets an embedder enforce its own path policy using its own canonicalization instead of reimplementing path checks inside the VM.
This commit is contained in:
parent
e144785436
commit
46cc281099
@ -78,6 +78,13 @@ typedef struct py_Callbacks {
|
||||
PY_MAYBENULL void (*gc_mark)(void (*f)(py_Ref val, void* ctx), void* ctx);
|
||||
/// Used by `PRINT_EXPR` bytecode.
|
||||
PY_MAYBENULL bool (*displayhook)(py_Ref val) PY_RAISE;
|
||||
// open_file hook contributed by fdtd.io (Hector), 2026.
|
||||
/// Consulted before a script-reachable file operation. `path` is the target path;
|
||||
/// `mode` is the fopen mode string for `io.FileIO`, or the literal "delete" for
|
||||
/// `os.remove`. Return true to allow the operation, false to reject it (the binding
|
||||
/// then raises OSError). NULL (the default) allows everything, so existing embedders
|
||||
/// are unaffected. Lets an embedder enforce its own path policy.
|
||||
PY_MAYBENULL bool (*open_file)(const char* path, const char* mode);
|
||||
} py_Callbacks;
|
||||
|
||||
/// A struct contains the application-level callbacks.
|
||||
|
||||
@ -92,6 +92,7 @@ void VM__ctor(VM* self) {
|
||||
self->callbacks.print = pk_default_print;
|
||||
self->callbacks.flush = pk_default_flush;
|
||||
self->callbacks.getchr = pk_default_getchr;
|
||||
self->callbacks.open_file = NULL;
|
||||
|
||||
self->last_retval = *py_NIL();
|
||||
self->unhandled_exc = *py_NIL();
|
||||
|
||||
@ -71,6 +71,10 @@ static bool os_remove(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
PY_CHECK_ARG_TYPE(0, tp_str);
|
||||
const char* path = py_tostr(py_arg(0));
|
||||
// open_file policy hook: "delete" pseudo-mode for os.remove.
|
||||
if(pk_current_vm->callbacks.open_file && !pk_current_vm->callbacks.open_file(path, "delete")) {
|
||||
return OSError("os.remove not permitted: '%s'", path);
|
||||
}
|
||||
int code = remove(path);
|
||||
if(code != 0) {
|
||||
const char* msg = strerror(errno);
|
||||
@ -111,6 +115,11 @@ static bool io_FileIO__new__(int argc, py_Ref argv) {
|
||||
io_FileIO* ud = py_newobject(py_retval(), cls, 0, sizeof(io_FileIO));
|
||||
ud->path = py_tostr(py_arg(1));
|
||||
ud->mode = py_tostr(py_arg(2));
|
||||
// open_file policy hook: consulted with the fopen mode string before the open.
|
||||
if(pk_current_vm->callbacks.open_file &&
|
||||
!pk_current_vm->callbacks.open_file(ud->path, ud->mode)) {
|
||||
return OSError("file open not permitted: '%s' (mode '%s')", ud->path, ud->mode);
|
||||
}
|
||||
ud->file = fopen(ud->path, ud->mode);
|
||||
if(ud->file == NULL) {
|
||||
const char* msg = strerror(errno);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user