Skip to content

security: encrypt credentials at rest, publish the posture page, and release 0.10.0 (Phase 3) - #324

Merged
cevheri merged 12 commits into
mainfrom
security/phase-3-credentials-at-rest
Aug 10, 2026
Merged

security: encrypt credentials at rest, publish the posture page, and release 0.10.0 (Phase 3)#324
cevheri merged 12 commits into
mainfrom
security/phase-3-credentials-at-rest

Conversation

@cevheri

@cevheri cevheri commented Aug 10, 2026

Copy link
Copy Markdown
Member

Phase 3, the last of the security maturity programme: credentials encrypted at rest, a published security posture, and the 0.10.0 release that carries all four phases.

Phases 0 (#320), 1 (#321) and 2 (#322) are merged, plus the Next.js bump (#323). None of them changed the version — this pull request does, once, for the whole programme.

What this adds

Credential encryption at rest AES-256-GCM over the six credential-bearing fields in the server-side store, installed at the storage factory's single choke point
A key that needs no configuration STORAGE_ENCRYPTION_KEY when set, otherwise derived from JWT_SECRET through HKDF-SHA256
A classification that cannot be forgotten Record<keyof DatabaseConnection, FieldClass> — a new field fails typecheck until someone classifies it
docs/SECURITY.md The posture page: 16 controls, each linked to the test that verifies it
bun run security:check A CI gate that fails when the posture page and the repository disagree, in either direction
0.10.0 App, chart 0.1.31, and the operator bundle

What is encrypted, and what is not

Six fields, all inside connections: password, connectionString, ssl.clientKey, sshTunnel.password, sshTunnel.privateKey, sshTunnel.passphrase. host, port, user and database stay readable on purpose, so an operator can still answer "which database is in this dump". ssl.caCert and ssl.clientCert are public certificates.

This is not a vault. Anyone who can read the process environment can read the credentials. It protects a leaked database file or dump, and that is the whole claim.

The browser's localStorage copy stays plaintext, by deliberate product decision. That is what lets Studio work without a master password, and it is why Phase 0's cross-site-scripting work carries the weight it does.

Operator notes — read before upgrading

Nothing is required of you. STORAGE_ENCRYPTION_KEY is optional and stays optional; all 27 distribution channels depend on that. With nothing set, the key derives from JWT_SECRET, which the auth bootstrap already generates and persists on first run.

Rotating JWT_SECRET invalidates every stored credential when no explicit STORAGE_ENCRYPTION_KEY is set. If you rotate a JWT secret for an unrelated reason, stored passwords stop opening. Set STORAGE_ENCRYPTION_KEY first if you expect to rotate.

A credential that cannot be decrypted is omitted, not fatal. The record survives, one warning is logged per read with a count, and the user sees an empty password box to retype. Throwing instead would take query history, saved queries and charts down with the passwords; dropping the record would be worse still, because useStorageSync is a write-through cache and a dropped connection is persisted as deleted on the next push.

Migration is lazy. Reads accept plaintext and enveloped values; writes always produce enveloped values. There is no migration command, no backfill and no version column.

Only the server-side store is affectedSTORAGE_PROVIDER=sqlite|postgres. STORAGE_PROVIDER=local has no server store at all.

The plan was wrong six times, and that is worth saying

This phase was executed from a written plan whose test code had never been run. Implementers stopped and asked rather than making the tests pass, and found six defects in it. Two mattered:

A tamper test that failed on correct code. It flipped the last base64url character of the sealed body. For a 25-byte body that character encodes only the low 2 bits of the final byte, so it can only be A, Q, g or w — and A to B decodes to an identical byte string. Enumerating all 256 final-byte values: 64 of 256, exactly 25.0 percent, change nothing, matching the 24.2 percent measured over 5000 runs. It would have reddened CI intermittently while looking like a real tamper-detection failure. Replaced with two deterministic byte-level tests — one flipping a ciphertext byte, one a tag byte, because those are different properties and neither stands in for the other.

The plan contradicted its own design decision. Decision D4 says a malformed v1: value is undecryptable, never plaintext. The task text mandated the opposite for a value with the wrong segment count, and shipped a test asserting it. So v1:abc and v2:abc were returned verbatim as passwords. The rule is now: if the first colon-segment matches ^v\d+$ the value is an envelope claim, and any malformation of that claim is undecryptable.

The other four: an off-by-one that stripped a 10-character prefix with slice(11) and silently dropped three fields from a derived list; a unit test asserting exact logger.warn call counts, which fails deterministically because bun run test runs unit, api and integration in one process and fire-and-forget warnings from unrelated route tests land inside the spy window; and three hand-counted numbers that were simply wrong.

A programme about claim accuracy should say when its own plan was inaccurate.

What review then found in the shipped code

Two independent reviewers went over this branch after it was pushed. Eleven findings between them; every one was valid, and two were real holes in the guarantee this pull request makes.

The backup claim was false for the default deployment. The documentation said encryption protects "a stolen database file, dump, backup or volume snapshot". But getDataDir() is defined as path.dirname(STORAGE_SQLITE_PATH), so auth-bootstrap.json — which holds the generated JWT_SECRET the key derives from when nothing is configured — sits beside the store by construction, and the chart mounts /app/data as one volume. A snapshot therefore contained the ciphertext and its key. Narrowed to what holds: the database file or dump on its own is protected always; a backup or snapshot only once STORAGE_ENCRYPTION_KEY comes from outside the volume. Two new tests pin resolveBootstrapPath()'s directory to getDataDir(), so if a future change separates them the tests fail and the claim can be widened deliberately rather than by accident.

The write path did not encrypt everything it claimed to. sealIfPlaintext encrypted only values classified plaintext, and passed an undecryptable one through unchanged. So a user whose password happens to match ^v\d+:v2:hunter2, say — had it written to the store in the clear, while the function directly below it claimed "Never returns a connection with a plaintext credential in it." Fixed to pass through only what actually opens and encrypt everything else, which also repairs that user's password: it now round-trips instead of being classified undecryptable and dropped.

The posture page's own guard had the shape this phase exists to eliminate — three times. Two controls named a script and a policy document rather than a test in their Verified by column, and security:check accepted both. Requiring a test there fixed those two but not the third: control 3.1, "credentials are encrypted at rest", was verified by a test containing zero references to getStorageProvider — it wraps the decorator directly, so it stays green even if the factory handed out a bare provider. Requiring a test turned out to be necessary and not sufficient; the test also has to exercise the claim beside it. The factory guard is now linked from that row.

The remaining findings: security:check accepted a duplicate control ID; the audit row contradicted the exception documented directly beneath it; both chart copies marked a four-phase security release as containsSecurityUpdates: "false"; a Helm install example referenced libredb-studio/libredb-studio when the repo alias this branch documents is libredb, so the copy-paste command failed (introduced here — origin/main has no such line); a unit test snapshotted only JWT_SECRET, so an ambient STORAGE_ENCRYPTION_KEY would silently defeat its own rotation cases while its sibling file snapshotted both; and the type-aware ESLint layer covered src/app/api and src/lib/db but not src/lib/storage, where every file this phase added lives — including a decorator that wraps async provider methods, which is exactly what those floating-promise rules are for.

One suggestion was declined on inspection rather than implemented: a request to document STORAGE_ENCRYPTION_KEY's 32-character minimum in .env.example, which already states it.

Verification

Every gate green at dea4f0b, including the two this phase deliberately carried red until the end:

format · lint · typecheck · knip · test (23/23 groups) · build
coverage      100.00% (30021/30021 lines)
build:lib     + attw
readme:check  OK
security:check  OK: 16 controls documented, 14 security tests accounted for
chart:check   OK: chart 0.1.31 / appVersion 0.10.0 in sync with package.json 0.10.0
e2e           39 passed, 2 flaky (both retried green), 0 policy violations
operator      make bundle, idempotent

The encryption was verified against a real store, not only against a fake

The threat suite proves the decorator seals before handing anything to a provider, but it does so through an in-memory capture provider — so it demonstrates the boundary, not the file on disk. The end-to-end suite does not close that gap either: it runs with STORAGE_PROVIDER unset, which returns null before any provider is constructed, so the decorator never executes during it.

That gap is now closed in CI, not only by hand. tests/integration/storage/sqlite-credential-encryption.test.ts drives a canary password through withCredentialEncryption(new SQLiteStorageProvider(...)) against a real file, checkpoints the WAL, asserts against the raw bytes on disk, then simulates a key rotation on that same file. It runs under real node through a bundled harness, following the existing sqlite-node-harness.ts pattern, because better-sqlite3 cannot load under bun — which is why this could not live in tests/security/. Disabling the seal makes it fail; restoring it makes it pass.

The same round trip was also driven by hand through the real HTTP API before that test existed:

Check Result
Plaintext canary anywhere in store.db or its WAL 0 occurrences
Envelope present v1:F2gpBx_6sERd6sES:waeMRgr69ZS…
host still readable in the file yes, by design
Password read back through the API correct plaintext
Same store, rotated key HTTP 200, record intact, password absent — not null, not the raw envelope

That last row is the one worth reading twice. A rotated key does not throw, does not drop the record, and does not hand the envelope to a driver as a password. The operator sees an empty password box; query history, saved queries and charts are untouched.

Each task carried a sabotage step: the control is not delivered until something fails when it is broken. Three results are worth naming, and I re-ran each rather than accepting the report.

The choke point. getStorageProvider has exactly three call sites, all under src/app/api/storage/; the factory constructs exactly two providers and both are wrapped; STORAGE_PROVIDER=local returns null before any provider exists. Unwrapping the factory reddens a wiring test, so the guard is mechanical rather than a comment.

The compile-time classification. Adding vaultToken?: string to DatabaseConnection produces error TS2741: Property 'vaultToken' is missing ... but required in type 'Record<keyof DatabaseConnection, FieldClass>', pointing at the classification map. A new field is opt out, not opt in.

The posture guard fails in both directions. Renaming one security test produced two errors: the control row now links a file that does not exist, and the renamed test is verified by no control row. A one-directional check would have caught only the first, and the second is the one that catches a page drifting from reality.

Deferred

docs/BACKLOG.md carries this phase's residuals rather than leaving them in review transcripts. The security:check script sits outside the coverage universe by design — scripts/merge-lcov.mjs filters to src/ — so it is unit-tested on the readme-check precedent instead.

Per D4: once a stored value's first colon-separated segment matches a
version tag (^v\d+$), it is classified as an envelope claim. Every way
that claim can be malformed - wrong segment count, unrecognised version,
bad IV, failed authentication - resolves to undecryptable, never to
plaintext. Only a value whose first segment is not version-tag-shaped is
plaintext. Per D3, the accepted cost is that an existing plaintext
password shaped exactly ^v\d+: becomes unreadable after this ships.

Copilot AI 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.

Pull request overview

Adds server-side credential encryption, publishes a verified security posture, and prepares release 0.10.0.

Changes:

  • Encrypts six credential fields using AES-256-GCM.
  • Adds security posture documentation and CI drift checks.
  • Updates application, Helm chart, and operator versions.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
.env.example Documents the encryption key.
.github/workflows/ci.yml Adds the posture CI gate.
SECURITY.md Updates support and storage security guidance.
charts/libredb-studio/Chart.yaml Bumps chart and application versions.
charts/libredb-studio/README.md Documents Helm encryption configuration.
docs/BACKLOG.md Records encryption residual risks.
docs/SECURITY.md Adds the security posture inventory.
docs/STORAGE.md Documents encryption behavior and recovery.
operator/bundle/manifests/libredb-studio-operator.clusterserviceversion.yaml Updates the operator bundle version.
operator/config/manager/kustomization.yaml Updates the controller image tag.
operator/helm-charts/libredb-studio/Chart.yaml Updates the operator-packaged chart.
operator/helm-charts/libredb-studio/README.md Documents operator Helm configuration.
package.json Bumps to 0.10.0 and adds the check command.
scripts/security-check.mjs Implements posture drift validation.
src/lib/storage/connection-secrets.ts Classifies and transforms credential fields.
src/lib/storage/encryption.ts Implements encryption envelopes and key derivation.
src/lib/storage/encrypting-provider.ts Decorates storage providers with encryption.
src/lib/storage/factory.ts Installs encryption at the provider factory.
tests/isolated/factory-singleton.test.ts Verifies factory encryption wiring.
tests/security/credential-at-rest.test.ts Tests the at-rest threat boundary.
tests/unit/lib/storage/connection-secrets.test.ts Tests field classification and transforms.
tests/unit/lib/storage/encrypting-provider.test.ts Tests decorator behavior and warnings.
tests/unit/lib/storage/encryption.test.ts Tests encryption, tampering, and keys.
tests/unit/lib/storage/providers/postgres.test.ts Verifies PostgreSQL serialization.
tests/unit/lib/storage/providers/sqlite.test.ts Verifies SQLite serialization.
tests/unit/security-check.test.ts Tests posture-check failure modes.
Suppressed comments (1)

docs/SECURITY.md:42

  • Control 3.1 omits the factory choke point and its wiring test. credential-at-rest.test.ts wraps the capture provider directly, so it remains green if getStorageProvider() later returns a bare provider—the exact regression the PR says factory-singleton.test.ts guards mechanically. Link both factory.ts and that isolated test so the published posture covers installation of the decorator, not only its behavior in isolation.
| 3.1 | Credentials are encrypted at rest in the server-side store | Implemented | [`src/lib/storage/encryption.ts`](../src/lib/storage/encryption.ts), [`src/lib/storage/connection-secrets.ts`](../src/lib/storage/connection-secrets.ts), [`src/lib/storage/encrypting-provider.ts`](../src/lib/storage/encrypting-provider.ts) | [`tests/security/credential-at-rest.test.ts`](../tests/security/credential-at-rest.test.ts) |

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/lib/storage/connection-secrets.ts Outdated
Comment thread charts/libredb-studio/Chart.yaml Outdated
Comment thread docs/SECURITY.md Outdated
Comment thread scripts/security-check.mjs
Comment thread docs/SECURITY.md Outdated
Comment thread operator/helm-charts/libredb-studio/Chart.yaml Outdated
… pass it through

sealIfPlaintext only encrypted the 'plaintext' outcome of readSecret and returned
the other two outcomes verbatim. That is correct for 'decrypted' (already-sealed,
skip re-encryption) but wrong for 'undecryptable': a real password shaped like an
envelope claim, e.g. literally v2:hunter2, classifies as undecryptable and was
being written to SQLite/PostgreSQL in the clear - exactly what this phase
promises cannot happen.

Fix: only skip encryption for a value PROVEN to open under the current key
(kind === decrypted). Plaintext and anything merely envelope-shaped, including a
corrupted three-segment claim, are both sealed. The write path is fed from
localStorage through useStorageSync, which holds plaintext by design, so a value
reaching here is overwhelmingly a real credential rather than corruption in
transit.

Adds regression tests for both shapes (two-segment and three-segment lookalikes)
and corrects the encryptConnections and sealIfPlaintext doc comments to state
what is now actually true.
…licate control ids

isExecuted treated every non-test path as executed, so a control could name the
checker script or the policy document itself as its own Verified by link and
the drift guard would accept it - 0.4 and 0.5 both did exactly this. Added
requireTest to isExecuted and apply it only to the Verified by column (the
Enforced in column legitimately links source files); a non-test target there
now fails as 'not a test'.

Added real verifier tests for both claims: tests/unit/security-check.test.ts
gained a CLI-level case that runs the checker against the actual repository
(the real proof behind 0.4, 'the security policy states only what the code
does'), and tests/security/vulnerability-disclosure.test.ts reads the root
SECURITY.md to verify 0.5's reporting-channel and response-time claim. Both
control rows now link these instead of a non-test path.

Also detects a duplicate control id: the exact-control-set check only compared
presence, so a page carrying all 16 ids plus a second 0.1 produced neither a
missing nor an extra id and passed. Added an explicit duplicate check plus a
sabotage test.

Finally, scoped control 3.2's claim to authoritative (server-generated) audit
events: the row claimed every audit event reaches stdout, contradicting the
note directly below it that documents POST /api/admin/audit deliberately
writing only to the ring buffer, because its body is fully client-supplied.
Both chart copies carried artifacthub.io/containsSecurityUpdates: "false" on a
release that is four phases of security work. Set to "true" in the source
chart and re-ran chart:bump to refresh the operator's vendored copy.

Copilot AI 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.

Pull request overview

Copilot reviewed 27 out of 27 changed files in this pull request and generated 1 comment.

Suppressed comments (3)

operator/helm-charts/libredb-studio/README.md:256

  • This vendored README has the same invalid chart reference: the configured repository alias is libredb, not libredb-studio, so this install command cannot resolve the chart. Keep the generated copy aligned with the source README's corrected command.
helm install libredb-studio libredb-studio/libredb-studio \

tests/unit/lib/storage/encrypting-provider.test.ts:55

  • These warning tests assume encryption falls back to JWT_SECRET, but the setup leaves any ambient STORAGE_ENCRYPTION_KEY in place. In such an environment changing JWT_SECRET at line 115 does not change the encryption key, so the expected undecryptable warning never occurs. Snapshot, clear, and restore the explicit key as the neighboring encryption suites do.
beforeEach(() => {
  snapshot.JWT_SECRET = process.env.JWT_SECRET;
  process.env.JWT_SECRET = "encrypting-provider-test-jwt-secret-32";
  resetStorageEncryptionKey();

charts/libredb-studio/README.md:256

  • This command uses the repository alias libredb-studio, but this README tells users to add the repository as libredb (line 16), and every other install example uses libredb/libredb-studio. As written, the copy-paste command fails with an unknown repository; use the documented alias.
helm install libredb-studio libredb-studio/libredb-studio \

Comment thread src/lib/storage/encryption.ts
…ually holds

docs/SECURITY.md and docs/STORAGE.md both claimed credential encryption at rest
protects a stolen backup or volume snapshot. For STORAGE_PROVIDER=sqlite with
no STORAGE_ENCRYPTION_KEY set, that is false: the fallback key derives from
JWT_SECRET, persisted in auth-bootstrap.json beside the SQLite file (both
resolve through the same getDataDir(), and the Helm chart mounts that one
directory as a single /app/data volume), so a snapshot of it carries the
ciphertext and the key that opens it side by side.

Narrowed both claims to a stolen database file or dump on its own, and stated
plainly what closes the gap: set STORAGE_ENCRYPTION_KEY from outside the
mounted volume (a Kubernetes Secret, an environment variable). postgres
deployments do not share this exposure by default, since the key material
lives in the app's own filesystem, a volume separate from the database being
backed up. STORAGE_ENCRYPTION_KEY stays optional; the zero-config default is
unchanged.

Also corrected the same claim in the encryption.ts module docstring, and added
the same actionable note to the chart README's key-separation section (ran
chart:bump afterward to refresh the operator's vendored copy).

Pinned the underlying fact rather than just the prose: two new tests in
tests/security/credential-at-rest.test.ts assert that the auth-bootstrap file
and the SQLite store resolve to the same directory, so a future change that
separates them fails the test - the signal that the docs can be widened back.
…s, and test isolation

Second-reviewer batch on #324, all four verified independently:

1. Control 3.1's Verified by named only tests/security/credential-at-rest.test.ts,
   which constructs withCredentialEncryption(inner) directly and cannot fail if
   the factory ever stopped wrapping the provider it hands out - the phase's
   central choke point. Linked the existing tests/isolated/factory-singleton.test.ts,
   which does guard the factory, and src/lib/storage/factory.ts in Enforced in.
   Chose linking the existing test over writing a new one: it already exercises
   exactly this claim and a second copy would just be a second thing to drift.

2. charts/libredb-studio/README.md told the reader to
   'helm install libredb-studio libredb-studio/libredb-studio', but the repo
   alias documented two lines above (and used by every other example in the
   file) is 'libredb', not 'libredb-studio' - the command as written does not
   resolve. Fixed to match every other example in the file. Verified with git
   blame: introduced on this branch, absent from origin/main. Edited the source
   chart, then ran chart:bump to refresh the operator's vendored copy.

3. tests/unit/lib/storage/encrypting-provider.test.ts snapshotted and restored
   only JWT_SECRET; an ambient STORAGE_ENCRYPTION_KEY (a developer's
   .env.local) takes precedence in inputKeyMaterial(), so rotating JWT_SECRET
   in that case would not change the derived key and the undecryptable-warning
   tests would stop testing what they claim to. Matched the sibling file's
   isolation (tests/security/credential-at-rest.test.ts already snapshots
   both). No new test added: the key-precedence behavior itself is already
   covered correctly in tests/unit/lib/storage/encryption.test.ts - this was
   purely a test-isolation gap, not a missing behavioral test.

4. eslint.config.mjs's type-aware layer (no-floating-promises,
   no-misused-promises, await-thenable) was scoped to src/app/api/** and
   src/lib/db/**, never reaching src/lib/storage/**, where this phase's
   async-heavy credential-encryption decorator lives. Added
   src/lib/storage/**/*.ts to the scope. Verified both directions: a floating
   promise injected into encrypting-provider.ts is now caught (confirmed, then
   reverted), and src/lib/db/providers/document/couchbase/introspect.ts:253:23: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/components/monitoring/tabs/PerformanceTab.tsx:21:59: warning react(no-object-type-as-default-prop): Do not use an array literal as default prop value. Use a stable reference instead. help: Default values are re-created on every render and break referential equality, causing unnecessary re-renders. Move the value out of the component or memoize it.
src/components/monitoring/tabs/PerformanceTab.tsx:265:17: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/monitoring/tabs/OverviewTab.tsx:21:56: warning react(no-object-type-as-default-prop): Do not use an array literal as default prop value. Use a stable reference instead. help: Default values are re-created on every render and break referential equality, causing unnecessary re-renders. Move the value out of the component or memoize it.
src/components/monitoring/tabs/OverviewTab.tsx:247:17: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
tests/helpers/mock-fetch.ts:33:15: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/components/monitoring/tabs/SessionsTab.tsx:267:17: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/monitoring/tabs/SessionsTab.tsx:284:25: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/lib/storage/providers/postgres.ts:119:11: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/db/utils/pool-manager.ts:168:14: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/db/utils/pool-manager.ts:180:7: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/components/monitoring/tabs/TablesTab.tsx:246:17: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/monitoring/tabs/TablesTab.tsx:263:25: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/monitoring/tabs/StorageTab.tsx:279:17: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/monitoring/tabs/StorageTab.tsx:296:20: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/monitoring/tabs/QueriesTab.tsx:229:17: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/monitoring/tabs/QueriesTab.tsx:246:25: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/hooks/use-ai-chat.ts:143:49: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/components/QuerySafetyDialog.tsx:183:35: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/components/QuerySafetyDialog.tsx:270:23: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/ResultsGrid.tsx:467:21: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/DataImportModal.tsx:437:27: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/DataImportModal.tsx:440:29: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/QueryEditor.tsx:112:16: warning react(no-object-type-as-default-prop): Do not use an array literal as default prop value. Use a stable reference instead. help: Default values are re-created on every render and break referential equality, causing unnecessary re-renders. Move the value out of the component or memoize it.
tests/components/QuerySafetyDialog.test.tsx:275:7: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/components/admin/tabs/OperationsTab.tsx:352:29: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/admin/tabs/OperationsTab.tsx:456:29: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/AIAutopilotPanel.tsx:98:33: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/components/CreateTableModal.tsx:161:19: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/NL2SQLPanel.tsx:135:35: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/components/NL2SQLPanel.tsx:208:16: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/admin/tabs/OverviewTab.tsx:658:18: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/DatabaseDocs.tsx:81:33: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/components/DatabaseDocs.tsx:131:15: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/DatabaseDocs.tsx:137:15: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/DatabaseDocs.tsx:143:15: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/DatabaseDocs.tsx:150:15: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/DatabaseDocs.tsx:157:14: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/DatabaseDocs.tsx:162:19: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/lib/db/factory.ts:169:9: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/db/factory.ts:176:9: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/components/PivotTable.tsx:259:21: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/SchemaDiff.tsx:415:17: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/SchemaDiff.tsx:427:29: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/SchemaDiff.tsx:449:17: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/SchemaDiff.tsx:459:25: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/SchemaDiff.tsx:477:17: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/SchemaDiff.tsx:486:25: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/VisualExplain.tsx:331:23: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/VisualExplain.tsx:426:27: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/VisualExplain.tsx:510:33: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/components/VisualExplain.tsx:540:18: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/VisualExplain.tsx:580:15: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/VisualExplain.tsx:586:15: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/VisualExplain.tsx:592:16: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/VisualExplain.tsx:600:16: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/VisualExplain.tsx:606:28: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/VisualExplain.tsx:609:14: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/VisualExplain.tsx:625:19: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/VisualExplain.tsx:632:17: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/VisualExplain.tsx:864:21: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/VisualExplain.tsx:928:22: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/DataCharts.tsx:228:12: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/DataCharts.tsx:961:27: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
tests/security/rate-limit-routes.test.ts:62:17: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/security/rate-limit-routes.test.ts:78:9: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/security/rate-limit-routes.test.ts:115:9: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/security/rate-limit-routes.test.ts:130:9: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/security/rate-limit-routes.test.ts:153:17: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/security/rate-limit-routes.test.ts:197:9: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/security/rate-limit-routes.test.ts:201:26: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/security/rate-limit-routes.test.ts:214:21: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/security/login-enumeration.test.ts:124:15: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/security/login-enumeration.test.ts:136:15: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/security/login-enumeration.test.ts:144:7: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/security/login-enumeration.test.ts:150:15: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/security/login-enumeration.test.ts:157:19: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/security/login-enumeration.test.ts:197:7: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/security/login-enumeration.test.ts:213:7: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/security/login-enumeration.test.ts:248:36: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/api/storage/storage-routes.test.ts:307:19: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/api/storage/storage-routes.test.ts:322:19: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/unit/llm/ollama-provider.test.ts:45:29: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/unit/llm/custom-provider.test.ts:52:29: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/unit/llm/gemini-provider.test.ts:55:29: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/helpers/mock-next.ts:74:29: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/hooks/use-inline-editing.ts:152:7: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/db/providers/keyvalue/redis.ts:330:36: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/db/providers/keyvalue/redis.ts:344:28: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/unit/llm/openai-provider.test.ts:52:29: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/hooks/use-inline-editing.test.ts:602:7: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/components/admin/tabs/AuditTab.tsx:163:25: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/admin/tabs/AuditTab.tsx:308:27: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/lib/db/providers/document/mongodb.ts:466:24: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/db/providers/document/mongodb.ts:471:23: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/db/providers/document/mongodb.ts:478:26: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/db/providers/document/mongodb.ts:482:25: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/db/providers/document/mongodb.ts:663:17: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/db/providers/document/mongodb.ts:685:19: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/db/providers/document/mongodb.ts:748:27: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/db/providers/document/mongodb.ts:895:27: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/db/providers/document/mongodb.ts:928:32: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/db/providers/document/mongodb.ts:931:25: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/components/DataProfiler.tsx:151:35: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/components/DataProfiler.tsx:310:35: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
tests/api/auth/oidc-login.test.ts:108:9: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/app/api/db/multi-query/route.ts:128:23: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/db/providers/sql/mysql.ts:563:31: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/db/providers/sql/mysql.ts:568:26: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/db/providers/sql/mysql.ts:573:29: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/llm/utils/retry.ts:62:14: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/llm/utils/retry.ts:82:7: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/components/ui/toggle-group.tsx:44:36: warning react(jsx-no-constructed-context-values): The Context `value` prop should not be constructed. help: Wrap the `value` prop in useMemo() or useCallback(), or use a constant value to prevent unnecessary re-renders. Alternatively, move the value outside the render function if it doesn't depend on props or state.
tests/security/csrf-origin.test.ts:175:19: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/security/route-auth.test.ts:143:7: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/unit/instrumentation.test.ts:27:5: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/db/providers/sql/oracle.ts:737:19: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/unit/llm/streaming.test.ts:12:29: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/lib/llm/utils/streaming.ts:168:37: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/app/api/db/profile/route.ts:116:26: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/components/ui/carousel.tsx:107:7: warning react(jsx-no-constructed-context-values): The Context `value` prop should not be constructed. help: Wrap the `value` prop in useMemo() or useCallback(), or use a constant value to prevent unnecessary re-renders. Alternatively, move the value outside the render function if it doesn't depend on props or state.
e2e/functional-smoke.spec.ts:69:7: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/api/auth/login.test.ts:158:19: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/api/auth/login.test.ts:316:7: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
src/components/ui/field.tsx:194:67: warning react(no-array-index-key): Usage of Array index in keys is not allowed help: Use a unique data-dependent key to avoid unnecessary rerenders
src/components/ui/form.tsx:37:32: warning react(jsx-no-constructed-context-values): The Context `value` prop should not be constructed. help: Wrap the `value` prop in useMemo() or useCallback(), or use a constant value to prevent unnecessary re-renders. Alternatively, move the value outside the render function if it doesn't depend on props or state.
src/components/ui/form.tsx:76:31: warning react(jsx-no-constructed-context-values): The Context `value` prop should not be constructed. help: Wrap the `value` prop in useMemo() or useCallback(), or use a constant value to prevent unnecessary re-renders. Alternatively, move the value outside the render function if it doesn't depend on props or state.
src/components/ui/chart.tsx:48:28: warning react(jsx-no-constructed-context-values): The Context `value` prop should not be constructed. help: Wrap the `value` prop in useMemo() or useCallback(), or use a constant value to prevent unnecessary re-renders. Alternatively, move the value outside the render function if it doesn't depend on props or state.
tests/unit/lib/auth.test.ts:268:20: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.
tests/unit/lib/auth.test.ts:273:20: warning eslint(no-await-in-loop): Unexpected `await` inside a loop. help: Collect all promises into an array and use `Promise.all()` to run them in parallel, rather than awaiting each one sequentially inside the loop.

/home/cevheri/projects/libredb/libredb-studio/.remember/tmp/last-ndc.ts
  1:1  warning  Expected an assignment or function call and instead saw an expression  @typescript-eslint/no-unused-expressions

/home/cevheri/projects/libredb/libredb-studio/bin/studio.js
  159:7  warning  Unused eslint-disable directive (no problems were reported from 'no-await-in-loop')
  167:7  warning  Unused eslint-disable directive (no problems were reported from 'no-await-in-loop')

/home/cevheri/projects/libredb/libredb-studio/src/app/admin/error.tsx
  27:15  warning  Do not use `window.location.href` to navigate to internal Next.js pages. Use `redirect()` in the render phase, or `useRouter().push()` in Client Components' event handlers instead. See: https://nextjs.org/docs/messages/no-location-assign-relative-destination  @next/next/no-location-assign-relative-destination

/home/cevheri/projects/libredb/libredb-studio/src/app/api/connections/managed/route.ts
  19:17  warning  'password' is assigned a value but never used          @typescript-eslint/no-unused-vars
  19:27  warning  'connectionString' is assigned a value but never used  @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/src/app/login/login-form.tsx
  226:23  warning  Do not use `window.location.href` to navigate to internal Next.js pages. Use `redirect()` in the render phase, or `useRouter().push()` in Client Components' event handlers instead. See: https://nextjs.org/docs/messages/no-location-assign-relative-destination  @next/next/no-location-assign-relative-destination

/home/cevheri/projects/libredb/libredb-studio/src/components/QueryEditor.tsx
  146:9  warning  Error: Calling setState synchronously within an effect can trigger cascading renders

Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
* Update external systems with the latest state from React.
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.

Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).

/home/cevheri/projects/libredb/libredb-studio/src/components/QueryEditor.tsx:146:9
  144 |       const saved = localStorage.getItem("editor-line-numbers");
  145 |       if (saved !== null) {
> 146 |         setShowLineNumbers(saved === "true");
      |         ^^^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect
  147 |       }
  148 |       setLineNumbersPreferenceReady(true);
  149 |     }, []);  react-hooks/set-state-in-effect

