Skip to content

Commit d86f49c

Browse files
hackall360claude
andcommitted
Security: Add CSP headers, DOMPurify, and contact form API
- Add Content-Security-Policy header to oldSiteProject - Add DOMPurify for XSS protection in oldSiteProject - Add rel="noopener noreferrer" to external links in talkingWithUnity - Update contact form to use VPS API with mailto fallback - Add contact.unityailab.com to CSP connect-src 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 374d412 commit d86f49c

File tree

4 files changed

+64
-75
lines changed

4 files changed

+64
-75
lines changed

apps/oldSiteProject/index.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
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';">
67
<title>Legacy Unity Chat - Unity AI Lab</title>
78

89
<!-- Bootstrap CSS -->
@@ -445,6 +446,9 @@ <h3 class="modal-title">Voice Chat</h3>
445446
<!-- Bootstrap JS -->
446447
<script src="../../vendor/bootstrap/bootstrap.bundle.min.js"></script>
447448

449+
<!-- DOMPurify for XSS Protection -->
450+
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.0.6/dist/purify.min.js"></script>
451+
448452
<!-- Prism JS for code highlighting -->
449453
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js" defer></script>
450454
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-javascript.min.js" defer></script>

apps/talkingWithUnity/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,8 @@ <h1 id="landing-title">Let's make sure every light is green</h1>
190190
your helmet is on straight. When a light glows amber, read the friendly tip, fix it, then press "Check again."
191191
</p>
192192
<div class="landing-links">
193-
<a class="landing-link" href="https://unityailab.online" target="_blank" rel="noopener">Back to Unity AI Lab home</a>
194-
<a class="landing-link" href="https://github.com/Unity-Lab-AI/Talking-with-Unity.git" target="_blank" rel="noopener">View the project on GitHub</a>
193+
<a class="landing-link" href="https://unityailab.online" target="_blank" rel="noopener noreferrer">Back to Unity AI Lab home</a>
194+
<a class="landing-link" href="https://github.com/Unity-Lab-AI/Talking-with-Unity.git" target="_blank" rel="noopener noreferrer">View the project on GitHub</a>
195195
</div>
196196
</div>
197197

contact/index.html

Lines changed: 58 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
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';">
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://contact.unityailab.com https://api.github.com; frame-ancestors 'none';">
77
<meta http-equiv="X-UA-Compatible" content="IE=edge">
88
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes, viewport-fit=cover">
99

@@ -463,32 +463,66 @@ <h6 class="footer-title">Connect</h6>
463463

464464
<!-- Contact Form Handler -->
465465
<script>
466-
document.getElementById('mainContactForm').addEventListener('submit', function(e) {
466+
document.getElementById('mainContactForm').addEventListener('submit', async function(e) {
467467
e.preventDefault();
468468

469-
const name = document.getElementById('contactName').value;
470-
const email = document.getElementById('contactEmail').value;
471-
const type = document.getElementById('contactType').value;
472-
const service = document.getElementById('contactService').value;
473-
const source = document.getElementById('contactSource').value;
474-
const urgency = document.getElementById('contactUrgency').value;
475-
const subject = document.getElementById('contactSubject').value;
476-
const message = document.getElementById('contactMessage').value;
477-
478-
// Build mailto URL
479-
const recipient = 'unityailabcontact@gmail.com';
480-
const mailtoSubject = `[${type}] ${subject}`;
481-
482-
let mailtoBody = `Name: ${name}\nEmail: ${email}\nType of Inquiry: ${type}\n`;
483-
if (service && service !== 'Not Applicable') {
484-
mailtoBody += `Service of Interest: ${service}\n`;
485-
}
486-
mailtoBody += `How they heard about us: ${source}\nPriority Level: ${urgency}\n\nMessage:\n${message}`;
487-
488-
const mailtoURL = `mailto:${recipient}?subject=${encodeURIComponent(mailtoSubject)}&body=${encodeURIComponent(mailtoBody)}`;
469+
const submitBtn = document.querySelector('#mainContactForm button[type="submit"]');
470+
const originalBtnText = submitBtn.textContent;
471+
472+
// Disable button and show loading state
473+
submitBtn.disabled = true;
474+
submitBtn.textContent = 'Sending...';
475+
476+
const formData = {
477+
name: document.getElementById('contactName').value,
478+
email: document.getElementById('contactEmail').value,
479+
type: document.getElementById('contactType').value,
480+
service: document.getElementById('contactService').value,
481+
source: document.getElementById('contactSource').value,
482+
urgency: document.getElementById('contactUrgency').value,
483+
subject: document.getElementById('contactSubject').value,
484+
message: document.getElementById('contactMessage').value
485+
};
486+
487+
try {
488+
const response = await fetch('https://contact.unityailab.com/api/contact', {
489+
method: 'POST',
490+
headers: {
491+
'Content-Type': 'application/json'
492+
},
493+
body: JSON.stringify(formData)
494+
});
495+
496+
const result = await response.json();
497+
498+
if (response.ok && result.success) {
499+
// Success - show message and reset form
500+
alert('Thank you! Your message has been sent successfully. We\'ll get back to you soon.');
501+
document.getElementById('mainContactForm').reset();
502+
} else {
503+
// API error
504+
alert(result.error || 'Failed to send message. Please try again.');
505+
}
506+
} catch (error) {
507+
console.error('Contact form error:', error);
508+
// Network error - fall back to mailto
509+
const recipient = 'unityailabcontact@gmail.com';
510+
const mailtoSubject = `[${formData.type}] ${formData.subject}`;
511+
let mailtoBody = `Name: ${formData.name}\nEmail: ${formData.email}\nType of Inquiry: ${formData.type}\n`;
512+
if (formData.service && formData.service !== 'Not Applicable') {
513+
mailtoBody += `Service of Interest: ${formData.service}\n`;
514+
}
515+
mailtoBody += `How they heard about us: ${formData.source}\nPriority Level: ${formData.urgency}\n\nMessage:\n${formData.message}`;
516+
const mailtoURL = `mailto:${recipient}?subject=${encodeURIComponent(mailtoSubject)}&body=${encodeURIComponent(mailtoBody)}`;
489517

490-
// Open mailto link
491-
window.location.href = mailtoURL;
518+
if (confirm('Could not connect to server. Would you like to send via email instead?')) {
519+
window.location.href = mailtoURL;
520+
}
521+
} finally {
522+
// Re-enable button
523+
submitBtn.disabled = false;
524+
submitBtn.textContent = originalBtnText;
525+
}
492526
});
493527
</script>
494528

package-lock.json

Lines changed: 0 additions & 49 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)