fix(github): check the private key before generating app tokens

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andras Bacsai
2026-09-28 11:18:29 +02:00
co-authored by Claude Opus 5.5
parent af19bd0a50
commit eed50d6781
2 changed files with 42 additions and 2 deletions
+11 -2
View File
@@ -166,11 +166,20 @@ function assertGithubClockInSync(string $apiUrl): void
}
}
function githubAppPrivateKey(GithubApp $source): ?PrivateKey
{
$privateKey = $source->privateKey;
return $privateKey?->team_id === $source->team_id ? $privateKey : null;
}
function generateGithubToken(GithubApp $source, string $type)
{
$privateKey = githubAppPrivateKey($source) ?? throw new RuntimeException('Private key not found for this GitHub App.');
assertGithubClockInSync($source->api_url);
$signingKey = InMemory::plainText($source->privateKey->private_key);
$signingKey = InMemory::plainText($privateKey->private_key);
$algorithm = new Sha256;
$tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default()));
$now = CarbonImmutable::now()->setTimezone('UTC');
@@ -279,7 +288,7 @@ function syncGithubAppName(GithubApp $source, bool $throw = false): ?string
return null;
}
$privateKey = $source->privateKey ?: PrivateKey::find($source->private_key_id);
$privateKey = githubAppPrivateKey($source);
if (! $privateKey) {
return null;
@@ -278,6 +278,37 @@ describe('GitHub Source Change Component', function () {
expect($githubApp->refresh()->private_key_id)->toBe($ownKey->id);
})->with(['submit', 'instantSave']);
test('github app tokens only use a key from the source team', function () {
Http::fake();
$otherTeam = Team::factory()->create();
$foreignKey = PrivateKey::create([
'name' => 'other-team-key',
'private_key' => validPrivateKey(),
'team_id' => $otherTeam->id,
]);
$githubApp = GithubApp::create([
'name' => 'Test GitHub App',
'api_url' => 'https://api.github.com',
'html_url' => 'https://github.com',
'custom_user' => 'git',
'custom_port' => 22,
'app_id' => 12345,
'installation_id' => 67890,
'team_id' => $this->team->id,
'is_system_wide' => false,
]);
GithubApp::query()->whereKey($githubApp->id)->update(['private_key_id' => $foreignKey->id]);
$githubApp->refresh();
expect(fn () => generateGithubJwt($githubApp))->toThrow(RuntimeException::class)
->and(syncGithubAppName($githubApp))->toBeNull()
->and($foreignKey->refresh()->name)->toBe('other-team-key');
Http::assertNothingSent();
});
test('ghe.com installation path encodes the organization segment', function () {
$githubApp = new GithubApp;
$githubApp->forceFill([