sickn33-antigravity-awesome-skills

access-review

Conduct periodic access reviews and certifications. Implement access governance and recertification workflows. Use when managing access compliance.
stars48451
forks7045
watches305
updated2026-10-24 22:34:41

Access Review

Implement periodic access review processes for AWS IAM, GitHub, Okta, and other identity providers, including automated reporting, certification workflows, and unused permission detection.

Access Review Process

access_review_workflow:
  1_scope:
    actions:
      - Define systems in scope for the review cycle
      - Identify review owners (managers, system owners)
      - Set review timeline and deadlines
      - Generate access inventory from all identity sources
    frequency:
      privileged_access: Quarterly
      standard_access: Semi-annually
      service_accounts: Quarterly
      api_keys: Monthly

  2_extract:
    actions:
      - Pull current access data from all systems
      - Correlate identities across platforms (SSO mapping)
      - Enrich with last login and activity data
      - Flag accounts for review (inactive, over-privileged, orphaned)

  3_review:
    actions:
      - Assign review items to appropriate managers
      - Manager certifies each user's access (approve/revoke/modify)
      - Risk-based prioritization (privileged users reviewed first)
      - Escalate non-responses after deadline
    decisions:
      approve: "Access is appropriate for current role"
      modify: "Access needs adjustment (reduce/change scope)"
      revoke: "Access is no longer needed"

  4_remediate:
    actions:
      - Revoke access flagged for removal
      - Modify access as directed by reviewers
      - Document exceptions with justification
      - Confirm changes with system owners
    sla:
      revocations: "Complete within 5 business days of decision"
      modifications: "Complete within 10 business days"
      exceptions: "Approved by security team, documented, time-limited"

  5_report:
    actions:
      - Generate completion metrics (% reviewed, % on time)
      - Document all decisions and actions taken
      - Archive evidence for compliance audits
      - Identify process improvements for next cycle

Review boundaries

Confirm exact tenant/accounts, read-only credentials, review period and authorized reviewers. Store reports privately with defined retention; proposed cadence/SLA numbers are examples, not compliance certification. Identity joins need verified immutable IDs, not matching display names. Owner approval and a recovery/break-glass plan are required before modifying access; inactivity heuristics never authorize revoking production service accounts automatically.

AWS IAM Access Review Scripts

Obtain the IAM credential report through a read-only scoped client, poll boundedly until complete, and decode it once into a private file. Failure/unsupported values are unknown, never zero/inactive. Use named CSV columns, not positional awk fields:

import csv

def review_console_mfa(report_path):
    candidates = []
    with open(report_path, newline="", encoding="utf-8") as source:
        for row in csv.DictReader(source):
            if row.get("password_enabled") == "true" and row.get("mfa_active") == "false":
                candidates.append({"user": row["user"], "finding": "review_console_mfa"})
    return candidates  # Private review data, not automatic disable actions.

Compare timezone-aware last-use/creation times and both access-key slots with an agreed threshold; missing history may mean never-used, unsupported or unavailable. Password last-use alone is not overall account inactivity. Effective privilege requires user/group/role/inline policies, boundaries, SCPs and resource policies; policy-name matching is only a hint. Cross-account trust needs structured principal and condition analysis, not grep or an AWS-account substring. No key or access is revoked solely because a report marks it stale.

GitHub Access Review

#!/usr/bin/env bash
# github-access-review.sh - Partial organization inventory
set -euo pipefail
umask 077

ORG="your-org"
OUTPUT_DIR="./access-review/github/$(date +%Y-%m)"
mkdir -p "$OUTPUT_DIR"

echo "=== GitHub Organization Access Review ==="

echo "--- Organization Members ---"
gh api orgs/$ORG/members --paginate \
  --jq '.[] | [.login, .site_admin] | @csv' \
  > "$OUTPUT_DIR/org-members.csv"

echo "--- Organization Owners ---"
gh api "orgs/$ORG/members?role=admin" --paginate \
  --jq '.[] | .login' \
  > "$OUTPUT_DIR/org-owners.txt"

echo "--- Outside Collaborators ---"
gh api orgs/$ORG/outside_collaborators --paginate \
  --jq '.[] | .login' \
  > "$OUTPUT_DIR/outside-collaborators.txt"

echo "--- Repository Access Per Repo ---"
for repo in $(gh api "orgs/$ORG/repos" --paginate --jq '.[].name'); do
  echo "Repo: $repo"
  gh api "repos/$ORG/$repo/collaborators" --paginate \
    --jq '.[] | [.login, .role_name] | @csv' \
    > "$OUTPUT_DIR/repo-$repo-access.csv"
done

echo "--- Team Memberships ---"
for team in $(gh api orgs/$ORG/teams --paginate --jq '.[].slug'); do
  echo "Team: $team"
  gh api "orgs/$ORG/teams/$team/members" --paginate \
    --jq '.[] | .login' \
    > "$OUTPUT_DIR/team-$team-members.txt"
done

echo "--- Pending Invitations ---"
gh api orgs/$ORG/invitations --paginate \
  --jq '.[] | [.login, .email, .role, .created_at] | @csv' \
  > "$OUTPUT_DIR/pending-invitations.csv"

