Skip to content

fix(trogon-gateway): verify GitLab signing tokens#157

Merged
yordis merged 1 commit into
mainfrom
yordis/fix-gitlab-signature
May 13, 2026
Merged

fix(trogon-gateway): verify GitLab signing tokens#157
yordis merged 1 commit into
mainfrom
yordis/fix-gitlab-signature

Conversation

@yordis
Copy link
Copy Markdown
Member

@yordis yordis commented May 13, 2026

  • Protect GitLab webhook ingestion with payload authenticity and integrity guarantees instead of relying on a static shared header.
  • Reduce replay risk for GitLab webhook deliveries.

@cursor
Copy link
Copy Markdown

cursor Bot commented May 13, 2026

PR Summary

High Risk
Security-sensitive change to GitLab webhook authentication that alters required headers/config (signing_token + timestamp tolerance) and will reject legacy X-Gitlab-Token deliveries, risking broken integrations if not updated.

Overview
GitLab webhook verification is switched from X-Gitlab-Token secret matching to Svix-style HMAC signature validation using webhook-id, webhook-timestamp, and webhook-signature, with a configurable timestamp_tolerance_secs (default 300) to reduce replay risk.

Configuration and docs are updated to replace webhook_secret with required signing_token (whsec_ + base64 32-byte key), including new parsing/validation via GitLabSigningToken, updated examples (gateway.toml, READMEs), and expanded tests for missing/invalid signatures, stale timestamps, and legacy-token rejection.

The existing incident.io signature verifier is refactored to use a new shared source::standard_webhooks module that implements the common HMAC-SHA256 v1 verification and delivery metadata parsing.

Reviewed by Cursor Bugbot for commit 1d2a062. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

Review Change Stack

Warning

Rate limit exceeded

@yordis has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 27 minutes and 27 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d1020122-6a7b-4dfd-8c34-53dbd6bcebbd

📥 Commits

Reviewing files that changed from the base of the PR and between d100570 and 1d2a062.

📒 Files selected for processing (16)
  • devops/docker/compose/services/trogon-gateway/README.md
  • rsworkspace/crates/trogon-gateway/README.md
  • rsworkspace/crates/trogon-gateway/src/config.rs
  • rsworkspace/crates/trogon-gateway/src/constants.rs
  • rsworkspace/crates/trogon-gateway/src/http.rs
  • rsworkspace/crates/trogon-gateway/src/source/gitlab/config.rs
  • rsworkspace/crates/trogon-gateway/src/source/gitlab/constants.rs
  • rsworkspace/crates/trogon-gateway/src/source/gitlab/gitlab_signing_token.rs
  • rsworkspace/crates/trogon-gateway/src/source/gitlab/mod.rs
  • rsworkspace/crates/trogon-gateway/src/source/gitlab/server.rs
  • rsworkspace/crates/trogon-gateway/src/source/gitlab/signature.rs
  • rsworkspace/crates/trogon-gateway/src/source/incidentio/signature.rs
  • rsworkspace/crates/trogon-gateway/src/source/mod.rs
  • rsworkspace/crates/trogon-gateway/src/source/standard_webhooks.rs
  • rsworkspace/crates/trogon-gateway/src/streams.rs
  • services/trogon-gateway/gateway.toml

Walkthrough

This PR replaces GitLab webhook authentication from simple token validation to HMAC-SHA256 signature verification. It introduces GitLabSigningToken with strict validation, rewrites signature verification to parse webhook headers and validate timestamp freshness, updates server state and config resolution, and comprehensively updates tests with signed-request builders.

Changes

GitLab Webhook Signature Authentication Migration

Layer / File(s) Summary
All changes
(multiple files across crate)
Implements signing-token parsing (GitLabSigningToken), adds DEFAULT_GITLAB_TIMESTAMP_TOLERANCE_SECS, introduces shared standard_webhooks verifier (WebhookId/Timestamp, HMAC v1 verification, timestamp tolerance), replaces GitLab and Incident.io inline verifiers with wrappers that call the shared verifier, updates AppState and handler to use signing_token + timestamp_tolerance, updates config schema/resolution to accept signing_token and timestamp_tolerance_secs, and updates tests, fixtures, and docs to match the new schema and verification flow.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hop on code with a tidy twitch,

Whsec becomes a signed-byte stitch,
Timestamps tick and HMAC sings,
Headers guard the webhook springs,
Little rabbit guards the switch.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.72% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly describes the main change: implementing GitLab signing token verification, which is the primary security enhancement across the changeset.
Description check ✅ Passed The description is relevant to the changeset, explaining the security motivations for the GitLab signing token verification and replay risk reduction.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch yordis/fix-gitlab-signature

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 13, 2026

badge

Code Coverage Summary

