Skip to content

Commit d757176

Browse files
committed
Fix RADAR issues: enhanced diagnostics, PR metadata display, and link behavior
1. GitHub PAT Authentication (401 Bad Credentials): - Added detailed logging in function_app.py to show token length, prefix, and source - Enhanced diagnostics in API response with bot_token_length and bot_token_prefix - Function restarted to ensure Key Vault reference resolution - Next HTML will show exact token status for debugging 2. PR Metadata Display: - Modified generate_html_report() to accept optional pr_metadata parameter - Added PR information card showing: PR number, title, author, branches, commit SHA - Card displays after main title with dark theme styling - Metadata passed from generate_multi_spec_report() 3. HTML Link Opening in New Tab: - Changed from <a href=...target='_blank'> (unsupported in GitHub Markdown) - Now uses standard Markdown link with user instructions - Added tip: 'Right-click and Open link in new tab' or Ctrl+Click All changes deployed to Azure Function and ready for next pipeline run.
1 parent 7d172a9 commit d757176

File tree

4 files changed

+274
-8
lines changed

4 files changed

+274
-8
lines changed
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# RADAR Label Workflow - Setup Instructions
2+
3+
## ✅ Completed Changes
4+
5+
### 1. Code Updates (Committed & Deployed)
6+
- **GitHubClient.py**: Added `add_label()` method for consistent label management
7+
- **CveSpecFilePRCheck.py**: Pipeline now adds `radar-issues-detected` label when posting PR check comments
8+
- **function_app.py**: Azure Function now uses `GITHUB_TOKEN` (bot PAT) and adds `radar-acknowledged` label
9+
10+
### 2. Authentication Pattern
11+
Following the same pattern as `GitHubClient`:
12+
```python
13+
# Both pipeline and Azure Function use GITHUB_TOKEN
14+
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN", "")
15+
16+
# Use 'token' format for GitHub PATs (not 'Bearer')
17+
headers = {
18+
"Authorization": f"token {GITHUB_TOKEN}",
19+
"Accept": "application/vnd.github.v3+json"
20+
}
21+
```
22+
23+
### 3. Deployment Status
24+
- ✅ Azure Function deployed successfully (radarfunc-labels.zip)
25+
- ✅ Code committed to `abadawi/multi-spec-radar` branch
26+
- ⏸️ Pending: Configure `GITHUB_TOKEN` environment variable
27+
28+
---
29+
30+
## 🔧 Required Configuration
31+
32+
### Step 1: Add GITHUB_TOKEN to Azure Function
33+
34+
The Azure Function needs the same bot PAT that the pipeline uses (`githubPrPat`).
35+
36+
**Option A: If you know the PAT value:**
37+
```bash
38+
az functionapp config appsettings set \
39+
--name radarfunc \
40+
--resource-group Radar-Storage-RG \
41+
--settings "GITHUB_TOKEN=<your_github_pat_here>"
42+
```
43+
44+
**Option B: Retrieve from Azure DevOps Key Vault:**
45+
The pipeline gets this from `$(githubPrPat)` variable. You may need to:
46+
1. Check Azure DevOps variable groups for the PAT value
47+
2. Or regenerate a new PAT from the CBL Mariner bot GitHub account
48+
49+
### Step 2: Create GitHub Labels
50+
51+
Create these 2 labels in the `microsoft/azurelinux` repository:
52+
53+
**Label 1: radar-issues-detected**
54+
- Name: `radar-issues-detected`
55+
- Description: `RADAR detected potential issues in this PR`
56+
- Color: `#D73A4A` (red)
57+
58+
**Label 2: radar-acknowledged**
59+
- Name: `radar-acknowledged`
60+
- Description: `Feedback submitted for RADAR findings`
61+
- Color: `#0E8A16` (green)
62+
63+
**How to create labels:**
64+
1. Go to https://github.com/microsoft/azurelinux/labels
65+
2. Click "New label"
66+
3. Enter name, description, and color
67+
4. Click "Create label"
68+
5. Repeat for the second label
69+
70+
---
71+
72+
## 📋 Complete Workflow
73+
74+
### When Pipeline Detects Issues:
75+
1. ✅ Pipeline runs CVE spec file check
76+
2. ✅ If issues found (severity >= WARNING):
77+
- Posts comment to PR with findings
78+
- **Adds `radar-issues-detected` label**
79+
3. ✅ Comment includes link to interactive HTML report (blob storage)
80+
81+
### When User Submits Challenge:
82+
1. ✅ User opens HTML report, clicks "Challenge" button
83+
2. ✅ User authenticates with GitHub OAuth
84+
3. ✅ User fills out challenge form (False Alarm/Needs Context/Acknowledged)
85+
4. ✅ Azure Function receives challenge:
86+
- Saves to analytics.json in blob storage
87+
- Posts comment to PR (using bot account with user attribution)
88+
- **Adds `radar-acknowledged` label**
89+
90+
### Label Benefits:
91+
- **Filtering**: Easily find PRs with RADAR issues or feedback
92+
- **Dashboards**: Track how many PRs have issues vs. acknowledged
93+
- **Automation**: Could trigger additional workflows based on labels
94+
- **Visibility**: Labels appear prominently in PR list and on the PR page
95+
96+
---
97+
98+
## 🧪 Testing Plan
99+
100+
### Test 1: Pipeline Label Addition
101+
1. Push changes to `test/basic-antipatterns` branch
102+
2. Pipeline should run and detect issues
103+
3. Verify PR #14904 has:
104+
- Comment posted by CBL Mariner bot
105+
- `radar-issues-detected` label added
106+
107+
### Test 2: Challenge Label Addition
108+
1. Open latest HTML report from blob storage
109+
2. Submit a challenge for any finding
110+
3. Verify PR #14904 has:
111+
- New comment posted by CBL Mariner bot (showing user attribution)
112+
- `radar-acknowledged` label added
113+
114+
### Test 3: End-to-End Workflow
115+
1. Create fresh test PR with spec file changes
116+
2. Pipeline runs → comment + `radar-issues-detected` label
117+
3. Submit challenge → comment + `radar-acknowledged` label
118+
4. Both labels visible on PR
119+
120+
---
121+
122+
## 📝 Next Steps
123+
124+
### Immediate (Required):
125+
1. **Add GITHUB_TOKEN to Azure Function** (see Step 1 above)
126+
2. **Create the 2 labels** in GitHub repository (see Step 2 above)
127+
3. **Test the workflow** on PR #14904
128+
129+
### Future Enhancements:
130+
- Add PR metadata to HTML reports (title, author, branches)
131+
- Create dashboard to track challenge statistics
132+
- Add webhook to notify team when challenges submitted
133+
- Implement auto-close for PRs with all findings acknowledged
134+
135+
---
136+
137+
## 🔍 Troubleshooting
138+
139+
### If labels not added:
140+
- Check function logs: `az functionapp logs tail --name radarfunc --resource-group Radar-Storage-RG`
141+
- Verify `GITHUB_TOKEN` is configured: `az functionapp config appsettings list --name radarfunc --resource-group Radar-Storage-RG`
142+
- Ensure labels exist in GitHub repository
143+
- Check that bot PAT has `repo` scope permissions
144+
145+
### If comments not posted:
146+
- Verify `GITHUB_TOKEN` has correct permissions
147+
- Check bot account has write access to repository
148+
- Review function logs for detailed error messages
149+
150+
---
151+
152+
## 📚 Files Changed
153+
154+
- `.pipelines/prchecks/CveSpecFilePRCheck/GitHubClient.py`
155+
- `.pipelines/prchecks/CveSpecFilePRCheck/CveSpecFilePRCheck.py`
156+
- `.pipelines/prchecks/CveSpecFilePRCheck/azure-function/function_app.py`
157+
158+
**Commit**: `d5ad71165` on `abadawi/multi-spec-radar` branch
159+
**Deployment**: Successfully deployed to `radarfunc` Azure Function

