Skip to content

feat: bootstrap catnet-core v0.1.0 — shared scanning engine - #1

Merged
mendsec merged 2 commits into
mainfrom
develop
Jun 2, 2026
Merged

mendsec merged 2 commits into
mainfrom
develop

Conversation

@mendsec

@mendsec mendsec commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR introduces the foundational v0.1.0 release of catnet-core, the shared Go engine for the CatNet scanning ecosystem. It provides reusable packages for network scanning, host discovery, result export (JSON/CSV/XML), and scanning lifecycle management — without any GUI/TUI/presentation-layer code.

Type of change

  • Feature

Key additions

pkg/scanner

  • DeviceInfo, ScanConfig, DefaultConfig, Sanitize
  • validateIPv4, Ping, ReverseDNS, GetMAC, ScanPorts
  • ParseRange with CIDR and dash range support
  • StartScan, StopScan with goroutines and context-based cancellation
  • Platform-specific build tags: Windows (SendARP) and POSIX (arp)

pkg/exporter

  • ExportJSON, ExportCSV, ExportXML with CSV injection sanitization

CI/CD

  • go vet + go test -race in GitHub Actions
  • Weekly govulncheck for vulnerability scanning

Validation

  • Built locally
  • Tests pass locally (TestValidateIPv4, TestExportCSV, TestExportXML, TestExportJSON, TestScanConcurrency)
  • Docs updated (README.md, CHANGELOG.md)

Statistics

  • 601 additions, 391 deletions
  • 25 files changed
  • Old packages (pkg/scan, pkg/export, pkg/errors, pkg/events, pkg/profile, pkg/results, pkg/targets) refactored into consolidated pkg/scanner and pkg/exporter

@mendsec
mendsec merged commit 2024f22 into main Jun 2, 2026
3 checks passed

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the CatNet core engine by consolidating several legacy packages into two main packages: pkg/scanner and pkg/exporter. It introduces a cleaner API for concurrent network scanning, target range parsing, and secure data exporting (JSON, CSV, XML) with CSV injection prevention. Additionally, it updates documentation, downgrades the Go version in go.mod to 1.25.0, and adds comprehensive unit tests. The review feedback highlights critical improvements: refactoring StartScan to accept a context.Context to avoid non-reentrant package-level globals, optimizing Windows MAC resolution by caching DLL loading at the package level, and checking writer.Error() after flushing the CSV writer to ensure data integrity.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread pkg/scanner/scan.go
Comment on lines +17 to +32
func StartScan(ips []string, cfg ScanConfig, onResult func(DeviceInfo), onProgress func(float64)) error {
if !isScanning.CompareAndSwap(false, true) {
return fmt.Errorf("scan already in progress")
}
defer isScanning.Store(false)

ctx, cancel := context.WithCancel(context.Background())
scanMu.Lock()
cancelScan = cancel
scanMu.Unlock()
defer func() {
scanMu.Lock()
cancelScan = nil
scanMu.Unlock()
cancel()
}()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Using package-level global variables (isScanning, scanMu, cancelScan) to manage scanning state and cancellation makes the package non-reentrant and unsafe for concurrent use (e.g., running multiple independent scans in parallel or running parallel tests). Additionally, hardcoding context.Background() and not accepting a context.Context parameter is non-idiomatic in Go for long-running network operations.

Consider refactoring StartScan to accept a context.Context as its first parameter. This allows the caller to handle cancellation and timeouts natively using standard Go contexts, eliminating the need for global cancellation state.

Suggested change
func StartScan(ips []string, cfg ScanConfig, onResult func(DeviceInfo), onProgress func(float64)) error {
if !isScanning.CompareAndSwap(false, true) {
return fmt.Errorf("scan already in progress")
}
defer isScanning.Store(false)
ctx, cancel := context.WithCancel(context.Background())
scanMu.Lock()
cancelScan = cancel
scanMu.Unlock()
defer func() {
scanMu.Lock()
cancelScan = nil
scanMu.Unlock()
cancel()
}()
func StartScan(ctx context.Context, ips []string, cfg ScanConfig, onResult func(DeviceInfo), onProgress func(float64)) error {
if !isScanning.CompareAndSwap(false, true) {
return fmt.Errorf("scan already in progress")
}
defer isScanning.Store(false)
ctx, cancel := context.WithCancel(ctx)
scanMu.Lock()
cancelScan = cancel
scanMu.Unlock()
defer func() {
scanMu.Lock()
cancelScan = nil
scanMu.Unlock()
cancel()
}()

Comment thread pkg/scanner/os_windows.go
Comment on lines +20 to 24
// osGetMAC obtém o MAC usando SendARP no Windows
func osGetMAC(ip string) string {
iphlpapi := syscall.NewLazyDLL("iphlpapi.dll")
sendARP := iphlpapi.NewProc("SendARP")

destIP := net.ParseIP(ip).To4()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Calling syscall.NewLazyDLL and iphlpapi.NewProc inside osGetMAC on every invocation is highly inefficient. Since osGetMAC is called for every active host during a scan, loading the DLL and looking up the procedure repeatedly introduces unnecessary overhead. Moving these to package-level variables ensures they are initialized only once.

Suggested change
// osGetMAC obtém o MAC usando SendARP no Windows
func osGetMAC(ip string) string {
iphlpapi := syscall.NewLazyDLL("iphlpapi.dll")
sendARP := iphlpapi.NewProc("SendARP")
destIP := net.ParseIP(ip).To4()
var (
iphlpapi = syscall.NewLazyDLL("iphlpapi.dll")
sendARP = iphlpapi.NewProc("SendARP")
)
// osGetMAC obtém o MAC usando SendARP no Windows
func osGetMAC(ip string) string {
destIP := net.ParseIP(ip).To4()

Comment thread pkg/exporter/exporter.go
Comment on lines +85 to +86
writer.Flush()
return buf.Bytes(), nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

In Go, csv.Writer buffers writes. Calling writer.Flush() writes any buffered data to the underlying writer, but it does not return an error. To ensure no errors occurred during flushing or writing, you should check writer.Error() after calling Flush().

	writer.Flush()
	if err := writer.Error(); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil

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