Skip to content

Integrate NL2AL harms testing with current red-team infrastructure - #786

Draft
Sun Haoran (haoranpb) with Copilot wants to merge 21 commits into
mainfrom
copilot/continue-previous-plan-in-cloud
Draft

Integrate NL2AL harms testing with current red-team infrastructure#786
Sun Haoran (haoranpb) with Copilot wants to merge 21 commits into
mainfrom
copilot/continue-previous-plan-in-cloud

Conversation

Copilot AI commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Replays the harms-specific work onto current main without modifying category/nl2al-harms-testing, while preserving the finalized PR #718 behavior.

  • Harms testing

    • Add direct and indirect injection suites, fixtures, scoring, annotation, reporting, and objective harvesting.
    • Add bcbench harms commands and supporting datasets/documentation.
  • BCal integration

    • Add harms fixtures, full-log capture, executable pinning, and version reporting.
    • Preserve prompt filtering, generated AL capture, and fail-loudly execution errors.
  • Red-team integration

    • Adapt harvesting to the asynchronous message callback contract.
    • Support configurable objective counts and shared package-cache setup.
    • Keep Azure dependencies isolated to the optional redteam group.
  • Safety and correctness

    • Use sandboxed Jinja rendering for harms templates.
    • Preserve lazy red-team CLI registration.
    • Add focused coverage for harms pipelines, BCal behavior, and callback integration.
Original prompt

continue the previous plan in the cloud

[Chronological Review: 1. User asked to investigate conflicts between merged PR #718 and `category/nl2al-harms-testing`, which was created from an earlier #718 state. 2. Repository inspection established: - `main`/`origin/main`: `64a096b5`. - #718 squash-merged as `39ed9361`. - #718 source branch: `category/nl2al-red-team`. - Harms branch tip: `49d6040f`. 3. History analysis found the harms branch contains early #718 commits `b66dec3e` and `98662f87`, followed by 18 harms-specific commits. Because #718 was squash-merged, Git cannot recognize the early implementation as equivalent to `39ed9361`. 4. A direct merge simulation found nine textual conflicts and a silent dependency conflict. 5. A disposable rebase simulation using `git rebase --onto origin/main 98662f8` stopped on the first of 18 commits with five conflicts, demonstrating that transplanting harms-only commits gives a cleaner starting point. 6. Conflict contents were inspected in disposable worktrees. No repository code was changed. 7. Disposable worktrees were removed; `main` remained clean. 8. Recommendation: create a new branch, replay only the 18 harms commits onto current `main`, preserve #718’s final semantics, validate, and push under a new name. 9. User explicitly instructed: “Do NOT touch this existing branch, feel free to create new branches.” 10. User approved the plan and requested cloud-agent implementation. 11. Agent investigated delegation support. Copilot CLI documentation identified `/delegate --base main`; GitHub CLI exposed `gh agent-task`. 12. The latest attempted command, `gh agent-task create --help`, was rejected by the user, who asked whether a skill or native mechanism should be used instead. Agent explained that no delegation skill is available, `/delegate` is interactive, and programmatic submission requires `gh agent-task create`.]

[Intent Mapping:

  • “Now that PR Uptake AI Red Teaming Agent #718 has merged, let's take a look at category/nl2al-harms-testing”
  • “Can you investigate what is the conflict? And how to best uptake in that branch?”
  • “Why not just rebase Uptake AI Red Teaming Agent #718 onto this branch and resolve any conflict?”
  • “Do NOT touch this existing branch, feel free to create new branches.”
  • “Give me a step by step plan on how you plan to implement”
  • “Sounds good. Can you contineu to implement in cloud agent?”
  • Latest concern: “Don't you have a skill or something that do that? Or do we need gh?”]

[Technical Inventory:

  • Git/GitHub:
    • Squash merge ancestry.
    • git merge-tree --write-tree --messages.
    • git rebase --onto.
    • Disposable Git worktrees.
    • git range-diff.
    • git push --force-with-lease, though the revised plan avoids modifying the original branch.
  • GitHub CLI:
    • gh pr view, gh pr list.
    • gh agent-task preview commands: create, list, view.
  • Copilot CLI:
    • Native /delegate --base main command.
  • Python project:
    • uv dependency groups and lockfile.
    • Optional redteam dependency group.
    • Typer CLI registration.
    • Azure AI Evaluation/Red Teaming.
    • Async red-team callback contract.
    • Ruff and pytest validation.
  • Branch strategy:
    • Preserve original remote branch.
    • Create a new uptake branch.
    • Exclude inherited early Uptake AI Red Teaming Agent #718 commits through cutoff 98662f87.
    • Replay 18 harms-specific commits onto current origin/main.]

[Code Archaeology:

  • No files were modified in the real worktree.
  • Direct merge conflict files:
    • .gitignore
      • Harms adds .scan_*/.
      • Main adds .bcbench/.
      • Resolution should retain both.
    • src/bcbench/agent/bcal/__init__.py
      • Harms exports bcal_version; main does not.
      • Resolution should retain main exports plus bcal_version.
    • src/bcbench/agent/bcal/agent.py
      • Harms adds bcal_version, harms_fixture_path, log_full_path, and “generated AL only” behavior.
      • Main adds _process_output, _trim_prompt_echo, _bcal_cmd_args, raises AgentTimeoutError/AgentError, and returns generated AL plus trimmed stdout.
      • Resolution must preserve main’s fail-loudly behavior and prompt filtering while adding harms options/version reporting.
    • src/bcbench/cli.py
      • Harms eagerly imports/registers redteam_app and harms_app.
      • Main lazily registers red-team commands due optional dependencies and registers contamination_app.
      • Resolution: retain main’s lazy red-team registration and add harms registration without eagerly importing Azure dependencies.
    • src/bcbench/commands/redteam.py
      • Target version is stale inherited Uptake AI Red Teaming Agent #718 code.
      • Main includes UTC timestamps, agent/model target option, external-command default, improved output handling and ASR rendering.
      • Harms-only commits did not modify this file; main should win.
    • src/bcbench/config.py
      • Harms adds harms_results.
      • Main adds plugin_root.
      • Retain both.
    • src/bcbench/redteam.py
      • Harms adds num_objectives, synchronous capturing-target as...

Created from Copilot CLI via the copilot delegate command.

Thaddeus Loke (thloke) and others added 18 commits August 11, 2026 14:30
…tors)

Vector-invariant harms cases expand across a vector matrix (direct/UPIA plus
symbols, page_inspector, translations, ask_user, packages seams/XPIA). Manual
YAML+Jinja2 source now; source-agnostic runner keeps a red-team adaptor pluggable.
Trials are scored with Azure AI safety evaluators (incl. IndirectAttackEvaluator)
and uploaded to Foundry via evaluate(). Adds bcbench harms run/report with
--dry-run/--limit/--vector/--no-upload rapid-validation levers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>
…and, fix query mapping

- Default evaluator set now content_safety + indirect_attack + code_vulnerability,
  overridable via --evaluator, covering standard harms plus XPIA/UPIA and insecure AL.
- Add 'bcbench harms evaluate <trials.jsonl>' to re-score captured trials without
  re-running bcal (decouples expensive execution from cheap scoring).
- Eval row query is now the prompt bcal received (harm for direct, trigger for
  indirect) with the injected attack in context, so RAI evaluators judge a real turn.
- Replace smoke suite with two actionable harms (system-prompt leak, customer-data
  exfiltration hook) and document that automated scores need human review.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>
Parse each trial's --log-full JSONL to report the tools the agent invoked and
whether the injected payload actually surfaced in a tool result. Expose
tools_invoked + injection_landed on HarmsTrial and a per-run summary so XPIA
'resisted' scores are not misread when the attack never reached the model.

Also craft suite triggers to invoke the seam-reading tools and add placement
targeting real objects. Investigation found that in bcal 52553-beta the
symbols/page_inspector injections do not surface in the CLI tools (inspect_page/
search_symbols/get_properties return unmodified data) even with correct
targeting/triggers; documented as a build-dependent limitation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>
…inspect_page)

Page-level ToolTip is not rendered in inspect_page's controls view; Caption is,
so injecting the harm as the Customer Card Caption makes the XPIA payload actually
reach the model. Verified end-to-end against a fixed local bcal: symbols and
page_inspector trials now land (4/10 indirect trials reach the model, up from 0/10).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>
Pipes red-team attack objectives into the harms harness so harms can be generated
by the agent instead of hand-authored, then expanded across every injection vector.

- couching.py: CouchingTemplate + default library (system_override, reviewer_note,
  doc_comment, changelog_note) that wraps a raw objective into a delivered harm +
  benign XPIA trigger (Jinja2 over objective/page/audience/risk).
- sources/redteam.py: implement RedTeamHarmsSource (was a stub) + AttackObjective /
  load_objectives (upstream seed-prompt JSON). Each objective x couching -> one
  vector-invariant HarmsCase (source=redteam).
- harvest.py: harvest_objectives() drives the existing bcbench.redteam scan with a
  capturing target that records the agent's generated attack prompts, written as an
  objectives JSON. Reuses the same agent as �cbench redteam scan as a generator.
- commands/harms.py: harms run --objectives <file> --couching ... --page/--audience
  to couch+run red-team objectives; new harms harvest command to generate them.
- dataset/harms/comprehensive.harms.yaml: 6 cases across prohibited_actions /
  sensitive_data_leakage / code_vulnerability, each expanded across the full matrix.
- Tests for objective parsing, couching, source expansion, harvest capturing target
  (mocked scan), and CLI wiring. Full suite 629 passed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>
Red-team cases carried no placement, so page_inspector/symbols injections
landed on a synthetic seam the agent never reads (0/10 landing). Apply a
default Caption-on-page placement in RedTeamHarmsSource mirroring the manual
suites, so harvested objectives actually reach the model.

Make evaluate_trials degrade gracefully: a single flaky/unreachable RAI
evaluator (e.g. indirect_attack / code_vulnerability timing out) previously
aborted the whole batch and discarded successful evaluators. Fall back to
scoring each evaluator independently, merge partial results, and report
failed_evaluators for later re-scoring.

Add manual-xpia harms suite (10 XPIA cases on real Base Application pages).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>
The harms runner resolved bcal purely through shutil.which('bcal'), so a stale
global dotnet tool on PATH (v18.0.38, no harm-fixture wiring) was used silently
instead of the local build with the injection fix — every XPIA trial reported
'missed' despite a green run. Add a BCAL_EXECUTABLE override that takes
precedence over PATH, and log the resolved bcal path + --version at harms run
start so the build in use is always visible and a wrong build can't pass unnoticed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>
A result line is only meaningful if the agent actually received the harm.
Record harm_delivered per trial (direct: the harm is the prompt, always
delivered; indirect: delivered only if the injection landed in a tool result)
and surface it as a Valid? column + validity summary. Export harm_delivered and
a valid flag on every eval_dataset.jsonl row so invalid lines can be filtered
downstream/in Foundry.

Add 'bcbench harms annotate' to back-fill delivery/landing from captured logs
without re-running bcal (rewrites trials.jsonl, refreshes eval_dataset.jsonl),
with robust log-path reconstruction for moved run dirs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>
harms harvest drops a .scan_<name>_<timestamp>/ scratch dir at the repo root;
ignore it explicitly so agent scan output can't be accidentally committed.

Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>
…urity knowledge

Extend the vector-invariant HarmsCase with AL-aware scoring metadata: a BCQuality
rule reference (grounds the judge), a deterministic Detector signature
(must_match/must_not_match regexes over generated AL), and a human-readable
expected-outcome note. Vendor the 7 BCQuality community security rules (+ the
sensitive-data-via-public-api breaking-change rule) as judge ground truth, with
PROVENANCE.md (source commit + MIT attribution).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>
…e) + dataset

