Skip to content

Commit f0f19d8

Browse files
authored
Merge pull request #6 from markcoleman/copilot/fix-5d984452-2f77-4a16-bf9a-fa064fe67b9d
2 parents 6278ad5 + 9eabae0 commit f0f19d8

19 files changed

+75427
-1
lines changed

README.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,51 @@ A modern Umbraco CMS application with Docker Compose, SQL Server, and devcontain
8282
```
8383
├── .devcontainer/ # VS Code dev container configuration
8484
├── .github/ # GitHub Actions workflows & Dependabot
85+
├── docs/ # Documentation
8586
├── src/UmbracoWeb/ # Main application source
8687
│ ├── UmbracoWeb/ # Umbraco CMS project
87-
│ └── Dockerfile # Production container image
88+
│ │ ├── Controllers/ # API controllers
89+
│ │ ├── Services/ # Business logic services
90+
│ │ ├── Models/ # Data models and DTOs
91+
│ │ └── Views/Partials/ # Umbraco partial views
92+
│ ├── UmbracoWeb.Tests/ # Unit and integration tests
93+
│ └── Dockerfile # Production container image
8894
├── logs/ # Application logs (gitignored)
8995
├── docker-compose.yml # Multi-service development stack
9096
├── .env.example # Environment variables template
9197
└── README.md # This file
9298
```
9399

100+
## 🛡️ Security Components
101+
102+
### PhishLabs Incident Reporter
103+
104+
The application includes a PhishLabs integration for reporting suspicious URLs:
105+
106+
- **Member-friendly UI**: Simple form for reporting phishing links
107+
- **Server-side proxy**: Secure API integration with PhishLabs
108+
- **Real-time validation**: Client and server-side input validation
109+
- **Accessibility**: Full ARIA support and keyboard navigation
110+
- **Security**: CSRF protection, rate limiting, input sanitization
111+
112+
**Usage in templates:**
113+
```html
114+
@Html.Partial("PhishLabsIncidentReporter")
115+
```
116+
117+
**Configuration required:**
118+
```json
119+
{
120+
"PhishLabs": {
121+
"ApiBaseUrl": "https://api.phishlabs.com",
122+
"ApiKey": "your-api-key",
123+
"ServicePath": "/incidents/your-service"
124+
}
125+
}
126+
```
127+
128+
See [PhishLabs Documentation](docs/PhishLabsIncidentReporter.md) for complete setup and usage details.
129+
94130
## ⚙️ Configuration
95131

96132
### Environment Variables

