| type | Feature |
|---|---|
| title | Add a bounded request timeout to outbound webhook axios.post calls |
| labels | type:security, area:webhooks, stack:nodejs, stack:typescript, stack:express, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN |
| assignees |
WebhookService.send in src/services/webhook.service.ts delivers each webhook with await axios.post(payload.url, payload.data, { headers }) and no timeout option. The bounded retry loop (maxAttempts = WEBHOOK_RETRY_POLICY.maxRetries + 1) does not help here: if a destination endpoint accepts the connection but never responds, each attempt can hang for a very long time, so a single slow or malicious receiver can pin a delivery worker indefinitely and stall the queue. Because the URL is caller-supplied (subject only to the SSRF guard), an attacker can register a deliberately slow endpoint to exhaust delivery capacity.
- Repository scope: Talenttrust/Talenttrust-Backend only.
- Add a configurable per-request timeout (e.g.
WEBHOOK_DELIVERY_TIMEOUT_MS, default ~10s) read from validated config insrc/config/env.schema.ts, and pass it to theaxios.postcall insrc/services/webhook.service.ts. - A timed-out attempt must be treated like any other transient failure: count toward
payload.retryCount, back off viacalculateWebhookRetryDelay, and fall through to the DLQ on exhaustion β never resolve silently. - Ensure the timeout interacts correctly with the existing per-host rate-limit and SSRF re-check; do not double-count a single attempt.
- Do not log the full destination URL or payload body at info level; keep host-only logging.
- Fork the repo and create a branch
git checkout -b security/webhooks-timeout-axios-post- Implement changes
- Write code in:
src/services/webhook.service.tsandsrc/config/env.schema.ts. - Write comprehensive tests in: create
src/services/webhook.service.test.tsβ mock axios to simulate a hanging endpoint and assert the timeout fires, the attempt is retried, and exhaustion routes to the DLQ. - Add documentation: document the timeout env var in
docs/WEBHOOK-DLQ.md. - Add TSDoc to the delivery method noting the timeout semantics.
- Validate security: a slow receiver cannot block delivery beyond the configured timeout.
- Write code in:
- Test and commit
- Run
npm run lintandnpm test. - Cover edge cases: hang on connect, hang after headers, timeout on the last attempt, fast success.
- Include the full
npm testoutput and a security notes section in the PR.
fix(webhooks): add bounded timeout to outbound webhook delivery
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the TalentTrust community on Discord for questions, reviews, and faster merges: https://discord.gg/WqnGpcPx
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Validate correlation IDs before injecting them into outbound webhook headers" labels: type:security, area:observability, stack:nodejs, stack:typescript, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
buildWebhookHeaders in src/utils/correlationId.ts sets headers['X-Correlation-Id'] = correlationId for any truthy value, and WebhookService.send in src/services/webhook.service.ts does the same inline. The module's own @security docblock claims correlation IDs are "validated before use (alphanumeric + hyphen/underscore, max 128 chars)" and "HTTP headers are only set after validation to prevent injection attacks" β but no such validation exists in the code. A correlation ID carrying CR/LF or other control characters could enable header injection or response splitting into downstream receivers. This issue makes the documented guarantee real.
- Repository scope: Talenttrust/Talenttrust-Backend only.
- Add a single shared
isValidCorrelationId/sanitizeCorrelationIdhelper insrc/utils/correlationId.tsenforcing the documented charset (alphanumeric, hyphen, underscore) and max length (128). - Apply it in
buildWebhookHeadersand in the inline header build insidesrc/services/webhook.service.ts; an invalid ID is dropped (header omitted), never passed through. - Reuse the same validation wherever a correlation ID is accepted from an inbound
X-Correlation-Idheader so untrusted input cannot reach logs or headers unfiltered. - Do not throw on invalid IDs in the delivery hot path; fail safe by omitting the header.
- Fork the repo and create a branch
git checkout -b security/correlation-id-header-validation- Implement changes
- Write code in:
src/utils/correlationId.tsandsrc/services/webhook.service.ts. - Write comprehensive tests in: create
src/utils/correlationId.test.tsβ assert CR/LF and over-length IDs are rejected/omitted and that valid IDs pass through. - Add documentation: keep the
@securitydocblock accurate and note the rule indocs/API.md. - Add TSDoc to the new validator.
- Validate security: no header-injection path remains via correlation IDs.
- Write code in:
- Test and commit
- Run
npm run lintandnpm test. - Cover edge cases: embedded newline, header-splitting payload, 129-char id, empty id, valid id.
- Include the full
npm testoutput and a security notes section in the PR.
fix(observability): validate correlation IDs before setting webhook headers
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the TalentTrust community on Discord for questions, reviews, and faster merges: https://discord.gg/WqnGpcPx
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Bound HTTP metrics route-label cardinality to prevent a metric-explosion DoS" labels: type:security, area:observability, stack:nodejs, stack:typescript, stack:express, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
MetricsService.trackHttpRequest in src/observability/metrics-service.ts labels http_requests_total and http_request_duration_seconds with route = extractRoute(req). If extractRoute ever falls back to a raw URL/path (rather than the matched Express route template), then paths embedding identifiers β /contracts/abc, /contracts/def, β¦ β each create a new Prometheus time series. An attacker hitting many distinct paths can blow up label cardinality, exhausting memory in the process and in the scraping Prometheus. This is distinct from existing header-redaction and per-provider-gauge work; it is specifically about the unbounded route label.
- Repository scope: Talenttrust/Talenttrust-Backend only.
- Ensure
extractRouteresolves to the matched route template (e.g.req.route?.pathjoined withbaseUrl) and never the concrete path with embedded ids; collapse unmatched requests to a singleunmatchedbucket. - Add a hard cap on distinct
routelabel values; once exceeded, attribute further routes to anotherbucket so cardinality cannot grow without bound. - Keep
methodandstatus_codelabels intact; do not change metric names. - Make the cap configurable via validated config in
src/config/env.schema.ts.
- Fork the repo and create a branch
git checkout -b security/metrics-route-cardinality-cap- Implement changes
- Write code in:
src/observability/metrics-service.ts. - Write comprehensive tests in: create
src/observability/metrics-service.test.tsβ fire many distinct concrete paths and assert the number ofroutelabel values stays bounded and unmatched paths collapse. - Add documentation: note the cardinality guard in the observability/health section of
README.md. - Add TSDoc to
extractRouteand the cap logic. - Validate security: distinct user-controlled paths cannot create unbounded series.
- Write code in:
- Test and commit
- Run
npm run lintandnpm test. - Cover edge cases: matched template, unmatched 404, cap boundary, high-cardinality flood.
- Include the full
npm testoutput and a security notes section in the PR.
fix(observability): bound HTTP metrics route-label cardinality
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the TalentTrust community on Discord for questions, reviews, and faster merges: https://discord.gg/WqnGpcPx
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Fail loudly instead of silently downgrading unknown npm-audit severities to low" labels: type:security, area:dependency-scan, stack:nodejs, stack:typescript, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
normalizeSeverity in src/security/npm-audit-parser.ts returns 'low' for any value it does not recognise: if (typeof value === 'string' && isSeverity(value)) return value; return 'low';. If npm changes its severity labels, or a vulnerability arrives with an unexpected label, a genuinely critical advisory is silently reclassified as low and may slip past the dependency policy gate in src/security/dependency-policy.ts. A security scanner must never quietly weaken a finding. This issue makes unknown severities fail safe (treated as most-severe and/or surfaced) instead of fail-open.
- Repository scope: Talenttrust/Talenttrust-Backend only.
- Change
normalizeSeverityso an unrecognised severity is preserved/flagged rather than downgraded β either map to the highest severity or carry an explicitunknownmarker that the policy evaluator treats as blocking. - Emit a structured warning (via
src/logger.ts) recording the original unrecognised value so operators can update mappings. - Keep
normalizeCountsand the summary shape consumed bysrc/security/dependency-scan-service.tsbackward compatible. - Coordinate with the fail-closed path already present in
dependency-policy.tsso behaviour is consistent and not double-counted.
- Fork the repo and create a branch
git checkout -b security/audit-parser-unknown-severity- Implement changes
- Write code in:
src/security/npm-audit-parser.ts. - Write comprehensive tests in: create
src/security/npm-audit-parser.test.tsβ assert known severities pass through, unknown labels are not silently downgraded, and a warning is emitted. - Add documentation: note the severity handling in
docs/dependency-scanning.md(create if absent). - Add TSDoc to
normalizeSeverity. - Validate security: no advisory can be silently weakened.
- Write code in:
- Test and commit
- Run
npm run lintandnpm test. - Cover edge cases: each known severity, a novel label, non-string value, missing field.
- Include the full
npm testoutput and a security notes section in the PR.
fix(dependency-scan): stop silently downgrading unknown audit severities
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the TalentTrust community on Discord for questions, reviews, and faster merges: https://discord.gg/WqnGpcPx
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add the missing notifications table migration backing NotificationRepository" labels: type:feature, area:notifications, stack:nodejs, stack:typescript, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
NotificationRepository in src/repositories/notificationRepository.ts reads and writes a notifications table β saveWebNotification inserts into it and findByUser runs SELECT ... FROM notifications WHERE user_id = ? ORDER BY created_at DESC β but the migration list in src/db/migrations.ts never creates that table. Any call therefore fails at runtime with "no such table: notifications", so web notifications are completely broken in a fresh database. This issue adds the migration (with a user_id index) so the repository works against a real schema.
- Repository scope: Talenttrust/Talenttrust-Backend only.
- Add a new, append-only migration in
src/db/migrations.tscreatingnotifications(id PK, user_id, title, message, created_at)matching the columns selected/inserted bysrc/repositories/notificationRepository.ts. - Add an index on
user_id(andcreated_at) sofindByUser's ordered lookup does not table-scan as volume grows. - Keep the migration consistent with the existing checksum/transaction machinery in
migrations.ts; do not edit prior migrations. - Confirm the repository's column names and the
created_atβcreatedAtmapping align with the new schema.
- Fork the repo and create a branch
git checkout -b feature/notifications-table-migration- Implement changes
- Write code in:
src/db/migrations.ts(new migration) and adjustsrc/repositories/notificationRepository.tsonly if needed for alignment. - Write comprehensive tests in: create
src/repositories/notificationRepository.test.tsβ run migrations on a fresh DB, save and read back notifications, and assert ordering and per-user isolation. - Add documentation: note the table in
docs/migrations.md(or the DB docs). - Add TSDoc to the repository methods.
- Validate: a freshly migrated database supports save/read without "no such table".
- Write code in:
- Test and commit
- Run
npm run lintandnpm test. - Cover edge cases: empty result for an unknown user, multiple notifications ordered desc, idempotent re-run of migrations.
- Include the full
npm testoutput and notes in the PR.
feat(notifications): add notifications table migration with user_id index
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the TalentTrust community on Discord for questions, reviews, and faster merges: https://discord.gg/WqnGpcPx
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Replace the resetting fixed-window per-host webhook limiter with a true sliding window" labels: type:enhancement, area:webhooks, stack:nodejs, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
WebhookService.checkHostRateLimit in src/services/webhook.service.ts implements a fixed window that fully resets count and windowStart whenever now - entry.windowStart > HOST_RATE_LIMIT_WINDOW_MS. Its own TSDoc claims it uses "the same sliding-window algorithm as the HTTP rate-limit middleware," but the reset boundary lets up to 2 Γ HOST_RATE_LIMIT_MAX deliveries through across a single boundary (e.g. 60 just before reset, then 60 just after). The blocked/blockedUntil fields on the entry are also initialised but never used. This issue makes the per-host limiter behave as documented.
- Repository scope: Talenttrust/Talenttrust-Backend only.
- Replace the reset-on-expiry logic with a genuine sliding window (timestamp ledger or weighted previous/current window) so the configured max holds across any rolling
HOST_RATE_LIMIT_WINDOW_MS. - Either use the dead
blocked/blockedUntilfields meaningfully or remove them to avoid confusion. - Keep the shared
RateLimitStorefromsrc/lib/rateLimitStore.tsand the existing DLQ-on-limit behaviour intact. - Continue keying on hostname only (no raw URLs) and keep the limiter shared across instances.
- Fork the repo and create a branch
git checkout -b enhancement/webhook-host-sliding-window- Implement changes
- Write code in:
src/services/webhook.service.ts(andsrc/lib/rateLimitStore.tsif shared logic is needed). - Write comprehensive tests in: create
src/services/webhook.service.test.tsβ use fake timers to drive a burst straddling the window boundary and assert the rolling max is enforced. - Add documentation: document the per-host limit and env vars in
docs/WEBHOOK-DLQ.md. - Add TSDoc clarifying the algorithm actually used.
- Validate: no boundary burst exceeds the configured rolling max.
- Write code in:
- Test and commit
- Run
npm run lintandnpm test. - Cover edge cases: at-limit, boundary burst, idle host eviction, multiple hosts independent.
- Include the full
npm testoutput and notes in the PR.
fix(webhooks): enforce a true sliding window in per-host rate limiter
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the TalentTrust community on Discord for questions, reviews, and faster merges: https://discord.gg/WqnGpcPx
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Advance the contract indexer cursor only over accepted events and never backwards" labels: type:feature, area:indexer, stack:nodejs, stack:typescript, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
ContractEventIndexer.indexBatch in src/contracts/indexer.ts computes maxSequence from accepted and duplicate events and then calls cursorRepository.updateCursor(sourceId, maxSequence). Two problems: (1) events that fail validation (status === 'invalid') are skipped for sequence tracking β good β but the cursor is advanced to the highest seen sequence even when later events in the same batch were rejected, so a gap can be created where rejected events are never revisited; and (2) updateCursor advances to whatever maxSequence is passed, with no guarantee it is monotonic, so an out-of-order or replayed batch could move the cursor backwards and force re-processing. This issue tightens both.
- Repository scope: Talenttrust/Talenttrust-Backend only.
- Advance the cursor only to the highest contiguously accepted/duplicate sequence, so a rejected event does not let the cursor skip past unprocessed work.
- Make
updateCursorinsrc/contracts/cursor.repository.tsreject a non-monotonic move (return a typed "cannot move cursor backwards" result) so replays cannot rewind it. - Preserve the dedupe guarantees in
src/contracts/dedupe.tsand the existingIndexerBatchResultshape. - Keep batch sorting (
sortEventsBySequence) and per-event error isolation unchanged.
- Fork the repo and create a branch
git checkout -b feature/indexer-cursor-monotonic-accepted- Implement changes
- Write code in:
src/contracts/indexer.tsandsrc/contracts/cursor.repository.ts. - Write comprehensive tests in: create
src/contracts/indexer.cursor.test.tsβ assert the cursor does not skip past a rejected event and a backward update is refused. - Add documentation: note the cursor invariants in
docs/backend. - Add TSDoc to
indexBatchandupdateCursor. - Validate: no replay can rewind the cursor; no gap is created by invalid events.
- Write code in:
- Test and commit
- Run
npm run lintandnpm test. - Cover edge cases: invalid event mid-batch, duplicate batch replay, out-of-order sequences, empty batch.
- Include the full
npm testoutput and notes in the PR.
fix(indexer): advance cursor only over accepted events and enforce monotonicity
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the TalentTrust community on Discord for questions, reviews, and faster merges: https://discord.gg/WqnGpcPx
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Equalize the login not-found path to remove a user-enumeration timing side channel" labels: type:security, area:auth, stack:nodejs, stack:typescript, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
AuthService.login in src/services/auth.service.ts tries to be constant-time by hashing a synthetic value when the user is missing: const storedHash = row?.password_hash ?? \${"a".repeat(32)}:${"b".repeat(128)}`. But the synthetic salt:hash is a fixed, trivially-shaped string, so verifyPassword` may exercise a different code path/cost than a real stored hash, leaving a measurable timing gap between "no such user" and "wrong password." That gap enables user enumeration. This issue makes the not-found path indistinguishable from the wrong-password path.
- Repository scope: Talenttrust/Talenttrust-Backend only.
- Replace the ad-hoc dummy hash with a precomputed, realistically-shaped decoy hash (same salt length, same KDF parameters as real hashes) so
verifyPassworddoes equivalent work on both paths. - Ensure the not-found and wrong-password branches return the identical error/code and take comparable time; keep the existing
invalid_credentialscontract. - Do not log whether the email existed; route any diagnostics through the structured logger without leaking existence.
- Keep
verifyPassword/hashRefreshTokenbehaviour and the issued-token flow unchanged.
- Fork the repo and create a branch
git checkout -b security/auth-login-timing-equalize- Implement changes
- Write code in:
src/services/auth.service.ts. - Write comprehensive tests in:
src/services/auth.service.test.tsβ assert identical error for unknown user vs wrong password and that the decoy hash matches real KDF parameters. - Add documentation: note the anti-enumeration rationale in the auth runbook / module TSDoc.
- Add TSDoc explaining the constant-time intent.
- Validate security: no response or timing distinguishes unknown email from wrong password.
- Write code in:
- Test and commit
- Run
npm run lintandnpm test. - Cover edge cases: unknown email, known email wrong password, known email correct password, empty inputs.
- Include the full
npm testoutput and a security notes section in the PR.
fix(auth): equalize login not-found path against user enumeration
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the TalentTrust community on Discord for questions, reviews, and faster merges: https://discord.gg/WqnGpcPx
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Surface web-notification persistence failures instead of returning success" labels: type:enhancement, area:notifications, stack:nodejs, stack:typescript, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
In src/services/notification.service.ts the web-notification path wraps this.repo.saveWebNotification(...) in a try/catch that only logger.errors and then continues, so the method still resolves as if the notification was delivered. When the database is down, full, or rejects a constraint, callers receive a success result while nothing was persisted β a silent data-loss disguised as success. findByUser will later return nothing, and operators have no signal. This issue makes a persistence failure a real failure result.
- Repository scope: Talenttrust/Talenttrust-Backend only.
- On a
saveWebNotificationfailure, return a{ success: false, ... }result (matching the existingNotificationResultshape) instead of swallowing the error; do not throw across the public boundary unless that is the established contract. - Keep logging redacted β do not log full user PII or message bodies at info level; route through
src/logger.ts. - Ensure the change is consistent with how other channels in the service report failures, so callers can branch on outcome.
- Update any caller in
src/services/notification.service.tsthat assumed success-on-return.
- Fork the repo and create a branch
git checkout -b enhancement/notification-persist-failure-surface- Implement changes
- Write code in:
src/services/notification.service.ts. - Write comprehensive tests in: create
src/services/notification.service.test.tsβ mock the repository to throw and assert the result reports failure (not success). - Add documentation: note the failure contract in
docs/email-notifications.md(or the notifications docs). - Add TSDoc to the web-notification method.
- Validate: a persistence error never returns a success result.
- Write code in:
- Test and commit
- Run
npm run lintandnpm test. - Cover edge cases: repo throws, repo succeeds, missing fields, redaction of PII in logs.
- Include the full
npm testoutput and notes in the PR.
fix(notifications): report failure when web-notification persistence fails
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the TalentTrust community on Discord for questions, reviews, and faster merges: https://discord.gg/WqnGpcPx
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Implement real SMTP, SES, and SendGrid email transports behind a fail-fast guard" labels: type:feature, area:notifications, stack:nodejs, stack:typescript, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
SMTPTransport, SESTransport, and SendGridTransport in src/services/notification.transport.ts are placeholders: each logs a (placeholder) line at info level and returns { success: true } without dispatching anything (the code even carries TODO: In production, install and use nodemailer here). Any deployment selecting one of these transports silently drops every email β password resets, dispute alerts, etc. β while reporting success. This issue implements a real dispatch path and makes a misconfigured/placeholder transport fail fast rather than succeed silently.
- Repository scope: Talenttrust/Talenttrust-Backend only.
- Implement at least one real provider end-to-end (SMTP via nodemailer is the cleanest) and wire provider selection from validated config in
src/config/env.schema.ts; keep theNotificationResultcontract. - For any provider that remains unimplemented, fail fast at construction or selection (not at send time) and log at WARN/ERROR β never INFO placeholder + success.
- Validate recipients and guard against header injection before dispatch; redact recipient/body in logs via
src/logger.ts. - Keep the transport injectable so tests can supply a mock without real network calls.
- Fork the repo and create a branch
git checkout -b feature/notification-real-email-transports- Implement changes
- Write code in:
src/services/notification.transport.ts. - Write comprehensive tests in: create
src/services/notification.transport.test.tsβ mock the provider client and assert real dispatch, provider-failure propagation, and fail-fast on an unconfigured transport. - Add documentation: document transport selection and config in
docs/email-notifications.md. - Add TSDoc to each transport's
send. - Validate security: header-injection guard; no PII in logs.
- Write code in:
- Test and commit
- Run
npm run lintandnpm test. - Cover edge cases: provider throws, provider times out, unconfigured transport, invalid recipient.
- Include the full
npm testoutput and notes in the PR.
feat(notifications): implement real email transports with fail-fast guards
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the TalentTrust community on Discord for questions, reviews, and faster merges: https://discord.gg/WqnGpcPx
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Preserve upstream error details so retry classification can branch on status" labels: type:enhancement, area:http-client, stack:nodejs, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
UpstreamHttpClient in src/dependencies/upstreamHttpClient.ts catches axios errors and rethrows a generic DependencyError, discarding the original status code and response body. The isRetryable callback then receives only the wrapped error, so it cannot distinguish a retryable 429/503 from a non-retryable 400/404 β retries are effectively all-or-nothing, and operators get an opaque message with no upstream context. This issue threads the original error through so retry logic and diagnostics can be precise.
- Repository scope: Talenttrust/Talenttrust-Backend only.
- Carry the upstream status code and (redacted) response detail on the thrown
DependencyError(e.g.statusCode,originalError) without leaking secrets. - Pass enough context to the
isRetryablepredicate that it can branch on status class (retry 408/425/429/5xx, do not retry 4xx others); keep the predicate injectable. - Redact sensitive headers/bodies before logging via
src/redact.ts/ the structured logger. - Preserve the public method signatures consumed by callers of the client.
- Fork the repo and create a branch
git checkout -b enhancement/upstream-client-error-context- Implement changes
- Write code in:
src/dependencies/upstreamHttpClient.ts. - Write comprehensive tests in: create
src/dependencies/upstreamHttpClient.test.tsβ assert status is preserved, retryable vs non-retryable branches, and that secrets are redacted. - Add documentation: note the retry-classification contract in
docs/backend. - Add TSDoc to the error-wrapping path.
- Validate security: no secret header/body leaks into the error or logs.
- Write code in:
- Test and commit
- Run
npm run lintandnpm test. - Cover edge cases: 429 retryable, 400 non-retryable, network error, response with sensitive body.
- Include the full
npm testoutput and notes in the PR.
fix(http-client): preserve upstream status for accurate retry classification
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the TalentTrust community on Discord for questions, reviews, and faster merges: https://discord.gg/WqnGpcPx
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Fail fast on an empty CORS allowlist in non-production environments" labels: type:security, area:cors, stack:nodejs, stack:typescript, stack:express, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
In src/config/security.ts, parseAllowedOrigins() can return an empty array (emitting only a console.warn), and the exported corsConfig is then built from that empty list. The result is a confusing failure mode: every cross-origin request β including local dev from localhost:3000 β is silently rejected with a CORS error, and the only signal is a warning developers routinely miss. The validation is also run twice (once in parseAllowedOrigins, again in createCorsConfig). This issue replaces the silent-warn path with a deliberate, visible decision.
- Repository scope: Talenttrust/Talenttrust-Backend only.
- Decide and implement explicit behaviour for an empty allowlist: fail fast at startup with a clear error outside production, or fall back to a documented safe default for local dev β but never silently reject all origins with only a
console.warn. - Route the message through the structured logger (
src/logger.ts) at an appropriate level, notconsole.warn. - Remove the redundant double validation between
parseAllowedOriginsandcreateCorsConfig. - Keep production behaviour strict: production must still require an explicit allowlist.
- Fork the repo and create a branch
git checkout -b security/cors-empty-allowlist-failfast- Implement changes
- Write code in:
src/config/security.ts. - Write comprehensive tests in: create
src/config/security.test.tsβ assert empty allowlist behaviour per environment and that valid origins are accepted. - Add documentation: document
CORS_ALLOWED_ORIGINSbehaviour indocs/configuration.md. - Add TSDoc to the origin parsing/validation helpers.
- Validate security: production cannot start with an empty/implicitly-permissive allowlist.
- Write code in:
- Test and commit
- Run
npm run lintandnpm test. - Cover edge cases: empty in dev, empty in prod, valid list, malformed origin.
- Include the full
npm testoutput and a security notes section in the PR.
fix(cors): fail fast on empty allowlist instead of silently rejecting
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the TalentTrust community on Discord for questions, reviews, and faster merges: https://discord.gg/WqnGpcPx
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Make poison-message removal in the webhook DLQ atomic to respect the replay cap" labels: type:enhancement, area:dlq, stack:nodejs, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
incrementReplayAttempts in src/queue/webhook-dlq.ts increments an entry's attempt count and, when the max is exceeded, calls this.deleteEntry(id) to drop the poison message β but the read/increment/delete sequence is not wrapped in a single transaction. A crash or concurrent replay between the increment and the delete can leave the entry in place, so it is picked up and incremented again on restart, exceeding the intended max-replay cap and allowing a poison message to keep failing forever. This issue makes the increment-and-drop atomic.
- Repository scope: Talenttrust/Talenttrust-Backend only.
- Wrap the read-modify-(delete | persist) of a DLQ entry in a single SQLite transaction in
src/queue/webhook-dlq.tsso the cap and the drop are committed together. - Ensure the poison-drop path and the metric increment (
incrementDLQMetric('drop_poison')) cannot diverge from the persisted state. - Keep the public
incrementReplayAttemptsresult shape ({ success, attempts, maxExceeded }) and the existing singleton accessor behaviour. - Make the operation idempotent under restart: a re-run must not double-count attempts.
- Fork the repo and create a branch
git checkout -b enhancement/dlq-poison-drop-transactional- Implement changes
- Write code in:
src/queue/webhook-dlq.ts. - Write comprehensive tests in: create
src/queue/webhook-dlq.test.tsβ assert the increment and drop commit atomically and that a simulated mid-operation failure does not exceed the cap. - Add documentation: note the cap/transaction guarantee in
docs/WEBHOOK-DLQ.md. - Add TSDoc to
incrementReplayAttempts. - Validate: poison messages cannot exceed the configured replay cap.
- Write code in:
- Test and commit
- Run
npm run lintandnpm test. - Cover edge cases: attempt just below cap, at cap, simulated crash before delete, concurrent replay.
- Include the full
npm testoutput and notes in the PR.
fix(dlq): make poison-message drop atomic with the replay-cap update
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the TalentTrust community on Discord for questions, reviews, and faster merges: https://discord.gg/WqnGpcPx
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Clamp retry-policy backoff multiplier overrides to a safe upper bound" labels: type:enhancement, area:queue, stack:nodejs, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
loadRetryPolicyOverrides in src/queue/retry-policy.ts parses per-job-type backoff overrides from environment variables and accepts any parsedMultiplier > 0 with no upper bound, and does not guard against incoherent combinations (e.g. a fixed backoff that still carries a multiplier). A misconfigured or hostile RETRY_POLICY_*_MULTIPLIER=100 produces an exponential-backoff explosion, pushing retry delays to absurd values and effectively stalling a job type. This issue clamps and validates override values.
- Repository scope: Talenttrust/Talenttrust-Backend only.
- Enforce a sane
[min, max]range on the parsed multiplier (and on base/max delay) insrc/queue/retry-policy.ts; clamp out-of-range values and log a warning rather than silently accepting them. - Reject/ignore incoherent combinations (a
multiplieron afixedbackoff) so the resulting policy is internally consistent. - Keep the merge with built-in defaults and the existing override precedence intact; coordinate with
MAX_RETRY_ATTEMPTSso total attempts remain bounded. - Surface validation through the structured logger; do not throw in the hot path unless boot-time validation is the established pattern.
- Fork the repo and create a branch
git checkout -b enhancement/retry-policy-multiplier-clamp- Implement changes
- Write code in:
src/queue/retry-policy.ts. - Write comprehensive tests in: create
src/queue/retry-policy.test.tsβ assert clamping of an out-of-range multiplier, rejection of fixed+multiplier, and that valid overrides pass through. - Add documentation: document the override env vars and bounds in
docs/configuration.md. - Add TSDoc to
loadRetryPolicyOverrides. - Validate: no env value can produce an unbounded backoff explosion.
- Write code in:
- Test and commit
- Run
npm run lintandnpm test. - Cover edge cases: multiplier above max, multiplier at boundary, fixed+multiplier, NaN/negative input.
- Include the full
npm testoutput and notes in the PR.
fix(queue): clamp and validate retry-policy backoff overrides
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the TalentTrust community on Discord for questions, reviews, and faster merges: https://discord.gg/WqnGpcPx
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Tighten the notification email validator against header injection and add tests" labels: type:test, area:notifications, stack:nodejs, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
isValidEmail in src/services/notification.service.ts rejects CR/LF but otherwise uses the loose ^[^\s@]+@[^\s@]+\.[^\s@]+$ pattern, so addresses containing quotes, backslashes, and other characters that SMTP can misinterpret pass validation, and the helper has no dedicated tests. Since validated addresses flow into the (soon real) email transport, a permissive validator is both a correctness and an injection concern. This issue tightens the rule and locks the behaviour down with tests.
- Repository scope: Talenttrust/Talenttrust-Backend only.
- Strengthen
isValidEmailto reject control characters, comma/semicolon-separated multi-recipients, and quoting/backslash forms that enable header or recipient injection, while still accepting normal RFC-shaped addresses. - Keep the CR/LF rejection and the boolean contract; do not change the method signature.
- Ensure the validator is applied before any dispatch in the notification path.
- Keep behaviour deterministic and documented so the email transport work can rely on it.
- Fork the repo and create a branch
git checkout -b test/notification-email-validator-hardening- Implement changes
- Write code in:
src/services/notification.service.ts. - Write comprehensive tests in: create
src/services/notification.email-validation.test.tsβ assert rejection of CR/LF, multi-recipient, quoted/backslash forms, and acceptance of valid addresses. - Add documentation: note the validation rules in
docs/email-notifications.md. - Add TSDoc to
isValidEmail. - Validate security: no header/recipient-injection form passes the validator.
- Write code in:
- Test and commit
- Run
npm run lintandnpm run test:ci. - Cover edge cases: embedded newline,
a@b,c@d,"x"@y.com, validuser@example.com, missing TLD. - Include the full
npm testoutput and a security notes section in the PR.
test(notifications): harden and cover the email validator
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the TalentTrust community on Discord for questions, reviews, and faster merges: https://discord.gg/WqnGpcPx
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add type-safe handling and tests for the request sanitize middleware" labels: type:test, area:middleware, stack:nodejs, stack:typescript, stack:express, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
The sanitize middleware in src/middleware/sanitize.ts reassigns req.body, req.query, and req.params from sanitizeObject(...), which returns any, so type information for Express's query/params is lost and downstream handlers can silently receive arrays/objects where strings are expected. The middleware also re-sanitizes on every invocation with no test coverage of its recursion, prototype-safety, or idempotency. This issue restores type safety and pins behaviour with tests.
- Repository scope: Talenttrust/Talenttrust-Backend only.
- Type
sanitizeObjectand the reassignments soreq.query/req.paramsretain their expected shapes, and guard against prototype-pollution keys (__proto__,constructor,prototype) during recursion. - Keep the middleware idempotent (running it twice yields the same result) and non-mutating of nested input it does not own where practical.
- Preserve the existing sanitization semantics for strings/nested objects/arrays.
- Do not regress request handling for routes already relying on sanitized input.
- Fork the repo and create a branch
git checkout -b test/sanitize-middleware-typesafe- Implement changes
- Write code in:
src/middleware/sanitize.ts. - Write comprehensive tests in: create
src/middleware/sanitize.test.tsβ assert nested sanitization, prototype-pollution rejection, idempotency, and preserved types for query/params. - Add documentation: note the sanitization contract in
docs/API.md. - Add TSDoc to
sanitizeandsanitizeObject. - Validate security: no
__proto__/constructorkey survives sanitization.
- Write code in:
- Test and commit
- Run
npm run lintandnpm run test:ci. - Cover edge cases: deeply nested object, array of objects, prototype-pollution payload, double invocation.
- Include the full
npm testoutput and a security notes section in the PR.
test(middleware): add type-safe sanitization with prototype-pollution guard
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the TalentTrust community on Discord for questions, reviews, and faster merges: https://discord.gg/WqnGpcPx
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Document the outbound notification subsystem: channels, transports, and persistence" labels: type:docs, area:notifications, stack:nodejs, stack:typescript, priority:low, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
The notification subsystem spans the orchestration in src/services/notification.service.ts, the transport abstractions in src/services/notification.transport.ts, persistence in src/repositories/notificationRepository.ts, and the types in src/types/notification.types.ts β but there is no single integrator-facing guide explaining the channels (web/email), how a transport is selected and configured, validation rules, and how web notifications are stored and queried. This issue produces that documentation so contributors can extend the subsystem safely.
- Repository scope: Talenttrust/Talenttrust-Backend only.
- Describe the supported channels, the
NotificationResultcontract, recipient validation, and how a transport (SMTP/SES/SendGrid) is selected from config. - Document the web-notification persistence path (the
notificationstable,saveWebNotification,findByUser) and the failure semantics (a persistence error must report failure, not success). - Enumerate every relevant env var and mark which are secrets handled by redaction; provide a
.env.example-aligned snippet without real values. - Cross-link from
README.md; ensure the document matches the code (no aspirational claims).
- Fork the repo and create a branch
git checkout -b docs/notification-subsystem-guide- Implement changes
- Write code in: none beyond doc-only clarifying TSDoc in the notification modules.
- Write comprehensive tests in: rely on the notification service/repository/transport tests to confirm documented behaviour.
- Add documentation: create
docs/email-notifications.md(and link it fromREADME.md). - Ensure documented channels/config match the code.
- Validate: examples reflect real request/response and config shapes.
- Test and commit
- Run
npm run lintto ensure no drift. - Cross-check documented env vars and channels against the implementation.
- Include notes in the PR confirming accuracy.
docs(notifications): document channels, transports, and persistence
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the TalentTrust community on Discord for questions, reviews, and faster merges: https://discord.gg/WqnGpcPx
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward.