From 420d07f144bf938294b5a237e55b3d57d1cf28b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20C=C3=A9sar=20Su=C3=A1stegui?= Date: Fri, 3 Apr 2026 03:13:16 -0600 Subject: [PATCH] fix(nselib/url): scope path params to segment per RFC 3986 s3.3 url.parse() used '%;(.*)' to extract path parameters, which consumed everything after the first semicolon including subsequent path segments. For '/aa/bb;cc/dd;ee/', this produced path='/aa/bb' and params='cc/dd;ee/' instead of path='/aa/bb/dd/' and params='cc'. RFC 3986 section 3.3 states that parameters (semicolon-delimited values) are scoped to individual path segments, not the entire remaining path. Fix: use '%;([^/]*)' to match only up to the next slash. Capture the first occurrence as parsed.params and remove all occurrences from the path string so subsequent segments are preserved correctly. Reproduces the cases from issue #3318 (reported by nnposter): Before: url.parse('/aa/bb;cc/dd;ee/').path == '/aa/bb' After: url.parse('/aa/bb;cc/dd;ee/').path == '/aa/bb/dd/' --- nselib/url.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nselib/url.lua b/nselib/url.lua index 3c013d04e..dd446b1d8 100644 --- a/nselib/url.lua +++ b/nselib/url.lua @@ -215,11 +215,11 @@ function parse(url, default) parsed.authority = n return "" end) - -- get params - url = string.gsub(url, "%;(.*)", function(p) - parsed.params = p - return "" - end) + -- get params (RFC 3986 s3.3: parameters are scoped to individual path + -- segments, not the entire remaining path). Capture the first occurrence + -- and strip all occurrences so the path is reconstructed correctly. + parsed.params = url:match("%;([^/]*)") + url = url:gsub("%;[^/]*", "") -- path is whatever was left parsed.path = url