diff --git a/CHANGELOG b/CHANGELOG index 5e711d18c..849b11dca 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ #Nmap Changelog ($Id$); -*-text-*- +o [NSE][GH#1129] Changed url.absolute() behavior with respect to dot and + dot-dot path segments to comply with RFC 3986, section 5.2. [nnposter] + o Removed deprecated and undocumented aliases for several long options that used underscores instead of hyphens, such as --max_retries. [Daniel Miller] diff --git a/nselib/url.lua b/nselib/url.lua index 9328854a9..232c99394 100644 --- a/nselib/url.lua +++ b/nselib/url.lua @@ -82,24 +82,27 @@ end -- @return The corresponding absolute path. ----------------------------------------------------------------------------- local function absolute_path(base_path, relative_path) - if string.sub(relative_path, 1, 1) == "/" then return relative_path end - local path = string.gsub(base_path, "[^/]*$", "") - .. relative_path - path = string.gsub(path, "([^/]*%./)", function (s) - if s ~= "./" then return s else return "" end - end) - path = string.gsub(path, "/%.$", "/") - local reduced - while reduced ~= path do - reduced = path - path = string.gsub(reduced, "([^/]*/%.%./)", function (s) - if s ~= "../../" then return "" else return s end - end) + -- Function for normalizing trailing dot and dot-dot by adding the final / + local fixdots = function (s) + return s:gsub("%f[^/\0]%.$", "./"):gsub("%f[^/\0]%.%.$", "../") + end + local path = relative_path + if path:sub(1, 1) ~= "/" then + path = fixdots(base_path):gsub("[^/]*$", path) end - path = string.gsub(reduced, "([^/]*/%.%.)$", function (s) - if s ~= "../.." then return "" else return s end - end) - return path + -- Break the path into segments, processing dot and dot-dot + local segs = {} + for s in fixdots(path):gmatch("[^/]*") do + if s == "." then -- ignore + elseif s == ".." then -- remove the previous segment + if #segs > 1 or #segs == 1 and segs[#segs] ~= "" then + table.remove(segs) + end + else -- add a regular segment, possibly empty + table.insert(segs, s) + end + end + return table.concat(segs, "/") end @@ -475,4 +478,27 @@ for k, v in pairs(expected) do test_suite:add_test(unittest.equal(result[k], v), k) end +-- path merging tests for compliance with RFC 3986, section 5.2 +-- https://tools.ietf.org/html/rfc3986#section-5.2 +local absolute_path_tests = { -- {bpath, rpath, expected} + {'a', '.', '' }, + {'a', './', '' }, + {'..', 'b', 'b' }, + {'../', 'b', 'b' }, + {'/', '..', '/' }, + {'/', '../', '/' }, + {'/../', '..', '/' }, + {'/../', '../', '/' }, + {'a/..', 'b', 'b' }, + {'a/../', 'b', 'b' }, + {'/a/..', '', '/' }, + {'', '/a/..', '/' }, + {'', '/a//..', '/a/' }, + } +for k, v in ipairs(absolute_path_tests) do + local bpath, rpath, expected = table.unpack(v) + test_suite:add_test(unittest.equal(absolute_path(bpath, rpath), expected), + ("absolute_path #%d (%q,%q)"):format(k, bpath, rpath)) +end + return _ENV;