Compare commits

...

1 Commits

Author SHA1 Message Date
Flatlogic Bot
4d480a4b4a Auto commit: 2025-12-01T11:05:34.155Z 2025-12-01 11:05:34 +00:00
4 changed files with 443 additions and 145 deletions

60
api/detect-type.php Normal file
View File

@ -0,0 +1,60 @@
<?php
header('Content-Type: application/json');
require_once __DIR__ . '/../ai/LocalAIApi.php';
$input = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$data = json_decode(file_get_contents('php://input'), true);
$input = $data['code'] ?? '';
}
if (empty($input)) {
echo json_encode(['error' => 'No input provided.']);
exit;
}
// Simple detection for URL
if (filter_var($input, FILTER_VALIDATE_URL)) {
$detected_language = 'URL';
$suggestion = 'Check for common web vulnerabilities';
} else {
$ai_prompt = "Classify the following code snippet and return only the language name (e.g., 'JavaScript', 'Python', 'SQL', 'PHP', 'HTML'). If it's not a recognizable language, return 'Text'. Snippet:
" . $input;
$resp = LocalAIApi::createResponse([
'input' => [ ['role' => 'system', 'content' => 'You are a code classification assistant.'],
['role' => 'user', 'content' => $ai_prompt],
],
]);
$detected_language = 'Text'; // Default
if (!empty($resp['success'])) {
$text = LocalAIApi::extractText($resp);
if (!empty($text)) {
$detected_language = trim($text);
}
}
$suggestion = 'Check for common vulnerabilities in ' . $detected_language;
switch ($detected_language) {
case 'JavaScript':
$suggestion = 'Check for XSS (Cross-Site Scripting) vulnerabilities';
break;
case 'SQL':
$suggestion = 'Check for SQL Injection vulnerabilities';
break;
case 'PHP':
$suggestion = 'Check for file inclusion and remote code execution risks';
break;
case 'HTML':
$suggestion = 'Check for broken HTML structure and insecure forms';
break;
}
}
echo json_encode([
'language' => $detected_language,
'suggestion' => $suggestion,
]);

121
assets/css/custom.css Normal file
View File

@ -0,0 +1,121 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
:root {
--primary-color: #0d9488;
--primary-hover: #14b8a6;
--secondary-color: #f97316;
--bg-color: #f8fafc;
--surface-color: #ffffff;
--text-dark: #1e293b;
--text-light: #475569;
--border-color: #e2e8f0;
--radius-md: 0.5rem;
--radius-sm: 0.375rem;
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
}
body {
background-color: var(--bg-color);
font-family: 'Inter', sans-serif;
color: var(--text-light);
}
.navbar-brand {
font-weight: 700;
color: var(--text-dark);
}
.scan-section {
background-color: var(--surface-color);
border-radius: var(--radius-md);
box-shadow: var(--shadow-md);
padding: 2rem;
}
.form-control, .form-select {
border-radius: var(--radius-sm);
border-color: var(--border-color);
}
.form-control:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.25rem rgba(13, 148, 136, 0.25);
}
.btn-primary {
background-color: var(--primary-color);
border-color: var(--primary-color);
border-radius: var(--radius-sm);
padding: 0.75rem 1.5rem;
font-weight: 600;
}
.btn-primary:hover, .btn-primary:focus {
background-color: var(--primary-hover);
border-color: var(--primary-hover);
}
.btn-primary:disabled {
background-color: #94a3b8;
border-color: #94a3b8;
}
.results-section {
display: none;
}
.risk-meter {
width: 150px;
height: 150px;
}
.badge-severity {
font-size: 0.8rem;
font-weight: 600;
padding: 0.4em 0.8em;
border-radius: var(--radius-sm);
}
.badge-critical { background-color: #ef4444; color: white; }
.badge-high { background-color: #f97316; color: white; }
.badge-medium { background-color: #facc15; color: var(--text-dark); }
.badge-low { background-color: #22c55e; color: white; }
.issue-card {
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
box-shadow: var(--shadow-sm);
margin-bottom: 1rem;
}
.issue-card .card-body {
padding: 1.5rem;
}
.issue-card h5 {
color: var(--text-dark);
font-weight: 600;
}
.fix-code {
background-color: #f1f5f9;
border: 1px solid var(--border-color);
border-radius: var(--radius-sm);
padding: 1rem;
font-family: monospace;
white-space: pre-wrap;
word-break: break-all;
}
#loader {
display: none;
}
/* Spinner animation */
.spinner-border {
width: 3rem;
height: 3rem;
color: var(--primary-color);
}

159
assets/js/main.js Normal file
View File

@ -0,0 +1,159 @@
document.addEventListener('DOMContentLoaded', function () {
constcodeInput = document.getElementById('codeInput');
const promptInput = document.getElementById('promptInput');
const scanButton = document.getElementById('scanButton');
const scanForm = document.getElementById('scanForm');
const loader = document.getElementById('loader');
const resultsSection = document.getElementById('resultsSection');
const riskScoreValue = document.getElementById('riskScoreValue');
const riskScoreChart = document.getElementById('riskScoreChart');
const issuesContainer = document.getElementById('issuesContainer');
const detectionResult = document.getElementById('detectionResult');
const detectedLanguage = document.getElementById('detectedLanguage');
const detectionSuggestion = document.getElementById('detectionSuggestion');
function debounce(func, delay) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), delay);
};
}
const handleCodeInputChange = debounce(async () => {
const code = codeInput.value.trim();
if (code.length < 10) { // Don't run for very short inputs
detectionResult.style.display = 'none';
return;
}
try {
const response = await fetch('/api/detect-type.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code: code })
});
if (response.ok) {
const data = await response.json();
if (data.language && data.language !== 'Text') {
detectedLanguage.textContent = data.language;
detectionSuggestion.textContent = data.suggestion;
detectionResult.style.display = 'block';
} else {
detectionResult.style.display = 'none';
}
}
} catch (error) {
console.error('Error detecting language:', error);
detectionResult.style.display = 'none';
}
}, 500); // 500ms delay
codeInput.addEventListener('input', handleCodeInputChange);
function validateInputs() {
const code = codeInput.value.trim();
const prompt = promptInput.value.trim();
scanButton.disabled = !(code && prompt);
}
codeInput.addEventListener('input', validateInputs);
promptInput.addEventListener('input', validateInputs);
scanForm.addEventListener('submit', function (e) {
e.preventDefault();
document.querySelector('.scan-section').style.display = 'none';
loader.style.display = 'block';
// Simulate API call
setTimeout(() => {
loader.style.display = 'none';
displayMockResults();
}, 3000);
});
function displayMockResults() {
const mockData = {
"risk_score": 78,
"issues": [
{
"type": "SQL Injection",
"severity": "Critical",
"location": "Login form, 'username' parameter",
"explanation": "Your script appears to be vulnerable to SQL Injection because it doesn't properly sanitize user input before including it in a database query.",
"fix": "Use prepared statements and parameterized queries. Example in PHP: $stmt = $pdo->prepare('SELECT * FROM users WHERE username = ?'); $stmt->execute([$username]);"
},
{
"type": "Cross-Site Scripting (XSS)",
"severity": "High",
"location": "Search results page",
"explanation": "The search term is reflected back to the page without being sanitized, allowing an attacker to inject malicious scripts.",
"fix": "Always escape HTML output. In PHP, use: echo htmlspecialchars($searchTerm, ENT_QUOTES, 'UTF-8');"
},
{
"type": "Weak Password Policy",
"severity": "Medium",
"location": "User registration script",
"explanation": "The script does not enforce a strong password policy, making user accounts susceptible to brute-force attacks.",
"fix": "Require passwords to be at least 12 characters long and include a mix of uppercase, lowercase, numbers, and symbols."
}
]
};
riskScoreValue.textContent = mockData.risk_score;
updateRiskChart(mockData.risk_score);
issuesContainer.innerHTML = '';
mockData.issues.forEach(issue => {
const severityClass = `badge-${issue.severity.toLowerCase()}`;
const issueHtml = `
<div class="card issue-card">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start">
<h5 class="card-title mb-1">${issue.type}</h5>
<span class="badge badge-severity ${severityClass}">${issue.severity}</span>
</div>
<h6 class="card-subtitle mb-2 text-muted">Location: ${issue.location}</h6>
<p class="card-text mt-3">${issue.explanation}</p>
<h6>Suggested Fix:</h6>
<pre class="fix-code"><code>${issue.fix}</code></pre>
</div>
</div>
`;
issuesContainer.insertAdjacentHTML('beforeend', issueHtml);
});
resultsSection.style.display = 'block';
}
function updateRiskChart(score) {
const chart = new Chart(riskScoreChart, {
type: 'doughnut',
data: {
datasets: [{
data: [score, 100 - score],
backgroundColor: [getScoreColor(score), '#e2e8f0'],
borderWidth: 0,
}]
},
options: {
cutout: '70%',
plugins: {
tooltip: { enabled: false },
legend: { display: false }
}
}
});
}
function getScoreColor(score) {
if (score > 75) return '#ef4444'; // Critical
if (score > 50) return '#f97316'; // High
if (score > 25) return '#facc15'; // Medium
return '#22c55e'; // Low
}
});

