diff --git a/.github/labeler.yml b/.github/labeler.yml index 4d80a6fc92f2..dd6c00aa56c6 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -254,10 +254,6 @@ plugins/opentelemetry: - changed-files: - any-glob-to-any-file: kong/plugins/opentelemetry/**/* -plugins/standard-webhooks: -- changed-files: - - any-glob-to-any-file: kong/plugins/standard-webhooks/**/* - schema-change-noteworthy: - changed-files: - any-glob-to-any-file: ['kong/db/schema/**/*.lua', 'kong/**/schema.lua', 'kong/plugins/**/daos.lua', 'plugins-ee/**/daos.lua', 'plugins-ee/**/schema.lua', 'kong/db/dao/*.lua', 'kong/enterprise_edition/redis/init.lua'] diff --git a/changelog/unreleased/kong/plugins-add-standard-webhooks.yml b/changelog/unreleased/kong/plugins-add-standard-webhooks.yml deleted file mode 100644 index a9448465fa2a..000000000000 --- a/changelog/unreleased/kong/plugins-add-standard-webhooks.yml +++ /dev/null @@ -1,4 +0,0 @@ -message: | - Add standard webhooks plugin -type: feature -scope: Plugin diff --git a/kong-3.7.0-0.rockspec b/kong-3.7.0-0.rockspec index 52e8899381cb..35a94a8afcac 100644 --- a/kong-3.7.0-0.rockspec +++ b/kong-3.7.0-0.rockspec @@ -608,10 +608,6 @@ build = { ["kong.plugins.ai-prompt-guard.handler"] = "kong/plugins/ai-prompt-guard/handler.lua", ["kong.plugins.ai-prompt-guard.schema"] = "kong/plugins/ai-prompt-guard/schema.lua", - ["kong.plugins.standard-webhooks.handler"] = "kong/plugins/standard-webhooks/handler.lua", - ["kong.plugins.standard-webhooks.internal"] = "kong/plugins/standard-webhooks/internal.lua", - ["kong.plugins.standard-webhooks.schema"] = "kong/plugins/standard-webhooks/schema.lua", - ["kong.vaults.env"] = "kong/vaults/env/init.lua", ["kong.vaults.env.schema"] = "kong/vaults/env/schema.lua", diff --git a/kong/constants.lua b/kong/constants.lua index 63df1f2f5a47..0050ab1fee4f 100644 --- a/kong/constants.lua +++ b/kong/constants.lua @@ -42,7 +42,6 @@ local plugins = { "ai-prompt-guard", "ai-request-transformer", "ai-response-transformer", - "standard-webhooks", } local plugin_map = {} diff --git a/kong/plugins/standard-webhooks/handler.lua b/kong/plugins/standard-webhooks/handler.lua deleted file mode 100644 index a013a6cee023..000000000000 --- a/kong/plugins/standard-webhooks/handler.lua +++ /dev/null @@ -1,12 +0,0 @@ -local plugin = require "kong.plugins.standard-webhooks.internal" - -local StandardWebhooks = { - VERSION = require("kong.meta").version, - PRIORITY = 760 -} - -function StandardWebhooks:access(conf) - plugin.access(conf) -end - -return StandardWebhooks diff --git a/kong/plugins/standard-webhooks/internal.lua b/kong/plugins/standard-webhooks/internal.lua deleted file mode 100644 index 488941f28d9d..000000000000 --- a/kong/plugins/standard-webhooks/internal.lua +++ /dev/null @@ -1,78 +0,0 @@ -local kong = kong -local mac = require "resty.openssl.mac" -local tonumber = tonumber -local ngx = ngx -local type = type - -local HEADER_WEBHOOK_ID = "webhook-id" -local HEADER_WEBHOOK_SIGN = "webhook-signature" -local HEADER_WEBHOOK_TS = "webhook-timestamp" - -local function getHeader(input) - if type(input) == "table" then - return input[1] - else - return input - end -end - -local function sign(secret, id, ts, payload) - local d, err = mac.new(secret, "HMAC", nil, "sha256") - if err then - kong.log.error(err) - return kong.response.error(500) - end - local r, err = d:final(id .. "." .. ts .. "." .. payload) - if err then - kong.log.error(err) - return kong.response.error(500) - end - return "v1," .. ngx.encode_base64(r) -end - -local function extract_webhook() - local headers = kong.request.get_headers() - - local id = getHeader(headers[HEADER_WEBHOOK_ID]) - local signature = getHeader(headers[HEADER_WEBHOOK_SIGN]) - local ts = getHeader(headers[HEADER_WEBHOOK_TS]) - if not id or not signature or not ts then - kong.log.debug("missing required headers") - return kong.response.error(400) - end - - ts = tonumber(ts) or 0 -- if parse fails we inject 0, which will fail on clock-skew check - - return id, signature, ts -end - - -local function access(config) - local id, signature, ts = extract_webhook() - - if ngx.now() - ts > config.tolerance_second then - kong.log.debug("timestamp tolerance exceeded") - return kong.response.error(400) - end - - local body = kong.request.get_raw_body() - - if not body or body == "" then - kong.log.debug("missing required body") - return kong.response.error(400) - end - - local expected_signature = sign(config.secret_v1, id, ts, body) - - if signature == expected_signature then - return - end - - kong.log.debug("signature not matched") - return kong.response.error(400) -end - -return { - access = access, - sign = sign -} diff --git a/kong/plugins/standard-webhooks/schema.lua b/kong/plugins/standard-webhooks/schema.lua deleted file mode 100644 index 165926f07aef..000000000000 --- a/kong/plugins/standard-webhooks/schema.lua +++ /dev/null @@ -1,38 +0,0 @@ -local typedefs = require "kong.db.schema.typedefs" - -local PLUGIN_NAME = "standard-webhooks" - -local schema = { - name = PLUGIN_NAME, - fields = { - { consumer = typedefs.no_consumer }, - { protocols = typedefs.protocols_http }, - { - config = { - type = "record", - fields = { - { - secret_v1 = { - type = "string", - required = true, - description = "Webhook secret", - referenceable = true, - encrypted = true, - }, - }, - { - tolerance_second = { - description = "Tolerance of the webhook timestamp in seconds. If the webhook timestamp is older than this number of seconds, it will be rejected with a '400' response.", - type = "integer", - required = true, - gt = -1, - default = 5 * 60 - } - } - } - } - } - } -} - -return schema diff --git a/spec/01-unit/12-plugins_order_spec.lua b/spec/01-unit/12-plugins_order_spec.lua index a051aeb98040..d897784255ec 100644 --- a/spec/01-unit/12-plugins_order_spec.lua +++ b/spec/01-unit/12-plugins_order_spec.lua @@ -78,7 +78,6 @@ describe("Plugins", function() "ai-prompt-guard", "ai-proxy", "ai-response-transformer", - "standard-webhooks", "aws-lambda", "azure-functions", "proxy-cache", diff --git a/spec/03-plugins/44-standard-webhooks/01-unit_spec.lua b/spec/03-plugins/44-standard-webhooks/01-unit_spec.lua deleted file mode 100644 index afff58e052cd..000000000000 --- a/spec/03-plugins/44-standard-webhooks/01-unit_spec.lua +++ /dev/null @@ -1,140 +0,0 @@ -local PLUGIN_NAME = "standard-webhooks" - - --- helper function to validate data against a schema -local validate do - local validate_entity = require("spec.helpers").validate_plugin_config_schema - local plugin_schema = require("kong.plugins."..PLUGIN_NAME..".schema") - - function validate(data) - return validate_entity(data, plugin_schema) - end -end - - -describe(PLUGIN_NAME .. ": (schema)", function() - - - it("accepts a valid config", function() - local ok, err = validate({ - secret_v1 = "abc123", - tolerance_second = 5*60, - }) - assert.is_nil(err) - assert.is_truthy(ok) - end) - - - describe("secret", function() - - it("must be set", function() - local ok, err = validate({ - secret_v1 = nil, - tolerance_second = 5*60, - }) - - assert.is_same({ - ["config"] = { - ["secret_v1"] = 'required field missing', - } - }, err) - assert.is_falsy(ok) - end) - - - it("is not nullable", function() - local ok, err = validate({ - secret_v1 = assert(ngx.null), - tolerance_second = 5*60, - }) - - assert.is_same({ - ["config"] = { - ["secret_v1"] = 'required field missing', - } - }, err) - assert.is_falsy(ok) - end) - - - it("must be a string", function() - local ok, err = validate({ - secret_v1 = 123, - tolerance_second = 5*60, - }) - - assert.is_same({ - ["config"] = { - ["secret_v1"] = 'expected a string', - } - }, err) - assert.is_falsy(ok) - end) - - end) - - - - describe("tolerance_second", function() - - it("gets a default", function() - local ok, err = validate({ - secret_v1 = "abc123", - tolerance_second = nil, - }) - - assert.is_nil(err) - assert.are.same(ok.config, { - secret_v1 = "abc123", - tolerance_second = 5*60, - }) - end) - - - it("is not nullable", function() - local ok, err = validate({ - secret_v1 = "abc123", - tolerance_second = assert(ngx.null), - }) - - assert.is_same({ - ["config"] = { - ["tolerance_second"] = 'required field missing', - } - }, err) - assert.is_falsy(ok) - end) - - - it("must be an integer", function() - local ok, err = validate({ - secret_v1 = "abc123", - tolerance_second = 5.67, - }) - - assert.is_same({ - ["config"] = { - ["tolerance_second"] = 'expected an integer', - } - }, err) - assert.is_falsy(ok) - end) - - - it("must be >= 0", function() - local ok, err = validate({ - secret_v1 = "abc123", - tolerance_second = -1, - }) - - assert.is_same({ - ["config"] = { - ["tolerance_second"] = 'value must be greater than -1', - } - }, err) - assert.is_falsy(ok) - end) - - end) - -end) diff --git a/spec/03-plugins/44-standard-webhooks/02-integration_spec.lua b/spec/03-plugins/44-standard-webhooks/02-integration_spec.lua deleted file mode 100644 index 490f0b2e5c66..000000000000 --- a/spec/03-plugins/44-standard-webhooks/02-integration_spec.lua +++ /dev/null @@ -1,133 +0,0 @@ -local PLUGIN_NAME = "standard-webhooks" -local helpers = require "spec.helpers" -local swh = require "kong.plugins.standard-webhooks.internal" - -local SECRET = "MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw" -local MESSAGE_ID = "msg_p5jXN8AQM9LWM0D4loKWxJek" - -for _, strategy in helpers.all_strategies() do - local client - - describe(PLUGIN_NAME .. ": (Access)", function() - lazy_setup(function() - local bp = helpers.get_db_utils(strategy, {"routes", "services", "plugins"}, {PLUGIN_NAME}) - - local r = bp.routes:insert({ - paths = {"/"} - }) - - bp.plugins:insert{ - route = r, - name = PLUGIN_NAME, - config = { - secret_v1 = SECRET - } - } - - -- start kong - assert(helpers.start_kong({ - -- set the strategy - database = strategy, - -- use the custom test template to create a local mock server - nginx_conf = "spec/fixtures/custom_nginx.template", - -- write & load declarative config, only if 'strategy=off' - declarative_config = strategy == "off" and helpers.make_yaml_file() or nil - })) - end) - lazy_teardown(function() - helpers.stop_kong() - end) - - before_each(function() - client = helpers.proxy_client() - end) - - after_each(function() - if client then - client:close() - end - end) - - it("rejects missing headers", function() - local res = client:post("/", { - headers = { - ["Content-Type"] = "application/json", - ["webhook-id"] = MESSAGE_ID, - ["webhook-timestamp"] = math.floor(ngx.now()) - }, - body = { - foo = "bar" - } - }) - - assert.response(res).has.status(400) - end) - - it("rejects invalid timestamp", function() - local res = client:post("/", { - headers = { - ["Content-Type"] = "application/json", - ["webhook-id"] = MESSAGE_ID, - ["webhook-signature"] = "asdf", - ["webhook-timestamp"] = "XYZ" - }, - body = { - foo = "bar" - } - }) - - assert.response(res).has.status(400) - end) - - it("rejects missing body", function() - local res = client:post("/", { - headers = { - ["Content-Type"] = "application/json", - ["webhook-id"] = MESSAGE_ID, - ["webhook-signature"] = "asdf", - ["webhook-timestamp"] = math.floor(ngx.now()) - } - }) - - assert.response(res).has.status(400) - end) - - it("accepts correct signature", function() - local ts = math.floor(ngx.now()) - local signature = swh.sign(SECRET, MESSAGE_ID, ts, '{"foo":"bar"}') - - local res = client:post("/", { - headers = { - ["Content-Type"] = "application/json", - ["webhook-id"] = MESSAGE_ID, - ["webhook-signature"] = signature, - ["webhook-timestamp"] = ts - }, - body = { - foo = "bar" - } - }) - - assert.response(res).has.status(200) - end) - - it("fails because the timestamp tolerance is exceeded", function() - local ts = math.floor(ngx.now()) - 6 * 60 - local signature = swh.sign(SECRET, MESSAGE_ID, ts, '{"foo":"bar"}') - - local res = client:post("/", { - headers = { - ["Content-Type"] = "application/json", - ["webhook-id"] = MESSAGE_ID, - ["webhook-signature"] = signature, - ["webhook-timestamp"] = ts - }, - body = { - foo = "bar" - } - }) - - assert.response(res).has.status(400) - end) - end) -end