Changed auth-token to cookie, moved mother script to js file, fixed wit file naming
This commit is contained in:
parent
f9266b8491
commit
cd4bf0c99e
@ -1,59 +0,0 @@
|
||||
interface contacts {
|
||||
enum capability {
|
||||
read-name-only,
|
||||
read,
|
||||
add,
|
||||
remove,
|
||||
}
|
||||
|
||||
variant request {
|
||||
/// requires ReadNameOnly capability
|
||||
/// lazy-load-blob: none.
|
||||
get-names,
|
||||
/// requires Read capability
|
||||
/// lazy-load-blob: none.
|
||||
get-all-contacts,
|
||||
/// requires Read capability
|
||||
/// lazy-load-blob: none.
|
||||
get-contact(string),
|
||||
/// requires Add capability
|
||||
/// lazy-load-blob: none.
|
||||
add-contact(string),
|
||||
/// requires Add capability
|
||||
/// lazy-load-blob: none.
|
||||
/// tuple<node, field, value>
|
||||
add-field(tuple<string, string, string>),
|
||||
/// requires Remove capability
|
||||
/// lazy-load-blob: none.
|
||||
remove-contact(string),
|
||||
/// requires Remove capability
|
||||
/// lazy-load-blob: none.
|
||||
/// tuple<node, field>
|
||||
remove-field(tuple<string, string>),
|
||||
}
|
||||
|
||||
variant response {
|
||||
/// lazy-load-blob: none.
|
||||
get-names(list<string>),
|
||||
/// lazy-load-blob: required; JSON all-contacts dict in blob.
|
||||
get-all-contacts,
|
||||
/// lazy-load-blob: required; JSON contact dict in blob.
|
||||
get-contact,
|
||||
/// lazy-load-blob: none.
|
||||
add-contact,
|
||||
/// lazy-load-blob: none.
|
||||
add-field,
|
||||
/// lazy-load-blob: none.
|
||||
remove-contact,
|
||||
/// lazy-load-blob: none.
|
||||
remove-field,
|
||||
/// any failed request will receive this response
|
||||
/// lazy-load-blob: none.
|
||||
err(string),
|
||||
}
|
||||
}
|
||||
|
||||
world contacts-sys-v0 {
|
||||
import contacts;
|
||||
include process-v1;
|
||||
}
|
6
client/api/loginex:sys-v0.wit
Normal file
6
client/api/loginex:sys-v0.wit
Normal file
@ -0,0 +1,6 @@
|
||||
interface loginex{}
|
||||
|
||||
world loginex-sys-v0 {
|
||||
import loginex;
|
||||
include process-v1;
|
||||
}
|
@ -1,12 +1,11 @@
|
||||
use crate::hyperware::process::contacts;
|
||||
use anyhow;
|
||||
use hyperware_process_lib::http::client::send_request_await_response;
|
||||
use hyperware_process_lib::http::server::{send_response, HttpBindingConfig, WsBindingConfig};
|
||||
use hyperware_process_lib::http::{Method, StatusCode};
|
||||
use hyperware_process_lib::logging::{info, init_logging, Level};
|
||||
use hyperware_process_lib::{
|
||||
await_message, call_init, eth, get_blob, get_typed_state, homepage, http, hypermap, kiprintln,
|
||||
set_state, Address, Capability, LazyLoadBlob, Message, Request,
|
||||
await_message, call_init, get_blob, get_typed_state, homepage, http, kiprintln, set_state,
|
||||
Address, Capability, Message, Request,
|
||||
};
|
||||
use process_macros::SerdeJsonInto;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -25,7 +24,7 @@ const WEB2_LOGIN_NONCE: &str = "lorem ipsum";
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "target/wit",
|
||||
world: "contacts-sys-v0",
|
||||
world: "loginex-sys-v0",
|
||||
generate_unused_types: true,
|
||||
additional_derives: [PartialEq, serde::Deserialize, serde::Serialize, process_macros::SerdeJsonInto],
|
||||
});
|
||||
@ -33,12 +32,12 @@ wit_bindgen::generate!({
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
enum FrontendRequest {
|
||||
Sign,
|
||||
CheckAuth,
|
||||
CheckCookie,
|
||||
Logout,
|
||||
Debug(String),
|
||||
}
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
enum LoginRequest {
|
||||
enum SignerRequest {
|
||||
Sign(SignRequest),
|
||||
Verify { from: Address, data: SignResponse },
|
||||
}
|
||||
@ -59,7 +58,7 @@ const ICON: &str = include_str!("icon");
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct ProxyStateV1 {
|
||||
// TODO this should probably be generic and go on login:sys:sys
|
||||
pub auth: Option<String>,
|
||||
pub cookie: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@ -73,22 +72,24 @@ enum VersionedState {
|
||||
impl VersionedState {
|
||||
fn load() -> Self {
|
||||
get_typed_state(|bytes| serde_json::from_slice(bytes))
|
||||
.unwrap_or(Self::V1(ProxyStateV1 { auth: None }))
|
||||
.unwrap_or(Self::V1(ProxyStateV1 { cookie: None }))
|
||||
}
|
||||
fn _save(&self) {
|
||||
set_state(&serde_json::to_vec(&self).expect("Failed to serialize state!"));
|
||||
}
|
||||
fn save_auth(&self, auth: String) {
|
||||
let ns = Self::V1(ProxyStateV1 { auth: Some(auth) });
|
||||
fn save_cookie(&self, cookie: String) {
|
||||
let ns = Self::V1(ProxyStateV1 {
|
||||
cookie: Some(cookie),
|
||||
});
|
||||
set_state(&serde_json::to_vec(&ns).expect("Failed to serialize state!"));
|
||||
}
|
||||
fn wipe_auth(&self) {
|
||||
let ns = Self::V1(ProxyStateV1 { auth: None });
|
||||
fn wipe_cookie(&self) {
|
||||
let ns = Self::V1(ProxyStateV1 { cookie: None });
|
||||
set_state(&serde_json::to_vec(&ns).expect("Failed to serialize state!"));
|
||||
}
|
||||
fn get_auth(&self) -> Option<String> {
|
||||
fn get_cookie(&self) -> Option<String> {
|
||||
match self {
|
||||
Self::V1(ps) => ps.auth.clone(),
|
||||
Self::V1(ps) => ps.cookie.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -101,8 +102,8 @@ fn initialize(our: Address) {
|
||||
homepage::add_to_homepage("Login", Some(ICON), Some("/"), None);
|
||||
|
||||
let mut state = VersionedState::load();
|
||||
let auth = state.get_auth();
|
||||
kiprintln!("auth {:#?}", auth);
|
||||
let cookie = state.get_cookie();
|
||||
kiprintln!("cookie{:#?}", cookie);
|
||||
|
||||
let mut http_server = http::server::HttpServer::new(5);
|
||||
let http_config = HttpBindingConfig::default().secure_subdomain(false);
|
||||
@ -207,7 +208,7 @@ fn handle_login_request(
|
||||
match request {
|
||||
FrontendRequest::Sign => {
|
||||
let target = Address::new(our.node(), ("login", "login", "sys"));
|
||||
let lr = LoginRequest::Sign(SignRequest {
|
||||
let lr = SignerRequest::Sign(SignRequest {
|
||||
site: WEB2_URL.to_string(),
|
||||
nonce: Some(WEB2_LOGIN_NONCE.to_string()),
|
||||
time: get_now(),
|
||||
@ -221,16 +222,16 @@ fn handle_login_request(
|
||||
// Send signature to designated endpoint on Web2 app
|
||||
attempt_login(our, state, res)?;
|
||||
}
|
||||
FrontendRequest::CheckAuth => {
|
||||
let auth = state.get_auth();
|
||||
let json = match auth {
|
||||
FrontendRequest::CheckCookie => {
|
||||
let cookie = state.get_cookie();
|
||||
let json = match cookie {
|
||||
None => serde_json::Value::Null,
|
||||
Some(s) => json!(s),
|
||||
};
|
||||
send_response(StatusCode::OK, None, serde_json::to_vec(&json)?)
|
||||
}
|
||||
FrontendRequest::Logout => {
|
||||
state.wipe_auth();
|
||||
state.wipe_cookie();
|
||||
let json = serde_json::to_vec(&json!(true))?;
|
||||
send_response(StatusCode::OK, None, json);
|
||||
}
|
||||
@ -271,17 +272,17 @@ fn attempt_login(
|
||||
)?;
|
||||
}
|
||||
Some(_) => {
|
||||
let auth_header = res.headers().get("x-hyperware-auth");
|
||||
match auth_header {
|
||||
let cookie_header = res.headers().get("set-cookie");
|
||||
match cookie_header {
|
||||
None => {
|
||||
send_json_response(
|
||||
StatusCode::OK,
|
||||
&json!({"error": "No auth string found in response"}),
|
||||
)?;
|
||||
}
|
||||
Some(av) => {
|
||||
let auth_string = av.to_str()?;
|
||||
state.save_auth(auth_string.to_string());
|
||||
Some(cookie_value) => {
|
||||
let cookie = cookie_value.to_str()?;
|
||||
state.save_cookie(cookie.to_string());
|
||||
send_json_response(StatusCode::OK, &json!({"ok": "go ahead"}))?;
|
||||
}
|
||||
}
|
||||
@ -295,9 +296,9 @@ fn handle_page_request(
|
||||
http_request: &http::server::IncomingHttpRequest,
|
||||
) -> anyhow::Result<()> {
|
||||
// TODO remove before release
|
||||
let auth = state.get_auth();
|
||||
let cookie = state.get_cookie();
|
||||
// let auth = Some(String::new());
|
||||
match auth {
|
||||
match cookie {
|
||||
None => {
|
||||
let package = our.package_id();
|
||||
let process = our.process();
|
||||
@ -311,27 +312,27 @@ fn handle_page_request(
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
Some(auth_token) => proxy::run_proxy(http_request, WEB2_URL, &auth_token),
|
||||
Some(cookie) => proxy::run_proxy(http_request, WEB2_URL, &cookie),
|
||||
}
|
||||
}
|
||||
|
||||
fn _invalid_node(
|
||||
hypermap: &hypermap::Hypermap,
|
||||
node: &str,
|
||||
) -> Option<(contacts::Response, Option<LazyLoadBlob>)> {
|
||||
if hypermap
|
||||
.get(&node)
|
||||
.map(|(tba, _, _)| tba != eth::Address::ZERO)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some((
|
||||
contacts::Response::Err("Node name invalid or does not exist".to_string()),
|
||||
None,
|
||||
))
|
||||
}
|
||||
}
|
||||
// fn _invalid_node(
|
||||
// hypermap: &hypermap::Hypermap,
|
||||
// node: &str,
|
||||
// ) -> Option<(contacts::Response, Option<LazyLoadBlob>)> {
|
||||
// if hypermap
|
||||
// .get(&node)
|
||||
// .map(|(tba, _, _)| tba != eth::Address::ZERO)
|
||||
// .unwrap_or(false)
|
||||
// {
|
||||
// None
|
||||
// } else {
|
||||
// Some((
|
||||
// contacts::Response::Err("Node name invalid or does not exist".to_string()),
|
||||
// None,
|
||||
// ))
|
||||
// }
|
||||
// }
|
||||
|
||||
fn send_json_response<T: serde::Serialize>(status: StatusCode, data: &T) -> anyhow::Result<()> {
|
||||
let json_data = serde_json::to_vec(data)?;
|
||||
|
366
client/loginex/src/mother_script.js
Normal file
366
client/loginex/src/mother_script.js
Normal file
@ -0,0 +1,366 @@
|
||||
console.log("mother script running");
|
||||
|
||||
// Store original functions
|
||||
const originalFetch = window.fetch;
|
||||
const originalXHROpen = XMLHttpRequest.prototype.open;
|
||||
const originalCreateElement = document.createElement;
|
||||
|
||||
function getURL(resource){
|
||||
if (typeof resource === "string") {
|
||||
const isURL = URL.canParse(resource);
|
||||
if (isURL) return URL.parse(resource)
|
||||
else {
|
||||
const urlTry = (window.location.origin + resource);
|
||||
if (URL.canParse(urlTry)) return URL.parse(urlTry)
|
||||
else return null
|
||||
}
|
||||
}
|
||||
else if ("pathname" in resource) return resource
|
||||
else if ("method" in resource) return URL.parse(resource.url)
|
||||
else {
|
||||
console.warn("weird fetch", resource);
|
||||
return null
|
||||
}
|
||||
}
|
||||
function reRouteToHyperware(url){
|
||||
console.log("Rewriting URL:", url);
|
||||
if (url.origin === window.location.origin)
|
||||
url.pathname = HYPERWARE_APP_PATH + url.pathname;
|
||||
else console.warn("external fetch?", url.href);
|
||||
return url
|
||||
}
|
||||
// Log all network activity for debugging
|
||||
window.fetch = async function(resource, options) {
|
||||
const url1 = getURL(resource);
|
||||
if (!url) return
|
||||
const url = reRouteToHyperware(url1);
|
||||
const res = await originalFetch(url, options);
|
||||
const resc = res.clone();
|
||||
const headers = {};
|
||||
for (let [k, v] of resc.headers.entries()){
|
||||
headers[k] = v;
|
||||
}
|
||||
console.warn({url, status: resc.status, headers});
|
||||
return res
|
||||
};
|
||||
|
||||
XMLHttpRequest.prototype.open = function(method, url, ...rest) {
|
||||
console.log("XHR intercepted:", url);
|
||||
let newUrl = url;
|
||||
if (url.includes('/_next/') || url.includes('/static/chunks/')) {
|
||||
console.log("Rewriting XHR URL:", url);
|
||||
newUrl = HYPERWARE_APP_PATH + url;
|
||||
}
|
||||
// return originalXHROpen.call(this, method, newUrl, ...rest);
|
||||
};
|
||||
|
||||
// Most important: intercept script tag creation since webpack uses this
|
||||
document.createElement = function(tagName) {
|
||||
const element = originalCreateElement.call(document, tagName);
|
||||
console.log("dynamic element created", tagName);
|
||||
const ltn = tagName.toLowerCase();
|
||||
|
||||
if (ltn === 'link' || ltn === "a") {
|
||||
let originalHref = '';
|
||||
Object.defineProperty(element, 'href', {
|
||||
get: function() {
|
||||
return originalHref;
|
||||
},
|
||||
set: function(value) {
|
||||
console.log("setting href", {new: value, old: element.getAttribute("href")});
|
||||
const uri = typeof value === "string" ? value : value.toString();
|
||||
const url = getURL(uri);
|
||||
originalHref = url;
|
||||
return url;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (['script', 'img', 'video', 'audio', 'track', 'iframe'].includes(ltn)) {
|
||||
// Add a property setter interceptor for the src attribute
|
||||
let originalSrc = '';
|
||||
Object.defineProperty(element, 'src', {
|
||||
get: function() {
|
||||
return originalSrc;
|
||||
},
|
||||
set: function(value) {
|
||||
const uri = typeof value === "string" ? value : value.toString();
|
||||
console.log("setting src", {new: value, old: element.src});
|
||||
// if (uri.includes("vercel")){
|
||||
// console.log({element});
|
||||
// return ""
|
||||
// }
|
||||
const url = getURL(uri);
|
||||
if (!url) return
|
||||
originalSrc = url
|
||||
return url;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (ltn === 'img') {
|
||||
const originalSetAttribute = element.setAttribute;
|
||||
element.setAttribute = function(name, value) {
|
||||
let newValue = value;
|
||||
console.log("setting img", {name, value});
|
||||
if (name === "src" || name === "href"){
|
||||
const uri = typeof value === "string" ? value : value.toString();
|
||||
const url1 = getURL(uri);
|
||||
if (url1) newValue = reRouteToHyperware(url1);
|
||||
}
|
||||
return originalSetAttribute.call(this, name, newValue);
|
||||
};
|
||||
}
|
||||
if (ltn === 'meta') {
|
||||
const originalSetAttribute = element.setAttribute;
|
||||
element.setAttribute = function(name, value) {
|
||||
if (name === 'http-equiv' && value.toLowerCase() === 'refresh') {
|
||||
console.warn("META REFRESH DETECTED");
|
||||
console.trace("Meta refresh stack trace");
|
||||
debugger;
|
||||
}
|
||||
return originalSetAttribute.call(this, name, value);
|
||||
};
|
||||
}
|
||||
else console.log("element created", {tagName, element});
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
// Add monitoring of script tags to see what's happening
|
||||
function observeMutations(){
|
||||
let oldHref = document.location.href;
|
||||
// let oldHref2 = window.location.href;
|
||||
const body = document.querySelector("body");
|
||||
const observer = new MutationObserver(mutations => {
|
||||
if (oldHref !== document.location.href){
|
||||
console.warn("mutation", {new: document.location.href, old: oldHref});
|
||||
// console.warn("mutationw", {neww: window.location.href, oldw: oldHref2});
|
||||
|
||||
};
|
||||
|
||||
for (let mutation of mutations){
|
||||
console.log(mutation.type, mutation)
|
||||
for (let node of mutation.addedNodes){
|
||||
const attrs = node.getAttributeNames();
|
||||
const data = Array.from(attrs).reduce((acc, item) => ({...acc, [item]: node.getAttribute(item)}), {});
|
||||
data.outerHTML = node.outerHTML;
|
||||
data.tagName = node.tagName;
|
||||
fuckNext();
|
||||
}
|
||||
}
|
||||
});
|
||||
observer.observe(body, {childList: true, subtree: true, attributes: true, attributeOldValue: true})
|
||||
|
||||
}
|
||||
|
||||
// next router crap
|
||||
|
||||
let fuckingNext = false;
|
||||
// Wait for Next.js to initialize
|
||||
const fuckNext = () => {
|
||||
if (fuckingNext) return
|
||||
if (window.next && window.next.router) {
|
||||
console.log("next router", window.next.router);
|
||||
// Store original router methods
|
||||
const originalPush = window.next.router.push;
|
||||
const originalReplace = window.next.router.replace;
|
||||
const originalPrefetch = window.next.router.prefetch;
|
||||
|
||||
// Override router methods
|
||||
window.next.router.push = function() {
|
||||
console.log("Router push intercepted:", arguments);
|
||||
// Return without calling the original to prevent navigation
|
||||
return new Promise(() => {}); // Return a never-resolving promise
|
||||
};
|
||||
|
||||
window.next.router.replace = function() {
|
||||
console.log("Router replace intercepted:", arguments);
|
||||
// Return without calling the original to prevent navigation
|
||||
return new Promise(() => {});
|
||||
};
|
||||
window.next.router.refresh= function() {
|
||||
console.log("Router refresh intercepted:", arguments);
|
||||
// Return without calling the original to prevent navigation
|
||||
return new Promise(() => {});
|
||||
};
|
||||
window.next.router.prefetch = function() {
|
||||
console.log("Router prefetch intercepted:", arguments);
|
||||
return originalPrefetch.call(arguments)
|
||||
};
|
||||
window.next.router.hmrRefresh = function() {
|
||||
console.log("Router hmrRefresh intercepted:", arguments);
|
||||
// Return without calling the original to prevent navigation
|
||||
return new Promise(() => {});
|
||||
};
|
||||
window.next.router.forward = function() {
|
||||
console.log("Router forward intercepted:", arguments);
|
||||
// Return without calling the original to prevent navigation
|
||||
return new Promise(() => {});
|
||||
};
|
||||
window.next.router.back= function() {
|
||||
console.log("Router back intercepted:", arguments);
|
||||
// Return without calling the original to prevent navigation
|
||||
return new Promise(() => {});
|
||||
};
|
||||
|
||||
console.log("Next.js router methods intercepted");
|
||||
fuckingNext = true;
|
||||
} else {
|
||||
console.log("Next.js router not found");
|
||||
}
|
||||
};
|
||||
|
||||
// Listeners
|
||||
// Start observing when body is available
|
||||
document.addEventListener("DOMContentLoaded",observeMutations);
|
||||
navigation.addEventListener("navigate", (e) => {
|
||||
console.log("navigation", e.destination)
|
||||
console.log("navigation", e.info)
|
||||
console.log("navigation", e.navigationType)
|
||||
console.log("navigation", e.userInitiated)
|
||||
console.log("navigation", e.canIntercept)
|
||||
e.intercept({
|
||||
async handler(){
|
||||
console.log("intercepting navigation")
|
||||
}
|
||||
})
|
||||
})
|
||||
window.addEventListener("hashchange", (e) => {
|
||||
console.warn("navigation", window.location)
|
||||
})
|
||||
window.addEventListener('beforeunload', function(e) {
|
||||
remoteLog("unloading!!", e);
|
||||
});
|
||||
window.addEventListener('popstate', function(e) {
|
||||
remoteLog("popstate", e);
|
||||
});
|
||||
window.addEventListener('pageswap', function(e) {
|
||||
remoteLog("pageswap", e);
|
||||
});
|
||||
|
||||
const originalListener = window.addEventListener;
|
||||
|
||||
window.addEventListener = function(type, listener, options) {
|
||||
if (type === 'popstate') {
|
||||
console.log("Popstate event listener added");
|
||||
// Replace with no-op function
|
||||
return originalListener.call(this, type, function(e) {
|
||||
console.log("Popstate event prevented", e);
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
}, options);
|
||||
}
|
||||
return originalListener.call(this, type, listener, options);
|
||||
};
|
||||
|
||||
// // prevent redirects
|
||||
// const originalLocationDescriptor = Object.getOwnPropertyDescriptor(window, 'location');
|
||||
// // Override location to prevent redirects
|
||||
// let interceptedLocation = { ...location };
|
||||
// Object.defineProperty(window, 'location', {
|
||||
// get: function() {
|
||||
// return interceptedLocation;
|
||||
// },
|
||||
// set: function(value) {
|
||||
// console.log("PREVENTED REDIRECT to:", value);
|
||||
// console.trace("Redirect stack trace");
|
||||
// // Don't actually set it, just log it
|
||||
// return interceptedLocation;
|
||||
// }
|
||||
// });
|
||||
|
||||
// // Also overwrite properties on our fake location
|
||||
// for (let prop in interceptedLocation) {
|
||||
// if (typeof interceptedLocation[prop] !== 'function') {
|
||||
// Object.defineProperty(interceptedLocation, prop, {
|
||||
// get: function() {
|
||||
// return location[prop];
|
||||
// },
|
||||
// set: function(value) {
|
||||
// console.log(`PREVENTED location.${prop} change to:`, value);
|
||||
// console.trace(`location.${prop} change stack trace`);
|
||||
// // Don't actually apply the change
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
// Store logs in sessionStorage
|
||||
const originalConsoleLog = console.warn;
|
||||
console.warn= function(...args) {{
|
||||
remoteLog(...args);
|
||||
// originalConsoleLog.apply(this, args);
|
||||
};
|
||||
// console.log = function() {{
|
||||
// const jon = JSON.stringify(arguments);
|
||||
// remoteLog(jon);
|
||||
// console.log(arguments)
|
||||
// originalConsoleLog.apply(this, arguments);
|
||||
// };
|
||||
|
||||
|
||||
console.log("mother script finished loading");
|
||||
|
||||
async function remoteLog(...args){
|
||||
const logObj = Array.from(args).reduce((acc, item, i) => ({...acc, [i.toString()]: safeStringify(item)}), {});
|
||||
|
||||
await originalFetch("http://localhost:8283", {
|
||||
method: 'POST',
|
||||
keepalive: true, // This allows the request to outlive the page
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
Debug: logObj
|
||||
})
|
||||
})}
|
||||
|
||||
|
||||
function safeStringify(obj, depth = 0) {
|
||||
if (depth > 3) return "[Object depth limit exceeded]";
|
||||
|
||||
try {
|
||||
if (obj === null) return "null";
|
||||
if (obj === undefined) return "undefined";
|
||||
|
||||
const type = typeof obj;
|
||||
|
||||
// Handle primitive types
|
||||
if (type !== 'object' && type !== 'function') {
|
||||
return String(obj);
|
||||
}
|
||||
|
||||
// Handle special objects
|
||||
if (obj instanceof Error) {
|
||||
return `Error: ${obj.message}\n${obj.stack || ''}`;
|
||||
}
|
||||
|
||||
if (obj instanceof HTMLElement) {
|
||||
return `[${obj.tagName.toLowerCase()}${obj.id ? '#'+obj.id : ''}]`;
|
||||
}
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
const items = obj.map(item => safeStringify(item, depth + 1));
|
||||
return `[${items.join(', ')}]`;
|
||||
}
|
||||
|
||||
// Handle regular objects
|
||||
const pairs = [];
|
||||
for (const key in obj) {
|
||||
try {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
const value = safeStringify(obj[key], depth + 1);
|
||||
pairs.push(`${key}: ${value}`);
|
||||
// Limit number of properties to avoid huge objects
|
||||
if (pairs.length >= 10) {
|
||||
pairs.push("...");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
pairs.push(`${key}: [Error during serialization]`);
|
||||
}
|
||||
}
|
||||
return `{${pairs.join(', ')}`;
|
||||
} catch (e) {
|
||||
return `[Non-serializable: ${e.message}]`;
|
||||
}
|
||||
}
|
||||
}
|
@ -178,14 +178,14 @@ fn modify_html(html_bytes: &[u8], prefix: &str) -> anyhow::Result<Vec<u8>> {
|
||||
pub fn run_proxy(
|
||||
request: &IncomingHttpRequest,
|
||||
web2_url: &str,
|
||||
auth_token: &str,
|
||||
cookie: &str,
|
||||
) -> anyhow::Result<()> {
|
||||
let blob = get_blob().unwrap();
|
||||
let body = blob.bytes().to_vec();
|
||||
let url = replace_domain(&request.url()?, web2_url)?;
|
||||
let (first_path_segment, url) = split_first_path_segment(&url)?;
|
||||
let mut headers = HashMap::new();
|
||||
headers.insert("hypr-auth".to_string(), auth_token.to_string());
|
||||
headers.insert("Cookie".to_string(), cookie.to_string());
|
||||
kiprintln!("fetching\n{}", url.to_string());
|
||||
let response = send_request_await_response(request.method()?, url, Some(headers), 6000, body)?;
|
||||
let resheaders = response.headers();
|
||||
@ -225,373 +225,8 @@ fn mother_script(prefix: &str) -> String {
|
||||
let script_text = format!(
|
||||
r#"
|
||||
<script>
|
||||
console.log("mother script running");
|
||||
|
||||
// Store original functions
|
||||
const originalFetch = window.fetch;
|
||||
const originalXHROpen = XMLHttpRequest.prototype.open;
|
||||
const originalCreateElement = document.createElement;
|
||||
|
||||
function getURL(resource){{
|
||||
if (typeof resource === "string") {{
|
||||
const isURL = URL.canParse(resource);
|
||||
if (isURL) return URL.parse(resource)
|
||||
else {{
|
||||
const urlTry = (window.location.origin + resource);
|
||||
if (URL.canParse(urlTry)) return URL.parse(urlTry)
|
||||
else return null
|
||||
}}
|
||||
}}
|
||||
else if ("pathname" in resource) return resource
|
||||
else if ("method" in resource) return URL.parse(resource.url)
|
||||
else {{
|
||||
console.warn("weird fetch", resource);
|
||||
return null
|
||||
}}
|
||||
}}
|
||||
function reRouteToHyperware(url){{
|
||||
console.log("Rewriting URL:", url);
|
||||
if (url.origin === window.location.origin)
|
||||
url.pathname = '{0}' + url.pathname;
|
||||
else console.warn("external fetch?", url.href);
|
||||
return url
|
||||
}}
|
||||
// Log all network activity for debugging
|
||||
window.fetch = async function(resource, options) {{
|
||||
const url1 = getURL(resource);
|
||||
if (!url) return
|
||||
const url = reRouteToHyperware(url1);
|
||||
const res = await originalFetch(url, options);
|
||||
const resc = res.clone();
|
||||
const headers = {{}};
|
||||
for (let [k, v] of resc.headers.entries()){{
|
||||
headers[k] = v;
|
||||
}}
|
||||
console.warn({{url, status: resc.status, headers}});
|
||||
return res
|
||||
}};
|
||||
|
||||
XMLHttpRequest.prototype.open = function(method, url, ...rest) {{
|
||||
console.log("XHR intercepted:", url);
|
||||
let newUrl = url;
|
||||
if (url.includes('/_next/') || url.includes('/static/chunks/')) {{
|
||||
console.log("Rewriting XHR URL:", url);
|
||||
newUrl = '{0}' + url;
|
||||
}}
|
||||
// return originalXHROpen.call(this, method, newUrl, ...rest);
|
||||
}};
|
||||
|
||||
// Most important: intercept script tag creation since webpack uses this
|
||||
document.createElement = function(tagName) {{
|
||||
const element = originalCreateElement.call(document, tagName);
|
||||
console.log("dynamic element created", tagName);
|
||||
const ltn = tagName.toLowerCase();
|
||||
|
||||
if (ltn === 'link' || ltn === "a") {{
|
||||
let originalHref = '';
|
||||
Object.defineProperty(element, 'href', {{
|
||||
get: function() {{
|
||||
return originalHref;
|
||||
}},
|
||||
set: function(value) {{
|
||||
console.log("setting href", {{new: value, old: element.getAttribute("href")}});
|
||||
const uri = typeof value === "string" ? value : value.toString();
|
||||
const url = getURL(uri);
|
||||
originalHref = url;
|
||||
return url;
|
||||
}}
|
||||
}});
|
||||
}}
|
||||
if (['script', 'img', 'video', 'audio', 'track', 'iframe'].includes(ltn)) {{
|
||||
// Add a property setter interceptor for the src attribute
|
||||
let originalSrc = '';
|
||||
Object.defineProperty(element, 'src', {{
|
||||
get: function() {{
|
||||
return originalSrc;
|
||||
}},
|
||||
set: function(value) {{
|
||||
const uri = typeof value === "string" ? value : value.toString();
|
||||
console.log("setting src", {{new: value, old: element.src}});
|
||||
// if (uri.includes("vercel")){{
|
||||
// console.log({{element}});
|
||||
// return ""
|
||||
// }}
|
||||
const url = getURL(uri);
|
||||
if (!url) return
|
||||
originalSrc = url
|
||||
return url;
|
||||
}}
|
||||
}});
|
||||
}}
|
||||
if (ltn === 'img') {{
|
||||
const originalSetAttribute = element.setAttribute;
|
||||
element.setAttribute = function(name, value) {{
|
||||
let newValue = value;
|
||||
console.log("setting img", {{name, value}});
|
||||
if (name === "src" || name === "href"){{
|
||||
const uri = typeof value === "string" ? value : value.toString();
|
||||
const url1 = getURL(uri);
|
||||
if (url1) newValue = reRouteToHyperware(url1);
|
||||
}}
|
||||
return originalSetAttribute.call(this, name, newValue);
|
||||
}};
|
||||
}}
|
||||
if (ltn === 'meta') {{
|
||||
const originalSetAttribute = element.setAttribute;
|
||||
element.setAttribute = function(name, value) {{
|
||||
if (name === 'http-equiv' && value.toLowerCase() === 'refresh') {{
|
||||
console.warn("META REFRESH DETECTED");
|
||||
console.trace("Meta refresh stack trace");
|
||||
debugger;
|
||||
}}
|
||||
return originalSetAttribute.call(this, name, value);
|
||||
}};
|
||||
}}
|
||||
else console.log("element created", {{tagName, element}});
|
||||
|
||||
return element;
|
||||
}};
|
||||
|
||||
// Add monitoring of script tags to see what's happening
|
||||
function observeMutations(){{
|
||||
let oldHref = document.location.href;
|
||||
// let oldHref2 = window.location.href;
|
||||
const body = document.querySelector("body");
|
||||
const observer = new MutationObserver(mutations => {{
|
||||
if (oldHref !== document.location.href){{
|
||||
console.warn("mutation", {{new: document.location.href, old: oldHref}});
|
||||
// console.warn("mutationw", {{neww: window.location.href, oldw: oldHref2}});
|
||||
|
||||
}};
|
||||
|
||||
for (let mutation of mutations){{
|
||||
console.log(mutation.type, mutation)
|
||||
for (let node of mutation.addedNodes){{
|
||||
const attrs = node.getAttributeNames();
|
||||
const data = Array.from(attrs).reduce((acc, item) => ({{...acc, [item]: node.getAttribute(item)}}), {{}});
|
||||
data.outerHTML = node.outerHTML;
|
||||
data.tagName = node.tagName;
|
||||
fuckNext();
|
||||
}}
|
||||
}}
|
||||
}});
|
||||
observer.observe(body, {{childList: true, subtree: true, attributes: true, attributeOldValue: true}})
|
||||
|
||||
}}
|
||||
|
||||
// next router crap
|
||||
|
||||
let fuckingNext = false;
|
||||
// Wait for Next.js to initialize
|
||||
const fuckNext = () => {{
|
||||
if (fuckingNext) return
|
||||
if (window.next && window.next.router) {{
|
||||
console.log("next router", window.next.router);
|
||||
// Store original router methods
|
||||
const originalPush = window.next.router.push;
|
||||
const originalReplace = window.next.router.replace;
|
||||
const originalPrefetch = window.next.router.prefetch;
|
||||
|
||||
// Override router methods
|
||||
window.next.router.push = function() {{
|
||||
console.log("Router push intercepted:", arguments);
|
||||
// Return without calling the original to prevent navigation
|
||||
return new Promise(() => {{}}); // Return a never-resolving promise
|
||||
}};
|
||||
|
||||
window.next.router.replace = function() {{
|
||||
console.log("Router replace intercepted:", arguments);
|
||||
// Return without calling the original to prevent navigation
|
||||
return new Promise(() => {{}});
|
||||
}};
|
||||
window.next.router.refresh= function() {{
|
||||
console.log("Router refresh intercepted:", arguments);
|
||||
// Return without calling the original to prevent navigation
|
||||
return new Promise(() => {{}});
|
||||
}};
|
||||
window.next.router.prefetch = function() {{
|
||||
console.log("Router prefetch intercepted:", arguments);
|
||||
return originalPrefetch.call(arguments)
|
||||
}};
|
||||
window.next.router.hmrRefresh = function() {{
|
||||
console.log("Router hmrRefresh intercepted:", arguments);
|
||||
// Return without calling the original to prevent navigation
|
||||
return new Promise(() => {{}});
|
||||
}};
|
||||
window.next.router.forward = function() {{
|
||||
console.log("Router forward intercepted:", arguments);
|
||||
// Return without calling the original to prevent navigation
|
||||
return new Promise(() => {{}});
|
||||
}};
|
||||
window.next.router.back= function() {{
|
||||
console.log("Router back intercepted:", arguments);
|
||||
// Return without calling the original to prevent navigation
|
||||
return new Promise(() => {{}});
|
||||
}};
|
||||
|
||||
console.log("Next.js router methods intercepted");
|
||||
fuckingNext = true;
|
||||
}} else {{
|
||||
console.log("Next.js router not found");
|
||||
}}
|
||||
}};
|
||||
|
||||
// Listeners
|
||||
// Start observing when body is available
|
||||
document.addEventListener("DOMContentLoaded",observeMutations);
|
||||
navigation.addEventListener("navigate", (e) => {{
|
||||
console.log("navigation", e.destination)
|
||||
console.log("navigation", e.info)
|
||||
console.log("navigation", e.navigationType)
|
||||
console.log("navigation", e.userInitiated)
|
||||
console.log("navigation", e.canIntercept)
|
||||
e.intercept({{
|
||||
async handler(){{
|
||||
console.log("intercepting navigation")
|
||||
}}
|
||||
}})
|
||||
}})
|
||||
window.addEventListener("hashchange", (e) => {{
|
||||
console.warn("navigation", window.location)
|
||||
}})
|
||||
window.addEventListener('beforeunload', function(e) {{
|
||||
remoteLog("unloading!!", e);
|
||||
}});
|
||||
window.addEventListener('popstate', function(e) {{
|
||||
remoteLog("popstate", e);
|
||||
}});
|
||||
window.addEventListener('pageswap', function(e) {{
|
||||
remoteLog("pageswap", e);
|
||||
}});
|
||||
|
||||
const originalListener = window.addEventListener;
|
||||
|
||||
window.addEventListener = function(type, listener, options) {{
|
||||
if (type === 'popstate') {{
|
||||
console.log("Popstate event listener added");
|
||||
// Replace with no-op function
|
||||
return originalListener.call(this, type, function(e) {{
|
||||
console.log("Popstate event prevented", e);
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
}}, options);
|
||||
}}
|
||||
return originalListener.call(this, type, listener, options);
|
||||
}};
|
||||
|
||||
// // prevent redirects
|
||||
// const originalLocationDescriptor = Object.getOwnPropertyDescriptor(window, 'location');
|
||||
// // Override location to prevent redirects
|
||||
// let interceptedLocation = {{ ...location }};
|
||||
// Object.defineProperty(window, 'location', {{
|
||||
// get: function() {{
|
||||
// return interceptedLocation;
|
||||
// }},
|
||||
// set: function(value) {{
|
||||
// console.log("PREVENTED REDIRECT to:", value);
|
||||
// console.trace("Redirect stack trace");
|
||||
// // Don't actually set it, just log it
|
||||
// return interceptedLocation;
|
||||
// }}
|
||||
// }});
|
||||
|
||||
// // Also overwrite properties on our fake location
|
||||
// for (let prop in interceptedLocation) {{
|
||||
// if (typeof interceptedLocation[prop] !== 'function') {{
|
||||
// Object.defineProperty(interceptedLocation, prop, {{
|
||||
// get: function() {{
|
||||
// return location[prop];
|
||||
// }},
|
||||
// set: function(value) {{
|
||||
// console.log(`PREVENTED location.${{prop}} change to:`, value);
|
||||
// console.trace(`location.${{prop}} change stack trace`);
|
||||
// // Don't actually apply the change
|
||||
// }}
|
||||
// }});
|
||||
// }}
|
||||
// }}
|
||||
|
||||
// Store logs in sessionStorage
|
||||
const originalConsoleLog = console.warn;
|
||||
console.warn= function(...args) {{{{
|
||||
remoteLog(...args);
|
||||
// originalConsoleLog.apply(this, args);
|
||||
}}}};
|
||||
// console.log = function() {{{{
|
||||
// const jon = JSON.stringify(arguments);
|
||||
// remoteLog(jon);
|
||||
// console.log(arguments)
|
||||
// originalConsoleLog.apply(this, arguments);
|
||||
// }}}};
|
||||
|
||||
|
||||
console.log("mother script finished loading");
|
||||
|
||||
async function remoteLog(...args){{
|
||||
const logObj = Array.from(args).reduce((acc, item, i) => ({{...acc, [i.toString()]: safeStringify(item)}}), {{}});
|
||||
|
||||
await originalFetch("http://localhost:8283", {{
|
||||
method: 'POST',
|
||||
keepalive: true, // This allows the request to outlive the page
|
||||
headers: {{'Content-Type': 'application/json'}},
|
||||
body: JSON.stringify({{
|
||||
Debug: logObj
|
||||
}})
|
||||
}})}}
|
||||
|
||||
|
||||
function safeStringify(obj, depth = 0) {{
|
||||
if (depth > 3) return "[Object depth limit exceeded]";
|
||||
|
||||
try {{
|
||||
if (obj === null) return "null";
|
||||
if (obj === undefined) return "undefined";
|
||||
|
||||
const type = typeof obj;
|
||||
|
||||
// Handle primitive types
|
||||
if (type !== 'object' && type !== 'function') {{
|
||||
return String(obj);
|
||||
}}
|
||||
|
||||
// Handle special objects
|
||||
if (obj instanceof Error) {{
|
||||
return `Error: ${{obj.message}}\n${{obj.stack || ''}}`;
|
||||
}}
|
||||
|
||||
if (obj instanceof HTMLElement) {{
|
||||
return `[${{obj.tagName.toLowerCase()}}${{obj.id ? '#'+obj.id : ''}}]`;
|
||||
}}
|
||||
|
||||
if (Array.isArray(obj)) {{
|
||||
const items = obj.map(item => safeStringify(item, depth + 1));
|
||||
return `[${{items.join(', ')}}]`;
|
||||
}}
|
||||
|
||||
// Handle regular objects
|
||||
const pairs = [];
|
||||
for (const key in obj) {{
|
||||
try {{
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {{
|
||||
const value = safeStringify(obj[key], depth + 1);
|
||||
pairs.push(`${{key}}: ${{value}}`);
|
||||
// Limit number of properties to avoid huge objects
|
||||
if (pairs.length >= 10) {{
|
||||
pairs.push("...");
|
||||
break;
|
||||
}}
|
||||
}}
|
||||
}} catch (e) {{
|
||||
pairs.push(`${{key}}: [Error during serialization]`);
|
||||
}}
|
||||
}}
|
||||
return `{{${{pairs.join(', ')}}}}`;
|
||||
}} catch (e) {{
|
||||
return `[Non-serializable: ${{e.message}}]`;
|
||||
}}
|
||||
}}
|
||||
|
||||
</script>
|
||||
const HYPERWARE_APP_PATH = '{0}';
|
||||
</script>
|
||||
"#,
|
||||
prefix
|
||||
);
|
||||
|
@ -6,7 +6,7 @@ document.addEventListener("DOMContentLoaded", checkAuth);
|
||||
|
||||
|
||||
async function checkAuth() {
|
||||
const res = await call_app({ CheckAuth: null });
|
||||
const res = await call_app({ CheckCookie: null });
|
||||
const j = await res.json();
|
||||
console.log({j})
|
||||
if (j) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user