Skip to content

FIRE-1166 | Red-Team | HTML Indirect prompt injection attack#160

Open
yuval-qf wants to merge 5 commits intomainfrom
feature/fire-1166-html-indirect-prompt-injection-attack
Open

FIRE-1166 | Red-Team | HTML Indirect prompt injection attack#160
yuval-qf wants to merge 5 commits intomainfrom
feature/fire-1166-html-indirect-prompt-injection-attack

Conversation

@yuval-qf
Copy link
Collaborator

Description

Motivation and Context

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 📝 Documentation update
  • 🎨 Code style/refactoring (no functional changes)
  • 🧪 Test updates
  • 🔧 Configuration/build changes

Changes Made

Screenshots/Examples (if applicable)

Checklist

  • I have read the CONTRIBUTING.md guide
  • My code follows the code style of this project (PEP 8, type hints, docstrings)
  • I have run uv run black . to format my code
  • I have run uv run flake8 . and fixed all issues
  • I have run uv run mypy --config-file .mypy.ini . and addressed type checking issues
  • I have run uv run bandit -c .bandit.yaml -r . for security checks
  • I have added tests that prove my fix is effective or that my feature works
  • I have run uv run pytest and all tests pass
  • I have manually tested my changes
  • I have updated the documentation accordingly
  • I have added/updated type hints for new/modified functions
  • My changes generate no new warnings
  • I have checked my code for security issues
  • Any dependent changes have been merged and published

Testing

Test Configuration:

  • Python version:
  • OS:
  • Other relevant details:

Test Steps:
1.
2.
3.

Additional Notes

Related Issues/PRs

  • Fixes #
  • Related to #
  • Depends on #

@yuval-qf yuval-qf requested a review from drorIvry as a code owner February 20, 2026 10:37
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 20, 2026

Summary by CodeRabbit

  • New Features

    • Added "HTML Indirect Prompt Injection" — a premium single-turn attack.
    • Evaluation UI: new Auth Type and Auth Credentials fields with full editing, paste/keyboard support, and persistence.
  • Improvements

    • Deckard service endpoint is now configurable and defaults to https://deckard.rogue.security.
    • Premium attack requests now use the Qualifire API key header for authentication.

Walkthrough

Adds a new premium attack "html-indirect-prompt-injection", threads an optional deckard_base_url from SDK request through orchestrator/factory/agent/service layers, updates Deckard default URL and premium auth header, and adds TUI authentication fields and persistence for evaluations.

Changes

