Skip to content

Entitlements, operations, and IAM

How CCE represents API actions (for example S3 read vs write), what to expect when one function uses multiple operations, and how to turn scan output into least-privilege IAM—without misconfiguring lenses.

What

An entitlement is one mapped call site in JSON:

Field Meaning
provider Cloud or custom label (AWS, PLATFORM, TECH_DEBT, …)
resource Service or module (s3, kms, platform package segment, …)
operation API action or method segment (GetObject, PutObject, Sync, …)
file / line Where the call appears in source
method Fully qualified name CCE extracted
parent_function Enclosing function (when available)

Model: one static call site → one operation per entitlement row. CCE does not emit a single row with multiple operations (for example GetObject + PutObject combined).

The built-in cloud mapper derives operation from the SDK method name at that line (cloud entitlements).

Why

IAM and PoLP need the set of actions code can invoke—not a single label per business function. A handler that downloads and uploads artifacts needs both s3:GetObject and s3:PutObject in policy (plus any others found in scan).

Understanding the one-call-site-one-operation model avoids:

  • Broken lens YAML that tries to “declare” read+write in one rule
  • Under-provisioned roles when only a wrapper method appears in app code
  • Confusion when cce diff reports two new entitlements for one logical feature change

CCE inventories code; it does not compute effective IAM permissions, resource ARNs, or bucket names from policies alone (KNOWN_LIMITATIONS).


One function, S3 read and S3 write (direct SDK calls)

What happens: If SyncArtifact() calls GetObject and PutObject (or equivalent SDK methods), the scan produces two entitlements—typically different lines, same or different parent_function.

cce -folder . -language AUTO -filter cloud -format json -output entitlements.json

Example rows:

{
  "provider": "AWS",
  "resource": "s3",
  "operation": "GetObject",
  "file": "internal/storage/artifact.go",
  "line": 42,
  "parent_function": "SyncArtifact"
}
{
  "provider": "AWS",
  "resource": "s3",
  "operation": "PutObject",
  "file": "internal/storage/artifact.go",
  "line": 58,
  "parent_function": "SyncArtifact"
}

Why this is correct: Least privilege is defined over API actions actually invoked. Two calls → two facts → union when building a policy.

How to build IAM from the report: Aggregate unique operations for the scope you need (function, package, or whole repo):

# All S3 operations used by one function
jq '[.entitlements[]
  | select(.resource == "s3" and .parent_function == "SyncArtifact")
  | .operation] | unique' entitlements.json

# All S3 operations in the repo
jq '[.entitlements[] | select(.resource == "s3") | .operation] | unique' entitlements.json

Map each operation to IAM action names your policy generator expects (for example GetObjects3:GetObject).

cce diff: Each entitlement is keyed by (provider, resource, operation, file, line, method). Adding a write path without a read path shows one new row—the new call site—not “dual operation on same key.”


Read vs write in custom lenses

What: Separate Grok rules (or prefix + captured operation) per operation family—not one rule that assigns two operations.

Why: The mapper returns a single MappedResource per call; the first matching rule wins when several could apply.

How: Example for AWS SDK for Go v2 S3 client methods:

definition:
  language:
    go:
      - name: "S3 read APIs"
        prefix: "github.com/aws/aws-sdk-go-v2/service/s3."
        provider: AWS
        rule: "github\\.com/aws/aws-sdk-go-v2/service/s3\\.(?<operation>GetObject|HeadObject|ListObjectsV2)"
      - name: "S3 write APIs"
        prefix: "github.com/aws/aws-sdk-go-v2/service/s3."
        provider: AWS
        rule: "github\\.com/aws/aws-sdk-go-v2/service/s3\\.(?<operation>PutObject|DeleteObject|CreateMultipartUpload)"

Each physical GetObject / PutObject invocation still produces one row with the captured operation.

Put more specific rules before generic ones to avoid “multiple language definitions matched” warnings (Custom lens YAML).

For runbooks that care about read vs write (KMS decrypt vs encrypt, etc.), filter on operation after scan — see runbook-targeting.


Wrappers and facades

What: Application code calls platform.Storage.Sync(); S3 SDK calls live inside the platform library.

Why it matters: CCE only maps call sites visible in the scanned tree. You may see one entitlement for Sync, not GetObject + PutObject, unless those SDK calls appear in analyzed files.

How to handle it:

Approach When
Scan the platform SDK repository (or vendored copy) Ground truth for what the wrapper invokes
Add lens rules for the wrapper FQN Inventory adoption; use a single operation label (e.g. Sync) for reporting
Maintain a wrapper → IAM action list outside CCE Policy templates for known composite helpers
Extend lens for wrapper shape See prowler for non-standard client patterns

Do not expect one lens rule to emit both read and write for a single wrapper method name—use multiple visible SDK call sites or external IAM documentation.


What CCE does not do (IAM synthesis)

Expectation Reality
One entitlement row with operations: [GetObject, PutObject] Not supported — aggregate in your tooling
Infer read+write from a high-level method without child calls Not supported — static call graph only
Effective IAM / SCP / permission boundaries Out of scope — code invocation inventory only
Bucket/key ARNs from all SDK calls Limited; some patterns use extract_resource_from — see Custom lens YAML

Decision guide

Your situation What to do
Function calls GetObject and PutObject in app code Default cce -filter cloud; union operations for IAM
CI gate on new cloud usage cce diff + optional policy YAML (recipes)
Custom lens for internal SDK One rule per detectable method/pattern; enterprise guide
Single wrapper, need both S3 read and write in policy Scan SDK implementation or document explicit action list; don’t overload one lens row
Platform vs direct cloud adoption platform-adoption lens