Compare commits

..

3 Commits

Author SHA1 Message Date
Henry
67cf523ee8 fix: API key authentication and REST handler errors (#446) 2026-03-19 12:20:19 +00:00
hjball
0b45ceff7f chore: update translations 2026-03-16 14:32:37 +00:00
Henry
76b2c58461 fix: security improvements to checklist input (#444)
* feat: sanitize input for checklists

* feat: add basic editor

* feat: use plain text editor for checklist items

* feat: remove key from user response
2026-03-16 14:31:13 +00:00
13 changed files with 63 additions and 59 deletions

View File

@@ -11,10 +11,10 @@ https://kan.bn/api/v1
## Authentication
Most endpoints require authentication using your API key. You can create one in the [settings page](https://kan.bn/settings) of your account. Include this key in the `x-api-key` header of each request.
Most endpoints require authentication using your API key. You can create one in the [settings page](https://kan.bn/settings) of your account. Include this key as a Bearer token in the `Authorization` header of each request.
```
'x-api-key': kan_123456789
'Authorization': 'Bearer kan_123456789'
```
## Response codes

View File

@@ -280,7 +280,7 @@
"message": "Add details...",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
"translation": "Details hinzufügen..."
},
"lyqwgn": {
@@ -4336,8 +4336,8 @@
["src/views/boards/components/ImportBoardsForm.tsx", 243],
["src/views/boards/components/ImportBoardsForm.tsx", 395],
["src/views/card/components/AttachmentThumbnails.tsx", 74],
["src/views/card/components/ChecklistItemRow.tsx", 69],
["src/views/card/components/ChecklistItemRow.tsx", 97],
["src/views/card/components/ChecklistItemRow.tsx", 67],
["src/views/card/components/ChecklistItemRow.tsx", 95],
["src/views/card/components/ChecklistNameInput.tsx", 47],
["src/views/card/components/Checklists.tsx", 81],
["src/views/card/components/Comment.tsx", 93],
@@ -5634,7 +5634,7 @@
"message": "Unable to delete checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
"translation": "Checklistenelement konnte nicht gelöscht werden"
},
"2QGEbi": {
@@ -5718,7 +5718,7 @@
"message": "Unable to update checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
"translation": "Checklisten-Element konnte nicht aktualisiert werden"
},
"fYVH36": {

View File

@@ -471,7 +471,7 @@
"origin": [
[
"src/views/card/components/ChecklistItemRow.tsx",
200
169
]
],
"translation": "Add details..."
@@ -7332,11 +7332,11 @@
],
[
"src/views/card/components/ChecklistItemRow.tsx",
69
67
],
[
"src/views/card/components/ChecklistItemRow.tsx",
97
95
],
[
"src/views/card/components/ChecklistNameInput.tsx",
@@ -9571,7 +9571,7 @@
"origin": [
[
"src/views/card/components/ChecklistItemRow.tsx",
96
94
]
],
"translation": "Unable to delete checklist item"
@@ -9707,7 +9707,7 @@
"origin": [
[
"src/views/card/components/ChecklistItemRow.tsx",
68
66
]
],
"translation": "Unable to update checklist item"

View File

@@ -280,7 +280,7 @@
"message": "Add details...",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
"translation": "Añadir detalles..."
},
"lyqwgn": {
@@ -4336,8 +4336,8 @@
["src/views/boards/components/ImportBoardsForm.tsx", 243],
["src/views/boards/components/ImportBoardsForm.tsx", 395],
["src/views/card/components/AttachmentThumbnails.tsx", 74],
["src/views/card/components/ChecklistItemRow.tsx", 69],
["src/views/card/components/ChecklistItemRow.tsx", 97],
["src/views/card/components/ChecklistItemRow.tsx", 67],
["src/views/card/components/ChecklistItemRow.tsx", 95],
["src/views/card/components/ChecklistNameInput.tsx", 47],
["src/views/card/components/Checklists.tsx", 81],
["src/views/card/components/Comment.tsx", 93],
@@ -5634,7 +5634,7 @@
"message": "Unable to delete checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
"translation": "No se pudo eliminar el elemento de la lista de verificación"
},
"2QGEbi": {
@@ -5718,7 +5718,7 @@
"message": "Unable to update checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
"translation": "No se pudo actualizar el elemento de la lista de verificación"
},
"fYVH36": {

View File

@@ -280,7 +280,7 @@
"message": "Add details...",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
"translation": "Ajouter des détails..."
},
"lyqwgn": {
@@ -4336,8 +4336,8 @@
["src/views/boards/components/ImportBoardsForm.tsx", 243],
["src/views/boards/components/ImportBoardsForm.tsx", 395],
["src/views/card/components/AttachmentThumbnails.tsx", 74],
["src/views/card/components/ChecklistItemRow.tsx", 69],
["src/views/card/components/ChecklistItemRow.tsx", 97],
["src/views/card/components/ChecklistItemRow.tsx", 67],
["src/views/card/components/ChecklistItemRow.tsx", 95],
["src/views/card/components/ChecklistNameInput.tsx", 47],
["src/views/card/components/Checklists.tsx", 81],
["src/views/card/components/Comment.tsx", 93],
@@ -5634,7 +5634,7 @@
"message": "Unable to delete checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
"translation": "Impossible de supprimer l'élément de liste de contrôle"
},
"2QGEbi": {
@@ -5718,7 +5718,7 @@
"message": "Unable to update checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
"translation": "Impossible de mettre à jour l'élément de la checklist"
},
"fYVH36": {

View File

@@ -280,7 +280,7 @@
"message": "Add details...",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
"translation": "Aggiungi dettagli..."
},
"lyqwgn": {
@@ -4336,8 +4336,8 @@
["src/views/boards/components/ImportBoardsForm.tsx", 243],
["src/views/boards/components/ImportBoardsForm.tsx", 395],
["src/views/card/components/AttachmentThumbnails.tsx", 74],
["src/views/card/components/ChecklistItemRow.tsx", 69],
["src/views/card/components/ChecklistItemRow.tsx", 97],
["src/views/card/components/ChecklistItemRow.tsx", 67],
["src/views/card/components/ChecklistItemRow.tsx", 95],
["src/views/card/components/ChecklistNameInput.tsx", 47],
["src/views/card/components/Checklists.tsx", 81],
["src/views/card/components/Comment.tsx", 93],
@@ -5634,7 +5634,7 @@
"message": "Unable to delete checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
"translation": "Impossibile eliminare l'elemento della checklist"
},
"2QGEbi": {
@@ -5718,7 +5718,7 @@
"message": "Unable to update checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
"translation": "Impossibile aggiornare l'elemento della checklist"
},
"fYVH36": {

View File

@@ -280,7 +280,7 @@
"message": "Add details...",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
"translation": "Voeg details toe..."
},
"lyqwgn": {
@@ -4336,8 +4336,8 @@
["src/views/boards/components/ImportBoardsForm.tsx", 243],
["src/views/boards/components/ImportBoardsForm.tsx", 395],
["src/views/card/components/AttachmentThumbnails.tsx", 74],
["src/views/card/components/ChecklistItemRow.tsx", 69],
["src/views/card/components/ChecklistItemRow.tsx", 97],
["src/views/card/components/ChecklistItemRow.tsx", 67],
["src/views/card/components/ChecklistItemRow.tsx", 95],
["src/views/card/components/ChecklistNameInput.tsx", 47],
["src/views/card/components/Checklists.tsx", 81],
["src/views/card/components/Comment.tsx", 93],
@@ -5634,7 +5634,7 @@
"message": "Unable to delete checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
"translation": "Kan checklistitem niet verwijderen"
},
"2QGEbi": {
@@ -5718,7 +5718,7 @@
"message": "Unable to update checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
"translation": "Kan checklistitem niet bijwerken"
},
"fYVH36": {

View File

@@ -280,7 +280,7 @@
"message": "Add details...",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
"translation": "Dodaj szczegóły..."
},
"lyqwgn": {
@@ -4336,8 +4336,8 @@
["src/views/boards/components/ImportBoardsForm.tsx", 243],
["src/views/boards/components/ImportBoardsForm.tsx", 395],
["src/views/card/components/AttachmentThumbnails.tsx", 74],
["src/views/card/components/ChecklistItemRow.tsx", 69],
["src/views/card/components/ChecklistItemRow.tsx", 97],
["src/views/card/components/ChecklistItemRow.tsx", 67],
["src/views/card/components/ChecklistItemRow.tsx", 95],
["src/views/card/components/ChecklistNameInput.tsx", 47],
["src/views/card/components/Checklists.tsx", 81],
["src/views/card/components/Comment.tsx", 93],
@@ -5634,7 +5634,7 @@
"message": "Unable to delete checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
"translation": "Nie można usunąć elementu listy kontrolnej"
},
"2QGEbi": {
@@ -5718,7 +5718,7 @@
"message": "Unable to update checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
"translation": "Nie można zaktualizować elementu listy kontrolnej"
},
"fYVH36": {

View File

@@ -280,7 +280,7 @@
"message": "Add details...",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
"translation": "Adicionar detalhes..."
},
"lyqwgn": {
@@ -4336,8 +4336,8 @@
["src/views/boards/components/ImportBoardsForm.tsx", 243],
["src/views/boards/components/ImportBoardsForm.tsx", 395],
["src/views/card/components/AttachmentThumbnails.tsx", 74],
["src/views/card/components/ChecklistItemRow.tsx", 69],
["src/views/card/components/ChecklistItemRow.tsx", 97],
["src/views/card/components/ChecklistItemRow.tsx", 67],
["src/views/card/components/ChecklistItemRow.tsx", 95],
["src/views/card/components/ChecklistNameInput.tsx", 47],
["src/views/card/components/Checklists.tsx", 81],
["src/views/card/components/Comment.tsx", 93],
@@ -5634,7 +5634,7 @@
"message": "Unable to delete checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
"translation": "Não foi possível excluir item da checklist"
},
"2QGEbi": {
@@ -5718,7 +5718,7 @@
"message": "Unable to update checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
"translation": "Não foi possível atualizar o item da checklist"
},
"fYVH36": {

View File

@@ -280,7 +280,7 @@
"message": "Add details...",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
"translation": "Добавить детали..."
},
"lyqwgn": {
@@ -4336,8 +4336,8 @@
["src/views/boards/components/ImportBoardsForm.tsx", 243],
["src/views/boards/components/ImportBoardsForm.tsx", 395],
["src/views/card/components/AttachmentThumbnails.tsx", 74],
["src/views/card/components/ChecklistItemRow.tsx", 69],
["src/views/card/components/ChecklistItemRow.tsx", 97],
["src/views/card/components/ChecklistItemRow.tsx", 67],
["src/views/card/components/ChecklistItemRow.tsx", 95],
["src/views/card/components/ChecklistNameInput.tsx", 47],
["src/views/card/components/Checklists.tsx", 81],
["src/views/card/components/Comment.tsx", 93],
@@ -5634,7 +5634,7 @@
"message": "Unable to delete checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
"translation": "Не удалось удалить элемент контрольного списка"
},
"2QGEbi": {
@@ -5718,7 +5718,7 @@
"message": "Unable to update checklist item",
"placeholders": {},
"comments": [],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
"translation": "Не удалось обновить элемент контрольного списка"
},
"fYVH36": {

View File

@@ -19,6 +19,7 @@ import { encryptToken } from "../utils/encryption";
export const integrationRouter = createTRPCRouter({
saveGitHubToken: protectedProcedure
.input(z.object({ token: z.string() }))
.output(z.object({ success: z.boolean() }))
.mutation(async ({ ctx, input }) => {
const user = ctx.user;
@@ -43,7 +44,9 @@ export const integrationRouter = createTRPCRouter({
return { success: true };
}),
disconnectGitHub: protectedProcedure.mutation(async ({ ctx }) => {
disconnectGitHub: protectedProcedure
.output(z.object({ success: z.boolean() }))
.mutation(async ({ ctx }) => {
const user = ctx.user;
if (!user)
@@ -56,7 +59,9 @@ export const integrationRouter = createTRPCRouter({
return { success: true };
}),
getGitHubStatus: protectedProcedure.query(async ({ ctx }) => {
getGitHubStatus: protectedProcedure
.output(z.object({ connected: z.boolean() }))
.query(async ({ ctx }) => {
const user = ctx.user;
if (!user)

View File

@@ -137,9 +137,7 @@ const loggingMiddleware = t.middleware(async ({ path, type, next, ctx }) => {
return result;
});
export const publicProcedure = t.procedure.use(loggingMiddleware).meta({
openapi: { method: "GET", path: "/public" },
});
export const publicProcedure = t.procedure.use(loggingMiddleware);
const enforceUserIsAuthed = t.middleware(async ({ ctx, next }) => {
if (!ctx.user) {
@@ -163,13 +161,7 @@ const enforceUserIsAdmin = t.middleware(async ({ ctx, next }) => {
export const protectedProcedure = t.procedure
.use(loggingMiddleware)
.use(enforceUserIsAuthed)
.meta({
openapi: {
method: "GET",
path: "/protected",
},
});
.use(enforceUserIsAuthed);
export const adminProtectedProcedure = t.procedure
.use(loggingMiddleware)

View File

@@ -165,6 +165,13 @@ export function createPlugins(db: dbClient) {
: []),
apiKey({
enableSessionForAPIKeys: true,
customAPIKeyGetter: (ctx) => {
const authorization = ctx.headers?.get("authorization");
if (authorization?.startsWith("Bearer ")) {
return authorization.slice(7);
}
return ctx.headers?.get("x-api-key") ?? undefined;
},
rateLimit: {
enabled: true,
timeWindow: 1000 * 60, // 1 minute