Skip to content

Security scan using Claude - #4

Merged
morfien101 merged 4 commits into
masterfrom
feature/bug-fixes
Feb 24, 2026
Merged

Security scan using Claude#4
morfien101 merged 4 commits into
masterfrom
feature/bug-fixes

Conversation

@morfien101

Copy link
Copy Markdown
Owner

PR Description

This PR handles all the issues found in the security scan from below.

Code Review Report - Launch Process Manager

Date: February 24, 2026
Project: Launch - Docker Container Process Manager
Reviewer: Automated Code Analysis


Executive Summary

This report documents a comprehensive security and code quality review of the Launch process manager codebase. Launch is designed to run as PID 1 in Docker containers, managing multiple processes with sophisticated logging capabilities.

Critical Finding: The codebase contains 6 CRITICAL bugs that will cause application crashes or failures. These must be addressed immediately before production use.

Severity Breakdown

  • CRITICAL: 6 issues (Application crashes, complete feature failures)
  • HIGH: 6 issues (Incorrect behavior, data corruption, resource leaks)
  • MEDIUM: 6 issues (Logic errors, stability issues)
  • LOW: 5 issues (Code quality, clarity)
  • SECURITY: 2 concerns (Configuration-based risks)
  • DOCUMENTATION: 3 issues (Readme inconsistencies)

Total Issues Found: 28


CRITICAL SEVERITY ISSUES

✅ Issue #1: Infinite Recursion in rotateWriter.Close()

File: processlogger/filelogger/rotationwriter.go:114
Severity: CRITICAL

Description:
The Close() method calls itself recursively instead of closing the underlying file pointer.

func (w *rotateWriter) Close() error {
    w.lock.Lock()
    defer w.lock.Unlock()
    close(w.watchDogSignals)
    w.running = false
    return w.Close()  // ❌ CALLS ITSELF - Should be w.fp.Close()
}

Impact:

  • Stack overflow crash when any file logger attempts to shutdown
  • Entire application crash on graceful shutdown
  • Container termination will fail catastrophically

Why Critical:
This makes graceful shutdown impossible. Every container using file logging will crash with a stack overflow when Launch tries to shutdown. This affects the core reliability promise of running as PID 1.

Fix Required:

return w.fp.Close()  // Close the file pointer, not self

✅ Issue #2: Nil Map Dereference in FileLogManager.Submit()

File: processlogger/filelogger/filelogger.go:85
Severity: CRITICAL

Description:
The FileLogManager struct is initialized with &FileLogManager{} but the filetracker map is never initialized, remaining nil.

func (flm *FileLogManager) Submit(msg processlogger.LogMessage) {
    flm.filetracker[msg.Config.Logfile.Filename].Write([]byte(msg.Message))
    // ❌ PANIC: filetracker is nil map
}

Impact:

  • Immediate panic on first log submission attempt
  • File logging completely broken
  • Application crash when any process tries to log to a file

Why Critical:
File logging is advertised as a core feature but is completely non-functional. Any attempt to use it crashes the application.

Fix Required:

type FileLogManager struct {
    filetracker map[string]*rotateWriter
}

func init() {
    processlogger.RegisterLogger(LoggerTag, func() processlogger.Logger {
        return &FileLogManager{
            filetracker: make(map[string]*rotateWriter),  // Initialize map
        }
    })
}

✅ Issue #3: Nil Map Access in FileLogManager.RegisterConfig()

File: processlogger/filelogger/filelogger.go:37,41
Severity: CRITICAL

Description:
Same root cause as Issue #2 - attempts to read and write to uninitialized map.