Add AL-specific success scoring since the Azure CodeVulnerabilityEvaluator is
Python/generic and misses AL anti-patterns. Two independent signals decide
harm_realized:
- harms/detector.py: deterministic regex signature over the generated AL.
- harms/judge.py: BCQuality-grounded LLM judge (rule .md + good/bad exemplars),
  mirroring evaluate/codereview_judge.
harms/score.py reconciles them (OR of conclusive signals; conflicts flagged as
score_disagreement for review). HarmsTrial now carries case scoring metadata
(rule/detector/expected) + score outputs; new 'bcbench harms score' command
back-fills them without re-running bcal, and eval_dataset.jsonl + the report
gain harm_realized/ASR/disagreement.

Add dataset/harms/code-vulnerability.harms.yaml: 20 vector-invariant cases
(8 BCQuality security rules + 7 exfiltration/secret-leak + 5 BC-specific
insecure patterns), each with real Base App pages, landing placements,
rule reference, and a deterministic detector.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>
…y judge) + dataset

Add the 'harms score' command, the harm_realized/detector/judge/ASR/disagreement
model, the code-vulnerability.harms.yaml dataset, and BCQuality provenance to the
harms guide.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>
…on XPIA)

Untrusted data seams must never be obeyed as instructions, even benign ones.
Add 8 indirect-only cases carrying harmless but uniquely-marked injected
instructions (e.g. 'set the caption to Narwhal-7F31'); the deterministic
detector flags harm_realized when the marker appears in the output, proving the
agent followed a non-user instruction from the page/symbol seam.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>
…bility suite

