Skip to content

Add AWS OIDC trust structural evaluator#138

Merged
pquerna merged 3 commits into
mainfrom
paul.querna/aws-oidc-trust-evaluator
Jul 22, 2026
Merged

Add AWS OIDC trust structural evaluator#138
pquerna merged 3 commits into
mainfrom
paul.querna/aws-oidc-trust-evaluator

Conversation

@c1-squire-dev

@c1-squire-dev c1-squire-dev Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements the read-only AWS OIDC trust evaluator core defined by SPEC-03, per D-NHI-012.

  • adds an isolated internal/awstrust package with the pure EvaluateWebIdentityTrust(expected, observed) []TrustMismatch boundary
  • validates provider/role ARN, account, provider URL/audience, exact trust-policy action and audience/subject conditions, optional tag-session permission, session duration, and bypassing parallel statements
  • extends IAM policy parsing for object/array statements; scalar/list action, federated principal, and condition values; and preserved unknown operators
  • returns stable safe mismatch codes without policy values, tokens, credentials, or session material
  • adds table-driven golden coverage for encoded/plain policies, parser shapes, exact and unsafe bindings, unrelated/parallel statements, duration boundaries, deterministic ordering, and output safety

Source grounding

The implementation is grounded in the existing connector and vendored SDK surfaces:

  • pkg/connector/role.go:(*roleResourceType).Grants already calls GetRole and consumes AssumeRolePolicyDocument
  • pkg/connector/helpers.go:TrustPolicy, Statement, Action, and Principal are the existing trust-policy parser types extended here
  • vendor/github.com/aws/aws-sdk-go-v2/service/iam/api_op_GetOpenIDConnectProvider.go:GetOpenIDConnectProviderOutput provides URL and client-ID/audience observations
  • vendor/github.com/aws/aws-sdk-go-v2/service/iam/types/types.go:Role provides role ARN, trust policy, and max-session observations

Relevant primary AWS references: GetRole, Role, GetOpenIDConnectProvider, OIDC provider configuration, Principal, and Condition operators.

Exact scope and non-goals

This PR is the internal structural evaluator only. It does not expose an action, add a C1 API/delivery path, mint or accept OIDC tokens, call STS, mutate IAM, persist health state, generate IaC, or claim end-to-end operational readiness. It is independent of baton-sdk #941 and does not modify or depend on baton-aws #137.

Test evidence

  • gofmt on changed Go files: pass
  • go test ./pkg/connector/... -count=1: pass
  • go test ./... -count=1: pass
  • golangci-lint run --new-from-rev=HEAD: pass, 0 issues
  • git diff --check: pass
  • repository-wide make lint: reports 19 pre-existing SDK deprecation (SA1019) findings in unchanged files; no findings are introduced by this diff

Add the pure, read-only structural evaluator and fail-closed IAM policy parsing required by SPEC-03. Cover stable mismatch codes with table-driven golden tests.

Grounded in baton-aws connector source and AWS IAM API documentation per D-NHI-012.

Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>
@c1-squire-dev
c1-squire-dev Bot requested a review from a team July 22, 2026 15:08
Comment thread pkg/connector/helpers.go Outdated
Comment on lines 317 to 329
type Condition map[string]map[string]StringValues

// StringValues handles both string and list forms used by IAM policy fields.
type StringValues []string

func (values *StringValues) UnmarshalJSON(data []byte) error {
parsed, err := unmarshalStringOrArray(data)
if err != nil {
return fmt.Errorf("condition value must be string or array: %w", err)
}
*values = parsed
return nil
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: Adding Condition to the shared Statement struct tightens parsing for the existing sync-path callers (extractTrustPrincipals, extractTrustPrincipalsByKind), not just the new evaluator. Previously the Condition key was ignored entirely, so any condition shape parsed. Now StringValues.UnmarshalJSON rejects anything that isn't a string or array-of-strings — IAM permits literal JSON booleans/numbers as condition values (e.g. "Bool":{"aws:MultiFactorAuthPresent":true}, numeric operators). If such a value appears in a role's trust policy, the whole statement fails to unmarshal, causing extractTrustPrincipals to silently drop that role's assume-role grants and extractTrustPrincipalsByKind to fall back to generic NHI classification. Consider coercing scalar bool/number to string in the unmarshaler so the strictness stays scoped to the evaluator rather than the shared type. (medium confidence)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e9e569b. I moved the strict evaluator and its private IAM policy parser into internal/awstrust, restored the shared pkg/connector Statement shape so existing sync parsing continues to ignore arbitrary Condition JSON, and added should ignore non-string condition values covering the boolean example. Full go test ./... and changed-code golangci-lint pass.

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Connector PR Review: Add AWS OIDC trust structural evaluator

Blocking Issues: 0 | Suggestions: 0 | Threads Resolved: 0
Criteria: Criteria status: loaded .claude/skills/ci-review.md from trusted base 943eaf16dd00.
Review mode: incremental since e9e569b
View review run

Review Summary

The full PR diff was scanned for security and correctness. The new commits migrate resource attributes (profile, status, created-at) off the deprecated trait options (WithUserProfile/WithGroupProfile/WithRoleProfile/WithAppProfile/WithStatus/WithSecretCreatedAt) onto the canonical resource-level options (WithResourceProfile/WithResourceStatus/WithResourceCreatedAt), and update the affected tests to read resource.GetProfile(). This aligns with the SDK, where trait-level profile/status/created-at are deprecated and mirrored onto the resource. Verified the cross-enum cast v2.Status_ResourceStatus(status) in sso_user.go is safe (both enums share identical values 0-3), the SDK helpers exist (no go.mod bump needed), and no connector code reads the removed trait profiles. No new issues found.

Security Issues

None found.

Correctness Issues

None found.

Suggestions

None.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

Move strict OIDC trust parsing into an internal package so it cannot change the connector sync parser. Add a regression test for valid boolean IAM conditions raised in PR review.

Decision: D-NHI-012

Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

Move profile, status, and created-at values off deprecated trait fields so the required repository lint check passes while preserving resource behavior.

Decision: D-NHI-012

Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>
@c1-squire-dev

c1-squire-dev Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

Required CI lint is fixed in 21d497e (not treated as approval-only): migrated all 19 deprecated trait profile/status/created-at fields to canonical Resource attributes and updated affected assertions. Local go test ./... -count=1, full golangci-lint run --timeout=6m (0 issues), and git diff --check pass. GitHub lint/test reruns are in progress.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@pquerna
pquerna merged commit b1f19c3 into main Jul 22, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant