Skip to content

fix(predictions): sanitize note content on write (closes #1624) - #1789

Open
bilhokista wants to merge 3 commits into
Arena1X:mainfrom
bilhokista:fix/1624-sanitize-prediction-note
Open

fix(predictions): sanitize note content on write (closes #1624)#1789
bilhokista wants to merge 3 commits into
Arena1X:mainfrom
bilhokista:fix/1624-sanitize-prediction-note

Conversation

@bilhokista

Copy link
Copy Markdown

Closes #1624.

One correction to the issue

The length limit already exists. UpdatePredictionNoteDto carries @MaxLength(1000), so notes are already bounded — the storage-bloat half of the issue is covered on main today.

What was genuinely missing is sanitization. PredictionsService.updateNote did prediction.note = dto.note with no filtering, so markup went straight to the text column.

The issue also points at submit-prediction.dto.ts, but SubmitPredictionDto has no note field — notes are set through the separate PATCH :id/note route and UpdatePredictionNoteDto. This PR touches that path instead.

Why sanitize in the service, not with @Transform

A @Transform decorator on the DTO would be the tidier-looking place, but it only runs when the route's ValidationPipe has transform: true. The note route uses a bare @Body() dto: UpdatePredictionNoteDto with no @UsePipes, unlike SearchController, which sets transform: true explicitly per route.

So a decorator here would silently do nothing — the worst kind of security fix, since it reads as protection while providing none. Sanitizing at the write makes it independent of pipe configuration.

Why strip rather than escape

Notes are personal free text and are never rendered as markup, so tags carry no meaning. Stripping keeps the stored value equal to what the user meant to write, and leaves nothing for a downstream consumer to mis-render. Escaping would preserve <script> in the note, which is safe but is not what anyone typed.

Script and style bodies are removed wholesale rather than just their tags — removing only the tags would leave the code sitting in the note as plain text.

Changes

  • dto/update-prediction-note.dto.ts — exported sanitizeNote(), following the shape of escapeLikeWildcards() in search/dto/search-query.dto.ts.
  • predictions.service.tsupdateNote sanitizes before assigning.
  • predictions.service.spec.ts — six table-driven cases added to the existing updateNote block.

Tests

'<script>alert(1)</script>Real analysis' -> 'Real analysis'
'<img src=x onerror=alert(1)>note'       -> 'note'
'<b>bold</b> and <i>italic</i>'          -> 'bold and italic'
'<style>body{}</style>clean'             -> 'clean'
'  padded  '                             -> 'padded'
'plain text, unchanged'                  -> 'plain text, unchanged'

The last case matters as much as the first: it asserts ordinary notes pass through untouched, so the sanitizer cannot quietly start eating legitimate text.

Assertions are on what reaches predictionsRepository.save, not on the return value, since the stored content is what the issue is about.

I also verified 'unclosed <div oops' collapses to 'unclosed' — the trailing-? in the tag pattern covers unterminated tags, which is the usual way a naive strip gets bypassed.

Verification

All three files parse clean via the TypeScript compiler API, and sanitizeNote was exercised against the seven cases above in isolation. I did not run the Jest suite — I worked from the individual files via the API rather than a full clone, so please treat CI as the authority.

Behaviour for a note containing no markup is byte-identical to before, apart from trimming surrounding whitespace.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CrfEY1tvXrbeMDAUzxfuk7

@vercel

vercel Bot commented Sep 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
insight-arena-4rll Ready Ready Preview Sep 10, 2026 12:53pm UTC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Backend] — Prediction Note Length and Sanitization

1 participant