Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

Videos.duplicate() copies createdAt and public, drops password → shareable-link quota bypass and public exposure of password-protected caps

Abierto
#2,223 2 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
4/5
Tiempo estimado
3-5 días
Aptitud para principiantes
58/100
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Activo
Stack tecnológico
mysql, typescript

Línea de trabajo

Comienza en packages/web-backend/src/Videos/index.ts y sigue duplicate() hasta create() en VideosRepo.ts; inspecciona Video.ts y las columnas de vídeo en packages/database/schema.ts. Determina el comportamiento previsto de password/public para los duplicados y verifica después que los identificadores de origen y las marcas de tiempo no lleguen al insert, que la protección no se debilite silenciosamente y que las pruebas de salida de duplicados cubran la regresión.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

bug
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 whole Video.Video instance. Never let
    id/createdAt/updatedAt reach the insert from the source object.
  • Decide deliberately whether a duplicate should inherit password protection; if not, force
    public: false on duplicates of password-protected videos (or carry the password forward
    explicitly but silently dropping it while keeping public: true is the wrong default
    either way).
Lenguaje dominante
Rust
Estrellas
22.8k
Forks
2k
Merge medio
10 h 40 min
PR fusionados (30 d)
89

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de CapSoftware/Cap

Todos los issues de CapSoftware/Cap

Issues similares

Más issues de Rust

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.