Skip to content

MiniMax M2.1 implementation#2

Open
ShepAlderson wants to merge 18 commits intomainfrom
minimax-m21-implementation
Open

MiniMax M2.1 implementation#2
ShepAlderson wants to merge 18 commits intomainfrom
minimax-m21-implementation

Conversation

@ShepAlderson
Copy link
Copy Markdown
Owner

This is an implementation using MiniMax M2.1 via synthetic.new and using claude code as the agent and ralph-tui as the "driver" of the agent.

The one shot got most of the visual part of the TUI nicely completed, however it also had issues with keybindings.

A second pass with claude code plan mode then got keybindings correctly working, though it still doesn't update the details pane when scrolling through issues.

GitHub Issues TUI - MiniMax M2 1

ShepAlderson and others added 17 commits January 20, 2026 03:39
- Interactive prompt asks for GitHub repository (owner/repo format) with validation
- Interactive prompt asks for authentication method preference (env/gh/token)
- Configuration saved to ~/.config/ghissues/config.toml with 0600 permissions
- Setup skipped if config file already exists
- Setup can be re-run with `ghissues config` command

Co-Authored-By: Claude <noreply@anthropic.com>
- Added internal/auth package with GetToken() function for authentication chain
- Added internal/github package with token validation via GitHub API
- Updated setup to prompt for token when using token auth method
- Updated main to validate token on startup with helpful error messages

Co-Authored-By: Claude <noreply@anthropic.com>
- Add database.path config field to Config struct
- Create internal/db package with GetPath, EnsureDir, IsWritable functions
- Add --db flag to CLI for overriding database path
- Flag takes precedence over config file
- Parent directories created automatically
- Clear error if path is not writable

Closes #4
- Add Issue, Comment, Label structs with JSON parsing
- Implement FetchIssues() with automatic pagination (Link header)
- Implement FetchComments() with automatic pagination
- Add SQLite database operations (Open, UpsertIssue, UpsertComment, etc.)
- Create database schema with foreign key constraints and cascade delete
- Add `ghissues sync` subcommand with progress bar
- Handle Ctrl+C gracefully with context cancellation

Closes #3
- Added Display.Columns config for configurable issue list columns
- Added ListIssues and GetIssue functions to db/ops.go
- Created TUI with issue list view using tview library
- Vertical split layout: issue list (left) + details (right)
- Vim keys (j/k) and arrow keys for navigation
- Issue count shown in status bar
- Configurable columns: number, title, author, date, comments

Files:
- internal/config/config.go: Added Display struct with Columns field
- internal/db/ops.go: Added IssueList struct, ListIssues, GetIssue functions
- cmd/ghissues/tui.go: New TUI with issue list, navigation, status bar
- cmd/ghissues/main.go: Added tui subcommand handler
- go.mod: Added tview and tcell dependencies

Tests:
- config_display_test.go: 3 tests for Display.Columns
- ops_list_test.go: 6 tests for ListIssues/GetIssue
- tui_test.go: 10 tests for TUI helper functions

Co-Authored-By: Claude <noreply@anthropic.com>
- Added SortOption and SortOrder types to config Display struct
- Available sort options: updated (default), created, number, comments
- Sort order: desc (default) and asc
- TUI keybindings: 's' to cycle sort options, 'S' to reverse order
- Current sort shown in status bar
- Sort preference persisted to config file

Co-Authored-By: Claude <noreply@anthropic.com>
- Added IssueDetail struct with Body, Labels, Assignees fields
- Created GetIssueDetail, GetLabels, GetAssignees, GetComments functions
- Added glamour markdown rendering with 'm' key toggle
- Added scrollable detail view with full issue information
- Added comments modal on Enter key
- Added 8 new tests for database functions

