Videos.duplicate() copies createdAt and public, drops password → shareable-link quota bypass and public exposure of password-protected caps
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 58/100
- Tipo di issue
- Bug
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Attiva
- Stack tecnologico
- mysql, typescript
Direzione di ricerca
Inizia da packages/web-backend/src/Videos/index.ts e segui duplicate() fino a create() in VideosRepo.ts; esamina Video.ts e le colonne dei video in packages/database/schema.ts. Determina il comportamento previsto di password/public per i duplicati, quindi verifica che gli identificatori di origine e i timestamp non possano raggiungere l'insert, che la protezione non venga indebolita silenziosamente e che i test dell'output dei duplicati coprano la regressione.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
Videos.duplicate() spreads the full source video into repo.create, which lets two
unrelated columns leak through unintended paths: createdAt/updatedAt are forged to the
original video's timestamp instead of getting a fresh one, and public is copied while
password is silently dropped.
Root cause
packages/web-backend/src/Videos/index.ts:413-425:
yield* repo.create(
{
...video,
source: publishedKeys.size > 0 ? { type: "desktopMP4" } : video.source,
metadata: Option.map(video.metadata, (metadata) => { ... }),
},
{ id: newVideoId },
);
video is a Video.Video instance (packages/web-domain/src/Video.ts:27-58), a
Schema.Class whose fields including createdAt, updatedAt, and public are own
enumerable instance properties. password is deliberately excluded from this class
(comment at line 26: "Purposefully doesn't include password as this is a public class"), so
it never rides along in the spread.
CreateVideoInput (packages/web-backend/src/Videos/VideosRepo.ts:11-14) is typed as
Omit<Schema.Type<typeof Video.Video>, "id" | "createdAt" | "updatedAt"> & {...}, but this
Omit only affects the compiler TypeScript does not run excess-property checks against a
spread, so createdAt/updatedAt pass through unflagged at both compile time and runtime.
create() (VideosRepo.ts:106-131) explicitly overrides nine keys
(id, orgId, bucket, storageIntegrationId, metadata, transcriptionStatus,
folderId, width, height, duration) before calling
db.insert(Db.videos).values([{ ...data, ... }]) but not createdAt, updatedAt, or
public. Both createdAt/updatedAt columns are .defaultNow()
(packages/database/schema.ts:439,446), which Drizzle/MySQL only apply when the key is
absent from the insert here it's present with the original video's value, so the default
never fires. password (schema.ts:448) is nullable with no default and is never a key on
data at all, so it comes out NULL.
Consequence 1: shareable-link quota bypass
SHAREABLE_LINK_LIMIT_ENFORCED_FROM = 2026-08-19T00:00:00.000Z
(packages/web-domain/src/Video.ts:22-24), and
apps/web/lib/shareable-link-quota.ts:32 exempts any video with
createdAt < SHAREABLE_LINK_LIMIT_ENFORCED_FROM from the free-tier 25-link/month cap,
permanently. Duplicating any video created before 2026-08-19 produces a new video row that
keeps the original createdAt, so the duplicate is also permanently quota-exempt. A free-tier
user can duplicate one old cap as many times as they like and get unlimited uncounted
shareable links.
Consequence 2: access-control regression
Duplicating a public: true + password-protected cap copies public: true forward but drops
the password (comes out NULL). The result is a public cap with no password, silently
created by a completely ordinary "duplicate" action no attacker or malicious intent required.
Consequence 3 (minor): dashboard sort
Duplicates keep the original's createdAt, so if the dashboard orders by creation date they
sort next to the original instead of appearing at the top, effectively burying themselves.
Existing work
None found. The quota feature landed via PR #2138 ("Shareable link limits", merged
2026-08-20) and never touched Videos/index.ts/duplicate(). No issue or PR references this
interaction, and there's no test coverage for Videos.duplicate()'s output fields at all.
Fix
- In
repo.create, build the insert payload by enumerating the columns that should carry over
from the source video, instead of spreading the wholeVideo.Videoinstance. Never let
id/createdAt/updatedAtreach the insert from the source object. - Decide deliberately whether a duplicate should inherit password protection; if not, force
public: falseon duplicates of password-protected videos (or carry the password forward
explicitly but silently dropping it while keepingpublic: trueis the wrong default
either way).
- Lingua principale
- Rust
- Stelle
- 22.5k
- Fork
- 1.9k
- Merge medio
- 7h 43m
- PR unite (30g)
- 80
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di CapSoftware/Cap
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
CapSoftware/Cap#2305 · 2 commenti ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
CapSoftware/Cap#1714 · 3 commenti ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 45/100
CapSoftware/Cap#2340 · 1 commento ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 45/100
CapSoftware/Cap#2339 · 1 commento ·
-
Difficoltà 3/5 1-2 giorni Idoneità per principianti 58/100
CapSoftware/Cap#2329 · 2 commenti ·
Tutte le issue di CapSoftware/Cap
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
TheLarkInn/aipm#2413 ·
-
documentation
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 90/100
alexgorbatchev/simple-ptt#15 ·
-
tooling
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
-
todo:ticket
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
taikoxyz/taiko-mono#22168 · 1 commento ·