This repository was archived by the owner on Sep 10, 2026. It is now read-only.
Name the search credential for what it has to be - #2
Open
tomj12k wants to merge 2 commits into
Open
Conversation
T118 / WI-20. vite.config.ts sets envPrefix: ["VITE_", "TAURI_"], so
TAURI_MEILI_API_KEY -- read in Search.tsx through import.meta.env -- is
inlined into the bundle at build time. Demonstrated rather than asserted:
building with a sentinel value puts it in the shipped JavaScript.
TAURI_MEILI_SEARCH_KEY=SENTINEL_KEY_9f3a1c npx vite build
grep -ro 'SENTINEL_KEY_9f3a1c' dist/ -> 4 occurrences
dist/assets/index-468969ec.js
CORRECTING THE THREAT MODEL: T118 says the key "ships inside every published
binary and web build". That is not true today. The release workflow has never
run, and the only release is a draft with zero assets, so nothing has been
distributed and this key has not leaked. The finding is latent, not realised
-- the design guarantees exposure on the first release, which has not
happened. Fixing it now costs nothing; after a release it would also cost a
rotation.
There is no way to hide a credential in a client that queries a search server
directly. The Rust binary is no better a hiding place than the JavaScript --
`strings` reads both. So the shipped value must be one the client is ALLOWED
to hold: a Meilisearch search key, scoped read-only to the indexes this app
searches, never the master key, which can create keys, write documents and
read every index on the instance.
The variable is now TAURI_MEILI_SEARCH_KEY in both the workflow and the
component, so the name states the requirement at the point where someone
would otherwise paste the wrong value. Comments in both places say the value
is inlined and public by construction.
Verified: npx tsc --noEmit exits 0; vite build succeeds and the value still
reaches the client.
BEFORE MERGING: the repository secret must be renamed
TAURI_MEILI_API_KEY -> TAURI_MEILI_SEARCH_KEY, and its contents confirmed to
be a search key rather than the master key. The workflow reads the new name.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D5xdKhPXJT4HMWwUyENiu1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Threat: T118 (
Morelitea/security, Boundary 15). Severity Medium. NIST SC-12, IA-5.Work item: WI-20.
The mechanism, demonstrated rather than asserted
vite.config.ts:17setsenvPrefix: ["VITE_", "TAURI_"], so anything namedTAURI_*and read throughimport.meta.envis inlined into the bundle at build time.Search.tsx:19readTAURI_MEILI_API_KEYthat way, andrelease.ymlfed it from a repository secret.I built it with a sentinel value:
A CI secret that ends up in the artifact is not a secret.
Correcting the threat model
T118 says the key "ships inside every published binary and web build … and is extractable by anyone holding an artifact."
That is not true today.
release.ymlhas never run — it triggers only on av*tag push orworkflow_dispatch, and neither has happened — and the only release is a draft with zero assets. Nothing has been distributed, so this key has not leaked.(An earlier draft of this said "
gh run listreturns nothing". That is now false: this pull request itself triggered two runs, Release Drafter andverify_frontend.yml. The substantive claim is unchanged — it isrelease.ymlspecifically that has never run — but the stated evidence no longer reproduces.)The finding is latent, not realised: the design guarantees exposure on the first release, and that release has not happened. Worth being precise about, because it changes what merging this costs. Fixing it now is a rename. Fixing it after a release would also mean rotating a key that strangers hold.
I will update the threat model wording separately.
Change
There is no way to hide a credential in a client that queries a search server directly. The Rust binary is no better a hiding place than the JavaScript —
stringsreads both. So the only correct answer is that the shipped value must be one the client is allowed to hold.Meilisearch is designed for exactly this: a search key, scoped read-only to specific indexes, is meant to be public. The master key is not — it can create keys, write documents, and read every index on the instance.
So the variable is now
TAURI_MEILI_SEARCH_KEYin both the workflow and the component. The name states the requirement at the point where someone would otherwise paste the wrong value, and comments in both files say the value is inlined and public by construction.Verification
Before merging — this needs a repository change, not just a merge
TAURI_MEILI_API_KEY→TAURI_MEILI_SEARCH_KEY. The workflow reads the new name; without the rename, search is disabled at runtime (the component no-ops when the key is empty) rather than breaking the build.What this does not do
It does not make the value private. Nothing can, for a desktop app that talks to a search server directly. If search ever needs a credential the client may not hold, the request has to go through something that holds it instead — a proxy, or a server-side endpoint. The comment in
Search.tsxsays so, so the next person does not reach for the Rust side thinking it is safer.🤖 Generated with Claude Code
https://claude.ai/code/session_01D5xdKhPXJT4HMWwUyENiu1