fix: token cache key, policy simulator creds, s3 rm guardrail, sync-aws-config - #18
Merged
Merged
Conversation
The key was built from the builtin hash(), which is randomized per process, so a cached token was never found by the next invocation. Every command re-ran the browser login and orphaned token entries accumulated. Use a truncated SHA-256 of the start URL instead.
…ones simulate() built its IAM client from the default credential chain, so check-policy and exec --check-action ran as whatever identity the shell happened to have (potentially a different account) rather than the role being checked. Pass the role credentials through explicitly. Also drop a reference to a --role-arn flag that does not exist.
The pattern required --recursive to appear before "s3", which no real invocation does, so "aws s3 rm s3://b --recursive" was never blocked despite the docs saying so. Match "aws*s3 rm*--recursive*" instead. Also drop the built-in ec2 terminate-instances pattern, which could not match any real command; users can still deny it via guardrails.yaml.
…g ~/.aws/config - --prefix collapsed every account into a single profile named exactly <prefix>, so only the last account survived. Profiles are now named <prefix>-<alias>[-<role>], and two account/role pairs that map to the same name raise a ConfigError instead of silently dropping one. - The file was round-tripped through configparser, which deleted comments, lowercased keys and reflowed everything, contradicting the docs. It is now edited as text: only orgctl-managed sections are replaced or appended and everything else, including line endings, is preserved byte-for-byte. - The single .bak was overwritten on every run, so the user's original file was gone after the second sync. Backups are now timestamped and never overwritten, and a run that changes nothing writes nothing. sync() now returns a SyncResult that includes the backup path.
This was referenced Sep 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes four correctness bugs found in a review of the repo. Each fix is its own commit and comes with regression tests.
hash(), which is randomized per process, so every command re-ran the browser login (andcreds-processdid on everyawscall) and orphaned token entries piled up. The key is now a truncated SHA-256 of the start URL.check-policy/--check-actionused the wrong credentials.simulate()built its IAM client from ambient credentials instead of the role's. It now takes the role credentials explicitly. Also removes a reference to a--role-arnflag that doesn't exist.s3 rmguardrail never matched. The pattern required--recursivebefores3. It is nowaws*s3 rm*--recursive*. The built-inec2 terminate-instancespattern could not match any real command and is removed (users can still deny it inguardrails.yaml).sync-aws-configbugs.--prefixcollapsed every account into one profile. Profiles are now<prefix>-<alias>[-<role>], and two entries mapping to the same name raise an error.configparser, deleting comments and reflowing everything. It is now edited as text, so only orgctl-managed sections change and everything else is preserved byte-for-byte, including line endings..bakwas overwritten on every run, so the original was lost after two syncs. Backups are now timestamped and never overwritten, and a run that changes nothing writes nothing.README and
docs/THREAT_MODEL.mdare updated to match.Behavior changes
--prefixwith--all-rolesnow yields<prefix>-<alias>-<role>(previously<prefix>-<role>, which collided across accounts).aws_config_sync.sync()returns aSyncResult(a named tuple that now includes the backup path). Callers unpacking four values need updating; onlycli.pyand the tests call it.~/.aws/config.bak-<timestamp>instead of a single~/.aws/config.bak.--prefix/--all-rolesleaves the old ones behind (documented in the README).Verification
ruff check,ruff format --check,mypy src: clean.pytest: 80 passed (was 49); coverage 43% to 51%.orgctl sync-aws-configend to end against a scratch~/.aws/configcontaining comments: comments survived,--prefixproduced one profile per account, an identical re-run made no backup, and each change made a new backup with the original intact.Not in this PR
Still open from the review: other built-in deny patterns also require
awsimmediately followed by the service (soaws --profile x s3 rb ... --forceslips through), CloudWatch push timestamps, uncaughtClientErroron a revoked token, and the CLI test coverage gap.