fix(releases): bind token to saved provider:identifier match

The token must only be used for repositories that are registered in the
widget's saved options, matched by provider:identifier (not by fragile
cuid2 id). This prevents tokens from being sent to arbitrary providerUrl
values submitted by the caller.

The saved providerUrl is used in preference to the request's providerUrl
when a token is being applied.
This commit is contained in:
ajnart committed 2026-07-10 17:53:26 +02:00
1 parent 173881f468
commit d33ed20149
1 file changed
+15 -2
+15 -2
View File
@@ -1,3 +1,4 @@
import SuperJSON from "superjson";
import { escapeForRegEx } from "@tiptap/react";
import { z } from "zod/v4";
@@ -44,6 +45,7 @@ export const releasesRouter = createTRPCRouter({
)
.query(async ({ ctx, input }) => {
const tokensByProvider = new Map<string, string>();
const savedRepos = new Map<string, { providerUrl?: string }>();
if (input.itemId) {
try {
@@ -51,6 +53,16 @@ export const releasesRouter = createTRPCRouter({
if (item && item.kind === "releases") {
await throwIfActionForbiddenAsync(ctx, eq(boards.id, item.boardId), "view");
const options = SuperJSON.parse<Record<string, unknown>>(item.options);
const repos = options.repositories as Array<{ provider?: string; identifier: string; providerUrl?: string }> | undefined;
if (repos) {
for (const repo of repos) {
if (repo.provider && repo.identifier) {
savedRepos.set(`${repo.provider}:${repo.identifier}`, { providerUrl: repo.providerUrl });
}
}
}
const secrets = await ctx.db.query.widgetSecrets.findMany({
where: eq(widgetSecrets.itemId, input.itemId),
});
@@ -70,6 +82,7 @@ export const releasesRouter = createTRPCRouter({
return await Promise.all(
input.repositories.map(async (repository) => {
const repositoryId = repository.id ?? repository.identifier;
const savedRepo = savedRepos.get(`${repository.provider}:${repository.identifier}`);
try {
const response = await releasesRequestHandler
.handler({
@@ -77,8 +90,8 @@ export const releasesRouter = createTRPCRouter({
provider: repository.provider,
identifier: repository.identifier,
versionRegex: formatVersionFilterRegex(repository.versionFilter),
providerUrl: repository.providerUrl,
token: tokensByProvider.get(repository.provider),
providerUrl: savedRepo?.providerUrl ?? repository.providerUrl,
token: savedRepo ? tokensByProvider.get(repository.provider) : undefined,
})
.getDataAsync();