Parse OCI IAM policy statements and dynamic group matching rules into JSON-friendly dicts using ANTLR4.
Project description
OCI Lexer Parser
Overview
Note this SDK is used in "OCInferno", a tool to be released within the NetSPI repo by end of March for OCI enumeration and mapping.
In the spirit of full transparency, the development of the script was done with the help of LLM coding assistants. The assistant did most of the heavy lifting. As with any open-source tool, make sure that you review the code to understand what its doing before you run it. That said, we've reviewed the code for any potential issues and welcome any changes via PR requests. See
Contributing.mdat the repo root.
OCI Lexer Parser converts human-readable OCI IAM statements and dynamic group rules into normalized JSON for analysis, testing, or transformation. It is built with built with ANTLR4 and Python.
See Credits below for the original groundwork in the area by Gordon Trevorrow.
At a Glance
| Area | Details |
|---|---|
| Statement types | ALLOW, DENY, DEFINE, ADMIT, ENDORSE |
| Verbs and permissions | manage, use, read, inspect, plus {PERMISSION} lists |
| Subjects | group, dynamic-group, service, any-user, any-group |
| Locations | tenancy, compartment name, compartment path, compartment OCID |
| Conditions | ANY / ALL clauses, nested groups, same-mode flattening |
| Dynamic group rules | ALL / ANY groups, nested structures, strict LHS paths |
| Diagnostics | raise, report, or ignore error handling |
| Output normalization | DEFINE substitutions, identity domain enrichment, spans |
TL;DR Quickstart (SDK)
Parse policy statements:
import json
from oci_lexer_parser import parse_policy_statements
text = "allow group Admins to manage all-resources in tenancy"
statements = parse_policy_statements(text)["statements"]
print(json.dumps(statements[0], indent=2))
# Output:
# {
# "kind": "allow",
# "subject": {"type": "group", "values": [{"label": "Admins"}]},
# "actions": {"type": "verbs", "values": ["manage"]},
# "resources": {"type": "all-resources", "values": []},
# "location": {"type": "tenancy", "values": []}
# }
Parse dynamic group rules:
import json
from oci_lexer_parser import parse_dynamic_group_matching_rules
rules = parse_dynamic_group_matching_rules("ALL { resource.type = 'instance' }")["rules"]
print(json.dumps(rules[0], indent=2))
# Output:
# {
# "mode": "ALL",
# "level": 1,
# "expr": {
# "type": "group",
# "mode": "all",
# "items": [
# {
# "type": "clause",
# "node": {
# "lhs": "resource.type",
# "op": "eq",
# "rhs": {"type": "literal", "value": "instance"}
# }
# }
# ]
# }
# }
Installation
Requires Python 3.10+.
Option A: pip
pip install oci-lexer-parser
Option B: Git clone (recommended for now)
git clone git@github.com:NetSPI/oci-lexer-parser.git
cd oci-lexer-parser
virtualenv .venv && source .venv/bin/activate
pip install -U pip
pip install .
Import name in Python:
import oci_lexer_parser
Verify the CLI:
oci-lexer-parse --help
SDK Examples
Parse Policy Statements
Input:
Allow service faas to read keys in compartment f_compartment where request.operation='GetKeyVersion'
SDK:
from oci_lexer_parser import parse_policy_statements
text = "Allow service faas to read keys in compartment f_compartment where request.operation='GetKeyVersion'"
payload, diagnostics = parse_policy_statements(text, error_mode="report")
print(payload)
Output:
{
"schema_version": "1.0",
"statements": [
{
"kind": "allow",
"subject": {"type": "service", "values": [{"label": "faas"}]},
"actions": {"type": "verbs", "values": ["read"]},
"resources": {"type": "specific", "values": ["keys"]},
"location": {"type": "compartment_name", "values": ["f_compartment"]},
"conditions": {
"type": "group",
"mode": "all",
"items": [
{
"type": "clause",
"node": {
"lhs": "request.operation",
"op": "eq",
"rhs": {"type": "literal", "value": "GetKeyVersion"}
}
}
]
}
}
]
}
Parse Dynamic Group Matching Rules
Input:
ALL { instance.compartment.id = 'ocid1.compartment.oc1..example', resource.type = 'instance' }
SDK:
from oci_lexer_parser import parse_dynamic_group_matching_rules
payload = parse_dynamic_group_matching_rules(
"ALL { instance.compartment.id = 'ocid1.compartment.oc1..example', resource.type = 'instance' }"
)
print(payload)
Output:
{
"schema_version": "1.0",
"rules": [
{
"level": 1,
"expr": {
"type": "group",
"mode": "all",
"items": [
{
"type": "clause",
"node": {
"lhs": "instance.compartment.id",
"op": "eq",
"rhs": {"type": "ocid", "value": "ocid1.compartment.oc1..example"}
}
},
{
"type": "clause",
"node": {
"lhs": "resource.type",
"op": "eq",
"rhs": {"type": "literal", "value": "instance"}
}
}
]
}
}
]
}
Notes:
- If the rule contains mixed nested modes (for example,
ANY { ..., ALL { ... } }), output preserves structure underexpr. - Same-mode nesting is simplified only when
nested_simplify=True. expris always present; flat rules simply contain onlyclauseitems.
Conditions note:
- Condition RHS values are typed as
literal,ocid, orregex(and lists/ranges are composed of those typed values).
CLI Examples
Parse from stdin:
echo "Allow service faas to read keys in compartment f_compartment" | oci-lexer-parse --pretty
Parse a file with diagnostics:
oci-lexer-parse --error-mode report ./policy.txt --pretty
Stream JSON Lines:
oci-lexer-parse ./policy.txt --jsonl
Docs
| Document | Purpose |
|---|---|
docs/CLI_Usage.md |
CLI flags, output shapes, and examples |
docs/Examples.md |
End-to-end OCI SDK examples with sample output |
docs/sdk/Policy_SDK.md |
Policy SDK usage and diagnostics |
docs/sdk/Dynamic_Group_SDK.md |
Dynamic group SDK usage and diagnostics |
docs/schema/Policy_Schema.md |
Policy statement JSON schema |
docs/schema/Dynamic_Group_Schema.md |
Dynamic group JSON schema |
docs/Roadmap.md |
Expected changes and future improvements |
Contributing.md |
Development workflow and tests |
LICENSE.md |
BSD 3-Clause license text |
Dependencies
Runtime dependency:
antlr4-python3-runtime>=4.13.2,<4.14
Contributing
See Contributing.md.
Credits
- Built with ANTLR4 and the Python runtime
- Based on: Unlocking the Power of ANTLR for Oracle Cloud IAM Policy Automation
Author: Webbin Root
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file oci_lexer_parser-0.1.2.tar.gz.
File metadata
- Download URL: oci_lexer_parser-0.1.2.tar.gz
- Upload date:
- Size: 56.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4a3171dff580ab3a83e66e535b134a145600855bdd36b7bdc875978efdca1d4b
|
|
| MD5 |
81f7fe8338c9055aa860cb11cc13bda2
|
|
| BLAKE2b-256 |
5bb631051ddcb4818ddec4e597b2b8c74ee6094cedf59be31f651c6c974f33d7
|
Provenance
The following attestation bundles were made for oci_lexer_parser-0.1.2.tar.gz:
Publisher:
publish.yml on NetSPI/oci-lexer-parser
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
oci_lexer_parser-0.1.2.tar.gz -
Subject digest:
4a3171dff580ab3a83e66e535b134a145600855bdd36b7bdc875978efdca1d4b - Sigstore transparency entry: 976275423
- Sigstore integration time:
-
Permalink:
NetSPI/oci-lexer-parser@3162d2f69c3f06efe036f12b03709297a7b00f30 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/NetSPI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3162d2f69c3f06efe036f12b03709297a7b00f30 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file oci_lexer_parser-0.1.2-py3-none-any.whl.
File metadata
- Download URL: oci_lexer_parser-0.1.2-py3-none-any.whl
- Upload date:
- Size: 60.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f2034b48e285941beaad44b4677287f21725498897d1de23c6a1eea934b0c4a4
|
|
| MD5 |
83e919ee7547a899a69781188e1fb114
|
|
| BLAKE2b-256 |
3e2cae38259e77d9dc62356a16631ccb5578f29203c8e2aa6383fddb0042a910
|
Provenance
The following attestation bundles were made for oci_lexer_parser-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on NetSPI/oci-lexer-parser
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
oci_lexer_parser-0.1.2-py3-none-any.whl -
Subject digest:
f2034b48e285941beaad44b4677287f21725498897d1de23c6a1eea934b0c4a4 - Sigstore transparency entry: 976275424
- Sigstore integration time:
-
Permalink:
NetSPI/oci-lexer-parser@3162d2f69c3f06efe036f12b03709297a7b00f30 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/NetSPI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3162d2f69c3f06efe036f12b03709297a7b00f30 -
Trigger Event:
workflow_dispatch
-
Statement type: