Skip to content

Feat/secure credentials#556

Open
JoelSass wants to merge 4 commits into
usestrix:mainfrom
JoelSass:feat/secure-credentials
Open

Feat/secure credentials#556
JoelSass wants to merge 4 commits into
usestrix:mainfrom
JoelSass:feat/secure-credentials

Conversation

@JoelSass

@JoelSass JoelSass commented Jun 9, 2026

Copy link
Copy Markdown

Adds --credentials KEY=VALUE[,...] and --credentials-file path.json CLI flags so authorization secrets (usernames, passwords, API keys) can be supplied to a scan without the LLM ever seeing the actual values.

How it works:

  • Credential names are listed in the system prompt so the LLM knows what is available.
  • The LLM writes {{NAME}} placeholders in any tool input (shell commands, HTTP bodies, file writes, etc.).
  • Before the tool executes the framework substitutes the real value; after execution, any credential values in the output are replaced with [CREDENTIAL:NAME] before the LLM sees the result.
  • The LLM therefore never handles the actual secret at any point in the conversation history.

Changes:

  • strix/interface/main.py: --credentials / --credentials-file flags with full validation (_parse_credentials helper)
  • strix/interface/cli.py, tui/app.py: forward credentials into scan_config
  • strix/core/inputs.py: add credential_names to system prompt context; skip parallel_tool_calls=False when routing via proxy to avoid a Bedrock tool_choice.type error
  • strix/core/runner.py: place credentials dict in runtime context so all tool wrappers can read them via ctx.context["credentials"]
  • strix/tools/credentials/tool.py: substitute_credentials() and scrub_credentials() pure utilities
  • strix/agents/factory.py: _wrap_credential_substitution() applied to _BASE_TOOLS, exec_command, write_stdin, and filesystem tools; uses dataclasses.replace for singleton FunctionTools, in-place mutation for subclasses (e.g. ViewImageTool) that override init
  • strix/agents/prompts/system_prompt.jinja: CREDENTIALS AVAILABLE block explains {{NAME}} placeholder syntax using {% raw %} so Jinja does not evaluate the braces as template expressions
  • pyproject.toml: pytest dev dependency and ruff per-file ignores
  • tests: 31 tests covering CLI parsing, scope context, substitution, scrubbing, and wrapper integration
  • docs: README, cli.mdx, instructions.mdx updated to document the new flags and remove inline secret examples

@greptile-apps

greptile-apps Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds --credentials KEY=VALUE[,...] and --credentials-file path.json CLI flags so secrets are never placed directly in the LLM conversation. Credential names appear in the system prompt; the LLM writes {{NAME}} placeholders in tool inputs; a wrapper layer substitutes real values before execution and scrubs them from output before the LLM sees the result.

  • _wrap_credential_substitution is applied as the outermost wrapper for _BASE_TOOLS, shell tools, and filesystem tools (chat-completions mode); credentials propagate to child agents via the shallow-copied runtime context dict.
  • _parse_credentials validates key format ([A-Za-z0-9_]+) for both the inline and file paths, handles file-overrides-inline precedence, and produces clean argparse error messages on every failure case.
  • 31 new tests cover parsing, scope context, substitution, scrubbing, singleton immutability, and wrapper integration.

Confidence Score: 5/5

Safe to merge; the credential isolation mechanism is structurally sound and all previously identified blocking issues have been addressed.

The security model is correctly implemented end-to-end: names-only in the system prompt, placeholder substitution at the outermost wrapper layer before any inner tool logic runs, and output scrubbing before results reach the LLM. Child agents correctly inherit the credentials dict through the shallow-copied context. The _parse_credentials helper now validates key format on both code paths. Remaining observations are narrow edge cases with no impact on the core isolation guarantee.

No files require special attention.

Important Files Changed

Filename Overview
strix/tools/credentials/tool.py New credential substitution utilities; scrub_credentials uses case-sensitive regex and skips values < 4 chars, which is documented but worth noting
strix/interface/main.py Adds --credentials / --credentials-file flags with full _parse_credentials helper; previous thread issues (key validation, comma-in-value, syntax error) all addressed
strix/agents/factory.py Adds _wrap_credential_substitution applied to _BASE_TOOLS, shell tools (always), and filesystem tools (chat-completions mode only); uses dataclasses.replace for singleton safety
strix/core/runner.py Places credentials dict into runtime context so all tool wrappers can access it; child agents inherit it via dict(parent_ctx) shallow copy
strix/core/inputs.py Adds credential_names (sorted keys) to scope context for system prompt; adds via_proxy parameter to make_model_settings to fix Bedrock tool_choice error
strix/agents/prompts/system_prompt.jinja Adds CREDENTIALS AVAILABLE block listing credential names and {{NAME}} placeholder syntax; uses Jinja {% raw %} guards correctly
tests/test_credential_substitution.py Good coverage of substitute/scrub utilities and the wrap integration, including immutability and passthrough tests
tests/test_credentials_parsing.py Covers happy paths, file overrides, error cases, and key-format validation for _parse_credentials
strix/interface/cli.py Forwards parsed credentials dict from args into scan_config
strix/interface/tui/app.py Forwards parsed credentials dict from args into TUI scan_config, mirroring cli.py

Reviews (4): Last reviewed commit: "fix: remove orphaned duplicate loop caus..." | Re-trigger Greptile

Comment thread strix/interface/main.py
Comment thread strix/interface/main.py
Comment thread strix/tools/credentials/tool.py
Adds --credentials KEY=VALUE[,...] and --credentials-file path.json CLI
flags so authorization secrets (usernames, passwords, API keys) can be
supplied to a scan without the LLM ever seeing the actual values.

How it works:
- Credential names are listed in the system prompt so the LLM knows
  what is available.
- The LLM writes {{NAME}} placeholders in any tool input (shell commands,
  HTTP bodies, file writes, etc.).
- Before the tool executes the framework substitutes the real value; after
  execution, any credential values in the output are replaced with
  [CREDENTIAL:NAME] before the LLM sees the result.
- The LLM therefore never handles the actual secret at any point in the
  conversation history.

Changes:
- strix/interface/main.py: --credentials / --credentials-file flags with
  full validation (_parse_credentials helper)
- strix/interface/cli.py, tui/app.py: forward credentials into scan_config
- strix/core/inputs.py: add credential_names to system prompt context;
  skip parallel_tool_calls=False when routing via proxy to avoid a Bedrock
  tool_choice.type error
- strix/core/runner.py: place credentials dict in runtime context so all
  tool wrappers can read them via ctx.context["credentials"]
- strix/tools/credentials/tool.py: substitute_credentials() and
  scrub_credentials() pure utilities
- strix/agents/factory.py: _wrap_credential_substitution() applied to
  _BASE_TOOLS, exec_command, write_stdin, and filesystem tools; uses
  dataclasses.replace for singleton FunctionTools, in-place mutation for
  subclasses (e.g. ViewImageTool) that override __init__
- strix/agents/prompts/system_prompt.jinja: CREDENTIALS AVAILABLE block
  explains {{NAME}} placeholder syntax using {% raw %} so Jinja does not
  evaluate the braces as template expressions
- pyproject.toml: pytest dev dependency and ruff per-file ignores
- tests: 31 tests covering CLI parsing, scope context, substitution,
  scrubbing, and wrapper integration
- docs: README, cli.mdx, instructions.mdx updated to document the new
  flags and remove inline secret examples

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@JoelSass JoelSass force-pushed the feat/secure-credentials branch from affaf32 to 664d64a Compare June 9, 2026 11:24
@JoelSass JoelSass marked this pull request as draft June 12, 2026 07:26
@JoelSass JoelSass marked this pull request as ready for review June 12, 2026 07:26
Comment thread strix/interface/main.py
joel-sass and others added 2 commits June 12, 2026 09:32
Keys with hyphens or other special characters would silently fail
substitution since _PLACEHOLDER_RE only matches word chars/digits/underscores.
Now mirrors the validation already applied to --credentials inline keys.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@JoelSass JoelSass marked this pull request as draft June 12, 2026 07:35
@JoelSass JoelSass marked this pull request as ready for review June 12, 2026 07:35
Comment thread strix/interface/main.py Outdated
The pulled commit introduced an incomplete duplicate of the credentials
file parsing loop (missing closing paren), preventing the module from
compiling. Remove the orphaned fragment, keeping the complete block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@JoelSass JoelSass marked this pull request as draft June 12, 2026 09:53
@JoelSass JoelSass marked this pull request as ready for review June 12, 2026 09:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants