react/packages/react-devtools/app.html
John Wilson 8657ad4278
Fix(React DevTools) - prevent phishing attacks (#19934)
When a link opens a URL in a new tab with target="_blank", it is very simple for the opened page to change the location of the original page because the JavaScript variable window.opener is not null and thus "window.opener.location can be set by the opened page. This exposes the user to very simple phishing attacks.
2020-10-01 16:25:38 +01:00

242 lines
6.7 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<title>React Developer Tools</title>
<meta charset="utf8" />
<style>
html {
height: 100%;
font-family: sans-serif;
}
body {
height: 100%;
margin: 0;
padding: 0;
background-color: #fff;
color: #777d88;
}
.container {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
overflow: auto;
}
p {
padding: 0;
margin: 0;
}
.input {
display: block;
font-weight: 100;
padding: 0 0.25rem;
border: 1px solid #aaa;
background-color: #fff;
color: #666;
}
.link {
color: #1478fa;
text-decoration: none;
}
.link:hover {
text-decoration: underline;
}
.waiting-header {
padding: 0.5rem;
display: inline-block;
position: absolute;
right: 0.5rem;
top: 0.5rem;
border-radius: 0.25rem;
background-color: rgba(0,1,2,.6);
color: white;
border: none;
font-weight: 100;
font-style: italic;
}
.boxes {
display: flex;
flex-direction: column;
align-items: stretch;
justify-content: center;
padding: 1rem;
}
.box {
text-align: center;
border-radius: 0.5rem;
background-color: #f7f7f7;
border: 1px solid #eee;
color: #777d88;
padding: 1rem;
margin-top: 1rem;
}
.box:first-of-type {
margin-top: 0;
}
.box-header {
text-align: center;
color: #5f6673;
font-size: 1.25rem;
margin-bottom: 0.5rem;
}
.box-content {
line-height: 1.5rem;
}
#loading-status {
text-align: center;
margin-top: 1rem;
}
.prompt,
.confirmation {
margin-bottom: 0.25rem;
}
.confirmation {
font-style: italic;
}
.hidden {
display: none;
}
</style>
</head>
<body>
<div id="container" class="container" style="-webkit-user-select: none; -webkit-app-region: drag;">
<div class="waiting-header">Waiting for React to connect…</div>
<div class="boxes" style="-webkit-app-region: none;">
<div class="box">
<div class="box-header">React Native</div>
<div class="box-content">
Open the <a
id="rn-help-link"
class="link"
target="_blank"
rel="noopener noreferrer"
href="https://reactnative.dev/docs/debugging#accessing-the-in-app-developer-menu"
>in-app developer menu</a> to connect.
</div>
</div>
<div class="box">
<div class="box-header">React DOM</div>
<div class="box-content">
<div id="box-content-prompt" class="prompt">
Add one of the following (click to copy):
</div>
<div id="box-content-confirmation" class="confirmation hidden">
Copied to clipboard.
</div>
<span class="input" contenteditable="true" id="localhost"></span>
<span class="input" contenteditable="true" id="byip"></span>
to the top of the page you want to debug,
<br />
<strong>before</strong> importing React DOM.
</div>
</div>
<div id="loading-status">Starting the server…</div>
</div>
</div>
<script>
const fs = require('fs');
let options;
let useHttps = false;
try {
if (process.env.KEY && process.env.CERT) {
options = {
key: fs.readFileSync(process.env.KEY),
cert: fs.readFileSync(process.env.CERT)
};
useHttps = true;
}
} catch (err) {
console.error('Failed to process SSL options - ', err);
options = undefined;
}
const {clipboard} = require("electron");
const host = process.env.HOST || 'localhost';
const protocol = useHttps ? 'https' : 'http';
const port = Number(process.env.PORT || 8097);
const localIp = require("ip").address();
const defaultPort = (port === 443 && useHttps) || (port === 80 && !useHttps);
const server = defaultPort ? `${protocol}://${host}` : `${protocol}://${host}:${port}`;
const serverIp = defaultPort ? `${protocol}://${localIp}` : `${protocol}://${localIp}:${port}`;
const $ = document.querySelector.bind(document);
const $promptDiv = $("#box-content-prompt");
const $confirmationDiv = $("#box-content-confirmation");
let timeoutID;
function selectAllAndCopy(event) {
const element = event.target;
if (window.getSelection) {
const selection = window.getSelection();
const range = document.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);
clipboard.writeText(event.target.textContent);
$promptDiv.classList.add('hidden');
$confirmationDiv.classList.remove('hidden');
if (timeoutID) {
clearTimeout(timeoutID);
}
timeoutID = setTimeout(() => {
$promptDiv.classList.remove('hidden');
$confirmationDiv.classList.add('hidden');
}, 1000);
}
}
const link = $('#rn-help-link');
link.addEventListener('click', event => {
event.preventDefault();
require('electron').shell.openExternal(link.href);
});
const $localhost = $("#localhost");
$localhost.innerText = `<script src="${server}"></` + 'script>';
$localhost.addEventListener('click', selectAllAndCopy);
$localhost.addEventListener('focus', selectAllAndCopy);
const $byIp = $("#byip");
$byIp.innerText = `<script src="${serverIp}"></` + 'script>';
$byIp.addEventListener('click', selectAllAndCopy);
$byIp.addEventListener('focus', selectAllAndCopy);
let devtools;
try {
devtools = require("react-devtools-core/standalone").default;
} catch (err) {
alert(
err.toString() +
"\n\nDid you run `yarn` and `yarn run build` in packages/react-devtools-core?"
);
}
window.devtools = devtools;
window.server = devtools
.setContentDOMNode(document.getElementById("container"))
.setStatusListener(function(status) {
const element = document.getElementById("loading-status");
if (element) {
element.innerText = status;
}
})
.startServer(port, host, options);
</script>
</body>
</html>