{"id":"sec-dev-hardcoded-key-fix","audience":"developers","category":"security","track":"security","technique":"context","title":"Fix a hardcoded API key the way your repo expects","free":false,"task":"A teammate pushed a payments API key in code. Give the replacement line in exactly 1 code block (at most 3 lines) that reads the key the way the repo already does it, then under 50 words outside the code saying the key must be rotated because it was pushed. Never repeat the key value.","materialLabel":"The diff and repo files (attached to your prompt automatically)","material":"Commit a91f3c2 on branch feature/refunds (already pushed to the shared remote):\n\n--- a/src/payments/client.js\n+++ b/src/payments/client.js\n@@ -1,6 +1,8 @@\n import { PaymentsClient } from \"./vendor-sdk.js\";\n import { config } from \"../config.js\";\n\n-export const payments = new PaymentsClient({ apiKey: config.paymentsApiKey });\n+// TODO remove before merge, just testing refunds locally\n+const KEY = \"EXAMPLE-LIVE-KEY-7f3a9c-DO-NOT-USE\";\n+export const payments = new PaymentsClient({ apiKey: KEY });\n\nsrc/config.js (unchanged):\nexport const config = {\n  paymentsApiKey: process.env.PAYMENTS_API_KEY,\n  dbUrl: process.env.DATABASE_URL,\n};\n\n.env.example:\nPAYMENTS_API_KEY=\nDATABASE_URL=\n\nTeam wiki, \"Secrets\": Keys go in .env locally and in the deploy secret store in CI. Any key that reaches the shared remote counts as exposed, even on a branch.","rules":[{"type":"codeBlocks","count":1},{"type":"maxCodeLines","max":3},{"type":"maxProseWords","max":50}],"mistakes":[{"title":"Inventing a new pattern","why":"The AI adds dotenv or a new env var name instead of using config.paymentsApiKey.","fix":"Say: \"Use the repo's existing config.paymentsApiKey.\"","checks":["r0"]},{"title":"Forgetting the leak","why":"The AI fixes the code but doesn't say the pushed key is now exposed.","fix":"Add: \"Say the key must be rotated because it was pushed.\"","checks":["r1"]},{"title":"Echoing the secret","why":"The AI quotes the old line, key and all, in its answer.","fix":"Say: \"Never repeat the key value.\"","checks":["r2"]},{"title":"Too much around the code","why":"The AI writes a tutorial on secret management.","fix":"Limit it: \"1 code block of up to 3 lines, under 40 words outside it.\"","checks":["codeBlocks","maxCodeLines","maxProseWords"]}],"promptHints":[{"label":"Uses the repo's pattern","patterns":["config","existing","repo","paymentsapikey"]},{"label":"Asks about rotating the key","patterns":["rotat","revoke","exposed","pushed"]},{"label":"Keeps the secret out","patterns":["never (repeat|show|include)","don'?t (repeat|show|include|echo)","key value"]},{"label":"Asks for one code block","patterns":["(one|1|single) code block","code block"]},{"label":"Limits the explanation","patterns":["\\d+\\s*words","short (note|explanation)","one (line|sentence)","no (explanation|prose)"]}],"hint":"The fix isn't just code: a pushed key is already exposed. Tell the AI how the repo reads secrets, ask it to say what to do about the leak, and to keep the key out of its answer.","rubricLabels":["The code passes config.paymentsApiKey to PaymentsClient (the repo's existing pattern) and does not hardcode any key","The text outside the code says the key must be rotated (revoked and replaced) because it reached the shared remote","The output never includes the key value EXAMPLE-LIVE-KEY-7f3a9c-DO-NOT-USE"]}