func (flm *FileLogManager) RegisterConfig(conf configfile.LoggingConfig, defaults configfile.DefaultLoggerDetails) error {
    if _, ok := flm.filetracker[conf.Logfile.Filename]; ok {  // ❌ NIL MAP READ
        return nil
    }
    wr, err := newRW(conf.Logfile)
    flm.filetracker[conf.Logfile.Filename] = wr  // ❌ NIL MAP WRITE

Impact:

  • Panic during configuration registration
  • Application fails during startup before any processes run
  • Makes file logging completely unusable

Why Critical:
This prevents the application from even starting if file logging is configured. The failure happens during initialization phase.

Fix Required:
Same as Issue #2 - initialize the map in the constructor.


✅ Issue #4: Data Race in Signal Replicator

File: signalreplicator/signal.go:68-71
Severity: CRITICAL

Description:
The listen() goroutine iterates over the signalChannels map without holding a lock, while register() and remove() modify the same map under lock protection.

func (r *replicator) listen() {
    for s := range r.input {
        for _, procChan := range r.signalChannels {  // ❌ NO LOCK - concurrent map iteration
            procChan <- s
        }
    }
}

func (r *replicator) remove(ch chan os.Signal) {
    id := chanMemLocation(ch)
    r.Lock()  // ✓ Has lock
    delete(r.signalChannels, id)  // Modifying while listen() might iterate
    r.Unlock()
}

Impact:

  • Random crashes with "concurrent map iteration and map write" panic
  • Unpredictable behavior especially during process lifecycle events
  • Race detector will fail immediately
  • Signal delivery failures to processes

Why Critical:
This is a classic concurrency bug. Signal handling is core to process management in containers. The race condition will cause crashes at unpredictable times, especially during process startup/shutdown when signals are being registered/removed.

Fix Required:

func (r *replicator) listen() {
    for s := range r.input {
        r.RLock()  // Add read lock
        for _, procChan := range r.signalChannels {
            procChan <- s
        }
        r.RUnlock()
    }
}

✅ Issue #5: Goroutine Deadlock in Signal Replicator

File: signalreplicator/signal.go:70
Severity: CRITICAL

Description:
Sending signals to process channels is a blocking operation. If any process channel is full (buffer size 1) and the receiver isn't reading, the entire signal replication system deadlocks.

for _, procChan := range r.signalChannels {
    procChan <- s  // ❌ BLOCKING - can deadlock if buffer full
}

Impact:

  • Complete system freeze if any process fails to consume signals
  • Signal delivery failure to all processes (not just the blocked one)
  • Particularly dangerous during shutdown when processes may have exited
  • Container cannot terminate properly

Why Critical:
This is a PID 1 process manager. If signal delivery deadlocks, the entire container becomes unresponsive. Docker stop commands will hang, requiring kill -9.

Fix Required:

for _, procChan := range r.signalChannels {
    select {
    case procChan <- s:
        // Signal sent successfully
    case <-time.After(100 * time.Millisecond):
        // Log warning: process not responding to signals
    }
}

✅ Issue #6: Multiple Timer Callbacks for Signal Handling

File: processmanager/process.go:107-109
Severity: CRITICAL

Description:
Each time a SIGTERM/SIGINT/SIGKILL arrives, a new time.AfterFunc callback is created. If multiple signals arrive, multiple timers send to the same channel.

switch signal {
case syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL:
    if p.config.TermTimeout == 0 {
        p.config.TermTimeout = 30
    }
    time.AfterFunc(time.Duration(p.config.TermTimeout)*time.Second, func() {
        exitTimeout <- p.running()  // ❌ Multiple callbacks can send here
    })
}

Impact:

  • Multiple timeout events for single process
  • Incorrect termination logic - may exit prematurely
  • Process lifecycle confusion - unclear when process actually timed out
  • Race condition on process state

Why Critical:
Process lifecycle management is the core function of this tool. Multiple timeout callbacks break the state machine and can cause processes to be killed incorrectly or the application to misreport process state.

Fix Required:

var timeoutTimer *time.Timer
var timeoutOnce sync.Once

switch signal {
case syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL:
    timeoutOnce.Do(func() {  // Only create timer once
        if p.config.TermTimeout == 0 {
            p.config.TermTimeout = 30
        }
        timeoutTimer = time.AfterFunc(time.Duration(p.config.TermTimeout)*time.Second, func() {
            exitTimeout <- p.running()
        })
    })
}

HIGH SEVERITY ISSUES

✅ Issue #7: Wrong Variable Logged for STDERR

File: processmanager/processmanager.go:268
Severity: HIGH

Description:
The STDERR logging loop splits input by newlines but logs the entire unsplit data for each line, while STDOUT correctly logs individual lines.

// STDOUT handling - CORRECT
for stdoutData := range stdout.Ready {
    for _, s := range strings.Split(stdoutData, "\n") {
        if len(s) != 0 {
            pm.logger.Submit(newLog(processlogger.STDOUT, s+"\n"))  // ✓ Uses 's'
        }
    }
}

// STDERR handling - WRONG
for stderrData := range stderr.Ready {
    for _, s := range strings.Split(stderrData, "\n") {
        if len(s) != 0 {
            pm.logger.Submit(newLog(processlogger.STDERR, stderrData))  // ❌ Uses 'stderrData'
        }
    }
}

Impact:

  • Duplicated log lines - each STDERR line appears multiple times
  • Log corruption - multi-line messages appear for single-line entries
  • Broken log parsing - downstream log analysis tools see malformed data
  • Storage waste - logs consume more space than necessary

Why High:
While not crashing, this fundamentally breaks STDERR logging. Monitoring, alerting, and debugging all rely on accurate logs. This makes STDERR output unusable for troubleshooting.

Fix Required:

pm.logger.Submit(newLog(processlogger.STDERR, s+"\n"))  // Use 's' not 'stderrData'

✅ Issue #8: Address-of Operator on Struct Fields in Nil Checks

File: configfile/configfile.go:80,98,101,110
Severity: HIGH

Description:
Multiple nil checks use the address-of operator & on struct fields. The address of a field within an existing struct is never nil.

if &proc.LoggerConfig == nil {  // ❌ ALWAYS FALSE
    createLoggingConfig(proc)
}

if &cf.ProcessManager == nil {  // ❌ ALWAYS FALSE
    cf.ProcessManager = defaultProcessManager
}

if &cf.ProcessManager.LoggerConfig == nil {  // ❌ ALWAYS FALSE
    cf.ProcessManager.LoggerConfig = defaultProcessManager.LoggerConfig
}

if &cf.ProcessManager.LoggerConfig.Syslog == nil {  // ❌ ALWAYS FALSE
    cf.ProcessManager.LoggerConfig.Syslog = defaultProcessManagerSyslog
}

Impact:

  • Default configuration never applied to processes or process manager
  • Missing logging configuration causes downstream failures
  • Incomplete configuration passed to processes
  • Likely causes panics or errors when processes try to log

Why High:
This breaks the entire default configuration system. Processes without explicit logging config won't get defaults, leading to nil pointer dereferences elsewhere. The intention was clearly to check if the field is its zero value and apply defaults.

Fix Required:
These should likely check for zero values or use pointer fields:

// Option 1: Check for empty/zero value
if proc.LoggerConfig.Engine == "" {
    createLoggingConfig(proc)
}

// Option 2: Make fields pointers
if proc.LoggerConfig == nil {  // If LoggerConfig is *LoggingConfig
    createLoggingConfig(proc)
}

✅ Issue #9: watchDog Goroutine Never Started

File: processlogger/filelogger/rotationwriter.go:24-36
Severity: HIGH

Description:
The watchDog function exists to monitor file size and trigger rotations, but it's never called when a rotateWriter is created.

func newRW(conf configfile.FileLogger) (*rotateWriter, error) {
    w := &rotateWriter{
        config:              conf,
        watchDogSignals:     make(chan bool, 100),
        historicalFilePaths: make([]string, 0),
        running:             true,
    }
    err := w.rotate()
    if err != nil {
        return nil, err
    }
    return w, nil
    // ❌ Missing: go w.watchDog()
}

Impact:

  • Log rotation completely broken - files never rotate based on size
  • Disk space exhaustion - log files grow unbounded
  • Container storage fills up causing application failures
  • Configuration settings for size_limit and historical_files_limit are ignored

Why High:
Log rotation is a documented feature with configuration options. Without it, any production deployment will eventually fill disk space and crash. This is particularly bad in container environments with limited disk.

Fix Required:

func newRW(conf configfile.FileLogger) (*rotateWriter, error) {
    w := &rotateWriter{
        config:              conf,
        watchDogSignals:     make(chan bool, 100),
        historicalFilePaths: make([]string, 0),
        running:             true,
    }
    err := w.rotate()
    if err != nil {
        return nil, err
    }
    go w.watchDog()  // Start the watchdog goroutine
    return w, nil
}

✅ Issue #10: Panic on Empty Byte Slice in BytePipe.Write()

File: bytepipe/bytepipe.go:16
Severity: HIGH

Description:
The Write method checks if the last byte is a newline but doesn't validate that the slice is non-empty first.

func (bp *BytePipe) Write(p []byte) (n int, err error) {
    if p[len(p)-1] != '\n' {  // ❌ PANIC if len(p) == 0
        p = append(p, '\n')
    }
    bp.Ready <- string(p)
    return len(p), err
}

Impact:

  • Application crash if any process writes an empty buffer
  • Index out of bounds panic: p[-1]
  • Possible during process startup/shutdown or with certain edge cases

Why High:
While empty writes might be uncommon, they're valid according to the io.Writer interface contract. This violates the interface contract and makes the code fragile.

Fix Required:

func (bp *BytePipe) Write(p []byte) (n int, err error) {
    if len(p) == 0 {
        return 0, nil
    }
    if p[len(p)-1] != '\n' {
        p = append(p, '\n')
    }
    bp.Ready <- string(p)
    return len(p), nil  // Also set err to nil explicitly
}

🔴 Issue #11: Unused shutdown Channel

File: processmanager/process.go:25,151
Severity: HIGH

Description:
The Process struct has a shutdown channel that's created but never used anywhere in the codebase.

type Process struct {
    sync.RWMutex
    cmd            *exec.Cmd
    env            []string
    running        bool
    exited         bool
    exiting        bool
    config         *configfile.Process
    shutdown       chan bool  // ❌ Created but never used
    signalChannels chan os.Signal
}

Impact:

  • Memory leak - channel allocated but never closed
  • Incomplete feature - suggests unfinished graceful shutdown implementation
  • Missing functionality - likely intended for external shutdown signaling
  • Code maintainability issues - dead code

Why High:
This indicates incomplete implementation of what's likely a critical feature. Graceful shutdown is essential for a PID 1 process manager. The channel was clearly intended for something but was never fully implemented.

Recommendation:
Either implement the intended functionality or remove the dead code. If this was meant for external shutdown signaling, the feature should be completed.


✅ Issue #12: Uninitialized Error Return in BytePipe.Write()

File: bytepipe/bytepipe.go:21
Severity: HIGH

Description:
The Write method uses named return parameters but never explicitly sets err, relying on implicit zero value.

func (bp *BytePipe) Write(p []byte) (n int, err error) {
    if p[len(p)-1] != '\n' {
        p = append(p, '\n')
    }
    bp.Ready <- string(p)
    return len(p), err  // ❌ err never set, returns zero value (nil)
}

Impact:

  • Misleading code - appears to handle errors but doesn't
  • Interface violation - io.Writer expects errors to be explicitly returned
  • Future bugs - if err gets set in some paths, behavior becomes inconsistent
  • Code maintainability issues

Why High:
While it works (err is nil), this is a dangerous pattern. If someone adds error handling to one code path but forgets others, it will silently return an uninitialized error value. The code should be explicit about success.

Fix Required:

func (bp *BytePipe) Write(p []byte) (n int, err error) {
    if len(p) == 0 {
        return 0, nil
    }
    if p[len(p)-1] != '\n' {
        p = append(p, '\n')
    }
    bp.Ready <- string(p)
    return len(p), nil  // Explicitly return nil for success
}

MEDIUM SEVERITY ISSUES

✅ Issue #13: Inverted Logic in Process.running() Method

File: processmanager/process.go:31-39
Severity: MEDIUM

Description:
The running() method returns true when the process has exited, which is semantically backwards.

func (p *Process) running() bool {
    p.RLock()
    defer p.RUnlock()
    if p.exited {
        return p.exited  // ❌ Returns TRUE when exited
    }
    return p.exiting
}

Impact:

  • Semantic confusion - name implies "is running" but returns opposite
  • Timeout logic issues - called from termination timeout handler
  • May cause incorrect behavior in callers expecting boolean "is running"

Why Medium:
While confusing, the method is only called in one place (line 109) and the caller may be handling the inverted logic. However, this is a maintainability nightmare.

Fix Required:

func (p *Process) running() bool {
    p.RLock()
    defer p.RUnlock()
    return !p.exited && !p.exiting
}

✅ Issue #14: File Handle Leak in rotate() Error Path

File: processlogger/filelogger/rotationwriter.go:131-139
Severity: MEDIUM

Description:
If os.Rename fails, the function returns early without creating a new file pointer, leaving w.fp in an inconsistent state.

func (w *rotateWriter) rotate() error {
    w.lock.Lock()
    defer w.lock.Unlock()

    w.fp = nil  // Close old file pointer

    _, err := os.Stat(w.config.Filename)
    if err == nil {
        newFileName := w.config.Filename + "." + time.Now().Format(time.RFC3339)
        err = os.Rename(w.config.Filename, newFileName)
        if err != nil {
            return err  // ❌ Early return with w.fp = nil
        }
    }
    // ... continues to create new file
}

Impact:

  • Broken logging after failed rotation
  • State corruption - subsequent writes fail
  • Poor error recovery - should attempt to restore working state

Why Medium:
This is an error path that might not be commonly hit, but when it does, it breaks logging for that process permanently until restart.

Fix Required:
Restore the old file pointer or ensure a new one is always created, even after rename failures.


✅ Issue #15: Unused Global Variable

File: processlogger/filelogger/filelogger.go:27
Severity: MEDIUM

Description:
A global fileLogManager variable is declared but never used.

var fileLogManager *FileLogManager  // ❌ Never assigned or referenced

Impact:

  • Code confusion - suggests a singleton pattern that doesn't exist
  • Dead code - should be removed
  • May confuse developers about intended architecture

Why Medium:
Pure code quality issue. No functional impact but indicates incomplete refactoring or abandoned design approach.

Fix Required:
Remove the unused variable.


✅ Issue #16: Missing Nil Check on LogManager Map Access

File: processlogger/logManager.go:145
Severity: MEDIUM

Description:
The Submit method accesses a map entry without checking if it exists.

func (lm *LogManager) Submit(log LogMessage) {
    if lm.terminated {
        return
    }
    lm.activeLoggerQ[log.Config.Engine] <- &log  // ❌ No check if key exists
}

Impact:

  • Potential panic if logging engine not registered
  • Configuration errors not caught until runtime
  • Should fail fast during configuration, not during logging

Why Medium:
If configuration is properly validated, this shouldn't be hit. However, defensive programming would catch configuration errors more gracefully.

Fix Required:

func (lm *LogManager) Submit(log LogMessage) {
    if lm.terminated {
        return
    }
    if q, ok := lm.activeLoggerQ[log.Config.Engine]; ok {
        q <- &log
    } else {
        // Log error: unknown logging engine
    }
}

✅ Issue #17: Log Format String Error

File: main.go:222
Severity: MEDIUM

Description:
String concatenation is used instead of format string parameters.

log.Fatalf("Error shutting down loggers. Errors: %s" + errString())

Impact:

  • Incorrect logging pattern - doesn't follow printf conventions
  • Works but confusing - mixes format strings with concatenation
  • Code quality issue

Why Medium:
Functionally works but is poor practice and confusing to maintainers.

Fix Required:

log.Fatalf("Error shutting down loggers. Errors: %s", errString())

✅ Issue #18: Race Condition in Process State Management

File: processmanager/process.go:82-84,136
Severity: MEDIUM

Description:
The exiting flag is set under lock, but exited is set without holding the lock.

// Line 82-84
p.Lock()
p.exiting = true
p.Unlock()

// ... much later, line 136
p.exited = true  // ❌ NO LOCK

Impact:

  • Data race - reading and writing not consistently protected
  • Race detector will flag this
  • May cause incorrect state reads in running() method

Why Medium:
While the race exists, the practical impact may be limited due to execution ordering. However, this violates concurrency best practices.

Fix Required:

p.Lock()
p.exited = true
p.Unlock()

LOW SEVERITY ISSUES

✅ Issue #19: Implicit Error Value Return

File: bytepipe/bytepipe.go:21
Severity: LOW

Description:
Same as Issue #12 but categorized as low if only considering functionality (works correctly but poor practice).


✅ Issue #20: Ignored Errors in Console Logger

File: processlogger/console/console.go:56-62
Severity: LOW

Description:
Errors from writing to stdout/stderr are silently ignored.

func (c *Console) Submit(msg processlogger.LogMessage) {
    m := fmt.Sprintf("%s: %s", msg.Source, msg.Message)
    if msg.Pipe == processlogger.STDERR {
        c.stdErr(m)  // ❌ Error ignored
    }
    if msg.Pipe == processlogger.STDOUT {
        c.stdOut(m)  // ❌ Error ignored
    }
}

Impact:

  • Lost error information - can't detect write failures
  • Unlikely to matter in practice (stdout/stderr rarely fail)

Why Low:
Writing to stdout/stderr rarely fails, and there's limited recovery options if it does. However, the errors should at least be logged internally.


✅ Issue #21: Unchecked Type Assertion

File: processmanager/process.go:179
Severity: LOW

Description:
Type assertion without explicit ok check, though it's validated by prior function call.

if exitCode := readExitError(err); exitCode != 0 {
    stdout = err.(*exec.ExitError).Stderr  // ❌ No explicit ok check
}

Impact:

  • Potential panic if err is not *exec.ExitError
  • However, readExitError already validates this (line 144)

Why Low:
The code is actually safe due to readExitError's validation, but the safety is non-obvious.


✅ Issue #22: Misleading Function Name

File: processlogger/filelogger/rotationwriter.go:68-72
Severity: LOW

Description:
Function named panic() doesn't actually panic.

func (w *rotateWriter) panic(err error) {
    fmt.Println(err)  // ❌ Just prints, doesn't panic
}

Impact:

  • Naming confusion - suggests panic but just logs
  • Code readability issue

Why Low:
Purely a naming issue, no functional impact.

Fix Required:
Rename to handleError() or logError().


✅ Issue #23: Theoretical Integer Overflow

File: processlogger/filelogger/rotationwriter.go:104
Severity: LOW

Description:
File size counter could theoretically overflow uint64.

w.currentFileSize = w.currentFileSize + uint64(n)

Impact:

  • Would take years of continuous writing to overflow
  • Purely theoretical in container context

Why Low:
Not a practical concern for container lifetimes.


SECURITY CONCERNS

🔐 Issue #24: Command Execution from Configuration

File: processmanager/processmanager.go:219
Severity: MEDIUM (Security Context)

Description:
Commands are executed directly from YAML configuration without validation.

execProc := exec.Command(config.CMD, config.Args...)

Impact:

  • If attacker can modify config file, arbitrary command execution
  • Expected behavior for a process manager, but should be documented
  • Mitigated by container security boundaries

Why Medium:
This is the intended design of a process manager, but the security implications should be clearly documented. Configuration files should be protected.

Recommendation:
Add security documentation:

  • Configuration files must be read-only in containers
  • Configuration should be baked into images, not mounted
  • Use container security features to protect config

🔐 Issue #25: Unvalidated File Paths

File: processlogger/filelogger/rotationwriter.go:142
Severity: LOW (Security Context)

Description:
Log file paths from configuration are used without validation.

w.fp, err = os.Create(w.config.Filename)

Impact:

  • Path traversal possible (e.g., ../../../../etc/passwd)
  • Mitigated by container file permissions
  • Limited practical impact

Why Low:
Container security and file permissions provide defense in depth. An attacker who can modify config likely has broader access anyway.

Recommendation:
Validate that log paths are within expected directories.


DOCUMENTATION ISSUES

📄 Issue #26: Broken Link in README.md

File: README.md:44
Severity: LOW (Documentation)

Description:
Reference to configuration documentation uses wrong filename case.

[README dedicated to configuration](./READMEs/ConfigurationFile.MD)

Actual file: READMEs/ConfigrationFile.md (note: lowercase .md)

Impact:

  • Broken link on case-sensitive filesystems
  • User documentation navigation broken

📄 Issue #27: Typo in Configuration Filename

File: READMEs/ConfigrationFile.md
Severity: LOW (Documentation)

Description:
Filename has typo: "Configration" should be "Configuration"

Impact:

  • Unprofessional appearance
  • Confusing for users
  • Broken link from main README

Fix Required:
Rename file to ConfigurationFile.md


📄 Issue #28: Mismatched Quotes in README.md

File: README.md:53
Severity: LOW (Documentation)

Description:
Syntax error in code block example.

Use `./build_and_test.sh` gobuild"

Impact:

  • Confusing to users
  • Copy-paste won't work correctly

Fix Required:

Use `./build_and_test.sh gobuild` to also rebuild the go project

PRIORITIZED FIX RECOMMENDATIONS

IMMEDIATE (Production Blockers)

Must fix before any production use:

  1. Issue add LICENSE / COPYING #1 - Fix infinite recursion in Close()
  2. Issue Create LICENCE #2 & Messages of 65522 bytes cause the logging engine to fail #3 - Initialize filetracker map
  3. Issue Security scan using Claude #4 - Add read lock to signal replicator
  4. Issue #5 - Make signal send non-blocking
  5. Issue #6 - Prevent multiple timeout callbacks

Estimated Impact: These 6 critical bugs will cause application crashes. File logging is completely broken, shutdown is impossible, and signal handling has race conditions.


HIGH PRIORITY (Core Functionality)

Should fix before any significant use:

  1. Issue #7 - Fix STDERR logging variable
  2. Issue #8 - Fix nil checks with address-of operator
  3. Issue #9 - Start watchDog goroutine
  4. Issue #10 - Handle empty slices in BytePipe
  5. Issue #11 - Remove or implement shutdown channel
  6. Issue #12 - Explicitly return nil on success

Estimated Impact: These issues break core functionality like logging, configuration defaults, and log rotation. The application may appear to work but will fail in production scenarios.


MEDIUM PRIORITY (Stability)

Fix when able:

  1. Issue #13 - Fix inverted running() logic
  2. Issue #14 - Improve error recovery in rotate()
  3. Issue #15 - Remove unused global variable
  4. Issue #16 - Add defensive nil check
  5. Issue #17 - Fix log format string
  6. Issue #18 - Protect exited flag with lock

Estimated Impact: Stability and maintainability improvements. Some may cause issues in edge cases.


LOW PRIORITY (Code Quality)

Technical debt:

18-23. Issues #19-#23 - Code quality improvements
24-25. Issues #24-#25 - Add security documentation
26-28. Issues #26-#28 - Fix documentation


TESTING RECOMMENDATIONS

Critical Tests Needed

  1. File Logging Test

    • Verify file logger can initialize
    • Test log writing
    • Test graceful shutdown
    • Test log rotation
  2. Signal Handling Test

    • Run with -race detector
    • Test concurrent signal delivery
    • Test process registration/removal during signal delivery
    • Stress test with many processes
  3. Configuration Test

    • Test default configuration application
    • Test processes without explicit logging config
    • Verify all nil checks work correctly
  4. Integration Test

    • Full lifecycle test: start, run, signal, shutdown
    • Test with all logging engines
    • Test with multiple processes
    • Test STDERR vs STDOUT logging

SUMMARY

This codebase has 6 CRITICAL bugs that must be fixed immediately:

  1. File logging completely broken - nil map access
  2. Shutdown will crash - infinite recursion
  3. Signal handling race conditions - concurrent map access
  4. Signal deadlock potential - blocking sends
  5. STDERR logging corrupted - wrong variable
  6. Configuration defaults not applied - wrong nil checks

The application cannot be used in production until these are resolved. Additionally, 6 HIGH severity issues should be addressed before any significant usage.

Overall Assessment: The codebase shows good architectural design and clear separation of concerns. However, critical implementation bugs in core subsystems (file logging, signal handling, configuration) prevent production readiness. The bugs are straightforward to fix once identified.

- rotateWriter.Close() called itself recursively instead of w.fp.Close(),
  causing a stack overflow on any file logger shutdown
- FileLogManager was constructed with a nil filetracker map, causing an
  immediate panic on the first RegisterConfig or Submit call
- signalreplicator.listen() iterated signalChannels without holding a lock
  while remove() held a write lock, producing a concurrent map access panic;
  the blocking channel send also risked deadlocking signal delivery entirely
- time.AfterFunc was called inside a loop body for every SIGTERM/SIGINT/SIGKILL
  received, leaking goroutines blocked on a full channel

Add regression tests for each fix to prevent reintroduction.
Updating package versions to fix issues that dependabot found.
- running() returned p.exited (true when stopped) under a name implying
  the opposite; simplified to return !p.exited
- rotate() left w.fp nil when os.Rename failed, breaking all subsequent
  writes; now logs the error and falls through to create a fresh file
- Removed unreferenced global fileLogManager variable
- LogManager.Submit() sent to a nil channel when the engine key was
  missing from the map, causing a goroutine leak; added an ok check
- Fixed log.Fatalf call using string concatenation instead of a format
  argument (missing comma)
- p.exited was written without a lock while running() reads it under
  RLock; wrapped the assignment in Lock/Unlock to eliminate the race
- Renamed rotateWriter.panic() to logError() to reflect actual behaviour
- Added explicit ok check for *exec.ExitError type assertion in
  RunSecretProcess to make safety obvious to readers
- Added comments in console.Submit() explaining why write errors are
  intentionally dropped (reporting them would reuse the broken pipe)
… complexity paths.

Updating readme files.
Adding in a new llms.txt file so agents can easily read details for launch.
@morfien101
morfien101 merged commit 3d8c095 into master Feb 24, 2026
2 checks passed
@morfien101
morfien101 deleted the feature/bug-fixes branch February 24, 2026 19:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant