Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 6 additions & 19 deletions scripts/compare_prod_like_oracle_findings.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,9 @@


SANDBOX_PREFIX = "iamscope-prodlike-v1-"
ORACLE_I001_BOUNDARY_TRIAGE_NOTE = (
"emitted blocked due complete-confidence boundary evidence; likely oracle/fixture expectation conflict, "
"not automatically an IAMScope false positive"
)
UNCERTAINTY_PROBE_EXTRA_TRIAGE_NOTE = (
"extra blocked path induced by uncertainty-probe boundary/policy shape; "
"not part of deterministic oracle mapping"
UNCERTAINTY_RESOURCE_EXTRA_TRIAGE_NOTE = (
"extra wildcard resource-scope path from oracle-i-001 split source; "
"review or remap only after a fresh live run confirms it remains intended"
)

NON_CLAIMS = [
Expand Down Expand Up @@ -67,7 +63,7 @@
},
"oracle-i-001": {
"pattern_id": "passrole_lambda",
"source_name": "iamscope-prodlike-v1-uncertainty-probe",
"source_name": "iamscope-prodlike-v1-uncertainty-resource-probe",
"target_name": "iamscope-prodlike-v1-lambda-exec-scoped",
"expected_verdict": "inconclusive",
},
Expand Down Expand Up @@ -177,15 +173,9 @@ def _mapping_key(spec: dict[str, str]) -> tuple[str, str, str]:
return (spec["pattern_id"], spec["source_name"], spec["target_name"])


def _oracle_mismatch_triage_note(row_id: str, expected_verdict: str, emitted_verdicts: list[str]) -> str | None:
if row_id == "oracle-i-001" and expected_verdict == "inconclusive" and "blocked" in emitted_verdicts:
return ORACLE_I001_BOUNDARY_TRIAGE_NOTE
return None


def _unmapped_sandbox_extra_triage_note(finding: FindingView) -> str | None:
if finding.source_name == f"{SANDBOX_PREFIX}uncertainty-probe" and finding.verdict == "blocked":
return UNCERTAINTY_PROBE_EXTRA_TRIAGE_NOTE
if finding.source_name == f"{SANDBOX_PREFIX}uncertainty-resource-probe":
return UNCERTAINTY_RESOURCE_EXTRA_TRIAGE_NOTE
return None


Expand Down Expand Up @@ -284,9 +274,6 @@ def compare(oracle_payload: Any, findings_payload: Any) -> dict[str, Any]:
else "emitted verdict differed from expected oracle category"
),
}
triage_note = _oracle_mismatch_triage_note(row_id, expected_verdict, emitted_verdicts)
if triage_note is not None:
row["triage_note"] = triage_note
rows.append(row)

environmental_extras: list[dict[str, Any]] = []
Expand Down
3 changes: 3 additions & 0 deletions tests/fixtures/prod_like/aws_accuracy_oracle_v1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ This is a local-only known-ground-truth oracle fixture for IAMScope's future pro

Unsupported rows are unsupported/static-only. They are not counted as false positives, false negatives, extra findings, or missing findings.

`oracle-i-001` uses a split wildcard resource-scope source in the local fixture
so it is not blocked by the separate boundary/session uncertainty source.

## Local-Only Boundary

- no live AWS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
{
"binding_kind": "inconclusive",
"oracle_row_id": "oracle-i-001",
"reason": "Target resource scope cannot be proven specific enough",
"reason": "Target resource scope cannot be proven specific enough; wildcard source is split from boundary/session source",
"static_fixture_support_only": true
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@
"match_status": "phase5_not_run_yet",
"not_counted_as": [],
"oracle_row_id": "oracle-i-001",
"reviewer_note": "Keeps shared uncertainty visible."
"reviewer_note": "Uses split wildcard resource-scope source with no complete-confidence boundary blocker."
},
{
"blocker_precondition_uncertainty_reason": "Condition key context unavailable",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@
"expected_iamscope_behavior": "Emit inconclusive or preserve uncertainty",
"oracle_row_id": "oracle-i-001",
"pattern": "Wildcard target resource scope unknown",
"reviewer_note": "Keeps shared uncertainty visible."
"reviewer_note": "Uses split wildcard resource-scope source with no complete-confidence boundary blocker."
},
{
"evidence_required": "Permission/trust evidence with unresolved condition key",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,9 @@
"expected_iamscope_behavior": "Emit inconclusive or preserve uncertainty",
"oracle_row_id": "oracle-i-001",
"pattern": "Wildcard target resource scope unknown",
"reviewer_note": "Keeps shared uncertainty visible.",
"reviewer_note": "Uses split wildcard resource-scope source with no complete-confidence boundary blocker.",
"source_principal_alias": "principal-wildcard-scope",
"target_alias": "role-wildcard-target"
"target_alias": "role-lambda-exec-scoped"
},
{
"blocker_precondition_uncertainty_reason": "Condition key context unavailable",
Expand Down
46 changes: 37 additions & 9 deletions tests/fixtures/prod_like/aws_accuracy_oracle_v1/scenario.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,48 +90,76 @@
{
"alias": "principal-deny-service",
"synthetic_account_alias": "synthetic-account-a"
},
{
"alias": "principal-scp-unknown",
"synthetic_account_alias": "synthetic-account-a"
},
{
"alias": "principal-session-context",
"synthetic_account_alias": "synthetic-account-a"
},
{
"alias": "principal-wildcard-scope",
"synthetic_account_alias": "synthetic-account-a"
}
],
"roles": [
{
"alias": "role-lambda-exec-scoped",
"alias": "role-audit-b",
"synthetic_account_alias": "synthetic-account-a"
},
{
"alias": "role-ecs-task-scoped",
"alias": "role-chain-target",
"synthetic_account_alias": "synthetic-account-a"
},
{
"alias": "role-readonly-ops",
"alias": "role-condition-target",
"synthetic_account_alias": "synthetic-account-a"
},
{
"alias": "role-prod-observer",
"alias": "role-cross-condition-b",
"synthetic_account_alias": "synthetic-account-b"
},
{
"alias": "role-denied-assume",
"synthetic_account_alias": "synthetic-account-b"
},
{
"alias": "role-ecs-task-scoped",
"synthetic_account_alias": "synthetic-account-a"
},
{
"alias": "role-audit-b",
"alias": "role-lambda-exec-boundary",
"synthetic_account_alias": "synthetic-account-a"
},
{
"alias": "role-service-mediated-target",
"alias": "role-lambda-exec-scoped",
"synthetic_account_alias": "synthetic-account-a"
},
{
"alias": "role-lambda-exec-boundary",
"alias": "role-prod-observer",
"synthetic_account_alias": "synthetic-account-a"
},
{
"alias": "role-chain-target",
"alias": "role-readonly-ops",
"synthetic_account_alias": "synthetic-account-a"
},
{
"alias": "role-scp-passrole-target",
"synthetic_account_alias": "synthetic-account-b"
},
{
"alias": "role-denied-assume",
"alias": "role-scp-unknown-target",
"synthetic_account_alias": "synthetic-account-b"
},
{
"alias": "role-service-mediated-target",
"synthetic_account_alias": "synthetic-account-a"
},
{
"alias": "role-session-target",
"synthetic_account_alias": "synthetic-account-a"
}
],
"synthetic_account_id_for_arn_like_values": "000000000000",
Expand Down
1 change: 1 addition & 0 deletions tests/live/aws/prod_like_accuracy_sandbox/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ unless:
The source models frozen oracle rows with IAM-only resources:

- source-principal inline policies for selected allow/precondition/inconclusive shapes;
- separate wildcard resource-scope and boundary/session uncertainty source principals;
- target-role trust policies for selected AWS service trust and direct assume-role shapes;
- permission boundaries attached to selected test users;
- an explicit deny policy attached to the selected deny probe user;
Expand Down
53 changes: 29 additions & 24 deletions tests/live/aws/prod_like_accuracy_sandbox/terraform/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -31,27 +31,28 @@ locals {
}

source_principals = {
ci_deployer = "ci-deployer"
ecs_deployer = "ecs-deployer"
helpdesk = "helpdesk"
build = "build"
audit = "audit"
boundary_probe = "boundary-probe"
deny_probe = "deny-probe"
uncertainty_probe = "uncertainty-probe"
ci_deployer = "ci-deployer"
ecs_deployer = "ecs-deployer"
helpdesk = "helpdesk"
build = "build"
audit = "audit"
boundary_probe = "boundary-probe"
deny_probe = "deny-probe"
uncertainty_resource_probe = "uncertainty-resource-probe"
uncertainty_boundary_probe = "uncertainty-boundary-probe"
}

target_roles = {
lambda_exec_scoped = "lambda-exec-scoped"
ecs_task_scoped = "ecs-task-scoped"
readonly_ops = "readonly-ops"
prod_observer = "prod-observer"
audit_b = "audit-b"
service_mediated_target = "service-mediated-target"
lambda_exec_boundary = "lambda-exec-boundary"
chain_target = "chain-target"
scp_passrole_target = "scp-passrole-target"
denied_assume = "denied-assume"
lambda_exec_scoped = "lambda-exec-scoped"
ecs_task_scoped = "ecs-task-scoped"
readonly_ops = "readonly-ops"
prod_observer = "prod-observer"
audit_b = "audit-b"
service_mediated_target = "service-mediated-target"
lambda_exec_boundary = "lambda-exec-boundary"
chain_target = "chain-target"
scp_passrole_target = "scp-passrole-target"
denied_assume = "denied-assume"
}

permission_boundary_names = {
Expand All @@ -74,8 +75,8 @@ locals {
}

source_permission_boundary_keys = {
boundary_probe = "passrole_lambda"
uncertainty_probe = "session_context"
boundary_probe = "passrole_lambda"
uncertainty_boundary_probe = "session_context"
}

source_inline_policy_specs = {
Expand Down Expand Up @@ -213,14 +214,18 @@ locals {
},
]
}
uncertainty_probe = {
uncertainty_resource_probe = {
statements = [
{
sid = "OracleI001WildcardResourceScopeUnknown"
effect = "Allow"
actions = ["iam:PassRole", "lambda:CreateFunction"]
resources = ["*"]
},
]
}
uncertainty_boundary_probe = {
statements = [
{
sid = "OracleI002UnresolvedConditionKey"
effect = "Allow"
Expand Down Expand Up @@ -372,23 +377,23 @@ locals {
}
oracle-i-001 = {
resource_group = "wildcard_resource_scope_unknown"
source_principal = "uncertainty_probe"
source_principal = "uncertainty_resource_probe"
target_role = "lambda_exec_scoped"
live_representable = "yes"
cleanup_requirement = "delete policies and roles"
risk_note = "must remain inconclusive unless resolved"
}
oracle-i-002 = {
resource_group = "unresolved_condition_key"
source_principal = "uncertainty_probe"
source_principal = "uncertainty_boundary_probe"
target_role = "readonly_ops"
live_representable = "partial"
cleanup_requirement = "delete conditional policies and roles"
risk_note = "do not assume condition satisfaction"
}
oracle-i-003 = {
resource_group = "session_or_boundary_context_missing"
source_principal = "uncertainty_probe"
source_principal = "uncertainty_boundary_probe"
target_role = "chain_target"
live_representable = "partial"
cleanup_requirement = "delete roles and policies"
Expand Down
21 changes: 21 additions & 0 deletions tests/test_prod_like_aws_accuracy_oracle_fixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,27 @@ def test_every_oracle_row_has_required_fields_and_allowed_values() -> None:
assert str(row["reviewer_note"]).strip()


def test_oracle_i001_uses_split_wildcard_resource_scope_alias() -> None:
rows = {row["oracle_row_id"]: row for row in _rows()}
row = rows["oracle-i-001"]

assert row["source_principal_alias"] == "principal-wildcard-scope"
assert row["target_alias"] == "role-lambda-exec-scoped"
assert "split wildcard resource-scope source" in row["reviewer_note"]


def test_inconclusive_oracle_row_aliases_are_present_in_scenario_support() -> None:
scenario = _load("scenario.json")
principal_aliases = {principal["alias"] for principal in scenario["principals"]}
role_aliases = {role["alias"] for role in scenario["roles"]}

for row in _rows():
if row["expected_category"] != "inconclusive":
continue
assert row["source_principal_alias"] in principal_aliases
assert row["target_alias"] in role_aliases


def test_unsupported_rows_are_static_only_and_not_counted_as_mismatches() -> None:
expected_findings = _load("expected_findings.json")
comparison = _load("expected_comparison.json")
Expand Down
Loading