Skip to content

fix: SignatureVerifier.is_valid raises ValueError on non-numeric time… - #1968

Open
Shubham-Padkonde wants to merge 2 commits into
slackapi:mainfrom
Shubham-Padkonde:fix/signature-verifier-non-numeric-timestamp
Open

Shubham-Padkonde wants to merge 2 commits into
slackapi:mainfrom
Shubham-Padkonde:fix/signature-verifier-non-numeric-timestamp

Conversation

@Shubham-Padkonde

@Shubham-Padkonde Shubham-Padkonde commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Return False instead of raising for non-numeric request timestamps. Regression coverage also preserves the five-minute replay window.

Testing

./scripts/run_validation.sh tests/slack_sdk/signature passes all 15 tests in the isolated Windows Python 3.13 environment. Project lint passes for 455 files. This was a targeted validation run, not the full SDK suite; the script skips mypy/format on Python 3.13.

Category

  • slack_sdk.web.WebClient (sync/async) (Web API client)

  • slack_sdk.webhook.WebhookClient (sync/async) (Incoming Webhook, response_url sender)

  • slack_sdk.socket_mode (Socket Mode client)

  • slack_sdk.signature (Request Signature Verifier)

  • slack_sdk.oauth (OAuth Flow Utilities)

  • slack_sdk.models (UI component builders)

  • slack_sdk.scim (SCIM API client)

  • slack_sdk.audit_logs (Audit Logs API client)

  • slack_sdk.rtm_v2 (RTM client)

  • /docs (Documents)

  • /tutorial (PythOnBoardingBot tutorial)

  • tests/integration_tests (Automated tests for this library)

Requirements

  • I've read and understood the Contributing Guidelines and have done my best effort to follow them.

  • I've read and agree to the Code of Conduct.

  • I've run python3 -m venv .venv && source .venv/bin/activate && ./scripts/run_validation.sh after making the changes.

Prepared with OpenAI Codex assistance. The full-validation and contributor-agreement checkboxes are left unchecked; the scoped validation above is the work actually performed.

…stamp

is_valid called int(timestamp) without catching ValueError, so any
request with a non-numeric X-Slack-Request-Timestamp header (e.g.
"not-a-number", an empty string, or a float like "1.5") caused an
unhandled exception rather than a clean False return.

Fix: wrap the int() conversion in a try/except (ValueError, TypeError)
and return False on failure, consistent with how None timestamps are
already handled.

Add two tests covering the new behaviour:
- test_is_valid_non_numeric_timestamp: direct is_valid() calls with
  invalid timestamp strings
- test_is_valid_request_non_numeric_timestamp_header: end-to-end call
  via is_valid_request() with a crafted header
@Shubham-Padkonde
Shubham-Padkonde requested a review from a team as a code owner September 24, 2026 04:42

@WilliamBergamin WilliamBergamin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for this @Shubham-Padkonde! 🙌

I reproduced this, a present but non-numeric X-Slack-Request-Timestamp header ("not-a-number", "", "1.5") makes is_valid raise ValueError instead of returning False. In bolt-python that bubbles out of the request verification middleware as a 500 rather than a clean 401. It fails closed either way, so there's no bypass, but returning False is the right behavior and the try/except is the minimal fix.

Left a few small nits. One more ask: could you fill in the PR template? Please tick the slack_sdk.signature and tests categories and the Requirements boxes, and confirm you've run ./scripts/run_validation.sh.

Comment on lines +102 to +103
# A non-integer X-Slack-Request-Timestamp header must cause is_valid to
# return False rather than raising ValueError from int().

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: The test name already says what this comment says, so I think we can drop it.

Suggested change
# A non-integer X-Slack-Request-Timestamp header must cause is_valid to
# return False rather than raising ValueError from int().

self.assertFalse(verifier.is_valid(self.body, "1.5", self.valid_signature))
self.assertFalse(verifier.is_valid(self.body, "", self.valid_signature))

def test_is_valid_request_non_numeric_timestamp_header(self):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: is_valid_request just normalizes the headers and hands off to is_valid, so this test runs the same code path as the one above. I'm fine keeping it as the end-to-end check. If you do, can the comment on lines 113–114 go too? Same reason as above.

)
self.assertFalse(verifier.is_valid(self.body, "not-a-number", self.valid_signature))
self.assertFalse(verifier.is_valid(self.body, "1.5", self.valid_signature))
self.assertFalse(verifier.is_valid(self.body, "", self.valid_signature))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit (optional): Could we add a case showing that a valid numeric timestamp outside the 5-minute window still returns False? That would pin down that the new try block didn't change the replay check. Something like verifier.is_valid(self.body, "1531420618", self.valid_signature) with a clock that's well past it.

@Shubham-Padkonde

Copy link
Copy Markdown
Contributor Author

Addressed in af94129: removed both redundant comments and added a deterministic replay-window test covering exactly 300 seconds, 301 seconds, and a future timestamp outside the window. All 15 signature tests and project lint pass. Prepared with Codex assistance.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants