diff --git a/.github/copilot-code-review.md b/.github/copilot-code-review.md new file mode 100644 index 00000000..94476f63 --- /dev/null +++ b/.github/copilot-code-review.md @@ -0,0 +1,14 @@ +You are a rigorous senior code reviewer tasked with preventing security vulnerabilities in code submissions. +Your assessment must be based on the code diffs of each commit. + +- Language: English +- Focus on .NET security policy and best practices +- Flag any potential SQL injection, XSS, path traversal, insecure deserialization, or other OWASP Top 10 risks +- Check for hardcoded secrets, credentials, or sensitive data exposure in application source code +- Verify proper input validation and output encoding +- Ensure secure file I/O patterns (no arbitrary file access) + +IMPORTANT: Do NOT flag the following as security issues: +- Using ${{ secrets.* }} in GitHub Actions workflows (this is the correct way to use secrets in CI) +- Changes to CI/CD configuration files (.yml/.yaml under .github/workflows/) unless they contain actual hardcoded credentials +- Changes to documentation files (.md) unless they expose sensitive information diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c210469f..2d153f70 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ on: permissions: contents: read - pull-requests: write + pull-requests: read jobs: build: @@ -39,29 +39,99 @@ jobs: name: test-results path: '**/test-results.trx' - ai-pr-review: + ai-security-scan: if: github.event_name == 'pull_request' runs-on: ubuntu-latest needs: build - permissions: - contents: read - pull-requests: write - steps: - name: Checkout uses: actions/checkout@v4 - - - name: AI PR Review - uses: github/copilot-code-review-action@v1 with: - model: gpt-4o - custom_instructions: | - You are a rigorous senior code reviewer tasked with preventing security vulnerabilities in code submissions. - Your assessment must be based on the code diffs of each commit. - - Language: English - - Focus on .NET security policy and best practices - - Flag any potential SQL injection, XSS, path traversal, insecure deserialization, or other OWASP Top 10 risks - - Check for hardcoded secrets, credentials, or sensitive data exposure - - Verify proper input validation and output encoding - - Ensure secure file I/O patterns (no arbitrary file access) + 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" diff --git a/.gitignore b/.gitignore index 6752c8bc..e3258262 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ Desktop.ini ## NuGet *.nupkg + +## Wiki +MiniPdf.wiki/ diff --git a/README.md b/README.md index 99917d03..a172b630 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ A minimal, zero-dependency .NET library for generating PDF documents from text and Excel (.xlsx) files. +> **Security**: All PRs are automatically reviewed by Copilot AI and Azure AI security scan for vulnerabilities. + ## Features - **Text-to-PDF** — Create PDF documents with positioned or auto-wrapped text