-
-
Notifications
You must be signed in to change notification settings - Fork 151
HUD Startup Flow
In Zap Desktop
- While HUD is turned on, Browser Quick Launch is pressed starting up a browser
In Browser
- User navigates to a target web page
- http request is sent
In ZAP Desktop
- http request is proxied with no changes
- http response is proxied and ZAP modifies all html responses to include the "injectionHtml.html" source (a single script tag) added right below the openning tag in the response
// These are the only files that use FILE_PREFIX
hudScript =
hudScript.replace("<<FILE_PREFIX>>", api.getUrlPrefix(api.getSite(msg)));
htmlEd.injectAtStartOfBody(hudScript);
htmlEd.rewriteHttpMessage();
In Browser
- Browser parses the html response
- when the browser parsing reaches the inject script tag it asynchronously requests the javascript file inject.js
In ZAP Desktop
- ZAP intercepts the request for the inject.js page and returns it
In Browser
- When the browser receives the javascript response, it begins executing the file
In inject.js
- the inject.js files checks whether the browser window it was loaded into is the "top window" and if not, does nothing more (this will be for iframes in the target page that get the inject script added)
- generates tabId
- adds postMessage listener
- adds iframes to DOM
- updates URL
if (window.top == window.self) {
tabId = generateTabId();
window.addEventListener("message", receiveMessages);
var template = document.createElement("template");
template.innerHTML = '<iframe id="management" src="' + ZAP_HUD_FILES + '?name=management.html&frameId=management&tabId=' + tabId + '" scrolling="no" style="position: fixed; right: 0px; bottom: 50px; width:28px; height:60px; border: medium none; overflow: hidden; z-index: 2147483647"></iframe>\n' +
'<iframe id="left-panel" src="' + ZAP_HUD_FILES + '?name=panel.html&url=' + URL + '&orientation=left&frameId=leftPanel&tabId=' + tabId + '" scrolling="no" style="position: fixed; border: medium none; top: 30%; border: medium none; left: 0px; width: 110px; height: 300px; z-index: 2147483646;"></iframe>\n' +
'<iframe id="right-panel" src="' + ZAP_HUD_FILES + '?name=panel.html&url=' + URL + '&orientation=right&frameId=rightPanel&tabId=' + tabId + '" scrolling="no" style="position: fixed; border: medium none; top: 30%; overflow: hidden; right: 0px; width: 110px; height: 300px; z-index: 2147483646;"></iframe>\n' +
'<iframe id="bottom-drawer" src="' + ZAP_HUD_FILES + '?name=drawer.html&frameId=drawer&tabId=' + tabId + '" scrolling="no" style="position: fixed; border: medium none; overflow: hidden; left: 0px; bottom: 0px; width: 100%; height: 50px; z-index: 2147483646;"></iframe>\n' +
'<iframe id="main-display" src="' + ZAP_HUD_FILES + '?name=display.html&frameId=display&tabId=' + tabId + '" style="position: fixed; right: 0px; top: 0px; width: 100%; height: 100%; border: 0px none; display: none; z-index: 2147483647;"></iframe>\n' +
'<iframe id="growler-alerts" src="' + ZAP_HUD_FILES + '?name=growlerAlerts.html&frameId=growlerAlerts&tabId=' + tabId + '" style="position: fixed; right: 0px; bottom: 0px; width: 500px; height: 0px;border: 0px none; z-index: 2147483647;"></iframe>';
document.body.appendChild(template.content);
https://github.com/zaproxy/zap-hud/blob/main/src/main/zapHomeFiles/hud/target/inject.js#L362
In management.js
- When the management.html iframe is added to the DOM if loads management.js
- management.js loads a vue copmonent
- on DomContentLoaded event
- if navigator.serviceworker.controller == null (if there is no serviceworker)
- hide all of the display frames (by sending postMessage to inject.js to hide the iframes)
// if first time starting HUD boot up the service worker
if (navigator.serviceWorker.controller === null) {
// temp time test
localforage.setItem('starttime', startTime)
parent.postMessage( {action: 'hideAllDisplayFrames'} , document.referrer);
localforage.setItem('is_first_load', true)
startServiceWorker();
- register the serviceworker
- Waits until the navigator.serviceworker.controller.ready promise resolves to true
function startServiceWorker() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register(utils.getZapFilePath('serviceworker.js'))
.then(registration => {
utils.log(LOG_INFO, 'Service worker registration was successful for the scope: ' + registration.scope);
// wait until serviceworker is installed and activated
return navigator.serviceWorker.ready;
})
In serviceworker.js When the browser registers a serviceworker it loads the script and starts executing
- import javascript files needed
importScripts(ZAP_HUD_FILES + "?name=libraries/localforage.min.js");
importScripts(ZAP_HUD_FILES + "?name=libraries/vue.js");
importScripts(ZAP_HUD_FILES + "?name=libraries/vue-i18n.js");
importScripts(ZAP_HUD_FILES + "?name=i18n.js");
importScripts(ZAP_HUD_FILES + "?name=utils.js");
importScripts(ZAP_HUD_FILES + "?name=tools/utils/alertUtils.js");
https://github.com/zaproxy/zap-hud/blob/main/src/main/zapHomeFiles/hud/serviceworker.js#L7
- import tool scripts
// Load Tool Scripts
localforage.setItem("tools", [])
.then(() => {
toolScripts.forEach(script => {
importScripts(script);
});
})
.then(() => {
var ts = [];
for (var tool in self.tools) {
ts.push(self.tools[tool].name);
}
return utils.registerTools(ts);
})
.catch(utils.errorHandler);
https://github.com/zaproxy/zap-hud/blob/main/src/main/zapHomeFiles/hud/serviceworker.js#L46
- add event listeners
- configured and start the web socket
/* Set up WebSockets */
{
let ZAP_HUD_WS = '<<ZAP_HUD_WS>>';
webSocket = new WebSocket(ZAP_HUD_WS);
}
https://github.com/zaproxy/zap-hud/blob/main/src/main/zapHomeFiles/hud/serviceworker.js#L173
- browser finishes parsing/executing serviceworker.js file
In Browser
- now that serviceworker.js file has been parsed in sends 'install' event to serviceworker
In serviceworker.js
- the onInstall event handler is invoked
- the serviceworker caches some files to be served up by the onFetch handler
- the onInstall handler finishes
const onInstall = event => {
utils.log(LOG_INFO, 'serviceworker.install', 'Installing...');
// Cache Files
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log("caching urls...");
return cache.addAll(urlsToCache);
})
.catch(utils.errorHandler)
);
};
https://github.com/zaproxy/zap-hud/blob/main/src/main/zapHomeFiles/hud/serviceworker.js#L63
In Browser
- now that the install event has been handled, it sends the 'activate' event to the serviceworker
In serviceworker.js
- the onActivate handler is invoked
- the serviceworker run the initializeHud function which sets a bunch of things in indexedDb
- the onActivate handler finishes
const onActivate = event => {
// Check Storage & Initiate
event.waitUntil(
utils.isStorageConfigured()
.then(isConfigured => {
if (!isConfigured || isDebugging) {
return utils.configureStorage();
}
})
.then(() => {
// set the default tools after configuring storage
utils.setDefaultTools();
})
.catch(utils.errorHandler)
);
};
https://github.com/zaproxy/zap-hud/blob/main/src/main/zapHomeFiles/hud/serviceworker.js#L77
In browser
- now that the activate event hadn been handled, it sets resolves the navigator.serviceworker.controller.ready promise
In management.js
- now that navigator.serviceworker.controller.ready had resolved, it refreshes all of the display frames by sending a postMessage to the inject.js file to refresh the iframes
- And then does nothing
.then(() => {
// refresh the frames so the service worker can take control
parent.postMessage( {action: 'refreshAllFrames'} , document.referrer);
})
.catch(utils.errorHandler);
In inject.js
- receiving the postMessage to refresh the iframes, it does so including the management.js file
In management.js
- After being refreshed it follows the same first step
- A vue component is created
- serviceworker != null now
- show all display frames (by sending postMessage to inject.js
- set IS_SERVICEWORKER_REFRESHED so that errors start to be logged
- if its the first time the HUD has been loaded and the welcome screen hasn't been hidden, show the welcome screen
- add postMessage listener from serviceworker and target page
- notify service worker vis postMessage that the target is loaded
- start heart beat loop from managemnt.js to serviceworker
parent.postMessage( {action: 'showAllDisplayFrames'} , document.referrer);
// temp time test
localforage.getItem('starttime')
.then(startT => {
let currentTime = new Date().getTime();
let diff = currentTime - parseInt(startT);
console.log('Time (ms) to load UI: ' + diff)
})
localforage.setItem(IS_SERVICEWORKER_REFRESHED, true);
localforage.getItem('is_first_load')
.then(isFirstLoad => {
localforage.setItem('is_first_load', false)
if (isFirstLoad && SHOW_WELCOME_SCREEN) {
parent.postMessage( {action: 'expandManagement'} , document.referrer);
app.showWelcomeScreen = true;
}
})
window.addEventListener('message', windowMessageListener)
navigator.serviceWorker.addEventListener('message', serviceWorkerMessageListener)
navigator.serviceWorker.controller.postMessage({action: 'targetload', tabId: tabId, targetUrl: context.url});