mirror of
https://github.com/iv-org/invidious.git
synced 2026-09-18 20:13:46 +00:00
feat: sanity check video IDs with regex (#6037)
* feat: sanity check video IDs with regex * Return 400 instead * fix wrong function * add coderabbit suggestion about PCRE2 regex and length of the string with a new line * Use validate_video_id on more places * Add video id validation to /watch route.
This commit is contained in:
@@ -38,3 +38,15 @@ end
|
|||||||
# some important informations, and that the query should be sent again.
|
# some important informations, and that the query should be sent again.
|
||||||
class RetryOnceException < Exception
|
class RetryOnceException < Exception
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Exception for invalid video IDs.
|
||||||
|
class InvalidVideoID < InfoException
|
||||||
|
getter id : String?
|
||||||
|
|
||||||
|
def initialize(@id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def message
|
||||||
|
return "Invalid video ID '#{id}'"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|||||||
@@ -404,3 +404,10 @@ def invidious_companion_encrypt(data)
|
|||||||
encrypted_data = encrypt_ecb_without_salt("#{timestamp}|#{data}", CONFIG.invidious_companion_key)
|
encrypted_data = encrypt_ecb_without_salt("#{timestamp}|#{data}", CONFIG.invidious_companion_key)
|
||||||
return Base64.urlsafe_encode(encrypted_data)
|
return Base64.urlsafe_encode(encrypted_data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def validate_video_id(id : String) : Bool
|
||||||
|
# This is the video ID regex. May be need to be changed
|
||||||
|
# if Youtube ever decides to add more characters to their
|
||||||
|
# video IDs.
|
||||||
|
/\A[a-zA-Z0-9_-]{11}\z/.matches?(id)
|
||||||
|
end
|
||||||
|
|||||||
@@ -81,9 +81,10 @@ module Invidious::Routes::API::V1::Authenticated
|
|||||||
return error_json(409, "Watch history is disabled in preferences.")
|
return error_json(409, "Watch history is disabled in preferences.")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Sanity checks
|
||||||
id = env.params.url["id"]
|
id = env.params.url["id"]
|
||||||
if !id.match(/^[a-zA-Z0-9_-]{11}$/)
|
unless validate_video_id(id)
|
||||||
return error_json(400, "Invalid video id.")
|
return error_json(400, InvalidVideoID.new(id))
|
||||||
end
|
end
|
||||||
|
|
||||||
Invidious::Database::Users.mark_watched(user, id)
|
Invidious::Database::Users.mark_watched(user, id)
|
||||||
@@ -97,12 +98,12 @@ module Invidious::Routes::API::V1::Authenticated
|
|||||||
return error_json(409, "Watch history is disabled in preferences.")
|
return error_json(409, "Watch history is disabled in preferences.")
|
||||||
end
|
end
|
||||||
|
|
||||||
id = env.params.url["id"]
|
video_id = env.params.url["id"]
|
||||||
if !id.match(/^[a-zA-Z0-9_-]{11}$/)
|
unless video_id && validate_video_id(video_id)
|
||||||
return error_json(400, "Invalid video id.")
|
return error_json(400, InvalidVideoID.new(video_id))
|
||||||
end
|
end
|
||||||
|
|
||||||
Invidious::Database::Users.mark_unwatched(user, id)
|
Invidious::Database::Users.mark_unwatched(user, video_id)
|
||||||
env.response.status_code = 204
|
env.response.status_code = 204
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -309,8 +310,9 @@ module Invidious::Routes::API::V1::Authenticated
|
|||||||
end
|
end
|
||||||
|
|
||||||
video_id = env.params.json["videoId"].try &.as(String)
|
video_id = env.params.json["videoId"].try &.as(String)
|
||||||
if !video_id
|
# Sanity checks
|
||||||
return error_json(403, "Invalid videoId")
|
unless video_id && validate_video_id(video_id)
|
||||||
|
return error_json(400, InvalidVideoID.new(video_id))
|
||||||
end
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
|||||||
@@ -32,8 +32,9 @@ module Invidious::Routes::API::V1::Videos
|
|||||||
id = env.params.url["id"]
|
id = env.params.url["id"]
|
||||||
region = env.params.query["region"]? || env.params.body["region"]?
|
region = env.params.query["region"]? || env.params.body["region"]?
|
||||||
|
|
||||||
if id.nil? || id.size != 11 || !id.matches?(/^[\w-]+$/)
|
# Sanity checks
|
||||||
return error_json(400, "Invalid video ID")
|
unless validate_video_id(id)
|
||||||
|
return error_json(400, InvalidVideoID.new(id))
|
||||||
end
|
end
|
||||||
|
|
||||||
# See https://github.com/ytdl-org/youtube-dl/blob/6ab30ff50bf6bd0585927cb73c7421bef184f87a/youtube_dl/extractor/youtube.py#L1354
|
# See https://github.com/ytdl-org/youtube-dl/blob/6ab30ff50bf6bd0585927cb73c7421bef184f87a/youtube_dl/extractor/youtube.py#L1354
|
||||||
@@ -256,11 +257,11 @@ module Invidious::Routes::API::V1::Videos
|
|||||||
def self.annotations(env)
|
def self.annotations(env)
|
||||||
env.response.content_type = "text/xml"
|
env.response.content_type = "text/xml"
|
||||||
|
|
||||||
id = env.params.url["id"]
|
video_id = env.params.url["id"]
|
||||||
source = env.params.query["source"]?
|
source = env.params.query["source"]?
|
||||||
source ||= "archive"
|
source ||= "archive"
|
||||||
|
|
||||||
if !id.match(/[a-zA-Z0-9_-]{11}/)
|
unless video_id && validate_video_id(video_id)
|
||||||
haltf env, 400
|
haltf env, 400
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -268,21 +269,21 @@ module Invidious::Routes::API::V1::Videos
|
|||||||
|
|
||||||
case source
|
case source
|
||||||
when "archive"
|
when "archive"
|
||||||
if CONFIG.cache_annotations && (cached_annotation = Invidious::Database::Annotations.select(id))
|
if CONFIG.cache_annotations && (cached_annotation = Invidious::Database::Annotations.select(video_id))
|
||||||
annotations = cached_annotation.annotations
|
annotations = cached_annotation.annotations
|
||||||
else
|
else
|
||||||
index = CHARS_SAFE.index!(id[0]).to_s.rjust(2, '0')
|
index = CHARS_SAFE.index!(video_id[0]).to_s.rjust(2, '0')
|
||||||
|
|
||||||
# IA doesn't handle leading hyphens,
|
# IA doesn't handle leading hyphens,
|
||||||
# so we use https://archive.org/details/youtubeannotations_64
|
# so we use https://archive.org/details/youtubeannotations_64
|
||||||
if index == "62"
|
if index == "62"
|
||||||
index = "64"
|
index = "64"
|
||||||
id = id.sub(/^-/, 'A')
|
video_id = video_id.sub(/^-/, 'A')
|
||||||
end
|
end
|
||||||
|
|
||||||
file = URI.encode_www_form("#{id[0, 3]}/#{id}.xml")
|
file = URI.encode_www_form("#{video_id[0, 3]}/#{video_id}.xml")
|
||||||
|
|
||||||
location = make_client(INTERNET_ARCHIVE_URL, &.get("/download/youtubeannotations_#{index}/#{id[0, 2]}.tar/#{file}"))
|
location = make_client(INTERNET_ARCHIVE_URL, &.get("/download/youtubeannotations_#{index}/#{video_id[0, 2]}.tar/#{file}"))
|
||||||
|
|
||||||
if !location.headers["Location"]?
|
if !location.headers["Location"]?
|
||||||
env.response.status_code = location.status_code
|
env.response.status_code = location.status_code
|
||||||
@@ -300,10 +301,10 @@ module Invidious::Routes::API::V1::Videos
|
|||||||
|
|
||||||
annotations = response.body
|
annotations = response.body
|
||||||
|
|
||||||
Helpers.cache_annotation(id, annotations)
|
Helpers.cache_annotation(video_id, annotations)
|
||||||
end
|
end
|
||||||
else # "youtube"
|
else # "youtube"
|
||||||
response = YT_POOL.client &.get("/annotations_invideo?video_id=#{id}")
|
response = YT_POOL.client &.get("/annotations_invideo?video_id=#{video_id}")
|
||||||
|
|
||||||
if response.status_code != 200
|
if response.status_code != 200
|
||||||
haltf env, response.status_code
|
haltf env, response.status_code
|
||||||
|
|||||||
@@ -268,8 +268,8 @@ module Invidious::Routes::VideoPlayback
|
|||||||
itag = env.params.query["itag"]?.try &.to_i?
|
itag = env.params.query["itag"]?.try &.to_i?
|
||||||
|
|
||||||
# Sanity checks
|
# Sanity checks
|
||||||
if id.nil? || id.size != 11 || !id.matches?(/^[\w-]+$/)
|
unless id && validate_video_id(id)
|
||||||
return error_template(400, "Invalid video ID")
|
return error_template(400, InvalidVideoID.new(id))
|
||||||
end
|
end
|
||||||
|
|
||||||
if !itag.nil? && (itag <= 0 || itag >= 1000)
|
if !itag.nil? && (itag <= 0 || itag >= 1000)
|
||||||
|
|||||||
@@ -18,14 +18,8 @@ module Invidious::Routes::Watch
|
|||||||
return error_template(400, "Invalid parameters.")
|
return error_template(400, "Invalid parameters.")
|
||||||
end
|
end
|
||||||
|
|
||||||
if id.size > 11
|
unless validate_video_id(id)
|
||||||
url = "/watch?v=#{id[0, 11]}"
|
return error_template(400, InvalidVideoID.new(id))
|
||||||
env.params.query.delete_all("v")
|
|
||||||
if env.params.query.size > 0
|
|
||||||
url += "&#{env.params.query}"
|
|
||||||
end
|
|
||||||
|
|
||||||
return env.redirect url
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return env.redirect "/"
|
return env.redirect "/"
|
||||||
@@ -235,7 +229,7 @@ module Invidious::Routes::Watch
|
|||||||
token = env.params.body["csrf_token"]?
|
token = env.params.body["csrf_token"]?
|
||||||
|
|
||||||
id = env.params.query["id"]?
|
id = env.params.query["id"]?
|
||||||
if !id
|
unless id && validate_video_id(id)
|
||||||
env.response.status_code = 400
|
env.response.status_code = 400
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user