/home/cevheri/projects/libredb/libredb-studio/src/components/QueryHistory.tsx
  49:5  warning  Error: Calling setState synchronously within an effect can trigger cascading renders

Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
* Update external systems with the latest state from React.
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.

Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).

/home/cevheri/projects/libredb/libredb-studio/src/components/QueryHistory.tsx:49:5
  47 |   // Refresh history when refreshTrigger changes (replaces key-based re-mount)
  48 |   useEffect(() => {
> 49 |     setHistory(storage.getHistory());
     |     ^^^^^^^^^^ Avoid calling setState() directly within an effect
  50 |   }, [refreshTrigger]);
  51 |
  52 |   const filteredHistory = useMemo(() => {  react-hooks/set-state-in-effect

/home/cevheri/projects/libredb/libredb-studio/src/components/ResultsGrid.tsx
  412:17  warning  Compilation Skipped: Use of incompatible library

This API returns functions which cannot be memoized without leading to stale UI. To prevent this, by default React Compiler will skip memoizing this component/hook. However, you may see issues if values from this API are passed to other components/hooks that are memoized.

/home/cevheri/projects/libredb/libredb-studio/src/components/ResultsGrid.tsx:412:17
  410 |   ]);
  411 |
> 412 |   const table = useReactTable({
      |                 ^^^^^^^^^^^^^ TanStack Table's `useReactTable()` API returns functions that cannot be memoized safely
  413 |     data: filteredRows,
  414 |     columns,
  415 |     state: {  react-hooks/incompatible-library

/home/cevheri/projects/libredb/libredb-studio/src/components/SavedQueries.tsx
  23:5  warning  Error: Calling setState synchronously within an effect can trigger cascading renders

Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
* Update external systems with the latest state from React.
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.

Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).

/home/cevheri/projects/libredb/libredb-studio/src/components/SavedQueries.tsx:23:5
  21 |   // Refresh queries when refreshTrigger changes (replaces key-based re-mount)
  22 |   useEffect(() => {
> 23 |     setQueries(storage.getSavedQueries());
     |     ^^^^^^^^^^ Avoid calling setState() directly within an effect
  24 |   }, [refreshTrigger]);
  25 |
  26 |   const filteredQueries = queries.filter((q) => {  react-hooks/set-state-in-effect

/home/cevheri/projects/libredb/libredb-studio/src/components/VisualExplain.tsx
  757:5  warning  Error: Calling setState synchronously within an effect can trigger cascading renders

Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
* Update external systems with the latest state from React.
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.

Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).

/home/cevheri/projects/libredb/libredb-studio/src/components/VisualExplain.tsx:757:5
  755 |   useEffect(() => {
  756 |     if (kind === null) return;
> 757 |     setActiveTab((current) => {
      |     ^^^^^^^^^^^^ Avoid calling setState() directly within an effect
  758 |       const available: readonly ExplainTab[] = kind === "tree" ? TREE_TABS : POSTGRES_TABS;
  759 |       return available.includes(current) ? current : available[0];
  760 |     });  react-hooks/set-state-in-effect

/home/cevheri/projects/libredb/libredb-studio/src/components/admin/tabs/AuditTab.tsx
  236:5  warning  Error: Calling setState synchronously within an effect can trigger cascading renders

Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
* Update external systems with the latest state from React.
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.

Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).

/home/cevheri/projects/libredb/libredb-studio/src/components/admin/tabs/AuditTab.tsx:236:5
  234 |
  235 |   useEffect(() => {
> 236 |     setHistory(storage.getHistory());
      |     ^^^^^^^^^^ Avoid calling setState() directly within an effect
  237 |   }, []);
  238 |
  239 |   const filteredHistory = useMemo(() => {  react-hooks/set-state-in-effect
  352:5  warning  Error: Calling setState synchronously within an effect can trigger cascading renders

Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
* Update external systems with the latest state from React.
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.

Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).

/home/cevheri/projects/libredb/libredb-studio/src/components/admin/tabs/AuditTab.tsx:352:5
  350 |
  351 |   useEffect(() => {
> 352 |     setHistory(storage.getHistory());
      |     ^^^^^^^^^^ Avoid calling setState() directly within an effect
  353 |   }, []);
  354 |
  355 |   const stats = useMemo(() => {            react-hooks/set-state-in-effect

/home/cevheri/projects/libredb/libredb-studio/src/components/admin/tabs/OverviewTab.tsx
  155:7  warning  Error: Calling setState synchronously within an effect can trigger cascading renders

Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
* Update external systems with the latest state from React.
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.

Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).

/home/cevheri/projects/libredb/libredb-studio/src/components/admin/tabs/OverviewTab.tsx:155:7
  153 |     prevTarget.current = target;
  154 |     if (target === 0) {
> 155 |       setValue(0);
      |       ^^^^^^^^ Avoid calling setState() directly within an effect
  156 |       return;
  157 |     }
  158 |  react-hooks/set-state-in-effect

/home/cevheri/projects/libredb/libredb-studio/src/components/admin/tabs/SecurityTab.tsx
  138:5  warning  Error: Calling setState synchronously within an effect can trigger cascading renders

Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
* Update external systems with the latest state from React.
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.

Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).

/home/cevheri/projects/libredb/libredb-studio/src/components/admin/tabs/SecurityTab.tsx:138:5
  136 |
  137 |   useEffect(() => {
> 138 |     setThresholds(storage.getThresholdConfig());
      |     ^^^^^^^^^^^^^ Avoid calling setState() directly within an effect
  139 |   }, []);
  140 |
  141 |   const updateThreshold = (index: number, field: "warning" | "critical", value: number) => {  react-hooks/set-state-in-effect

/home/cevheri/projects/libredb/libredb-studio/src/components/monitoring/MonitoringDashboard.tsx
  78:5  warning  Error: Calling setState synchronously within an effect can trigger cascading renders

Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
* Update external systems with the latest state from React.
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.

Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).

/home/cevheri/projects/libredb/libredb-studio/src/components/monitoring/MonitoringDashboard.tsx:78:5
  76 |   useEffect(() => {
  77 |     if (allConns.length === 0) return;
> 78 |     setConnections(allConns);
     |     ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect
  79 |
  80 |     setSelectedConnection((prev) => {
  81 |       if (prev) return prev;  react-hooks/set-state-in-effect

/home/cevheri/projects/libredb/libredb-studio/src/components/schema-explorer/SchemaExplorer.tsx
  7:10  warning  'Button' is defined but never used  @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/src/components/schema-explorer/TableItem.tsx
  17:10  warning  'Button' is defined but never used  @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/src/components/sidebar/ConnectionItem.tsx
  5:10  warning  'Button' is defined but never used  @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/src/components/sidebar/Sidebar.tsx
  7:10  warning  'Button' is defined but never used  @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/src/components/ui/sidebar.tsx
  570:26  warning  Error: Cannot call impure function during render

`Math.random` is an impure function. Calling an impure function can produce unstable results that update unpredictably when the component happens to re-render. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent).

/home/cevheri/projects/libredb/libredb-studio/src/components/ui/sidebar.tsx:570:26
  568 |   // Random width between 50 to 90%.
  569 |   const width = React.useMemo(() => {
> 570 |     return `${Math.floor(Math.random() * 40) + 50}%`;
      |                          ^^^^^^^^^^^^^ Cannot call impure function
  571 |   }, []);
  572 |
  573 |   return (  react-hooks/purity

/home/cevheri/projects/libredb/libredb-studio/src/hooks/use-connection-form.ts
  293:6  warning  React Hook useCallback has an unnecessary dependency: 'type'. Either exclude it or remove the dependency array  react-hooks/exhaustive-deps

/home/cevheri/projects/libredb/libredb-studio/src/hooks/use-provider-metadata.ts
  22:7  warning  Error: Calling setState synchronously within an effect can trigger cascading renders

Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
* Update external systems with the latest state from React.
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.

Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).

/home/cevheri/projects/libredb/libredb-studio/src/hooks/use-provider-metadata.ts:22:7
  20 |   useEffect(() => {
  21 |     if (!connection) {
> 22 |       setMetadata(null);
     |       ^^^^^^^^^^^ Avoid calling setState() directly within an effect
  23 |       lastConnectionId.current = null;
  24 |       return;
  25 |     }  react-hooks/set-state-in-effect

/home/cevheri/projects/libredb/libredb-studio/src/hooks/use-tab-manager.ts
  160:6  warning  React Hook useCallback has an unnecessary dependency: 'activeConnection'. Either exclude it or remove the dependency array  react-hooks/exhaustive-deps

/home/cevheri/projects/libredb/libredb-studio/src/lib/db/providers/sql/druid/http-transport.ts
  264:7  warning  'UNDESCRIBED' is assigned a value but never used  @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/src/lib/db/providers/sql/sqlite-driver.ts
  99:33  warning  '_options' is defined but never used  @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/src/workspace/StudioWorkspace.tsx
  278:5  warning  React Hook useCallback has a missing dependency: 'conn.activeConnection?.type'. Either include it or remove the dependency array  react-hooks/exhaustive-deps

/home/cevheri/projects/libredb/libredb-studio/tests/api/db/maintenance.test.ts
  32:29  warning  '_event' is defined but never used  @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/tests/components/DataCharts.test.tsx
  59:33  warning  '_el' is defined but never used       @typescript-eslint/no-unused-vars
  59:48  warning  '_options' is defined but never used  @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/tests/components/QueryEditor.test.tsx
    19:37  warning  '_a' is defined but never used  @typescript-eslint/no-unused-vars
    20:34  warning  '_a' is defined but never used  @typescript-eslint/no-unused-vars
   311:37  warning  '_a' is defined but never used  @typescript-eslint/no-unused-vars
   312:34  warning  '_a' is defined but never used  @typescript-eslint/no-unused-vars
  1820:26  warning  '_a' is defined but never used  @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/tests/components/QuerySafetyDialog.test.tsx
  399:41  warning  '_params' is defined but never used  @typescript-eslint/no-unused-vars
  428:41  warning  '_params' is defined but never used  @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/tests/components/SchemaDiagram.test.tsx
  136:40  warning  '_options' is defined but never used  @typescript-eslint/no-unused-vars
  339:50  warning  '_options' is defined but never used  @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/tests/components/StudioWorkspace.test.tsx
   46:25  warning  '_args' is defined but never used   @typescript-eslint/no-unused-vars
   48:35  warning  '_blob' is defined but never used   @typescript-eslint/no-unused-vars
  303:37  warning  '_query' is defined but never used  @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/tests/components/sidebar/ConnectionItem.test.tsx
  13:7   warning  Unused eslint-disable directive (no problems were reported from '@typescript-eslint/no-unused-vars')
  18:13  warning  'initial' is defined but never used                                                                   @typescript-eslint/no-unused-vars
  19:13  warning  'animate' is defined but never used                                                                   @typescript-eslint/no-unused-vars
  20:13  warning  'exit' is defined but never used                                                                      @typescript-eslint/no-unused-vars
  21:13  warning  'variants' is defined but never used                                                                  @typescript-eslint/no-unused-vars
  22:13  warning  'whileHover' is defined but never used                                                                @typescript-eslint/no-unused-vars
  23:13  warning  'whileTap' is defined but never used                                                                  @typescript-eslint/no-unused-vars
  24:13  warning  'layoutId' is defined but never used                                                                  @typescript-eslint/no-unused-vars
  25:13  warning  'transition' is defined but never used                                                                @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/tests/components/sidebar/ConnectionsList.test.tsx
  13:7   warning  Unused eslint-disable directive (no problems were reported from '@typescript-eslint/no-unused-vars')
  18:13  warning  'initial' is defined but never used                                                                   @typescript-eslint/no-unused-vars
  19:13  warning  'animate' is defined but never used                                                                   @typescript-eslint/no-unused-vars
  20:13  warning  'exit' is defined but never used                                                                      @typescript-eslint/no-unused-vars
  21:13  warning  'variants' is defined but never used                                                                  @typescript-eslint/no-unused-vars
  22:13  warning  'whileHover' is defined but never used                                                                @typescript-eslint/no-unused-vars
  23:13  warning  'whileTap' is defined but never used                                                                  @typescript-eslint/no-unused-vars
  24:13  warning  'layoutId' is defined but never used                                                                  @typescript-eslint/no-unused-vars
  25:13  warning  'transition' is defined but never used                                                                @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/tests/hooks/use-ai-chat.test.ts
  628:14  warning  '_params' is defined but never used  @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/tests/integration/db/oracle-provider.test.ts
  14:38  warning  '_opts' is defined but never used  @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/tests/isolated/factory-singleton.test.ts
  38:52  warning  'getStorageProviderType' is defined but never used  @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/tests/isolated/use-storage-sync.test.ts
  444:13  warning  'fetchMock' is assigned a value but never used  @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/tests/unit/lib/connection-string-parser.test.ts
  654:5  warning  Unused eslint-disable directive (no problems were reported from 'no-extend-native')
  662:7  warning  Unused eslint-disable directive (no problems were reported from 'no-extend-native')

/home/cevheri/projects/libredb/libredb-studio/tests/unit/lib/storage/providers/postgres.test.ts
    9:34  warning  '_args' is defined but never used  @typescript-eslint/no-unused-vars
  238:41  warning  'sql' is defined but never used    @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/tests/unit/lib/storage/providers/sqlite.test.ts
   10:17  warning  '_args' is defined but never used  @typescript-eslint/no-unused-vars
   12:27  warning  '_args' is defined but never used  @typescript-eslint/no-unused-vars
   13:29  warning  '_args' is defined but never used  @typescript-eslint/no-unused-vars
  115:30  warning  '_args' is defined but never used  @typescript-eslint/no-unused-vars
  136:26  warning  '_args' is defined but never used  @typescript-eslint/no-unused-vars
  164:30  warning  '_args' is defined but never used  @typescript-eslint/no-unused-vars
  198:30  warning  '_args' is defined but never used  @typescript-eslint/no-unused-vars

/home/cevheri/projects/libredb/libredb-studio/tests/unit/schema-diagram/layout-engine.test.ts
   29:15  warning  '_url' is defined but never used  @typescript-eslint/no-unused-vars
  126:19  warning  '_url' is defined but never used  @typescript-eslint/no-unused-vars

✖ 78 problems (0 errors, 78 warnings)
  0 errors and 6 warnings potentially fixable with the `--fix` option. surfaces zero new errors against the existing
   code in that directory, so nothing pre-existing needed silencing.

Also attempted, as requested: a real end-to-end round trip against a genuine
STORAGE_PROVIDER=sqlite file. tests/integration/storage/ now has a Node-run
harness (bundled with bun build v1.3.14 (0d9b296a)
error: Missing entrypoints. What would you like to bundle?

Usage:
  $ bun build <entrypoint> [...<entrypoints>] [...flags]

To see full documentation:
  $ bun build --help and
executed under real , since Bun cannot load better-sqlite3) that writes
a canary password through the same withCredentialEncryption() decorator the
factory uses, checkpoints the WAL, and reads the RAW BYTES ON DISK plus the
read-back after a simulated key rotation. Confirmed it catches a real
regression: reverting the connection-secrets.ts fix from the previous commit
turns this test red (verified locally, then re-fixed). The one obstacle noted
in advance - better-sqlite3 needing real node_modules resolution when the
bundle lives outside the project tree - is worked around with a symlink into
the temp build directory rather than moving output into the repo.

docs/SECURITY.md's control 3.1 row now lists three verifiers instead of one.
@sonarqubecloud

Copy link
Copy Markdown

Copilot AI 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.

Pull request overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated 1 comment.

Suppressed comments (3)

src/lib/storage/encryption.ts:79

  • An explicitly configured empty value bypasses the minimum-length validation because it is falsy, so STORAGE_ENCRYPTION_KEY="" silently falls back to JWT_SECRET. This defeats an operator's intended key separation and contradicts the documented rule that configured values shorter than 32 characters are rejected. Distinguish an absent variable from an empty one and validate the latter.
  const explicit = process.env.STORAGE_ENCRYPTION_KEY;
  if (explicit) {

SECURITY.md:98

  • This backup claim is still too broad. In the zero-configuration SQLite case, JWT_SECRET is persisted in auth-bootstrap.json beside the database, so a backup of that data directory contains both ciphertext and the key; the new docs/STORAGE.md and posture page correctly document this exception. Narrow this paragraph too so the published disclosure policy does not contradict them.
  still identify what a dump contains. A leaked database file or backup is therefore not by itself
  enough to read the credentials — but anyone who can read the server's environment still can.
  Rotating the key makes stored credentials unreadable; see
  [docs/STORAGE.md](docs/STORAGE.md#credential-encryption-at-rest).

SECURITY.md:10

  • The support table now declares every pre-0.10 release unsupported, but this same document's image-SBOM commands at lines 215 and 220 still resolve :0.9.67. Copying the published verification example would therefore inspect an unsupported old image rather than this 0.10.0 release; update those two occurrences to 0.10.0 (or a version placeholder).
| 0.10.x   | :white_check_mark: |
| < 0.10.0 | :x:                |

Comment thread src/lib/storage/connection-secrets.ts
@cevheri
cevheri merged commit 39c51a6 into main Aug 10, 2026
20 checks passed
@cevheri
cevheri deleted the security/phase-3-credentials-at-rest branch August 10, 2026 08:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants