Contributing
Contributions are welcome — especially new escalation techniques, bug fixes, and improvements to the documentation.
Setup
git clone https://github.com/sxaAspri/sxaIAM
cd sxaIAM
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
Running tests
# All tests (no AWS credentials needed)
pytest
# Only unit tests
pytest tests/unit/
# With coverage report
pytest --cov=sxaiam --cov-report=term-missing
Adding a new escalation technique
This is the most common contribution. The architecture is designed to make this easy — you only need to touch two files.
Step 1 — Create the technique class
In sxaiam/findings/techniques.py, add a new class that inherits from EscalationTechnique:
class MyNewTechnique(EscalationTechnique):
@property
def technique_id(self) -> str:
return "my-new-technique"
@property
def name(self) -> str:
return "MyNewTechnique description"
@property
def severity(self) -> Severity:
return Severity.HIGH
@property
def required_actions(self) -> list[str]:
return ["iam:SomeAction"]
@property
def description(self) -> str:
return "One paragraph explaining the attack vector."
def check(
self,
identity: ResolvedIdentity,
snapshot: IAMSnapshot,
) -> list[TechniqueMatch]:
matches = []
# 1. Check if the identity has the required permissions
if not identity.can("iam:SomeAction", "*"):
return []
# 2. Find viable targets in the snapshot
for target in snapshot.users: # or roles, groups, policies
if target.arn == identity.arn:
continue
# 3. Build evidence and return a TechniqueMatch
evidence = self._build_evidence(identity, ["iam:SomeAction"])
matches.append(TechniqueMatch(
technique_id=self.technique_id,
technique_name=self.name,
severity=self.severity,
origin_arn=identity.arn,
origin_name=identity.name,
target_arn="sxaiam::admin",
target_name=target.name,
description=self.description,
evidence=evidence,
attack_steps=[
f"1. Call iam:SomeAction with ...",
f"2. Result: escalated to admin",
],
))
return matches
Step 2 — Register the technique
At the bottom of techniques.py, add your class to ALL_TECHNIQUES:
ALL_TECHNIQUES = [
CreatePolicyVersionTechnique,
# ... existing techniques ...
MyNewTechnique, # ← add here
]
Step 3 — Write tests
In tests/unit/findings/test_techniques.py, add a test class:
class TestMyNewTechnique:
def test_detects_attack(self) -> None:
# Build a minimal snapshot with the vulnerable configuration
policy = make_policy("MyPolicy", "arn:aws:iam::123:policy/MyPolicy", "iam:SomeAction")
user = IAMUser(name="attacker", arn="arn:aws:iam::123:user/attacker", ...)
snapshot = make_snapshot(users=[user], policies=[policy])
identity = resolve_user(user, snapshot)
matches = MyNewTechnique().check(identity, snapshot)
assert len(matches) >= 1
assert matches[0].severity == Severity.HIGH
def test_no_match_without_permission(self) -> None:
# Verify it doesn't fire when the permission is absent
...
Step 4 — Add Terraform (optional but recommended)
If you want to validate the technique against a real AWS account, add a
path{N}_your_technique.tf file in terraform/modules/vulnerable_identities/.
Step 5 — Update the technique count test
In test_techniques.py, update TestAllTechniques.test_registry_has_nine_techniques
to reflect the new count.
Architecture principles
Before submitting a PR, make sure your contribution respects these rules:
- Extensible node types — don't assume the graph only has users and roles
- Resolver and graph are separate — the technique never calls the resolver directly
- Technique knowledge stays in techniques.py — no escalation logic in the graph engine or builder
Code style
PRs should pass both without errors.