The app's primary attack surface is attacker-controlled URLs reaching a native
curl engine and the yt-dlp runtime. URLs arrive via the exported MainActivity
(ACTION_VIEW/ACTION_SEND for http(s)), the clipboard, and the Add dialog.
Secondary surface: archive.org credentials at rest, and untrusted HTTP responses
parsed by the native extractors.
- TLS verification is never weakened.
CURLOPT_SSL_VERIFYPEER/VERIFYHOSTare left at libcurl's secure defaults everywhere; trust is pinned to a shippedcacert.pemviaCURLOPT_CAINFO(dlm_ca_bundle). The Android network-security-config (usesCleartextTraffic=false) governs only the Java/OkHttp stack — not native curl — so the native posture is enforced in C. - Protocol allow-list. Every curl handle restricts protocols:
- downloads (
download.c):CURLOPT_PROTOCOLS_STR = "http,https,ftp,ftps"; - extractor/auth (
httpget.c):"http,https". This blocksfile://(local-file disclosure),gopher:///dict:///scp:///smb://(protocol smuggling), and SSRF-via-redirect to non-network schemes.
- downloads (
- No credential downgrade on redirect. Requests carrying
Authorization:/Cookie:headers setCURLOPT_REDIR_PROTOCOLS_STR = "https", so a redirect can't forward the archive.org S3 key / session cookie over cleartext. (libcurl resends custom headers across redirects;CURLOPT_UNRESTRICTED_AUTHdoes not apply to them.) - Residual risk: a redirect to a different HTTPS host still receives the
custom auth headers. In practice archive.org redirects stay in-domain; full
cross-host stripping would require disabling
FOLLOWLOCATIONand re-issuing auth per verified host. Documented, not yet implemented.
isSafeDownloadInput(UI + repository boundary) rejects option-injection (leading-) and non-network schemes (file:/content:/intent:/javascript:/data:/…). Unit-tested inSiteGroupingTest.- yt-dlp argument injection is neutralized at the source: both request
builders append
--(end-of-options) before the URL, so a--leading URL can never become a flag (--exec,--config-location, …). The boundary check is belt-and-suspenders. - Path traversal is defended in two layers: native filename sanitizers
(
sanitize_filenameover both title and ext inytdlp.c;dlm_filename_from_urlneutralizes separators + leading./-) and the KotlinconfineToDownloadDirguard inresolveOut(canonicalize; collapse escapes to a leaf name inside the download dir).
- archive.org credentials (S3 keys / login cookie) are stored as plaintext
JSON at
<app-config>/credentials, mode0600, written viaO_CREAT|O_EXCL|O_WRONLY|O_NOFOLLOW+ atomicrename(symlink/TOCTOU-hardened), temp unlinked on failure. There is no at-rest encryption layer — protection is app-private storage permissions only (normal on non-rooted devices). - Freed secrets are wiped with
OPENSSL_cleanse(resists dead-store elimination). allowBackup="false"keeps the credentials file out ofadb backup/ cloud auto-backup. Do not re-enable backup or adddataExtractionRulescoveringfilesDirwithout first encrypting the file.- Future hardening: wrap the file with Android Keystore /
EncryptedFile. This needs a redesign (Kotlin would decrypt and pass secrets to native at runtime, since native currently reads the file directly). The deadandroidx.security:security-cryptodependency was removed to avoid implying encryption that doesn't exist.
No secrets are logged. URLs are reduced to host-only (redactUrl); yt-dlp failure
logs include only the host + exception class, never the raw exception (whose
message embeds the full command line and query tokens).
In-memory HTTP bodies are capped (httpget 64 MiB; yt-dlp capture 256 MiB; task
arrays clamped). Responses are not auto-decompressed (no ACCEPT_ENCODING), so
there is no gzip-bomb amplification; downloads stream to disk via pwrite.
MainActivityis the only exported component; it parses intent extras only as a URL string (no nested-intent redirection). TheVIEWfilter matches allhttp(s)links — incoming URLs are treated as untrusted and validated.DownloadServiceisexported="false",dataSync, started only via an internal explicit intent.PendingIntents useFLAG_IMMUTABLEwith an explicit target.
dlmcore is built with -fstack-protector-strong, _FORTIFY_SOURCE=2 (release),
and relro/now/noexecstack link flags. SQLite is built with a reduced option
set. See Build.md.
- Cross-host (same-scheme) auth-header forwarding on redirect (see above).
- A third-party app can
ACTION_VIEW-launch the activity and, with clipboard monitoring on, trigger a network resolve of clipboard contents (stages only; no auto-download without global autostart). - archive.org metadata cookie values aren't CR/LF-checked (trusted TLS source).
- Generic-file fallback can save a non-media HTML page if a user forces it; gated on yt-dlp being READY to avoid the common accidental case.