Files
honeydany/app/dashboard/webtemplate_manager.go
T
2025-09-28 21:28:39 +01:00

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
}