-
Notifications
You must be signed in to change notification settings - Fork 11
154 lines (129 loc) · 5.34 KB
/
ci.yml
File metadata and controls
154 lines (129 loc) · 5.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
name: CI
on:
push:
branches: [ main, master ]
paths-ignore:
- '**.md'
- 'documents/**'
- 'dotnet_ai_azure_book/**'
- 'book/**'
- 'MiniPdf.wiki/**'
- 'LICENSE'
pull_request:
branches: [ main, master ]
paths-ignore:
- '**.md'
- 'documents/**'
- 'dotnet_ai_azure_book/**'
- 'book/**'
- 'MiniPdf.wiki/**'
- 'LICENSE'
permissions:
contents: read
pull-requests: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore --configuration Release
- name: Test
run: dotnet test --no-build --configuration Release --verbosity normal --logger "trx;LogFileName=test-results.trx"
- name: Pack (verify NuGet package)
run: dotnet pack src/MiniPdf/MiniPdf.csproj --no-build --configuration Release --output ./nupkg
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: '**/test-results.trx'
ai-security-scan:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
needs: build
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get PR diff
id: diff
run: |
DIFF=$(git diff origin/${{ github.base_ref }}...HEAD -- '*.cs' '*.csproj' || true)
if [ -z "$DIFF" ]; then
echo "No code changes detected."
echo "skip=true" >> $GITHUB_OUTPUT
else
# Save diff to file to avoid shell escaping issues
echo "$DIFF" > /tmp/pr_diff.txt
echo "skip=false" >> $GITHUB_OUTPUT
fi
- name: AI Security Review
if: steps.diff.outputs.skip != 'true'
env:
AZURE_OPENAI_API_KEY: ${{ secrets.AZURE_OPENAI_API_KEY }}
AZURE_OPENAI_ENDPOINT: ${{ secrets.AZURE_OPENAI_ENDPOINT }}
AZURE_OPENAI_DEPLOYMENT: ${{ secrets.AZURE_OPENAI_DEPLOYMENT }}
run: |
DIFF=$(cat /tmp/pr_diff.txt)
# Truncate diff if too large (max ~12000 chars to fit in context)
if [ ${#DIFF} -gt 12000 ]; then
DIFF="${DIFF:0:12000}... [truncated]"
fi
INSTRUCTIONS=$(cat .github/copilot-code-review.md 2>/dev/null || echo "Review for security issues.")
API_VERSION="2025-01-01-preview"
URL="${AZURE_OPENAI_ENDPOINT%/}/openai/deployments/${AZURE_OPENAI_DEPLOYMENT}/chat/completions?api-version=${API_VERSION}"
# Build JSON payload safely using jq
PAYLOAD=$(jq -n \
--arg instructions "$INSTRUCTIONS" \
--arg diff "$DIFF" \
'{
messages: [
{ role: "system", content: $instructions },
{ role: "user", content: ("Review this code diff for security vulnerabilities in APPLICATION SOURCE CODE ONLY. Respond ONLY with a JSON object (no markdown, no code blocks): {\"passed\": true/false, \"issues\": [\"description1\", \"description2\"]}. Set passed=true if no security issues found in application code, passed=false only for real security concerns like SQL injection, XSS, path traversal, hardcoded credentials in source code, etc. Do NOT flag CI/CD workflow configuration or documentation changes as issues.\n\nDiff:\n" + $diff) }
],
temperature: 0.1
}')
RESPONSE=$(curl -s "$URL" \
-H "Content-Type: application/json" \
-H "api-key: $AZURE_OPENAI_API_KEY" \
-d "$PAYLOAD")
# Extract content
CONTENT=$(echo "$RESPONSE" | jq -r '.choices[0].message.content // empty')
if [ -z "$CONTENT" ]; then
echo "::error::Failed to get AI review response"
echo "$RESPONSE" | jq .
exit 1
fi
echo "=== AI Security Review Result ==="
echo "$CONTENT"
echo "================================="
# Parse JSON from response: strip markdown code blocks, then parse with jq
CLEAN_CONTENT=$(echo "$CONTENT" | sed '/^```/d')
# Use 'if .passed then "true" else "false" end' to handle boolean false correctly
PASSED=$(echo "$CLEAN_CONTENT" | jq -r 'if .passed == true then "true" elif .passed == false then "false" else "unknown" end' 2>/dev/null)
if [ "$PASSED" = "false" ]; then
echo ""
echo "::error::AI Security Review FAILED - security issues detected"
echo "$CLEAN_CONTENT" | jq -r '.issues[]?' 2>/dev/null | while read -r issue; do
echo "::warning::$issue"
done
exit 1
elif [ "$PASSED" = "true" ]; then
echo ""
echo "✅ AI Security Review PASSED - no security issues found"
else
echo "::warning::Could not parse AI review result, treating as FAIL for safety"
exit 1
fi
- name: Skip notice
if: steps.diff.outputs.skip == 'true'
run: echo "✅ No code changes to review"