Skip to content

Commit 1299288

Browse files
britaniarCopilot
andcommitted
feat: run trivy daily at 6AM UTC and create Copilot issue for CVEs
- Add daily cron schedule (6:00 AM UTC) - Add issues: write permission - Switch scan output from table to JSON format - Add vulnerability check step that aggregates results - On non-scheduled runs: fail with error details - On scheduled runs: build markdown summary and create GitHub issue assigned to Copilot with security/trivy labels - Deduplicate issues (skip if today's issue already exists) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: Britania Rodriguez Reyes <britaniar@microsoft.com>
1 parent 39cb47d commit 1299288

1 file changed

Lines changed: 105 additions & 13 deletions

File tree

.github/workflows/trivy.yml

Lines changed: 105 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
name: Trivy Vulnerability Scanner
22
on:
3+
schedule:
4+
- cron: '0 6 * * *' # Daily at 6:00 AM UTC
35
push:
46
branches:
57
- main
@@ -11,6 +13,7 @@ on:
1113
permissions:
1214
contents: read
1315
packages: write
16+
issues: write
1417

1518
env:
1619
REGISTRY: ghcr.io
@@ -22,21 +25,21 @@ env:
2225

2326
jobs:
2427
export-registry:
25-
runs-on: ubuntu-latest #Latest tag points to the latest LTS release of Ubuntu per docker hub
28+
runs-on: ubuntu-latest
2629
outputs:
2730
registry: ${{ steps.export.outputs.registry }}
2831
steps:
2932
- id: export
3033
run: |
3134
# registry must be in lowercase
3235
# store the images under dev
33-
# TODO: need to cleanup dev images periodically
36+
# TODO: need to cleanup dev images periodically
3437
echo "registry=$(echo "${{ env.REGISTRY }}/${{ github.repository }}" | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
3538
scan-images:
3639
needs: export-registry
3740
env:
3841
REGISTRY: ${{ needs.export-registry.outputs.registry }}
39-
runs-on: ubuntu-latest #Latest tag points to the latest LTS release of Ubuntu per docker hub
42+
runs-on: ubuntu-latest
4043
steps:
4144
- name: Set up Go ${{ env.GO_VERSION }}
4245
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
@@ -67,44 +70,133 @@ jobs:
6770
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
6871
with:
6972
image-ref: ${{ env.REGISTRY }}/${{ env.HUB_AGENT_IMAGE_NAME }}:${{ env.IMAGE_VERSION }}
70-
format: 'table'
71-
exit-code: '1'
73+
format: 'json'
74+
output: 'trivy-hub-agent.json'
7275
ignore-unfixed: true
7376
vuln-type: 'os,library'
7477
severity: 'CRITICAL,HIGH'
7578
timeout: '5m0s'
7679
env:
7780
TRIVY_USERNAME: ${{ github.actor }}
7881
TRIVY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
79-
TRIVY_DB_REPOSITORY: mcr.microsoft.com/mirror/ghcr/aquasecurity/trivy-db
80-
82+
TRIVY_DB_REPOSITORY: mcr.microsoft.com/mirror/ghcr/aquasecurity/trivy-db
8183

8284
- name: Scan ${{ env.REGISTRY }}/${{ env.MEMBER_AGENT_IMAGE_NAME }}:${{ env.IMAGE_VERSION }}
8385
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
8486
with:
8587
image-ref: ${{ env.REGISTRY }}/${{ env.MEMBER_AGENT_IMAGE_NAME }}:${{ env.IMAGE_VERSION }}
86-
format: 'table'
87-
exit-code: '1'
88+
format: 'json'
89+
output: 'trivy-member-agent.json'
8890
ignore-unfixed: true
8991
vuln-type: 'os,library'
9092
severity: 'CRITICAL,HIGH'
9193
timeout: '5m0s'
9294
env:
9395
TRIVY_USERNAME: ${{ github.actor }}
9496
TRIVY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
95-
TRIVY_DB_REPOSITORY: mcr.microsoft.com/mirror/ghcr/aquasecurity/trivy-db
97+
TRIVY_DB_REPOSITORY: mcr.microsoft.com/mirror/ghcr/aquasecurity/trivy-db
9698

9799
- name: Scan ${{ env.REGISTRY }}/${{ env.REFRESH_TOKEN_IMAGE_NAME }}:${{ env.IMAGE_VERSION }}
98100
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
99101
with:
100102
image-ref: ${{ env.REGISTRY }}/${{ env.REFRESH_TOKEN_IMAGE_NAME }}:${{ env.IMAGE_VERSION }}
101-
format: 'table'
102-
exit-code: '1'
103+
format: 'json'
104+
output: 'trivy-refresh-token.json'
103105
ignore-unfixed: true
104106
vuln-type: 'os,library'
105107
severity: 'CRITICAL,HIGH'
106108
timeout: '5m0s'
107109
env:
108110
TRIVY_USERNAME: ${{ github.actor }}
109111
TRIVY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
110-
TRIVY_DB_REPOSITORY: mcr.microsoft.com/mirror/ghcr/aquasecurity/trivy-db
112+
TRIVY_DB_REPOSITORY: mcr.microsoft.com/mirror/ghcr/aquasecurity/trivy-db
113+
114+
- name: Check for vulnerabilities
115+
id: check-vulns
116+
run: |
117+
has_vulns=false
118+
for file in trivy-hub-agent.json trivy-member-agent.json trivy-refresh-token.json; do
119+
count=$(jq '[.Results[]? | .Vulnerabilities[]?] | length' "$file")
120+
if [ "$count" -gt 0 ]; then
121+
has_vulns=true
122+
break
123+
fi
124+
done
125+
echo "has_vulns=$has_vulns" >> "$GITHUB_OUTPUT"
126+
127+
- name: Fail on vulnerabilities (non-scheduled runs)
128+
if: steps.check-vulns.outputs.has_vulns == 'true' && github.event_name != 'schedule'
129+
run: |
130+
echo "::error::Vulnerabilities found. See trivy scan output."
131+
for file in trivy-hub-agent.json trivy-member-agent.json trivy-refresh-token.json; do
132+
echo "--- $file ---"
133+
jq -r '.Results[]? | .Vulnerabilities[]? | "\(.VulnerabilityID) \(.Severity) \(.PkgName) \(.InstalledVersion) -> \(.FixedVersion)"' "$file"
134+
done
135+
exit 1
136+
137+
- name: Build vulnerability summary
138+
if: steps.check-vulns.outputs.has_vulns == 'true' && github.event_name == 'schedule'
139+
id: vuln-summary
140+
run: |
141+
{
142+
echo 'body<<EOF'
143+
echo "## Trivy CVE Scan - $(date -u +%Y-%m-%d)"
144+
echo ""
145+
echo "The daily vulnerability scan found **HIGH/CRITICAL** CVEs in our container images."
146+
echo "Please update the affected Go dependencies in \`go.mod\` to the fixed versions listed below,"
147+
echo "then run \`go mod tidy\` and verify the build passes with \`make build\`."
148+
echo ""
149+
echo "### Vulnerabilities"
150+
echo ""
151+
echo "| CVE | Severity | Package | Installed | Fixed | Image |"
152+
echo "|-----|----------|---------|-----------|-------|-------|"
153+
for file in trivy-hub-agent.json trivy-member-agent.json trivy-refresh-token.json; do
154+
image=$(echo "$file" | sed 's/trivy-//;s/.json//')
155+
jq -r --arg img "$image" \
156+
'.Results[]? | .Vulnerabilities[]? | "| \(.VulnerabilityID) | \(.Severity) | \(.PkgName) | \(.InstalledVersion) | \(.FixedVersion) | \($img) |"' \
157+
"$file"
158+
done | sort -u
159+
echo ""
160+
echo "### Instructions"
161+
echo ""
162+
echo "1. For each Go library CVE, run: \`go get <package>@<fixed_version>\`"
163+
echo "2. Run \`go mod tidy\` to clean up dependencies."
164+
echo "3. Run \`make build\` to verify the build passes."
165+
echo "4. Run \`make test\` to verify tests pass."
166+
echo 'EOF'
167+
} >> "$GITHUB_OUTPUT"
168+
169+
- name: Create issue for Copilot
170+
if: steps.check-vulns.outputs.has_vulns == 'true' && github.event_name == 'schedule'
171+
uses: actions/github-script@v7
172+
with:
173+
script: |
174+
const today = new Date().toISOString().split('T')[0];
175+
const title = `fix: address trivy CVEs found on ${today}`;
176+
177+
// Check if an open issue already exists for today
178+
const existing = await github.rest.issues.listForRepo({
179+
owner: context.repo.owner,
180+
repo: context.repo.repo,
181+
state: 'open',
182+
labels: 'security,trivy',
183+
per_page: 10
184+
});
185+
const alreadyExists = existing.data.some(i => i.title === title);
186+
if (alreadyExists) {
187+
console.log('Issue already exists for today, skipping.');
188+
return;
189+
}
190+
191+
const body = process.env.ISSUE_BODY;
192+
await github.rest.issues.create({
193+
owner: context.repo.owner,
194+
repo: context.repo.repo,
195+
title: title,
196+
body: body,
197+
labels: ['security', 'trivy'],
198+
assignees: ['copilot']
199+
});
200+
env:
201+
ISSUE_BODY: ${{ steps.vuln-summary.outputs.body }}
202+

0 commit comments

Comments
 (0)