PluginBench
Skill
Official
Pass
Audit score 90

setup-security-agent

aws/agent-toolkit-for-aws

Configure AWS Security Agent workspace with agent space, IAM role, and S3 bucket.

What is setup-security-agent?

Sets up the foundational AWS Security Agent infrastructure for a workspace, including agent space provisioning, IAM service role creation, and S3 bucket setup with security controls. Run this once before executing scans or pentests.

  • Provisions or reuses an AWS Security Agent space
  • Creates or validates IAM service role (SecurityAgentScanRole) with appropriate permissions
  • Sets up S3 bucket with public access blocking and 30-day lifecycle policy
  • Manages workspace-local state in .security-agent/ directory
  • Derives AWS account, region, and resource names by convention to minimize configuration drift
  • Validates bucket ownership to prevent bucket-squatting attacks

How to install setup-security-agent

npx skills add https://github.com/aws/agent-toolkit-for-aws --skill setup-security-agent
Prerequisites
  • AWS CLI configured with credentials and default region
  • IAM permissions to create/read roles, S3 buckets, and SecurityAgent resources
  • Existing AWS account with appropriate service limits
Claude Code
Cursor
Windsurf
Cline

How to use setup-security-agent

  1. 1.Run the skill when prompted or when setting up a new workspace
  2. 2.If agent spaces exist, select one to reuse or choose to create a new one
  3. 3.The skill will create SecurityAgentScanRole IAM role if it doesn't exist
  4. 4.The skill will create the S3 bucket (security-agent-scans-ACCOUNT-REGION) if needed
  5. 5.Verify .security-agent/config.json is populated with agent_space_id and region
  6. 6.Proceed to scan or pentest skills once setup completes successfully

Use cases

Good for
  • First-time setup before running security scans or pentests
  • Verifying agent configuration is complete in an existing workspace
  • Switching to a different agent space within the same AWS account
  • Ensuring IAM permissions and S3 bucket are correctly configured after AWS account changes
Who it's for
  • DevSecOps engineers setting up security scanning pipelines
  • AWS developers preparing workspaces for automated security assessments
  • Security teams configuring agent-based code review and penetration testing
  • Platform engineers establishing shared security agent infrastructure

setup-security-agent FAQ

What happens if the S3 bucket name is already taken?

If another AWS account owns the bucket name, setup aborts with a fatal error to prevent bucket-squatting attacks. The bucket name is derived from your account ID and region, so it's predictable; the skill uses --expected-bucket-owner to ensure you own it.

Can I reuse an existing agent space?

Yes. If agent spaces already exist in your account, the skill will show them and ask whether to reuse one or create a new one. It does not auto-select.

What is stored in .security-agent/config.json?

Only agent_space_id, region, and a map of code review IDs. Role name and bucket name are derived by convention (not stored) to avoid drift if resources are recreated manually.

What IAM permissions does SecurityAgentScanRole need?

S3 read access to the scans bucket, and CloudWatch Logs write access for agent output. The skill creates the role with these permissions automatically.

Do I need to run this skill every time I scan?

No, only once per workspace. Subsequent scans and pentests assume this setup is complete and reuse the same agent space, role, and bucket.

Full instructions (SKILL.md)

Source of truth, from aws/agent-toolkit-for-aws.


name: setup-security-agent description: Configure AWS Security Agent for the current workspace — provision or reuse an agent space, IAM service role, and S3 bucket. Use when the user asks to "set up security agent", "configure security scanner", "is security agent configured", or on first-time use before any scan or pentest.

AWS Security Agent — Setup

This skill handles ONE thing: making sure the workspace has a working agent space, IAM service role, and S3 bucket linked together. Scans and pentests live in separate skills and assume this is done.


Local state convention

All Security Agent skills share workspace-local state at .security-agent/:

  • config.json — { "agent_space_id": "as-...", "region": "us-east-1", "code_reviews": { "<abs_path>": "cr-..." } }. Account ID, role ARN, and bucket name are derived by convention. The code_reviews map lets scans reuse the same CodeReview for a workspace.
  • scans.json — array of { scan_id, code_review_id, job_id, agent_space_id, scan_type, title, started_at, status, path } (keep last 50)
  • pentests.json — same shape, for pentest jobs
  • .gitignore — contents * so this directory stays untracked
  • findings-{scan_id}.md — written by the scan skill after each scan completes

This skill's job is to populate config.json and create .gitignore.

Derived values (convention over config)

Other skills compute these on each invocation rather than reading them from config.json:

ValueConvention
ACCOUNTaws sts get-caller-identity --query Account --output text
REGIONconfig.region (default us-east-1)
service_role_arnarn:aws:iam::${ACCOUNT}:role/SecurityAgentScanRole
s3_bucketsecurity-agent-scans-${ACCOUNT}-${REGION}

Why minimal config: the role name and bucket name are deterministic, so storing them adds drift risk (a user re-creating a role manually would silently use a stale path). Only agent_space_id is stored because users may have multiple agent spaces and we don't want to ask which one every session.


Workflow

  1. Check existing state: read .security-agent/config.json if it exists.

  2. Caller identity + region:

    export ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
    export REGION="${AWS_REGION:-us-east-1}"
    
  3. Agent space:

    • If config.agent_space_id is set, verify with:

      aws securityagent batch-get-agent-spaces --agent-space-ids <id>
      

      If the response shows it doesn't exist, treat as missing.

    • If missing, list existing:

      aws securityagent list-agent-spaces
      
      • If any exist → show them to the user with name + id and ask: "Would you like to reuse one of these, or should I create a new one?" Wait for the answer. Do not auto-select.

      • If user picks one, use that agentSpaceId.

      • If user wants new, or none exist:

        aws securityagent create-agent-space --name security-scans
        

        Capture returned agentSpaceId.

  4. Service role (SecurityAgentScanRole, ARN arn:aws:iam::$ACCOUNT:role/SecurityAgentScanRole):

    • Probe:

      aws iam get-role --role-name SecurityAgentScanRole
      
    • If NoSuchEntity is returned, create the role. Idempotency note: create-role will fail with EntityAlreadyExists if the role already exists. If that happens, fall through to update-assume-role-policy to ensure the trust policy is correct.

      # Trust policy — includes aws:SourceAccount confused-deputy guard
      cat > /tmp/sa-trust.json <<EOF
      {"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"securityagent.amazonaws.com"},"Action":"sts:AssumeRole","Condition":{"StringEquals":{"aws:SourceAccount":"${ACCOUNT}"}}}]}
      EOF
      # Permissions policy (S3 + CloudWatch Logs)
      cat > /tmp/sa-perms.json <<EOF
      {"Version":"2012-10-17","Statement":[
        {"Effect":"Allow","Action":["s3:GetObject","s3:GetObjectVersion","s3:ListBucket"],"Resource":["arn:aws:s3:::security-agent-scans-${ACCOUNT}-${REGION}","arn:aws:s3:::security-agent-scans-${ACCOUNT}-${REGION}/*"]},
        {"Effect":"Allow","Action":["logs:CreateLogGroup","logs:CreateLogStream","logs:PutLogEvents"],"Resource":"arn:aws:logs:*:${ACCOUNT}:log-group:/aws/securityagent/*"}
      ]}
      EOF
      
      aws iam create-role --role-name SecurityAgentScanRole --assume-role-policy-document file:///tmp/sa-trust.json
      # if EntityAlreadyExists:
      aws iam update-assume-role-policy --role-name SecurityAgentScanRole --policy-document file:///tmp/sa-trust.json
      # always (re)apply permissions:
      aws iam put-role-policy --role-name SecurityAgentScanRole --policy-name SecurityAgentCodeReviewAccess --policy-document file:///tmp/sa-perms.json
      
  5. S3 bucket (security-agent-scans-$ACCOUNT-$REGION):

    Bucket-ownership enforcement (required). The bucket name is derived from the caller's AWS account ID and region — both non-secret and publicly derivable from ARNs / ECR URIs — so any third party can pre-register ("squat") the predictable name in their own account. Every S3 call MUST pass --expected-bucket-owner "$ACCOUNT" so the operation fails closed if the bucket is owned by someone else. A 403 Forbidden on a bucket that exists but is foreign-owned is fatal — abort setup and never upload.

    • Probe (asserts ownership):

      BUCKET="security-agent-scans-${ACCOUNT}-${REGION}"
      NEED_CREATE=0
      if aws s3api head-bucket --bucket "$BUCKET" --expected-bucket-owner "$ACCOUNT" 2>/tmp/sa-head.err; then
        : # bucket exists and is owned by this account — safe to reuse
      elif grep -q '404' /tmp/sa-head.err; then
        NEED_CREATE=1
      elif grep -Eq '403|Forbidden' /tmp/sa-head.err; then
        echo "FATAL: bucket $BUCKET exists but is owned by another account (403). Possible bucket-squatting — aborting. Nothing was uploaded." >&2
        exit 1
      else
        cat /tmp/sa-head.err >&2; exit 1
      fi
      
    • If not found, create it. A BucketAlreadyExists error means another account already holds the global name — treat it as fatal and distinct from BucketAlreadyOwnedByYou (which is a safe no-op). Re-assert ownership after creation before any further use:

      if [ "$NEED_CREATE" = "1" ]; then
        if [ "$REGION" = "us-east-1" ]; then
          # us-east-1: no LocationConstraint
          aws s3api create-bucket --bucket "$BUCKET" 2>/tmp/sa-create.err || true
        else
          # other regions:
          aws s3api create-bucket --bucket "$BUCKET" \
            --create-bucket-configuration LocationConstraint="$REGION" 2>/tmp/sa-create.err || true
        fi
        if grep -q 'BucketAlreadyExists' /tmp/sa-create.err; then
          echo "FATAL: bucket name $BUCKET is already owned by another account (BucketAlreadyExists). Possible bucket-squatting — aborting." >&2
          exit 1
        elif [ -s /tmp/sa-create.err ] && ! grep -q 'BucketAlreadyOwnedByYou' /tmp/sa-create.err; then
          cat /tmp/sa-create.err >&2; exit 1
        fi
        # Confirm ownership of the freshly created bucket before using it.
        aws s3api head-bucket --bucket "$BUCKET" --expected-bucket-owner "$ACCOUNT"
      fi
      
    • Always (re)apply public access block + 30-day lifecycle:

      aws s3api put-public-access-block --bucket "$BUCKET" \
        --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
      
      cat > /tmp/sa-lifecycle.json <<'EOF'
      {"Rules":[{"ID":"AutoDeleteUploads","Status":"Enabled","Filter":{"Prefix":""},"Expiration":{"Days":30}}]}
      EOF
      aws s3api put-bucket-lifecycle-configuration --bucket "$BUCKET" --lifecycle-configuration file:///tmp/sa-lifecycle.json
      
  6. Register role + bucket on the agent space (idempotent):

    • Read existing resources:

      aws securityagent batch-get-agent-spaces --agent-space-ids <id>
      

      Look at agentSpaces[0].awsResources.iamRoles and awsResources.s3Buckets.

    • If the role ARN or the bucket name is missing from those lists, merge and update:

      aws securityagent update-agent-space --agent-space-id <id> --name <existing-name> \
        --aws-resources iamRoles=[<arn1>,<arn2>...],s3Buckets=[<bucket1>,<bucket2>...]
      
  7. Persist to .security-agent/config.json (minimal — account/role/bucket are derived):

    {
      "agent_space_id": "as-xxxxx",
      "region": "us-east-1"
    }
    
  8. Create gitignore if missing:

    mkdir -p .security-agent
    echo '*' > .security-agent/.gitignore
    
  9. Confirm to user: "Setup complete. You can run security scans or pentests now."


Rules

  • Never auto-select an agent space when multiple exist — always ask the user
  • Never disable safety protections (the public-access-block stays on)
  • Every S3 call against the derived bucket MUST pass --expected-bucket-owner "$ACCOUNT". The bucket name is derived from a non-secret account ID, so a third party can pre-register it; a 403/BucketAlreadyExists on a foreign-owned bucket is fatal — abort and never upload.
  • Trust policy must allow securityagent.amazonaws.com (production service principal) and include the aws:SourceAccount confused-deputy guard
  • If the user provides their own role name or bucket name (different from the conventional defaults), tell them: this plugin uses convention-based defaults (SecurityAgentScanRole / security-agent-scans-${ACCOUNT}-${REGION}). Either accept those defaults or extend the skill — the other skills derive these names rather than reading them from config.
  • The scan and pentest skills can call this skill inline if config.json is missing — first-time users don't need to run setup separately.

Troubleshooting

  • AccessDenied calling iam:CreateRole → user lacks IAM permissions. Ask them to run setup with their own role ARN, or to grant iam:CreateRole + iam:PutRolePolicy.
  • AccessDenied on s3api create-bucket → either the bucket name is taken globally, or the user lacks s3:CreateBucket. Suggest using an existing bucket they own and pass it explicitly.
  • 403 Forbidden / BucketAlreadyExists on the derived bucket → the predictable name is owned by a different account (bucket-squatting). This is fatal by design — do not upload. Have the user pick a bucket name they own (or run from the expected account/region) and re-run setup.
  • Role exists but trust policy is wrong → update-assume-role-policy (step 4 fallback). If they don't want that role updated, ask them for a different role ARN.
  • Agent space exists but in a different region → tell the user; suggest using the right region or creating a new space in the current region.