Skip to content

v0.5.0

Latest

Choose a tag to compare

@chamals3n4 chamals3n4 released this 12 Aug 19:52

This release fixes what was broken instead of adding features. Nothing here is
new functionality; it makes what already existed trustworthy.

If you self-host OpenATS, upgrade. Socket.IO was completely
unauthenticated, so anyone who could reach your server could read every hiring
team's chat and post messages as any user.

Security

  • Socket.IO connections now require a valid Asgardeo JWT, verified in the
    handshake before any handler runs. Previously the server accepted any
    connection with cors: "*", and the client supplied its own senderId, so an
    anonymous client could write chat messages impersonating a real user. The
    sender is now taken from the verified token.
  • Room joins are authorized, not just authenticated. join_job and
    join_candidate are checked against hiring-team membership, and the chat write
    handlers require the socket to already be in the room, so a client cannot skip
    the join and write to a job it has no access to.
  • Chat history over HTTP is gated the same way. GET /chat/job/:jobId and
    /chat/candidate/:candidateId previously returned any conversation to any
    logged-in user.
  • Dashboard broadcasts are scoped to a room. Candidate stage moves, offers
    and interviews were sent to every connected client with a bare io.emit().
  • Reconnects survive token expiry. The socket token was read once when the
    dashboard rendered; if it expired while a tab was open, realtime silently
    stopped until a page refresh.
  • The authenticated API is rate limited, keyed by user id rather than IP so
    an office behind one NAT does not share a budget. Tunable with
    RATE_LIMIT_API and RATE_LIMIT_EXPENSIVE.

Fixes

  • A partial offer edit wiped the offer's start date. Any
    PATCH /api/offers/:id that did not itself mention startDate erased it,
    after which the offer could not be sent and failed with "Missing required
    fields: startDate". Editing benefits or the reporting manager on a draft offer
    was enough to trigger it.
  • Assessment results could never show a text answer. The frontend declared
    question types that do not exist in the database (single_choice, text), so
    the branch that renders short and long answers was unreachable.
  • The interview stage-type indicator never rendered, because the type used
    by the candidate page was missing five fields the API actually returns.
  • Eleven catch blocks returned HTTP 500 while discarding the underlying error,
    making those failures uninvestigable. They now log the cause.
  • Job updates stringified salary values while creation passed numbers, which
    disagreed with the schema.

Testing

The project had 31 tests and no coverage of the things that had actually broken
in production. It now has 83.

  • Authentication is tested — expired, wrong-issuer and wrongly-signed
    tokens, the claim checks, JIT user provisioning, deactivated accounts, and the
    auth middleware. Only the JWKS fetch is faked, so signature, exp and iss
    are genuinely verified. This includes a regression test for the v0.4.0 login
    outage, where a changed Asgardeo sub broke every login.
  • The core hiring flow is tested end to end through the real HTTP API:
    apply, duplicate application, stage move, interview scheduling, and the offer
    draft-to-send path. Writing these found the offer startDate bug above.
  • Frontend tests exist for the first time, using Vitest with jsdom and
    Testing Library.
  • Coverage reporting via pnpm test:coverage.

Tooling

  • The backend has ESLint, passes with zero problems, and no-explicit-any is
    enforced as an error — all 108 uses were removed rather than suppressed.
  • The frontend's 112 lint errors are fixed, so pnpm lint at the repo root now
    passes and can become a CI gate.
  • CI gates backend lint and type-checks the backend test files, which had
    silently been checking nothing.
  • The worker validates its environment on boot, matching the API.
  • Added CHANGELOG.md.

Upgrade notes

  • No database migrations in this release.
  • New optional environment variables: RATE_LIMIT_API (default 1000) and
    RATE_LIMIT_EXPENSIVE (default 60), both per 15-minute window. The defaults
    are deliberately generous; you do not need to set them.
  • The frontend must be redeployed alongside the backend. Sockets now require
    a token, and the client fetches one from a new /api/socket-token route. An
    old frontend against a new backend gets no realtime updates.

Known issues

54 dependency advisories are open (15 high), almost all reached through
next@16.1.6. The upgrade is held until immediately before v1.0.0 so the
version bump is as fresh as possible at GA.

Full Changelog: v0.4.0...v0.5.0