Skip to content

Commit 5a4b238

Browse files
authored
Merge pull request #39 from Unity-Lab-AI/develop
Security: Move CSP to HTTP headers and externalize inline scripts
2 parents 90c001a + 424d62f commit 5a4b238

File tree

17 files changed

+510
-523
lines changed

17 files changed

+510
-523
lines changed

_headers

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
X-XSS-Protection: 1; mode=block
55
Referrer-Policy: strict-origin-when-cross-origin
66
Permissions-Policy: camera=(), microphone=(self), geolocation=()
7+
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://unpkg.com; font-src 'self' https://fonts.gstatic.com data:; img-src 'self' data: blob: https://image.pollinations.ai https://*.gravatar.com https://avatars.githubusercontent.com; connect-src 'self' https://text.pollinations.ai https://image.pollinations.ai https://users.unityailab.com https://api.github.com https://contact.unityailab.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'

about/about-contact.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* about-contact.js - About page contact form handler
3+
*/
4+
5+
document.getElementById('aboutContactForm').addEventListener('submit', function(e) {
6+
e.preventDefault();
7+
8+
var name = document.getElementById('contactName').value;
9+
var email = document.getElementById('contactEmail').value;
10+
var reason = document.getElementById('contactReason').value;
11+
var source = document.getElementById('contactSource').value;
12+
var subject = document.getElementById('contactSubject').value;
13+
var message = document.getElementById('contactMessage').value;
14+
15+
// Build mailto URL
16+
var recipient = 'unityailabcontact@gmail.com';
17+
var mailtoSubject = '[' + reason + '] ' + subject;
18+
var mailtoBody = 'Name: ' + name + '\nEmail: ' + email + '\nReason for Contact: ' + reason + '\nHow they heard about us: ' + source + '\n\nMessage:\n' + message;
19+
20+
var mailtoURL = 'mailto:' + recipient + '?subject=' + encodeURIComponent(mailtoSubject) + '&body=' + encodeURIComponent(mailtoBody);
21+
22+
// Open mailto link
23+
window.location.href = mailtoURL;
24+
});

about/index.html

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
<head>
44
<!-- Meta Tags for Cross-Browser Compatibility -->
55
<meta charset="UTF-8">
6-
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://unpkg.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: blob: https://image.pollinations.ai https://*.gravatar.com https://avatars.githubusercontent.com; connect-src 'self' https://text.pollinations.ai https://image.pollinations.ai https://users.unityailab.com https://api.github.com; frame-ancestors 'none';">
76
<meta http-equiv="X-UA-Compatible" content="IE=edge">
87
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes, viewport-fit=cover">
98

@@ -657,34 +656,9 @@ <h6 class="footer-title">Connect</h6>
657656
<script src="about.js"></script>
658657

659658
<!-- About Contact Form Handler -->
660-
<script>
661-
document.getElementById('aboutContactForm').addEventListener('submit', function(e) {
662-
e.preventDefault();
663-
664-
const name = document.getElementById('contactName').value;
665-
const email = document.getElementById('contactEmail').value;
666-
const reason = document.getElementById('contactReason').value;
667-
const source = document.getElementById('contactSource').value;
668-
const subject = document.getElementById('contactSubject').value;
669-
const message = document.getElementById('contactMessage').value;
670-
671-
// Build mailto URL
672-
const recipient = 'unityailabcontact@gmail.com';
673-
const mailtoSubject = `[${reason}] ${subject}`;
674-
const mailtoBody = `Name: ${name}\nEmail: ${email}\nReason for Contact: ${reason}\nHow they heard about us: ${source}\n\nMessage:\n${message}`;
675-
676-
const mailtoURL = `mailto:${recipient}?subject=${encodeURIComponent(mailtoSubject)}&body=${encodeURIComponent(mailtoBody)}`;
677-
678-
// Open mailto link
679-
window.location.href = mailtoURL;
680-
});
681-
</script>
659+
<script src="about-contact.js"></script>
682660

683-
<!-- Remove FOUC class when page loads -->
684-
<script>
685-
window.addEventListener('load', function() {
686-
document.body.classList.add('loaded');
687-
});
688-
</script>
661+
<!-- Page Initialization -->
662+
<script src="../page-init.js"></script>
689663
</body>
690664
</html>

ai/ai-init.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* ai-init.js - AI page initialization and visitor tracking
3+
*/
4+
5+
// Load and auto-refresh visitor count for demo page
6+
document.addEventListener('DOMContentLoaded', function() {
7+
var countElement = document.getElementById('visitorCount');
8+
if (!countElement || typeof VisitorTracking === 'undefined') {
9+
return;
10+
}
11+
12+
var currentCount = null;
13+
14+
// Function to fetch and update visitor count
15+
async function updateVisitorCount() {
16+
try {
17+
var count = await VisitorTracking.getVisitorCount('demo');
18+
if (count !== null) {
19+
// Only update if count has changed or is first load
20+
if (currentCount !== count) {
21+
countElement.textContent = count;
22+
currentCount = count;
23+
console.log('Visitor count updated:', count);
24+
}
25+
} else {
26+
if (currentCount === null) {
27+
countElement.textContent = '0';
28+
currentCount = '0';
29+
}
30+
}
31+
} catch (error) {
32+
console.error('Failed to load visitor count:', error);
33+
if (currentCount === null) {
34+
countElement.textContent = '0';
35+
currentCount = '0';
36+
}
37+
}
38+
}
39+
40+
// Initial load
41+
updateVisitorCount();
42+
43+
// Auto-refresh every 5 minutes (300,000 ms)
44+
setInterval(updateVisitorCount, 300000);
45+
console.log('Visitor count auto-refresh enabled (every 5 minutes)');
46+
});

ai/demo/index.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
<html lang="en">
33
<head>
44
<meta charset="UTF-8">
5-
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://unpkg.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: blob: https://image.pollinations.ai https://*.gravatar.com https://avatars.githubusercontent.com; connect-src 'self' https://text.pollinations.ai https://image.pollinations.ai https://users.unityailab.com https://api.github.com; frame-ancestors 'none';">
65
<meta http-equiv="X-UA-Compatible" content="IE=edge">
76
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
87

ai/index.html

Lines changed: 4 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
<head>
44
<!-- Meta Tags for Cross-Browser Compatibility -->
55
<meta charset="UTF-8">
6-
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://unpkg.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: blob: https://image.pollinations.ai https://*.gravatar.com https://avatars.githubusercontent.com; connect-src 'self' https://text.pollinations.ai https://image.pollinations.ai https://users.unityailab.com https://api.github.com; frame-ancestors 'none';">
76
<meta http-equiv="X-UA-Compatible" content="IE=edge">
87
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes, viewport-fit=cover">
98

@@ -537,54 +536,10 @@ <h6 class="footer-title">Connect</h6>
537536
<!-- Visitor Tracking System -->
538537
<script src="../visitor-tracking.js"></script>
539538

540-
<!-- Remove FOUC class when page loads -->
541-
<script>
542-
window.addEventListener('load', function() {
543-
document.body.classList.add('loaded');
544-
});
545-
546-
// Load and auto-refresh visitor count for demo page
547-
document.addEventListener('DOMContentLoaded', function() {
548-
const countElement = document.getElementById('visitorCount');
549-
if (!countElement || typeof VisitorTracking === 'undefined') {
550-
return;
551-
}
552-
553-
let currentCount = null;
554-
555-
// Function to fetch and update visitor count
556-
async function updateVisitorCount() {
557-
try {
558-
const count = await VisitorTracking.getVisitorCount('demo');
559-
if (count !== null) {
560-
// Only update if count has changed or is first load
561-
if (currentCount !== count) {
562-
countElement.textContent = count;
563-
currentCount = count;
564-
console.log('Visitor count updated:', count);
565-
}
566-
} else {
567-
if (currentCount === null) {
568-
countElement.textContent = '0';
569-
currentCount = '0';
570-
}
571-
}
572-
} catch (error) {
573-
console.error('Failed to load visitor count:', error);
574-
if (currentCount === null) {
575-
countElement.textContent = '0';
576-
currentCount = '0';
577-
}
578-
}
579-
}
539+
<!-- AI Page Initialization -->
540+
<script src="ai-init.js"></script>
580541

581-
// Initial load
582-
updateVisitorCount();
583-
584-
// Auto-refresh every 5 minutes (300,000 ms)
585-
setInterval(updateVisitorCount, 300000);
586-
console.log('Visitor count auto-refresh enabled (every 5 minutes)');
587-
});
588-
</script>
542+
<!-- Page Initialization -->
543+
<script src="../page-init.js"></script>
589544
</body>
590545
</html>

apps/apps-init.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* apps-init.js - Apps page initialization
3+
*/
4+
5+
// Initialize AOS
6+
AOS.init({
7+
duration: 800,
8+
once: true,
9+
offset: 100
10+
});
11+
12+
// Navbar scroll effect
13+
window.addEventListener('scroll', function() {
14+
const navbar = document.querySelector('.navbar');
15+
if (window.scrollY > 50) {
16+
navbar.classList.add('scrolled');
17+
} else {
18+
navbar.classList.remove('scrolled');
19+
}
20+
});