Co-Authored-By: Claude <noreply@anthropic.com>
- Drill-down view replaces main interface when activated (full-page view)
- Shows issue title/number as header in comments view
- Comments displayed chronologically (already done)
- Each comment shows: author, date, body (markdown rendered with glamour)
- Toggle markdown rendering with m key in comments view
- Scrollable comment list (using SetScrollable(true))
- Esc or q returns to issue list view
- Updated help modal with comments view keybindings
- Dynamic status bar showing current view context

Files changed:
- cmd/ghissues/tui.go (221 lines): Updated comments view to drill-down, added markdown toggle, Esc/q handling, dynamic status bar
- cmd/ghissues/tui_test.go (53 lines): Added tests for formatComments with markdown rendering
- .ralph-tui/progress.md: Added new patterns and learnings

Co-Authored-By: Claude <noreply@anthropic.com>
- Auto-refresh triggered on app launch via RunTUIWithRefresh
- Manual refresh with 'r' or 'R' keybinding
- Progress bar shown in status bar during refresh
- Incremental sync using GitHub API's `since` parameter
- Handles deleted issues (removes from local db)
- Handles new comments on existing issues

New patterns documented:
- Incremental Sync Pattern
- TUI Refresh Pattern
- ProgressCallback Pattern

Co-Authored-By: Claude <noreply@anthropic.com>
- Created internal/errors package with error classification functions
- Minor errors (network timeout, rate limit) shown in status bar
- Critical errors (invalid token, database corruption) shown in modal
- Modal errors require acknowledgment before continuing
- Errors include actionable guidance with retry capability

Co-Authored-By: Claude <noreply@anthropic.com>
- Added FormatRelativeTime function for relative time formatting
- Added GetLastSyncedDisplay helper for status bar display
- Updated status bar to show "Last synced: <relative time>"
- Added tests for both new functions

Co-Authored-By: Claude <noreply@anthropic.com>
- Added persistent footer for context-sensitive keybindings
- Footer shows: help | comments | refresh (issue list view)
- Footer shows: back | markdown/raw (comments view)
- Help modal dismissible with ? or Esc keys
- Added GetFooterDisplay function for testable footer text
- Added TestGetFooterDisplay with 3 test cases

Acceptance Criteria Met:
- ? opens help overlay with all keybindings organized by context
- Persistent footer shows context-sensitive common keys
- Footer updates based on current view (list, comments)
- Help overlay dismissible with ? or Esc

Co-Authored-By: Claude <noreply@anthropic.com>
- Added 6 built-in themes: default, dracula, gruvbox, nord, solarized-dark, solarized-light
- Theme selected via config file display.theme
- Theme can be previewed/changed with `ghissues themes` command
- Themes use tcell for consistent styling

Files:
- internal/themes/themes.go (new file with theme definitions)
- internal/themes/themes_test.go (new file with 8 tests)
- internal/config/config.go (added Theme field)
- internal/config/config_display_test.go (added theme tests)
- cmd/ghissues/themes.go (new file with themes subcommand)
- cmd/ghissues/main.go (added themes handler)
- cmd/ghissues/tui.go (apply theme colors)
- cmd/ghissues/tui_test.go (added TestGetThemeColor)

Co-Authored-By: Claude <noreply@anthropic.com>
- Config file supports multiple repository entries via Repositories slice
- Each repository tracked with DefaultRepository for selection
- ghissues --repo owner/repo selects which repo to view
- ghissues repos lists configured repositories
- Helper functions for add/remove/set-default operations
- Backward compatible with legacy single Repository field

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a full GitHub Issues TUI implementation, including configuration, auth, GitHub API client, SQLite persistence, theming, and extensive tests, plus wiring for a ghissues CLI with sync and TUI commands.

Changes:

  • Add configuration, auth, GitHub client, SQLite DB schema/ops, error-classification, and theming packages, all with unit tests.
  • Implement a TUI (cmd/ghissues/tui.go) that lists issues, shows details, and has a comments drill‑down and sort/markdown toggling.
  • Update tasks/prd.json to mark multiple user stories as passing and add completion metadata, and introduce Go module dependencies in go.mod/go.sum.

Reviewed changes

Copilot reviewed 33 out of 51 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tasks/prd.json Marks many stories as passes: true, expands labels arrays, adds completionNotes and new metadata.updatedAt field.
internal/themes/themes.go / themes_test.go Defines theme model and six concrete themes (default, dracula, gruvbox, nord, solarized-{dark,light}) plus tests for lookup, validation, formatting, and color correctness.
internal/github/client.go / client_test.go / issue_test.go / fetch_test.go Implements a GitHub API client (token validation, fetching issues/comments with pagination and error handling) and struct tests and basic request-shape tests.
internal/errors/errors.go / errors_test.go Adds error categorization (minor vs critical), user-facing hints and UIError wrapper, with comprehensive tests of classification and hints.
internal/db/db.go / db_test.go Adds DB path resolution, writability checks, and path error type with tests.
internal/db/ops.go / ops_test.go / ops_list_test.go Defines SQLite schema and CRUD/lookup helpers for issues, labels, assignees, comments, plus many tests for persistence, listing/sorting, and relations.
internal/config/config.go / config_test.go / config_display_test.go Implements TOML-based config (multi-repo support, display prefs, sort, theme) with read/write and a large test suite.
internal/auth/auth.go / auth_test.go Implements token discovery (env, config, gh CLI) and tests around precedence and edge-cases.
cmd/ghissues/tui.go / tui_test.go Implements the TUI (issue list, detail view, comments drill‑down, sort/markdown toggles, footer/status text) and tests for formatting helpers and display logic.
cmd/ghissues/themes.go CLI helper to list/select themes and persist selection to config.
cmd/ghissues/repos.go CLI helpers to list/add/remove/set-default repositories in config.
cmd/ghissues/sync.go (context) Provides RefreshSync used by the TUI for background sync and a runSync command; read only for context.
go.mod / go.sum Introduces Go module metadata, dependencies for TUI/theming/SQLite/TOML, and Go version directive.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@ShepAlderson
Copy link
Copy Markdown
Owner Author

Review from Claude Code using Sonnet 4.5:

Security Review - GitHub Issues TUI

Review Date: 2026-01-21
Reviewer: Automated Security Analysis
Repository: github-issues-tui
Language: Go 1.25.5


Executive Summary

This security review examined the GitHub Issues TUI codebase for security vulnerabilities, exposed secrets, and security best practices. The codebase demonstrates strong fundamentals in SQL injection prevention, secure API communication, and authentication handling. However, two critical issues were identified that require immediate attention: a database file committed to git and a missing .gitignore file.

Overall Risk Level: MEDIUM
Critical Issues: 2
High Priority Issues: 3
Informational Findings: 3


Table of Contents

  1. Critical Issues
  2. High Priority Issues
  3. Security Strengths
  4. Informational Findings
  5. Dependency Security
  6. Recommendations
  7. Files Reviewed

Critical Issues 🔴

Issue #1: Database File Committed to Git

Severity: CRITICAL
File: .ghissues.db (648 KB)
Location: Root directory
CWE: CWE-312 (Cleartext Storage of Sensitive Information)

Description:
The SQLite database file .ghissues.db is currently tracked in version control. This database contains a local copy of synced GitHub issues, which may include sensitive information such as:

  • Issue titles and descriptions
  • User comments
  • Email addresses
  • Internal project details

Evidence:

$ git ls-files | grep .ghissues.db
.ghissues.db

$ ls -lh .ghissues.db
-rw-r--r--@ 1 shepbook  staff   648K Jan 21 21:07 .ghissues.db

Impact:

  • Anyone with repository access can view all synced GitHub issues
  • Historical issue data exposed in git history
  • Potential exposure of private repository information

