{"record":{"id":"76d1d93fd780ccdd","repo":"affaan-m/ECC","slug":"refusing-to-save-memory-containing-a-suspected-sec","errorCode":null,"errorMessage":"Refusing to save memory containing a suspected secret (${secretKinds.join(', ')}).","messagePattern":"Refusing to save memory containing a suspected secret \\((.+?)\\)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/memory-vault.js","lineNumber":324,"sourceCode":"    scope: input.scope || 'project',\n    trust: 'unreviewed',\n    status: 'active',\n    sourceHarness: input.sourceHarness || 'unknown',\n    targetHarnesses: input.targetHarnesses || ['all'],\n    tags: input.tags || [],\n    links: input.links || [],\n    createdAt: now,\n    updatedAt: now,\n    body: input.body || '',\n  });\n}\n\nfunction saveMemory(input, options = {}) {\n  const roots = options.roots || resolveVaultRoots(options);\n  const memory = normalizeSaveInput(input || {}, options);\n  const secretKinds = findPotentialSecrets(JSON.stringify(memory));\n  if (secretKinds.length > 0) {\n    throw new Error(`Refusing to save memory containing a suspected secret (${secretKinds.join(', ')}).`);\n  }\n\n  const root = assertMemoryRootSafe(roots, memory.scope);\n  fs.mkdirSync(root, { recursive: true, mode: 0o700 });\n  ensureProjectScopeIgnored(roots, memory.scope);\n  const directory = path.join(root, `${memory.kind}s`);\n  assertMemoryDirectorySafe(directory, root);\n  fs.mkdirSync(directory, { recursive: true, mode: 0o700 });\n  const destination = path.join(directory, `${memory.id}.md`);\n\n  try {\n    writeCreateOnlyTextFile(destination, serializeMemoryDocument(memory), root);\n  } catch (error) {\n    if (error && error.code === 'EEXIST') {\n      throw new Error(`Memory ${memory.id} already exists; writes are create-only.`);\n    }\n    throw error;\n  }","sourceCodeStart":306,"sourceCodeEnd":342,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/memory-vault.js#L306-L342","documentation":"Before persisting, saveMemory serializes the normalized memory to JSON and runs findPotentialSecrets across the whole document. If any substring matches a known secret pattern (API keys, tokens, private keys, etc.), the save is refused outright — the vault is designed never to store secrets, so this is a hard stop, not a warning. The secret kinds are listed in the message.","triggerScenarios":"Calling saveMemory with a body, title, tags, or links field that contains a string matching a secret heuristic (e.g. an OpenAI sk- key, a GitHub PAT, an AWS secret access key, a PEM private key block, a JWT). The check runs on JSON.stringify(memory), so secrets hidden anywhere in the object are caught.","commonSituations":"Pasting an API key into a memory body as a 'note to self'; storing a .env excerpt; recording a curl command that includes a Bearer token; capturing an error message that logged a secret; a stack trace containing decrypted credentials.","solutions":["Remove the secret from the memory body and reference it by name only (e.g. 'uses OPENAI_API_KEY from the env') — never paste the value.","Store the actual secret in a dedicated secret manager (env var, vault, keychain) and keep only its identifier in the memory.","If the heuristic is flagging a false positive (a long high-entropy string that is not a secret), restructure the content so it does not match (split it, describe it, or store an identifier instead of the raw value).","Audit the body, title, tags, and links fields — the check scans all of them via JSON.stringify."],"exampleFix":"// before\nsaveMemory({ title: 'deploy token', body: 'ghp_abcdefghijklmnopqrstuvwxyz1234' });\n// after\nsaveMemory({ title: 'deploy token', body: 'The deploy token is stored in GH_DEPLOY_TOKEN (1Password). Rotate quarterly.' });","handlingStrategy":"validation","validationCode":"const { findPotentialSecrets } = require('./scripts/lib/memory-vault');\nfunction assertNoSecrets(memory) {\n  const kinds = findPotentialSecrets(JSON.stringify(memory));\n  if (kinds.length > 0) {\n    throw new Error(`Refusing to save memory containing a suspected secret (${kinds.join(', ')}).`);\n  }\n}\n// before saveMemory:\nassertNoSecrets(normalizeMemory(input));","typeGuard":null,"tryCatchPattern":"try { saveMemory(input); }\ncatch (error) {\n  if (/suspected secret/i.test(error.message)) {\n    console.error('Strip secrets from the memory body/title/tags/links:', error.message);\n    return;\n  }\n  throw error;\n}","preventionTips":["Never paste secret values into a memory — reference them by env var name instead.","Run input through findPotentialSecrets in a pre-save hook to fail early.","Audit memory bodies, titles, tags, and links — the check scans the whole serialized object.","Store real secrets in a dedicated secret manager (1Password, env, keychain)."],"tags":["security","memory-vault","secrets","validation"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}