aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-09-09 16:54:08 -0400
committerBruce Hill <bruce@bruce-hill.com>2024-09-09 16:54:08 -0400
commit23de8e1f5a80fdf05469b4961ba0eaafb4531b0e (patch)
tree6ec00ca90b9d0d829869d108e1e3aabdd66a3348
parent0bc207266ed50d628b2071a7be128ace92de3dec (diff)
Fix up some stuff with path escaping
-rw-r--r--builtins/path.c21
-rw-r--r--builtins/path.h5
-rw-r--r--compile.c9
-rw-r--r--environment.c2
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);
diff --git a/compile.c b/compile.c
index ff721b4b..99f94237 100644
--- a/compile.c
+++ b/compile.c
@@ -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;