Skip to content

Latest commit

 

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

REMORA SecOps Reference Profile

CI CodeQL

A small, runnable reference implementation of governed security automation: AI agents may investigate freely and propose response actions, and execution stays constrained by Policy as Code, scoped to one authorized call, and confirmed against authoritative state afterwards.

It is built on REMORA, an assurance engine for tool-using agents. The engine is a dependency, not a copy: the decisions, the execution authority and the argument binding in this repository are the real implementation running in-process. Remove a control and the tests here fail.

The problem it addresses

User intent
    -> Agent proposes an action
    -> The action is authorized
    -> time passes, state changes, arguments move
    -> something executes
    -> Was it still exactly the authorized action?
    -> Did it actually have the intended effect?

Most agent tooling answers the first question and stops. In security response the last two are where the cost is: an approval redeemed for a different target, or an incident record that says a host was contained when it was not.

Run it

python -m pip install git+https://github.com/darklordVirtual/REMORA-research
python -m pip install -e .

python -m scenarios.suspicious_login
python -m scenarios.endpoint_isolation
python -m scenarios.threat_hunt
python -m scenarios.purple_team_validation
python -m scenarios.ot_engineering_workstation

python -m pytest tests/ -q

No API keys, no network, no services. The scenarios print incident-shaped records.

What it prints

Two excerpts from an actual run, in the form an incident record would keep them.

Confidence is not authority. The agent has correlated two independent signals and is at 0.97, and the engine still declines to act alone:

-- The agent proposes disabling the account.

  action        : disable_account(user_principal_name='erik.hansen@example.no')
  engine verdict: ESCALATE
                  schema_unverified_verify
                  evidence_insufficient
  policy        : within_scope
                  All containment preconditions satisfied.
  OUTCOME       : HELD_FOR_APPROVAL

-- The incident lead approves that exact call.

  action        : disable_account(user_principal_name='erik.hansen@example.no')
  engine verdict: HUMAN_APPROVED
  approved by   : ir.lead
  authority     : one-shot lease 59095483
  dispatch      : executed
  effect        : VERIFIED
                  erik.hansen@example.no reads back as disabled.
  OUTCOME       : EXECUTED

Accepted is not done. The EDR takes the isolation request, returns a ticket, and the host stays on the network:

-- WS-9002: the EDR accepts the request and nothing is contained.

  action        : isolate_endpoint(endpoint_id='WS-9002')
  approved by   : ir.lead
  authority     : one-shot lease c1da8209
  dispatch      : executed
  effect        : MISMATCH
                  WS-9002 accepted isolation ticket ISO-WS-9002-162702 and
                  still reads back as connected. The request succeeded; the
                  containment did not.
  OUTCOME       : EXECUTED_EFFECT_UNCONFIRMED

A platform that reads an accepted request as a completed containment writes "endpoint isolated" into the incident record and closes the task. The host keeps its network path, and nobody looks again, because the record says it is handled.

What the five scenarios show

suspicious_login works an impossible-travel detection. The agent reads the identity log and endpoint telemetry autonomously, correlates two independent signals, and proposes disabling the account. With a trust score of 0.97 the engine returns ESCALATE. High model confidence does not become authority: a critical identity action is held for a person because of what it is, not because of how sure the agent was. A named approver then authorizes one exact call, and the account is confirmed disabled by reading it back.

endpoint_isolation runs three isolations that end three ways. One lands and is confirmed. One is accepted by the EDR, returns a ticket, and leaves the host on the network. One is a domain controller outside the responder's scope and never reaches the EDR. The middle case is the interesting one, and it is the reason EXECUTED and EXECUTED_EFFECT_UNCONFIRMED are different words.

threat_hunt is hypothesis generation with no hands. The hunting agent holds telemetry_read and nothing else, so a containment proposal from inside a hunt is refused on the scope it does not hold, before the strength of the finding is considered. There is no prompt asking the agent not to contain things, because a prompt is a request and this is a boundary.

purple_team_validation emulates MITRE ATT&CK techniques and verifies detection from telemetry rather than assuming it from a completed emulation. Two of three are detected; the third is recorded as a coverage gap. An exercise that asked whether the emulation finished would have reported three of three.

ot_engineering_workstation is the case where the IT reflex is wrong. An engineering workstation in the level-3 DMZ shows remote execution followed by PLC programming software launched outside a change window. Isolating the host outright would sever the process-control path and turn an intrusion response into a process trip, so the permitted containment cuts the IT-facing path only, and the read-back confirms both halves: IT path down, control path up. The agent then proposes correcting the PLC logic, and is refused for a reason no amount of evidence would move. This broker holds no process-control engineering scope, and no executor is registered for that tool at all. A safety-instrumented device is refused even for an identity that does hold the scope, because an SIS change runs through management of change rather than through incident response.

The controls, and the tests that attack them

Control Attack it survives Test
Registry-resolved risk metadata An agent describing its own action as low risk test_an_unregistered_tool_is_refused_rather_than_defaulted
Risk-tier routing High model confidence used as authority test_high_model_confidence_does_not_authorize_a_critical_action
Authority scopes, reads included A caller with no scopes querying the SIEM test_no_scope_cannot_read_telemetry
Authority scopes A read-only hunting agent reaching a containment tool test_a_read_scope_cannot_reach_a_containment_action
An approval answers an assessment A signed approval for an action nobody assessed test_an_approval_cannot_exist_without_a_proposal
Approval-proposal correspondence An approval for proposal A authorizing proposal B test_an_approval_for_one_proposal_cannot_authorize_another
Single-use approval The same signature presented twice test_an_approval_is_single_use
Exact-call binding The target changing between approval and execution test_the_target_cannot_change_between_approval_and_execution
Single-use authority An approval replayed at the dispatcher test_a_spent_authorization_cannot_be_redeemed_twice
Time-bounded authority An old approval used as a standing one test_an_expired_authorization_is_refused
Scope containment A /8 block approved under a /24 authorization test_a_widened_firewall_block_is_refused_by_scope
Effect verification An accepted request that never applied test_an_accepted_request_that_did_not_apply_is_not_reported_as_success
No process-control capability A security agent proposing a PLC write test_a_security_responder_cannot_write_to_a_plc
Safety-instrumented protection An SIS change proposed by an engineer who holds the scope test_a_safety_instrumented_device_is_refused_even_with_the_scope
OT containment stays off the process A containment that also severed the control path test_a_severed_control_path_is_reported_as_a_mismatch

The tampering tests substitute targets that are inside the responder's authorized set on purpose. A refusal that only worked because the second target was forbidden would prove the scope check, not the binding.

Shape of the flow

Telemetry
    |
Detection
    |
Agent investigation            <- read scope only, runs autonomously
    |
REMORA assessment              <- ACCEPT / VERIFY / ABSTAIN / ESCALATE
    |
Containment policy             <- organisation scope; can only narrow
    |
Human approval                 <- answers one open, assessed proposal
    |
One-shot execution authority   <- bound to this exact call, single use
    |
Policy enforcement point       <- re-verifies the binding before running
    |
Security action executor       <- holds the credential; the agent never does
    |
Effect verification            <- authoritative read-back
    |
Evidence

architecture/threat-response-flow.md walks each stage and says what it does not cover.

Layout

secops/registry.py     governance metadata per security action
secops/policy.py       Policy as Code: scopes, severity, corroboration, blast radius
secops/authority.py    assessment -> policy -> approval -> authority -> dispatch -> verification
secops/wiring.py       executors paired with the readers that confirm them
tools/estate.py        a small mutable IT and OT estate, so read-back has something to read
scenarios/             five runnable workflows
tests/                 thirty-six tests, most of them written as attacks

Security scanning

CodeQL runs on every push and pull request with the security-and-quality pack, and weekly so a new query reaches this code without waiting for a commit. GitHub Actions are pinned to commit SHAs rather than tags.

The first run raised two findings and both were acted on. An unused import was removed. Clear-text logging of identity telemetry in the login scenario was mitigated: the data there is fabricated so nothing leaked, but the finding was right about the shape, since that output is written to be pasted into a ticket. Source addresses are now masked to the network, which the impossible-travel narrative did not need anyway.

The query still flags the line, because the timestamp, country and MFA state that remain come from the same record. That residue is dismissed in the repository's code-scanning view with the reason written out rather than left open, since those three fields are precisely what the narrative exists to show. Open alerts: none.

Scope and limits

This is a reference profile, not a product, and not a security control anyone should deploy as-is.

The estate in tools/estate.py is simulated. Nothing here talks to a real EDR, SIEM or identity provider, and the integration surface is the part a real deployment has to build.

Responder authority is a dataclass. In a deployment it comes from the authenticated identity at the transport boundary, never from the request body.

Credential custody separation is modelled in-process. The agent's code path has no route to the tool callables, so calling one directly is a deviation someone has to write rather than a default. A Python underscore prefix is a convention and not a security boundary: anything in this interpreter can reach broker._dispatcher, and one test deliberately does. A production deployment requires the executor and the credential path to sit behind an independently enforced process, service or network boundary. The structure here is the design; that boundary is what makes it a control.

The properties demonstrated are the ones the tests assert and no more. In particular this repository shows that an authorization cannot be transferred to a different call, replayed, or claimed as an effect without a read-back. It does not show that the surrounding system cannot be bypassed by other means: the executor holds real credentials by design and can reach the downstream system directly, which is a deployment boundary rather than a code one.

Licence

Apache-2.0. The REMORA engine it depends on is source-available under BUSL-1.1; this profile is separately licensed so the patterns, the ToolSpec shape and the adversarial tests can be read and reused freely.

About

Governed security automation for detection, investigation and response: AI agents may propose actions, execution stays policy-constrained, scoped and verified. A REMORA reference profile.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages