Repository navigation
Webhook Subscriptions Modernized
TestLink CI edited this page Oct 20, 2026
·
1 revision
Net-new, project-scoped outbound webhook subscriptions registry for TestLink 2.0.1, added as a System ASIDE entry. This is the third delivery slice of parent feature #1072 after the REST API Reference hub (#1937) and the OpenAPI Specification viewer (#1944): a registry of endpoints that TestLink can notify, with a real signed test delivery. Tracking issue #1946.
TestLink 1.9.20 and the initial 2.0.1 BFF layer shipped no webhook entity at all, so this is net-new — nothing was dropped from legacy.

- Parent enhancement #1072 asks for documented API access: a reference hub, a machine-readable spec and an outbound event mechanism. The first two landed (#1937, #1944); the delivery half was still missing.
- The ledger's TODO section is empty (every ASIDE entry already maps to a modern
.html+ BFF), so a genuine net-new System screen was taken instead of a legacy port. - Integrators (Jenkins, GitLab CI, chat bots, incident tooling) need a first-class, per-project place to declare endpoints and prove they are reachable before relying on them in a pipeline.
| Layer | File |
|---|---|
| Screen |
gui/templates/system/webhooks.html (Dashio standalone HTML/JS/CSS) |
| BFF |
api/webhooks/index.php (GET|HEAD ?action=init|list, POST ?action=save|delete|test; session auth + same-origin guard + session-inactivity check) |
| Wiring |
$actions->webhooks in lib/functions/common.php, consumed by ASIDE System section (view_tc), plus api/aside/index.php
|
| DB |
tc_webhooks (installer DDL + lazy CREATE TABLE IF NOT EXISTS, whitelisted in lib/functions/object.class.php) |
| i18n |
whk.* (78 keys, incl. whk.noMatch) in all 10 client bundles; $TLS_title_webhooks in all 19 server locales |
- Metric tiles — subscriptions, active, inactive, test OK, test failed, never tested.
- Filters — status (all / active / inactive) and event topic, plus a translated no subscription matches the current filters state.
- Registry DataTable — Name / Endpoint URL / Topic / Status / Secret (Set or None — the raw secret is never echoed back) / Last test / Updated / Actions.
- Create / edit modal — name, endpoint URL (http/https, credentials refused), event topic, active flag and an optional HMAC secret. On edit an empty secret keeps the current one.
- Details modal — read-only summary including how deliveries are signed.
- Delete confirmation — interpolates the subscription name.
- Test delivery — POSTs a signed JSON payload and records HTTP status, duration and OK/failed into the row and tiles.
-
Rights — reads require
mgt_view_tcormgt_modify_tc; writes requiremgt_modify_tc, checked on the addressed project before it is resolved (the #1697 "no existence oracle" lesson). A foreign-project id returns the same404as a nonexistent one. -
Project proof on writes — a bogus
tproject_idcannot insert an orphan row (404tproject_not_found). -
Verb gate before rights — a wrong HTTP method answers
405, never403. -
SSRF guard — the test-delivery POST refuses loopback, link-local and cloud
metadata targets (
127.0.0.0/8,169.254.0.0/16,0.0.0.0/8,::1,fe80::); RFC1918 LAN targets stay allowed on purpose (Jenkins/GitLab runners), and$tlCfg->webhooksAllowLocalTargetsopts loopback back in. cURL protocols are pinned to HTTP/HTTPS. -
Signing — every delivery carries
X-TestLink-Eventand, when a secret is set,X-TestLink-Signature: sha256=<hex HMAC-SHA256 of the raw body>. -
Stable error codes — 400
no_testproject/name_required/bad_url/bad_topic/unknown_action, 401not_authenticated, 403no_right, 404tproject_not_found/webhook_not_found, 405wrong_method, 409duplicate_name, 500server_error, 502delivery_failed.
- Suite
Issue #1946intmp/TLU_Test_Cases.md— 15/15 PASS (BFF contract + browser flows), Event Viewer AUDIT-only. - BFF contract re-verified on the fix pass: loopback/metadata URL → 400,
LAN host → 200, bogus project → 404, PUT → 405, anon → 401, role-3 → 403,
duplicate → 409, JSON
nullsecret keeps the stored secret. - Browser (admin + role-3
whknorights): create / edit / details / test delivery / delete, filters + no-match, Romanian locale, 403 state card, ASIDE entry visible for admin and hidden for role 3, console clean (0 issues).


- Refs #1946, parent #1072 — net-new Webhook Subscriptions manager.