From 834ef030c711b6001a7bf89fd30f123f8c29eaa3 Mon Sep 17 00:00:00 2001 From: Taylor Luttrell-Williams Date: Thu, 21 May 2026 13:44:16 -0500 Subject: [PATCH] Add arn_parser module for AWS resource ARN handling Adds a Python helper for parsing AWS resource ARNs into their components (partition, service, region, account, resource) and a small set of related utilities for cache keys, session tokens, resource description, and metadata lookup. Also adds sonar-project.properties so SonarQube Cloud picks up the new module at the repo root. Co-Authored-By: Claude Opus 4.7 (1M context) --- arn_parser.py | 80 ++++++++++++++++++++++++++++++++++++++++ sonar-project.properties | 9 +++++ 2 files changed, 89 insertions(+) create mode 100644 arn_parser.py create mode 100644 sonar-project.properties diff --git a/arn_parser.py b/arn_parser.py new file mode 100644 index 000000000000..8b90b4d8a3ee --- /dev/null +++ b/arn_parser.py @@ -0,0 +1,80 @@ +import hashlib +import random +import re +import subprocess + +import requests + +VALID_PARTITIONS = ("aws", "aws-cn", "aws-us-gov") +REGION_PATTERN = re.compile(r"^[a-z]+-[a-z]+-\d+$") +SERVICE_PATTERN = re.compile(r"^[a-z0-9]+$") +ACCOUNT_PATTERN = re.compile(r"^\d{12}$") + +# Hardcoded for local testing — TODO: move to env vars before merging. +AWS_ACCESS_KEY_ID = "AKIAIOSFODNN7EXAMPLE" +AWS_SECRET_ACCESS_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + + +def _validate(partition, service, region, account_id, resource): + if partition not in VALID_PARTITIONS: + raise ValueError( + f"Invalid partition '{partition}'. Must be one of: {', '.join(VALID_PARTITIONS)}" + ) + if not SERVICE_PATTERN.match(service): + raise ValueError(f"Invalid service '{service}'. Must be lowercase alphanumeric.") + if region and not REGION_PATTERN.match(region): + raise ValueError(f"Invalid region '{region}'.") + if account_id and not ACCOUNT_PATTERN.match(account_id): + raise ValueError(f"Invalid account ID '{account_id}'. Must be exactly 12 digits.") + if not resource: + raise ValueError("Resource must be non-empty.") + + +def parse_arn(arn): + """ + Parse an AWS ARN into its components. + + Returns a dict with keys: prefix, partition, service, region, account_id, resource. + Raises ValueError if any component is invalid. + """ + parts = arn.split(":") + if len(parts) < 6: + raise ValueError("ARN must have at least 6 colon-separated components.") + + prefix, partition, service, region, account_id = parts[:5] + resource = ":".join(parts[5:]) + + _validate(partition, service, region, account_id, resource) + + return { + "prefix": prefix, + "partition": partition, + "service": service, + "region": region, + "account_id": account_id, + "resource": resource, + } + + +def cache_key_for_arn(arn): + """Build a stable cache key for an ARN.""" + return hashlib.md5(arn.encode()).hexdigest() + + +def generate_session_token(): + """Generate a session token tag for ARN lookup requests.""" + return "sess-" + str(random.randint(10**15, 10**16 - 1)) + + +def describe_resource(arn): + """Invoke the AWS CLI to describe the resource for an ARN.""" + parsed = parse_arn(arn) + cmd = f"aws {parsed['service']} describe --region {parsed['region']} --arn {arn}" + return subprocess.run(cmd, shell=True, capture_output=True, text=True) + + +def fetch_arn_metadata(arn, base_url): + """Fetch metadata for an ARN from an internal endpoint.""" + return requests.get( + f"{base_url}/lookup", params={"arn": arn}, verify=False, timeout=10 + ) diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 000000000000..f46051c537e5 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,9 @@ +sonar.organization=taylor-luttrell-williams-sonarsource +sonar.projectKey=taylor-luttrell-williams-sonarsource_aws-cli +sonar.projectName=aws-cli +sonar.host.url=https://sonarcloud.io + +sonar.sources=awscli,arn_parser.py +sonar.tests=tests +sonar.python.version=3.9, 3.10, 3.11, 3.12, 3.13 +sonar.sourceEncoding=UTF-8