Add build and CodeQL scan scripts
- Created a build script to compile the Go backend. - Added a CodeQL scan script to automate the creation and analysis of CodeQL databases for Go and JavaScript/TypeScript, including necessary checks for dependencies.
This commit is contained in:
@@ -49,7 +49,11 @@ func (h *BackupHandler) Delete(c *gin.Context) {
|
||||
|
||||
func (h *BackupHandler) Download(c *gin.Context) {
|
||||
filename := c.Param("filename")
|
||||
path := h.service.GetBackupPath(filename)
|
||||
path, err := h.service.GetBackupPath(filename)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Backup not found"})
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Wikid82/CaddyProxyManagerPlus/backend/internal/models"
|
||||
"github.com/Wikid82/CaddyProxyManagerPlus/backend/internal/services"
|
||||
@@ -65,6 +66,10 @@ func (h *LogsHandler) Download(c *gin.Context) {
|
||||
filename := c.Param("filename")
|
||||
path, err := h.service.GetLogPath(filename)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "invalid filename") {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Log file not found"})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -159,22 +159,41 @@ func (s *BackupService) addDirToZip(w *zip.Writer, srcDir, zipBase string) error
|
||||
|
||||
// DeleteBackup removes a backup file
|
||||
func (s *BackupService) DeleteBackup(filename string) error {
|
||||
// Basic sanitization to prevent directory traversal
|
||||
clean := filepath.Base(filepath.Clean(filename))
|
||||
return os.Remove(filepath.Join(s.BackupDir, clean))
|
||||
cleanName := filepath.Base(filename)
|
||||
if filename != cleanName {
|
||||
return fmt.Errorf("invalid filename: path traversal attempt detected")
|
||||
}
|
||||
path := filepath.Join(s.BackupDir, cleanName)
|
||||
if !strings.HasPrefix(path, filepath.Clean(s.BackupDir)) {
|
||||
return fmt.Errorf("invalid filename: path traversal attempt detected")
|
||||
}
|
||||
return os.Remove(path)
|
||||
}
|
||||
|
||||
// GetBackupPath returns the full path to a backup file (for downloading)
|
||||
func (s *BackupService) GetBackupPath(filename string) string {
|
||||
clean := filepath.Base(filepath.Clean(filename))
|
||||
return filepath.Join(s.BackupDir, clean)
|
||||
func (s *BackupService) GetBackupPath(filename string) (string, error) {
|
||||
cleanName := filepath.Base(filename)
|
||||
if filename != cleanName {
|
||||
return "", fmt.Errorf("invalid filename: path traversal attempt detected")
|
||||
}
|
||||
path := filepath.Join(s.BackupDir, cleanName)
|
||||
if !strings.HasPrefix(path, filepath.Clean(s.BackupDir)) {
|
||||
return "", fmt.Errorf("invalid filename: path traversal attempt detected")
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
// RestoreBackup restores the database and caddy data from a zip archive
|
||||
func (s *BackupService) RestoreBackup(filename string) error {
|
||||
cleanName := filepath.Base(filename)
|
||||
if filename != cleanName {
|
||||
return fmt.Errorf("invalid filename: path traversal attempt detected")
|
||||
}
|
||||
// 1. Verify backup exists
|
||||
clean := filepath.Base(filepath.Clean(filename))
|
||||
srcPath := filepath.Join(s.BackupDir, clean)
|
||||
srcPath := filepath.Join(s.BackupDir, cleanName)
|
||||
if !strings.HasPrefix(srcPath, filepath.Clean(s.BackupDir)) {
|
||||
return fmt.Errorf("invalid filename: path traversal attempt detected")
|
||||
}
|
||||
if _, err := os.Stat(srcPath); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -214,13 +233,16 @@ func (s *BackupService) unzip(src, dest string) error {
|
||||
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
outFile.Close()
|
||||
_ = outFile.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(outFile, rc)
|
||||
|
||||
outFile.Close()
|
||||
// Check for close errors on writable file
|
||||
if closeErr := outFile.Close(); closeErr != nil && err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
rc.Close()
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -50,7 +50,8 @@ func TestBackupService_CreateAndList(t *testing.T) {
|
||||
assert.True(t, backups[0].Size > 0)
|
||||
|
||||
// Test GetBackupPath
|
||||
path := service.GetBackupPath(filename)
|
||||
path, err := service.GetBackupPath(filename)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, filepath.Join(service.BackupDir, filename), path)
|
||||
|
||||
// Test Restore
|
||||
@@ -113,14 +114,14 @@ func TestBackupService_PathTraversal(t *testing.T) {
|
||||
os.MkdirAll(service.BackupDir, 0755)
|
||||
|
||||
// Test GetBackupPath with traversal
|
||||
// Should resolve to .../backups/passwd, NOT .../etc/passwd
|
||||
path := service.GetBackupPath("../../etc/passwd")
|
||||
expected := filepath.Join(service.BackupDir, "passwd")
|
||||
assert.Equal(t, expected, path)
|
||||
// Should return error
|
||||
_, err := service.GetBackupPath("../../etc/passwd")
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "invalid filename")
|
||||
|
||||
// Test DeleteBackup with traversal
|
||||
// Should try to delete .../backups/passwd, fail because it doesn't exist
|
||||
err := service.DeleteBackup("../../etc/passwd")
|
||||
// Should return error
|
||||
err = service.DeleteBackup("../../etc/passwd")
|
||||
assert.Error(t, err)
|
||||
assert.True(t, os.IsNotExist(err))
|
||||
assert.Contains(t, err.Error(), "invalid filename")
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package services
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -59,8 +60,14 @@ func (s *LogService) ListLogs() ([]LogFile, error) {
|
||||
|
||||
// GetLogPath returns the absolute path to a log file if it exists and is valid
|
||||
func (s *LogService) GetLogPath(filename string) (string, error) {
|
||||
clean := filepath.Base(filepath.Clean(filename))
|
||||
path := filepath.Join(s.LogDir, clean)
|
||||
cleanName := filepath.Base(filename)
|
||||
if filename != cleanName {
|
||||
return "", fmt.Errorf("invalid filename: path traversal attempt detected")
|
||||
}
|
||||
path := filepath.Join(s.LogDir, cleanName)
|
||||
if !strings.HasPrefix(path, filepath.Clean(s.LogDir)) {
|
||||
return "", fmt.Errorf("invalid filename: path traversal attempt detected")
|
||||
}
|
||||
|
||||
// Verify file exists
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
|
||||
@@ -103,6 +103,15 @@ func TestLogService(t *testing.T) {
|
||||
_, err = service.GetLogPath("missing.log")
|
||||
assert.Error(t, err)
|
||||
|
||||
// Test GetLogPath - Invalid
|
||||
_, err = service.GetLogPath("nonexistent.log")
|
||||
assert.Error(t, err)
|
||||
|
||||
// Test GetLogPath - Traversal
|
||||
_, err = service.GetLogPath("../../etc/passwd")
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "invalid filename")
|
||||
|
||||
// Test ListLogs - Directory Not Exist
|
||||
nonExistService := NewLogService(&config.Config{DatabasePath: filepath.Join(t.TempDir(), "missing", "cpm.db")})
|
||||
logs, err = nonExistService.ListLogs()
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
cd backend && go build ./...
|
||||
Executable
+42
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Check if gh is installed
|
||||
if ! command -v gh &> /dev/null; then
|
||||
echo "Error: GitHub CLI (gh) is not installed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if gh-codeql extension is installed
|
||||
if ! gh extension list | grep -q "github/gh-codeql"; then
|
||||
echo "Installing GitHub CodeQL extension..."
|
||||
gh extension install github/gh-codeql
|
||||
fi
|
||||
|
||||
echo "Creating CodeQL database..."
|
||||
# Remove existing db if any
|
||||
rm -rf codeql-db
|
||||
|
||||
# Clean up build artifacts and coverage reports to prevent false positives
|
||||
echo "Cleaning up build artifacts..."
|
||||
rm -rf frontend/dist backend/coverage
|
||||
|
||||
# Create the database cluster
|
||||
echo "Creating CodeQL database cluster..."
|
||||
# We specify --command to ensure Go builds correctly
|
||||
# We include javascript to scan the frontend (TypeScript/React)
|
||||
# We use --db-cluster to support multiple languages
|
||||
gh codeql database create codeql-db --language=go,javascript --db-cluster --source-root . --command "./tools/build.sh" --overwrite
|
||||
|
||||
echo "Analyzing CodeQL database..."
|
||||
# Analyze Go
|
||||
echo "Analyzing Go..."
|
||||
gh codeql database analyze codeql-db/go codeql/go-queries:codeql-suites/go-security-and-quality.qls --format=sarif-latest --output=codeql-results-go.sarif --download
|
||||
|
||||
# Analyze JavaScript/TypeScript
|
||||
echo "Analyzing JavaScript/TypeScript..."
|
||||
gh codeql database analyze codeql-db/javascript codeql/javascript-queries:codeql-suites/javascript-security-and-quality.qls --format=sarif-latest --output=codeql-results-js.sarif --download
|
||||
|
||||
echo "Scan complete."
|
||||
echo "Go results: codeql-results-go.sarif"
|
||||
echo "JS/TS results: codeql-results-js.sarif"
|
||||
Reference in New Issue
Block a user