Cleanup, refactoring, fix edge cases for directory jailing

parent 34c5dd64
function (modules) {
if (typeof global === 'undefined') {
(function () {
var global = new Function('return this;')();
Object.defineProperty(global, 'global', {
value: global,
writable: true,
enumerable: false,
configurable: true
});
})();
}
// A stupid way to make dependencies available, that will
// probably necessary to be compatible with ye olde node.
global.dir = modules.dir
var testme = modules.compile('subexample/grandchild.js');
modules.print(JSON.stringify(testme(3, 4)));
return
}
......@@ -4,24 +4,9 @@ function (args) {
var modules = args.modules;
var Queues = args.Queues;
var timers = Queues.timers;
if (typeof global === 'undefined') {
(function () {
var global = new Function('return this;')();
Object.defineProperty(global, 'global', {
value: global,
writable: true,
enumerable: false,
configurable: true
});
})();
}
var result = modules.dir('./src');
result.sort();
modules.print(JSON.stringify(result));
var testme = modules.assert_safe_path('testme.js');
var testme = modules.compile('testme.js');
var testme = modules.compile('child.js');
testme(modules);
// modules.print(Object.keys(args.modules));
// modules.print(args.Queues.timers.length);
return result
return
}
function (a, b) {
// var foo = dir('.');
var result = dir('docs');
result.push(a * b);
result.sort();
return result;
}
function (a, b) {
return a * b;
}
function (modules) {
var testme = modules.assert_safe_path('subexample/testme.js');
var testme = modules.compile('subexample/testme.js');
modules.print(testme(3, 4));
return
}
......@@ -6,11 +6,13 @@ duk_int_t app_assert_safe_path (duk_context *ctx) {
return duk_error(ctx, DUK_ERR_ERROR, "Path cannot be empty.");
} else if (filename[0] == '/') {
return duk_error(ctx, DUK_ERR_ERROR, "Path should not start with '/' to reference the root directory: %s", filename);
} else if (strcmp(filename, "..") == 0) {
return duk_error(ctx, DUK_ERR_ERROR, "Path cannot be the parent directory '..'");
} else if (strstr(filename, "../") != NULL) {
return duk_error(ctx, DUK_ERR_ERROR, "Path should not contain '..' to reference parent directories: %s", filename);
} else if (filename[0] == '.') {
} else if (strncmp(filename, "./", 2) == 0) {
// (this restriction isn't about safety, just consistancy and KISS)
return duk_error(ctx, DUK_ERR_ERROR, "Path should not start with '.' to reference the current directory: %s", filename);
return duk_error(ctx, DUK_ERR_ERROR, "Path should not start with extraneous './' reference the current directory: %s", filename);
} else if (strstr(filename, "\\") != NULL) {
// No paths with non-standard file separators.
return duk_error(ctx, DUK_ERR_ERROR, "Path should not contain '\\' character, only use '/' to separate paths: %s", filename);
......
int app_push_dir (duk_context *ctx) {
// Check argument
const char *dirname = duk_require_string(ctx, -1);
// Safety check filename argument
app_assert_safe_path(ctx);
const char *dirname = duk_get_string(ctx, -1);
// Construct array
duk_idx_t dir_idx = duk_push_array(ctx);
// Shove filenames into array
struct dirent *dir;
int i = 0;
int ii = 0;
DIR *d = opendir(dirname);
if (d) {
while (i < 100) {
dir = readdir(d);
if (dir == NULL) break;
duk_push_string(ctx, dir->d_name);
duk_put_prop_index(ctx, dir_idx, i);
if (strcmp(dir->d_name, ".") != 0 &&
strcmp(dir->d_name, "..") != 0) {
duk_push_string(ctx, dir->d_name);
duk_put_prop_index(ctx, dir_idx, ii);
ii++;
}
i++;
}
closedir(d);
......
......@@ -13,29 +13,20 @@ int app_push_trusted_function (duk_context *ctx) {
// Replace with absolute filename
duk_pop(ctx);
duk_push_string(ctx, joined_path);
// Open the file
FILE * pFile = fopen(joined_path, "rb");
if (pFile == NULL) {
return duk_error(ctx, DUK_ERR_ERROR, "could not open file: %s", joined_path);
// Read the file
char *message = NULL;
char *buffer = NULL;
size_t bytesRead = NULL;
duk_int_t ret = c_read_script_file(joined_path, &message, &buffer, &bytesRead);
if (ret) {
if (buffer != NULL) free(buffer);
duk_push_error_object(ctx, DUK_ERR_ERROR, "%s", message);
free(message);
return duk_throw(ctx);
}
// Get file size http://www.cplusplus.com/reference/cstdio/fread/
fseek(pFile, 0, SEEK_END);
long fileSize = ftell(pFile);
rewind(pFile);
// Allocate a buffer the write size
char * buffer = (char*) malloc (sizeof(char) * fileSize);
if (buffer == NULL) {
return duk_error(ctx, DUK_ERR_ERROR, "could not allocate memory: needed %d bytes to read %s", fileSize, joined_path);
}
// Dump file into memory
size_t bytesRead = fread(buffer, 1, fileSize, pFile);
if (bytesRead != fileSize) {
return duk_error(ctx, DUK_ERR_ERROR, "failed reading file: %s", joined_path);
}
// Close the file bc we're done with it
fclose(pFile);
// Create a function from the file buffer. Leaves it on the stack.
duk_compile_lstring_filename(ctx, DUK_COMPILE_FUNCTION + DUK_COMPILE_STRICT, buffer, bytesRead);
free(buffer);
if (message != NULL) free(message);
return 1;
}
......@@ -11,7 +11,8 @@ int app_push_trusted_function (duk_context *ctx);
void app_stash_global_string(duk_context *ctx, char *name);
void app_unstash_global_string(duk_context *ctx, char *name);
char* c_join_paths(const char *base, const char *filename);
int native_import (duk_context *ctx, char * filename);
int c_read_script_file (const char *joined_path, char **message, char **buffer, size_t *bytesRead);
int native_import (duk_context *ctx, const char *filename);
static duk_ret_t native_print(duk_context *ctx);
int queue_create(duk_context *ctx, char *name);
int queue_push_queue(duk_context *ctx, char *name);
......@@ -25,6 +26,7 @@ int queue_push_queue(duk_context *ctx, char *name);
#include "app_stash_global_string.c"
#include "app_unstash_global_string.c"
#include "c_join_paths.c"
#include "c_read_script_file.c"
#include "native_import.c"
#include "native_print.c"
#include "queue/queue_create.c"
......
int c_read_script_file (const char *joined_path, char **message, char **buffer, size_t *bytesRead) {
// Open the file
FILE * pFile = fopen(joined_path, "rb");
if (pFile == NULL) {
asprintf(message, "could not open file %s", joined_path);
return 1;
}
// Get file size http://www.cplusplus.com/reference/cstdio/fread/
fseek(pFile, 0, SEEK_END);
long int fileSize = ftell(pFile);
rewind(pFile);
// Allocate a buffer the write size
*buffer = (char*) malloc (sizeof(char) * fileSize);
if (*buffer == NULL) {
asprintf(message, "could not allocate the %ld bytes of memory to read %s", fileSize, joined_path);
return 2;
}
// Dump file into memory
*bytesRead = fread(*buffer, 1, fileSize, pFile);
if (*bytesRead != fileSize) {
asprintf(message, "failed reading file %s", joined_path);
return 3;
}
// Close the file bc we're done with it
fclose(pFile);
return 0;
}
......@@ -4,6 +4,7 @@
extern char **environ;
// Include dependencies
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
......@@ -51,10 +52,6 @@ int main(int argc, char *argv[] /* char *environ[] */) {
// Create a global (techinically heap-level) event queue
queue_create(ctx, "timers");
// Creat a 2nd JS thread (yes, it's possible in Duktape!)
duk_push_thread(ctx); // TODO: Use duk_push_thread_new_globalenv for modules?
duk_context *ctx2 = duk_get_context(ctx, -1);
// Load top-level function module
duk_int_t ret = native_import(ctx, argv[1]);
if (ret != 0) {
......@@ -62,7 +59,7 @@ int main(int argc, char *argv[] /* char *environ[] */) {
goto finally;
}
// Construct the all-powerful god argument
// Construct the prime argument
duk_push_object(ctx);
// Create argv object
app_push_argv(ctx, argc, argv);
......@@ -89,7 +86,7 @@ int main(int argc, char *argv[] /* char *environ[] */) {
duk_put_prop_string(ctx, -2, "timers");
duk_put_prop_string(ctx, -2, "Queues");
// Run top-level module, passing the god arg as the sole argument.
// Run top-level module, passing the prime arg as the sole argument.
ret = duk_pcall(ctx, 1);
if (ret != 0) {
fprintf(stderr, "%s %s\n", argv[1], duk_safe_to_string(ctx, -1));
......@@ -99,61 +96,6 @@ int main(int argc, char *argv[] /* char *environ[] */) {
// duk_push_context_dump(ctx);
// printf("%s\n", duk_to_string(ctx, -1));
// }
// Now trigger SOME of the event callbacks but not all
if (omp_get_thread_num() % 2) {
queue_push_queue(ctx, "timers");
duk_get_prop_index(ctx, -1, 0);
duk_remove(ctx, -2);
ret = duk_pcall(ctx, 0);
if (ret != 0) {
fprintf(stderr, "%s %s\n", argv[1], duk_safe_to_string(ctx, -1));
goto finally;
}
}
// Load top-level function module
ret = native_import(ctx2, argv[1]);
if (ret != 0) {
fprintf(stderr, "%s %s\n", argv[1], duk_safe_to_string(ctx2, -1));
goto finally;
}
// Construct the all-powerful god argument
duk_push_object(ctx2);
// Create argv object
app_push_argv(ctx2, argc, argv);
duk_put_prop_string(ctx2, -2, "argv");
// Create environment variables object
app_push_environ(ctx2);
duk_put_prop_string(ctx2, -2, "environ");
// Provide access to native functions
duk_push_object(ctx2);
duk_push_c_function(ctx2, app_push_dir, 1);
duk_put_prop_string(ctx2, -2, "dir");
duk_push_c_function(ctx2, native_print, DUK_VARARGS);
duk_put_prop_string(ctx2, -2, "print");
duk_get_global_string(ctx2, "Duktape");
duk_put_prop_string(ctx2, -2, "Duktape");
duk_put_prop_string(ctx2, -2, "modules");
// Provide direct access to the event loop
duk_push_object(ctx2);
queue_push_queue(ctx2, "timers");
duk_put_prop_string(ctx2, -2, "timers");
duk_put_prop_string(ctx2, -2, "Queues");
// Run top-level module, passing the god arg as the sole argument.
ret = duk_pcall(ctx2, 1);
if (ret != 0) {
fprintf(stderr, "%s %s\n", argv[1], duk_safe_to_string(ctx2, -1));
goto finally;
}
// else {
// duk_push_context_dump(ctx);
// printf("%s\n", duk_to_string(ctx, -1));
// }
finally:
duk_destroy_heap(ctx);
#pragma omp critical
......
int native_import (duk_context *ctx, char * filename) {
// Open the file
FILE * pFile = fopen(filename, "rb");
if (pFile == NULL) {
fputs("File error\n", stderr);
exit(1);
int native_import (duk_context *ctx, const char *filename) {
char *message = NULL;
char *buffer = NULL;
size_t bytesRead = NULL;
duk_int_t ret = c_read_script_file(filename, &message, &buffer, &bytesRead);
if (ret) {
if (message != NULL) fprintf(stderr, "%s\n", message);
if (message != NULL) free(message);
if (buffer != NULL) free(buffer);
exit(ret);
}
// Get file size http://www.cplusplus.com/reference/cstdio/fread/
fseek(pFile, 0, SEEK_END);
long fileSize = ftell(pFile);
rewind(pFile);
// Allocate a buffer the write size
char * buffer = (char*) malloc (sizeof(char) * fileSize);
if (buffer == NULL) {
fputs("Memory error\n", stderr);
exit(2);
}
// Dump file into memory
size_t bytesRead = fread(buffer, 1, fileSize, pFile);
if (bytesRead != fileSize) {
fputs("Reading error\n", stderr);
exit(3);
}
// Close the file bc we're done with it
fclose(pFile);
// Create a function from the file buffer. Leaves it on the stack.
duk_int_t ret;
duk_push_string(ctx, filename);
ret = duk_pcompile_lstring_filename(ctx, DUK_COMPILE_FUNCTION + DUK_COMPILE_STRICT, buffer, bytesRead);
free(buffer);
if (message != NULL) free(message);
return ret;
}
......@@ -2,13 +2,13 @@
DUKBOOTS="${1:-./dukboots}"
NUM_CORES="$(grep -c ^processor /proc/cpuinfo)"
echo "$NUM_CORES cores"
EXPECT_ONE='[".","..","app_push_argv.c","app_push_dir.c","app_push_environ.c","app_stash_global_string.c","app_unstash_global_string.c","main.c","native_import.c","native_print.c"]'
EXPECT_ONE='[12,"index.html"]'
EXPECT="$EXPECT_ONE"
for n in $(seq 2 $((NUM_CORES * 2))); do
for n in $(seq 2 $((NUM_CORES * 1))); do
EXPECT="$EXPECT
$EXPECT_ONE"
done
RESULT="$($DUKBOOTS example.js)"
RESULT="$($DUKBOOTS example/example.js)"
echo "$EXPECT"
echo "---"
echo "$RESULT"
......
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