Skip to content

feat: FormData support, custom field names, direct API targeting for Gandalf + persistent API key config#53

Merged
andrebyrd-odingard merged 5 commits intomainfrom
devin/1776331581-gandalf-formdata-support
Apr 16, 2026
Merged

feat: FormData support, custom field names, direct API targeting for Gandalf + persistent API key config#53
andrebyrd-odingard merged 5 commits intomainfrom
devin/1776331581-gandalf-formdata-support

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot commented Apr 16, 2026

Summary

Adds three capabilities needed for ARGUS to scan non-standard AI targets like Gandalf:

  1. FormData body format_fire_via_agent_endpoint() can now send multipart/form-data requests (data=) instead of JSON (json=) when body_format="formdata" is set on the target config. build_body_for_format() gains support for formdata and custom formats with configurable prompt field names and extra static fields.

  2. CLI options for body negotiation--body-format, --prompt-field, and --extra-field flags added to scan, live, and alec-export. Example Gandalf usage:

    argus scan gandalf \
      --target https://gandalf-api.lakera.ai/api/send-message \
      --body-format formdata \
      --prompt-field prompt \
      --extra-field defender=baseline
    
  3. Persistent config (src/argus/config.py) — new argus config set/get/delete/list/path commands backed by ~/.argus/argusrc (TOML, 0600 perms). API keys resolve with priority: CLI flag → env var → config file. This replaces the need to export ANTHROPIC_API_KEY=... every session.

Additional changes in prompt_injection.py:

  • Response JSON parsing now checks answer key first (Gandalf returns {"answer": "..."})
  • Non-JSON responses are handled gracefully instead of crashing

Review & Testing Checklist for Human

  • Verify build_body_for_format("formdata") actually produces correct FormData — the function returns a dict which is then passed as data= to httpx. Confirm httpx sends this as multipart/form-data and not application/x-www-form-urlencoded (httpx uses data= for form-encoded; files= is needed for true multipart). This is the most critical correctness question — Gandalf may require one or the other.
  • Test argus config set anthropic_api_key sk-ant-... end-to-end — verify the file is created at ~/.argus/argusrc with 0600 permissions, and that a subsequent argus scan picks up the key without --agent-api-key or env var.
  • Test against Gandalf baseline — run the example command above and confirm ARGUS actually gets a response from gandalf-api.lakera.ai (not a 405 or format error).
  • Check that data.get("answer", ...) in prompt_injection.py doesn't break existing targets — this changes the JSON field extraction priority for ALL targets, not just Gandalf. Targets that have an answer field with different semantics could be affected.
  • Note: extra-field parsing is duplicated 3x across scan/live/alec-export with inconsistent error handling (only scan warns on malformed values). Acceptable for now but should be extracted to a helper.

Notes

  • Only the prompt_injection agent is wired to use body_format/prompt_field/extra_fields. Other agents still use their default HTTP dispatch. This is intentional — prompt injection is the primary agent that fires at chat endpoints.
  • The new config.py module (228 lines) has no dedicated unit tests. The 352 existing tests pass but don't exercise config file read/write/permissions.
  • argus_config._config_path() (private function) is referenced in CLI display code — minor code smell.

Link to Devin session: https://app.devin.ai/sessions/8b0c5ca873934d77aa254157cc41924c
Requested by: @andrebyrd-odingard


Open with Devin

…Gandalf + persistent API key config

- Add body_format, prompt_field, extra_fields to TargetConfig
- build_body_for_format() supports formdata/custom mode with .data= dispatch
- _fire_via_agent_endpoint() switches between .json= and .data= based on format
- CLI: --body-format, --prompt-field, --extra-field on scan/live/alec-export
- Persistent config: ~/.argus/argusrc with argus config set/get/list/delete
- resolve_agent_api_key() + resolve_llm_api_key() read CLI > env > config file
- 352 tests pass, lint clean

Architecture: James, Security Features: Mark
@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

devin-ai-integration[bot]

This comment was marked as resolved.

- Add _escape_toml_value() to prevent config corruption from quotes/backslashes
- CLI now shows resolved API key (from env/config) with source indicator, not just CLI flag
- Fixes Devin Review findings #1 and #2

QA/QC Testing: Jamie
devin-ai-integration[bot]

This comment was marked as resolved.

…c config write

- FormData now uses files= for true multipart/form-data (not x-www-form-urlencoded)
- list_all() returns raw key names (agent_api_key) not section-prefixed (keys.agent_api_key)
- Config file written atomically via os.open() with 0o600 — no world-readable window

QA/QC Testing: Jamie
devin-ai-integration[bot]

This comment was marked as resolved.

The default return in build_body_for_format() silently dropped extra_fields
when prompt_field was 'message' (the default). Now all code paths include
extra_fields when provided.

QA/QC Testing: Jamie
devin-ai-integration[bot]

This comment was marked as resolved.

Devin Review: live and alec-export silently ignored --extra-field values
without '=' separator. Now prints a warning matching scan command behavior.

QA/QC Testing: Jamie
@andrebyrd-odingard andrebyrd-odingard merged commit 1ced33c into main Apr 16, 2026
7 checks passed
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.

1 participant