Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion src/download/engine.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import WebTorrent, { type Torrent } from "webtorrent";
import parseTorrent from "parse-torrent";

export interface TorrentProgress {
progress: number;
Expand Down Expand Up @@ -54,7 +55,8 @@

// `source` is a magnet URI, an infoHash, or a path to a .torrent file. Seeding
// an existing file passes the stored .torrent path so webtorrent can verify it
// locally instead of re-fetching metadata from the swarm.
// locally instead of re-fetching metadata from the swarm (which a bare magnet
// would require).
// `announce` supplements whatever trackers are already in the source URI;
// webtorrent dedupes internally.
add(
Expand Down Expand Up @@ -83,7 +85,39 @@
}
this.torrents.set(id, torrent);

// Guard against the webtorrent bug in Torrent._onTorrentId (webtorrent
// ≤2.4.x): _onTorrentId is async and calls arr2hex(parsedTorrent.infoHash)
// without checking if infoHash is defined. When parseTorrent resolves to a
// truthy object whose infoHash is undefined (e.g. malformed magnet from a
// corrupted queue file), arr2hex(undefined) throws a TypeError *inside* the
// async function, producing an unhandled promise rejection that crashes the
// process via Node's default unhandledRejection handler.
//
// We pre-validate the infoHash asynchronously: if parseTorrent cannot
// resolve it, we destroy the torrent and invoke onError before webtorrent
// can blow up. This runs concurrently with webtorrent's own _onTorrentId
// call but safely races it via the torrent.destroyed flag and the error
// listeners already attached below.
const isTorrentFilePath = !source.startsWith("magnet:") && source.toLowerCase().endsWith(".torrent");
if (!isTorrentFilePath) {
void Promise.resolve().then(() => parseTorrent(source))
if (torrent.destroyed) return;
if (!parsed?.infoHash) {
const err = `Invalid torrent source: infoHash could not be resolved (source: ${source.slice(0, 80)})`;
handlers.onError?.(err);
this.torrents.delete(id);
try { torrent.destroy(); } catch {}
}
})

Check failure on line 111 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on ubuntu-latest

Declaration or statement expected.

Check failure on line 111 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on ubuntu-latest

Declaration or statement expected.

Check failure on line 111 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on macos-latest

Declaration or statement expected.

Check failure on line 111 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on macos-latest

Declaration or statement expected.

Check failure on line 111 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on windows-latest

Declaration or statement expected.

Check failure on line 111 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on windows-latest

Declaration or statement expected.
.catch((e: unknown) => {

Check failure on line 112 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on ubuntu-latest

Identifier expected.

Check failure on line 112 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on ubuntu-latest

'try' expected.

Check failure on line 112 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on ubuntu-latest

Declaration or statement expected.

Check failure on line 112 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on ubuntu-latest

Identifier expected.

Check failure on line 112 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on ubuntu-latest

'try' expected.

Check failure on line 112 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on ubuntu-latest

Declaration or statement expected.

Check failure on line 112 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on macos-latest

Identifier expected.

Check failure on line 112 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on macos-latest

'try' expected.

Check failure on line 112 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on macos-latest

Declaration or statement expected.

Check failure on line 112 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on macos-latest

Identifier expected.

Check failure on line 112 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on macos-latest

'try' expected.

Check failure on line 112 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on macos-latest

Declaration or statement expected.

Check failure on line 112 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on windows-latest

Identifier expected.

Check failure on line 112 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on windows-latest

'try' expected.

Check failure on line 112 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on windows-latest

Declaration or statement expected.

Check failure on line 112 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on windows-latest

Identifier expected.

Check failure on line 112 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on windows-latest

'try' expected.

Check failure on line 112 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on windows-latest

Declaration or statement expected.
if (torrent.destroyed) return;
handlers.onError?.(message(e));
this.torrents.delete(id);
try { torrent.destroy(); } catch {}
});

Check failure on line 117 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on ubuntu-latest

';' expected.

Check failure on line 117 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on ubuntu-latest

';' expected.

Check failure on line 117 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on macos-latest

';' expected.

Check failure on line 117 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on macos-latest

';' expected.

Check failure on line 117 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on windows-latest

';' expected.

Check failure on line 117 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on windows-latest

';' expected.
}

torrent.on("metadata", () => {

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on ubuntu-latest

Parameter declaration expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on ubuntu-latest

Parameter declaration expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on ubuntu-latest

Identifier expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on ubuntu-latest

Unexpected token. A constructor, method, accessor, or property was expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on ubuntu-latest

Unexpected keyword or identifier.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on ubuntu-latest

Parameter declaration expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on ubuntu-latest

Parameter declaration expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on ubuntu-latest

Identifier expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on ubuntu-latest

Unexpected token. A constructor, method, accessor, or property was expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on ubuntu-latest

Unexpected keyword or identifier.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on macos-latest

Parameter declaration expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on macos-latest

Parameter declaration expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on macos-latest

Identifier expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on macos-latest

Unexpected token. A constructor, method, accessor, or property was expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on macos-latest

Unexpected keyword or identifier.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on macos-latest

Parameter declaration expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on macos-latest

Parameter declaration expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on macos-latest

Identifier expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on macos-latest

Unexpected token. A constructor, method, accessor, or property was expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on macos-latest

Unexpected keyword or identifier.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on windows-latest

Parameter declaration expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on windows-latest

Parameter declaration expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on windows-latest

Identifier expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on windows-latest

Unexpected token. A constructor, method, accessor, or property was expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 24 on windows-latest

Unexpected keyword or identifier.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on windows-latest

Parameter declaration expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on windows-latest

Parameter declaration expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on windows-latest

Identifier expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on windows-latest

Unexpected token. A constructor, method, accessor, or property was expected.

Check failure on line 120 in src/download/engine.ts

View workflow job for this annotation

GitHub Actions / node 22 on windows-latest

Unexpected keyword or identifier.
handlers.onMetadata?.({
name: torrent.name,
total: torrent.length,
Expand Down
6 changes: 5 additions & 1 deletion src/parse-torrent.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
declare module "parse-torrent" {
interface ParsedTorrent {
infoHash: string;
// infoHash can be undefined in practice when parseTorrent resolves a truthy
// object but cannot derive a hash from the input (e.g. malformed magnet).
// Webtorrent's _onTorrentId checks `if (parsedTorrent)` but then calls
// arr2hex(parsedTorrent.infoHash) without guarding for undefined, crashing.
infoHash?: string;
name?: string;
}
export default function parseTorrent(
Expand Down
1 change: 1 addition & 0 deletions src/webtorrent.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ declare module "webtorrent" {
magnetURI: string;
torrentFile: Uint8Array;
ready: boolean;
destroyed: boolean;
name: string;
length: number;
downloaded: number;
Expand Down