Cohort / File(s) Summary
Attack Catalogs
packages/tui/internal/screens/redteam/catalog.go, rogue/server/red_teaming/catalog/attacks.py, rogue/server/red_teaming/catalog/vulnerabilities.py
Added premium attack html-indirect-prompt-injection (enum/AttackDef) and mapped it into related vulnerability/default_attacks.
Deckard / Premium Attacks
rogue/server/qualifire_attacks.py, rogue/server/red_teaming/orchestrator.py
Changed premium auth header to X-Qualifire-API-Key, added the new premium attack to allowed list, and updated default Deckard URL to https://deckard.rogue.security.
Deckard Base URL Wiring
sdks/python/rogue_sdk/types.py, rogue/evaluator_agent/red_team/factory.py, rogue/evaluator_agent/red_team/base_red_team_attacker_agent.py, rogue/server/core/red_team_orchestrator.py, rogue/server/services/red_team_service.py
Added deckard_base_url field to request types and threaded optional deckard_base_url through factory/orchestrator/attacker agent construction and service startup.
TUI: Eval Auth UI & Persistence
packages/tui/internal/screens/evaluations/*, packages/tui/internal/tui/*, packages/tui/internal/tui/utils.go
Introduced AuthType/AuthCredentials form fields, navigation/editing/paste/keyboard handling, persistence changes, renamed/added auth constants/tags, and extended Model.StartEvaluation to pass auth params. Extensive UI wiring and state changes.

Sequence Diagram(s)

sequenceDiagram
  participant TUI as TUI (Client)
  participant Orch as RedTeamOrchestrator
  participant Factory as AttackerFactory
  participant Agent as RedTeamAttackerAgent
  participant Deckard as Deckard Service
  TUI->>Orch: Start red-team job (includes deckard_base_url, auth)
  Orch->>Factory: create_red_team_attacker_agent(..., deckard_base_url)
  Factory->>Agent: instantiate agent(..., deckard_base_url)
  Agent->>Deckard: call premium endpoints (uses deckard_base_url + X-Qualifire-API-Key)
  Deckard-->>Agent: attack payloads / responses
  Agent-->>Orch: results
  Orch-->>TUI: job status/results
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • osher-qualifire

Poem

"I'm a rabbit in the code with a pen so bright,
I slipped a prompt injection into the night.
I threaded a URL from request to core,
Deckard now listens from shore to shore.
Hop, hop — premium headers and attacks take flight!" 🐇✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is entirely blank—it contains only the template with no filled-in sections, no description, no motivation, no type of change selection, no changes listed, and no checklist items completed. Fill in all required sections of the PR description: add a clear description of the HTML Indirect Prompt Injection attack addition, explain motivation, select 'New feature', list specific changes made, and verify/complete the relevant checklist items.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding HTML Indirect Prompt Injection attack support to the red-team module, which is the primary feature across all modified files.
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/fire-1166-html-indirect-prompt-injection-attack

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
rogue/server/qualifire_attacks.py (1)

76-83: ⚠️ Potential issue | 🟡 Minor

Verify header naming consistency between DeckardClient and Qualifire service integration.

The DeckardClient uses X-Qualifire-API-Key while the established Qualifire service integration uses X-qualifire-key (line 30 & 148 of rogue/server/services/qualifire_service.py). Both default to the same backend URL (https://app.qualifire.ai), which suggests they target the same service. Before rollout, confirm whether the backend accepts the new header name or if both variants need to be supported for backward compatibility.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rogue/server/qualifire_attacks.py` around lines 76 - 83, The header name used
in _get_headers (X-Qualifire-API-Key) is inconsistent with the header used in
the Qualifire integration (X-qualifire-key) and may cause auth failures; update
_get_headers in the DeckardClient (or the class containing that method) to use
the canonical header name used by the Qualifire service (X-qualifire-key),
and/or add both headers when self.api_key is present to preserve backward
compatibility (i.e., set both "X-qualifire-key" and "X-Qualifire-API-Key" to
self.api_key), ensuring the change is applied where _get_headers is invoked so
requests to https://app.qualifire.ai authenticate correctly.
🧹 Nitpick comments (1)
rogue/evaluator_agent/red_team/factory.py (1)

68-68: Consider documenting the new parameter in the docstring.

The deckard_base_url parameter is not documented in the function's docstring (lines 73-96). While this follows the existing pattern (some parameters like attacker_llm_api_base are also undocumented), consider adding documentation for clarity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rogue/evaluator_agent/red_team/factory.py` at line 68, The new
deckard_base_url parameter is missing from the function's docstring; update the
docstring for the factory function that defines deckard_base_url to include a
concise description (purpose, type Optional[str], and expected format/example)
following the same style as other parameters (e.g., attacker_llm_api_base) so
readers can understand its use and expected value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@rogue/server/qualifire_attacks.py`:
- Around line 76-83: The header name used in _get_headers (X-Qualifire-API-Key)
is inconsistent with the header used in the Qualifire integration
(X-qualifire-key) and may cause auth failures; update _get_headers in the
DeckardClient (or the class containing that method) to use the canonical header
name used by the Qualifire service (X-qualifire-key), and/or add both headers
when self.api_key is present to preserve backward compatibility (i.e., set both
"X-qualifire-key" and "X-Qualifire-API-Key" to self.api_key), ensuring the
change is applied where _get_headers is invoked so requests to
https://app.qualifire.ai authenticate correctly.

---

Nitpick comments:
In `@rogue/evaluator_agent/red_team/factory.py`:
- Line 68: The new deckard_base_url parameter is missing from the function's
docstring; update the docstring for the factory function that defines
deckard_base_url to include a concise description (purpose, type Optional[str],
and expected format/example) following the same style as other parameters (e.g.,
attacker_llm_api_base) so readers can understand its use and expected value.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/tui/internal/screens/evaluations/form_view.go (1)

300-309: Consider using FormField constants instead of hard-coded indices.

The Python protocol path uses hard-coded indices (5, 6, 7) for Judge LLM, Deep Test, and Mode fields. Consider using int(FormFieldJudgeModel), int(FormFieldDeepTest), and int(FormFieldEvaluationMode) for maintainability.

♻️ Suggested improvement
 		formFields = []string{
 			renderDropdownField(0, "Protocol:", protocol),
 			renderTextField(1, "Python File:", pythonFile),
 			// No Transport, AuthType, AuthCredentials for Python protocol
-			renderTextField(5, "Judge LLM:", judge),
-			renderToggleField(6, "Deep Test:", deep),
-			renderDropdownField(7, "Mode:", evalMode),
+			renderTextField(int(FormFieldJudgeModel), "Judge LLM:", judge),
+			renderToggleField(int(FormFieldDeepTest), "Deep Test:", deep),
+			renderDropdownField(int(FormFieldEvaluationMode), "Mode:", evalMode),
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/tui/internal/screens/evaluations/form_view.go` around lines 300 -
309, The Python protocol form uses hard-coded field indices for Judge LLM, Deep
Test and Mode which risks drift; replace the numeric indices (5,6,7) in the
python branch where formFields is built (the calls to renderTextField,
renderToggleField, renderDropdownField) with the corresponding FormField
constants cast to int (int(FormFieldJudgeModel), int(FormFieldDeepTest),
int(FormFieldEvaluationMode)) so the fields align with the rest of the form and
remain maintainable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/tui/internal/tui/utils.go`:
- Line 40: The struct field EvaluatedAgentCredentials in
packages/tui/internal/tui/utils.go was renamed in its JSON tag to
evaluated_agent_auth_credentials which breaks the backend API contract; change
the struct tag back to `json:"evaluated_agent_credentials,omitempty"` so the TUI
sends the expected field name (leave the Go field name EvaluatedAgentCredentials
as-is, only update the JSON tag).

---

Nitpick comments:
In `@packages/tui/internal/screens/evaluations/form_view.go`:
- Around line 300-309: The Python protocol form uses hard-coded field indices
for Judge LLM, Deep Test and Mode which risks drift; replace the numeric indices
(5,6,7) in the python branch where formFields is built (the calls to
renderTextField, renderToggleField, renderDropdownField) with the corresponding
FormField constants cast to int (int(FormFieldJudgeModel),
int(FormFieldDeepTest), int(FormFieldEvaluationMode)) so the fields align with
the rest of the form and remain maintainable.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
packages/tui/internal/tui/form_controller.go (2)

99-106: Combine duplicate case branches for cleaner code.

The EvalFieldAuthCredentials case performs the same cursor decrement as the combined EvalFieldAgentURL, EvalFieldJudgeModel case.

♻️ Proposed consolidation
-	case EvalFieldAgentURL, EvalFieldJudgeModel: // Text fields - move cursor left
-		if m.evalState.cursorPos > 0 {
-			m.evalState.cursorPos--
-		}
-	case EvalFieldAuthCredentials: // Text field - move cursor left
+	case EvalFieldAgentURL, EvalFieldJudgeModel, EvalFieldAuthCredentials: // Text fields - move cursor left
 		if m.evalState.cursorPos > 0 {
 			m.evalState.cursorPos--
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/tui/internal/tui/form_controller.go` around lines 99 - 106, The
switch contains duplicate branches that decrement m.evalState.cursorPos for
EvalFieldAgentURL, EvalFieldJudgeModel and EvalFieldAuthCredentials; consolidate
them into a single case list (e.g. case EvalFieldAgentURL, EvalFieldJudgeModel,
EvalFieldAuthCredentials:) and keep the shared body that checks and decrements
m.evalState.cursorPos to remove the duplicated logic.

79-92: Consider extracting cursor position helper to reduce duplication.

The cursor position setting logic (lines 79-92) is identical to lines 38-51. Extracting this into a helper method would improve maintainability.

♻️ Proposed helper extraction
+// setCursorToEndOfField sets cursor position to end of the current field's content
+func (s *EvaluationViewState) setCursorToEndOfField() {
+	switch s.currentField {
+	case EvalFieldAgentURL:
+		if s.AgentProtocol == ProtocolPython {
+			s.cursorPos = len([]rune(s.PythonEntrypointFile))
+		} else {
+			s.cursorPos = len([]rune(s.AgentURL))
+		}
+	case EvalFieldAuthCredentials:
+		s.cursorPos = len([]rune(s.AgentAuthCredentials))
+	case EvalFieldJudgeModel:
+		s.cursorPos = len([]rune(s.JudgeModel))
+	default:
+		s.cursorPos = 0
+	}
+}

Then replace both occurrences with:

m.evalState.setCursorToEndOfField()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/tui/internal/tui/form_controller.go` around lines 79 - 92, The
cursor-position-setting switch in form_controller.go is duplicated; extract it
into a helper on the evalState struct (e.g., func (s *EvalState)
setCursorToEndOfField()) that reads s.currentField, s.AgentProtocol,
s.PythonEntrypointFile, s.AgentURL, s.AgentAuthCredentials and s.JudgeModel and
sets s.cursorPos appropriately (use len([]rune(...)) for each string and 0
default). Replace both occurrences with a call to
m.evalState.setCursorToEndOfField().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/tui/internal/tui/form_controller.go`:
- Around line 99-106: The switch contains duplicate branches that decrement
m.evalState.cursorPos for EvalFieldAgentURL, EvalFieldJudgeModel and
EvalFieldAuthCredentials; consolidate them into a single case list (e.g. case
EvalFieldAgentURL, EvalFieldJudgeModel, EvalFieldAuthCredentials:) and keep the
shared body that checks and decrements m.evalState.cursorPos to remove the
duplicated logic.
- Around line 79-92: The cursor-position-setting switch in form_controller.go is
duplicated; extract it into a helper on the evalState struct (e.g., func (s
*EvalState) setCursorToEndOfField()) that reads s.currentField, s.AgentProtocol,
s.PythonEntrypointFile, s.AgentURL, s.AgentAuthCredentials and s.JudgeModel and
sets s.cursorPos appropriately (use len([]rune(...)) for each string and 0
default). Replace both occurrences with a call to
m.evalState.setCursorToEndOfField().

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

Comments