.pipelines/prchecks/CveSpecFilePRCheck/ResultAnalyzer.py

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -482,12 +482,13 @@ def _get_severity_emoji(self, severity: Severity) -> str:
482482
}
483483
return emoji_map.get(severity, "ℹ️")
484484

485-
def generate_html_report(self, analysis_result: 'MultiSpecAnalysisResult') -> str:
485+
def generate_html_report(self, analysis_result: 'MultiSpecAnalysisResult', pr_metadata: Optional[dict] = None) -> str:
486486
"""
487487
Generate an interactive HTML report with dark theme and expandable sections.
488488
489489
Args:
490490
analysis_result: MultiSpecAnalysisResult with all spec data
491+
pr_metadata: Optional dict with PR metadata (pr_number, pr_title, pr_author, etc.)
491492
492493
Returns:
493494
HTML string with embedded CSS and JavaScript for interactivity
@@ -506,7 +507,40 @@ def generate_html_report(self, analysis_result: 'MultiSpecAnalysisResult') -> st
506507
Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC')}
507508
</p>
508509
</div>
509-
510+
"""
511+
512+
# Add PR metadata section if provided
513+
if pr_metadata:
514+
pr_number = pr_metadata.get('pr_number', 'Unknown')
515+
pr_title = html_module.escape(pr_metadata.get('pr_title', 'Unknown'))
516+
pr_author = html_module.escape(pr_metadata.get('pr_author', 'Unknown'))
517+
source_branch = html_module.escape(pr_metadata.get('source_branch', 'unknown'))
518+
target_branch = html_module.escape(pr_metadata.get('target_branch', 'main'))
519+
source_commit = pr_metadata.get('source_commit_sha', '')[:8]
520+
521+
html += f"""
522+
<div style="background: #161b22; border: 1px solid #30363d; border-radius: 6px; padding: 16px; margin-bottom: 20px;">
523+
<h3 style="margin: 0 0 12px 0; color: #58a6ff; font-size: 16px;">📋 Pull Request Information</h3>
524+
<div style="display: grid; grid-template-columns: auto 1fr; gap: 8px 16px; font-size: 13px;">
525+
<span style="color: #8b949e;">PR Number:</span>
526+
<span style="color: #c9d1d9; font-weight: 600;">#{pr_number}</span>
527+
528+
<span style="color: #8b949e;">Title:</span>
529+
<span style="color: #c9d1d9;">{pr_title}</span>
530+
531+
<span style="color: #8b949e;">Author:</span>
532+
<span style="color: #c9d1d9;">@{pr_author}</span>
533+
534+
<span style="color: #8b949e;">Branches:</span>
535+
<span style="color: #c9d1d9;"><code style="background: #0d1117; padding: 2px 6px; border-radius: 3px; font-size: 12px;">{source_branch}</code> → <code style="background: #0d1117; padding: 2px 6px; border-radius: 3px; font-size: 12px;">{target_branch}</code></span>
536+
537+
<span style="color: #8b949e;">Commit:</span>
538+
<span style="color: #c9d1d9;"><code style="background: #0d1117; padding: 2px 6px; border-radius: 3px; font-size: 12px;">{source_commit}</code></span>
539+
</div>
540+
</div>
541+
"""
542+
543+
html += f"""
510544
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-bottom: 20px;">
511545
<div style="background: #161b22; padding: 15px; border-radius: 6px; text-align: center; border: 1px solid #30363d;">
512546
<div style="font-size: 24px; font-weight: bold; color: #58a6ff;">{stats['total_specs']}</div>
@@ -661,7 +695,7 @@ def generate_multi_spec_report(self, analysis_result: 'MultiSpecAnalysisResult',
661695
# Add HTML report - try blob storage first, fall back to Gist
662696
# Note: Blob storage preferred for production, Gist as fallback
663697
if include_html and (blob_storage_client or github_client):
664-
html_report = self.generate_html_report(analysis_result)
698+
html_report = self.generate_html_report(analysis_result, pr_metadata=pr_metadata)
665699

666700
# Create a self-contained HTML page with authentication
667701
html_page = f"""<!DOCTYPE html>
@@ -1201,6 +1235,9 @@ def generate_multi_spec_report(self, analysis_result: 'MultiSpecAnalysisResult',
12011235
console.log(' GitHub label added:', result.github_label_added);
12021236
if (result.diagnostics) {{
12031237
console.log(' Diagnostics:', result.diagnostics);
1238+
console.log(' Bot token loaded:', result.diagnostics.using_bot_token);
1239+
console.log(' Bot token length:', result.diagnostics.bot_token_length);
1240+
console.log(' Bot token prefix:', result.diagnostics.bot_token_prefix);
12041241
}}
12051242
12061243
let message = '✅ Challenge submitted successfully!\\n\\n';
@@ -1351,16 +1388,14 @@ def generate_multi_spec_report(self, analysis_result: 'MultiSpecAnalysisResult',
13511388

13521389
if html_url:
13531390
# Add prominent HTML report link section
1354-
# Note: GitHub Markdown doesn't support target="_blank" in <a> tags
1355-
# Use HTML anchor tag to open in new tab
13561391
report_lines.append("")
13571392
report_lines.append("---")
13581393
report_lines.append("")
13591394
report_lines.append("## 📊 Interactive HTML Report")
13601395
report_lines.append("")
1361-
report_lines.append(f"### 🔗 <a href=\"{html_url}\" target=\"_blank\"><strong>CLICK HERE to open the Interactive HTML Report</strong></a>")
1396+
report_lines.append(f"### 🔗 **[CLICK HERE to open the Interactive HTML Report]({html_url})**")
13621397
report_lines.append("")
1363-
report_lines.append("*The report will open in a new tab automatically*")
1398+
report_lines.append("💡 **Tip:** Right-click the link above and select 'Open link in new tab' (or Ctrl+Click on Windows/Linux, Cmd+Click on Mac)")
13641399
report_lines.append("")
13651400
report_lines.append("**Features:**")
13661401
report_lines.append("- 🎯 Interactive anti-pattern detection results")
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Key Vault Access Request for Azure Function
2+
3+
## Summary
4+
The `radarfunc` Azure Function needs to read the GitHub PAT from Key Vault to post PR comments securely.
5+
6+
## Current Configuration
7+
**Azure Function**: `radarfunc` (Radar-Storage-RG)
8+
**User-Assigned Managed Identity**: `cblmargh-identity`
9+
- Client ID: `7bf2e2c3-009a-460e-90d4-eff987a8d71d`
10+
- Principal ID: `4cb669bf-1ae6-463a-801a-2d491da37b9d`
11+
**Key Vault Reference Configured**:
12+
```
13+
[email protected](SecretUri=https://mariner-pipelines-kv.vault.azure.net/secrets/cblmarghGithubPRPat/)
14+
```
15+
16+
## Required Action
17+
**Grant RBAC Permission** to allow the UMI to read secrets from Key Vault.
18+
19+
### Command to Run:
20+
```bash
21+
az role assignment create \
22+
--assignee 7bf2e2c3-009a-460e-90d4-eff987a8d71d \
23+
--role "Key Vault Secrets User" \
24+
--scope "/subscriptions/0012ca50-c773-43b2-80e2-f24b6377145c/resourceGroups/MarinerPipelines_RG/providers/Microsoft.KeyVault/vaults/mariner-pipelines-kv"
25+
```
26+
27+
### Who Can Run This:
28+
- User with **Owner** or **User Access Administrator** role on:
29+
- The `mariner-pipelines-kv` Key Vault, OR
30+
- The `MarinerPipelines_RG` resource group, OR
31+
- The subscription
32+
33+
### Why This Is Needed:
34+
1. The Azure Function posts GitHub comments when users submit challenge feedback
35+
2. It needs the GitHub PAT to authenticate with GitHub API
36+
3. Storing PAT in Key Vault (vs app settings) is more secure:
37+
- No plaintext secrets in configuration
38+
- Automatic rotation support
39+
- Centralized secret management
40+
- Audit trail of secret access
41+
42+
### Verification After Granting Access:
43+
Check if the permission was granted:
44+
```bash
45+
az role assignment list \
46+
--assignee 7bf2e2c3-009a-460e-90d4-eff987a8d71d \
47+
--scope "/subscriptions/0012ca50-c773-43b2-80e2-f24b6377145c/resourceGroups/MarinerPipelines_RG/providers/Microsoft.KeyVault/vaults/mariner-pipelines-kv"
48+
```
49+
50+
Test if the function can resolve the Key Vault reference:
51+
```bash
52+
# Restart the function to pick up the permission
53+
az functionapp restart --name radarfunc --resource-group Radar-Storage-RG
54+
55+
# Check function logs for any Key Vault access errors
56+
az functionapp log tail --name radarfunc --resource-group Radar-Storage-RG
57+
```
58+
59+
---
60+
61+
## Context
62+
- **Pipeline**: Already uses this same UMI and Key Vault secret successfully
63+
- **Function**: Shares the same infrastructure pattern for consistency
64+
- **Security**: Follows Azure best practices for secret management

.pipelines/prchecks/CveSpecFilePRCheck/azure-function/function_app.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,9 +313,14 @@ def submit_challenge(req: func.HttpRequest) -> func.HttpResponse:
313313
# Use GITHUB_TOKEN (bot PAT) for reliable comment posting
314314
# This is the same CBL Mariner bot that posts PR check comments
315315
bot_token = GITHUB_TOKEN
316+
logger.info(f"🔑 GITHUB_TOKEN loaded: {'Yes' if GITHUB_TOKEN else 'No (empty)'}")
317+
logger.info(f"🔑 Bot token length: {len(bot_token) if bot_token else 0} chars")
318+
logger.info(f"🔑 Bot token starts with: {bot_token[:10] if bot_token else 'N/A'}...")
319+
316320
if not bot_token:
317321
logger.warning("⚠️ GITHUB_TOKEN not configured, falling back to user token")
318322
bot_token = github_token
323+
logger.info(f"🔑 Using user token instead (length: {len(bot_token) if bot_token else 0})")
319324

320325
comment_headers = {
321326
"Authorization": f"token {bot_token}", # Use 'token' not 'Bearer' for GitHub PATs
@@ -337,7 +342,8 @@ def submit_challenge(req: func.HttpRequest) -> func.HttpResponse:
337342
logger.error(f"❌ Failed to post GitHub comment:")
338343
logger.error(f" Status: {comment_response.status_code}")
339344
logger.error(f" Response: {comment_response.text}")
340-
logger.error(f" GitHub Token (first 10 chars): {github_token[:10] if github_token else 'None'}...")
345+
logger.error(f" Bot Token (first 10 chars): {bot_token[:10] if bot_token else 'None'}...")
346+
logger.error(f" Token source: {'GITHUB_TOKEN env var' if GITHUB_TOKEN else 'User JWT token'}")
341347
logger.error(f" Comment URL: {comment_url}")
342348

343349
# Add simple label to indicate PR has been acknowledged/reviewed
@@ -382,6 +388,8 @@ def submit_challenge(req: func.HttpRequest) -> func.HttpResponse:
382388
'message': label_response.text[:200]
383389
}
384390
diagnostic_info['using_bot_token'] = bool(GITHUB_TOKEN)
391+
diagnostic_info['bot_token_length'] = len(GITHUB_TOKEN) if GITHUB_TOKEN else 0
392+
diagnostic_info['bot_token_prefix'] = GITHUB_TOKEN[:10] if GITHUB_TOKEN else 'empty'
385393

386394
return func.HttpResponse(
387395
json.dumps({

0 commit comments

Comments
 (0)