Fix docker-compose credential corruption and blank local database credentials - #1676
Conversation
…ocal dev config
The `azure-debug-generate` skill wrote concrete credentials directly into
generated files (e.g. a literal `user:password@host` DATABASE_URL in
docker-compose.yml and local.settings.json). That produced two distinct
failure modes for local development:
1. Fatal YAML corruption. Secret-redaction filters rewrite a concrete
credential literal to a masked value. In YAML a leading `*` is an alias
indicator, so a redacted value makes docker-compose.yml stop parsing
entirely. Local dev is dead on arrival, and the error points at YAML
syntax rather than the credential that caused it.
2. Silent blank credentials. Where compose did use ${POSTGRES_USER} /
${POSTGRES_PASSWORD}, the skill often generated no workspace-root .env.
Compose interpolates ${...} from .env or the shell environment, never
from a service's own `environment:` block, so the values resolved to
empty strings and Postgres started with blank credentials. The result
is an auth failure that looks like an application bug.
The guidance now declares local credentials once in a workspace-root .env,
references them via ${...} everywhere else, forbids inlining a concrete
user:password@host URL, and requires .env to exist and to declare every
variable compose references (.env.example is documentation only — nothing
loads it). postgres.md also documents which host to use from the host
machine vs. from another compose service.
Co-authored-by: Copilot App <[email protected]>
|
I ran into this while testing. Verified that my change does fix it. However, I am worried that what Copilot wrote here is too verbose |
…nce docs Three reference docs still carried a concrete user:password@host URL, which is the same defect fixed for the azure-debug-generate emulator and runtime docs. A reference doc is read by the agent before it writes anything, so a literal there is masked in the agent's context by secret redaction and then transcribed into the generated project — where a masked value beginning with `*` is a fatal YAML alias error, or resolves to a blank credential. - azure-project-plan/plan.md: the two rows on the connection-string tables are templates the planning agent fills in, so a masked value propagates into .azure/project-plan.md and downstream into generated code. - shared-references/resilience.md and service-abstraction.md: the sample test setup now builds the URL from process.env via a template literal (quotes changed from single quotes to backticks accordingly). All concrete credential literals under resources/ are now eliminated. Co-authored-by: Copilot App <[email protected]>
… commit Review feedback: the guidance told the agent to create a workspace-root .env holding real credential values but never said it must be ignored. Adds the requirement at each point where the file is actually written, and states the underlying rule plainly: a secret that reaches a commit is compromised and must be rotated. Deleting it in a later commit does not undo that, because the value stays in history and in every clone and fork. Private repositories are no exception. - emulators/postgres.md, references/generate.md: .gitignore must list .env before it is created; fix a missing or incomplete .gitignore first. - shared-references/architecture.md: the rule stated in full where the .gitignore contents are defined. - azure-project-scaffold/instructions.md: .gitignore is written in Step 2, before Step 3 creates env files, so the ordering is now explicit. Also tightens the prose added earlier in this PR, which ran long without adding information. Net length is roughly unchanged despite the new rule. Co-authored-by: Copilot App <[email protected]>
Matthew Fisher (MicroFish91)
left a comment
There was a problem hiding this comment.
LGTM
I ran into this while testing. Verified that my change does fix it. However, I am worried that what Copilot wrote here is too verbose
Yeah I noticed this too. I think it's because it's reiterating the same reminders across multiple agents. Ideally I would just like the agent responsible for creating the .env the first time to add it to .gitignore and not have to keep reiterating / reminding the later agents about it. In practice though I know an agent could accidentally skip and I could see the benefit of having those reminders as fallbacks in later phases, but that does come at the cost of instruction bloat.
Problem
Reference docs under
resources/inlined concrete credentials — for example a literalDATABASE_URL: postgresql://user:password@host:5432/db. Theazure-debug-generateskill then wrote the same shape into generated local dev files such asdocker-compose.yml. This produced two distinct failure modes.1. Fatal YAML corruption
A secret-redaction filter rewrites the credential literal to a masked value. In YAML a leading
*is an alias indicator, so the moment a redacted value lands indocker-compose.ymlthe file stops parsing entirely.This is why redaction is fatal here rather than merely cosmetic: the masked text isn't just an unusable value, it's a syntax error. Local dev is dead on arrival, and the error the user sees points at YAML syntax — not at the credential that caused it.
2. Silent blank credentials
Where compose did use
${POSTGRES_USER}/${POSTGRES_PASSWORD}, the skill often generated no workspace-root.env. Docker Compose interpolates${...}from.envor the shell environment — never from a service's ownenvironment:block. With no.env, the variables resolve to empty strings and Postgres starts with blank credentials, producing auth failures that look like application bugs.Fix
The guidance now:
.env, and references them via${...}everywhere else.user:password@hostURL into any generated file, and explains the YAML-alias rationale so the rule isn't mistaken for style advice..envto exist and to declare every variable compose references — including a note that.env.exampleis documentation only and nothing loads it.localhostfor clients running on the host machine (VS Code debug target,npmtask) vs. thepostgresservice name for clients running inside compose.local.settings.jsonis plain JSON with no${...}interpolation, and that a runtime settings file does not cover host-run tasks such asnpm run db:migrate..envmust be gitignored, and secrets belong in no commitPer review feedback: the guidance told the agent to create a workspace-root
.envholding real credential values, but never said it must be ignored..gitignoremust now list.envbefore the file is created, stated at each point where it actually gets written (emulators/postgres.md,references/generate.md), with an explicit instruction to fix a missing or incomplete.gitignorefirst.shared-references/architecture.md§ .gitignore Additions carries the rule in full, since that section defines the generated.gitignorecontents: a secret that reaches a commit is compromised the moment it is pushed, deleting it in a later commit does not undo that because the value stays in history and in every clone and fork, recovery means rotating the credential rather than editing a file, and private repositories are no exception.azure-project-scaffold/instructions.mdmakes the ordering explicit —.gitignoreis written in Step 2 (Foundation), before Step 3 (Configuration & Environment) creates env files.Audit: no reference doc can seed a masked value
The remaining reference docs carrying a concrete credential literal are fixed too. This matters because a reference doc is read by the agent before it writes anything — the literal is masked in the agent's context by secret redaction and then transcribed verbatim into the generated project. All concrete credential literals under
resources/are now eliminated, so no reference doc can seed a masked value into a generated project.azure-project-plan/plan.md— the two connection-string table rows are templates the planning agent fills in, so a masked value propagates into.azure/project-plan.mdand downstream into generated code. Highest-impact of the set.shared-references/resilience.mdandshared-references/service-abstraction.md— the sample test setup now builds the URL fromprocess.envvia a template literal (quoting changed from single quotes to backticks accordingly).Verified with
git grep -lE '://[A-Za-z0-9_.-]+:[A-Za-z0-9_.!$-]+@' HEAD -- resources/→ zero files.Files changed
resources/agents/azure-debug-generate/references/emulators/postgres.mdresources/agents/azure-debug-generate/references/generate.mdresources/agents/azure-project-plan/plan.mdresources/agents/azure-project-scaffold/instructions.mdresources/agents/shared-references/architecture.mdresources/agents/shared-references/resilience.mdresources/agents/shared-references/runtimes/python.mdresources/agents/shared-references/runtimes/typescript.mdresources/agents/shared-references/service-abstraction.mdMarkdown instruction files only — no extension source changes, nothing to compile.
Related
The accompanying evaluation-system detection for this defect ships separately in #1669.