You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
agent: config-file credentials and read_log output still reach the model after #3041 — mask credential values in agent reads #3097
#3041 narrows the built-in agent's config filesystem scope to the config file, and makes the fs tools refuse key material (key directories, *.pem/*.key/.jwtPass, PEM private-key text) in every scope. Two read paths still return credential values to the model, and from there they go into the session transcript, which is persisted, audited and replicated with the system database:
The config file's own values.read_file(root:'config', path:'harper-config.yaml') returns the file whole. That includes plaintext credentials such as models.<kind>.<name>.apiKey: a key supplied through HARPER_SET_CONFIG is written into the file. get_configuration returns the same values, and it is kept off the agent's default operations allow-list for exactly that reason (components/mcp/tools/operations.ts, DEFAULT_ALLOW); the fs read reaches the same bytes.
Checked on david/agent-config-scope (the #3041 branch) with a dist-booted Harper driven by a fake model. With models.generative.default.apiKey supplied through HARPER_SET_CONFIG, the key is persisted in harper-config.yaml, and read_file(root:'config', path:'harper-config.yaml') returns it. apiKey: ${OPENAI_API_KEY} is supported (resources/models/bootstrap.ts), and then the file holds only the reference, but plaintext is the path HARPER_SET_CONFIG takes.
Proposal
When an fs read tool returns text from the config scope, mask the value of every credential-named key (apiKey, *secret*, *password*, *token*, …) as [redacted]. Do it line by line, so comments and line numbers survive. Leave a ${ENV} reference as is. The scope is read-only, so a masked copy can never be written back.
Share one credential-key pattern. resources/models/decisionStore.ts already has CREDENTIAL_KEY; lift it to one place so the agent, decisionStore and any future get_configuration redaction agree.
Acceptance
read_file, grep_files and tail_file on the config file return apiKey: [redacted] for a plaintext key, and apiKey: ${OPENAI_API_KEY} unchanged.
read_log through the agent masks the same keys and refuses PEM private-key text.
Non-credential config values are returned unchanged.
Summary
#3041 narrows the built-in agent's
configfilesystem scope to the config file, and makes the fs tools refuse key material (key directories,*.pem/*.key/.jwtPass, PEM private-key text) in every scope. Two read paths still return credential values to the model, and from there they go into the session transcript, which is persisted, audited and replicated with thesystemdatabase:read_file(root:'config', path:'harper-config.yaml')returns the file whole. That includes plaintext credentials such asmodels.<kind>.<name>.apiKey: a key supplied throughHARPER_SET_CONFIGis written into the file.get_configurationreturns the same values, and it is kept off the agent's default operations allow-list for exactly that reason (components/mcp/tools/operations.ts,DEFAULT_ALLOW); the fs read reaches the same bytes.read_log. It is on the default allow-list and returns log text unfiltered.tail_fileon the same log refuses PEM private-key text after agent: the read-onlyconfigfilesystem scope is the whole Harper root —keys/anddatabase/are readable; addagent.configScopeand deny key material #3041, butread_logdoes not, and neither filters other credentials a component or error message might log.Current behavior
Checked on
david/agent-config-scope(the #3041 branch) with a dist-booted Harper driven by a fake model. Withmodels.generative.default.apiKeysupplied throughHARPER_SET_CONFIG, the key is persisted inharper-config.yaml, andread_file(root:'config', path:'harper-config.yaml')returns it.apiKey: ${OPENAI_API_KEY}is supported (resources/models/bootstrap.ts), and then the file holds only the reference, but plaintext is the pathHARPER_SET_CONFIGtakes.Proposal
configscope, mask the value of every credential-named key (apiKey,*secret*,*password*,*token*, …) as[redacted]. Do it line by line, so comments and line numbers survive. Leave a${ENV}reference as is. The scope is read-only, so a masked copy can never be written back.configfilesystem scope is the whole Harper root —keys/anddatabase/are readable; addagent.configScopeand deny key material #3041, toread_logoutput when the caller is the agent.resources/models/decisionStore.tsalready hasCREDENTIAL_KEY; lift it to one place so the agent,decisionStoreand any futureget_configurationredaction agree.Acceptance
read_file,grep_filesandtail_fileon the config file returnapiKey: [redacted]for a plaintext key, andapiKey: ${OPENAI_API_KEY}unchanged.read_logthrough the agent masks the same keys and refuses PEM private-key text.Related
configfilesystem scope is the whole Harper root —keys/anddatabase/are readable; addagent.configScopeand deny key material #3041 — the agent'sconfigscope and key-material refusalhttp_fetchconfigurable — off, or a host allow-list (agent.httpFetch) #2974 —agent.httpFetch(the egress half)🤖 Generated by Claude Opus 5.5 (Claude Code); posted via @DavidCockerill.