Details
Filename                                                                      Stmts    Miss  Cover    Missing
--------------------------------------------------------------------------  -------  ------  -------  ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
crates/mcp-nats-server/src/allowed_host.rs                                       90       0  100.00%
crates/mcp-nats-server/src/config.rs                                            276       0  100.00%
crates/mcp-nats-server/src/main.rs                                              357     127  64.43%   149-166, 202-204, 214, 220-221, 228-231, 255-257, 261-270, 292-305, 310-358, 489, 492, 500-542
crates/acp-nats/src/nats/subjects/responses/response.rs                          20       0  100.00%
crates/acp-nats/src/nats/subjects/responses/prompt_response.rs                   27       0  100.00%
crates/acp-nats/src/nats/subjects/responses/update.rs                            27       0  100.00%
crates/acp-nats/src/nats/subjects/responses/cancelled.rs                         15       0  100.00%
crates/acp-nats/src/nats/subjects/responses/ext_ready.rs                         12       0  100.00%
crates/acp-nats/src/telemetry/metrics.rs                                         53       0  100.00%
crates/mcp-nats/src/nats/subjects/mod.rs                                         89       0  100.00%
crates/mcp-nats/src/mcp_peer_id.rs                                               33       0  100.00%
crates/mcp-nats/src/config.rs                                                   110       0  100.00%
crates/mcp-nats/src/mcp_prefix.rs                                                36       0  100.00%
crates/mcp-nats/src/jsonrpc.rs                                                   22       0  100.00%
crates/mcp-nats/src/server.rs                                                    31       0  100.00%
crates/mcp-nats/src/transport.rs                                                722       0  100.00%
crates/mcp-nats/src/client.rs                                                    31       0  100.00%
crates/trogon-gateway/src/source/incidentio/server.rs                           343       0  100.00%
crates/trogon-gateway/src/source/incidentio/signature.rs                        206       0  100.00%
crates/trogon-gateway/src/source/incidentio/incidentio_signing_secret.rs         67       0  100.00%
crates/trogon-gateway/src/source/incidentio/config.rs                            16       0  100.00%
crates/trogon-gateway/src/source/incidentio/incidentio_event_type.rs             62       0  100.00%
crates/trogon-nats/src/connect.rs                                                94       9  90.43%   22-23, 33, 60-65
crates/trogon-nats/src/auth.rs                                                  114       0  100.00%
crates/trogon-nats/src/nats_token.rs                                            157       0  100.00%
crates/trogon-nats/src/subject_token_violation.rs                                17       0  100.00%
crates/trogon-nats/src/mocks.rs                                                 314       0  100.00%
crates/trogon-nats/src/messaging.rs                                             561       2  99.64%   144, 154
crates/trogon-nats/src/client.rs                                                 22      22  0.00%    50-86
crates/trogon-nats/src/token.rs                                                   6       0  100.00%
crates/trogon-std/src/secret_string.rs                                           35       0  100.00%
crates/trogon-std/src/args.rs                                                    19       9  52.63%   11-28
crates/trogon-std/src/signal.rs                                                  26      12  53.85%   6-11, 18-25, 34
crates/trogon-std/src/uuid.rs                                                     7       0  100.00%
crates/trogon-std/src/json.rs                                                    30       0  100.00%
crates/trogon-std/src/duration.rs                                                45       0  100.00%
crates/trogon-std/src/http.rs                                                    19       0  100.00%
crates/acp-nats-agent/src/connection.rs                                        1270       1  99.92%   607
crates/trogon-std/src/dirs/fixed.rs                                              80       0  100.00%
crates/trogon-std/src/dirs/system.rs                                             71       0  100.00%
crates/trogon-std/src/env/in_memory.rs                                           73       0  100.00%
crates/trogon-std/src/env/system.rs                                              17       0  100.00%
crates/trogon-gateway/src/source/discord/gateway.rs                             426       1  99.77%   137
crates/trogon-gateway/src/source/discord/config.rs                              108       0  100.00%
crates/trogon-std/src/time/system.rs                                             31       0  100.00%
crates/trogon-std/src/time/mock.rs                                              125       0  100.00%
crates/trogon-telemetry/src/log.rs                                               68       1  98.53%   33
crates/trogon-telemetry/src/resource_attribute.rs                                23       0  100.00%
crates/trogon-telemetry/src/service_name.rs                                      44       0  100.00%
crates/trogon-telemetry/src/trace.rs                                             23       1  95.65%   22
crates/trogon-telemetry/src/lib.rs                                              197      23  88.32%   94, 99, 104, 114-115, 121-139, 175, 178, 181, 187
crates/trogon-telemetry/src/metric.rs                                            26       1  96.15%   29
crates/mcp-nats/src/nats/subjects/server/elicitation_completed.rs                12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/resource_updated.rs                     12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/resource_list_changed.rs                12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/complete.rs                             12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/unsubscribe_resource.rs                 12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/list_resource_templates.rs              12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/cancel_task.rs                          12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/list_resources.rs                       12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/get_prompt.rs                           12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/get_task.rs                             12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/initialize.rs                           12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/get_task_result.rs                      12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/progress.rs                             12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/set_logging_level.rs                    12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/read_resource.rs                        12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/list_tasks.rs                           12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/list_tools.rs                           12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/logging_message.rs                      12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/ping.rs                                  9       0  100.00%
crates/mcp-nats/src/nats/subjects/server/list_prompts.rs                         12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/subscribe_resource.rs                   12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/tool_list_changed.rs                    12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/call_tool.rs                            12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/cancelled.rs                            12       0  100.00%
crates/mcp-nats/src/nats/subjects/server/prompt_list_changed.rs                  12       0  100.00%
crates/acp-nats/src/nats/subjects/commands/set_model.rs                          15       0  100.00%
crates/acp-nats/src/nats/subjects/commands/resume.rs                             15       0  100.00%
crates/acp-nats/src/nats/subjects/commands/set_config_option.rs                  15       0  100.00%
crates/acp-nats/src/nats/subjects/commands/set_mode.rs                           15       0  100.00%
crates/acp-nats/src/nats/subjects/commands/fork.rs                               15       0  100.00%
crates/acp-nats/src/nats/subjects/commands/load.rs                               15       0  100.00%
crates/acp-nats/src/nats/subjects/commands/cancel.rs                             15       0  100.00%
crates/acp-nats/src/nats/subjects/commands/close.rs                              15       0  100.00%
crates/acp-nats/src/nats/subjects/commands/prompt.rs                             15       0  100.00%
crates/trogon-gateway/src/source/slack/config.rs                                 17       0  100.00%
crates/trogon-gateway/src/source/slack/signature.rs                              77       0  100.00%
crates/trogon-gateway/src/source/slack/server.rs                                863       0  100.00%
crates/trogon-nats/src/lease/mod.rs                                             561      13  97.68%   180-193
crates/trogon-nats/src/lease/acquire.rs                                           5       5  0.00%    9-14
crates/trogon-nats/src/lease/renew_interval.rs                                   61       0  100.00%
crates/trogon-nats/src/lease/lease_key.rs                                        19       0  100.00%
crates/trogon-nats/src/lease/lease_config_error.rs                               11       0  100.00%
crates/trogon-nats/src/lease/nats_kv_lease_config.rs                             26       0  100.00%
crates/trogon-nats/src/lease/lease_timing.rs                                     15       0  100.00%
crates/trogon-nats/src/lease/provision.rs                                       187      10  94.65%   82-92
crates/trogon-nats/src/lease/release.rs                                           5       5  0.00%    8-12
crates/trogon-nats/src/lease/renew.rs                                           246      19  92.28%   23-29, 48-59
crates/trogon-nats/src/lease/ttl.rs                                              73       0  100.00%
crates/trogon-nats/src/lease/lease_bucket.rs                                     19       0  100.00%
crates/trogon-nats/src/jetstream/traits.rs                                       46      46  0.00%    145-248
crates/trogon-nats/src/jetstream/mocks.rs                                       670      32  95.22%   364-378, 384-392, 407-413, 427-430, 494-496
crates/trogon-nats/src/jetstream/claim_check.rs                                 346       0  100.00%
crates/trogon-nats/src/jetstream/stream_max_age.rs                               18       0  100.00%
crates/trogon-nats/src/jetstream/create_conflicts.rs                             24       0  100.00%
crates/trogon-nats/src/jetstream/publish.rs                                      64       0  100.00%
crates/trogon-gateway/src/source/microsoft_graph/server.rs                      325       0  100.00%
crates/trogon-gateway/src/source/microsoft_graph/client_state.rs                 30       0  100.00%
crates/trogon-gateway/src/source/standard_webhooks.rs                           172       0  100.00%
crates/trogon-gateway/src/source/notion/notion_verification_token.rs             17       0  100.00%
crates/trogon-gateway/src/source/notion/notion_event_type.rs                     46       3  93.48%   47-49
crates/trogon-gateway/src/source/notion/signature.rs                             56       1  98.21%   32
crates/trogon-gateway/src/source/notion/server.rs                               318       8  97.48%   92-96, 129-130, 149-150
crates/acp-nats-server/src/connection.rs                                        171      32  81.29%   76-83, 88-99, 115, 117-118, 123, 132-133, 138, 142, 146, 149, 157, 161, 164, 167-171, 207
crates/acp-nats-server/src/transport.rs                                        1852     110  94.06%   277, 452, 536, 554, 581, 635, 640, 659, 671, 790, 813-815, 867, 884-887, 982-985, 1059, 1062, 1065, 1074, 1078, 1081, 1084-1087, 1106, 1138-1141, 1149-1154, 1166-1170, 1174-1183, 1195-1196, 1214-1215, 1225, 1241-1245, 1273-1279, 1290, 1293-1300, 1305-1309, 1312-1317, 1334, 1336-1337, 1419-1420, 1432-1433, 1453-1454, 1506-1522, 2218, 2261, 2313, 2368, 2380
crates/acp-nats-server/src/acp_connection_id.rs                                  45       0  100.00%
crates/acp-nats-server/src/config.rs                                            137       9  93.43%   41, 50-61
crates/acp-nats-server/src/main.rs                                              896      10  98.88%   100, 231-238, 437
crates/acp-nats-stdio/src/main.rs                                               135      25  81.48%   65, 113-120, 126-128, 145, 174-193
crates/acp-nats-stdio/src/config.rs                                              66       0  100.00%
crates/trogon-std/src/telemetry/http.rs                                         217       0  100.00%
crates/trogon-gateway/src/source/twitter/config.rs                               17       0  100.00%
crates/trogon-gateway/src/source/twitter/server.rs                              525       0  100.00%
crates/trogon-gateway/src/source/twitter/signature.rs                            69       0  100.00%
crates/mcp-nats/src/nats/subjects/subscriptions/one_client.rs                     9       0  100.00%
crates/mcp-nats/src/nats/subjects/subscriptions/one_server.rs                     9       0  100.00%
crates/mcp-nats/src/nats/subjects/subscriptions/all_server.rs                     6       0  100.00%
crates/mcp-nats/src/nats/subjects/subscriptions/all_client.rs                     6       0  100.00%
crates/trogon-gateway/src/source/gitlab/gitlab_signing_token.rs                  74       0  100.00%
crates/trogon-gateway/src/source/gitlab/server.rs                               460       0  100.00%
crates/trogon-gateway/src/source/gitlab/signature.rs                            165       0  100.00%
crates/acp-nats/src/acp_prefix.rs                                                50       0  100.00%
crates/acp-nats/src/config.rs                                                   203       0  100.00%
crates/acp-nats/src/jsonrpc.rs                                                    6       0  100.00%
crates/acp-nats/src/error.rs                                                     82       0  100.00%
crates/acp-nats/src/pending_prompt_waiters.rs                                   134       0  100.00%
crates/acp-nats/src/req_id.rs                                                    39       0  100.00%
crates/acp-nats/src/ext_method_name.rs                                           68       0  100.00%
crates/acp-nats/src/session_id.rs                                                71       0  100.00%
crates/acp-nats/src/client_proxy.rs                                             181       0  100.00%
crates/acp-nats/src/lib.rs                                                       69       0  100.00%
crates/acp-nats/src/in_flight_slot_guard.rs                                      32       0  100.00%
crates/trogon-gateway/src/source/telegram/server.rs                             339       0  100.00%
crates/trogon-gateway/src/source/telegram/config.rs                             109       0  100.00%
crates/trogon-gateway/src/source/telegram/signature.rs                           32       0  100.00%
crates/trogon-gateway/src/source/telegram/registration.rs                       327       0  100.00%
crates/mcp-nats/src/nats/mod.rs                                                  99       0  100.00%
crates/mcp-nats/src/nats/parsing.rs                                             191       0  100.00%
crates/trogon-nats/src/telemetry/messaging.rs                                    82       0  100.00%
crates/trogon-gateway/src/config.rs                                            2559      48  98.12%   91, 110, 328-329, 332, 712, 715, 875, 878, 881, 885, 960, 963, 966, 970, 1054-1061, 1138, 1141, 1144, 1149, 1207, 1210, 1213, 1292, 1295, 1298, 1302, 1366, 1369, 1372, 1435, 1438, 1441, 1446, 1521, 1524, 1527, 1532, 1590, 1593, 1596, 1809-1811
crates/trogon-gateway/src/http.rs                                               192       1  99.48%   119
crates/trogon-gateway/src/source_integration_id.rs                               61       3  95.08%   55, 57, 65
crates/trogon-gateway/src/streams.rs                                            169      10  94.08%   11, 23, 31, 39, 47, 55, 63, 71, 79, 87
crates/trogon-gateway/src/source_status.rs                                       28       0  100.00%
crates/trogon-gateway/src/main.rs                                                 4       0  100.00%
crates/mcp-nats/src/nats/subjects/client/create_message.rs                       12       0  100.00%
crates/mcp-nats/src/nats/subjects/client/create_elicitation.rs                   12       0  100.00%
crates/mcp-nats/src/nats/subjects/client/roots_list_changed.rs                   12       0  100.00%
crates/mcp-nats/src/nats/subjects/client/cancelled.rs                            12       0  100.00%
crates/mcp-nats/src/nats/subjects/client/list_roots.rs                           12       0  100.00%
crates/mcp-nats/src/nats/subjects/client/progress.rs                             12       0  100.00%
crates/mcp-nats/src/nats/subjects/client/ping.rs                                  9       0  100.00%
crates/mcp-nats/src/nats/subjects/client/initialized.rs                          12       0  100.00%
crates/mcp-nats-stdio/src/main.rs                                               212       0  100.00%
crates/mcp-nats-stdio/src/config.rs                                             160       0  100.00%
crates/acp-nats/src/nats/parsing.rs                                             278       1  99.64%   151
crates/acp-nats/src/nats/extensions.rs                                            3       0  100.00%
crates/acp-nats/src/nats/mod.rs                                                  23       0  100.00%
crates/acp-nats/src/jetstream/ext_policy.rs                                      26       0  100.00%
crates/acp-nats/src/jetstream/provision.rs                                       53       0  100.00%
crates/acp-nats/src/jetstream/consumers.rs                                       91       0  100.00%
crates/acp-nats/src/jetstream/streams.rs                                        163       4  97.55%   206-208, 218
crates/trogon-gateway/src/source/sentry/signature.rs                             54       0  100.00%
crates/trogon-gateway/src/source/sentry/server.rs                               311       0  100.00%
crates/trogon-gateway/src/source/sentry/sentry_client_secret.rs                  17       0  100.00%
crates/trogon-gateway/src/source/linear/server.rs                               386       0  100.00%
crates/trogon-gateway/src/source/linear/signature.rs                             54       1  98.15%   16
crates/trogon-gateway/src/source/linear/config.rs                                17       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/all_client.rs                     9       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/one_session.rs                   12       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/all_agent_ext.rs                  9       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/prompt_wildcard.rs                9       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/all_agent.rs                      9       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/one_agent.rs                     15       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/all_session.rs                    9       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/global_all.rs                     9       0  100.00%
crates/acp-nats/src/nats/subjects/subscriptions/one_client.rs                    15       0  100.00%
crates/acp-nats/src/agent/js_request.rs                                         283       0  100.00%
crates/acp-nats/src/agent/set_session_config_option.rs                           67       0  100.00%
crates/acp-nats/src/agent/ext_method.rs                                          82       0  100.00%
crates/acp-nats/src/agent/initialize.rs                                          79       0  100.00%
crates/acp-nats/src/agent/load_session.rs                                        89       0  100.00%
crates/acp-nats/src/agent/new_session.rs                                         82       0  100.00%
crates/acp-nats/src/agent/set_session_model.rs                                   67       0  100.00%
crates/acp-nats/src/agent/bridge.rs                                             123       4  96.75%   108-111
crates/acp-nats/src/agent/mod.rs                                                 65       0  100.00%
crates/acp-nats/src/agent/logout.rs                                              49       0  100.00%
crates/acp-nats/src/agent/fork_session.rs                                        94       0  100.00%
crates/acp-nats/src/agent/list_sessions.rs                                       47       0  100.00%
crates/acp-nats/src/agent/authenticate.rs                                        49       0  100.00%
crates/acp-nats/src/agent/prompt.rs                                             471       0  100.00%
crates/acp-nats/src/agent/close_session.rs                                       63       0  100.00%
crates/acp-nats/src/agent/ext_notification.rs                                    82       0  100.00%
crates/acp-nats/src/agent/resume_session.rs                                      90       0  100.00%
crates/acp-nats/src/agent/set_session_mode.rs                                    67       0  100.00%
crates/acp-nats/src/agent/cancel.rs                                             101       0  100.00%
crates/acp-nats/src/agent/test_support.rs                                       267       0  100.00%
crates/trogon-service-config/src/lib.rs                                          92       0  100.00%
crates/acp-nats/src/client/terminal_release.rs                                  347       0  100.00%
crates/acp-nats/src/client/rpc_reply.rs                                          64       0  100.00%
crates/acp-nats/src/client/request_permission.rs                                308       0  100.00%
crates/acp-nats/src/client/mod.rs                                              2851       0  100.00%
crates/acp-nats/src/client/session_update.rs                                     55       0  100.00%
crates/acp-nats/src/client/fs_write_text_file.rs                                418       0  100.00%
crates/acp-nats/src/client/terminal_create.rs                                   274       0  100.00%
crates/acp-nats/src/client/terminal_output.rs                                   206       0  100.00%
crates/acp-nats/src/client/terminal_wait_for_exit.rs                            378       0  100.00%
crates/acp-nats/src/client/ext_session_prompt_response.rs                       135       0  100.00%
crates/acp-nats/src/client/terminal_kill.rs                                     290       0  100.00%
crates/acp-nats/src/client/ext.rs                                               308       8  97.40%   163-172, 189-198
crates/acp-nats/src/client/fs_read_text_file.rs                                 356       0  100.00%
crates/trogon-std/src/fs/mem.rs                                                 216      10  95.37%   61-63, 77-79, 132-134, 157
crates/trogon-std/src/fs/system.rs                                               92       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/fs_read_text_file.rs                12       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/fs_write_text_file.rs               12       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_output.rs                  12       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_wait_for_exit.rs           12       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/session_update.rs                   12       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_create.rs                  12       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/session_request_permission.rs       12       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_release.rs                 12       0  100.00%
crates/acp-nats/src/nats/subjects/client_ops/terminal_kill.rs                    12       0  100.00%
crates/acp-nats/src/nats/subjects/mod.rs                                        362       0  100.00%
crates/acp-nats/src/nats/subjects/stream.rs                                      56       0  100.00%
crates/trogon-gateway/src/source/github/server.rs                               328       0  100.00%
crates/trogon-gateway/src/source/github/config.rs                                17       0  100.00%
crates/trogon-gateway/src/source/github/signature.rs                             61       0  100.00%
crates/acp-nats/src/nats/subjects/global/ext.rs                                   9       0  100.00%
crates/acp-nats/src/nats/subjects/global/authenticate.rs                          6       0  100.00%
crates/acp-nats/src/nats/subjects/global/initialize.rs                            6       0  100.00%
crates/acp-nats/src/nats/subjects/global/session_new.rs                           6       0  100.00%
crates/acp-nats/src/nats/subjects/global/ext_notify.rs                            9       0  100.00%
crates/acp-nats/src/nats/subjects/global/logout.rs                                6       0  100.00%
crates/acp-nats/src/nats/subjects/global/session_list.rs                          6       0  100.00%
crates/mcp-nats/src/telemetry/transport.rs                                        6       0  100.00%
TOTAL                                                                         33158     627  98.11%

