Skip to content

v1.0.10

Choose a tag to compare

@rodrigoslayertech rodrigoslayertech released this 20 Oct 12:50
· 39 commits to main since this release

Focus: Security stabilization, continued: refresh-token replay held across a fleet, RBAC caches bound to generations, plaintext PostgreSQL auth refused after a downgrade, JWKS redirects pinned, project listings that never run project code, and repository URLs screened before git sees them

🛠️ PATCH version

Changes

  • Unknown identifiers cost the same as a wrong password. Users verified a miss against a decoy hash minted on each instance's first miss, so with a store built per request every unknown email cost a hash plus a verify — a silent enumeration oracle. The decoy is now a well-formed argon2id string of the current policy: no hash is computed and no password verifies.
  • Refresh-token replay detection holds across hosts. On a fleet sharing one store, a concurrent double-submit lost theft detection and a revoked family could come back. The successor and the tombstone are stored before the atomic take that elects the consumer (the loser gets Replay), families stay revoked until their newest token expires, and a refused store write leaves the presented token valid for a retry.
  • RBAC permission caches are bound to generations and a realm. A revoke plus invalidate() racing a cache fill was undone for the whole lifetime, and two resolvers on one cache served each other's lists. Lists are now cached under a global and a per-identity generation read before the database, keyed by a realm of the connection and the tables (or an explicit realm:). Database errors deny instead of throwing; cache faults answer from the database.
  • A password reset during a login stands. The login's rehash no longer overwrites it: the upgrade is compare-and-set over the hash just verified, and a login that loses the race fails.
  • PostgreSQL refuses plaintext authentication after a TLS downgrade. Under the default prefer, an on-path attacker could refuse TLS and receive the password in cleartext or as an MD5 response. After a downgrade on a non-loopback host, cleartext and MD5 are refused and SCRAM needs at least 4096 iterations; SCRAM attributes are parsed strictly and the iteration count is capped.
  • JWKS redirects cannot reach private addresses. Each hop is resolved once and its vetted addresses dialed in order within the hop timeout; a non-public hop after a public answer is refused.
  • projects list, project info and the create picker never run a project's code. They read the signature as tokens: string literals only (a computed field shows (dynamic)), exportable only as the literal true, and a missing, symlinked, oversized or unparseable signature becomes one (invalid signature: …) row. Rendered metadata is cleaned of control characters and Output markup.
  • projects import refuses a symlinked signature and a source carrying a bootgly launcher, and its consent names every path that runs the imported code (start, restart, startup, migrate, seed, schedule, tests and queues).
  • Repository URLs are screened before git sees them (projects import, its prompts and the Wizard, and kit): credentials in the URL, remote helpers (ext::, fd::), plaintext http:///git:///ftp:// and malformed URLs are refused, and every displayed URL is redacted. Transfers run under GIT_ALLOW_PROTOCOL=file:https:ssh (narrowed, never widened, by one you export), option parsing ends before caller values, and the import clones without a shell into a private random staging directory that is always removed.
  • kit never adopts a spoofed or helper remote as its releases remote, and accepts a token your own insteadOf adds.
  • Session cache keys are classified with one look (a concurrent worker could make another refuse a regular file), and the launcher tip finds the global bootgly without a shell (a fatal error on php-wasm or with shell_exec disabled).

Upgrading

Kit (installed with the installer, cloned, or created from the GitHub template) — at the kit root:

bootgly kit upgrade v1.0.10
  • Running projects keep the code they loaded: stop them first (bootgly project <Name> stop) or reload
    them after (bootgly project <Name> reload).
  • Coming from a release older than v1.0.4, lay down the agent rules once afterwards:
    bootgly kit boot --agents.
  • A kit older than v1.0.0-rc.1 has no kit upgrade yet — move it once by hand:
    git fetch --tags && git checkout refs/tags/v1.0.10 && git submodule update.

Docker:

docker pull bootgly/bootgly.kit:1.0.10

Start the new image with the same volumes (-v): what lives in a volume is kept, and a container run
without one keeps nothing. Inside the image kit upgrade refuses — the image tag is the release.

Release-specific notes:

  • PostgreSQL: a server reached without TLS under the default prefer (non-loopback) can no longer authenticate with cleartext or MD5 passwords. Enable TLS on the server, or set secure.mode => 'disable' (DB_SSLMODE=disable) to declare plaintext deliberately.
  • Refresh tokens: a fleet double-submit now ends in Replay and a re-login; a refused store write throws RuntimeException ('JWT refresh token could not be consumed.') and leaves the token valid. A Vault over an injected Redis or custom driver now needs a shared secret: (pass the contents of {path}{prefix}.secret to keep existing records). Upgrade every host: a mixed rolling deploy keeps the old race until all run 1.0.10.
  • RBAC: cache keys move to rbac:v2:{realm}:… (old keys are deleted by invalidate()); upgrade the processes that grant and revoke first, call invalidate() after the commit, and make writers and readers derive one realm (or pass realm:). Permission reads always go to the primary, an RBAC over a Transaction is never cached, and a check costs 3 cache reads on a hit.
  • Users::verify() returns null when a reset or a deletion wins the race during the login.
  • Projects: signature metadata (name, description, version, author) must be string literals to be listed, and exportable must be the literal true.
  • projects import: put credentials in a git credential helper or use SSH — URLs carrying them are refused, and git asks no username or password during the import. http://, git://, ftp:// and <helper>:: URLs are refused (clone those yourself and import the local path). A GIT_ALLOW_PROTOCOL you export narrows what Bootgly's transfers may use.

Commits

Fixes

fix — Bug fixes:

• fix(vcs): screen repository URLs and clone imports without a shell
• fix(projects): read signatures as tokens in list and info
• fix(security): refuse plaintext PostgreSQL auth after a downgrade and pin JWKS hops
• fix(api): bind the RBAC cache to generations and close the rehash race
• fix(api): keep refresh-token replay detection across hosts
• fix(wpi): classify the session cache key path with one look
• fix(cli): find the global launcher without a shell
• fix(api): verify unknown identifiers against a synthetic policy decoy

Implementations

test — Tests:

• test(wpi): time each immediate refork retry against the first backoff step

docs — Documentation:

• docs(kit): fit the agent rules back within their size budget
• docs(kit): ask every question through the agent's question tool
• docs(readme): clarify that only the core is built in

chore — Maintenance:

• chore(release): 1.0.10
• chore: open 1.1.0 development


🖼 Highlights