better safe handling?

This commit is contained in:
cranci1 2025-07-01 17:56:00 +02:00
parent ad323efe34
commit 1b6c62a204
3 changed files with 95 additions and 54 deletions

View file

@ -86,18 +86,48 @@ extension JSContext {
} }
var headers: [String: String]? = nil var headers: [String: String]? = nil
if let headersDict = headersAny as? [String: Any] {
if let headersAny = headersAny {
if headersAny is NSNull {
headers = nil
} else if let headersDict = headersAny as? [String: Any] {
var safeHeaders: [String: String] = [:] var safeHeaders: [String: String] = [:]
for (key, value) in headersDict { for (key, value) in headersDict {
if let valueStr = value as? String { let stringValue: String
safeHeaders[key] = valueStr if let str = value as? String {
stringValue = str
} else if let num = value as? NSNumber {
stringValue = num.stringValue
} else if value is NSNull {
continue
} else { } else {
Logger.shared.log("Header value is not a String: \(key): \(value)", type: "Error") stringValue = String(describing: value)
} }
safeHeaders[key] = stringValue
}
headers = safeHeaders.isEmpty ? nil : safeHeaders
} else if let headersDict = headersAny as? [AnyHashable: Any] {
var safeHeaders: [String: String] = [:]
for (key, value) in headersDict {
let stringKey = String(describing: key)
let stringValue: String
if let str = value as? String {
stringValue = str
} else if let num = value as? NSNumber {
stringValue = num.stringValue
} else if value is NSNull {
continue
} else {
stringValue = String(describing: value)
}
safeHeaders[stringKey] = stringValue
}
headers = safeHeaders.isEmpty ? nil : safeHeaders
} else {
Logger.shared.log("Headers argument is not a dictionary, type: \(type(of: headersAny))", type: "Warning")
headers = nil
} }
headers = safeHeaders
} else if headersAny != nil {
Logger.shared.log("Headers argument is not a dictionary", type: "Error")
} }
let httpMethod = method ?? "GET" let httpMethod = method ?? "GET"
@ -132,7 +162,9 @@ extension JSContext {
let textEncoding = getEncoding(from: encoding) let textEncoding = getEncoding(from: encoding)
if httpMethod == "GET", let body = body, !body.isEmpty, body != "null", body != "undefined" { let bodyIsEmpty = body == nil || (body)?.isEmpty == true || body == "null" || body == "undefined"
if httpMethod == "GET" && !bodyIsEmpty {
Logger.shared.log("GET request must not have a body", type: "Error") Logger.shared.log("GET request must not have a body", type: "Error")
DispatchQueue.main.async { DispatchQueue.main.async {
reject.call(withArguments: ["GET request must not have a body"]) reject.call(withArguments: ["GET request must not have a body"])
@ -140,8 +172,13 @@ extension JSContext {
return return
} }
if httpMethod != "GET", let body = body, !body.isEmpty, body != "null", body != "undefined" { if httpMethod != "GET" && !bodyIsEmpty {
request.httpBody = body.data(using: .utf8) if let bodyString = body {
request.httpBody = bodyString.data(using: .utf8)
} else {
let bodyString = String(describing: body!)
request.httpBody = bodyString.data(using: .utf8)
}
} }
if let headers = headers { if let headers = headers {
@ -149,7 +186,8 @@ extension JSContext {
request.setValue(value, forHTTPHeaderField: key) request.setValue(value, forHTTPHeaderField: key)
} }
} }
Logger.shared.log("Redirect value is \(redirect.boolValue)", type: "Error")
Logger.shared.log("Redirect value is \(redirect.boolValue)", type: "Debug")
let session = URLSession.fetchData(allowRedirects: redirect.boolValue) let session = URLSession.fetchData(allowRedirects: redirect.boolValue)
let task = session.downloadTask(with: request) { tempFileURL, response, error in let task = session.downloadTask(with: request) { tempFileURL, response, error in
@ -181,8 +219,13 @@ extension JSContext {
var safeHeaders: [String: String] = [:] var safeHeaders: [String: String] = [:]
if let httpResponse = response as? HTTPURLResponse { if let httpResponse = response as? HTTPURLResponse {
for (key, value) in httpResponse.allHeaderFields { for (key, value) in httpResponse.allHeaderFields {
if let keyString = key as? String, if let keyString = key as? String {
let valueString = value as? String { let valueString: String
if let str = value as? String {
valueString = str
} else {
valueString = String(describing: value)
}
safeHeaders[keyString] = valueString safeHeaders[keyString] = valueString
} }
} }
@ -225,23 +268,26 @@ extension JSContext {
task.resume() task.resume()
} }
self.setObject(fetchV2NativeFunction, forKeyedSubscript: "fetchV2Native" as NSString) self.setObject(fetchV2NativeFunction, forKeyedSubscript: "fetchV2Native" as NSString)
let fetchv2Definition = """ let fetchv2Definition = """
function fetchv2(url, headers = {}, method = "GET", body = null, redirect = true, encoding ) { function fetchv2(url, headers = {}, method = "GET", body = null, redirect = true, encoding) {
var processedBody = null; var processedBody = null;
if(method != "GET") if(method != "GET") {
{
processedBody = (body && (typeof body === 'object')) ? JSON.stringify(body) : (body || null) processedBody = (body && (typeof body === 'object')) ? JSON.stringify(body) : (body || null)
} }
var finalEncoding = encoding || "utf-8"; var finalEncoding = encoding || "utf-8";
// Ensure headers is an object and not null/undefined
var processedHeaders = {};
if (headers && typeof headers === 'object' && !Array.isArray(headers)) {
processedHeaders = headers;
}
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
fetchV2Native(url, headers, method, processedBody, redirect, finalEncoding, function(rawText) { fetchV2Native(url, processedHeaders, method, processedBody, redirect, finalEncoding, function(rawText) {
const responseObj = { const responseObj = {
headers: rawText.headers, headers: rawText.headers,
status: rawText.status, status: rawText.status,
@ -261,7 +307,6 @@ extension JSContext {
}, reject); }, reject);
}); });
} }
""" """
self.evaluateScript(fetchv2Definition) self.evaluateScript(fetchv2Definition)
} }

View file

@ -336,13 +336,11 @@ struct SettingsViewGeneral: View {
.listStyle(.plain) .listStyle(.plain)
.frame(height: CGFloat(metadataProvidersOrder.count * 65)) .frame(height: CGFloat(metadataProvidersOrder.count * 65))
.background(Color.clear) .background(Color.clear)
.padding(.bottom, 8)
Text(NSLocalizedString("Drag to reorder", comment: "")) Text(NSLocalizedString("Drag to reorder", comment: ""))
.font(.caption) .font(.caption)
.foregroundStyle(.gray) .foregroundStyle(.gray)
.frame(maxWidth: .infinity, alignment: .center) .frame(maxWidth: .infinity, alignment: .center)
.padding(.bottom, 8)
} }
.environment(\.editMode, .constant(.active)) .environment(\.editMode, .constant(.active))
} }
@ -446,13 +444,11 @@ struct SettingsViewGeneral: View {
.listStyle(.plain) .listStyle(.plain)
.frame(height: CGFloat(librarySectionsOrder.count * 70)) .frame(height: CGFloat(librarySectionsOrder.count * 70))
.background(Color.clear) .background(Color.clear)
.padding(.bottom, 8)
Text(NSLocalizedString("Drag to reorder sections", comment: "")) Text(NSLocalizedString("Drag to reorder sections", comment: ""))
.font(.caption) .font(.caption)
.foregroundStyle(.gray) .foregroundStyle(.gray)
.frame(maxWidth: .infinity, alignment: .center) .frame(maxWidth: .infinity, alignment: .center)
.padding(.bottom, 8)
} }
.environment(\.editMode, .constant(.active)) .environment(\.editMode, .constant(.active))
} }

View file

@ -633,8 +633,8 @@
133D7C8A2D2BE2640075467E /* JSLoader */ = { 133D7C8A2D2BE2640075467E /* JSLoader */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
04536F702E04BA3B00A11248 /* JSController-Novel.swift */,
134A387B2DE4B5B90041B687 /* Downloads */, 134A387B2DE4B5B90041B687 /* Downloads */,
04536F702E04BA3B00A11248 /* JSController-Novel.swift */,
133D7C8B2D2BE2640075467E /* JSController.swift */, 133D7C8B2D2BE2640075467E /* JSController.swift */,
132AF1202D99951700A0140B /* JSController-Streams.swift */, 132AF1202D99951700A0140B /* JSController-Streams.swift */,
132AF1222D9995C300A0140B /* JSController-Details.swift */, 132AF1222D9995C300A0140B /* JSController-Details.swift */,