-
Notifications
You must be signed in to change notification settings - Fork 2
fix: Check org membership to determine external PR #46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -640,7 +640,14 @@ public function getEvent(string $event, string $payload): array | |||||||||||||||||||||||||||||||||||||||||||||||
| $authorAvatarUrl = $payload['pull_request']['user']['avatar_url'] ?? ''; | ||||||||||||||||||||||||||||||||||||||||||||||||
| $commitHash = $payload['pull_request']['head']['sha'] ?? ''; | ||||||||||||||||||||||||||||||||||||||||||||||||
| $headCommitUrl = $repositoryUrl . "/commits/" . $commitHash; | ||||||||||||||||||||||||||||||||||||||||||||||||
| $external = $payload['pull_request']['head']['user']['login'] !== $payload['pull_request']['base']['user']['login']; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| $authorUsername = $payload['pull_request']['user']['login'] ?? ($payload['pull_request']['head']['user']['login'] ?? ''); | ||||||||||||||||||||||||||||||||||||||||||||||||
| $isOrgRepository = ($payload['repository']['owner']['type'] ?? '') === 'Organization'; | ||||||||||||||||||||||||||||||||||||||||||||||||
| if ($isOrgRepository) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| $external = !$this->isUserMemberOfOrganization($authorUsername, $owner); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||
| $external = $authorUsername !== $owner; | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| return [ | ||||||||||||||||||||||||||||||||||||||||||||||||
| 'branch' => $branch, | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -686,4 +693,11 @@ public function validateWebhookEvent(string $payload, string $signature, string | |||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return $signature === ('sha256=' . hash_hmac('sha256', $payload, $signatureKey)); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| public function isUserMemberOfOrganization(string $username, string $organization): bool | ||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||
| $url = "/orgs/{$organization}/memberships/{$username}"; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This API needs "Org: read member" permission for the GitHub app, so that's something we need to change for Appwrite's GH app before deploying this. |
||||||||||||||||||||||||||||||||||||||||||||||||
| $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"]); | ||||||||||||||||||||||||||||||||||||||||||||||||
| return $response['headers']['status-code'] >= 200 && $response['headers']['status-code'] < 300; | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+697
to
+702
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Security bug: treating pending org invites as internal. The memberships API returns 2xx for both active and pending memberships. You must ensure state === 'active'. Also, avoid failing event parsing on transient API errors; default to “not a member”. Apply this diff: - public function isUserMemberOfOrganization(string $username, string $organization): bool
- {
- $url = "/orgs/{$organization}/memberships/{$username}";
- $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"]);
- return $response['headers']['status-code'] >= 200 && $response['headers']['status-code'] < 300;
- }
+ public function isUserMemberOfOrganization(string $username, string $organization): bool
+ {
+ if ($username === '' || $organization === '') {
+ return false;
+ }
+ try {
+ $url = "/orgs/{$organization}/memberships/{$username}";
+ $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"]);
+ $status = $response['headers']['status-code'] ?? 0;
+ if ($status >= 200 && $status < 300) {
+ return (($response['body']['state'] ?? '') === 'active');
+ }
+ return false;
+ } catch (\Throwable $e) {
+ return false;
+ }
+ }📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
New abstract contract looks good; verify all concrete adapters implement it.
Ensure no other non-abstract adapters are missing this method.
Run to confirm coverage:
🏁 Script executed:
Length of output: 669
🏁 Script executed:
Length of output: 102
Implement isUserMemberOfOrganization in Git adapter
src/VCS/Adapter/Git.php is missing the new abstract method
isUserMemberOfOrganization(…); add it to satisfy the Adapter contract.🤖 Prompt for AI Agents