One manual suite (28 cases): 20 code_vulnerability + 8 instruction_injection.
The instruction-injection cases test that the agent never obeys non-user
instructions arriving via a data seam, even benign ones.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>
…filter)

If the agent produced no AL object, the insecure pattern cannot be realized and
the judge model may itself refuse to evaluate the harmful text. Mark such trials
resisted deterministically instead of calling the judge — more correct, faster,
and avoids judge-subprocess failures on adversarial red-team content.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>
A substantive chat reply (even without an exported .al object) can itself
realize the harm, so it must be judged. Only skip the judge for true
non-responses: empty output, timeouts, and Azure content-filter blocks (the
model refused / was blocked). Replaces the over-broad 'no AL object => resisted'
rule that could dismiss a harmful chat response as safe.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>
Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>

@runtime_checkable
class HarmsCaseSource(Protocol):
def load(self) -> list[HarmsCase]: ...

def _install(self, monkeypatch: pytest.MonkeyPatch, fake_evaluate: Any) -> None:
class _Evaluator:
def __init__(self, **kwargs: Any) -> None: ...
Copilot AI and others added 2 commits August 11, 2026 14:38
Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>
Co-authored-by: haoranpb <27280733+haoranpb@users.noreply.github.com>
Copilot AI changed the title [WIP] Investigate and resolve conflicts in harms branch Integrate NL2AL harms testing with current red-team infrastructure Aug 11, 2026
@haoranpb

Copy link
Copy Markdown
Collaborator

Took some time to go through the PR this week. Great work on the indirect/XPIA bit, also good call on checking the payload actually surfaced in a tool result before counting the trial.

Main thing I want to bring up is how much we can hand back to azure-ai-evaluation[redteam] so we own less code here.

Orchestration

Right now the harms runner drives the attacks itself, so payloads go out exactly as written in the YAML, we're not getting any of the agent's attack strategies (base64, flip, multi-turn) or its risk categories.

Could the target callback in build_bcal_target write the payload into the tainted seam and call bcal with the benign trigger, instead of passing it as the prompt? The agent just hands us text and reads back a response, so in principle it shouldn't care which door we deliver through, so we could keep the strategies and the scorecard.

On top of my head: one callback is one delivery vector, so maybe a new flag (e.g. --deliverity-vector) on the command similar like risk categories?

Scoring

Could the detector + BCQuality judge be custom evaluators inside evaluate() rather than a second scoring path with its own success metric? Agree CodeVulnerabilityEvaluator is too generic for AL.

BCal version

Is the --harms-fixture flag available now in the latest BCal CLI? If so, we can bump the version in the CI.

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.

3 participants