work / Beruni
a read-only AWS security scanner that ranks findings by what can actually reach your data, not by raw severity.
Two engineers who couldn't justify a Wiz quote.
the problem
Most AWS breaches don't come from clever exploits. They come from a misconfiguration someone shipped on a Friday: a public S3 bucket, an over-permissioned IAM role, a security group open to the world. The tools that catch this (the enterprise CNAPP suites) start at quotes a ten-person startup can't sign.
The cheaper scanners have the opposite problem. They dump a flat list sorted by CVSS severity, and you're left reading 200 "criticals" with no idea which three can actually be reached from the internet. Severity in isolation is noise.
how it works
You deploy one CloudFormation template in your AWS account. It creates a read-only IAM role with an ExternalId, scoped to AWS's SecurityAudit policy: List*, Describe*, Get* and nothing else. No write, no modify, no delete.
Beruni's scanner calls sts:AssumeRole with your ExternalId. AWS hands back a short-lived session token, the scanner walks the resources it can read, and evaluates each against the rule library. Nothing is deployed inside your account beyond the role itself; the scan runs on our infrastructure. We never see access keys, only per-scan STS tokens that expire when the scan finishes. Rotating the ExternalId revokes us instantly.
the findings table
Findings land in a table: severity, rule ID, resource, region, time, and a 0–100 risk score. The score is the point. It comes from a reachability and identity graph, so the findings that something can actually reach sort to the top, instead of a single account-wide grade that hides which of 200 findings matter.
Every finding cites the control it traces to: CIS AWS Foundations, AWS FSBP, PCI DSS, GDPR. The table doubles as a compliance map, not just a bug list.
reachability & attack paths
This is the part flat scanners skip. Beruni builds a graph of what can reach what, then pins the toxic combinations on top: an internet-exposed instance, the role it can assume, the sensitive bucket that role can read. One attack path, three findings that are each unremarkable alone.
So the critical finding nothing can reach drops down the list, and the medium one sitting on a path to your data rises. You read fewer findings, in the right order.
a single finding
Open any finding and you get the resource, the exact AWS field that tripped the rule, the controls it maps to, and the raw evidence the scan pulled. No black box: you can see why Beruni flagged it and check it against your own console.
scope
IAM, S3, EC2, VPC, and RDS: five services where most AWS breaches happen, and roughly the entire stack a SaaS startup runs on day one. More services land as the beta runs.
rules are plain JSON
Each check is one JSON file: the AWS field it inspects and the condition it expects, written in JMESPath, the same query language the AWS CLI uses. No proprietary DSL.
the lineage
Beruni is my senior project, IntraSec, grown up. IntraSec scanned an internal network for vulnerabilities with no agent on any host. Beruni scans an AWS account for misconfigurations with nothing deployed but a read-only role. Same constraint (read-only, no agent, infer the risk from the outside) pointed at the cloud instead of the LAN, and shipped as a product instead of a thesis.
links