apps/index.html

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
<head>
44
<!-- Meta Tags for Cross-Browser Compatibility -->
55
<meta charset="UTF-8">
6-
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://unpkg.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: blob: https://image.pollinations.ai https://*.gravatar.com https://avatars.githubusercontent.com; connect-src 'self' https://text.pollinations.ai https://image.pollinations.ai https://users.unityailab.com https://api.github.com; frame-ancestors 'none';">
76
<meta http-equiv="X-UA-Compatible" content="IE=edge">
87
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes, viewport-fit=cover">
98

@@ -293,30 +292,10 @@ <h6 class="footer-title">Connect</h6>
293292
<!-- Custom JavaScript -->
294293
<script src="../script.js"></script>
295294

296-
<script>
297-
// Initialize AOS
298-
AOS.init({
299-
duration: 800,
300-
once: true,
301-
offset: 100
302-
});
303-
304-
// Navbar scroll effect
305-
window.addEventListener('scroll', function() {
306-
const navbar = document.querySelector('.navbar');
307-
if (window.scrollY > 50) {
308-
navbar.classList.add('scrolled');
309-
} else {
310-
navbar.classList.remove('scrolled');
311-
}
312-
});
313-
</script>
314-
315-
<!-- Remove FOUC class when page loads -->
316-
<script>
317-
window.addEventListener('load', function() {
318-
document.body.classList.add('loaded');
319-
});
320-
</script>
295+
<!-- Apps Page Initialization -->
296+
<script src="apps-init.js"></script>
297+
298+
<!-- Page Initialization -->
299+
<script src="../page-init.js"></script>
321300
</body>
322301
</html>

apps/oldSiteProject/index.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdnjs.cloudflare.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: blob: https://image.pollinations.ai https://*.gravatar.com https://avatars.githubusercontent.com; connect-src 'self' https://text.pollinations.ai https://image.pollinations.ai https://users.unityailab.com https://api.github.com; frame-ancestors 'none';">
76
<title>Legacy Unity Chat - Unity AI Lab</title>
87

98
<!-- Bootstrap CSS -->

contact/contact-form.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* contact-form.js - Main contact page form handler
3+
*/
4+
5+
document.getElementById('mainContactForm').addEventListener('submit', async function(e) {
6+
e.preventDefault();
7+
8+
var submitBtn = document.querySelector('#mainContactForm button[type="submit"]');
9+
var originalBtnText = submitBtn.textContent;
10+
11+
// Disable button and show loading state
12+
submitBtn.disabled = true;
13+
submitBtn.textContent = 'Sending...';
14+
15+
var formData = {
16+
name: document.getElementById('contactName').value,
17+
email: document.getElementById('contactEmail').value,
18+
type: document.getElementById('contactType').value,
19+
service: document.getElementById('contactService').value,
20+
source: document.getElementById('contactSource').value,
21+
urgency: document.getElementById('contactUrgency').value,
22+
subject: document.getElementById('contactSubject').value,
23+
message: document.getElementById('contactMessage').value
24+
};
25+
26+
try {
27+
var response = await fetch('https://contact.unityailab.com/api/contact', {
28+
method: 'POST',
29+
headers: {
30+
'Content-Type': 'application/json'
31+
},
32+
body: JSON.stringify(formData)
33+
});
34+
35+
var result = await response.json();
36+
37+
if (response.ok && result.success) {
38+
// Success - show message and reset form
39+
alert('Thank you! Your message has been sent successfully. We\'ll get back to you soon.');
40+
document.getElementById('mainContactForm').reset();
41+
} else {
42+
// API error
43+
alert(result.error || 'Failed to send message. Please try again.');
44+
}
45+
} catch (error) {
46+
console.error('Contact form error:', error);
47+
// Network error - fall back to mailto
48+
var recipient = 'unityailabcontact@gmail.com';
49+
var mailtoSubject = '[' + formData.type + '] ' + formData.subject;
50+
var mailtoBody = 'Name: ' + formData.name + '\nEmail: ' + formData.email + '\nType of Inquiry: ' + formData.type + '\n';
51+
if (formData.service && formData.service !== 'Not Applicable') {
52+
mailtoBody += 'Service of Interest: ' + formData.service + '\n';
53+
}
54+
mailtoBody += 'How they heard about us: ' + formData.source + '\nPriority Level: ' + formData.urgency + '\n\nMessage:\n' + formData.message;
55+
var mailtoURL = 'mailto:' + recipient + '?subject=' + encodeURIComponent(mailtoSubject) + '&body=' + encodeURIComponent(mailtoBody);
56+
57+
if (confirm('Could not connect to server. Would you like to send via email instead?')) {
58+
window.location.href = mailtoURL;
59+
}
60+
} finally {
61+
// Re-enable button
62+
submitBtn.disabled = false;
63+
submitBtn.textContent = originalBtnText;
64+
}
65+
});

0 commit comments

Comments
 (0)