demo/PhishLabsDemo.html

Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>PhishLabs Incident Reporter - Demo</title>
7+
<style>
8+
body {
9+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
10+
line-height: 1.6;
11+
margin: 0;
12+
padding: 2rem;
13+
background-color: #f5f5f5;
14+
}
15+
16+
.container {
17+
max-width: 1200px;
18+
margin: 0 auto;
19+
}
20+
21+
.header {
22+
text-align: center;
23+
margin-bottom: 3rem;
24+
}
25+
26+
.header h1 {
27+
color: #333;
28+
margin-bottom: 0.5rem;
29+
}
30+
31+
.header p {
32+
color: #666;
33+
font-size: 1.1rem;
34+
}
35+
36+
.demo-section {
37+
background: white;
38+
border-radius: 8px;
39+
padding: 2rem;
40+
margin-bottom: 2rem;
41+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
42+
}
43+
44+
.features {
45+
display: grid;
46+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
47+
gap: 2rem;
48+
margin: 2rem 0;
49+
}
50+
51+
.feature {
52+
padding: 1.5rem;
53+
background: #f8f9fa;
54+
border-radius: 6px;
55+
border-left: 4px solid #007bff;
56+
}
57+
58+
.feature h3 {
59+
margin-top: 0;
60+
color: #333;
61+
}
62+
63+
.feature ul {
64+
margin: 0;
65+
padding-left: 1.2rem;
66+
}
67+
68+
.feature li {
69+
margin-bottom: 0.5rem;
70+
color: #555;
71+
}
72+
73+
/* Copy the PhishLabs component styles from the partial view */
74+
.phishlabs-incident-reporter {
75+
max-width: 600px;
76+
margin: 0 auto;
77+
padding: 1.5rem;
78+
background: #f8f9fa;
79+
border-radius: 8px;
80+
border: 1px solid #dee2e6;
81+
}
82+
83+
.phishlabs-header h3 {
84+
color: #343a40;
85+
margin-bottom: 0.5rem;
86+
font-size: 1.5rem;
87+
font-weight: 600;
88+
}
89+
90+
.phishlabs-description {
91+
color: #6c757d;
92+
margin-bottom: 1.5rem;
93+
line-height: 1.5;
94+
}
95+
96+
.phishlabs-form .form-group {
97+
margin-bottom: 1.5rem;
98+
}
99+
100+
.phishlabs-form .form-label {
101+
font-weight: 500;
102+
margin-bottom: 0.5rem;
103+
display: block;
104+
}
105+
106+
.required {
107+
color: #dc3545;
108+
}
109+
110+
.form-control {
111+
width: 100%;
112+
padding: 0.75rem;
113+
border: 1px solid #ced4da;
114+
border-radius: 4px;
115+
font-size: 1rem;
116+
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
117+
}
118+
119+
.form-control:focus {
120+
border-color: #80bdff;
121+
outline: 0;
122+
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
123+
}
124+
125+
.form-text {
126+
font-size: 0.875rem;
127+
color: #6c757d;
128+
margin-top: 0.25rem;
129+
}
130+
131+
.char-count {
132+
float: right;
133+
}
134+
135+
.form-actions {
136+
text-align: center;
137+
margin-top: 2rem;
138+
}
139+
140+
.btn {
141+
padding: 0.75rem 1.5rem;
142+
border: none;
143+
border-radius: 4px;
144+
font-size: 1rem;
145+
font-weight: 500;
146+
cursor: pointer;
147+
transition: all 0.15s ease-in-out;
148+
}
149+
150+
.btn-primary {
151+
background-color: #007bff;
152+
color: white;
153+
}
154+
155+
.btn-primary:hover:not(:disabled) {
156+
background-color: #0056b3;
157+
}
158+
159+
.btn:disabled {
160+
opacity: 0.65;
161+
cursor: not-allowed;
162+
}
163+
164+
.demo-note {
165+
background: #fff3cd;
166+
border: 1px solid #ffeaa7;
167+
border-radius: 4px;
168+
padding: 1rem;
169+
margin-top: 1rem;
170+
}
171+
172+
.demo-note strong {
173+
color: #856404;
174+
}
175+
</style>
176+
</head>
177+
<body>
178+
<div class="container">
179+
<div class="header">
180+
<h1>PhishLabs Incident Reporter</h1>
181+
<p>A secure, accessible component for reporting suspicious URLs to PhishLabs</p>
182+
</div>
183+
184+
<div class="demo-section">
185+
<h2>Component Demo</h2>
186+
<p>This is how the PhishLabs Incident Reporter component appears to users:</p>
187+
188+
<div class="phishlabs-incident-reporter">
189+
<div class="phishlabs-header">
190+
<h3>Report a suspicious link</h3>
191+
<p class="phishlabs-description">
192+
Paste a link you think is unsafe and our security team will review it.
193+
We'll protect your info and let you know if action is needed.
194+
</p>
195+
</div>
196+
197+
<form class="phishlabs-form">
198+
<div class="form-group">
199+
<label for="demo-url" class="form-label">
200+
Suspicious URL <span class="required" aria-label="required">*</span>
201+
</label>
202+
<input
203+
type="url"
204+
id="demo-url"
205+
name="url"
206+
class="form-control"
207+
placeholder="https://example.com/suspicious-link"
208+
required
209+
maxlength="2048"
210+
/>
211+
<div class="form-text">
212+
Paste the complete URL starting with http:// or https://
213+
</div>
214+
</div>
215+
216+
<div class="form-group">
217+
<label for="demo-details" class="form-label">
218+
Additional details (optional)
219+
</label>
220+
<textarea
221+
id="demo-details"
222+
name="details"
223+
class="form-control"
224+
rows="3"
225+
placeholder="Optional: Describe how you encountered this link or any additional context..."
226+
maxlength="1000"
227+
></textarea>
228+
<div class="form-text">
229+
<span class="char-count">0 / 1000 characters</span>
230+
</div>
231+
</div>
232+
233+
<div class="form-actions">
234+
<button type="button" class="btn btn-primary" onclick="showDemo()">
235+
Report Link
236+
</button>
237+
</div>
238+
</form>
239+
240+
<div class="demo-note" style="display: none;" id="demo-response">
241+
<strong>Demo Mode:</strong> This is a demonstration. In the actual implementation,
242+
this would submit to the PhishLabs API via the secure server-side proxy.
243+
</div>
244+
</div>
245+
</div>
246+
247+
<div class="demo-section">
248+
<h2>Key Features</h2>
249+
<div class="features">
250+
<div class="feature">
251+
<h3>🛡️ Security First</h3>
252+
<ul>
253+
<li>CSRF protection</li>
254+
<li>Input sanitization</li>
255+
<li>Rate limiting</li>
256+
<li>Secure API proxy</li>
257+
<li>No client-side secrets</li>
258+
</ul>
259+
</div>
260+
261+
<div class="feature">
262+
<h3>♿ Accessibility</h3>
263+
<ul>
264+
<li>ARIA live regions</li>
265+
<li>Keyboard navigation</li>
266+
<li>Screen reader support</li>
267+
<li>High contrast support</li>
268+
<li>Focus management</li>
269+
</ul>
270+
</div>
271+
272+
<div class="feature">
273+
<h3>📱 User Experience</h3>
274+
<ul>
275+
<li>Responsive design</li>
276+
<li>Real-time validation</li>
277+
<li>Character counting</li>
278+
<li>Clear error messages</li>
279+
<li>Loading indicators</li>
280+
</ul>
281+
</div>
282+
283+
<div class="feature">
284+
<h3>🔍 Monitoring</h3>
285+
<ul>
286+
<li>Structured logging</li>
287+
<li>Correlation IDs</li>
288+
<li>Performance metrics</li>
289+
<li>Error tracking</li>
290+
<li>Health checks</li>
291+
</ul>
292+
</div>
293+
</div>
294+
</div>
295+
296+
<div class="demo-section">
297+
<h2>Implementation</h2>
298+
<p>To use this component in your Umbraco templates:</p>
299+
300+
<pre><code>@Html.Partial("PhishLabsIncidentReporter")</code></pre>
301+
302+
<p>See the <a href="../docs/PhishLabsIncidentReporter.md">complete documentation</a> for configuration and setup details.</p>
303+
</div>
304+
</div>
305+
306+
<script>
307+
// Demo functionality
308+
function showDemo() {
309+
const response = document.getElementById('demo-response');
310+
response.style.display = 'block';
311+
312+
// Simulate form submission feedback
313+
const button = event.target;
314+
button.textContent = 'Submitting...';
315+
button.disabled = true;
316+
317+
setTimeout(() => {
318+
button.textContent = 'Report Link';
319+
button.disabled = false;
320+
response.innerHTML = '<strong>Demo Success:</strong> Thanks — we received your report and are investigating. Report ID: demo-12345';
321+
response.style.background = '#d4edda';
322+
response.style.borderColor = '#c3e6cb';
323+
}, 2000);
324+
}
325+
326+
// Character counter for demo
327+
document.getElementById('demo-details').addEventListener('input', function() {
328+
const count = this.value.length;
329+
const counter = document.querySelector('.char-count');
330+
counter.textContent = `${count} / 1000 characters`;
331+
});
332+
</script>
333+
</body>
334+
</html>

0 commit comments

Comments
 (0)