From 6dd72aaed4d2ffa9f2a7aabe5a22ba17aeb82e60 Mon Sep 17 00:00:00 2001
From: Xorlent <94985297+Xorlent@users.noreply.github.com>
Date: Fri, 9 May 2025 07:02:03 -0700
Subject: [PATCH 1/5] Create README.md
---
website/README.md | 1 +
1 file changed, 1 insertion(+)
create mode 100644 website/README.md
diff --git a/website/README.md b/website/README.md
new file mode 100644
index 0000000..9c558e3
--- /dev/null
+++ b/website/README.md
@@ -0,0 +1 @@
+.
From 679a252e6ffa739e93f58c06943556d5a84b426f Mon Sep 17 00:00:00 2001
From: Xorlent <94985297+Xorlent@users.noreply.github.com>
Date: Fri, 9 May 2025 07:12:21 -0700
Subject: [PATCH 2/5] Static site draft for security.wdes.eu
Currently untested
---
website/index.html | 52 ++++++
website/script.js | 442 +++++++++++++++++++++++++++++++++++++++++++++
website/styles.css | 133 ++++++++++++++
3 files changed, 627 insertions(+)
create mode 100644 website/index.html
create mode 100644 website/script.js
create mode 100644 website/styles.css
diff --git a/website/index.html b/website/index.html
new file mode 100644
index 0000000..7c5998a
--- /dev/null
+++ b/website/index.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+ Security Tools - WDES
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/website/script.js b/website/script.js
new file mode 100644
index 0000000..ed06f25
--- /dev/null
+++ b/website/script.js
@@ -0,0 +1,442 @@
+document.addEventListener('DOMContentLoaded', () => {
+ // Smooth scrolling for navigation links
+ document.querySelectorAll('a[href^="#"]').forEach(anchor => {
+ anchor.addEventListener('click', function (e) {
+ e.preventDefault();
+ document.querySelector(this.getAttribute('href')).scrollIntoView({
+ behavior: 'smooth'
+ });
+ });
+ });
+
+ // Tool button click handlers
+ const toolButtons = document.querySelectorAll('.tool-button');
+ toolButtons.forEach(button => {
+ button.addEventListener('click', function() {
+ const toolName = this.parentElement.querySelector('h3').textContent;
+ handleToolClick(toolName);
+ });
+ });
+
+ // API Key Button
+ document.getElementById('apiKeyBtn').addEventListener('click', () => {
+ const modal = createModal('Get API Key', `
+
+ `);
+
+ document.getElementById('apiKeyForm').addEventListener('submit', (e) => {
+ e.preventDefault();
+ const form = e.target;
+ const formData = new FormData(form);
+
+ // Submit the form data to /register
+ fetch('/register', {
+ method: 'POST',
+ body: formData
+ })
+ .then(response => response.json())
+ .then(data => {
+ alert('API key request submitted. You will receive your key via email.');
+ modal.remove();
+ })
+ .catch(error => {
+ alert('Error submitting request. Please try again.');
+ console.error('Error:', error);
+ });
+ });
+ });
+
+ // Report IP Button
+ document.getElementById('reportIpBtn').addEventListener('click', () => {
+ const modal = createModal('Report Abusive IP', `
+
+ `);
+
+ const ipInput = modal.querySelector('#ip');
+ ipInput.addEventListener('input', (e) => validatePublicIP(e));
+
+ modal.querySelector('#reportIpForm').addEventListener('submit', (e) => {
+ e.preventDefault();
+ if (!validatePublicIP({ target: ipInput })) {
+ return;
+ }
+ const form = e.target;
+ const formData = new FormData(form);
+
+ // Submit the form data to /report
+ fetch('/report', {
+ method: 'POST',
+ body: formData
+ })
+ .then(response => response.json())
+ .then(data => {
+ alert('IP report submitted. Thank you for your contribution!');
+ modal.remove();
+ })
+ .catch(error => {
+ alert('Error submitting report. Please try again.');
+ console.error('Error:', error);
+ });
+ });
+ });
+
+ // Scan Button
+ document.getElementById('scanBtn').addEventListener('click', () => {
+ const modal = createModal('Security Scan', `
+
+ `);
+
+ const targetInput = modal.querySelector('#target');
+ targetInput.addEventListener('input', (e) => validateCIDR(e));
+
+ modal.querySelector('#scanForm').addEventListener('submit', (e) => {
+ e.preventDefault();
+ if (!validateCIDR({ target: targetInput })) {
+ return;
+ }
+ const form = e.target;
+ const formData = new FormData(form);
+
+ // Submit the form data to /scan
+ fetch('/scan', {
+ method: 'POST',
+ body: formData
+ })
+ .then(response => response.json())
+ .then(data => {
+ alert('Target added to scan queue. Results will be available shortly.');
+ modal.remove();
+ })
+ .catch(error => {
+ alert('Error submitting scan request. Please try again.');
+ console.error('Error:', error);
+ });
+ });
+ });
+});
+
+function validatePublicIP(event) {
+ const input = event.target;
+ const value = input.value.trim();
+
+ // Check if it's IPv4
+ if (isIPv4(value)) {
+ if (isPrivateIPv4(value)) {
+ input.setCustomValidity('Private IPv4 addresses are not allowed');
+ return false;
+ }
+ if (isReservedIPv4(value)) {
+ input.setCustomValidity('Reserved IPv4 addresses are not allowed');
+ return false;
+ }
+ if (isLoopbackIPv4(value)) {
+ input.setCustomValidity('Loopback IPv4 addresses are not allowed');
+ return false;
+ }
+ input.setCustomValidity('');
+ return true;
+ }
+
+ // Check if it's IPv6
+ if (isIPv6(value)) {
+ if (isPrivateIPv6(value)) {
+ input.setCustomValidity('Private IPv6 addresses are not allowed');
+ return false;
+ }
+ if (isReservedIPv6(value)) {
+ input.setCustomValidity('Reserved IPv6 addresses are not allowed');
+ return false;
+ }
+ if (isLoopbackIPv6(value)) {
+ input.setCustomValidity('Loopback IPv6 addresses are not allowed');
+ return false;
+ }
+ input.setCustomValidity('');
+ return true;
+ }
+
+ input.setCustomValidity('Please enter a valid public IPv4 or IPv6 address');
+ return false;
+}
+
+function isIPv4(ip) {
+ const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
+ if (!ipv4Regex.test(ip)) return false;
+
+ const parts = ip.split('.');
+ return parts.every(part => {
+ const num = parseInt(part);
+ return num >= 0 && num <= 255;
+ });
+}
+
+function isIPv6(ip) {
+ const ipv6Regex = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^([0-9a-fA-F]{1,4}:){1,7}:|^([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}$|^([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}$|^([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}$|^([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}$|^([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}$|^[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})$|^:((:[0-9a-fA-F]{1,4}){1,7}|:)$/;
+ return ipv6Regex.test(ip);
+}
+
+function isPrivateIPv4(ip) {
+ const parts = ip.split('.').map(Number);
+
+ // Check for private IP ranges
+ return (
+ // 10.0.0.0/8
+ (parts[0] === 10) ||
+ // 172.16.0.0/12
+ (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) ||
+ // 192.168.0.0/16
+ (parts[0] === 192 && parts[1] === 168) ||
+ // 169.254.0.0/16 (Link-local)
+ (parts[0] === 169 && parts[1] === 254)
+ );
+}
+
+function isReservedIPv4(ip) {
+ const parts = ip.split('.').map(Number);
+
+ return (
+ // 0.0.0.0/8
+ (parts[0] === 0) ||
+ // 127.0.0.0/8 (Loopback)
+ (parts[0] === 127) ||
+ // 224.0.0.0/4 (Multicast)
+ (parts[0] >= 224 && parts[0] <= 239) ||
+ // 240.0.0.0/4 (Reserved)
+ (parts[0] >= 240) ||
+ // 100.64.0.0/10 (Carrier-grade NAT)
+ (parts[0] === 100 && parts[1] >= 64 && parts[1] <= 127)
+ );
+}
+
+function isLoopbackIPv4(ip) {
+ const parts = ip.split('.').map(Number);
+ return parts[0] === 127;
+}
+
+function isPrivateIPv6(ip) {
+ return (
+ // fc00::/7 (Unique Local Addresses)
+ ip.startsWith('fc') ||
+ ip.startsWith('fd') ||
+ // fe80::/10 (Link-local)
+ ip.startsWith('fe8') ||
+ ip.startsWith('fe9') ||
+ ip.startsWith('fea') ||
+ ip.startsWith('feb')
+ );
+}
+
+function isReservedIPv6(ip) {
+ return (
+ // ::/128 (Unspecified)
+ ip === '::' ||
+ // ff00::/8 (Multicast)
+ ip.startsWith('ff') ||
+ // 2001:db8::/32 (Documentation)
+ ip.startsWith('2001:db8')
+ );
+}
+
+function isLoopbackIPv6(ip) {
+ return ip === '::1';
+}
+
+function validateCIDR(event) {
+ const input = event.target;
+ const value = input.value.trim();
+
+ // Check if it's IPv4 with optional CIDR
+ if (isIPv4(value.split('/')[0])) {
+ const ip = value.split('/')[0];
+ if (isPrivateIPv4(ip)) {
+ input.setCustomValidity('Private IPv4 addresses are not allowed');
+ return false;
+ }
+ if (isReservedIPv4(ip)) {
+ input.setCustomValidity('Reserved IPv4 addresses are not allowed');
+ return false;
+ }
+ if (isLoopbackIPv4(ip)) {
+ input.setCustomValidity('Loopback IPv4 addresses are not allowed');
+ return false;
+ }
+
+ // Validate CIDR if present
+ if (value.includes('/')) {
+ const cidr = parseInt(value.split('/')[1]);
+ if (cidr < 0 || cidr > 32) {
+ input.setCustomValidity('IPv4 CIDR must be between 0 and 32');
+ return false;
+ }
+ }
+
+ input.setCustomValidity('');
+ return true;
+ }
+
+ // Check if it's IPv6 with optional CIDR
+ if (isIPv6(value.split('/')[0])) {
+ const ip = value.split('/')[0];
+ if (isPrivateIPv6(ip)) {
+ input.setCustomValidity('Private IPv6 addresses are not allowed');
+ return false;
+ }
+ if (isReservedIPv6(ip)) {
+ input.setCustomValidity('Reserved IPv6 addresses are not allowed');
+ return false;
+ }
+ if (isLoopbackIPv6(ip)) {
+ input.setCustomValidity('Loopback IPv6 addresses are not allowed');
+ return false;
+ }
+
+ // Validate CIDR if present
+ if (value.includes('/')) {
+ const cidr = parseInt(value.split('/')[1]);
+ if (cidr < 0 || cidr > 128) {
+ input.setCustomValidity('IPv6 CIDR must be between 0 and 128');
+ return false;
+ }
+ }
+
+ input.setCustomValidity('');
+ return true;
+ }
+
+ input.setCustomValidity('Please enter a valid public IPv4 or IPv6 address with optional CIDR notation');
+ return false;
+}
+
+function createModal(title, content) {
+ const modal = document.createElement('div');
+ modal.style.cssText = `
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background: white;
+ padding: 2rem;
+ border-radius: 10px;
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
+ z-index: 1000;
+ min-width: 300px;
+ max-width: 500px;
+ `;
+
+ modal.innerHTML = `
+ ${title}
+ ${content}
+
+ `;
+
+ // Add styles for form elements
+ const style = document.createElement('style');
+ style.textContent = `
+ .form-group {
+ margin-bottom: 1rem;
+ }
+ .form-group label {
+ display: block;
+ margin-bottom: 0.5rem;
+ color: #2c3e50;
+ }
+ .form-group input,
+ .form-group textarea,
+ .form-group select {
+ width: 100%;
+ padding: 0.5rem;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ font-size: 1rem;
+ }
+ .form-group textarea {
+ height: 100px;
+ resize: vertical;
+ }
+ .submit-btn {
+ background-color: #3498db;
+ color: white;
+ border: none;
+ padding: 0.8rem 1.5rem;
+ border-radius: 5px;
+ cursor: pointer;
+ width: 100%;
+ font-size: 1rem;
+ margin-top: 1rem;
+ }
+ .submit-btn:hover {
+ background-color: #2980b9;
+ }
+ .help-text {
+ display: block;
+ margin-top: 0.25rem;
+ color: #666;
+ font-size: 0.875rem;
+ }
+ `;
+ document.head.appendChild(style);
+
+ document.body.appendChild(modal);
+ return modal;
+}
+
+// Add scroll-based animations
+window.addEventListener('scroll', () => {
+ const sections = document.querySelectorAll('section');
+ sections.forEach(section => {
+ const sectionTop = section.getBoundingClientRect().top;
+ const windowHeight = window.innerHeight;
+
+ if (sectionTop < windowHeight * 0.75) {
+ section.style.opacity = '1';
+ section.style.transform = 'translateY(0)';
+ }
+ });
+});
\ No newline at end of file
diff --git a/website/styles.css b/website/styles.css
new file mode 100644
index 0000000..90a54ed
--- /dev/null
+++ b/website/styles.css
@@ -0,0 +1,133 @@
+:root {
+ --primary-color: #2c3e50;
+ --secondary-color: #3498db;
+ --background-color: #f8f9fa;
+ --text-color: #2c3e50;
+ --card-background: #ffffff;
+}
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ line-height: 1.6;
+ color: var(--text-color);
+ background-color: var(--background-color);
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+}
+
+/* Header and Navigation */
+header {
+ background-color: var(--primary-color);
+ padding: 1rem 0;
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
+}
+
+nav {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 0 2rem;
+}
+
+.logo h1 {
+ color: white;
+ font-size: 1.5rem;
+}
+
+/* Main Content */
+main {
+ flex: 1;
+ padding: 2rem;
+}
+
+.tools-section {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 2rem;
+}
+
+.tools-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: 2rem;
+ margin-top: 2rem;
+}
+
+.tool-card {
+ background: var(--card-background);
+ padding: 2rem;
+ border-radius: 10px;
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
+ text-align: center;
+ transition: transform 0.3s ease;
+}
+
+.tool-card:hover {
+ transform: translateY(-5px);
+}
+
+.tool-card i {
+ font-size: 2.5rem;
+ color: var(--secondary-color);
+ margin-bottom: 1rem;
+}
+
+.tool-card h3 {
+ margin-bottom: 1rem;
+ color: var(--primary-color);
+}
+
+.tool-card p {
+ margin-bottom: 1.5rem;
+ color: #666;
+}
+
+.tool-button {
+ background-color: var(--secondary-color);
+ color: white;
+ border: none;
+ padding: 0.8rem 1.5rem;
+ border-radius: 5px;
+ cursor: pointer;
+ transition: background-color 0.3s ease;
+ font-size: 1rem;
+ width: 100%;
+ max-width: 200px;
+}
+
+.tool-button:hover {
+ background-color: #2980b9;
+}
+
+/* Footer */
+footer {
+ background-color: var(--primary-color);
+ color: white;
+ padding: 1rem 0;
+ margin-top: auto;
+}
+
+.footer-content {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 0 2rem;
+ text-align: center;
+}
+
+/* Responsive Design */
+@media (max-width: 768px) {
+ .tools-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .tool-card {
+ max-width: 400px;
+ margin: 0 auto;
+ }
+}
\ No newline at end of file
From a672590f68e4a1846e66cfa31e9ad2723ef9e745 Mon Sep 17 00:00:00 2001
From: Xorlent <94985297+Xorlent@users.noreply.github.com>
Date: Fri, 9 May 2025 07:21:25 -0700
Subject: [PATCH 3/5] Update README.md
---
website/README.md | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/website/README.md b/website/README.md
index 9c558e3..d357a41 100644
--- a/website/README.md
+++ b/website/README.md
@@ -1 +1,8 @@
-.
+### Static site with responsive design
+1. Adds an optional "purpose" field to API key /register POST action
+2. Adds an optional "reason" field to the /report POST action
+3. Allows for CIDR ranges for /report
+4. Adds "scanType" field with default of "snow" to the /scan POST action, allowing for other/add'l scanners in the future
+5. Validates all IPv4 and IPv6 values to ensure they are formatted correctly and not reserved, loopback, etc (applies to /report and /scan)
+
+_Currently untested_
From 143eb65b4e6604d7b0a470170a460bb37ea964d9 Mon Sep 17 00:00:00 2001
From: Xorlent <94985297+Xorlent@users.noreply.github.com>
Date: Fri, 9 May 2025 07:22:06 -0700
Subject: [PATCH 4/5] Update README.md
---
website/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/website/README.md b/website/README.md
index d357a41..edeb38e 100644
--- a/website/README.md
+++ b/website/README.md
@@ -1,7 +1,7 @@
### Static site with responsive design
1. Adds an optional "purpose" field to API key /register POST action
2. Adds an optional "reason" field to the /report POST action
-3. Allows for CIDR ranges for /report
+3. Allows for CIDR ranges for /report and /scan
4. Adds "scanType" field with default of "snow" to the /scan POST action, allowing for other/add'l scanners in the future
5. Validates all IPv4 and IPv6 values to ensure they are formatted correctly and not reserved, loopback, etc (applies to /report and /scan)
From 16a5209569675ad36a3ad2178e81ec060bc3ac44 Mon Sep 17 00:00:00 2001
From: Xorlent <94985297+Xorlent@users.noreply.github.com>
Date: Fri, 9 May 2025 07:36:25 -0700
Subject: [PATCH 5/5] Update script.js
---
website/script.js | 11 +----------
1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/website/script.js b/website/script.js
index ed06f25..b8c899e 100644
--- a/website/script.js
+++ b/website/script.js
@@ -9,15 +9,6 @@ document.addEventListener('DOMContentLoaded', () => {
});
});
- // Tool button click handlers
- const toolButtons = document.querySelectorAll('.tool-button');
- toolButtons.forEach(button => {
- button.addEventListener('click', function() {
- const toolName = this.parentElement.querySelector('h3').textContent;
- handleToolClick(toolName);
- });
- });
-
// API Key Button
document.getElementById('apiKeyBtn').addEventListener('click', () => {
const modal = createModal('Get API Key', `
@@ -439,4 +430,4 @@ window.addEventListener('scroll', () => {
section.style.transform = 'translateY(0)';
}
});
-});
\ No newline at end of file
+});