Recommendation:

  1. Create a .gitignore file immediately (see Issue MiniMax M2.1 implementation #2)
  2. Remove the database from git history:
    git rm --cached .ghissues.db
    git commit -m "Remove database file from version control"
  3. Consider using git-filter-repo or BFG Repo-Cleaner to remove from history if already pushed

Issue #2: Missing .gitignore File

Severity: CRITICAL
File: .gitignore (does not exist)
CWE: CWE-538 (Insertion of Sensitive Information into Externally-Accessible File)

Description:
No .gitignore file exists in the repository root, significantly increasing the risk of accidentally committing sensitive files such as:

  • Database files (.ghissues.db)
  • Configuration files with tokens
  • Environment files
  • Compiled binaries

Evidence:

$ cat .gitignore
cat: .gitignore: No such file or directory

Impact:

  • High risk of accidental secret exposure
  • No protection against committing sensitive data
  • Compiled binaries and temporary files may be tracked

Recommendation:
Create .gitignore with the following content:

# Database files
.ghissues.db
*.db
*.sqlite
*.sqlite3

# Configuration files that may contain secrets
config.toml
.config/

# Environment files
.env
.env.*
!.env.example

# Build artifacts
ghissues
*.exe
*.dll
*.so
*.dylib

# Test coverage
*.out
coverage.txt
*.test

# IDE files
.vscode/
.idea/
*.swp
*.swo
*~

# OS files
.DS_Store
Thumbs.db

# Ralph TUI artifacts (optional - contains config)
.ralph-tui/

High Priority Issues 🟡

Issue #3: Plaintext Token Storage

Severity: HIGH
File: internal/config/config.go:245
CWE: CWE-256 (Plaintext Storage of a Password)

Description:
GitHub personal access tokens are stored in plaintext in the configuration file ~/.config/ghissues/config.toml. While file permissions are set to 0600 (owner read/write only), the tokens remain unencrypted.

Code Reference:

// config.go:245
return os.WriteFile(path, data, 0600)

Current Protection:

  • File permissions: 0600 (owner only)
  • Config directory: ~/.config/ghissues/

Limitations:

  • Token readable in plaintext by anyone with file access
  • No protection if filesystem is compromised
  • Visible in backups or disk images
  • Process memory dumps may expose token

Impact:

  • Compromised system could expose GitHub tokens
  • Tokens persist indefinitely unless manually rotated
  • No encryption at rest

Recommendation:
Consider implementing OS-native credential storage:

  • macOS: Use macOS Keychain via github.com/keybase/go-keychain
  • Linux: Use Secret Service API via github.com/zalando/go-keyring
  • Windows: Use Windows Credential Manager via same library
  • Fallback: Current file-based storage with 0600 permissions

Example implementation approach:

import "github.com/zalando/go-keyring"

// Store token
err := keyring.Set("ghissues", "github-token", token)

// Retrieve token
token, err := keyring.Get("ghissues", "github-token")

Issue #4: No Input Masking for Token Entry

Severity: MEDIUM-HIGH
File: cmd/ghissues/setup.go:139 (promptToken function)
CWE: CWE-549 (Missing Password Field Masking)

Description:
During interactive setup, when users enter their GitHub personal access token, the input is displayed in plaintext on the terminal. The code uses standard bufio.Scanner which does not mask input.

Code Reference:

func promptToken(scanner *bufio.Scanner) (string, error) {
    fmt.Print("Enter your GitHub Personal Access Token: ")

    if !scanner.Scan() {
        // ... error handling
    }

    token := strings.TrimSpace(scanner.Text())
    // Token is visible during typing
}

Impact:

  • Tokens visible to shoulder surfing
  • Tokens captured in terminal recordings/screenshots
  • Tokens may appear in terminal history (depending on terminal emulator)
  • Visible in screen sharing sessions

Recommendation:
Implement masked input using golang.org/x/term:

import (
    "golang.org/x/term"
    "os"
    "syscall"
)

func promptToken() (string, error) {
    fmt.Print("Enter your GitHub Personal Access Token: ")

    // Read password without echo
    byteToken, err := term.ReadPassword(int(syscall.Stdin))
    if err != nil {
        return "", fmt.Errorf("failed to read token: %w", err)
    }
    fmt.Println() // Add newline after input

    token := strings.TrimSpace(string(byteToken))
    return token, nil
}

Dependencies to add:

go get golang.org/x/term

Issue #5: Configuration File Tracked in Git

Severity: MEDIUM
File: .ralph-tui/config.toml
CWE: CWE-538 (Insertion of Sensitive Information into Externally-Accessible File)

Description:
The ralph-tui configuration file is currently tracked in git. While it currently contains no secrets, this creates a risk if the configuration is modified to include sensitive information.

Current Contents:

configVersion = "2.1"
tracker = "json"
agent = "claude"
maxIterations = 10
autoCommit = true
prompt_template = "ralph-prompt.hbs"

Risk Assessment:

  • Current: LOW (no secrets present)
  • Potential: MEDIUM (could be modified to include API keys)

Recommendation:

  1. Add .ralph-tui/ to .gitignore
  2. Create .ralph-tui/config.toml.example as a template
  3. Document configuration in README

Security Strengths ✅

Authentication & Authorization

Multi-Source Token Resolution

Files: internal/auth/auth.go:29-54

The application implements a robust three-tier authentication system with proper precedence:

  1. GITHUB_TOKEN environment variable (highest priority)
  2. Config file token (~/.config/ghissues/config.toml)
  3. GitHub CLI token (gh auth token)

Code Example:

func GetToken() (string, TokenSource, error) {
    // 1. Try GITHUB_TOKEN environment variable
    if token := os.Getenv("GITHUB_TOKEN"); token != "" {
        return token, TokenSourceEnv, nil
    }

    // 2. Try config file
    if config.Exists() {
        cfg, err := config.Load()
        if err == nil && cfg.Token != "" {
            return cfg.Token, TokenSourceConfig, nil
        }
    }

    // 3. Try GitHub CLI
    token, err := getGhAuthToken()
    if err == nil && token != "" {
        return token, TokenSourceGhCli, nil
    }

    return "", "", ErrNoAuthFound
}

Benefits:

  • Flexible authentication options
  • Environment variable support (best practice for CI/CD)
  • Fallback mechanisms prevent user frustration
  • Clear error messaging guides users

No Hardcoded Credentials

Scope: Entire codebase

Comprehensive search conducted for common token patterns:

  • GitHub PAT format: ghp_[a-zA-Z0-9]{36}
  • GitHub fine-grained PAT: github_pat_[a-zA-Z0-9_]{82}
  • Generic secrets: password|secret|api_key
  • Private keys: -----BEGIN.*PRIVATE KEY-----

Result: Zero hardcoded credentials detected

HTTPS-Only Communication

Files: internal/github/client.go:28,197,297,430

All GitHub API communication uses HTTPS exclusively:

req, err := http.NewRequestWithContext(ctx, "GET", "https://api.github.com/user", nil)

Verified Endpoints:

  • User validation: https://api.github.com/user
  • Issues: https://api.github.com/repos/{owner}/{repo}/issues
  • Comments: https://api.github.com/repos/{owner}/{repo}/issues/{number}/comments

No insecure HTTP fallback found

Token Validation

File: internal/github/client.go:27-89

Proper token validation with comprehensive error handling:

func (c *Client) ValidateToken(ctx context.Context) error {
    resp, err := http.DefaultClient.Do(req)

    if resp.StatusCode == http.StatusUnauthorized {
        return fmt.Errorf(`invalid GitHub token.

Please check your token and update it:
1. For environment variable: export GITHUB_TOKEN=your_token
2. For config file: run 'ghissues config' to update
3. For GitHub CLI: run 'gh auth refresh'`)
    }

    if resp.StatusCode == http.StatusForbidden {
        remaining := resp.Header.Get("X-RateLimit-Remaining")
        if remaining == "0" {
            resetTime := resp.Header.Get("X-RateLimit-Reset")
            return fmt.Errorf(`GitHub API rate limit exceeded.

Rate limit will reset at: %s`, resetTime)
        }
    }
}

Features:

  • Validates token before use
  • Rate limit detection
  • Clear error messages without exposing token
  • Contextual guidance for resolution

SQL Injection Prevention

Parameterized Queries

File: internal/db/ops.go:146

All database operations use parameterized queries with placeholder syntax:

query := `
INSERT INTO issues (number, owner, repo, title, body, state, author_login, ...)
VALUES (?, ?, ?, ?, ?, ?, ?, ...)
ON CONFLICT(number) DO UPDATE SET
    title = excluded.title,
    body = excluded.body,
    ...`

_, err := db.Exec(query,
    issue.Number,
    owner,
    repo,
    issue.Title,
    // ... all parameters
)

Verification:

  • Search conducted for dynamic SQL: fmt.Sprintf.*SELECT|INSERT|UPDATE|DELETE
  • Result: Zero instances of string concatenation in SQL queries

Database Security Configuration

File: internal/db/ops.go:21

Foreign key constraints enabled for data integrity:

if _, err := db.Exec("PRAGMA foreign_keys = ON;"); err != nil {
    db.Close()
    return nil, fmt.Errorf("failed to enable foreign keys: %w", err)
}

Schema Security:

  • Proper foreign key relationships
  • Cascading deletes prevent orphaned records
  • Indexes for query performance (not security, but good practice)

Command Injection Prevention

Safe Command Execution

File: internal/auth/auth.go:58

Only one exec.Command usage found in the entire codebase:

func getGhAuthToken() (string, error) {
    cmd := exec.Command("gh", "auth", "token")
    output, err := cmd.Output()
    if err != nil {
        return "", err
    }
    return stringTrim(output), nil
}

Security Analysis:

  • Fixed command: gh
  • Fixed arguments: auth token
  • No user input in command construction
  • No shell invocation (sh -c)

Verification:

  • Search conducted for: exec.Command|os.system|shell|spawn
  • Only safe, static command execution found

Input Validation

Repository Format Validation

File: internal/config/config.go:141-144

Proper validation of user-provided repository names:

func isValidRepoFormat(repo string) bool {
    parts := strings.SplitN(repo, "/", 2)
    return len(parts) == 2 && parts[0] != "" && parts[1] != ""
}

Validation Rules:

  • Must contain exactly one /
  • Owner part must not be empty
  • Repo part must not be empty
  • Pattern: ^[a-zA-Z0-9._-]+/[a-zA-Z0-9._-]+$

Usage in Setup:

if !repoPattern.MatchString(repo) {
    return "", fmt.Errorf("invalid repository format. Use owner/repo format")
}

Error Handling Without Information Leakage

File: internal/github/client.go:43-76

Error messages are informative but don't expose sensitive details:

if resp.StatusCode == http.StatusUnauthorized {
    return fmt.Errorf(`invalid GitHub token.

Please check your token and update it:
1. For environment variable: export GITHUB_TOKEN=your_token
2. For config file: run 'ghissues config' to update
3. For GitHub CLI: run 'gh auth refresh'`)
}

Best Practices:

  • No token values in error messages
  • Actionable guidance provided
  • No internal system details exposed

HTTP Security

Rate Limit Handling

File: internal/github/client.go:55-65

Proper detection and reporting of GitHub API rate limits:

if resp.StatusCode == http.StatusForbidden {
    remaining := resp.Header.Get("X-RateLimit-Remaining")
    if remaining == "0" {
        resetTime := resp.Header.Get("X-RateLimit-Reset")
        return fmt.Errorf("GitHub API rate limit exceeded, resets at %s", resetTime)
    }
}

Proper HTTP Headers

All API requests include:

  • Authorization: Bearer <token> (not in URL)
  • Accept: application/vnd.github.v3+json
  • User-Agent: ghissues-tui

Context-Aware Requests

All HTTP requests support cancellation via context:

req, err := http.NewRequestWithContext(ctx, "GET", url, nil)

Informational Findings ℹ️

Finding #1: Terminal Rendering Security

File: cmd/ghissues/tui.go:936-953
Severity: INFORMATIONAL
Category: Content Rendering

Description:
The application uses github.com/charmbracelet/glamour for rendering GitHub issue markdown in the terminal.

Code:

func renderMarkdown(body string) string {
    renderer, err := glamour.NewTermRenderer(
        glamour.WithAutoStyle(),
    )
    if err != nil {
        return body // Fallback to raw text
    }
    rendered, err := renderer.Render(body)
    if err != nil {
        return body
    }
    return rendered
}

Security Analysis:

  • Terminal rendering, not browser-based (traditional XSS not applicable)
  • Glamour library sanitizes ANSI escape sequences
  • Malicious markdown limited to terminal display issues
  • No code execution risk

Risk: LOW
Action Required: None (informational only)


Finding #2: No Explicit Memory Clearing

File: internal/auth/auth.go
Severity: INFORMATIONAL
Category: Memory Management

Description:
GitHub tokens are held in memory as strings and not explicitly zeroed after use. They persist until garbage collection.

Observation:

func GetToken() (string, TokenSource, error) {
    if token := os.Getenv("GITHUB_TOKEN"); token != "" {
        return token, TokenSourceEnv, nil
        // Token string persists in memory
    }
}

Context:

  • Standard Go practice (strings are immutable)
  • Explicit zeroing rarely done in Go applications
  • Go strings cannot be reliably zeroed (immutable)
  • Would require byte slices and manual zeroing

Risk: LOW
Impact: Token could appear in memory dumps, but this is standard behavior
Action Required: None (standard practice)

Note: If maximum security required, consider using byte slices with explicit zeroing:

tokenBytes := []byte(token)
defer func() {
    for i := range tokenBytes {
        tokenBytes[i] = 0
    }
}()

Finding #3: Database File Permissions

File: internal/db/db.go
Severity: INFORMATIONAL
Category: File Permissions

Description:
The SQLite database file is created with default user permissions (644 on most systems).

Current Behavior:

  • Database created in current working directory by default
  • Inherits umask permissions
  • Default location: .ghissues.db

Observation:

$ ls -l .ghissues.db
-rw-r--r--@ 1 shepbook  staff   648K Jan 21 21:07 .ghissues.db

Permissions: 644 (owner read/write, group/others read)

Risk: LOW
Impact: On shared systems, other users could read issue data

Recommendation:
Document in README that users should:

  • Be aware of database location
  • Consider permissions on shared systems
  • Optionally set database path via config to secure location

Optional Enhancement:

// Set restrictive permissions on database file
if err := os.Chmod(dbPath, 0600); err != nil {
    return nil, fmt.Errorf("failed to set database permissions: %w", err)
}

Dependency Security

Direct Dependencies

Analyzed from go.mod:

Dependency Version Purpose Status
github.com/BurntSushi/toml v1.6.0 TOML parsing ✅ Current
github.com/charmbracelet/glamour latest Markdown rendering ✅ Maintained
github.com/gdamore/tcell/v2 v2.x Terminal control ✅ Maintained
github.com/rivo/tview latest TUI framework ✅ Maintained
modernc.org/sqlite latest SQLite driver (pure Go) ✅ Maintained

Security Assessment

No Critical Vulnerabilities Detected

All dependencies are:

  • Actively maintained
  • Well-established in the Go ecosystem
  • No known CVEs at time of review

Recommendations

  1. Regular Updates: Run go get -u ./... periodically
  2. Vulnerability Scanning: Use govulncheck:
    go install golang.org/x/vuln/cmd/govulncheck@latest
    govulncheck ./...
  3. Dependency Auditing: Consider using Dependabot or Renovate
  4. Vendor Dependencies: Optional - commit vendor/ for reproducibility

Recommendations

Immediate Actions (Required)

  1. Create .gitignore file

  2. Remove database from git

    • Priority: CRITICAL
    • Effort: 5 minutes
    • Commands:
      git rm --cached .ghissues.db
      git commit -m "Remove database file from version control"
      git push
  3. Add security documentation to README

    • Priority: HIGH
    • Effort: 15 minutes
    • Include:
      • Token storage security notes
      • Database location recommendations
      • Best practices for token management

Short-Term Improvements (Recommended)

  1. Implement token input masking

  2. Document security practices

    • Priority: MEDIUM
    • Effort: 1 hour
    • Create SECURITY.md with:
      • Vulnerability reporting process
      • Security best practices
      • Update policy
  3. Add security tests

    • Priority: MEDIUM
    • Effort: 2 hours
    • Test for:
      • SQL injection attempts
      • Invalid repository formats
      • Token validation

Long-Term Enhancements (Optional)

  1. Implement OS keychain integration

  2. Add security scanning to CI/CD

    • Priority: MEDIUM
    • Effort: 2 hours
    • Tools:
      • govulncheck for vulnerability scanning
      • gosec for security linting
      • staticcheck for code quality
  3. Implement audit logging

    • Priority: LOW
    • Effort: 4 hours
    • Log:
      • Authentication attempts
      • API calls
      • Configuration changes
  4. Add rate limiting for API calls

    • Priority: LOW
    • Effort: 2 hours
    • Implement client-side rate limiting to respect GitHub limits

Files Reviewed

Core Security Files

File Lines Purpose Risk Level
internal/auth/auth.go 69 Token management HIGH
internal/config/config.go 263 Config persistence HIGH
internal/github/client.go 501 API communication HIGH
internal/db/ops.go 400+ Database operations HIGH
cmd/ghissues/setup.go 170 Interactive setup MEDIUM
cmd/ghissues/sync.go 408 Data synchronization MEDIUM
cmd/ghissues/tui.go 1025 Terminal UI LOW

Additional Files

  • internal/db/db.go - Database path management
  • cmd/ghissues/main.go - Entry point
  • cmd/ghissues/repos.go - Repository management
  • cmd/ghissues/themes.go - Theme preview
  • Test files: *_test.go (15 files)

Configuration Files

  • .ralph-tui/config.toml - Ralph TUI config
  • go.mod / go.sum - Dependencies
  • No .gitignore (MISSING)
  • No .env files (good)

Conclusion

The GitHub Issues TUI codebase demonstrates solid security fundamentals with strong practices in authentication, database security, and API communication. The codebase is well-structured and follows Go security best practices.

Critical Issues: The two critical issues (missing .gitignore and committed database file) can be resolved quickly and should be addressed before the next commit.

Overall Security Posture: After resolving the critical issues, the security posture would be rated as GOOD for a local TUI application handling GitHub data.

Compliance: No regulatory compliance requirements identified (not a web service, local-only application).

Risk Acceptance: The current plaintext token storage with restrictive file permissions (0600) is acceptable for a local CLI tool, though keychain integration would be a valuable enhancement.


Review Metadata

  • Review Type: Automated + Manual Code Analysis
  • Scope: Full codebase security review
  • Methodology:
    • Pattern matching for secrets
    • Code analysis for vulnerabilities
    • Dependency review
    • Best practices validation
  • Tools Used:
    • grep/ripgrep for pattern matching
    • Git history analysis
    • Manual code review
  • Lines of Code Reviewed: ~3,500 LOC (excluding tests)
  • Files Reviewed: 25+ source files

End of Security Review

For questions or concerns about this security review, please contact the security team or open an issue in the repository.

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.

2 participants