diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2024-09-09 16:54:08 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2024-09-09 16:54:08 -0400 |
| commit | 23de8e1f5a80fdf05469b4961ba0eaafb4531b0e (patch) | |
| tree | 6ec00ca90b9d0d829869d108e1e3aabdd66a3348 | |
| parent | 0bc207266ed50d628b2071a7be128ace92de3dec (diff) | |
Fix up some stuff with path escaping
| -rw-r--r-- | builtins/path.c | 21 | ||||
| -rw-r--r-- | builtins/path.h | 5 | ||||
| -rw-r--r-- | compile.c | 9 | ||||
| -rw-r--r-- | environment.c | 2 |
4 files changed, 19 insertions, 18 deletions
diff --git a/builtins/path.c b/builtins/path.c index 1dde5aa2..7c2198cf 100644 --- a/builtins/path.c +++ b/builtins/path.c @@ -22,10 +22,12 @@ PUREFUNC public Path_t Path$escape_text(Text_t text) { - if (Text$has(text, Pattern("/")) || Text$has(text, Pattern(";"))) - fail("Invalid path component: %k", &text); + if (Text$has(text, Pattern("/"))) + fail("Path interpolations cannot contain slashes: %k", &text); + else if (Text$has(text, Pattern(";"))) + fail("Path interpolations cannot contain semicolons: %k", &text); else if (Text$equal_values(text, Path(".")) || Text$equal_values(text, Path(".."))) - fail("Invalid path component: %k", &text); + fail("Path interpolation is \"%k\" which is disallowed to prevent security vulnerabilities", &text); return (Path_t)text; } @@ -113,18 +115,7 @@ static inline Path_t Path$_expand_home(Path_t path) public Path_t Path$_concat(int n, Path_t items[n]) { - for (int i = 1; i < n; i++) { - if (Text$starts_with(items[i], Path("~/")) || Text$starts_with(items[i], Path("/"))) - fail("Cannot insert absolute path (%k) after another path (%k)", &items[i], &items[0]); - } - - Array_t items_array = { - .length=n, - .stride=sizeof(Path_t), - .data=items, - .data_refcount=1, - }; - Path_t cleaned_up = Path$cleanup(Text$join(Text("/"), items_array)); + Path_t cleaned_up = Path$cleanup(Text$_concat(n, items)); if (cleaned_up.length > PATH_MAX) fail("Path exceeds the maximum path length: %k", &cleaned_up); return cleaned_up; diff --git a/builtins/path.h b/builtins/path.h index 4ee1b0a7..6772f72d 100644 --- a/builtins/path.h +++ b/builtins/path.h @@ -11,12 +11,13 @@ #define Path_t Text_t #define Path(text) ((Path_t)Text(text)) -#define Paths(...) Path$concat(__VA_ARGS__) +#define Paths(...) Path$_concat(sizeof((Path_t[]){__VA_ARGS__})/sizeof(Path_t), (Path_t[]){__VA_ARGS__}) Path_t Path$cleanup(Path_t path); Path_t Path$_concat(int n, Path_t items[n]); -#define Path$concat(...) Path$_concat(sizeof((Path_t[]){__VA_ARGS__})/sizeof(Path_t), (Path_t[]){__VA_ARGS__}) +#define Path$concat(a, b) Paths(a, Path("/"), b) PUREFUNC Path_t Path$escape_text(Text_t text); +PUREFUNC Path_t Path$escape_path(Text_t path); Path_t Path$resolved(Path_t path, Path_t relative_to); Path_t Path$relative(Path_t path, Path_t relative_to); bool Path$exists(Path_t path); @@ -1909,7 +1909,14 @@ CORD compile(env_t *env, ast_t *ast) if (chunk->ast->tag == TextLiteral) { chunk_code = compile(env, chunk->ast); } else if (chunk_t->tag == TextType && streq(Match(chunk_t, TextType)->lang, lang)) { - chunk_code = compile(env, chunk->ast); + binding_t *esc = get_lang_escape_function(env, lang, chunk_t); + if (esc) { + arg_t *arg_spec = Match(esc->type, FunctionType)->args; + arg_ast_t *args = new(arg_ast_t, .value=chunk->ast); + chunk_code = CORD_all(esc->code, "(", compile_arguments(env, ast, arg_spec, args), ")"); + } else { + chunk_code = compile(env, chunk->ast); + } } else if (lang) { binding_t *esc = get_lang_escape_function(env, lang, chunk_t); if (!esc) diff --git a/environment.c b/environment.c index 8eefc2b2..d6c6673c 100644 --- a/environment.c +++ b/environment.c @@ -253,6 +253,7 @@ env_t *new_compilation_unit(CORD *libname) {"base_name", "Path$base_name", "func(path:Path)->Text"}, {"children", "Path$children", "func(path:Path, include_hidden=no)->[Path]"}, {"create_directory", "Path$create_directory", "func(path:Path, permissions=0o644_i32)"}, + {"escape_path", "Path$escape_path", "func(path:Path)->Path"}, {"escape_text", "Path$escape_text", "func(text:Text)->Path"}, {"exists", "Path$exists", "func(path:Path)->Bool"}, {"extension", "Path$extension", "func(path:Path, full=yes)->Text"}, @@ -598,6 +599,7 @@ binding_t *get_namespace_binding(env_t *env, ast_t *self, const char *name) binding_t *get_lang_escape_function(env_t *env, const char *lang_name, type_t *type_to_escape) { + if (!lang_name) lang_name = "Text"; binding_t *typeinfo = get_binding(env, lang_name); assert(typeinfo && typeinfo->type->tag == TypeInfoType); env_t *lang_env = Match(typeinfo->type, TypeInfoType)->env; |
