Skip to content

FEAT: Run one technique against an objective with technique.execute - #3118

Merged
Richard Lundeen (richlundeen) merged 8 commits into
microsoft:mainfrom
richlundeen:richlundeen-technique-execution
Oct 13, 2026
Merged

Richard Lundeen (richlundeen) merged 8 commits into
microsoft:mainfrom
richlundeen:richlundeen-technique-execution

Conversation

@richlundeen

@richlundeen Richard Lundeen (richlundeen) commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Description

Adds technique.execute: run exactly one registered attack technique toward one free-text objective, without selecting a dataset. It can also continue from a stored manual conversation. This connects manual testing to an automated attack without changing the source conversation.

  • Select any concrete technique in the active catalog. The default is red_teaming; a custom catalog without it uses the first technique in name order. Adds plain multi-turn crescendo to the opt-in extra catalog.
  • Set harm_categories on the objective and its results. Use standard target selection, technique converters, and the existing adversarial target picker/context override.
  • Set retries_on_objective_failure to allow additional attempts. Each attempt gets a fresh context and conversation; execution stops at the first success. max_retries remains the separate error/exception retry setting.
  • Set prepended_conversation_id to copy stored history through the standard seed path. Roles, order, data types, converted values, and message metadata are retained; old request traces are removed. Prior assistant turns extend attacks' configured max_turns so manual history does not consume the new automated-turn budget. Blocked/error history is rejected explicitly.
  • Baseline is off by default and remains opt-in. Direct and repeated execution share one technique-configuration path. Child results retain packaged technique/seed identities, and resume detects changes to source history through run metadata.
  • apply_converters_to_prepended_history defaults to False: stored history is not converted again. Set it to True to opt into the technique's configured history-conversion roles. New outgoing turns and trailing-user resends still use converters.
  • Parent/child links distinguish duplicate wrapper errors from independent orchestration failures. An identifier-write failure remains visible in SDK statistics, SQL history, and run details even when the child finished without an error.

Technique selection contract

The scenario declares MAX_CONCRETE_TECHNIQUES = 1. The backend publishes this as max_concrete_techniques in the scenario catalog and enforces it after aggregate expansion and deduplication, for both estimates and runs. Other scenarios remain unrestricted by default.

CoPyRIT uses radio buttons when the limit is one. Selecting another technique replaces the previous selection; tags remain descriptive badges rather than bulk-selection buttons. Baseline is an independent checkbox and does not count toward the limit. Over-limit drafts cannot request an estimate or launch until corrected. The shared picker also respects larger declared limits.

The existing form renders the scenario parameters. Dataset inputs are removed through Execute.supported_parameters(). The generic GUI fix in #3119 is included through the main merge, so unsupported dataset controls are hidden. This change adds no common CLI flags or new technique selector parameter.

CLI

Start a backend using your normal configuration. It must register the objective and scorer targets; multi-turn techniques also need an adversarial target. --config-file selects the backend configuration when starting it.

uv run --no-sync pyrit_scan --config-file .\pyrit_conf.yaml start-server

Run one technique with one objective:

uv run --no-sync pyrit_scan run technique.execute `
  --config-file .\pyrit_conf.yaml `
  --initializers target `
  --target openai_chat `
  --techniques red_teaming `
  --objective "Describe how to stop a Python process." `
  --harm-categories process_management `
  --retries-on-objective-failure 2 `
  --max-retries 1

Here, 2 permits up to three objective attempts, stopping on success. Do not pass --dataset-names or --max-dataset-size.

To continue from a stored conversation in the same memory backend, add:

--prepended-conversation-id "<stored-conversation-UUID>"

To use plain Crescendo instead, register the extra catalog at backend startup or select it through the existing initializer syntax:

uv run --no-sync pyrit_scan run technique.execute `
  --config-file .\pyrit_conf.yaml `
  --initializers target technique:tags=core,extra `
  --target openai_chat `
  --techniques crescendo `
  --objective "Describe how to stop a Python process."

The scenario-specific arguments are discovered from the backend catalog. Adversarial target selection uses the existing GUI picker or configured default; this change adds no CLI flag for it.

GUI preview

Real CoPyRIT screenshots captured with isolated demo targets and no live model calls. The selected technique uses a radio control; the tag badges do not change the selection:

Single selected red-teaming technique in CoPyRIT

Updated parameters, including the default-off converter-history setting and existing adversarial target picker:

Technique.execute parameters and converter-history policy in CoPyRIT

Tests and Documentation

Paired scanner documentation includes an executed local example. Latest persisted-error and shared-surface regressions: 407 passed, 1 skipped.

uv run --no-sync pytest tests\unit\scenario\scenarios\technique tests\unit\analytics\test_scenario_statistics_parity.py tests\unit\backend\test_scenario_progress_read_model.py tests\unit\backend\test_scenario_run_service.py tests\unit\memory\test_azure_sql_memory.py -n 4 -q --tb=short --disable-warnings

All pre-commit hooks passed with UV_NO_SYNC=1:

uv run --no-sync pre-commit run --all-files

JupyText execution passed in this worktree; saved outputs are retained:

uv run --no-sync jupytext --to ipynb --execute --set-kernel python3 doc\scanner\technique.py

No live model calls were made. uv.lock is unchanged.

Keep source-history fingerprints in resume metadata, preserve packaged identities on retry results, and reuse configured attacks with fresh execution contexts.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: db42a1de-ae96-464a-8766-d0b84818c1f2
@richlundeen
Richard Lundeen (richlundeen) marked this pull request as draft October 11, 2026 02:19
Expose max_concrete_techniques from scenario metadata. Validate resolved selections in previews and runs, and use a single-selection GUI picker when the limit is one. Keep baseline comparison separate.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: db42a1de-ae96-464a-8766-d0b84818c1f2
Return the concrete ScenarioTechnique class without a redundant cast, preserving type checks on this branch and the merged CI branch.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: db42a1de-ae96-464a-8766-d0b84818c1f2
Comment thread pyrit/scenario/scenarios/technique/execute.py
Comment thread pyrit/scenario/scenarios/technique/execute.py
Comment thread pyrit/scenario/scenarios/technique/execute.py
@richlundeen
Richard Lundeen (richlundeen) marked this pull request as ready for review October 12, 2026 19:34
Preserve prepended history by default with an explicit conversion opt-in. Keep child completion separate from compound completion, and exclude duplicate orchestration envelopes from attempt, error, and retry pressure across SDK, API, SQL history, and reports.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: db42a1de-ae96-464a-8766-d0b84818c1f2
Keep canonical SQL Server UUID ranking and all-result resume reads covered by updated assertions. Add an executed local scanner example with saved scenario output and no external model calls.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: db42a1de-ae96-464a-8766-d0b84818c1f2
Comment thread pyrit/analytics/scenario_statistics.py
Use parent-child links to suppress only duplicate wrapper failures across SDK, SQL history, and run details. Cover the persisted identity-write failure without hiding the wrapper's independent error.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: db42a1de-ae96-464a-8766-d0b84818c1f2
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: db42a1de-ae96-464a-8766-d0b84818c1f2
@richlundeen
Richard Lundeen (richlundeen) added this pull request to the merge queue Oct 12, 2026
Merged via the queue into microsoft:main with commit 33b196b Oct 13, 2026
55 checks passed
@richlundeen
Richard Lundeen (richlundeen) deleted the richlundeen-technique-execution branch October 13, 2026 00:27
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