harden(serve): strict JIRA_BOARD_ID parse + share one digits-only int primitive#79
Merged
Conversation
…mitive Follow-up to #77 (review findings F3 + F2). F3 — JIRA_BOARD_ID used Number.parseInt, which silently coerced trailing garbage and signs into a plausible-but-wrong board id ('12abc'→12, '-5'→-5, '1e3'→1) that was then pinned via Number.isFinite. Now resolved through a strict loader: unset/blank → undefined (no board), set-but-invalid → throw INVALID_CONFIG. This matches how resolvePollingSyncIntervalMs already treats a malformed optional numeric env var (the codebase's own precedent), instead of silently dropping or misinterpreting it. BEHAVIOR CHANGE: a non-empty malformed JIRA_BOARD_ID now fails config loading with INVALID_CONFIG rather than being silently ignored. Updated the wiring test that previously asserted silent fallback. F2 — extracted parseDecimalDigits (transport-input.ts) as the single pure digits-only/safe-integer primitive; parseBoundedInt, resolvePollingSyncIntervalMs, and the new JIRA_BOARD_ID loader all build on it, so "what counts as an integer" has one definition and can't drift (no third hand-rolled copy). Error codes/messages unchanged (INVALID_ARGUMENT vs INVALID_CONFIG preserved per path). +4 tests (parseDecimalDigits unit coverage; JIRA_BOARD_ID accept/omit/reject). Suite 499 pass / 0 fail, bun run check clean. Claude-Session: https://claude.ai/code/session_01LQjR5poSV2M3nxt8h5hNad
This was referenced Jun 23, 2026
abpai
added a commit
that referenced
this pull request
Jun 24, 2026
The serve-hardening and CLI work since v0.7.0 (PRs #76, #77, #78, #79) merged without changesets, so the Release workflow had nothing to consume and the package is still published at 0.7.0. Add the missing changesets so the next release captures this work. - serve-api-webhook-hardening (patch): #76 - envelope webhook-route errors plus tunnel / Postgres-receipt / broadcast / base-path fixes (10 defects). - webhook-secret-fail-open (patch): #77 - single WEBHOOK_SECRET_ENV source of truth for the tunnel gate + stricter KANBAN_SYNC_INTERVAL_MS parsing. - honor-default-task-column (patch): #78 - SQLite createTask honors the configured default task column, with first-column fallback parity with Postgres. - strict-jira-board-id (minor): #79 - a malformed JIRA_BOARD_ID now throws INVALID_CONFIG instead of being silently misparsed (behavior change). Net bump: minor -> 0.8.0. #80 (pure internal refactor) and the README link fix are intentionally omitted as they have no user-facing change. Claude-Session: https://claude.ai/code/session_01X9j5Rs6kXK8BuHguhy3x33
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #77, addressing the two review findings that were deferred there (F3, F2).
F3 — strict
JIRA_BOARD_ID(the headline change)trackerConfigFromEnvparsedJIRA_BOARD_IDwithNumber.parseInt(raw, 10)+Number.isFinite, which silently coerced malformed values into a plausible-but-wrong board id:'12abc'→12,'1e3'→1,'-5'→-5,'0x10'→0— all pinned a wrong board'notanumber'→ dropped silentlyNow resolved through a strict loader:
undefined(no board pinned) — unchangedINVALID_CONFIG(JIRA_BOARD_ID must be a positive integer)A non-empty malformed
JIRA_BOARD_IDnow fails config loading instead of being silently ignored. This matches howresolvePollingSyncIntervalMsalready treats a malformed optional numeric env var (the codebase's own precedent — it throwsINVALID_CONFIGrather than falling back), and avoids silently pinning a wrong board. The wiring test that asserted silent fallback was updated to assert the throw.F2 — one shared digits-only primitive
Extracted
parseDecimalDigits(value): number | nullas the single pure digits-only / safe-integer parser.parseBoundedInt(CLI/HTTP,INVALID_ARGUMENT),resolvePollingSyncIntervalMs(env,INVALID_CONFIG), and the newJIRA_BOARD_IDloader all build on it — so "what counts as an integer" has one definition and can't drift. Each caller keeps its own error code/message (no error-code threading, per the review's guidance). Removes the previously hand-rolled duplicate parse body.Verification
bun run checkclean; suite 499 pass / 27 skip / 0 fail (+4 new:parseDecimalDigitsunit coverage;JIRA_BOARD_IDaccept/omit/reject).parseBoundedIntandresolvePollingSyncIntervalMsbehavior byte-identical (the refactor is a pure dedup); no import cycle.https://claude.ai/code/session_01LQjR5poSV2M3nxt8h5hNad