221 lines
6.2 KiB
Go
221 lines
6.2 KiB
Go
package dashboard
|
|
|
|
import (
|
|
"fmt"
|
|
"html/template"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// WebTemplateManager manages HTML templates for web honeypot services
|
|
type WebTemplateManager struct {
|
|
templateDir string
|
|
}
|
|
|
|
// NewWebTemplateManager creates a new web template manager
|
|
func NewWebTemplateManager(templateDir string) *WebTemplateManager {
|
|
// Ensure template directory exists
|
|
if err := os.MkdirAll(templateDir, 0755); err != nil {
|
|
// Log error but continue - directory will be created when first template is saved
|
|
}
|
|
|
|
return &WebTemplateManager{
|
|
templateDir: templateDir,
|
|
}
|
|
}
|
|
|
|
// GetTemplate retrieves a template by name
|
|
func (wtm *WebTemplateManager) GetTemplate(templateName string) (string, error) {
|
|
if !wtm.isValidTemplateName(templateName) {
|
|
return "", fmt.Errorf("invalid template name: %s", templateName)
|
|
}
|
|
|
|
templatePath := filepath.Join(wtm.templateDir, templateName)
|
|
content, err := os.ReadFile(templatePath)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to read template %s: %w", templateName, err)
|
|
}
|
|
|
|
return string(content), nil
|
|
}
|
|
|
|
// SaveTemplate saves a template with the given name and content
|
|
func (wtm *WebTemplateManager) SaveTemplate(templateName, content string) error {
|
|
if !wtm.isValidTemplateName(templateName) {
|
|
return fmt.Errorf("invalid template name: %s", templateName)
|
|
}
|
|
|
|
// Ensure template directory exists
|
|
if err := os.MkdirAll(wtm.templateDir, 0755); err != nil {
|
|
return fmt.Errorf("failed to create template directory: %w", err)
|
|
}
|
|
|
|
templatePath := filepath.Join(wtm.templateDir, templateName)
|
|
if err := os.WriteFile(templatePath, []byte(content), 0644); err != nil {
|
|
return fmt.Errorf("failed to save template %s: %w", templateName, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ListTemplates returns a list of available template names
|
|
func (wtm *WebTemplateManager) ListTemplates() ([]string, error) {
|
|
if _, err := os.Stat(wtm.templateDir); os.IsNotExist(err) {
|
|
return []string{}, nil
|
|
}
|
|
|
|
entries, err := os.ReadDir(wtm.templateDir)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read template directory: %w", err)
|
|
}
|
|
|
|
var templates []string
|
|
for _, entry := range entries {
|
|
if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".html") {
|
|
templates = append(templates, entry.Name())
|
|
}
|
|
}
|
|
|
|
return templates, nil
|
|
}
|
|
|
|
// DeleteTemplate removes a template
|
|
func (wtm *WebTemplateManager) DeleteTemplate(templateName string) error {
|
|
if !wtm.isValidTemplateName(templateName) {
|
|
return fmt.Errorf("invalid template name: %s", templateName)
|
|
}
|
|
|
|
templatePath := filepath.Join(wtm.templateDir, templateName)
|
|
if err := os.Remove(templatePath); err != nil {
|
|
return fmt.Errorf("failed to delete template %s: %w", templateName, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ValidateTemplate validates template content for basic HTML structure
|
|
func (wtm *WebTemplateManager) ValidateTemplate(content string) error {
|
|
// Basic validation - check if it's valid HTML template
|
|
_, err := template.New("test").Parse(content)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid template syntax: %w", err)
|
|
}
|
|
|
|
// Additional validation - ensure it contains basic HTML structure
|
|
content = strings.ToLower(content)
|
|
if !strings.Contains(content, "<html") {
|
|
return fmt.Errorf("template must contain <html> tag")
|
|
}
|
|
|
|
if !strings.Contains(content, "<body") {
|
|
return fmt.Errorf("template must contain <body> tag")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// CreateDefaultTemplate creates a default template with the given name
|
|
func (wtm *WebTemplateManager) CreateDefaultTemplate(templateName string) error {
|
|
if !wtm.isValidTemplateName(templateName) {
|
|
return fmt.Errorf("invalid template name: %s", templateName)
|
|
}
|
|
|
|
defaultContent := `<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Login - {{ .ServiceName }}</title>
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
background: #f5f5f5;
|
|
margin: 0;
|
|
padding: 20px;
|
|
}
|
|
.login-container {
|
|
max-width: 400px;
|
|
margin: 50px auto;
|
|
background: white;
|
|
padding: 30px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
}
|
|
.form-group {
|
|
margin-bottom: 20px;
|
|
}
|
|
label {
|
|
display: block;
|
|
margin-bottom: 5px;
|
|
font-weight: bold;
|
|
}
|
|
input {
|
|
width: 100%;
|
|
padding: 10px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
box-sizing: border-box;
|
|
}
|
|
button {
|
|
width: 100%;
|
|
padding: 12px;
|
|
background: #007bff;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-size: 16px;
|
|
}
|
|
button:hover {
|
|
background: #0056b3;
|
|
}
|
|
.error {
|
|
color: red;
|
|
margin-top: 10px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="login-container">
|
|
<h2>{{ .ServiceName }}</h2>
|
|
<form method="POST" action="{{ .LoginPath }}">
|
|
<div class="form-group">
|
|
<label for="username">Username</label>
|
|
<input type="text" id="username" name="username" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="password">Password</label>
|
|
<input type="password" id="password" name="password" required>
|
|
</div>
|
|
<button type="submit">Login</button>
|
|
</form>
|
|
</div>
|
|
</body>
|
|
</html>`
|
|
|
|
return wtm.SaveTemplate(templateName, defaultContent)
|
|
}
|
|
|
|
// isValidTemplateName validates template name for security
|
|
func (wtm *WebTemplateManager) isValidTemplateName(name string) bool {
|
|
name = strings.TrimSpace(name)
|
|
// Must end with .html
|
|
if !strings.HasSuffix(strings.ToLower(name), ".html") {
|
|
return false
|
|
}
|
|
// Disallow path separators and traversal, but allow single dots
|
|
if strings.Contains(name, "/") || strings.Contains(name, "\\") || strings.Contains(name, "..") {
|
|
return false
|
|
}
|
|
// Must not be empty or too long
|
|
if len(name) == 0 || len(name) > 100 {
|
|
return false
|
|
}
|
|
// Must be a valid filename
|
|
if name != filepath.Base(name) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|