add option to disable mixed content security, workaround for ajax restrictions

This commit is contained in:
ccd0 2014-08-03 19:53:01 -07:00
parent e70cdc620a
commit 790a62d1bd
5 changed files with 96 additions and 56 deletions

View File

@ -54,8 +54,8 @@ Redirect =
# For fuuka-based archives: # For fuuka-based archives:
# https://github.com/eksopl/fuuka/issues/27 # https://github.com/eksopl/fuuka/issues/27
protocol = Redirect.protocol archive protocol = Redirect.protocol archive
return '' unless protocol is 'https://' or location.protocol is 'http:'
URL = new String "#{protocol}#{archive.domain}/_/api/chan/post/?board=#{boardID}&num=#{postID}" URL = new String "#{protocol}#{archive.domain}/_/api/chan/post/?board=#{boardID}&num=#{postID}"
return '' unless Redirect.securityCheck URL
URL.archive = archive URL.archive = archive
URL URL
@ -76,10 +76,14 @@ Redirect =
"#{boardID}/?task=search2&search_#{if type is 'image' then 'media_hash' else type}=#{value}" "#{boardID}/?task=search2&search_#{if type is 'image' then 'media_hash' else type}=#{value}"
"#{Redirect.protocol archive}#{archive.domain}/#{path}" "#{Redirect.protocol archive}#{archive.domain}/#{path}"
securityCheck: (URL) ->
/^https:\/\//.test(URL) or
location.protocol is 'http:' or
Conf['Allow Mixed Content from Archives']
navigate: (URL, alternative) -> navigate: (URL, alternative) ->
if URL and ( if URL and (
/^https:\/\//.test(URL) or Redirect.securityCheck(URL) or
location.protocol is 'http:' or
confirm "Redirect to #{URL}?\n\nYour connection will not be encrypted." confirm "Redirect to #{URL}?\n\nYour connection will not be encrypted."
) )
location.replace URL location.replace URL

View File

@ -29,6 +29,10 @@ Config =
true true
'Redirect dead threads and images.' 'Redirect dead threads and images.'
] ]
'Allow Mixed Content from Archives': [
false
'Permit warningless access to HTTP-only archives from HTTPS pages.'
]
'Keybinds': [ 'Keybinds': [
true true
'Bind actions to keyboard shortcuts.' 'Bind actions to keyboard shortcuts.'

View File

@ -1,46 +1,73 @@
CrossOrigin = do -> CrossOrigin =
file: do ->
makeBlob = (urlBlob, contentType, contentDisposition, url) ->
name = url.match(/([^\/]+)\/*$/)?[1]
mime = contentType?.match(/[^;]*/)[0] or 'application/octet-stream'
match =
contentDisposition?.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)?[1] or
contentType?.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)?[1]
if match
name = match.replace /\\"/g, '"'
blob = new Blob([urlBlob], {type: mime})
blob.name = name
blob
makeBlob = (urlBlob, contentType, contentDisposition, url) -> (url, cb) ->
name = url.match(/([^\/]+)\/*$/)?[1] <% if (type === 'crx') { %>
mime = contentType?.match(/[^;]*/)[0] or 'application/octet-stream' $.ajax url,
match = responseType: 'blob'
contentDisposition?.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)?[1] or onload: ->
contentType?.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)?[1] return cb null unless @readyState is @DONE and @status is 200
if match contentType = @getResponseHeader 'Content-Type'
name = match.replace /\\"/g, '"' contentDisposition = @getResponseHeader 'Content-Disposition'
blob = new Blob([urlBlob], {type: mime}) cb (makeBlob @response, contentType, contentDisposition, url)
blob.name = name onerror: ->
blob cb null
<% } %>
<% if (type === 'userscript') { %>
GM_xmlhttpRequest
method: "GET"
url: url
overrideMimeType: "text/plain; charset=x-user-defined"
onload: (xhr) ->
r = xhr.responseText
data = new Uint8Array r.length
i = 0
while i < r.length
data[i] = r.charCodeAt i
i++
contentType = xhr.responseHeaders.match(/Content-Type:\s*(.*)/i)?[1]
contentDisposition = xhr.responseHeaders.match(/Content-Disposition:\s*(.*)/i)?[1]
cb (makeBlob data, contentType, contentDisposition, url)
onerror: ->
cb null
<% } %>
file = (url, cb) -> json: do ->
<% if (type === 'crx') { %> callbacks = {}
$.ajax url, responses = {}
responseType: 'blob' (url, cb) ->
onload: -> <% if (type === 'crx') { %>
return cb null unless @readyState is @DONE and @status is 200 $.cache url, (-> cb @response), responseType: 'json'
contentType = @getResponseHeader 'Content-Type' <% } %>
contentDisposition = @getResponseHeader 'Content-Disposition' <% if (type === 'userscript') { %>
cb (makeBlob @response, contentType, contentDisposition, url) if responses[url]
onerror: -> cb responses[url]
cb null return
<% } %> if callbacks[url]
<% if (type === 'userscript') { %> callbacks[url].push cb
GM_xmlhttpRequest return
method: "GET" callbacks[url] = [cb]
url: url GM_xmlhttpRequest
overrideMimeType: "text/plain; charset=x-user-defined" method: "GET"
onload: (xhr) -> url: url
r = xhr.responseText onload: (xhr) ->
data = new Uint8Array r.length response = JSON.parse xhr.responseText
i = 0 cb response for cb in callbacks[url]
while i < r.length delete callbacks[url]
data[i] = r.charCodeAt i responses[url] = response
i++ onerror: ->
contentType = xhr.responseHeaders.match(/Content-Type:\s*(.*)/i)?[1] delete callbacks[url]
contentDisposition = xhr.responseHeaders.match(/Content-Disposition:\s*(.*)/i)?[1] onabort: ->
cb (makeBlob data, contentType, contentDisposition, url) delete callbacks[url]
onerror: -> <% } %>
cb null
<% } %>
{file}

View File

@ -132,20 +132,25 @@ Get =
Get.insert post, root, context Get.insert post, root, context
archivedPost: (boardID, postID, root, context) -> archivedPost: (boardID, postID, root, context) ->
return false unless url = Redirect.to 'post', {boardID, postID} return false unless url = Redirect.to 'post', {boardID, postID}
$.cache url, if /^https:\/\//.test(URL) or location.protocol is 'http:'
-> Get.parseArchivedPost @, boardID, postID, root, context $.cache url,
, -> Get.parseArchivedPost @response, boardID, postID, root, context
responseType: 'json' ,
withCredentials: url.archive.withCredentials responseType: 'json'
return true withCredentials: url.archive.withCredentials
parseArchivedPost: (req, boardID, postID, root, context) -> return true
else if Conf['Allow Mixed Content from Archives']
CrossOrigin.json url, (response) ->
Get.parseArchivedPost response, boardID, postID, root, context
return true
return false
parseArchivedPost: (data, boardID, postID, root, context) ->
# In case of multiple callbacks for the same request, # In case of multiple callbacks for the same request,
# don't parse the same original post more than once. # don't parse the same original post more than once.
if post = g.posts["#{boardID}.#{postID}"] if post = g.posts["#{boardID}.#{postID}"]
Get.insert post, root, context Get.insert post, root, context
return return
data = req.response
if data.error if data.error
$.addClass root, 'warning' $.addClass root, 'warning'
root.textContent = data.error root.textContent = data.error

View File

@ -12,7 +12,7 @@ ImageCommon =
URL = Redirect.to 'file', URL = Redirect.to 'file',
boardID: post.board.ID boardID: post.board.ID
filename: src[src.length - 1] filename: src[src.length - 1]
unless URL and (/^https:\/\//.test(URL) or location.protocol is 'http:') unless URL and Redirect.securityCheck URL
URL = null URL = null
return cb URL if (post.isDead or post.file.isDead) and file.src.split('/')[2] is 'i.4cdn.org' return cb URL if (post.isDead or post.file.isDead) and file.src.split('/')[2] is 'i.4cdn.org'