تعزيز أمان الحاويات باستخدام Anchore Grype: الدليل الشامل لمنصة الذكاء الاصطناعي السحابية
⏱️ وقت القراءة المقدر: 15 دقيقة
أمان الحاويات في منصات الذكاء الاصطناعي السحابية يكتسب أهمية متزايدة. مع تزايد اعتماد الفرق على صور Docker لنشر نماذج الذكاء الاصطناعي وخدمات الاستدلال، أصبح فحص الثغرات الأمنية في هذه الصور خطوة لا غنى عنها في دورة التطوير.
Grype من Anchore هو أداة مفتوحة المصدر لفحص الثغرات في صور الحاويات، تحظى بأكثر من 10,100 نجمة على GitHub. تتميز بسرعتها وقاعدة بياناتها الشاملة وسهولة دمجها في خطوط CI/CD.
نظرة عامة على Grype
الميزات الرئيسية
دعم واسع: تفحص صور Docker وملفات Dockerfile وتوزيعات Linux المختلفة (Alpine, Ubuntu, Debian, CentOS, RHEL, Amazon Linux).
دعم مديري الحزم المتعددين: pip (Python), npm (Node.js), gem (Ruby), jar/war (Java), go modules, cargo (Rust).
قاعدة بيانات ثغرات محدّثة: تتزامن مع NVD وGitHub Advisory وRed Hat Security وDebian Security.
مخرجات مرنة: يدعم تنسيقات JSON, table, cyclonedx, sarif.
دمج CI/CD: يتكامل بسلاسة مع GitHub Actions وGitLab CI وJenkins وغيرها.
بيئة الاختبار
# المواصفات المستخدمة في هذا الدليل
النظام: macOS 15 / Ubuntu 22.04
Docker: 24.0.5
Grype: 0.74.x
Syft: 0.90.x (اختياري، لإنشاء SBOM)
التثبيت
على macOS
# عبر Homebrew (موصى به)
brew install anchore/grype/grype
# التحقق من الإصدار
grype version
على Linux
# باستخدام سكريبت التثبيت الرسمي
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
# أو عبر APT (Debian/Ubuntu)
# تثبيت المتطلبات أولًا
sudo apt-get install -y wget apt-transport-https gnupg
# إضافة مستودع Anchore
wget -qO - https://artifacts.anchore.io/apt/gpg.key | sudo apt-key add -
echo "deb https://artifacts.anchore.io/apt stable main" | sudo tee /etc/apt/sources.list.d/anchore.list
sudo apt-get update && sudo apt-get install -y grype
عبر Docker
# الفحص بدون تثبيت
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
anchore/grype:latest \
your-image:latest
عبر Go
# تثبيت من المصدر
go install github.com/anchore/grype@latest
الاستخدام الأساسي
فحص صورة Docker
# فحص بسيط
grype nginx:latest
# فحص مع مخرجات تفصيلية
grype python:3.11-slim -v
# فحص مع تصفية حسب الخطورة
grype ubuntu:22.04 --fail-on critical
# فحص بمخرجات JSON
grype alpine:3.18 -o json > vulnerabilities.json
تنسيقات المخرجات
# تنسيق الجدول (الافتراضي)
grype nginx:latest -o table
# تنسيق JSON للمعالجة البرمجية
grype nginx:latest -o json
# تنسيق CycloneDX (SBOM)
grype nginx:latest -o cyclonedx-json
# تنسيق SARIF (لـ GitHub Code Scanning)
grype nginx:latest -o sarif
تصفية حسب مستوى الخطورة
# إخفاء الثغرات منخفضة الخطورة
grype nginx:latest --ignore-states = won't-fix
# الفشل فقط عند وجود ثغرات حرجة أو عالية
grype nginx:latest --fail-on high
# فحص مع تعيين مستوى الحد الأدنى
grype nginx:latest --fail-on critical --fail-on high
فحص مجلد محلي
# فحص مجلد المشروع
grype dir:./myproject
# فحص بيئة Python المحلية
grype dir:./venv
# فحص مع تحديد النوع
grype dir:./app --scope all-layers
حالات الاستخدام في منصة الذكاء الاصطناعي السحابية
دمج مع GitHub Actions
# .github/workflows/security-scan.yml
# سير عمل فحص الأمان
name: أمان الحاويات - Grype
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
schedule:
# فحص يومي في منتصف الليل
- cron: '0 0 * * *'
jobs:
scan-images:
name: فحص الثغرات الأمنية
runs-on: ubuntu-latest
strategy:
matrix:
image:
- name: "واجهة API"
path: "./services/api"
tag: "api-service"
- name: "خدمة الاستدلال"
path: "./services/inference"
tag: "inference-service"
- name: "خدمة النماذج"
path: "./services/models"
tag: "model-service"
steps:
- name: استنساخ المستودع
uses: actions/checkout@v4
- name: إعداد Docker Buildx
uses: docker/setup-buildx-action@v3
- name: بناء صورة Docker
uses: docker/build-push-action@v5
with:
context: $
tags: $:test
load: true
cache-from: type=gha
cache-to: type=gha,mode=max
- name: تثبيت Grype
uses: anchore/scan-action/download-grype@v3
- name: فحص الثغرات الأمنية
id: security-scan
uses: anchore/scan-action@v3
with:
image: "$:test"
fail-build: true
severity-cutoff: high
output-format: sarif
continue-on-error: true
- name: رفع نتائج SARIF إلى GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: $
category: "container-security-$"
- name: إنشاء تقرير مفصل
if: failure()
run: |
echo "## تقرير الأمان: $" >> $GITHUB_STEP_SUMMARY
grype $:test \
--fail-on high \
-o table | head -50 >> $GITHUB_STEP_SUMMARY
- name: فشل في حالة ثغرات حرجة
if: steps.security-scan.outcome == 'failure'
run: exit 1
دمج مع GitLab CI
# .gitlab-ci.yml (قسم الأمان)
# دمج Grype في GitLab CI
variables:
GRYPE_VERSION: "0.74.0"
DOCKER_TLS_CERTDIR: ""
stages:
- build
- security
- deploy
# قالب مشترك لفحص الأمان
.security-scan-template: &security-scan
stage: security
image: docker:24
services:
- docker:24-dind
before_script:
- curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin v${GRYPE_VERSION}
script:
- grype ${TARGET_IMAGE} --fail-on high -o json > grype-report.json
- grype ${TARGET_IMAGE} --fail-on high -o table
artifacts:
when: always
reports:
# SARIF لـ GitLab Security Dashboard
sast: grype-report.json
paths:
- grype-report.json
expire_in: 30 days
# فحص صورة الإنتاج
scan-production-image:
<<: *security-scan
variables:
TARGET_IMAGE: "${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHA}"
needs: ["build-production"]
only:
- main
- tags
وحدة تحكم قبول Kubernetes
# k8s-admission-controller/main.py
# وحدة تحكم قبول Kubernetes مع Grype
from flask import Flask, request, jsonify
import subprocess
import json
import logging
import base64
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
def scan_image_with_grype(image: str) -> dict:
"""فحص صورة Docker باستخدام Grype"""
try:
result = subprocess.run(
["grype", image, "-o", "json", "--quiet"],
capture_output=True,
text=True,
timeout=300
)
if result.returncode != 0 and not result.stdout:
logging.error(f"خطأ في فحص {image}: {result.stderr}")
return {"error": result.stderr, "matches": []}
return json.loads(result.stdout)
except subprocess.TimeoutExpired:
logging.error(f"انتهت مهلة فحص {image}")
return {"error": "timeout", "matches": []}
except Exception as e:
logging.error(f"استثناء أثناء فحص {image}: {e}")
return {"error": str(e), "matches": []}
def check_vulnerabilities(scan_result: dict, max_severity: str = "high") -> tuple:
"""التحقق من وجود ثغرات فوق الحد المسموح"""
severity_levels = {"low": 1, "medium": 2, "high": 3, "critical": 4}
max_level = severity_levels.get(max_severity.lower(), 3)
violations = []
for match in scan_result.get("matches", []):
vuln = match.get("vulnerability", {})
severity = vuln.get("severity", "unknown").lower()
if severity in severity_levels and severity_levels[severity] >= max_level:
violations.append({
"id": vuln.get("id"),
"severity": severity,
"package": match.get("artifact", {}).get("name"),
"fix_version": vuln.get("fix", {}).get("versions", [])
})
return len(violations) == 0, violations
@app.route('/validate', methods=['POST'])
def validate_pod():
"""نقطة نهاية التحقق من Pod"""
admission_review = request.get_json()
pod_spec = admission_review.get('request', {}).get('object', {}).get('spec', {})
containers = pod_spec.get('containers', []) + pod_spec.get('initContainers', [])
all_allowed = True
denial_reasons = []
for container in containers:
image = container.get('image', '')
logging.info(f"فحص صورة: {image}")
scan_result = scan_image_with_grype(image)
if "error" in scan_result:
logging.warning(f"فشل الفحص للصورة {image}: {scan_result['error']}")
continue
is_safe, violations = check_vulnerabilities(scan_result, max_severity="high")
if not is_safe:
all_allowed = False
denial_reasons.append({
"image": image,
"violations": violations[:5] # أول 5 ثغرات فقط في الرسالة
})
# بناء الاستجابة
uid = admission_review.get('request', {}).get('uid', '')
if all_allowed:
response = {
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"response": {
"uid": uid,
"allowed": True
}
}
else:
message = f"رُفض: وُجدت ثغرات عالية الخطورة في {len(denial_reasons)} صورة(صور)"
response = {
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"response": {
"uid": uid,
"allowed": False,
"status": {
"code": 403,
"message": message
}
}
}
return jsonify(response)
@app.route('/health', methods=['GET'])
def health():
"""فحص صحة الخدمة"""
return jsonify({"status": "healthy"}), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8443, ssl_context=('cert.pem', 'key.pem'))
سياسة أمان Pod في Kubernetes
# pod-security-policy.yaml
# سياسة أمان للـ Pods في Kubernetes
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: grype-scanner-policy
namespace: security
spec:
podSelector:
matchLabels:
app: grype-scanner
policyTypes:
- Egress
egress:
# السماح بالوصول إلى قاعدة بيانات Grype
- to: []
ports:
- port: 443
---
# ValidatingWebhookConfiguration للتحقق من الصور
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: grype-image-validator
webhooks:
- name: validate-images.security.thakicloud.io
rules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
clientConfig:
service:
name: grype-admission-service
namespace: security
path: /validate
caBundle: LS0t... # شهادة CA مشفرة بـ base64
admissionReviewVersions: ["v1"]
sideEffects: None
failurePolicy: Fail # رفض الـ Pods في حالة فشل الفحص
سكريبت مراقبة تلقائي
#!/bin/bash
# scripts/grype-monitor.sh
# مراقبة دورية لأمان الصور في الإنتاج
set -euo pipefail
# المتغيرات
REGISTRY="your-registry.azurecr.io"
NAMESPACE="production"
SLACK_WEBHOOK="${SLACK_WEBHOOK_URL:-}"
MAX_SEVERITY="high"
REPORT_DIR="/tmp/grype-reports"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
# إنشاء مجلد التقارير
mkdir -p "${REPORT_DIR}"
# دالة إرسال تنبيه Slack
send_slack_alert() {
local message="$1"
if [[ -n "${SLACK_WEBHOOK}" ]]; then
curl -s -X POST -H 'Content-type: application/json' \
--data "{\"text\": \"${message}\"}" \
"${SLACK_WEBHOOK}"
fi
}
# دالة فحص صورة واحدة
scan_image() {
local image="$1"
local report_file="${REPORT_DIR}/$(echo ${image} | tr '/:' '-')-${TIMESTAMP}.json"
echo "فحص: ${image}"
# تنفيذ الفحص
grype "${image}" -o json --quiet > "${report_file}" 2>/dev/null || true
# عد الثغرات حسب الخطورة
local critical_count=$(jq '[.matches[] | select(.vulnerability.severity == "Critical")] | length' "${report_file}" 2>/dev/null || echo 0)
local high_count=$(jq '[.matches[] | select(.vulnerability.severity == "High")] | length' "${report_file}" 2>/dev/null || echo 0)
echo " الثغرات الحرجة: ${critical_count}"
echo " الثغرات العالية: ${high_count}"
# إرسال تنبيه إذا وُجدت ثغرات خطيرة
if [[ "${critical_count}" -gt 0 ]] || [[ "${high_count}" -gt 5 ]]; then
local alert_msg="تنبيه أمان الحاويات: ${image} - ${critical_count} حرجة، ${high_count} عالية"
send_slack_alert "${alert_msg}"
fi
echo "${report_file}"
}
# الحصول على قائمة الصور من Kubernetes
echo "جلب قائمة الصور من Kubernetes..."
IMAGES=$(kubectl get pods -n "${NAMESPACE}" \
-o jsonpath='{range .items[*]}{range .spec.containers[*]}{.image}{"\n"}{end}{end}' \
| sort -u)
# فحص جميع الصور
TOTAL_VULNS=0
declare -a FAILED_IMAGES=()
while IFS= read -r image; do
[[ -z "${image}" ]] && continue
report=$(scan_image "${image}")
# إضافة إلى قائمة الفاشلة إذا لزم
high_count=$(jq '[.matches[] | select(.vulnerability.severity == "High" or .vulnerability.severity == "Critical")] | length' "${report}" 2>/dev/null || echo 0)
TOTAL_VULNS=$((TOTAL_VULNS + high_count))
if [[ "${high_count}" -gt 0 ]]; then
FAILED_IMAGES+=("${image}")
fi
done <<< "${IMAGES}"
# ملخص نهائي
echo ""
echo "=== ملخص الفحص الأمني ==="
echo "الصور الفاشلة: ${#FAILED_IMAGES[@]}"
echo "إجمالي الثغرات العالية والحرجة: ${TOTAL_VULNS}"
if [[ "${#FAILED_IMAGES[@]}" -gt 0 ]]; then
echo ""
echo "الصور التي تحتاج معالجة فورية:"
for img in "${FAILED_IMAGES[@]}"; do
echo " - ${img}"
done
send_slack_alert "تقرير الأمان اليومي: ${#FAILED_IMAGES[@]} صورة تحتاج معالجة فورية (${TOTAL_VULNS} ثغرة)"
exit 1
fi
echo "جميع الصور آمنة."
Tekton Pipeline للفحص الأمني
# tekton-security-pipeline.yaml
# خط أنابيب Tekton لفحص الأمان
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: grype-security-scan
namespace: ci-cd
spec:
params:
- name: IMAGE
description: صورة Docker للفحص
- name: SEVERITY_THRESHOLD
description: الحد الأدنى للخطورة
default: "high"
- name: OUTPUT_FORMAT
description: تنسيق مخرجات التقرير
default: "json"
results:
- name: scan-result
description: نتيجة الفحص (passed/failed)
- name: vulnerability-count
description: عدد الثغرات المكتشفة
steps:
- name: install-grype
image: curlimages/curl:latest
script: |
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh \
| sh -s -- -b /usr/local/bin
- name: scan-image
image: alpine:3.18
script: |
set -e
echo "فحص الصورة: $(params.IMAGE)"
# تنفيذ الفحص
grype "$(params.IMAGE)" \
-o "$(params.OUTPUT_FORMAT)" \
--fail-on "$(params.SEVERITY_THRESHOLD)" \
> /workspace/scan-results.json 2>&1 || SCAN_FAILED=true
# حفظ نتيجة الفحص
if [[ "${SCAN_FAILED:-false}" == "true" ]]; then
echo "failed" > $(results.scan-result.path)
else
echo "passed" > $(results.scan-result.path)
fi
# حفظ عدد الثغرات
VULN_COUNT=$(jq '.matches | length' /workspace/scan-results.json 2>/dev/null || echo 0)
echo "${VULN_COUNT}" > $(results.vulnerability-count.path)
volumeMounts:
- name: workspace
mountPath: /workspace
volumes:
- name: workspace
emptyDir: {}
تصدير مقاييس Prometheus
# scripts/grype_metrics_exporter.py
# تصدير مقاييس فحص Grype إلى Prometheus
from prometheus_client import start_http_server, Gauge, Counter
import subprocess
import json
import time
import logging
from typing import Optional
# تعريف المقاييس
vulnerability_gauge = Gauge(
'grype_vulnerabilities_total',
'إجمالي الثغرات المكتشفة',
['image', 'severity', 'package_type']
)
scan_duration_gauge = Gauge(
'grype_scan_duration_seconds',
'مدة فحص الصورة بالثواني',
['image']
)
scan_success_counter = Counter(
'grype_scans_successful_total',
'عدد الفحوصات الناجحة',
['image']
)
scan_failure_counter = Counter(
'grype_scans_failed_total',
'عدد الفحوصات الفاشلة',
['image', 'reason']
)
def scan_image(image: str) -> Optional[dict]:
"""فحص صورة وإرجاع نتائج JSON"""
start_time = time.time()
try:
result = subprocess.run(
["grype", image, "-o", "json", "--quiet"],
capture_output=True,
text=True,
timeout=600
)
duration = time.time() - start_time
scan_duration_gauge.labels(image=image).set(duration)
if result.stdout:
scan_data = json.loads(result.stdout)
scan_success_counter.labels(image=image).inc()
return scan_data
else:
logging.warning(f"لا توجد مخرجات للصورة {image}: {result.stderr}")
scan_failure_counter.labels(image=image, reason="no_output").inc()
return None
except subprocess.TimeoutExpired:
scan_failure_counter.labels(image=image, reason="timeout").inc()
logging.error(f"انتهت مهلة فحص {image}")
return None
except Exception as e:
scan_failure_counter.labels(image=image, reason="exception").inc()
logging.error(f"خطأ في فحص {image}: {e}")
return None
def update_metrics(images: list):
"""تحديث مقاييس Prometheus"""
for image in images:
logging.info(f"جارٍ فحص: {image}")
scan_result = scan_image(image)
if not scan_result:
continue
# إعادة تعيين المقياس للصورة
for severity in ["critical", "high", "medium", "low", "negligible"]:
vulnerability_gauge.labels(
image=image,
severity=severity,
package_type="all"
).set(0)
# حساب الثغرات
for match in scan_result.get("matches", []):
vuln = match.get("vulnerability", {})
severity = vuln.get("severity", "unknown").lower()
artifact = match.get("artifact", {})
pkg_type = artifact.get("type", "unknown")
vulnerability_gauge.labels(
image=image,
severity=severity,
package_type=pkg_type
).inc()
def main():
"""النقطة الرئيسية لتصدير المقاييس"""
# قائمة الصور للمراقبة
images_to_monitor = [
"your-registry.io/api-service:latest",
"your-registry.io/inference-service:latest",
"your-registry.io/model-service:latest",
]
# بدء خادم Prometheus
start_http_server(8080)
logging.info("تصدير مقاييس Grype على المنفذ 8080")
# حلقة المراقبة الدورية
while True:
update_metrics(images_to_monitor)
time.sleep(3600) # كل ساعة
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
main()
لوحة Grafana
// grafana-dashboard.json (مقتطف)
// لوحة Grafana لمراقبة Grype
{
"title": "أمان الحاويات - Grype",
"panels": [
{
"title": "إجمالي الثغرات الحرجة",
"type": "stat",
"targets": [
{
"expr": "sum(grype_vulnerabilities_total{severity='critical'})",
"legendFormat": "ثغرات حرجة"
}
],
"thresholds": {
"steps": [
{"color": "green", "value": 0},
{"color": "red", "value": 1}
]
}
},
{
"title": "توزيع الثغرات حسب الخطورة",
"type": "piechart",
"targets": [
{
"expr": "sum by (severity) (grype_vulnerabilities_total)",
"legendFormat": ""
}
]
},
{
"title": "اتجاه الثغرات عبر الزمن",
"type": "timeseries",
"targets": [
{
"expr": "sum by (severity) (grype_vulnerabilities_total)",
"legendFormat": ""
}
]
}
]
}
الإعدادات المتقدمة
ملف قواعد التجاهل
# .grype.yaml
# قواعد تجاهل الثغرات المقبولة
ignore:
# تجاهل ثغرات بعيدة الاستغلال
- vulnerability: CVE-2021-44228 # Log4Shell - محمي بجدار الحماية
reason: "محمي بجدار الحماية الداخلي وعدم تعرض الخدمة للإنترنت مباشرة"
expiration: "2025-12-31T00:00:00Z"
# تجاهل ثغرات في حزم محددة
- package:
name: openssl
version: "1.1.1t"
vulnerability: CVE-2023-0286
reason: "نسخة معدّلة مع تطبيق إصلاح جزئي"
# تجاهل بناءً على الخطورة لبيئات معينة
- fix-state: wont-fix
reason: "المشروع المصدري لن يُصلح هذه الثغرة"
# إعدادات قاعدة البيانات
db:
# تحديث قاعدة البيانات تلقائيًا
auto-update: true
# مصدر قاعدة البيانات
update-url: "https://toolbox-data.anchore.io/grype/databases/listing.json"
# الحد الأقصى لعمر قاعدة البيانات (بالساعات)
max-allowed-built-age: 120h
# إعدادات الفحص
check-for-app-update: false
fail-on-severity: "high"
output: "table"
quiet: false
مطابقة مخصصة
# .grype-custom-matchers.yaml
# مطابقة مخصصة للحزم الداخلية
matchers:
# تعطيل مطابقة معينة
- type: java
use-cpes: false
# إضافة مصادر بيانات إضافية
additional-vulnerability-sources:
- name: "قاعدة بيانات الشركة الداخلية"
url: "https://vuln-db.internal.thakicloud.io/api/v1"
headers:
Authorization: "Bearer ${INTERNAL_VULN_DB_TOKEN}"
اختصارات Zsh
# إضافة هذه الاختصارات إلى ~/.zshrc
# اختصارات Grype الأساسية
alias gs='grype' # فحص سريع
alias gsc='grype --fail-on critical' # فحص مع فشل عند ثغرات حرجة
alias gsh='grype --fail-on high' # فحص مع فشل عند ثغرات عالية
alias gsjson='grype -o json' # مخرجات JSON
alias gslocal='grype dir:.' # فحص المجلد الحالي
# دالة فحص سريع مع ملخص
grype-summary() {
local image="${1:-alpine:latest}"
echo "فحص: ${image}"
grype "${image}" -o json --quiet 2>/dev/null | \
jq -r '.matches | group_by(.vulnerability.severity) |
map({(.[0].vulnerability.severity): length}) |
add |
to_entries |
sort_by(.key) |
.[] | "\(.key): \(.value) ثغرة"'
}
# دالة فحص وتصفية JSON
grype-critical() {
local image="${1}"
grype "${image}" -o json --quiet 2>/dev/null | \
jq '.matches[] | select(.vulnerability.severity == "Critical") |
{cve: .vulnerability.id, package: .artifact.name, fix: .vulnerability.fix.versions}'
}
تحسين الأداء
التخزين المؤقت لقاعدة البيانات
# تحديث قاعدة البيانات مرة واحدة وتخزينها مؤقتًا
grype db update
# مشاركة قاعدة البيانات عبر الحاويات
docker run -v grype-db:/tmp/grype-db \
-e GRYPE_DB_CACHE_DIR=/tmp/grype-db \
anchore/grype:latest \
your-image:latest
الفحص المتوازي
#!/bin/bash
# فحص متوازٍ لصور متعددة
IMAGES=(
"nginx:latest"
"python:3.11-slim"
"node:20-alpine"
"redis:7-alpine"
)
# استخدام GNU parallel إذا كان متاحًا
if command -v parallel &>/dev/null; then
parallel grype {} -o json --quiet '>' /tmp/grype-{}.json ::: "${IMAGES[@]}"
else
# بديل باستخدام &
for image in "${IMAGES[@]}"; do
safe_name=$(echo "${image}" | tr '/:' '-')
grype "${image}" -o json --quiet > "/tmp/grype-${safe_name}.json" &
done
wait
fi
echo "اكتمل الفحص المتوازٍ"
تجميع النتائج
# scripts/aggregate_grype_results.py
# تجميع نتائج الفحص من ملفات JSON متعددة
import json
import glob
from pathlib import Path
from collections import defaultdict
def aggregate_scan_results(results_dir: str = "/tmp") -> dict:
"""تجميع نتائج فحوصات Grype المتعددة"""
all_results = {
"images_scanned": 0,
"total_vulnerabilities": 0,
"by_severity": defaultdict(int),
"top_packages": defaultdict(int),
"cve_frequency": defaultdict(int),
"images_with_critical": []
}
# قراءة جميع ملفات النتائج
for result_file in glob.glob(f"{results_dir}/grype-*.json"):
image_name = Path(result_file).stem.replace("grype-", "")
try:
with open(result_file) as f:
data = json.load(f)
except Exception as e:
print(f"خطأ في قراءة {result_file}: {e}")
continue
all_results["images_scanned"] += 1
matches = data.get("matches", [])
all_results["total_vulnerabilities"] += len(matches)
has_critical = False
for match in matches:
vuln = match.get("vulnerability", {})
severity = vuln.get("severity", "unknown").lower()
pkg_name = match.get("artifact", {}).get("name", "unknown")
cve_id = vuln.get("id", "unknown")
all_results["by_severity"][severity] += 1
all_results["top_packages"][pkg_name] += 1
all_results["cve_frequency"][cve_id] += 1
if severity == "critical":
has_critical = True
if has_critical:
all_results["images_with_critical"].append(image_name)
# ترتيب النتائج
all_results["top_packages"] = dict(
sorted(all_results["top_packages"].items(), key=lambda x: x[1], reverse=True)[:10]
)
all_results["cve_frequency"] = dict(
sorted(all_results["cve_frequency"].items(), key=lambda x: x[1], reverse=True)[:10]
)
all_results["by_severity"] = dict(all_results["by_severity"])
return all_results
# تنفيذ التجميع
results = aggregate_scan_results()
print(json.dumps(results, ensure_ascii=False, indent=2))
نتائج الاختبار الفعلية
# مثال على مخرجات فحص nginx:latest
$ grype nginx:latest
✔ Vulnerability DB [no update available]
✔ Indexed image
✔ Cataloged packages [97 packages]
✔ Scanning for vulnerabilities
NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY
curl 7.88.1-10 7.88.1-10+deb12u5 deb CVE-2023-38545 High
libssl1.1 1.1.1w-0 (لا يوجد إصلاح) deb CVE-2023-5678 Medium
openssl 3.0.11-1 3.0.13-1+deb12u1 deb CVE-2024-0727 Medium
...
الملخص:
الحرجة: 0
العالية: 1
المتوسطة: 8
المنخفضة: 12
إجمالي: 21
استكشاف الأخطاء وإصلاحها
مشكلة: قاعدة البيانات قديمة
# تحديث قاعدة البيانات يدويًا
grype db update
# أو حذفها وإعادة التنزيل
grype db delete
grype db update
# التحقق من حالة قاعدة البيانات
grype db status
مشكلة: بطء الفحص
# استخدام وضع الهدوء لتسريع المخرجات
grype nginx:latest --quiet
# تقليل نطاق الفحص
grype nginx:latest --scope squashed # بدلًا من all-layers (الافتراضي)
# تجاهل الطبقات المؤقتة
grype nginx:latest --platform linux/amd64
مشكلة: نتائج إيجابية كاذبة
# إضافة قاعدة تجاهل في .grype.yaml
ignore:
- vulnerability: CVE-XXXX-XXXX
reason: "إيجابي كاذب - تأكيد من الفريق الأمني بتاريخ 2025-06-01"
expiration: "2025-12-31T00:00:00Z"
الخلاصة
Grype هو الأداة المثالية لتعزيز DevSecOps في منصات الذكاء الاصطناعي السحابية بفضل:
- السرعة والدقة: نتائج دقيقة خلال ثوانٍ حتى مع الصور الكبيرة
- التكامل السلس: يدخل في CI/CD دون تغيير جوهري في سير العمل
- المرونة: يدعم بيئات متعددة من المحلية إلى Kubernetes
- قاعدة بيانات شاملة: تغطية واسعة لثغرات Linux والحزم
- المصدر المفتوح: مجتمع نشط ومستمر في التطوير
ابدأ بدمج Grype في خطوط CI/CD الخاصة بك اليوم، وانتقل تدريجيًا إلى المراقبة المستمرة والإبلاغ التلقائي لبناء منصة ذكاء اصطناعي آمنة.