Diff against main

Filename                                                           Stmts    Miss  Cover
---------------------------------------------------------------  -------  ------  --------
crates/trogon-gateway/src/source/incidentio/signature.rs            -160       0  +100.00%
crates/trogon-gateway/src/source/standard_webhooks.rs               +172       0  +100.00%
crates/trogon-gateway/src/source/gitlab/gitlab_signing_token.rs      +74       0  +100.00%
crates/trogon-gateway/src/source/gitlab/server.rs                    +63       0  +100.00%
crates/trogon-gateway/src/source/gitlab/signature.rs                +137       0  +100.00%
crates/trogon-gateway/src/config.rs                                  +35      -1  +0.07%
TOTAL                                                               +321      -1  +0.02%

Results for commit: 1d2a062

Minimum allowed coverage is 95%

♻️ This comment has been updated with latest results

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
rsworkspace/crates/trogon-gateway/src/source/gitlab/signature.rs (2)

183-190: ⚡ Quick win

Keep WebhookId and WebhookTimestamp typed through the verifier.

verify() constructs the value objects and then immediately unwraps them back to &str for verify_signature() and signed_content(). Passing the domain types through those helpers keeps the construction-time guarantee intact and makes it harder for a future internal caller to bypass validation.

As per coding guidelines, "Prefer domain-specific value objects over primitives (e.g., AcpPrefix instead of String). Each type's factory must guarantee correctness at construction—invalid instances should be unrepresentable."

Also applies to: 214-220, 263-270

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rsworkspace/crates/trogon-gateway/src/source/gitlab/signature.rs` around
lines 183 - 190, The verifier currently constructs WebhookId and
WebhookTimestamp then immediately converts them to &str for verify_signature()
and signed_content(), losing the typed guarantees; update verify() (and the
other occurrences around the blocks at lines noted) to pass the WebhookId and
WebhookTimestamp value objects directly into verify_signature() and
signed_content() (and modify those helpers' signatures if needed) so the typed
domain objects are used end-to-end—ensure verify_signature(signing_token,
webhook_id: &WebhookId, webhook_timestamp: &WebhookTimestamp, body,
signature_header) and any callers (including the signed_content helper) accept
the typed values rather than raw &str to preserve construction-time validation.

221-224: ⚡ Quick win

Hash the three segments directly instead of materializing signed_content.

This currently allocates and copies the full body on every request before feeding HMAC. Updating the MAC incrementally removes that extra allocation on the request path.

♻️ Proposed change
-    let signed_content = signed_content(webhook_id, webhook_timestamp, body);
     let mut mac = HmacSha256::new_from_slice(signing_token.as_bytes()).expect("HMAC-SHA256 accepts any key length");
-    mac.update(&signed_content);
+    mac.update(webhook_id.as_bytes());
+    mac.update(b".");
+    mac.update(webhook_timestamp.as_bytes());
+    mac.update(b".");
+    mac.update(body);
     let computed = mac.finalize().into_bytes();

Also applies to: 263-270

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rsworkspace/crates/trogon-gateway/src/source/gitlab/signature.rs` around
lines 221 - 224, The code currently builds signed_content and then feeds it to
the HMAC, causing an extra allocation; change to update the HMAC incrementally
instead: after creating the HmacSha256 via
HmacSha256::new_from_slice(signing_token.as_bytes()), call mac.update for the
webhook_id bytes, then for the newline separator, then for the webhook_timestamp
bytes, then for the newline separator, and finally for the raw body bytes, and
then call mac.finalize().into_bytes() to produce computed; apply the same
incremental-update pattern to the other identical block around the
signed_content usage (the later block referenced at lines 263-270) and remove
the signed_content helper/usage.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@rsworkspace/crates/trogon-gateway/src/source/gitlab/signature.rs`:
- Around line 183-190: The verifier currently constructs WebhookId and
WebhookTimestamp then immediately converts them to &str for verify_signature()
and signed_content(), losing the typed guarantees; update verify() (and the
other occurrences around the blocks at lines noted) to pass the WebhookId and
WebhookTimestamp value objects directly into verify_signature() and
signed_content() (and modify those helpers' signatures if needed) so the typed
domain objects are used end-to-end—ensure verify_signature(signing_token,
webhook_id: &WebhookId, webhook_timestamp: &WebhookTimestamp, body,
signature_header) and any callers (including the signed_content helper) accept
the typed values rather than raw &str to preserve construction-time validation.
- Around line 221-224: The code currently builds signed_content and then feeds
it to the HMAC, causing an extra allocation; change to update the HMAC
incrementally instead: after creating the HmacSha256 via
HmacSha256::new_from_slice(signing_token.as_bytes()), call mac.update for the
webhook_id bytes, then for the newline separator, then for the webhook_timestamp
bytes, then for the newline separator, and finally for the raw body bytes, and
then call mac.finalize().into_bytes() to produce computed; apply the same
incremental-update pattern to the other identical block around the
signed_content usage (the later block referenced at lines 263-270) and remove
the signed_content helper/usage.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2f5d011b-632d-4a10-9189-9511660d8642

📥 Commits

Reviewing files that changed from the base of the PR and between 53dc901 and 07365a5.

📒 Files selected for processing (12)
  • devops/docker/compose/services/trogon-gateway/README.md
  • rsworkspace/crates/trogon-gateway/README.md
  • rsworkspace/crates/trogon-gateway/src/config.rs
  • rsworkspace/crates/trogon-gateway/src/constants.rs
  • rsworkspace/crates/trogon-gateway/src/http.rs
  • rsworkspace/crates/trogon-gateway/src/source/gitlab/config.rs
  • rsworkspace/crates/trogon-gateway/src/source/gitlab/constants.rs
  • rsworkspace/crates/trogon-gateway/src/source/gitlab/mod.rs
  • rsworkspace/crates/trogon-gateway/src/source/gitlab/server.rs
  • rsworkspace/crates/trogon-gateway/src/source/gitlab/signature.rs
  • rsworkspace/crates/trogon-gateway/src/streams.rs
  • services/trogon-gateway/gateway.toml

Comment thread rsworkspace/crates/trogon-gateway/src/source/gitlab/config.rs Outdated
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 07365a5. Configure here.

Comment thread rsworkspace/crates/trogon-gateway/src/source/gitlab/signature.rs Outdated
@yordis yordis force-pushed the yordis/fix-gitlab-signature branch 2 times, most recently from b6631ce to d100570 Compare May 13, 2026 03:33
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
rsworkspace/crates/trogon-gateway/src/config.rs (1)

2499-2499: ⚡ Quick win

Centralize GitLab test token literals to reduce leak-scanner noise.

These repeated inline whsec_... literals are likely to keep triggering secret scanners and create avoidable test-fixture churn. Please source them from one helper/constant and interpolate where needed.

♻️ Example consolidation pattern
- let toml = r#"
- [sources.gitlab.integrations.primary.webhook]
- signing_token = "whsec_MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE="
- "#;
+ let toml = format!(
+     r#"
+ [sources.gitlab.integrations.primary.webhook]
+ signing_token = "{}"
+ "#,
+     gitlab_signing_token()
+ );

Also applies to: 2822-2822, 2838-2838, 3287-3287, 3427-3427, 3574-3574

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rsworkspace/crates/trogon-gateway/src/config.rs` at line 2499, Centralize the
repeated GitLab test token string by adding a single constant (e.g.,
TEST_GITLAB_SIGNING_TOKEN) in the same module and replace every inline
"whsec_..." literal assigned to signing_token with that constant; update places
referenced (the signing_token assignments at the shown sites) to interpolate or
reference TEST_GITLAB_SIGNING_TOKEN instead of hardcoding the value so
secret-scanner noise is reduced and maintenance is easier.
rsworkspace/crates/trogon-gateway/src/source/standard_webhooks.rs (1)

24-65: ⚡ Quick win

Capture the missing header name in MissingHeaders for parity with InvalidHeaderValue.

InvalidHeaderValue already carries the offending header name, but MissingHeaders collapses three distinct failure points (webhook_id, webhook_timestamp, webhook_signature) into one opaque variant. When a sender misconfigures their headers in production, you currently can't tell from the typed error which header was absent. Promoting the name into the variant is a small change that materially improves diagnostics and stays consistent with the rest of the error contract.

♻️ Proposed change
 #[derive(Debug)]
 pub enum SignatureError {
-    MissingHeaders,
+    MissingHeader { name: &'static str },
     InvalidHeaderValue {
         name: &'static str,
         source: axum::http::header::ToStrError,
     },
     ...
 }

 impl fmt::Display for SignatureError {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            Self::MissingHeaders => f.write_str("missing required signature headers"),
+            Self::MissingHeader { name } => write!(f, "missing required signature header {name}"),
             ...
         }
     }
 }

 impl std::error::Error for SignatureError {
     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
         match self {
             ...
-            Self::MissingHeaders | Self::StaleTimestamp | Self::MissingV1Signature | Self::Mismatch => None,
+            Self::MissingHeader { .. } | Self::StaleTimestamp | Self::MissingV1Signature | Self::Mismatch => None,
         }
     }
 }

 fn header_str<'a>(headers: &'a HeaderMap, name: &'static str) -> Result<&'a str, SignatureError> {
     headers
         .get(name)
-        .ok_or(SignatureError::MissingHeaders)?
+        .ok_or(SignatureError::MissingHeader { name })?
         .to_str()
         .map_err(|source| SignatureError::InvalidHeaderValue { name, source })
 }

