“Read-only” is a claim until something checks it: how Skyline verifies credentials before scanning
Most inventory tools ask for read-only access and trust you got it right. We made Skyline prove it first, because the ReadOnlyAccess policy a team hands you is very often not what they think it is.
The first thing anyone asks before pointing an external tool at their cloud is "what can it do in there?". The honest answer for most inventory tools is: whatever the credential you gave it can do. They ask for read-only access, document that they only call read APIs, and leave the rest to your good intentions.
That is not good enough, for a reason we hit repeatedly in assessments: the credential a team believes is read-only frequently is not. Someone attached ReadOnlyAccess and also left an inline policy from a migration. Someone reused an existing automation role because creating a new one needed a ticket. Someone pasted their own keys because it was faster. In three of the last ten engagements, the "read-only" credential we were handed could write.
So Skyline does not take the claim on faith. Before the first inventory call, it asks the provider what the credential is actually allowed to do, and refuses to scan if the answer includes writes.
Asking the provider instead of asking the user
Each cloud has an API that evaluates a policy without executing anything. Skyline uses the one that fits the provider:
- AWS —
iam:SimulatePrincipalPolicy, which evaluates a list of actions against the principal's full effective policy set in a single call. It is included inReadOnlyAccess, so a genuinely read-only credential can always run it. - Google Cloud —
testIamPermissionson the project, folder or organisation in scope, which returns the subset of the probed permissions the caller actually holds. - Azure — the
Microsoft.Authorization/permissionslist for the subscription, which returns the effective actions and not-actions of the assigned roles. - Oracle Cloud — the tenancy's effective policies for the compartments in scope.
None of these calls change anything. They are the provider answering a hypothetical question about its own authorisation model, which is a much better source of truth than a human describing the role they think they created.
What we probe for
Probing every write action in AWS would be absurd, and unnecessary. What matters is a representative set: if a credential can do any of these, it is not read-only and the rest hardly matters. Skyline probes around forty actions grouped by what they would let an attacker do:
- Privilege escalation —
iam:CreateAccessKey,iam:AttachRolePolicy,iam:PutUserPolicy,iam:PassRole,iam:UpdateAssumeRolePolicy. A credential that can do any of these is effectively an administrator credential with extra steps. - Code execution —
ec2:RunInstances,lambda:UpdateFunctionCode,ecs:RunTask,ssm:SendCommand,ssm:StartSession. These turn read access into a shell. - Data tampering and destruction —
s3:PutObject,s3:PutBucketPolicy,rds:ModifyDBInstance,dynamodb:DeleteTable,kms:ScheduleKeyDeletion. - Audit evasion —
cloudtrail:StopLogging,cloudtrail:DeleteTrail. Anything that can turn off the record of what it did belongs in its own category.
The list is deliberately skewed towards permissions that are dangerous rather than permissions that are common. s3:PutObject on one logging bucket is a finding worth stopping for; so is iam:PassRole, which looks innocuous in a policy review and is the single most reliable escalation path in AWS.
Root credentials are refused outright
A special case worth stating plainly: if the identity resolves to the account root, Skyline stops before probing anything. Root always has write permissions, and no simulation is needed to know that. The error tells you to create a role with ReadOnlyAccess and come back with that.
This catches more people than it should. Root keys are still the fastest way for someone to get a scan done on a Friday afternoon, and the fastest way to hand a third party the keys to the entire account.
When verification itself is not possible
There is a failure mode we had to think about carefully. Some strictly read-only policies do not include the permission needed to run the verification. AWS ViewOnlyAccess, for instance, does not grant iam:SimulatePrincipalPolicy. A naive implementation would refuse the most restrictive credentials on the market, which is exactly backwards.
So the rule is asymmetric:
- Proven writes — refuse the scan and say which actions came back allowed.
- Proven read-only — scan.
- Cannot determine — scan, and carry a warning through to the report, so whoever reads it knows the guarantee was not established for this run.
Refusing to work when you cannot prove safety sounds rigorous, but in practice it pushes people towards broader credentials to make the tool stop complaining. A tool that makes the secure path annoying gets routed around.
The version with no credentials at all
The strongest version of this guarantee is not needing the credential. Skyline reads .tf files, whole project folders and terraform plan JSON, and builds the same map, the same security findings and the same cost estimate from the code alone. No role, no keys, nothing to revoke afterwards.
For a first look, that is usually the right call, and it is what we suggest when a client's security team is understandably reluctant to issue anything to an outside tool. The live scan adds what the code cannot know — resources created by hand, drift, real spend from the billing export, fourteen days of CPU metrics — but it is an upgrade, not a prerequisite.
What to take from this for your own tooling
The pattern generalises beyond our tool, and it is cheap to implement:
- Verify the permission, do not document it. Every major cloud has a policy-simulation API. One call before you start is the difference between a promise and a control.
- Probe for consequences, not for coverage. Forty well-chosen actions across escalation, execution, tampering and audit evasion beat a thousand exhaustive ones.
- Fail open with a warning when you cannot verify, fail closed when you can. Otherwise you train people to over-grant.
- Make the zero-credential path a first-class feature, not a degraded mode.
If you want the report without the argument about credentials, try Skyline on a Terraform folder first. And if this is the kind of thinking you want applied to your own access model, that is what our security work is.