Skip to content

Commit 83af839

Browse files
author
Your Name
committed
fix(pipeline): reject invalid route paths from vendored JS files
Vendored/minified JS files (tsc.js, typescript.js) inside non-JS repos produce false positive routes when the Express route extractor matches JS operators and keywords as route paths. Add a validation filter that rejects: - JS/TS operators: !, +, ++, -, --, :, ~ - JS/TS keywords: void, null, true, false, throw, this, typeof, etc. - Single-character non-slash paths (*, ?, #) - Paths with no alphanumeric or slash characters Also trims leading/trailing whitespace before comparison to catch 'void ' and 'throw ' variants from minified source. Tested: cube routes went from 42 (20 garbage) to 22 real routes.
1 parent 77f8a92 commit 83af839

File tree

1 file changed

+58
-0
lines changed

1 file changed

+58
-0
lines changed

src/pipeline/pass_httplinks.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,64 @@ static int insert_route_nodes(cbm_pipeline_ctx_t *ctx, cbm_route_handler_t *rout
881881
for (int i = 0; i < route_count; i++) {
882882
cbm_route_handler_t *rh = &routes[i];
883883

884+
/* Reject obviously invalid route paths.
885+
* Vendored/minified JS files (e.g. tsc.js, typescript.js) inside non-JS
886+
* repos can produce false positives where JS operators/keywords get
887+
* matched as route paths by the Express extractor. */
888+
{
889+
const char *p = rh->path;
890+
/* Skip empty paths */
891+
if (!p || !*p) continue;
892+
893+
/* Reject paths that are JS operators or keywords — not valid URL routes */
894+
static const char *const invalid_paths[] = {
895+
"!", "+", "++", "-", "--", ":", "~", "void", "null", "true",
896+
"false", "throw", "this", "typeof", "delete", "new", "return",
897+
"undefined", "NaN", "Infinity", "var", "let", "const",
898+
"function", "class", "if", "else", "for", "while", "do",
899+
"switch", "case", "break", "continue", "try", "catch",
900+
"finally", "with", "in", "of", "yield", "await", "async",
901+
"super", "import", "export", "default", "extends", "static",
902+
"_this", "self", "__proto__", "arguments", "range",
903+
NULL
904+
};
905+
bool rejected = false;
906+
/* Work with a trimmed copy for comparison */
907+
char trimmed[256];
908+
/* Trim leading whitespace */
909+
while (*p == ' ' || *p == '\t') p++;
910+
strncpy(trimmed, p, sizeof(trimmed) - 1);
911+
trimmed[sizeof(trimmed) - 1] = '\0';
912+
/* Trim trailing whitespace */
913+
size_t tlen = strlen(trimmed);
914+
while (tlen > 0 && (trimmed[tlen - 1] == ' ' || trimmed[tlen - 1] == '\t' ||
915+
trimmed[tlen - 1] == '\n' || trimmed[tlen - 1] == '\r')) {
916+
trimmed[--tlen] = '\0';
917+
}
918+
for (int k = 0; invalid_paths[k]; k++) {
919+
if (strcmp(trimmed, invalid_paths[k]) == 0) {
920+
rejected = true;
921+
break;
922+
}
923+
}
924+
if (rejected) continue;
925+
926+
/* Reject single-character non-slash paths (e.g. "*", "?", "#") */
927+
if (p[0] && !p[1] && p[0] != '/') continue;
928+
929+
/* Reject paths that contain no alphanumeric or slash characters.
930+
* Valid routes like "/api/v1" always have at least one alnum. */
931+
bool has_alnum_or_slash = false;
932+
for (const char *c = p; *c; c++) {
933+
if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') ||
934+
(*c >= '0' && *c <= '9') || *c == '/') {
935+
has_alnum_or_slash = true;
936+
break;
937+
}
938+
}
939+
if (!has_alnum_or_slash) continue;
940+
}
941+
884942
/* Build Route QN and name */
885943
char normal_method[16];
886944
snprintf(normal_method, sizeof(normal_method), "%s", rh->method[0] ? rh->method : "ANY");

0 commit comments

Comments
 (0)