Also applies to: 209-215

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rsworkspace/crates/trogon-gateway/src/source/standard_webhooks.rs` around
lines 24 - 65, SignatureError::MissingHeaders currently has no context; change
the enum variant to MissingHeaders { name: &'static str } (mirroring
InvalidHeaderValue), update fmt::Display arm to print the header name (e.g.,
"missing required header {name}"), and adjust any constructors/places that
produce SignatureError::MissingHeaders to pass the specific header names
("webhook_id", "webhook_timestamp", "webhook_signature"); no change to source()
is needed since MissingHeaders has no inner error. Also update any references
noted around the other occurrence (lines ~209-215) to construct the new variant
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@rsworkspace/crates/trogon-gateway/src/config.rs`:
- Line 2499: Centralize the repeated GitLab test token string by adding a single
constant (e.g., TEST_GITLAB_SIGNING_TOKEN) in the same module and replace every
inline "whsec_..." literal assigned to signing_token with that constant; update
places referenced (the signing_token assignments at the shown sites) to
interpolate or reference TEST_GITLAB_SIGNING_TOKEN instead of hardcoding the
value so secret-scanner noise is reduced and maintenance is easier.

In `@rsworkspace/crates/trogon-gateway/src/source/standard_webhooks.rs`:
- Around line 24-65: SignatureError::MissingHeaders currently has no context;
change the enum variant to MissingHeaders { name: &'static str } (mirroring
InvalidHeaderValue), update fmt::Display arm to print the header name (e.g.,
"missing required header {name}"), and adjust any constructors/places that
produce SignatureError::MissingHeaders to pass the specific header names
("webhook_id", "webhook_timestamp", "webhook_signature"); no change to source()
is needed since MissingHeaders has no inner error. Also update any references
noted around the other occurrence (lines ~209-215) to construct the new variant
accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e41ffbeb-1d3e-44b3-b9c6-37b8ff9447cb

