diff --git a/changelog/unreleased/kong/fix-vault-reference-parsing-endslash.yml b/changelog/unreleased/kong/fix-vault-reference-parsing-endslash.yml new file mode 100644 index 000000000000..fdebe2687f4c --- /dev/null +++ b/changelog/unreleased/kong/fix-vault-reference-parsing-endslash.yml @@ -0,0 +1,4 @@ +message: | + **Vault**: Reference ending with slash when parsed should not return a key. +type: bugfix +scope: PDK diff --git a/kong/pdk/vault.lua b/kong/pdk/vault.lua index 4a29f405aff8..4d2b231a1626 100644 --- a/kong/pdk/vault.lua +++ b/kong/pdk/vault.lua @@ -58,6 +58,11 @@ local BRACE_START = byte("{") local BRACE_END = byte("}") local COLON = byte(":") local SLASH = byte("/") +local BYT_V = byte("v") +local BYT_A = byte("a") +local BYT_U = byte("u") +local BYT_L = byte("l") +local BYT_T = byte("t") local VAULT_QUERY_OPTS = { workspace = ngx.null } @@ -72,13 +77,17 @@ local VAULT_QUERY_OPTS = { workspace = ngx.null } -- @tparam string reference reference to check -- @treturn boolean `true` is the passed in reference looks like a reference, otherwise `false` local function is_reference(reference) - return type(reference) == "string" - and byte(reference, 1) == BRACE_START - and byte(reference, -1) == BRACE_END - and byte(reference, 7) == COLON - and byte(reference, 8) == SLASH - and byte(reference, 9) == SLASH - and sub(reference, 2, 6) == "vault" + return type(reference) == "string" + and byte(reference, 1) == BRACE_START + and byte(reference, 2) == BYT_V + and byte(reference, 3) == BYT_A + and byte(reference, 4) == BYT_U + and byte(reference, 5) == BYT_L + and byte(reference, 6) == BYT_T + and byte(reference, 7) == COLON + and byte(reference, 8) == SLASH + and byte(reference, 9) == SLASH + and byte(reference, -1) == BRACE_END end @@ -146,18 +155,15 @@ local function parse_reference(reference) local key local parts = parse_path(resource) local count = #parts - if count == 1 then + if count == 0 then + return nil, fmt("reference url has invalid path [%s]", reference) + elseif count == 1 then resource = unescape_uri(parts[1]) - + elseif parts.is_directory then + resource = unescape_uri(concat(parts, "/", 1, count)) else resource = unescape_uri(concat(parts, "/", 1, count - 1)) - if parts[count] ~= "" then - key = unescape_uri(parts[count]) - end - end - - if resource == "" then - return nil, fmt("reference url has invalid path [%s]", reference) + key = unescape_uri(parts[count]) end local config diff --git a/spec/01-unit/23-vaults_spec.lua b/spec/01-unit/23-vaults_spec.lua index 5eae2fc5aef0..f522f86ac6a0 100644 --- a/spec/01-unit/23-vaults_spec.lua +++ b/spec/01-unit/23-vaults_spec.lua @@ -70,6 +70,20 @@ describe("Vault PDK", function() assert.is_nil(res.version) end) + it("test init path with only slashes does not work", function() + local res, err = parse_reference("{vault://env}") + assert.is_nil(res) + assert.equal("reference url is missing path [{vault://env}]", err) + + local res, err = parse_reference("{vault://env/}") + assert.is_nil(res) + assert.equal("reference url has empty path [{vault://env/}]", err) + + local res, err = parse_reference("{vault://env/////}") + assert.is_nil(res) + assert.equal("reference url has invalid path [{vault://env/////}]", err) + end) + it("test init nested/path", function() local res, err = parse_reference("{vault://env/test-secret/test-key}") assert.is_nil(err) @@ -80,6 +94,46 @@ describe("Vault PDK", function() assert.is_nil(res.version) end) + it("test init nested/path is url decoded", function() + local res, err = parse_reference("{vault://env/test%3Asecret/test%3Akey}") + assert.is_nil(err) + assert.is_nil(res.config) + assert.is_equal("env", res.name) + assert.is_equal("test:secret", res.resource) + assert.is_equal("test:key", res.key) + assert.is_nil(res.version) + end) + + it("test init nested/path ignores consecutive slashes", function() + local res, err = parse_reference("{vault://env//////test-secret//////test-key}") + assert.is_nil(err) + assert.is_nil(res.config) + assert.is_equal("env", res.name) + assert.is_equal("test-secret", res.resource) + assert.is_equal("test-key", res.key) + assert.is_nil(res.version) + end) + + it("test init nested/path ending with slash", function() + local res, err = parse_reference("{vault://env/test-secret/test-key/}") + assert.is_nil(err) + assert.is_nil(res.config) + assert.is_equal("env", res.name) + assert.is_equal("test-secret/test-key", res.resource) + assert.is_nil(res.key) + assert.is_nil(res.version) + end) + + it("test init nested/path ending with slash ignores consecutive slashes", function() + local res, err = parse_reference("{vault://env//////test-secret//////test-key//////}") + assert.is_nil(err) + assert.is_nil(res.config) + assert.is_equal("env", res.name) + assert.is_equal("test-secret/test-key", res.resource) + assert.is_nil(res.key) + assert.is_nil(res.version) + end) + it("test init opts", function() local res, err = parse_reference("{vault://env/test?opt1=val1}") assert.is_nil(err)