driftctl/pkg/cmd/scan/output/testdata/output.html

776 lines
24 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<title>driftctl Scan Report</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="shortcut icon"
href="https://raw.githubusercontent.com/cloudskiff/driftctl-docs/main/static/img/favicon.ico"/>
<style>html, body, div, span, h1, h2, p, pre, a, code, img, ul, li, form, label, table, tbody, thead, tr, th, td, header, section, button {
border: 0;
font: inherit;
margin: 0;
padding: 0;
vertical-align: baseline;
}
body {
background-color: #f7f7f9;
color: #1c1e21;
font-family: Helvetica, sans-serif;
padding-bottom: 50px;
}
form {
align-items: center;
display: flex;
flex-direction: column;
justify-content: center;
margin-bottom: 20px;
}
h1 {
font-size: 24px;
font-weight: 700;
margin-bottom: 5px;
}
h2 {
font-size: 20px;
font-weight: 700;
margin-bottom: 5px;
}
header {
align-items: center;
display: flex;
flex-direction: row;
height: 130px;
justify-content: center;
}
img {
margin-right: 20px;
}
input::placeholder {
color: #ccc;
opacity: 1;
}
main {
background-color: #fff;
border-top: 3px solid #71b2c3;
box-shadow: 0 0 5px #0000000a;
padding: 25px;
}
section {
background: #fff;
border-radius: 3px;
box-shadow: 0 0 5px #0000000a;
color: #747578;
display: flex;
flex-direction: column;
font-size: 15px;
margin-bottom: 20px;
padding: 15px;
}
select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: url(data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0Ljk1IDEwIj48ZGVmcz48c3R5bGU+LmNscy0xe2ZpbGw6I2ZmZjt9LmNscy0ye2ZpbGw6IzQ0NDt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPmFycm93czwvdGl0bGU+PHJlY3QgY2xhc3M9ImNscy0xIiB3aWR0aD0iNC45NSIgaGVpZ2h0PSIxMCIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtMiIgcG9pbnRzPSIxLjQxIDQuNjcgMi40OCAzLjE4IDMuNTQgNC42NyAxLjQxIDQuNjciLz48cG9seWdvbiBjbGFzcz0iY2xzLTIiIHBvaW50cz0iMy41NCA1LjMzIDIuNDggNi44MiAxLjQxIDUuMzMgMy41NCA1LjMzIi8+PC9zdmc+) no-repeat 97% 50%;
}
table {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
}
tbody, ul, .table-body {
border-left: 1px solid #ececec;
border-right: 1px solid #ececec;
border-top: 1px solid #ececec;
border-radius: 3px;
display: block;
}
ul {
list-style: none;
}
[role="tab"] {
background: transparent;
border-radius: 3px;
color: #747578;
cursor: pointer;
display: inline-block;
font-size: 16px;
margin: 4px;
padding: 10px 20px;
}
[role="tab"]:hover {
background-color: #f9f9f9;
}
[role="tab"][aria-selected="true"] {
background: #71b2c3;
color: #fff;
}
[role="tablist"] {
display: flex;
flex-direction: column;
}
[role="tabpanel"] {
-webkit-animation: fadein .8s;
animation: fadein .8s;
width: 100%;
}
[role="tabpanel"].is-hidden {
opacity: 0;
}
input[type="reset"] {
background-color: transparent;
border: none;
color: #5faabd;
cursor: pointer;
font-size: 14px;
height: 34px;
margin: 5px;
width: 100px;
}
input[type="search"], select {
border: 1px solid #ececec;
border-radius: 3px;
color: #6e7071;
font-size: 14px;
height: 36px;
margin: 5px;
max-width: 300px;
padding: 8px;
width: 100%;
}
.card {
align-items: center;
display: flex;
flex-direction: row;
justify-content: center;
margin: 5px 0;
}
.code-box {
background: #eee;
border-radius: 3px;
color: #747578;
display: flex;
margin-top: 20px;
}
.code-box-line {
line-height: 30px;
overflow-x: auto;
padding: 10px;
width: 100%;
}
.code-box-line-create {
background-color: #22863a1a;
border-radius: 3px;
color: #22863a;
padding: 3px;
}
.code-box-line-delete {
background-color: #bf404a17;
border-radius: 3px;
color: #bf404a;
padding: 3px;
text-decoration: line-through;
}
.congrats {
color: #4d9221;
text-align: center;
margin: 50px 0;
}
.container {
margin: auto;
max-width: 100%;
width: 1280px;
}
.empty-panel {
color: #747578;
display: flex;
flex-direction: row;
font-size: 20px;
font-weight: 600;
justify-content: center;
padding: 25px;
}
.fraction {
background: #e8e8e8;
border-radius: 3px;
color: #555;
font-size: 12px;
margin-left: 5px;
padding: 4px 5px;
}
.panels {
padding: 10px;
width: 100%;
}
.resource-item {
border-bottom: 1px solid #ececec;
color: #6e7071;
font-size: 14px;
padding: 15px;
}
.resource-item:hover {
background-color: #f9f9f9;
}
.row {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.strong {
color: #333;
font-weight: 700;
margin-left: 5px;
}
.table-header {
color: #747578;
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 10px;
}
.tabs-wrapper {
align-items: center;
display: flex;
flex-direction: column;
}
.visuallyhidden {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
.is-hidden {
display: none;
}
@-webkit-keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@media (min-width: 768px) {
form {
flex-direction: row;
}
header {
padding: 0 50px;
justify-content: flex-start;
}
section {
flex-direction: row;
justify-content: space-around;
}
[role="tab"] {
font-size: 18px;
}
[role="tablist"] {
flex-direction: row;
}
.card {
margin: 0;
}
.panels {
padding: 20px;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<img src="https://raw.githubusercontent.com/cloudskiff/driftctl-docs/main/static/img/driftctl_light.svg"
width="100px" height="81px" alt="driftctl logo"/>
<div>
<h1>Scan Report</h1>
<h2>Jun 10, 2021</h2>
<p>Scan Duration: 1m31s</p>
</div>
</header>
<section>
<div class="card">
<span>Total Resources:</span>
<span class="strong">13</span>
</div>
<div class="card">
<span>Coverage:</span>
<span class="strong">15%</span>
</div>
<div class="card">
<span>Managed:</span>
<span class="strong">15%</span>
<span class="fraction">2/13</span>
</div>
<div class="card">
<span>Unmanaged:</span>
<span class="strong">38%</span>
<span class="fraction">5/13</span>
</div>
<div class="card">
<span>Missing:</span>
<span class="strong">46%</span>
<span class="fraction">6/13</span>
</div>
</section>
<main>
<form role="search">
<label for="search" class="visuallyhidden">Search Resources:</label>
<input type="search" id="search" name="search" placeholder="Search Resources...">
<label for="select" class="visuallyhidden">Select Resource Type:</label>
<select id="select" name="select">
<option value="">Resource Type</option>
<option value="aws_unmanaged_resource">aws_unmanaged_resource</option>
<option value="aws_deleted_resource">aws_deleted_resource</option>
<option value="aws_diff_resource">aws_diff_resource</option>
</select>
<input type="reset" value="Reset Filters">
</form>
<div class="tabs-wrapper">
<div role="tablist" aria-label="List of tabs">
<button type="button" role="tab" aria-selected="true" aria-controls="unmanaged-tab" id="unmanaged">
Unmanaged Resources (<span data-count="resource-unmanaged">5</span>)
</button>
<button type="button" role="tab" aria-selected="false" aria-controls="changed-tab" id="changed"
tabindex="-1">
Changed Resources (<span data-count="resource-changed">2</span>)
</button>
<button type="button" role="tab" aria-selected="false" aria-controls="missing-tab" id="missing"
tabindex="-1">
Missing Resources (<span data-count="resource-deleted">6</span>)
</button>
<button type="button" role="tab" aria-selected="false" aria-controls="alerts-tab" id="alerts"
tabindex="-1">
Alerts (<span data-count="resource-alerts">0</span>)
</button>
</div>
<div class="panels">
<div tabindex="0" role="tabpanel" id="unmanaged-tab" aria-labelledby="unmanaged">
<table>
<thead>
<tr class="table-header">
<th>Resource ID</th>
<th>Resource Type</th>
</tr>
</thead>
<tbody>
<tr data-kind="resource-unmanaged" class="resource-item row">
<td data-type="resource-id">unmanaged-id-1</td>
<td data-type="resource-type">aws_unmanaged_resource</td>
</tr>
<tr data-kind="resource-unmanaged" class="resource-item row">
<td data-type="resource-id">unmanaged-id-2</td>
<td data-type="resource-type">aws_unmanaged_resource</td>
</tr>
<tr data-kind="resource-unmanaged" class="resource-item row">
<td data-type="resource-id">unmanaged-id-3</td>
<td data-type="resource-type">aws_unmanaged_resource</td>
</tr>
<tr data-kind="resource-unmanaged" class="resource-item row">
<td data-type="resource-id">unmanaged-id-4</td>
<td data-type="resource-type">aws_unmanaged_resource</td>
</tr>
<tr data-kind="resource-unmanaged" class="resource-item row">
<td data-type="resource-id">unmanaged-id-5</td>
<td data-type="resource-type">aws_unmanaged_resource</td>
</tr>
</tbody>
</table>
<div class="empty-panel is-hidden">
<p>No results matched your filters</p>
</div>
</div>
<div class="is-hidden" tabindex="0" role="tabpanel" id="changed-tab" aria-labelledby="changed">
<div role="table">
<div role="rowgroup">
<div role="row" class="table-header">
<span role="columnheader">Resource ID</span>
<span role="columnheader">Resource Type</span>
</div>
</div>
<div role="rowgroup" class="table-body">
<div role="row" data-kind="resource-changed" class="resource-item">
<div class="row">
<span role="cell" data-type="resource-id">diff-id-1</span>
<span role="cell" data-type="resource-type">aws_diff_resource</span>
</div>
<pre class="code-box">
<code class="code-box-line">&emsp;~ updated.field: <span class="code-box-line-delete">"foobar"</span> => <span class="code-box-line-create">"barfoo"</span><br>&emsp;+ new.field: <span class="code-box-line-create">"newValue"</span><br>&emsp;- a: <span class="code-box-line-delete">"oldValue"</span><br></code>
</pre>
</div>
<div role="row" data-kind="resource-changed" class="resource-item">
<div class="row">
<span role="cell" data-type="resource-id">diff-id-2</span>
<span role="cell" data-type="resource-type">aws_diff_resource</span>
</div>
<pre class="code-box">
<code class="code-box-line">&emsp;- path.to.fields.[0]: <span class="code-box-line-delete">"value"</span><br>&emsp;~ path.to.fields.[1]: <span class="code-box-line-delete">12</span> => <span class="code-box-line-create">"12"</span><br>&emsp;- group_ids: <span class="code-box-line-delete">["a071314398026"]</span><br>&emsp;~ Policies.[0]: <span class="code-box-line-delete">{
Id: "",
Type: ""
}</span> => <span class="code-box-line-create">{
Id: "093cd6ba-cf6d-4800-b252-6a50ca8903cd",
Type: "aws_iam_policy"
}</span><br>&emsp;+ Tags.[0].Name: <span class="code-box-line-create">"test"</span><br>&emsp;~ InstanceInitiatedShutdownBehavior: <span class="code-box-line-delete">""</span> => <span class="code-box-line-create">null</span><br></code>
</pre>
</div>
</div>
</div>
<div class="empty-panel is-hidden">
<p>No results matched your filters</p>
</div>
</div>
<div class="is-hidden" tabindex="0" role="tabpanel" id="missing-tab" aria-labelledby="missing">
<table>
<thead>
<tr class="table-header">
<th>Resource ID</th>
<th>Resource Type</th>
</tr>
</thead>
<tbody>
<tr data-kind="resource-deleted" class="resource-item row">
<td data-type="resource-id">deleted-id-1</td>
<td data-type="resource-type">aws_deleted_resource</td>
</tr>
<tr data-kind="resource-deleted" class="resource-item row">
<td data-type="resource-id">deleted-id-2</td>
<td data-type="resource-type">aws_deleted_resource</td>
</tr>
<tr data-kind="resource-deleted" class="resource-item row">
<td data-type="resource-id">deleted-id-3</td>
<td data-type="resource-type">aws_deleted_resource</td>
</tr>
<tr data-kind="resource-deleted" class="resource-item row">
<td data-type="resource-id">deleted-id-4</td>
<td data-type="resource-type">aws_deleted_resource</td>
</tr>
<tr data-kind="resource-deleted" class="resource-item row">
<td data-type="resource-id">deleted-id-5</td>
<td data-type="resource-type">aws_deleted_resource</td>
</tr>
<tr data-kind="resource-deleted" class="resource-item row">
<td data-type="resource-id">deleted-id-6</td>
<td data-type="resource-type">aws_deleted_resource</td>
</tr>
</tbody>
</table>
<div class="empty-panel is-hidden">
<p>No results matched your filters</p>
</div>
</div>
<div class="is-hidden" tabindex="0" role="tabpanel" id="alerts-tab" aria-labelledby="alerts">
<ul>
<li data-kind="resource-alerts" class="resource-item">
<span>Ignoring aws_vpc from drift calculation: Listing aws_vpc is forbidden.</span>
</li>
<li data-kind="resource-alerts" class="resource-item">
<span>Ignoring aws_sqs from drift calculation: Listing aws_sqs is forbidden.</span>
</li>
<li data-kind="resource-alerts" class="resource-item">
<span>Ignoring aws_sns from drift calculation: Listing aws_sns is forbidden.</span>
</li>
</ul>
<div class="empty-panel is-hidden">
<p>No results matched your filters</p>
</div>
</div>
</div>
</div>
</main>
</div>
<script>
const form = document.querySelector("form");
form.addEventListener("submit", (event) => event.preventDefault());
const resources = document.querySelectorAll("[data-kind^='resource-']");
const searchInput = document.querySelector('[type="search"]');
const selectBox = document.querySelector("select");
const resetButton = document.querySelector('[type="reset"]');
searchInput.addEventListener("input", filterResources);
selectBox.addEventListener("input", filterResources);
resetButton.addEventListener("click", resetResources);
function refreshPanel(count, el) {
const panel = document.getElementById(
el.parentElement.getAttribute("aria-controls")
);
if (!panel) {
return;
}
if (count === 0) {
panel.firstElementChild.classList.add("is-hidden");
panel.children[1].classList.remove("is-hidden");
} else {
panel.firstElementChild.classList.remove("is-hidden");
panel.children[1].classList.add("is-hidden");
}
}
function refreshCounters() {
const map = {
"[data-kind='resource-unmanaged']": "[data-count='resource-unmanaged']",
"[data-kind='resource-changed']": "[data-count='resource-changed']",
"[data-kind='resource-deleted']": "[data-count='resource-deleted']",
"[data-kind='resource-alerts']": "[data-count='resource-alerts']",
};
for (const key in map) {
const countEl = document.querySelector(map[key]);
if (countEl) {
const count = Array.from(document.querySelectorAll(key)).filter(
(el) => !el.classList.contains("is-hidden")
).length;
countEl.textContent = count;
refreshPanel(count, countEl);
}
}
}
function resourceIdContains(res, id) {
if (id === "") {
return true;
}
const el = res.querySelector("[data-type='resource-id']");
if (!el) {
return false;
}
return el.innerText.toLowerCase().includes(id.toLowerCase());
}
function resourceTypeEquals(res, type) {
if (type === "") {
return true;
}
const el = res.querySelector("[data-type='resource-type']");
if (!el) {
return false;
}
return el.innerText === type;
}
function filterResources() {
const id = searchInput.value;
const type = selectBox.value;
for (const res of resources) {
const matchId = resourceIdContains(res, id);
const matchType = resourceTypeEquals(res, type);
if (matchId && matchType) {
res.classList.remove("is-hidden");
} else {
res.classList.add("is-hidden");
}
}
refreshCounters();
}
function resetResources() {
for (const res of resources) {
res.classList.remove("is-hidden");
}
refreshCounters();
}
resetResources()
</script>
<script>
const tablist = document.querySelector('[role="tablist"]')
const tabs = document.querySelectorAll('[role="tab"]')
const panels = document.querySelectorAll('[role="tabpanel"]')
const keys = {left: 37, right: 39}
const direction = {37: -1, 39: 1}
for (let i = 0; i < tabs.length; ++i) {
addListeners(i)
}
function addListeners(index) {
tabs[index].addEventListener('click', clickEventListener)
tabs[index].addEventListener('keyup', keyupEventListener)
tabs[index].index = index
}
function clickEventListener(event) {
let tab
if (event.target.getAttribute("role") === "tab") {
tab = event.target
} else {
tab = event.target.closest("button")
}
const selected = tab.getAttribute("aria-selected")
if (selected === "false") {
activateTab(tab, false)
}
}
function keyupEventListener(event) {
const key = event.keyCode
switch (key) {
case keys.left:
case keys.right:
switchTabOnArrowPress(event)
break
}
}
function switchTabOnArrowPress(event) {
const pressed = event.keyCode
for (let x = 0; x < tabs.length; x++) {
tabs[x].addEventListener('focus', focusEventHandler)
}
if (direction[pressed]) {
const target = event.target
if (target.index !== undefined) {
if (tabs[target.index + direction[pressed]]) {
tabs[target.index + direction[pressed]].focus()
} else if (pressed === keys.left) {
tabs[tabs.length - 1].focus()
} else if (pressed === keys.right) {
tabs[0].focus()
}
}
}
}
function activateTab(tab, setFocus) {
setFocus = setFocus || true
deactivateTabs()
tab.removeAttribute('tabindex')
tab.setAttribute('aria-selected', 'true')
const controls = tab.getAttribute('aria-controls')
document.getElementById(controls).classList.remove('is-hidden')
if (setFocus) {
tab.focus()
}
}
function deactivateTabs() {
for (let t = 0; t < tabs.length; t++) {
tabs[t].setAttribute('tabindex', '-1')
tabs[t].setAttribute('aria-selected', 'false')
tabs[t].removeEventListener('focus', focusEventHandler)
}
for (let p = 0; p < panels.length; p++) {
panels[p].classList.add('is-hidden')
}
}
function focusEventHandler(event) {
const target = event.target
if (target === document.activeElement) {
activateTab(target, false)
}
}
</script>
</body>
</html>