|
| 1 | +# Implementation Plan: Workflow Permissions Policy Evaluator |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +Implement the `correct_workflow_permissions` policy evaluator to check that repositories have their GitHub Actions default workflow permissions set to "read" (read repository contents and packages permissions) as specified in the PRD. |
| 6 | + |
| 7 | +## Context |
| 8 | + |
| 9 | +According to the PRD (section 3.4, policy #3), the application must verify that repositories have workflow permissions set to 'Read repository contents and packages permissions'. This is a security best practice that prevents workflows from having unnecessary write access. |
| 10 | + |
| 11 | +## Implementation Steps |
| 12 | + |
| 13 | +### 1. Add GitHub API Method for Workflow Permissions |
| 14 | + |
| 15 | +**File**: `10xGitHubPolicies.App/Services/GitHub/IGitHubService.cs` |
| 16 | + |
| 17 | +Add a new method to the interface: |
| 18 | + |
| 19 | +```csharp |
| 20 | +Task<string> GetWorkflowPermissionsAsync(long repositoryId); |
| 21 | +``` |
| 22 | + |
| 23 | +**File**: `10xGitHubPolicies.App/Services/GitHub/GitHubService.cs` |
| 24 | + |
| 25 | +Implement the method using Octokit's REST API client: |
| 26 | + |
| 27 | +```csharp |
| 28 | +public async Task<string> GetWorkflowPermissionsAsync(long repositoryId) |
| 29 | +{ |
| 30 | + var client = await GetAuthenticatedClient(); |
| 31 | + try |
| 32 | + { |
| 33 | + // Use the GitHub API endpoint: GET /repos/{owner}/{repo}/actions/permissions/workflow |
| 34 | + var connection = client.Connection; |
| 35 | + var endpoint = new Uri($"repositories/{repositoryId}/actions/permissions/workflow", UriKind.Relative); |
| 36 | + var response = await connection.Get<WorkflowPermissionsResponse>(endpoint, null); |
| 37 | + return response.Body.DefaultWorkflowPermissions; |
| 38 | + } |
| 39 | + catch (NotFoundException) |
| 40 | + { |
| 41 | + _logger.LogWarning("Workflow permissions not found for repository {RepositoryId}. Actions may be disabled.", repositoryId); |
| 42 | + return null; |
| 43 | + } |
| 44 | +} |
| 45 | + |
| 46 | +// Add a private class for deserialization |
| 47 | +private class WorkflowPermissionsResponse |
| 48 | +{ |
| 49 | + public string DefaultWorkflowPermissions { get; set; } |
| 50 | + public bool CanApprovePullRequestReviews { get; set; } |
| 51 | +} |
| 52 | +``` |
| 53 | + |
| 54 | +### 2. Implement the Policy Evaluator Logic |
| 55 | + |
| 56 | +**File**: `10xGitHubPolicies.App/Services/Policies/Evaluators/CorrectWorkflowPermissionsEvaluator.cs` |
| 57 | + |
| 58 | +Replace the TODO implementation with actual logic: |
| 59 | + |
| 60 | +```csharp |
| 61 | +public async Task<PolicyViolation?> EvaluateAsync(Octokit.Repository repository) |
| 62 | +{ |
| 63 | + var permissions = await _githubService.GetWorkflowPermissionsAsync(repository.Id); |
| 64 | + |
| 65 | + // If permissions is null, Actions might be disabled - consider this compliant |
| 66 | + if (permissions == null) |
| 67 | + { |
| 68 | + return null; |
| 69 | + } |
| 70 | + |
| 71 | + // Check if permissions are set to "read" (the secure, restrictive setting) |
| 72 | + if (permissions != "read") |
| 73 | + { |
| 74 | + return new PolicyViolation |
| 75 | + { |
| 76 | + PolicyType = PolicyType |
| 77 | + }; |
| 78 | + } |
| 79 | + |
| 80 | + return null; |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | +### 3. Update Documentation |
| 85 | + |
| 86 | +**File**: `docs/github-integration.md` |
| 87 | + |
| 88 | +Add the new method to the IGitHubService methods list (around line 16): |
| 89 | + |
| 90 | +```markdown |
| 91 | +- `Task<string> GetWorkflowPermissionsAsync(long repositoryId)`: Gets the default workflow permissions for a repository. Returns "read" or "write", or null if Actions are disabled. |
| 92 | +``` |
| 93 | + |
| 94 | +Add a usage example in the document: |
| 95 | + |
| 96 | +````markdown |
| 97 | +### Example: Checking Workflow Permissions |
| 98 | + |
| 99 | +```csharp |
| 100 | +public async Task CheckRepositorySecurityAsync(long repositoryId) |
| 101 | +{ |
| 102 | + var permissions = await _gitHubService.GetWorkflowPermissionsAsync(repositoryId); |
| 103 | + |
| 104 | + if (permissions == "write") |
| 105 | + { |
| 106 | + _logger.LogWarning("Repository {RepoId} has write permissions for workflows", repositoryId); |
| 107 | + } |
| 108 | +} |
| 109 | +```` |
| 110 | + |
| 111 | +``` |
| 112 | +
|
| 113 | +**File**: `docs/policy-evaluation.md` |
| 114 | +
|
| 115 | +Update the example configuration (around line 119) to include a complete example for the workflow permissions policy: |
| 116 | +
|
| 117 | +```yaml |
| 118 | +- name: 'Verify Workflow Permissions' |
| 119 | + type: 'correct_workflow_permissions' |
| 120 | + action: 'create-issue' |
| 121 | + issue_details: |
| 122 | + title: 'Security: Workflow permissions should be read-only' |
| 123 | + body: 'This repository has GitHub Actions workflow permissions set to write. For security, please change the default workflow permissions to "Read repository contents and packages permissions" in Settings > Actions > General.' |
| 124 | + labels: ['policy-violation', 'security'] |
| 125 | +``` |
| 126 | + |
| 127 | +## Key Technical Details |
| 128 | + |
| 129 | +- **GitHub API Endpoint**: `GET /repos/{owner}/{repo}/actions/permissions/workflow` |
| 130 | +- **Expected Values**: |
| 131 | + - `"read"` - Read-only permissions (compliant) |
| 132 | + - `"write"` - Read and write permissions (violation) |
| 133 | + - `null` - Actions disabled or permissions not configured (treated as compliant) |
| 134 | +- **Octokit Usage**: Uses the low-level `Connection.Get<T>()` method since Octokit.net may not have a high-level wrapper for this endpoint |
| 135 | +- **Error Handling**: Returns null for NotFoundException, treating disabled Actions as compliant |
| 136 | + |
| 137 | +## Testing Considerations |
| 138 | + |
| 139 | +After implementation, test with: |
| 140 | + |
| 141 | +1. A repository with read-only workflow permissions (should pass) |
| 142 | +2. A repository with write workflow permissions (should fail) |
| 143 | +3. A repository with Actions disabled (should pass) |
| 144 | + |
| 145 | +## Dependencies |
| 146 | + |
| 147 | +- Existing `IGitHubService` infrastructure |
| 148 | +- Octokit.net library (already installed) |
| 149 | +- Entity Framework entities (PolicyViolation) |
0 commit comments