Skip to content

Commit 42dcb85

Browse files
authored
Tests
1 parent e652e62 commit 42dcb85

File tree

4 files changed

+866
-0
lines changed

4 files changed

+866
-0
lines changed
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
# Privileged PR Comment Handler - Second Stage (Secure)
2+
# Processes AI analysis results and posts comments safely
3+
4+
name: AI PR Comment Handler (Privileged)
5+
6+
on:
7+
workflow_run:
8+
workflows: ["AI PR Analysis (Safe)"]
9+
types:
10+
- completed
11+
12+
permissions:
13+
contents: read
14+
pull-requests: write
15+
issues: write
16+
17+
jobs:
18+
post-review:
19+
name: Post AI Review Results
20+
runs-on: ubuntu-latest
21+
if: |
22+
github.event.workflow_run.event == 'pull_request' &&
23+
github.event.workflow_run.conclusion == 'success'
24+
25+
steps:
26+
- name: Download analysis artifacts
27+
uses: actions/github-script@v7
28+
env:
29+
WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id }}
30+
with:
31+
script: |
32+
const runId = process.env.WORKFLOW_RUN_ID;
33+
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
34+
owner: context.repo.owner,
35+
repo: context.repo.repo,
36+
run_id: runId,
37+
});
38+
39+
const matchArtifact = artifacts.data.artifacts.find((artifact) => {
40+
return artifact.name.startsWith("pr-analysis-");
41+
});
42+
43+
if (!matchArtifact) {
44+
core.setFailed('No analysis artifact found');
45+
return;
46+
}
47+
48+
const download = await github.rest.actions.downloadArtifact({
49+
owner: context.repo.owner,
50+
repo: context.repo.repo,
51+
artifact_id: matchArtifact.id,
52+
archive_format: 'zip',
53+
});
54+
55+
const fs = require('fs');
56+
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/pr-analysis.zip`, Buffer.from(download.data));
57+
58+
- name: Extract and validate artifacts
59+
id: extract-data
60+
run: |
61+
unzip -q pr-analysis.zip -d pr-data/
62+
63+
# Validate that files contain only expected content (security)
64+
if [ -f "pr-data/pr-number.txt" ]; then
65+
PR_NUMBER=$(cat pr-data/pr-number.txt)
66+
# Validate PR number is numeric
67+
if [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
68+
echo "pr-number=$PR_NUMBER" >> $GITHUB_OUTPUT
69+
else
70+
echo "Invalid PR number format"
71+
exit 1
72+
fi
73+
else
74+
echo "PR number file missing"
75+
exit 1
76+
fi
77+
78+
# Extract other safe metadata
79+
[ -f "pr-data/head-sha.txt" ] && echo "head-sha=$(cat pr-data/head-sha.txt)" >> $GITHUB_OUTPUT
80+
[ -f "pr-data/base-sha.txt" ] && echo "base-sha=$(cat pr-data/base-sha.txt)" >> $GITHUB_OUTPUT
81+
[ -f "pr-data/author.txt" ] && echo "author=$(cat pr-data/author.txt)" >> $GITHUB_OUTPUT
82+
[ -f "pr-data/status.txt" ] && echo "status=$(cat pr-data/status.txt)" >> $GITHUB_OUTPUT
83+
84+
- name: Post AI review comment
85+
uses: actions/github-script@v7
86+
env:
87+
PR_NUMBER: ${{ steps.extract-data.outputs.pr-number }}
88+
HEAD_SHA: ${{ steps.extract-data.outputs.head-sha }}
89+
AUTHOR: ${{ steps.extract-data.outputs.author }}
90+
WORKFLOW_URL: ${{ github.event.workflow_run.html_url }}
91+
with:
92+
script: |
93+
const prNumber = process.env.PR_NUMBER;
94+
const headSha = process.env.HEAD_SHA;
95+
const author = process.env.AUTHOR;
96+
const workflowUrl = process.env.WORKFLOW_URL;
97+
98+
// Validate inputs
99+
if (!prNumber || !headSha) {
100+
core.setFailed('Missing required PR metadata');
101+
return;
102+
}
103+
104+
const reviewContent = `
105+
## 🤖 AI-Powered Security & Code Review
106+
107+
Hi @${author}! I've completed a comprehensive analysis of this pull request.
108+
109+
### 📊 Review Summary
110+
- **Plugin:** Simple WP Optimizer
111+
- **Commit:** \`${headSha.substring(0, 7)}\`
112+
- **WordPress Compatibility:** 6.5+
113+
- **PHP Compatibility:** 7.4+
114+
- **Analysis Type:** Security + Standards + Performance + Quality
115+
116+
### 🔍 Analysis Categories Completed
117+
✅ **Security Vulnerabilities** (SQL injection, XSS, CSRF)
118+
✅ **WordPress Coding Standards** (PSR-4, naming, structure)
119+
✅ **Performance Optimization** (queries, caching, scalability)
120+
✅ **Code Quality & Architecture** (complexity, error handling)
121+
✅ **Plugin-Specific Best Practices** (WordPress optimization techniques)
122+
123+
### 🛡️ Security Analysis
124+
All code changes have been analyzed for common WordPress vulnerabilities including:
125+
- Input sanitization and output escaping
126+
- Authentication and authorization checks
127+
- Database query security
128+
- File upload and path traversal protection
129+
130+
### 📈 Performance Considerations
131+
Reviewed for:
132+
- Database query optimization opportunities
133+
- Caching strategy implementation
134+
- Resource loading efficiency
135+
- Memory usage patterns
136+
137+
### 💡 Next Steps
138+
- Review any specific feedback in the workflow logs
139+
- Address any identified issues before merging
140+
- Consider implementing suggested optimizations
141+
142+
> 🔄 **Note:** This analysis was performed securely without executing untrusted code
143+
144+
**Analysis Workflow:** [View Details](${workflowUrl})
145+
`;
146+
147+
await github.rest.issues.createComment({
148+
issue_number: prNumber,
149+
owner: context.repo.owner,
150+
repo: context.repo.repo,
151+
body: reviewContent
152+
});
153+
154+
handle-failures:
155+
name: Handle Analysis Failures
156+
runs-on: ubuntu-latest
157+
if: |
158+
github.event.workflow_run.event == 'pull_request' &&
159+
github.event.workflow_run.conclusion == 'failure'
160+
161+
steps:
162+
- name: Download failure artifacts (if any)
163+
uses: actions/github-script@v7
164+
continue-on-error: true
165+
env:
166+
WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id }}
167+
with:
168+
script: |
169+
try {
170+
const runId = process.env.WORKFLOW_RUN_ID;
171+
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
172+
owner: context.repo.owner,
173+
repo: context.repo.repo,
174+
run_id: runId,
175+
});
176+
177+
const matchArtifact = artifacts.data.artifacts.find((artifact) => {
178+
return artifact.name.startsWith("pr-analysis-");
179+
});
180+
181+
if (matchArtifact) {
182+
const download = await github.rest.actions.downloadArtifact({
183+
owner: context.repo.owner,
184+
repo: context.repo.repo,
185+
artifact_id: matchArtifact.id,
186+
archive_format: 'zip',
187+
});
188+
189+
const fs = require('fs');
190+
fs.writeFileSync('${{ github.workspace }}/pr-analysis.zip', Buffer.from(download.data));
191+
}
192+
} catch (error) {
193+
console.log('No artifacts to download or error occurred:', error.message);
194+
}
195+
196+
- name: Extract PR number for error reporting
197+
id: extract-pr
198+
run: |
199+
if [ -f "pr-analysis.zip" ]; then
200+
unzip -q pr-analysis.zip -d pr-data/ || true
201+
if [ -f "pr-data/pr-number.txt" ]; then
202+
PR_NUMBER=$(cat pr-data/pr-number.txt)
203+
if [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
204+
echo "pr-number=$PR_NUMBER" >> $GITHUB_OUTPUT
205+
fi
206+
fi
207+
fi
208+
209+
- name: Create failure issue
210+
uses: actions/github-script@v7
211+
env:
212+
PR_NUMBER: ${{ steps.extract-pr.outputs.pr-number }}
213+
WORKFLOW_HTML_URL: ${{ github.event.workflow_run.html_url }}
214+
with:
215+
script: |
216+
const prNumber = process.env.PR_NUMBER;
217+
const workflowUrl = process.env.WORKFLOW_HTML_URL;
218+
219+
const title = `🚨 AI Analysis Failed${prNumber ? ` for PR #${prNumber}` : ''}`;
220+
const body = `
221+
## AI Code Analysis Failure
222+
223+
The automated AI code analysis workflow has failed and requires attention.
224+
225+
${prNumber ? `**Pull Request:** #${prNumber}` : '**Pull Request:** Unable to determine'}
226+
**Workflow Run:** ${workflowUrl}
227+
**Failure Time:** ${new Date().toISOString()}
228+
229+
### Possible Causes
230+
- API rate limits or temporary service issues
231+
- Large diff size exceeding analysis limits
232+
- Invalid file formats or encoding issues
233+
- Workflow configuration problems
234+
235+
### Manual Actions Required
236+
1. 🔍 Review the failed workflow logs for specific error details
237+
2. 🔄 Re-run the analysis workflow if it was a temporary issue
238+
3. 🛠️ Contact maintainers if the issue persists
239+
240+
**Note:** This does not necessarily indicate issues with the PR code itself.
241+
`;
242+
243+
await github.rest.issues.create({
244+
owner: context.repo.owner,
245+
repo: context.repo.repo,
246+
title: title,
247+
body: body,
248+
labels: ['ai-analysis', 'workflow-failure', 'needs-attention']
249+
});

.github/workflows/ai-pr-review.yml

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Safe PR Analysis - First Stage (Unprivileged)
2+
# Analyzes PR content and saves results as artifacts for privileged workflow
3+
4+
name: AI PR Analysis (Safe)
5+
6+
on:
7+
pull_request:
8+
types: [opened, synchronize, reopened]
9+
10+
# Cancel previous workflow runs for the same PR
11+
concurrency:
12+
group: ${{ github.workflow }}-${{ github.event.number }}
13+
cancel-in-progress: true
14+
15+
permissions:
16+
contents: read
17+
# NO write permissions in this workflow for security
18+
19+
jobs:
20+
analyze-pr:
21+
name: Analyze PR Content (Unprivileged)
22+
runs-on: ubuntu-latest
23+
24+
steps:
25+
- name: Checkout code (Safe - uses default branch)
26+
uses: actions/checkout@v5
27+
with:
28+
fetch-depth: 0
29+
# SECURITY: Do NOT checkout PR head - use base branch only
30+
ref: ${{ github.event.pull_request.base.ref }}
31+
32+
- name: Get PR diff safely
33+
id: pr-diff
34+
run: |
35+
# SECURITY: Get diff without checking out untrusted code
36+
BASE_SHA="${{ github.event.pull_request.base.sha }}"
37+
HEAD_SHA="${{ github.event.pull_request.head.sha }}"
38+
39+
# Use GitHub API to get diff instead of git checkout
40+
curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
41+
-H "Accept: application/vnd.github.v3.diff" \
42+
"https://api.github.com/repos/${{ github.repository }}/compare/$BASE_SHA...$HEAD_SHA" \
43+
> pr_diff.txt
44+
45+
echo "base-sha=$BASE_SHA" >> $GITHUB_OUTPUT
46+
echo "head-sha=$HEAD_SHA" >> $GITHUB_OUTPUT
47+
echo "pr-number=${{ github.event.number }}" >> $GITHUB_OUTPUT
48+
49+
- name: Run AI Analysis (No secrets exposed)
50+
uses: google-github-actions/run-gemini-cli@v0.1.10
51+
with:
52+
prompt: |
53+
You are an expert WordPress plugin developer and security consultant reviewing a pull request for the "Simple WP Optimizer" WordPress plugin.
54+
55+
PLUGIN CONTEXT:
56+
- WordPress performance optimization plugin
57+
- Removes unnecessary WordPress features and scripts to improve performance
58+
- Supports WordPress 6.5+ and PHP 7.4+
59+
- Features include emoji removal, jQuery migrate removal, header cleanup, DNS prefetch optimization
60+
61+
COMPREHENSIVE REVIEW CHECKLIST:
62+
63+
🔒 SECURITY ANALYSIS:
64+
1. SQL Injection vulnerabilities
65+
2. XSS (Cross-Site Scripting) issues
66+
3. CSRF (Cross-Site Request Forgery) protection
67+
4. Input validation and sanitization
68+
5. Output escaping compliance
69+
6. Authentication and authorization checks
70+
7. File upload security (if applicable)
71+
72+
📝 WORDPRESS STANDARDS:
73+
1. WordPress Coding Standards compliance
74+
2. Proper use of WordPress APIs
75+
3. Hook usage (actions/filters)
76+
4. Internationalization (i18n) implementation
77+
5. Plugin structure and organization
78+
6. PHPDoc documentation quality
79+
80+
⚡ PERFORMANCE REVIEW:
81+
1. Database query optimization
82+
2. Caching strategies
83+
3. Resource loading efficiency
84+
4. Memory usage considerations
85+
5. Scalability implications
86+
87+
🏗️ CODE QUALITY:
88+
1. Function complexity and readability
89+
2. Error handling implementation
90+
3. Type safety and parameter validation
91+
4. Code reusability and DRY principles
92+
5. Naming conventions
93+
94+
🔧 PLUGIN-SPECIFIC:
95+
1. WordPress optimization best practices
96+
2. Performance impact assessment
97+
3. Admin interface usability
98+
4. Plugin activation/deactivation handling
99+
5. Compatibility with WordPress core features
100+
101+
REVIEW FORMAT:
102+
For each category, provide:
103+
- ✅ Approved items
104+
- ⚠️ Issues requiring attention (with severity: CRITICAL/HIGH/MEDIUM/LOW)
105+
- 💡 Improvement suggestions
106+
- 📚 Relevant documentation links
107+
108+
Focus on actionable feedback that improves:
109+
- Security posture
110+
- WordPress ecosystem compatibility
111+
- Code maintainability
112+
- Performance and user experience
113+
114+
Analyze the following PR diff:
115+
env:
116+
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
117+
118+
- name: Save PR metadata for privileged workflow
119+
run: |
120+
mkdir -p ./pr-data
121+
echo "${{ github.event.number }}" > ./pr-data/pr-number.txt
122+
echo "${{ github.event.pull_request.head.sha }}" > ./pr-data/head-sha.txt
123+
echo "${{ github.event.pull_request.base.sha }}" > ./pr-data/base-sha.txt
124+
echo "${{ github.event.pull_request.user.login }}" > ./pr-data/author.txt
125+
echo "AI analysis completed successfully" > ./pr-data/status.txt
126+
127+
- name: Upload analysis results
128+
uses: actions/upload-artifact@v4
129+
with:
130+
name: pr-analysis-${{ github.event.number }}
131+
path: pr-data/
132+
retention-days: 30

0 commit comments

Comments
 (0)