echo "--- Deploy Keys ---"
for repo in $(gh api "orgs/$ORG/repos" --paginate --jq '.[].name'); do
  keys=$(gh api "repos/$ORG/$repo/keys" --jq '.[].title' 2>/dev/null)
  if [ -n "$keys" ]; then
    echo "$repo: $keys"
  fi
done > "$OUTPUT_DIR/deploy-keys.txt"

# Branch/ruleset enforcement needs a separate authorized read of each repository's
# actual default/protected branches, classic protection and repository/org rulesets.
# A 403/404 is unknown or absent for that API, not proof the branch is unprotected.
# site_admin is GitHub site administration, NOT an organization-owner indicator.

echo "Report generated in $OUTPUT_DIR"

Okta Access Review

Use a secret-aware SDK/client against the approved Okta HTTPS domain; do not put SSWS tokens in curl arguments. Disable unexpected redirects, set deadlines and bounded page/retry budgets, and follow only same-origin API Link: rel=next URLs. Enumerate users, factors, apps/assignments and admin roles with pagination; a single limit=200 response is incomplete. Iterate structured app IDs, not CSV rows containing quoted commas. Record permissions/errors/coverage explicitly. Factor enrollment is not proof that the current sign-on policy requires MFA or that a user's enrollment is usable. Compare current authenticators, policy and assignment data before recommendations. Keep names/email inventories restricted.

Unused Permission Detection

"""
Detect unused IAM permissions using CloudTrail and IAM Access Analyzer.
Generates recommendations for right-sizing access.
"""
import boto3
import json
import time
from datetime import datetime, timedelta, timezone


def analyze_iam_usage(days_lookback=90):
    """Analyze IAM user and role activity against granted permissions."""
    iam = boto3.client("iam")

    report = {
        "generated_at": datetime.now(timezone.utc).isoformat(),
        "lookback_days": days_lookback,
        "findings": [],
    }

    users = (user for page in iam.get_paginator("list_users").paginate() for user in page["Users"])
    for user in users:
        username = user["UserName"]

        # Get service last accessed data
        job_id = iam.generate_service_last_accessed_details(
            Arn=user["Arn"]
        )["JobId"]

        for attempt in range(60):
            result = iam.get_service_last_accessed_details(JobId=job_id)
            if result["JobStatus"] == "COMPLETED":
                break
            if result["JobStatus"] == "FAILED":
                raise RuntimeError("IAM last-access job failed")
            time.sleep(2)
        else:
            raise TimeoutError("IAM last-access job exceeded polling budget")
        services = list(result["ServicesLastAccessed"])
        while result.get("IsTruncated"):
            result = iam.get_service_last_accessed_details(JobId=job_id, Marker=result["Marker"])
            services.extend(result["ServicesLastAccessed"])

        threshold = datetime.now(timezone.utc) - timedelta(days=days_lookback)
        unused_services = []

        for service in services:
            last_accessed = service.get("LastAuthenticated")
            if last_accessed is None or last_accessed < threshold:
                unused_services.append({
                    "service": service["ServiceNamespace"],
                    "last_accessed": str(last_accessed) if last_accessed else "Unknown/no recorded activity",
                })

        if unused_services:
            report["findings"].append({
                "type": "unused_permissions",
                "user": username,
                "arn": user["Arn"],
                "unused_service_count": len(unused_services),
                "unused_services": unused_services[:10],
                "recommendation": "Review and remove unused service permissions",
            })

    return report


def list_external_access_candidates():
    """External-access findings are not a full effective-privilege/unused-access analysis."""
    analyzer = boto3.client("accessanalyzer")

    findings = analyzer.list_findings(
        analyzerArn="arn:aws:access-analyzer:us-east-1:123456789012:analyzer/org-analyzer",
        filter={
            "status": {"eq": ["ACTIVE"]},
            "resourceType": {"eq": ["AWS::IAM::Role"]},
        },
    )

    return [
        {
            "resource": f["resource"],
            "resource_type": f["resourceType"],
            "condition": f.get("condition", {}),
            "principal": f.get("principal", {}),
            "action": f.get("action", []),
            "created_at": str(f["createdAt"]),
        }
        for f in findings.get("findings", [])
    ]

Contents

When to Use

  • Conducting quarterly or annual access reviews for compliance (SOC 2, HIPAA, PCI DSS, ISO 27001)
  • Identifying and removing stale accounts and unused credentials
  • Certifying that current access levels match job responsibilities
  • Detecting excessive privileges and dormant service accounts
  • Generating evidence for auditor requests on access governance

Limitations

  • Guidance and checklists only; not legal advice and not a substitute for a qualified auditor.
  • Docs-only import: upstream templates and scripts not bundled.

Example

Map this skill's control checklist to our current evidence and list gaps.

Adapted from BagelHole/DevOps-Security-Agent-Skills (MIT); frontmatter, When to Use/Limitations, and safety boundaries added for upstream compliance. Docs-only import: helper scripts and templates not bundled.