Skip to content
Notifications
Clear all

Check out my Python script to compare Aqua scan results across registries.

2 Posts
2 Users
0 Reactions
2 Views
(@db_diver)
Estimable Member
Joined: 4 months ago
Posts: 93
Topic starter   [#10620]

Having recently completed a security audit for a multi-cloud application leveraging containerized microservices, I was tasked with consolidating vulnerability reports across several disparate artifact registries (AWS ECR, Google Artifact Registry, and a private Harbor instance). While Aqua Security's Trivy provides excellent scanning capabilities, the native output and console experience can become cumbersome when you need a unified, comparative view of image security posture across these different environments. The Aqua Enterprise console does aggregate this, but for my specific need—a quick, scriptable diff for a curated list of production images—I found the API approach more direct.

I developed a Python script to programmatically fetch scan results from multiple registries configured within Aqua, normalize the critical data, and present a consolidated table. The primary goal was to identify inconsistencies (e.g., the same image tag having different vulnerability counts in different registries due to scan timing or policy differences) and to get a registry-level health summary. The script leverages the Aqua `v2/images` API endpoint with appropriate filtering.

```python
import requests
import pandas as pd
from datetime import datetime

AQUA_URL = "https://your-aqua-instance.com"
API_KEY = "your_api_key_here"
HEADERS = {"X-API-Key": API_KEY}

# Define your registries and the images to compare
REGISTRIES = ["aws-ecr-prod", "gcp-artifact-prod", "on-prem-harbor"]
IMAGE_LIST = ["myapp/api", "myapp/worker", "myapp/frontend"]

def fetch_images(registry):
"""Fetches image scan data for a given registry."""
all_images = []
url = f"{AQUA_URL}/api/v2/images"
params = {
"registry": registry,
"include_summary": "true",
"order_by": "image",
"page_size": 50
}
response = requests.get(url, headers=HEADERS, params=params)
if response.status_code == 200:
all_images.extend(response.json().get('result', []))
return {img['name']: img for img in all_images}

def main():
comparison_data = []
for registry in REGISTRIES:
registry_images = fetch_images(registry)
for target_image in IMAGE_LIST:
img_data = registry_images.get(target_image)
if img_data:
summary = img_data.get('summary', {})
comparison_data.append({
'registry': registry,
'image': target_image,
'critical_vulns': summary.get('critical_vulns', 0),
'high_vulns': summary.get('high_vulns', 0),
'total_vulns': summary.get('total_vulns', 0),
'last_scan': img_data.get('last_scan_date', 'N/A'),
'digest': img_data.get('digest', '')[:16]
})

# Create DataFrame and pivot for easier comparison
df = pd.DataFrame(comparison_data)
if not df.empty:
pivot = df.pivot_table(index='image', columns='registry',
values=['critical_vulns', 'high_vulns', 'total_vulns'],
aggfunc='first', fill_value='N/A')
print(pivot.to_markdown())
# Optional: Export to CSV
df.to_csv(f'aqua_registry_comparison_{datetime.now().date()}.csv', index=False)
else:
print("No matching image data found.")

if __name__ == "__main__":
main()
```

Key considerations and observations from building this:

* The script highlights a common operational challenge: the same logical image (by tag) can have different digests and thus different vulnerability states across registries. The digest column helps confirm if you are comparing identical binaries.
* Rate limiting and pagination handling are omitted for clarity but are essential for production use against large catalogs. The Aqua API paginates results via `page_token`.
* This approach is useful for generating periodic reports or for integration into a CI/CD pipeline that gates promotions based on cross-registry vulnerability thresholds.
* A notable limitation is that the script compares only the *summary* counts. For a deep dive into specific CVE discrepancies, one would need to extend it to call the per-image vulnerability endpoint (`/api/v2/images/{image_id}/vulnerabilities`) and perform a diff.

This method proved more efficient for my team than manually toggling between registry views in the UI, providing a clear, actionable snapshot. I'm interested if others have tackled similar consolidation challenges with Aqua or other security scanners and what architectural patterns you used.


SQL is not dead.


   
Quote
(@fionah)
Estimable Member
Joined: 1 week ago
Posts: 80
 

The API approach is more direct, until Aqua changes the endpoint or the auth schema and your script breaks in six months. What's your plan for handling that maintenance burden versus just using the console you already pay for?

And comparing image tags across registries is a neat trick, but you're assuming the tags are immutable. If someone rebuilt and pushed the same tag, your 'inconsistency' is just a different image. The script would report a vulnerability diff for what it thinks is the same artifact, but isn't.


trust but verify


   
ReplyQuote