Skip to content

#321 feat(cli): add --dry-run to 'alerts test' to preview payload without sending FIXED - #582

Merged
AbdulmalikAlayande merged 1 commit into
TegoLabs:mainfrom
veloura-dev:#321-feat(cli)--add---dry-run-to-'alerts-test'-to-preview-payload-without-sending-FIX
Jul 31, 2026
Merged

AbdulmalikAlayande merged 1 commit into
TegoLabs:mainfrom
veloura-dev:#321-feat(cli)--add---dry-run-to-'alerts-test'-to-preview-payload-without-sending-FIX

Conversation

@veloura-dev

Copy link
Copy Markdown
Contributor

What does this PR do?

This PR adds a --dry-run flag to the sorokeep alerts test command in src/commands/alerts.ts. When this flag is provided, the command constructs the test AlertEvent and prints the exact JSON payload (as well as the computed X-Sorokeep-Signature header for signed webhooks) directly to the console instead of dispatching the alert via deliverSingleAlert.

Closes #321


Why?

When configuring a new webhook or notification receiver, developers need a way to inspect the exact payload structure and HTTP headers (specifically for payload validation using webhook signing secrets) that sorokeep transmits. The --dry-run flag enables offline validation and debugging without executing actual HTTP requests or triggering destination alerting systems.


Does this touch secret-key handling or transaction submission?

  • Yes — see notes above

  • No


Checklist

  • Tests pass (npm test)

  • Type check passes (npx tsc --noEmit)

  • Lint passes (npm run lint)

  • Tests cover the new functionality (TDD preferred — see CONTRIBUTING.md)

  • No unnecessary dependencies added

  • Commit messages follow conventional format

  • No console.log in core logic

  • ADR added if this is a significant design decision (see docs/adr)

  • E2E sandbox tested, if this touches RPC or daemon behavior (see docs/e2e-sandbox.md)

@drips-wave

drips-wave Bot commented Jul 29, 2026

Copy link
Copy Markdown

@veloura-dev Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai

coderabbitai Bot commented Jul 29, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added a --dry-run option to alerts test for previewing alert payloads without sending them.
    • Displays rendered webhook content or raw JSON for other channels.
    • Shows the generated webhook signature when a secret is configured.

Walkthrough

Adds --dry-run to alerts test, printing the generated payload and optional webhook signature without invoking alert delivery. Tests cover unsigned and signed webhook configurations.

Changes

Alert dry-run preview

Layer / File(s) Summary
Dry-run payload, signing, and validation
src/commands/alerts.ts, tests/commands/alerts.test.ts
The command renders or serializes the test event, prints an HMAC signature for signed webhooks, returns before delivery, and tests both webhook paths.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers: abdulmalikalayande

Poem

I hop through dry-run fields with care,
A test alert blooms in printed air.
No webhook flies, no sender calls,
Yet signed headers grace the walls.
— A cheerful rabbit 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: adding a dry-run mode to alerts test.
Description check ✅ Passed The description matches the changeset and explains the dry-run payload and signature preview behavior.
Linked Issues check ✅ Passed The changes implement the linked issue's dry-run flag, payload output, signature preview, and delivery bypass with tests.
Out of Scope Changes check ✅ Passed The PR only touches the command and its tests, with no unrelated code changes apparent.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@gitguardian

gitguardian Bot commented Jul 29, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

Since your pull request originates from a forked repository, GitGuardian is not able to associate the secrets uncovered with secret incidents on your GitGuardian dashboard.
Skipping this check run and merging your pull request will create secret incidents on your GitGuardian dashboard.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
- - Generic High Entropy Secret ded54f4 tests/commands/guard-cli-export-import.test.ts View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@tests/commands/alerts.test.ts`:
- Around line 811-816: Strengthen the assertion around signatureCall in the
alert test to validate the complete X-Sorokeep-Signature header, not only its
prefix. Derive the expected HMAC using the test secret and printed payload, then
compare the logged header exactly against the corresponding sha256 signature.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 94ff9e7e-7112-48a5-8f90-8215f974d3d2

📥 Commits

Reviewing files that changed from the base of the PR and between 35d9237 and c16ea09.

📒 Files selected for processing (2)
  • src/commands/alerts.ts
  • tests/commands/alerts.test.ts
📜 Review details
⚠️ CI failures not shown inline (2)

GitHub Actions: CI Pipeline / 0_build-and-test (22.x).txt: #321 feat(cli): add --dry-run to 'alerts test' to preview payload without sending FIXED

Conclusion: failure

View job details

##[group]Run npm audit --audit-level=high
 �[36;1mnpm audit --audit-level=high�[0m
 shell: /usr/bin/bash -e {0}
 ##[endgroup]
 # npm audit report
 `@hono/node-server`  <2.0.5
 Severity: moderate
 Node.js Adapter for Hono: Path traversal in `serve-static` on Windows via encoded backslash (`%5C`) - https://github.com/advisories/GHSA-frvp-7c67-39w9
 fix available via `npm audit fix`
 node_modules/@hono/node-server
   `@modelcontextprotocol/sdk`  1.25.0 - 1.29.0
   Depends on vulnerable versions of `@hono/node-server`
   node_modules/@modelcontextprotocol/sdk
 axios  1.0.0 - 1.17.0
 Severity: high
 Axios: Excessive recursion in formDataToJSON can cause denial of service - https://github.com/advisories/GHSA-42h9-826w-cgv3
 Axios: Prototype pollution auth subfields can inject Basic auth - https://github.com/advisories/GHSA-xj6q-8x83-jv6g
 Axios: Deep formToJSON Key Recursion Can Cause Denial of Service - https://github.com/advisories/GHSA-pmv8-rq9r-6j72
 Axios: Fetch adapter `ReadableStream` uploads bypass `maxBodyLength` - https://github.com/advisories/GHSA-jqh4-m9w3-8hp9
 Axios: Prototype pollution gadgets can alter axios request construction - https://github.com/advisories/GHSA-mmx7-hfxf-jppx
 Axios: NO_PROXY bypass for 0.0.0.0 local addresses in axios - https://github.com/advisories/GHSA-f4gw-2p7v-4548
 Axios Node HTTP adapter can use an inherited proxy after interceptor config cloning - https://github.com/advisories/GHSA-gcfj-64vw-6mp9
 Axios form serializer maxDepth bypass via {} metatoken - https://github.com/advisories/GHSA-hcpx-6fm6-wx23
 Axios: Nested axios option objects can consume polluted prototype values - https://github.com/advisories/GHSA-7q8q-rj6j-mhjq
 Axios: HTTP/2 streamed uploads bypass `maxBodyLength` - https://github.com/advisories/GHSA-mwf2-3pr3-8698
 fix available via `npm audit fix`
 node_modules/axios
   `@stellar/stellar-sdk`  15.0.1 - 16.0.1
   Depends on vulnerable versions of axios
   node_modules/@stellar/stellar-sdk
 brace-expansion  <=5.0.7
 ...

GitHub Actions: CI Pipeline / build-and-test (22.x): #321 feat(cli): add --dry-run to 'alerts test' to preview payload without sending FIXED

Conclusion: failure

View job details

##[group]Run npm audit --audit-level=high
 �[36;1mnpm audit --audit-level=high�[0m
 shell: /usr/bin/bash -e {0}
 ##[endgroup]
 # npm audit report
 `@hono/node-server`  <2.0.5
 Severity: moderate
 Node.js Adapter for Hono: Path traversal in `serve-static` on Windows via encoded backslash (`%5C`) - https://github.com/advisories/GHSA-frvp-7c67-39w9
 fix available via `npm audit fix`
 node_modules/@hono/node-server
   `@modelcontextprotocol/sdk`  1.25.0 - 1.29.0
   Depends on vulnerable versions of `@hono/node-server`
   node_modules/@modelcontextprotocol/sdk
 axios  1.0.0 - 1.17.0
 Severity: high
 Axios: Excessive recursion in formDataToJSON can cause denial of service - https://github.com/advisories/GHSA-42h9-826w-cgv3
 Axios: Prototype pollution auth subfields can inject Basic auth - https://github.com/advisories/GHSA-xj6q-8x83-jv6g
 Axios: Deep formToJSON Key Recursion Can Cause Denial of Service - https://github.com/advisories/GHSA-pmv8-rq9r-6j72
 Axios: Fetch adapter `ReadableStream` uploads bypass `maxBodyLength` - https://github.com/advisories/GHSA-jqh4-m9w3-8hp9
 Axios: Prototype pollution gadgets can alter axios request construction - https://github.com/advisories/GHSA-mmx7-hfxf-jppx
 Axios: NO_PROXY bypass for 0.0.0.0 local addresses in axios - https://github.com/advisories/GHSA-f4gw-2p7v-4548
 Axios Node HTTP adapter can use an inherited proxy after interceptor config cloning - https://github.com/advisories/GHSA-gcfj-64vw-6mp9
 Axios form serializer maxDepth bypass via {} metatoken - https://github.com/advisories/GHSA-hcpx-6fm6-wx23
 Axios: Nested axios option objects can consume polluted prototype values - https://github.com/advisories/GHSA-7q8q-rj6j-mhjq
 Axios: HTTP/2 streamed uploads bypass `maxBodyLength` - https://github.com/advisories/GHSA-mwf2-3pr3-8698
 fix available via `npm audit fix`
 node_modules/axios
   `@stellar/stellar-sdk`  15.0.1 - 16.0.1
   Depends on vulnerable versions of axios
   node_modules/@stellar/stellar-sdk
 brace-expansion  <=5.0.7
 ...
🔇 Additional comments (2)
src/commands/alerts.ts (1)

17-17: LGTM!

Also applies to: 210-210, 238-259

tests/commands/alerts.test.ts (1)

763-787: LGTM!

Comment on lines +811 to +816
// Should print X-Sorokeep-Signature: sha256=<hmac>
const signatureCall = consoleLogSpy.mock.calls.find((args) =>
typeof args[0] === "string" && args[0].startsWith("X-Sorokeep-Signature:")
);
expect(signatureCall).toBeTruthy();
expect(signatureCall![0]).toContain("X-Sorokeep-Signature: sha256=");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the signature value, not just its prefix.

This passes for any sha256= value, including a signature generated with the wrong secret or body. Compute the expected HMAC over the printed payload and compare the complete header.

Proposed test assertion
+            const { createHmac } = await import("node:crypto");
+            const bodyCall = consoleLogSpy.mock.calls.find(([value]) => {
+                try {
+                    return JSON.parse(value).type === "threshold_crossed";
+                } catch {
+                    return false;
+                }
+            });
+
             expect(signatureCall).toBeTruthy();
-            expect(signatureCall![0]).toContain("X-Sorokeep-Signature: sha256=");
+            const expected = createHmac("sha256", "dry-run-secret")
+                .update(bodyCall![0])
+                .digest("hex");
+            expect(signatureCall![0]).toBe(`X-Sorokeep-Signature: sha256=${expected}`);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Should print X-Sorokeep-Signature: sha256=<hmac>
const signatureCall = consoleLogSpy.mock.calls.find((args) =>
typeof args[0] === "string" && args[0].startsWith("X-Sorokeep-Signature:")
);
expect(signatureCall).toBeTruthy();
expect(signatureCall![0]).toContain("X-Sorokeep-Signature: sha256=");
const { createHmac } = await import("node:crypto");
const bodyCall = consoleLogSpy.mock.calls.find(([value]) => {
try {
return JSON.parse(value).type === "threshold_crossed";
} catch {
return false;
}
});
// Should print X-Sorokeep-Signature: sha256=<hmac>
const signatureCall = consoleLogSpy.mock.calls.find((args) =>
typeof args[0] === "string" && args[0].startsWith("X-Sorokeep-Signature:")
);
expect(signatureCall).toBeTruthy();
const expected = createHmac("sha256", "dry-run-secret")
.update(bodyCall![0])
.digest("hex");
expect(signatureCall![0]).toBe(`X-Sorokeep-Signature: sha256=${expected}`);
🤖 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 `@tests/commands/alerts.test.ts` around lines 811 - 816, Strengthen the
assertion around signatureCall in the alert test to validate the complete
X-Sorokeep-Signature header, not only its prefix. Derive the expected HMAC using
the test secret and printed payload, then compare the logged header exactly
against the corresponding sha256 signature.

@AbdulmalikAlayande
AbdulmalikAlayande merged commit 5fb268f into TegoLabs:main Jul 31, 2026
2 of 5 checks passed
@AbdulmalikAlayande

Copy link
Copy Markdown
Collaborator

Merged into main (5fb268f) after local verification (tsc, lint, full suite 1192/1192, npm audit, build) and a manual end-to-end smoke test — confirmed the dry-run signature output matches an independently-computed HMAC-SHA256 over the exact printed payload byte-for-byte. Clean implementation, correctly mirrors webhook.ts's real signing logic. Thanks!

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.

feat(cli): add --dry-run to 'alerts test' to preview payload without sending

3 participants