Aqua's built-in reports are too generic for tracking SLA breaches. I needed a custom vulnerability aging report for compliance. Here's how to pull the data via their API and structure it.
First, get a list of images with vulnerabilities older than your threshold (e.g., 30 days). Use the `/risks/vulnerabilities` endpoint with a filter.
```bash
curl -X GET "https:///api/v2/risks/vulnerabilities"
-H "Authorization: Bearer $TOKEN"
-H "Content-Type: application/json"
--data-raw '{
"filters": [
{"field": "date", "operator": ">", "value": "30d"},
{"field": "resource_type", "operator": "=", "value": "image"}
]
}'
```
Parse the response, then for each image, fetch its details and the specific vulnerability history. Key fields:
* Image name & registry
* CVE ID
* Severity
* First discovered date (`discovered_date`)
* Fix status
I pipe this into a simple script that calculates the age and outputs CSV. The API paginates, so handle `next_page` tokens. This gives you a clear list to pressure engineering teams with. -dk
Trust but verify, then don't trust.
Nice! I've been wrestling with similar compliance reports and the `discovered_date` field is a lifesaver for aging. One thing I ran into: that filter you wrote for `"field": "date"` might actually be pulling the *detection* date in your current scan, not the *first* discovered date in Aqua's history. I had to cross-reference with the `/images` endpoint to get the accurate timeline.
Also, when handling pagination, watch out for rate limits if you have a big backlog. I ended up adding a small sleep in my script between page requests. What's your typical batch size for these reports?
Data nerd out
You're absolutely right about that date field - I made the same mistake initially. The detection date resets on every scan, which completely throws off aging calculations for vulnerabilities that were previously fixed and reappeared. I ended up building a small lookup table from the audit logs to track the true first discovery timeline.
For pagination, I keep batches at 100 items and throttle to one request per second. Even then, we occasionally hit limits during monthly compliance runs because our registry is huge. The API team suggested using the async export endpoints for datasets over 10k records, but that adds complexity with job polling.
The right tool saves a thousand meetings.
Ah, the audit log workaround. Clever. I tried something similar but ran into retention issues - our logs only go back 90 days, which makes "true first discovery" a bit of a guessing game for older images.
The async export is a double-edged sword. Sure, it handles volume, but you're trading rate limits for job status hell and eventual consistency. I've had exports finish with data that's already 20 minutes stale, which is just fantastic for compliance deadlines.
Data over dogma.
So you're building a whole custom system because their built-in reporting fails at its core job. Sounds like a product issue, not a workflow hack. What's the point of paying for an enterprise security tool if you have to script around basic compliance reporting?
That date field caveat everyone's mentioning? Means your initial filter's already broken. You'll be chasing ghosts.
And pressuring engineering teams with CSV exports just creates noise. They'll ignore it. You'd get better results fixing one old image than generating a hundred-page report.
Keep it simple
Your filter's using the wrong date field. That'll give you detection dates from the latest scan, not when the vuln first appeared. So your "30 day" threshold is meaningless.
Even if you fix that with discovered_date, you're just building a worse version of what their reporting should do out of the box. Now you're on the hook for maintaining the pagination logic, rate limit handling, and data integrity.
Piping CSV to pressure engineering teams is a great way to get ignored. They see a giant spreadsheet and tune out.
-- old school
The discovered_date field is crucial for your filter, but you should verify its behavior in your specific Aqua deployment. In some configurations, that field actually reflects when Aqua first ingested the vulnerability from its feeds, not necessarily when it appeared in *your* image during a scan. You'll need to cross-reference the image scan history endpoint to confirm the timeline for your compliance evidence.
Also, your CSV output strategy is sound, but for engineering pressure to be effective, you must segment the data further. A single list sorted by age will just overwhelm them. You need to group by:
* owning team (requires pulling image metadata tags)
* fix availability (exploitability score from the `/vulnerabilities` endpoint)
* environment (production vs. development, from registry metadata)
Without that segmentation, you're just creating noise.
You hit the nail on the head about stale data in async exports. That 20-minute lag isn't just an inconvenience, it's a data integrity problem for a compliance snapshot. I've resorted to tagging the export job's creation timestamp in the final report filename as a CYA measure, because the "finished" timestamp they give you is useless.
The 90-day audit log retention is a product-level failure. If they're marketing this as an enterprise compliance tool, historical provenance for findings is non-negotiable. My workaround was to start dumping relevant log events to our own SIEM weekly, but that's just another script to babysit.
Speed up your build
Exactly. The timestamp in the filename is the only audit trail you've got, because the async job's own metadata is garbage for compliance. I've been burned by that same lag.
Your SIEM dump is the correct, painful answer. We did the same, but we went a step further and stopped using the async export endpoint altogether for anything time-sensitive. Instead, we run the paginated sync calls during a predefined maintenance window when scan activity is low, and we accept that it might take a few hours. It's slower but the data is coherent. The async feature is only for historical backfills now.
The 90-day log retention is indefensible. It means you can't prove a critical finding was ignored for a year. Our "workaround" was to escalate it as a contractual compliance gap during renewal negotiations. That got their product team's attention faster than any support ticket.
The audit log retention limit is a contractual problem, not a technical one. We had the same 90-day ceiling and made it a renewal issue. Our amendment now requires 13 months of immutable audit trails for any finding classified as critical or high.
On the async export staleness, we found the same 20-minute lag. Our compliance team rejected it as evidence for quarterly audits. Their position was that a lagged snapshot doesn't prove the state at the report generation time. We had to revert to paginated sync calls during change freezes, as user423 mentioned.
Buy once, cry once.
Thanks for sharing your script. A lot of folks here have rightly pointed out the date field issue, which trips everyone up at first.
While CSV outputs are great for you, I'd gently push back on using them directly to pressure engineering teams. From experience, a massive, unsegmented list often backfires. They need it filtered by owning team and fix availability, or it just becomes noise.
Have you hit the pagination limits yet with your registry size? That's where the real pain starts for a lot of users.
~Harry
That discovered_date field is a trap, honestly. In our setup, it sometimes reflects when Aqua pulled the CVE data from its feed, not the first scan of our image. You have to cross-check the image scan history endpoint to get a timeline you can actually defend in an audit.
Also, piping raw CSV to engineers is a recipe for getting tuned out. I found you need to group it by team tags and, crucially, include the fix availability flag from the vulnerabilities endpoint. A list of 500 vulns is noise, but 10 vulns with available fixes for Team X is actionable.
Oh wow, the discovered_date field thing is a real gotcha. So even if you fix the filter from the guide, you might still be tracking the wrong timeline? That's... not great for an audit report.
You mentioned grouping by team tags and fix availability - how do you actually get the owning team? I'm assuming that's from image metadata or tags, but I've seen some teams not tag their images consistently at all. Do you have a fallback method?