📥 Commits

Reviewing files that changed from the base of the PR and between 07365a5 and d100570.

📒 Files selected for processing (16)
  • devops/docker/compose/services/trogon-gateway/README.md
  • rsworkspace/crates/trogon-gateway/README.md
  • rsworkspace/crates/trogon-gateway/src/config.rs
  • rsworkspace/crates/trogon-gateway/src/constants.rs
  • rsworkspace/crates/trogon-gateway/src/http.rs
  • rsworkspace/crates/trogon-gateway/src/source/gitlab/config.rs
  • rsworkspace/crates/trogon-gateway/src/source/gitlab/constants.rs
  • rsworkspace/crates/trogon-gateway/src/source/gitlab/gitlab_signing_token.rs
  • rsworkspace/crates/trogon-gateway/src/source/gitlab/mod.rs
  • rsworkspace/crates/trogon-gateway/src/source/gitlab/server.rs
  • rsworkspace/crates/trogon-gateway/src/source/gitlab/signature.rs
  • rsworkspace/crates/trogon-gateway/src/source/incidentio/signature.rs
  • rsworkspace/crates/trogon-gateway/src/source/mod.rs
  • rsworkspace/crates/trogon-gateway/src/source/standard_webhooks.rs
  • rsworkspace/crates/trogon-gateway/src/streams.rs
  • services/trogon-gateway/gateway.toml
✅ Files skipped from review due to trivial changes (4)
  • devops/docker/compose/services/trogon-gateway/README.md
  • rsworkspace/crates/trogon-gateway/src/constants.rs
  • services/trogon-gateway/gateway.toml
  • rsworkspace/crates/trogon-gateway/README.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • rsworkspace/crates/trogon-gateway/src/source/gitlab/constants.rs
  • rsworkspace/crates/trogon-gateway/src/source/gitlab/server.rs

@yordis yordis force-pushed the yordis/fix-gitlab-signature branch 2 times, most recently from b1ddb06 to 6dfbe0b Compare May 13, 2026 03:56
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
@yordis yordis force-pushed the yordis/fix-gitlab-signature branch from 6dfbe0b to 1d2a062 Compare May 13, 2026 04:06
@yordis yordis merged commit 442f17f into main May 13, 2026
7 checks passed
@yordis yordis deleted the yordis/fix-gitlab-signature branch May 13, 2026 04:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant