Skip to content

Feat: AIAC Phase 1 — functional agent, UC-1 service onboarding + integration tests - #710

Open
oblinder wants to merge 286 commits into
rossoctl:mainfrom
s-and-p-team:aiac-phase1-pr
Open

Feat: AIAC Phase 1 — functional agent, UC-1 service onboarding + integration tests#710
oblinder wants to merge 286 commits into
rossoctl:mainfrom
s-and-p-team:aiac-phase1-pr

Conversation

@oblinder

Copy link
Copy Markdown
Contributor

Summary

Delivers Phase 1 of the AI-based Access Control (AIAC) Kagenti extension and the UC-1 (service onboarding) use case, addressing #645.

AIAC translates a natural-language access-control policy into concrete PDP (OPA) rules for AI agents on Kubernetes. This PR lands the full Phase 1 stack plus the integration-test ladder that proves UC-1 end-to-end by evaluating the generated Rego (Phase 1 explicitly defers live enforcement/traffic).

Scope note: This PR covers the implementation, the integration-test suite, and the demo scenario workloads (github-agent + simplified github-tool) that the integration tests drive. The integration suite is not itself a demo. The runnable demo flow / walkthrough will follow in a separate PR.

What's included

Core services & library (aiac/src/aiac/, all Python 3.12, idp / pdp / policy namespace split):

  • IdP Configuration Service (aiac.idp.configuration) — Keycloak Admin REST proxy exposing subjects/roles/services/scopes with per-service enrichment; stamps the aiac.managed provisioning marker.
  • PDP Policy Writer (OPA) — filesystem-stub writer emitting {slug}.inbound.rego + {slug}.outbound.rego (the Phase 1 Interface-Pod deployment).
  • Policy Store — SQLite-backed FastAPI StatefulSet holding the authoritative ServicePolicyModel/AgentPolicyModel state; SPM-centric.
  • Policy Computation Engine (aiac.policy.computation) — in-process library; single compute_and_apply(rules) entry point, additive merge with drift-GC + authoritative decommission() offboard.
  • Policy model (aiac.policy.model) — PolicyRule, ServicePolicyModel, AgentPolicyModel, PolicyModel.

AIAC Agent (aiac.agent) — FastAPI Controller dispatching /apply/* to UC sub-agents:

  • UC-1 Service Onboarding orchestrator — classifies a service from its kagenti.io/type label, discovers capabilities (AgentCard for agents; MCP tools/list for tools), provisions roles/scopes into Keycloak, runs the Policy Rules Builder (PRB, LLM temperature=0), and calls the PCE.
  • Policy-update (build/rebuild) and role-update sub-agents; service-offboarding (UC-4) route.
  • Per-scope two-gate AND outbound gate with capability-match (RC-B); user gate discriminates.

Demo scenario workloads (aiac/demo/) — github-agent (A2A, source + issue skills) and a simplified github-tool MCP server, with their k8s manifests. These are the workloads onboarded by the integration tests.

Kubernetes manifests (aiac/k8s/) — Interface Pod, Policy Store StatefulSet, Agent deployment (Phase 1).

Specs & issue trackingaiac/docs/specs/ (master PRD + per-component + integration-test specs) and aiac/docs/issues/ (local markdown issue tracker + handoffs).

Testing

  • Unit suite is green: .venv/bin/pytest test/ -m "not integration" (no --ignore flags needed after the Wave 3 SPM migration).
  • Integration-test ladder (aiac/test/integration/, @pytest.mark.integration, opa-gated) — uc1-onboarding-pipeline: drives the real UC-1 agent (POST /apply/service/{id}) against really-deployed workloads and asserts the generated Rego with opa eval, computing verdicts from scenario_uc1.py (the oracle):
    • Rung 1 (agent only), Rung 2 (agent → tool), Rung 3 (tool → agent, asserts onboarding-order-independence vs rung 2). Rung 4 (two policies) deferred.
    • Rung 1 verified fully green live; RC-A (stale store) and RC-B (outbound gate) defects surfaced by the first live run are resolved on this branch (store clear-all endpoint + per-scope two-gate AND capability-match).
  • Also ships policy-pipeline and pdp-policy-writer standalone launcher integration tests.

Out of scope (Phase 1)

Live trigger path / NATS Event Broker, live enforcement & token exchange, writing Rego to the AuthorizationPolicy CR, the RAG knowledge base, and UC-2/UC-3 — all deferred to later phases per the PRD.

Notes for reviewers

  • Almost all changes are under aiac/; the only root change is a new pyrightconfig.json.
  • Specs of record: aiac/docs/specs/PRD.md and aiac/docs/specs/integration-test/uc1-onboarding-pipeline.md.

Addresses #645 (Phase 1 implementation + integration tests; runnable demo flow to follow in a separate PR).

Assisted-By: Claude (Anthropic AI) noreply@anthropic.com

oblinder and others added 30 commits June 7, 2026 16:32
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
…lidation

PDP Configuration Service and PDP Policy Service are co-located in a
single PDP Interface Pod, reducing the Kubernetes Pod count from six to
four and the manifest file count from six to four.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
PDP Configuration Service: 7070 → 7071
PDP Policy Service: 7073 → 7072

Matches the port assignments from the PDP Interface Pod redesign.

Updated sources and tests:
- src/aiac/pdp/library/{configuration,policy}.py — fallback URL defaults
- src/aiac/pdp/service/{configuration,policy}/keycloak/Dockerfile — EXPOSE and --port
- test/pdp/library/{test_configuration,test_policy}.py — BASE constant and fallback assertions
- test/pdp/library/show_keycloak_data.py — doc-comment default URL
- src/aiac/pdp/library/.env, test/pdp/library/.env — renamed AC_SERVICE_URL → AIAC_PDP_CONFIG_URL and updated port (gitignored; on-disk only)

Updated deployment docs:
- k8s/pdp-configuration-keycloak-pod.yaml — containerPort updated; dev-only header comment added
- k8s/install-pdp-config-service.md — port references updated; dev-only callout added; title clarified

In production both PDP services run as containers in the PDP Interface Pod
defined in pdp-interface-deployment.yaml (issue 4.2).

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Add step-by-step event and call flow diagrams for UC-1a (service
on-boarding), UC-1b (realm role on-boarding), UC-2a (incremental policy
update), and UC-2b (full rebuild) under a new Call Flows subsection in
the Technical Addendum.

Restructure the Technical Addendum: promote Key Interface Surface to a
named subsection (###) and demote the five endpoint description
subsections to ####, keeping Call Flows at the same ### level.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Incorporated Abstract, Problem Description, Problem Solution, Major
Use-Cases, Component Architecture overview, and Kagenti/Keycloak/OPA
Interfaces sections from ARCHITECTURE-SUMMARY.md into PRD.md.

Restructured PRD layout:
- Removed redundant Purpose section; promoted its design content to
  standalone Problem Description, Problem Solution, and Design
  Principles sections
- Added Major Use-Cases (UC-1 through UC-4) as a top-level section
- Added Component Summary table and data-flow diagram to Architecture
  Overview
- Added Kagenti / Keycloak / OPA Interfaces section
- Grouped component sections 7.1–7.8 under a single AIAC System
  Components parent section
- Renumbered all sections 1–10 in document order

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
… semantics

Replace generic (read config)/(set policy) labels in both the
architecture summary and PRD diagrams with descriptive italic call
text matching the PDP interface endpoints.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Technology-neutral greenfield evaluation of Red Hat AMQ Broker, AMQ
Streams, and AMQ Interconnect against the ten functional requirements
derived from the AIAC PRD. AMQ Broker satisfies all requirements;
selection guidance covers conditions where it is preferred over the
PRD's NATS JetStream choice.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
…l Addendum

Move Call Flows before Short-Term Objectives for a more logical reading
order (structure before goals). Remove the Technical Addendum wrapper,
promoting Call Flows to a top-level section. Fix missing blank line before
the NATS JetStream interface divider.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
Reshape models, refactor Configuration and Policy to stateful class APIs
based on design decisions from grilling session.

Models:
- Remove Assignments and Permission
- Role: drop clientRole; add childRoles (list[Role]), mappedScopes (list[Scope])
- Subject: add roles (list[Role])
- Service: drop clientId, protocol, publicClient; add type (Agent/Tool), roles, scopes
- Scope: drop protocol
- Definition order: Subject → Role → Service → Scope with model_rebuild() calls

Configuration: refactor from module-level functions to stateful Configuration
class with for_realm() factory; drop get_subject_assignments,
get_service_permissions, get_role_composites

Policy: refactor from module-level functions to stateful Policy class with
for_realm() factory; drop all composite/permission functions; rename
create_service_scope → create_scope

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
… PRD

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
…ients

Reshape aiac.pdp.library to match the redesigned PRD (issues 1.6, 2.9,
2.10). Implementation done via TDD red-green-refactor cycles.

Models (issue 1.6):
- Remove Assignments, Permission; remove clientRole from Role; remove
  clientId/protocol/publicClient from Service; remove protocol from Scope
- Add Service.type: Literal["Agent","Tool"] | None
- Add nested roles/scopes lists to Subject, Role, Service
- Add childRoles/mappedScopes to Role; call model_rebuild() for forward refs

Configuration (issue 2.9):
- Rewrite module-level functions as Configuration class with for_realm()
  classmethod factory; reads AIAC_PDP_CONFIG_URL env var with fallback
- get_subjects / get_roles / get_services / get_scopes return typed lists

Policy (issue 2.10):
- Rewrite as Policy class with for_realm() factory; reads AIAC_PDP_POLICY_URL
- create_scope POSTs to /services/{id}/scopes and returns Scope

Supporting changes:
- Empty pdp/__init__.py and pdp/library/__init__.py (no re-exports at pkg root)
- Adapt read_api_from_config to new model shapes; stub out-of-scope functions
  (get_subject_assignments, get_service_permissions, get_role_composites)
  pending issues 3.5 / 3.7–3.11
- Rewrite test_models.py (31 tests), test_configuration.py (15 tests),
  test_policy.py (8 tests) — all 54 pass
- Update show_keycloak_data.py to the new class-based API; confirmed green
  against live PDP config service in kind-kagenti cluster

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
…dule

Scopes are configuration entities, not policy entities. The library split
is by domain: configuration (subjects/roles/services/scopes read+write)
vs policy (composite role mappings / Rego rules). Move create_scope and
its HTTP endpoint from AIAC_PDP_POLICY_URL to AIAC_PDP_CONFIG_URL.

Update PRD.md to remove the read-only characterisation of the PDP
Configuration Service and the configuration library module.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
…d}/scopes endpoint

- Add `Configuration.create_scope(service_id, scope_name, description)` to
  `aiac.pdp.library.configuration` — POSTs to the config service and returns
  a typed `Scope` instance
- Strip `Policy` to a skeleton (`__init__` + `for_realm` only); scope creation
  belongs to `Configuration`, not `Policy`
- Add `POST /services/{service_id}/scopes` to the PDP Configuration Keycloak
  service: creates a realm-level client scope and assigns it as a default scope
  to the given service in a single operation (201 on success, 502 on
  KeycloakError)
- Add `pydantic` to the configuration service `requirements.txt`
- Update PRD (`pdp-configuration-service.md`) with the new endpoint spec
- Full TDD coverage: 93 unit tests pass

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Replace the atomic `create_scope(service_id, scope_name, description)`
with two separate methods: `create_scope` (realm-level creation) and
`map_scope_to_service` (assigns existing scope to a service, returns
refreshed Service). Add matching `create_role` / `map_role_to_service`
methods for realm-level roles.

Update the PDP Configuration Service PRD accordingly: remove the old
atomic `POST /services/{id}/scopes`, add `POST /scopes`, `POST /roles`,
`POST /services/{id}/scopes/{scope_id}`, `POST /services/{id}/roles/{role_id}`,
and `GET /services/{id}` endpoints with Keycloak Admin API mappings and
409/502 response semantics.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Add 5 new write endpoints to the PDP Configuration Service (issue 1.9):
- GET /services/{service_id}
- POST /scopes (creates realm-level scope with protocol openid-connect)
- POST /services/{id}/scopes/{scope_id} (assigns scope as default)
- POST /roles (creates realm-level role)
- POST /services/{id}/roles/{role_id} (assigns role to service scope mappings)
All endpoints return 409 on conflict and 502 on KeycloakError.

Update Configuration library client (issue 2.9) to replace the old
create_scope(service_id, ...) with four new write methods:
- create_scope(scope_name, scope_description) → POST /scopes
- map_scope_to_service(service, scope) → POST + GET re-fetch
- create_role(role_name, role_description) → POST /roles
- map_role_to_service(service, role) → POST + GET re-fetch

Add unit tests for all 5 service endpoints (issue 2.11) and update
library configuration tests to cover the new write methods (issue 3.13).

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
Extract Service Onboarding, Policy Update, and Role Update (formerly
Realm Roles) into dedicated sub-PRDs under components/aiac-agent/.
Trim aiac-agent.md to shared infrastructure only (NATS Consumer,
Controller, Shared Module, Validate Node, Config, Error Handling).
Add scoped architecture diagrams to each UC sub-PRD. Rename Realm
Roles orchestrator to Role Update across all PRD documents.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
aiac.apply.build → aiac.apply.policy.build
POST /apply/build → POST /apply/policy/build
POST /apply/rebuild → POST /apply/policy/rebuild

Updated across uc2-policy-update.md, aiac-agent.md, ARCHITECTURE-SUMMARY.md,
PRD.md, event-broker.md, and rag-ingest-service.md.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
All AIAC components (Agent, Event Broker, PDP Interface, RAG) now use
the domain term 'role'. Keycloak-facing services translate to the
Keycloak 'realm role' concept internally at the boundary.

- NATS subject: aiac.apply.realm-role.{id} → aiac.apply.role.{id}
- HTTP path: /apply/realm-role/{id} → /apply/role/{id}
- Python dirs: realm_roles/ → roles/, realm_role/ → role/
- Prose, Mermaid labels, section headings updated across 13 files
- python-keycloak method names and Keycloak Admin API URLs unchanged

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
…o aiac

Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
oblinder added 14 commits July 27, 2026 18:54
The outbound Rego package emitted an agent_scopes list that no
outbound rule referenced -- agent_scopes is the inbound audience
gate consumed by subject_ok/source_ok, not part of outbound's
subject_ok/target_ok logic. Remove it from generate_outbound_rego
only; generate_inbound_rego is unchanged.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
…_subjects()

Replaces name-based exclusion sourced from get_service()/get_roles() with
ownership-based exclusion (role id / scope.serviceId) sourced from
get_services() (correct kind/ownership) plus get_subjects() (membership-
derived user roles), aligning the builder with the PCE's worldview.

Fixes I1 (self-reference: focus service's own role leaking into its own
scope's candidates) and I2 (role-kind erasure: agent-held roles rendering
as kind=User instead of kind=Agent).

Updates docs/specs/components/aiac-agent.md and
docs/specs/components/aiac-agent/uc1-service-onboarding.md to match.
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
…ontract

Replace get_service/get_roles name-based exclusion mocks with
get_services/get_scopes/get_subjects, matching ServicePolicyBuilder.build's
ownership-based (role id / scope.serviceId) candidate selection. Add tests
for ownership-beats-membership, kind routing (Agent vs User), non-aiac.managed
scope drop, and missing-focus 404 handling.
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
The builder's focus-service lookup matched the trigger id against
Service.serviceId (the human-readable Keycloak clientId), but the
/apply/service/{id} route is keyed on the internal client UUID
(Service.id) -- a clientId can be a slash-bearing SPIFFE URI the
single-segment route cannot carry. The two id shapes never matched,
so every onboard raised HTTP 404 "service not found in IdP catalog"
(regression introduced when the builder switched from get_service(id)
to scanning get_services()).

Match on Service.id instead. Add a regression test with distinct
id/serviceId values (the prior _service() fixture conflated them,
which is why the mismatch went uncaught); extend the helper with a
`ref` param for the clientId.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
The Service Policy Builder focus is resolved from get_services() by
id (the Keycloak internal client UUID the /apply/service/{id} route and
Trigger.entity_id carry), not by serviceId/clientId (which may be a
slash-bearing SPIFFE URI). Sync the uc1-service-onboarding spec, the
provision-state comment, and the builder docstring to this contract.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
The archived onboarding.old/policy subtree is no longer part of the
build, so remove its ruff extend-exclude entry and its pytest
pythonpath entry.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Add DELETE /policy/services (clear-all) to the Policy Store Service and a
clear_service_policies() wrapper in the store library. The store's SQLite
lives on a StatefulSet PV that survives image redeploys, and UC-1 onboarding
appends with override=False, so pre-fix cruft accumulated across runs and the
PCE replayed it into every regenerated Rego (issue 6.3 RC-A).

Wire a per-run store clear into the UC-1 integration harness
(uc1_onboard.py onboarded_stack), alongside the existing Keycloak
cleanup_provisioned, so each run derives its Rego from only the edges it
onboarded. The clear is best-effort via a port-forward + DELETE, keeping the
harness free of any aiac import.

Verified live: clearing the deployed store (53 stale edges -> 204 -> 404) and
re-running rung 2 produces clean inbound Rego (I1 view-profile, I2 dups /
self-reference, and the git-issue-agent-aud pollution all gone; SPM edge count
53 -> 3+5). Rung 2/3 outbound stays red by design pending the PCE<->UC-1
outbound-gate spec decision (RC-B, deferred).

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
…d UC-1 specs

Reconcile the UC-1 outbound-gate specs with the policy-model owner's RC-B
decision (Handoff 09): outbound access is a per-scope two-gate AND — a
requested scope is allowed iff both the user role and the agent's own
per-skill operator role reach it — with the capability gate decided by the
PRB from descriptions (capability-match). Range is tool union other agent.

- policy-computation-engine.md: generalize the outbound-subject gate range
  to tool-or-agent and clarify the directional-relevance rule is a
  derivation-layer property, not a claim the user gate is empty.
- uc1-onboarding-pipeline.md: replace the degenerate/user-gate-only
  narrative with the per-scope two-gate AND + capability-matched agent gate;
  provision one operator role per skill in place of the generic agent role.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Resolve RC-B of issue 6.3: outbound access is now a per-scope AND
where a requested scope (input.function_name) is allowed iff BOTH the
user (subject) role and the agent's own operator role reach that scope.

- provision: per-skill operator roles so the agent's capability gate is
  scope-specific rather than a single generic {workload}.agent role
- rego writer: per-scope AND on input.function_name across the subject
  and capability gates; generalized rubric
- builder: source other-service scopes from get_services() (owner-bearing
  serviceId) instead of the global get_scopes() catalog, whose empty
  serviceId broke self-exclusion and routed rules to SPM("") (422)
- rename emitted outbound map outbound_subject_role_scopes ->
  subject_role_scopes, paralleling the sibling agent_role_scopes
- un-degenerate the UC-1 integration contract; update probes, specs,
  and writer/PCE unit tests to match

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
…ue_operations)

Rename the 5.3 policy-pipeline scenario's github-agent client roles from
source-operator / issues-operator to source_operations / issue_operations,
matching exactly the per-skill operator roles UC-1 provisioning emits from
the AgentCard skill ids.

- scenario.py: AGENT_ROLES keys + OUTBOUND_PAIRS; rewrite the plural-naming
  NOTE — the _operations suffix (not the actor-noun operator) sidesteps the
  same PRB-auditor actor-confusion the old plural issues-operator dodged
- policy.explicit.md: the two "Agent roles -> tool operations" grants
- policy-pipeline.md spec + demo docs (github-agent.md, github_agent README)

The PRB-auditor collision regression (test_auditor_dimension_integration.py,
issue 3.20) deliberately keeps the singular issue-operator and is untouched.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Add the authoritative service-offboard path that closes drift "case 3"
(onboard followed by offboard): reconcile's catalog-anchored drift-GC
cannot prune a decommissioned service because it is gone from
get_services(), so its own SPM inbound edges, its outbound footprint on
other SPMs, and — if an agent — its APM/Rego would linger indefinitely.

- engine: decommission(service_id) + _decommission — purge SPM(X), strip
  X's outbound edges from every other SPM, delete APM(X) if X was an
  agent, and re-derive every still-live agent whose policy changed.
  Keyed by clientId (SPM key), not the Keycloak UUID, since an offboarded
  client cannot be resolved via get_services(). Factored spm()/is_agent()
  into _spm_cache, shared with _run. Preserves the single-get_services()
  read and per-agent partial-upsert invariants.
- offboarding UC: thin offboard_service stub + POST /apply/offboard/
  {service_id:path} route dispatching to decommission; bypasses
  compute_and_apply. Re-export decommission from aiac.policy.computation.
- tests: two case-3 PCE cases (tool offboard strands no edges + rederives
  agent; agent offboard deletes APM + purges outbound footprint) and
  three offboard-route tests.
- specs: PRD merge-semantics, PCE component spec (Reconcile + Decommission
  sections), aiac-agent endpoints/use-case tables.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
# Conflicts:
#	.gitignore
#	aiac/docs/specs/ARCHITECTURE-SUMMARY.md
#	aiac/docs/specs/PRD.md
#	aiac/docs/specs/components/aiac-agent.md
#	aiac/docs/specs/components/aiac-agent/uc1-service-onboarding.md
#	aiac/docs/specs/components/event-broker.md
#	aiac/docs/specs/components/idp-configuration-service.md
#	aiac/docs/specs/components/keycloak-service.md
#	aiac/docs/specs/components/library-idp.md
#	aiac/docs/specs/components/library-policy-store.md
#	aiac/docs/specs/components/pdp-policy-keycloak-service.md
#	aiac/docs/specs/components/pdp-policy-writer-opa.md
#	aiac/docs/specs/components/policy-computation-engine.md
#	aiac/docs/specs/components/policy-model.md
#	aiac/docs/specs/components/policy-store.md
#	aiac/docs/specs/demo/github-agent.md
#	aiac/docs/specs/demo/github-tool.md
#	aiac/docs/specs/integration-test/pdp-policy-writer.md
#	aiac/docs/specs/integration-test/policy-pipeline.md
#	aiac/docs/specs/integration-test/uc1-onboarding-pipeline.md
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
- Fix invalid trailing comma in aiac/pyrightconfig.json (check-json)
- Trailing whitespace / EOF fixes on PRD.md, github-mcp-tools-summary.json, Dockerfile
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
@oblinder
oblinder requested a review from a team as a code owner July 29, 2026 10:27
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 204 files, which is 104 over the limit of 100.

To get a review, narrow the scope:
• coderabbit review --committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8fc8e5ef-98a3-46d1-a25b-e36196f1d13c

📥 Commits

Reviewing files that changed from the base of the PR and between 9a97ad4 and 36c7d73.

⛔ Files ignored due to path filters (2)
  • aiac/demo/agents/github_agent/uv.lock is excluded by !**/*.lock
  • aiac/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (204)
  • .github/workflows/security-scans.yaml
  • aiac/.gitignore
  • aiac/CLAUDE.md
  • aiac/demo/agents/github_agent/.dockerignore
  • aiac/demo/agents/github_agent/.env.template
  • aiac/demo/agents/github_agent/Dockerfile
  • aiac/demo/agents/github_agent/README.md
  • aiac/demo/agents/github_agent/a2a_agent.py
  • aiac/demo/agents/github_agent/github_agent/__init__.py
  • aiac/demo/agents/github_agent/github_agent/agents.py
  • aiac/demo/agents/github_agent/github_agent/config.py
  • aiac/demo/agents/github_agent/github_agent/data_types.py
  • aiac/demo/agents/github_agent/github_agent/event.py
  • aiac/demo/agents/github_agent/github_agent/llm.py
  • aiac/demo/agents/github_agent/github_agent/main.py
  • aiac/demo/agents/github_agent/github_agent/prompts.py
  • aiac/demo/agents/github_agent/github_agent/tools.py
  • aiac/demo/agents/github_agent/k8s/configmaps.yaml
  • aiac/demo/agents/github_agent/k8s/github-agent-deployment.yaml
  • aiac/demo/agents/github_agent/pyproject.toml
  • aiac/demo/agents/github_agent/test/test_agent_card.py
  • aiac/demo/agents/github_agent/test/test_prereq.py
  • aiac/demo/agents/github_agent/test/test_tools.py
  • aiac/demo/agents/github_agent/test_startup.exp
  • aiac/demo/tools/github_tool/Dockerfile
  • aiac/demo/tools/github_tool/k8s/github-tool-deployment.yaml
  • aiac/demo/tools/github_tool/pytest.ini
  • aiac/demo/tools/github_tool/requirements.txt
  • aiac/demo/tools/github_tool/server.py
  • aiac/demo/tools/github_tool/test/__init__.py
  • aiac/demo/tools/github_tool/test/conftest.py
  • aiac/demo/tools/github_tool/test/test_server.py
  • aiac/docs/analysis/discover_mcp_services.py
  • aiac/docs/analysis/github-agent-card.json
  • aiac/docs/analysis/github-mcp-tools-summary.html
  • aiac/docs/analysis/github-mcp-tools-summary.json
  • aiac/docs/analysis/keycloak-access-control-analysis.md
  • aiac/docs/specs/ARCHITECTURE-SUMMARY.md
  • aiac/docs/specs/PRD.md
  • aiac/docs/specs/components/aiac-agent.md
  • aiac/docs/specs/components/aiac-agent/uc1-service-onboarding.md
  • aiac/docs/specs/components/event-broker.md
  • aiac/docs/specs/components/idp-configuration-service.md
  • aiac/docs/specs/components/keycloak-service.md
  • aiac/docs/specs/components/library-idp.md
  • aiac/docs/specs/components/library-policy-store.md
  • aiac/docs/specs/components/pdp-policy-keycloak-service.md
  • aiac/docs/specs/components/pdp-policy-writer-opa.md
  • aiac/docs/specs/components/policy-computation-engine.md
  • aiac/docs/specs/components/policy-model.md
  • aiac/docs/specs/components/policy-store.md
  • aiac/docs/specs/demo/github-agent.md
  • aiac/docs/specs/demo/github-tool.md
  • aiac/docs/specs/integration-test/pdp-policy-writer.md
  • aiac/docs/specs/integration-test/policy-pipeline.md
  • aiac/docs/specs/integration-test/uc1-onboarding-pipeline.md
  • aiac/k8s/agent-deployment.yaml
  • aiac/k8s/aiac-deployment-guide.md
  • aiac/k8s/idp-configuration-keycloak-pod.yaml
  • aiac/k8s/pdp-interface-deployment.yaml
  • aiac/k8s/policy-store-statefulset.yaml
  • aiac/pyproject.toml
  • aiac/pyrightconfig.json
  • aiac/src/aiac/__init__.py
  • aiac/src/aiac/agent/__init__.py
  • aiac/src/aiac/agent/controller/Dockerfile
  • aiac/src/aiac/agent/controller/__init__.py
  • aiac/src/aiac/agent/controller/requirements.txt
  • aiac/src/aiac/agent/controller/routes.py
  • aiac/src/aiac/agent/policy_rules_builder/__init__.py
  • aiac/src/aiac/agent/policy_rules_builder/generic_policy.md
  • aiac/src/aiac/agent/policy_rules_builder/graph.py
  • aiac/src/aiac/agent/policy_rules_builder/policy_source.py
  • aiac/src/aiac/agent/policy_rules_builder/prompts.py
  • aiac/src/aiac/agent/shared/__init__.py
  • aiac/src/aiac/agent/shared/roles.py
  • aiac/src/aiac/agent/uc/__init__.py
  • aiac/src/aiac/agent/uc/offboarding/__init__.py
  • aiac/src/aiac/agent/uc/offboarding/offboard.py
  • aiac/src/aiac/agent/uc/onboarding/__init__.py
  • aiac/src/aiac/agent/uc/onboarding/orchestrator.py
  • aiac/src/aiac/agent/uc/onboarding/policy_builder/__init__.py
  • aiac/src/aiac/agent/uc/onboarding/policy_builder/builder.py
  • aiac/src/aiac/agent/uc/onboarding/provision/__init__.py
  • aiac/src/aiac/agent/uc/onboarding/provision/graph.py
  • aiac/src/aiac/agent/uc/onboarding/provision/kube.py
  • aiac/src/aiac/agent/uc/onboarding/provision/nodes.py
  • aiac/src/aiac/agent/uc/onboarding/provision/state.py
  • aiac/src/aiac/agent/uc/onboarding/provision/types.py
  • aiac/src/aiac/agent/uc/policy_update/__init__.py
  • aiac/src/aiac/agent/uc/policy_update/build.py
  • aiac/src/aiac/agent/uc/policy_update/rebuild.py
  • aiac/src/aiac/agent/uc/role_update/__init__.py
  • aiac/src/aiac/agent/uc/role_update/role.py
  • aiac/src/aiac/idp/__init__.py
  • aiac/src/aiac/idp/configuration/__init__.py
  • aiac/src/aiac/idp/configuration/api.py
  • aiac/src/aiac/idp/configuration/models.py
  • aiac/src/aiac/idp/service/__init__.py
  • aiac/src/aiac/idp/service/configuration/__init__.py
  • aiac/src/aiac/idp/service/configuration/keycloak/Dockerfile
  • aiac/src/aiac/idp/service/configuration/keycloak/__init__.py
  • aiac/src/aiac/idp/service/configuration/keycloak/keycloak_admin_methods.md
  • aiac/src/aiac/idp/service/configuration/keycloak/main.py
  • aiac/src/aiac/idp/service/configuration/keycloak/requirements.txt
  • aiac/src/aiac/pdp/__init__.py
  • aiac/src/aiac/pdp/library/__init__.py
  • aiac/src/aiac/pdp/library/read_api_from_config.py
  • aiac/src/aiac/pdp/policy/__init__.py
  • aiac/src/aiac/pdp/policy/library/__init__.py
  • aiac/src/aiac/pdp/policy/library/api.py
  • aiac/src/aiac/pdp/service/__init__.py
  • aiac/src/aiac/pdp/service/policy/__init__.py
  • aiac/src/aiac/pdp/service/policy/keycloak/Dockerfile
  • aiac/src/aiac/pdp/service/policy/keycloak/__init__.py
  • aiac/src/aiac/pdp/service/policy/keycloak/main.py
  • aiac/src/aiac/pdp/service/policy/keycloak/requirements.txt
  • aiac/src/aiac/pdp/service/policy/opa/Dockerfile
  • aiac/src/aiac/pdp/service/policy/opa/__init__.py
  • aiac/src/aiac/pdp/service/policy/opa/main.py
  • aiac/src/aiac/pdp/service/policy/opa/rego.py
  • aiac/src/aiac/pdp/service/policy/opa/requirements.txt
  • aiac/src/aiac/policy/__init__.py
  • aiac/src/aiac/policy/computation/__init__.py
  • aiac/src/aiac/policy/computation/engine.py
  • aiac/src/aiac/policy/model/__init__.py
  • aiac/src/aiac/policy/model/models.py
  • aiac/src/aiac/policy/store/__init__.py
  • aiac/src/aiac/policy/store/keying.py
  • aiac/src/aiac/policy/store/library/__init__.py
  • aiac/src/aiac/policy/store/library/api.py
  • aiac/src/aiac/policy/store/service/Dockerfile
  • aiac/src/aiac/policy/store/service/__init__.py
  • aiac/src/aiac/policy/store/service/main.py
  • aiac/src/aiac/policy/store/service/requirements.txt
  • aiac/src/aiac/shared/__init__.py
  • aiac/src/aiac/shared/upstream.py
  • aiac/test/__init__.py
  • aiac/test/agent/__init__.py
  • aiac/test/agent/controller/test_routes.py
  • aiac/test/agent/policy_rules_builder/__init__.py
  • aiac/test/agent/policy_rules_builder/test_auditor_dimension_integration.py
  • aiac/test/agent/policy_rules_builder/test_graph.py
  • aiac/test/agent/policy_rules_builder/test_isolation.py
  • aiac/test/agent/policy_rules_builder/test_policy_source.py
  • aiac/test/agent/shared/test_roles.py
  • aiac/test/agent/uc/__init__.py
  • aiac/test/agent/uc/onboarding/__init__.py
  • aiac/test/agent/uc/onboarding/policy_builder/__init__.py
  • aiac/test/agent/uc/onboarding/policy_builder/test_builder.py
  • aiac/test/agent/uc/onboarding/provision/__init__.py
  • aiac/test/agent/uc/onboarding/provision/test_analyze_agent.py
  • aiac/test/agent/uc/onboarding/provision/test_analyze_tool.py
  • aiac/test/agent/uc/onboarding/provision/test_classify_service.py
  • aiac/test/agent/uc/onboarding/provision/test_graph.py
  • aiac/test/agent/uc/onboarding/provision/test_provision_service.py
  • aiac/test/agent/uc/onboarding/test_orchestrator.py
  • aiac/test/idp/__init__.py
  • aiac/test/idp/configuration/__init__.py
  • aiac/test/idp/configuration/show_keycloak_data.py
  • aiac/test/idp/configuration/test_configuration.py
  • aiac/test/idp/configuration/test_models.py
  • aiac/test/idp/service/__init__.py
  • aiac/test/idp/service/configuration/__init__.py
  • aiac/test/idp/service/configuration/keycloak/__init__.py
  • aiac/test/idp/service/configuration/keycloak/test_main.py
  • aiac/test/integration/__init__.py
  • aiac/test/integration/launcher.py
  • aiac/test/integration/policy.abstract.md
  • aiac/test/integration/policy.explicit.md
  • aiac/test/integration/probe.rego
  • aiac/test/integration/probe_uc1.rego
  • aiac/test/integration/scenario.py
  • aiac/test/integration/scenario_uc1.py
  • aiac/test/integration/test_policy_pipeline.py
  • aiac/test/integration/test_uc1_onboard_agent_only.py
  • aiac/test/integration/test_uc1_onboard_agent_then_tool.py
  • aiac/test/integration/test_uc1_onboard_tool_then_agent.py
  • aiac/test/integration/uc1_onboard.py
  • aiac/test/pdp/__init__.py
  • aiac/test/pdp/library/__init__.py
  • aiac/test/pdp/policy/__init__.py
  • aiac/test/pdp/policy/generate_rego.py
  • aiac/test/pdp/policy/library/__init__.py
  • aiac/test/pdp/policy/library/test_api.py
  • aiac/test/pdp/service/__init__.py
  • aiac/test/pdp/service/policy/__init__.py
  • aiac/test/pdp/service/policy/keycloak/__init__.py
  • aiac/test/pdp/service/policy/keycloak/test_main.py
  • aiac/test/pdp/service/policy/opa/__init__.py
  • aiac/test/pdp/service/policy/opa/test_main.py
  • aiac/test/pdp/service/policy/opa/test_rego.py
  • aiac/test/policy/__init__.py
  • aiac/test/policy/computation/__init__.py
  • aiac/test/policy/computation/test_engine.py
  • aiac/test/policy/model/__init__.py
  • aiac/test/policy/model/test_models.py
  • aiac/test/policy/store/__init__.py
  • aiac/test/policy/store/library/__init__.py
  • aiac/test/policy/store/library/test_api.py
  • aiac/test/policy/store/service/__init__.py
  • aiac/test/policy/store/service/test_main.py
  • aiac/test/policy/store/test_keying.py
  • pyrightconfig.json

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Comment thread aiac/src/aiac/pdp/policy/library/api.py Fixed
Comment thread aiac/src/aiac/pdp/service/policy/opa/main.py Fixed
Comment thread aiac/src/aiac/policy/store/library/api.py Fixed
Comment thread aiac/src/aiac/policy/store/library/api.py Fixed
Comment thread aiac/src/aiac/policy/computation/engine.py Fixed
Comment thread aiac/src/aiac/policy/store/service/main.py Fixed
Comment thread aiac/src/aiac/policy/store/service/main.py Fixed
Comment thread aiac/src/aiac/policy/store/service/main.py Fixed
Resolve the security, correctness, test, and doc/spec findings from the
CodeRabbit CLI review of the UC-1 service-onboarding Phase 1 branch.

Security:
- Remove committed keycloak-admin-secret (admin/admin) from
  pdp-interface-deployment.yaml and idp-configuration-keycloak-pod.yaml;
  document the Secret + namespace as pre-provisioned preconditions.
- .dockerignore the demo agent's .env so secrets are never baked into the image.
- Escape all values/keys emitted into Rego string literals via json.dumps
  (rego.py) to prevent Rego injection / malformed output.

Live-source correctness:
- Policy Store: 422 guard on path/body service_id mismatch; serialize
  cache+DB mutations under a single write lock; mkdir the SQLite data dir.
- Policy Store library: add request timeouts to all HTTP calls.
- IdP config: fail fast (RuntimeError) when the realm env var is unset.
- Keycloak IdP service: narrow the over-broad KeycloakError handler and
  replace the O(scopes x clients) owner scan with a one-pass reverse index.
- Onboarding provision nodes: guard missing tool/skill names and empty
  service ports with actionable HTTP 502s instead of KeyError/IndexError.
- Retries (shared/upstream + policy_rules_builder): retry only transient
  failures; tolerate an unset/non-numeric UPSTREAM_MAX_RETRIES.
- PCE engine: fold changed owners into the affected set so policy override
  revocation propagates (previously left stale APM/SPM state).
- Legacy Keycloak writer: correct add_client_default_client_scope call.

Tests:
- Fix vacuous startswith() outbound filters (substring match, matching rung 2).
- Harden the integration harness (opa_dump guard, store-clear failure
  handling, ConfigMap rollout, port-forward readiness + stderr drain).
- Assorted docstring/name/marker fixes.

Docs/specs:
- Reconcile the PCE exception contract (re-raise), module paths, trigger-id
  wording (service_id/UUID), concurrency, OPA subject-gate semantics, and
  analysis-doc inaccuracies to match the implemented behavior.

Unit suite green: 466 passed. Integration suite collects (154).

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Comment thread aiac/src/aiac/policy/store/library/api.py Fixed
Comment thread aiac/src/aiac/policy/store/library/api.py Fixed
Comment thread aiac/src/aiac/policy/store/service/main.py Fixed
Comment thread aiac/src/aiac/policy/store/service/main.py Fixed
Comment thread aiac/src/aiac/policy/store/service/main.py Fixed
oblinder added 2 commits July 29, 2026 19:41
Resolve the 11 findings from the follow-up CodeRabbit review:

- github_tool Dockerfile: install -r requirements.txt (was hardcoding
  mcp[cli]+uvicorn, dropping the pinned bounds and httpx).
- pdp-policy-writer-opa spec: correct the outbound behaviour note
  (subject_ok matches subject_role_scopes[role], target_ok matches
  target_scopes[input.target]); fix the stale file tree
  (aiac/pdp/library/policy.py -> aiac/pdp/policy/library/api.py); add a
  note reconciling the client-UUID vs clientId identifiers used at the
  onboarding vs writer layers.
- idp-configuration-service spec: reconcile the /services/{id}/roles
  summary row, behaviour note, and Assumption 3 with the code, which
  returns client roles PLUS aiac.managed realm roles on the service
  account.
- ARCHITECTURE-SUMMARY: mark the NATS Event Broker / subscription path as
  Phase 2 and the direct Controller /apply/* invocation as the Phase 1
  trigger, matching event-broker.md; align the library import path with
  the .api submodule convention.
- uc1-service-onboarding spec: make the focus-resolution snippet use a
  default + explicit 404 (matches builder.py and the surrounding prose).
- k8s: rename the standalone pod's ConfigMap to aiac-pdp-config-standalone
  to avoid clobbering the interface ConfigMap; document the deployment as
  a Phase 1 local/Kind dev topology (node-local images).

Docs/specs and build config only; no source changes, unit suite unaffected.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Address 8 open GitHub Advanced Security (CodeQL) alerts on PR rossoctl#710:

- py/stack-trace-exposure (x3, store/service/main.py): SQLite errors were
  echoed to clients via {"error": str(e)}. Log the exception server-side
  and return a generic {"error": "database error"} body instead.
- py/log-injection (engine.py): strip CR/LF from service_id before logging
  so a hostile id cannot forge log records.
- py/partial-ssrf (pdp/policy/library/api.py): URL-encode the agent_id path
  segment with quote(..., safe=''); also fixes routing for slash-bearing
  clientIds so they cannot split into extra path segments.
- py/partial-ssrf (x2, store/library via keying.py): assert the base64url
  encoded service_id matches [A-Za-z0-9_-]+ before it reaches a request URL.
- py/path-injection (rego.py slugify): assert the slug is a single
  [a-z0-9_]+ segment (blocks path traversal, rejects empty slugs) before it
  becomes a filename in the PDP Policy Writer output dir.

No behaviour change for valid inputs; full unit suite (466 tests) green.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
oblinder added a commit to s-and-p-team/cortex that referenced this pull request Jul 29, 2026
Address 8 open GitHub Advanced Security (CodeQL) alerts on PR rossoctl#710:

- py/stack-trace-exposure (x3, store/service/main.py): SQLite errors were
  echoed to clients via {"error": str(e)}. Log the exception server-side
  and return a generic {"error": "database error"} body instead.
- py/log-injection (engine.py): strip CR/LF from service_id before logging
  so a hostile id cannot forge log records.
- py/partial-ssrf (pdp/policy/library/api.py): URL-encode the agent_id path
  segment with quote(..., safe=''); also fixes routing for slash-bearing
  clientIds so they cannot split into extra path segments.
- py/partial-ssrf (x2, store/library via keying.py): assert the base64url
  encoded service_id matches [A-Za-z0-9_-]+ before it reaches a request URL.
- py/path-injection (rego.py slugify): assert the slug is a single
  [a-z0-9_]+ segment (blocks path traversal, rejects empty slugs) before it
  becomes a filename in the PDP Policy Writer output dir.

No behaviour change for valid inputs; full unit suite (466 tests) green.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Comment thread aiac/src/aiac/pdp/policy/library/api.py Fixed
…w findings

Two automated checks on PR rossoctl#710 were failing:

CodeQL (2 new alerts):
- py/partial-ssrf (critical, pdp/policy/library/api.py): the prior quote()
  fix did not satisfy CodeQL. Add a regex fullmatch barrier on the encoded
  segment (mirroring policy/store/keying.py, which CodeQL accepts) via a new
  _agent_id_segment() helper used by apply_agent_policy + delete_agent_policy.
- py/path-injection (high, pdp/service/policy/opa/main.py): the /health
  handler took out_dir as a FastAPI route parameter, which CodeQL treats as
  request-controlled input flowing into a filesystem path check. Resolve it
  from get_output_dir() inside the handler instead (it is operator config,
  never a request). Update TestHealth to drive REGO_OUTPUT_DIR via env.

Dependency Review:
- chromadb CVE-2026-45829 / GHSA-f4j7-r4q5-qw2c (critical, transitive via
  crewai in the github_agent demo) has no patched version (affected <= 1.5.9)
  and is already documented as a mitigated/accepted risk in the demo's
  pyproject.toml. Add it to the dependency-review-action allowlist.

Full unit suite (466 tests) green.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
oblinder added a commit to s-and-p-team/cortex that referenced this pull request Jul 29, 2026
…w findings

Two automated checks on PR rossoctl#710 were failing:

CodeQL (2 new alerts):
- py/partial-ssrf (critical, pdp/policy/library/api.py): the prior quote()
  fix did not satisfy CodeQL. Add a regex fullmatch barrier on the encoded
  segment (mirroring policy/store/keying.py, which CodeQL accepts) via a new
  _agent_id_segment() helper used by apply_agent_policy + delete_agent_policy.
- py/path-injection (high, pdp/service/policy/opa/main.py): the /health
  handler took out_dir as a FastAPI route parameter, which CodeQL treats as
  request-controlled input flowing into a filesystem path check. Resolve it
  from get_output_dir() inside the handler instead (it is operator config,
  never a request). Update TestHealth to drive REGO_OUTPUT_DIR via env.

Dependency Review:
- chromadb CVE-2026-45829 / GHSA-f4j7-r4q5-qw2c (critical, transitive via
  crewai in the github_agent demo) has no patched version (affected <= 1.5.9)
  and is already documented as a mitigated/accepted risk in the demo's
  pyproject.toml. Add it to the dependency-review-action allowlist.

Full unit suite (466 tests) green.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
The dependency-review check flagged json-repair@0.25.3 (transitive via crewai
in the github_agent demo) for GHSA-xf7x-x43h-rpqh (high, unbounded-CPU DoS via
circular JSON Schema $ref). Patched in 0.60.1. Pin it in pyproject.toml
following the existing indirect-dep pattern and refresh uv.lock (also pulls
minor crewai 1.15.2->1.15.8, mcp, pydantic-settings bumps from re-resolution).

chromadb (CVE-2026-45829, no patched version) remains allowlisted via
allow-ghsas in security-scans.yaml.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
@oblinder oblinder self-assigned this Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: New/ToDo

Development

Successfully merging this pull request may close these issues.

4 participants