248
index.php
View File

@ -1,150 +1,108 @@
<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
?>
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>New Style</title>
<?php
// Read project preview data from environment
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
?>
<?php if ($projectDescription): ?>
<!-- Meta description -->
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
<!-- Open Graph meta tags -->
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<!-- Twitter meta tags -->
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<!-- Open Graph image -->
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image -->
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color-start: #6a11cb;
--bg-color-end: #2575fc;
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
}
body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% { background-position: 0% 0%; }
100% { background-position: 100% 100%; }
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
.loader {
margin: 1.25rem auto 1.25rem;
width: 48px;
height: 48px;
border: 3px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hint {
opacity: 0.9;
}
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}
h1 {
font-size: 3rem;
font-weight: 700;
margin: 0 0 1rem;
letter-spacing: -1px;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
}
code {
background: rgba(0,0,0,0.2);
padding: 2px 6px;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
footer {
position: absolute;
bottom: 1rem;
font-size: 0.8rem;
opacity: 0.7;
}
</style>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SecureScan - AI-Powered Vulnerability Scanner</title>
<meta name="description" content="<?php echo htmlspecialchars($_SERVER['PROJECT_DESCRIPTION'] ?? 'A simple, beautiful web app where anyone can check a URL or code snippet for security vulnerabilities using AI.'); ?>">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom CSS -->
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<!-- Platform-managed Meta Tags -->
<?php if (!empty($_SERVER['PROJECT_IMAGE_URL'])): ?>
<meta property="og:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL']); ?>">
<meta name="twitter:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL']); ?>">
<?php endif; ?>
</head>
<body>
<main>
<div class="card">
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
</div>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
<p class="hint">This page will update automatically as the plan is implemented.</p>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
</div>
</main>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</footer>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<a class="navbar-brand" href="#">
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="currentColor" class="bi bi-shield-check" viewBox="0 0 16 16" style="color: var(--primary-color);">
<path d="M5.338 1.59a61.44 61.44 0 0 0-2.837.856.481.481 0 0 0-.328.39c-.554 4.157.726 7.19 2.253 9.188a10.725 10.725 0 0 0 2.287 2.233c.346.244.652.42.893.533.12.057.218.095.293.118a.55.55 0 0 0 .101.025.615.615 0 0 0 .1-.025c.076-.023.174-.06.294-.118.24-.113.547-.29.893-.533a10.726 10.726 0 0 0 2.287-2.233c1.527-1.997 2.807-5.031 2.253-9.188a.48.48 0 0 0-.328-.39c-.952-.325-1.882-.626-2.837-.855C9.552 1.29 8.531 1.007 8 1.007c-.531 0-1.552.283-2.662.583zM8.5 1.531c.315.068.736.148 1.145.253c.41.106.772.24 1.1.352a.639.639 0 0 1 .325.32c.394 2.94.024 5.37-1.082 7.246a9.14 9.14 0 0 1-1.857 1.858a.48.48 0 0 1-.444.002a9.14 9.14 0 0 1-1.857-1.858C4.593 9.471 4.223 7.045 4.617 4.105a.638.638 0 0 1 .325-.321c.328-.112.69-.246 1.1-.352C6.469 1.68 6.89 1.6 7.205 1.531c.316-.068.649-.115.995-.115s.679.047.995.115z"/>
<path d="M10.854 6.146a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 1 1 .708-.708L7.5 8.793l2.646-2.647a.5.5 0 0 1 .708 0z"/>
</svg>
SecureScan
</a>
</div>
</nav>
<main class="container my-5">
<div class="scan-section mx-auto" style="max-width: 700px;">
<div class="text-center mb-4">
<h1 class="h2 text-dark">Scan for Vulnerabilities</h1>
<p class="lead">Instantly analyze a URL or code snippet with AI.</p>
</div>
<form id="scanForm">
<div class="mb-3">
<label for="codeInput" class="form-label">URL or Code Snippet</label>
<textarea class="form-control" id="codeInput" rows="8" placeholder="e.g., https://example.com or paste your code here"></textarea>
<div id="detectionResult" class="form-text mt-2" style="display: none;">
<span class="badge bg-secondary" id="detectedLanguage"></span>
<span id="detectionSuggestion" class="text-muted ms-2"></span>
</div>
</div>
<div class="mb-3">
<label for="promptInput" class="form-label">What should I check for?</label>
<input type="text" class="form-control" id="promptInput" placeholder="e.g., 'Check this login page for hacking risks'">
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary btn-lg" id="scanButton" disabled>Scan Now</button>
</div>
</form>
</div>
<div id="loader" class="text-center my-5">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-3 text-dark">Analyzing... this may take a moment.</p>
</div>
<div id="resultsSection" class="results-section mx-auto" style="max-width: 800px;">
<div class="text-center mb-5">
<h1 class="h2 text-dark">Scan Report</h1>
</div>
<div class="card p-4 mb-5" style="background-color: var(--surface-color); border-radius: var(--radius-md); box-shadow: var(--shadow-md);">
<div class="row align-items-center">
<div class="col-md-4 text-center">
<div class="risk-meter mx-auto position-relative">
<canvas id="riskScoreChart"></canvas>
<div class="position-absolute top-50 start-50 translate-middle">
<span id="riskScoreValue" class="h1 text-dark fw-bold">--</span>
<div class="text-muted" style="margin-top: -5px;">Score</div>
</div>
</div>
</div>
<div class="col-md-8">
<h4 class="text-dark">Overall Risk Score</h4>
<p>This score represents the estimated security risk based on the vulnerabilities found. A higher score indicates a greater risk that should be addressed immediately.</p>
</div>
</div>
</div>
<h3 class="text-dark mb-4">Found Issues</h3>
<div id="issuesContainer">
<!-- Issues will be dynamically inserted here -->
</div>
</div>
</main>
<footer class="text-center text-muted py-4">
<p>&copy; <?php echo date("Y"); ?> SecureScan. All Rights Reserved.</p>
</footer>
<!-- Chart.js for the risk meter -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<!-- Custom JS -->
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
<!-- Reminder to save changes -->
<script>console.log("Reminder: click Save in the editor to sync changes.");</script>
</body>
</html>
</html>