Files
go-caatsm/internal/adapter/parser/aviation/constants.go
T
windyboyandClaude Sonnet 4.5 c0a66cf845 Enhance aviation parser with security fixes and comprehensive refactoring
This commit implements a complete refactoring of the ICAO aviation parser,
addressing 15 identified issues across security, performance, code quality,
and documentation.

Security Enhancements (P0 - Critical):
- Add input size validation (max 1800 chars per AFTN standard)
- Implement ReDoS protection with 100ms regex timeout mechanism
- Add field validation to prevent nil pointer dereferences
- Document intentional error handling pattern for audit compliance

Performance & Design Improvements (P1 - Important):
- Remove unnecessary mutex from BodyParser (eliminates serialization)
- Fix tokenizer slash handling logic
- Remove global logger dependencies (zap.S() calls)

Code Quality Improvements (P2):
- Refactor parseRemainingLines with clear helper functions
- Document all regex patterns with ICAO format specifications
- Replace magic numbers with named constants (5 new constants)
- Add error message sanitization to prevent data leakage

Documentation & Polish (P3):
- Create comprehensive package documentation (doc.go)
- Verify naming consistency across all functions
- Add 54 comprehensive tests (all passing)
- Verify performance with benchmarks (~10µs for simple messages)

New Files:
- validation.go: Input validation utilities with AFTN limits
- validation_test.go: Comprehensive validation tests
- regex_timeout.go: ReDoS protection mechanism
- regex_timeout_test.go: Timeout protection tests
- suite_test.go: Ginkgo test suite registration
- doc.go: Package-level documentation

All changes maintain backward compatibility and existing architecture
while significantly enhancing security, maintainability, and code quality.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-26 17:55:25 +08:00

160 lines
7.1 KiB
Go

package aviation
import "regexp"
// String constants
const (
StartIndicatorPrefix = "ZCZC"
EndHeaderMarker = "."
BeginPartMarker = "BEGIN PART"
Category = "category"
CategoryArrival = "ARR"
CategoryDeparture = "DEP"
CategoryCancellation = "CNL"
CategoryDelay = "DLA"
CategoryFlightPlan = "FPL"
FlightNumber = "number"
Register = "reg"
SSR = "ssr"
DepartureCode = "dep"
DepartureTime = "dep_time"
ArrivalCode = "arr"
ArrivalTime = "arr_time"
DestinationCode = "dest"
OtherInfo = "other"
ReferenceData = "reference_data"
CategorySurveillance = "surve"
Indicator = "indicator"
Other = "other"
AircraftID = "aircraft"
Surveillance = "surve"
Speed = "speed"
Level = "level"
Route = "route"
EstimatedTime = "estt"
AlternateAirport = "alter"
PBN = "pbn"
NavigationEquipment = "nav"
EstimatedElapsedTime = "eet"
SELCALCode = "sel"
PerformanceCategory = "per"
RerouteInformation = "rif"
Remarks = "remark"
)
// Parser configuration constants
const (
// MinHeaderLines is the minimum number of lines required for a valid telegram header
MinHeaderLines = 3
// MinStartIndicatorParts is the minimum number of parts in the start indicator line (ZCZC MessageID DateTime)
MinStartIndicatorParts = 3
// MinPriorityLineParts is the minimum number of parts in the priority line (Priority PrimaryAddress)
MinPriorityLineParts = 2
// MinOriginatorParts is the minimum number of parts in an originator line (.CODE DATETIME)
MinOriginatorParts = 2
// MinOriginatorMatchGroups is the minimum number of regex match groups for originator pattern
MinOriginatorMatchGroups = 3
)
// Regular expression patterns for ICAO telegram body parsing.
// These patterns match specific message types defined in ICAO standards.
const (
// ArrPatternString matches ARR (Arrival) messages.
// Format: (ARR-FLIGHTNUM[/SSR]-DEPICAO-ARRICAOTIME)
// Example: (ARR-CES5470/A1234-ZBTJ-ZSHC1614)
// Capture groups:
// - category: Message type (ARR)
// - number: Flight number (alphanumeric, e.g., CES5470)
// - ssr: SSR mode and code (optional, after /, e.g., A1234)
// - dep: Departure airport (4-letter ICAO code, e.g., ZBTJ)
// - arr: Arrival airport (4-letter ICAO code, e.g., ZSHC)
// - arr_time: Arrival time (4 digits HHMM, e.g., 1614)
ArrPatternString = `^\((?P<category>[A-Z]{3})-(?P<number>[A-Z0-9]+)(\/?(?P<ssr>[A-Z0-9]+))?-(?P<dep>[A-Z]{4})-(?P<arr>[A-Z]{4})(?P<arr_time>\d{4})\)$`
// DepPatternString matches DEP (Departure) messages.
// Format: (DEP-FLIGHTNUM[/SSR]-DEPICAOTIME-ARRICAO)
// Example: (DEP-CYZ9017/A5633-ZBTJ1638-ZSPD)
// Capture groups:
// - category: Message type (DEP)
// - number: Flight number (alphanumeric, e.g., CYZ9017)
// - ssr: SSR mode and code (optional, after /, e.g., A5633)
// - dep: Departure airport (4-letter ICAO code, e.g., ZBTJ)
// - dep_time: Departure time (4 digits HHMM, e.g., 1638)
// - arr: Destination airport (4-letter ICAO code, e.g., ZSPD)
DepPatternString = `^\((?P<category>[A-Z]{3})-(?P<number>[A-Z0-9]+)(\/(?P<ssr>[A-Z0-9]+))?-(?P<dep>[A-Z]{4})(?P<dep_time>\d{4})-(?P<arr>[A-Z]{4})\)$`
// FplPatternString matches FPL (Flight Plan) messages.
// This is the most complex pattern, matching ICAO Doc 4444 Field Type 15 format.
// Format spans multiple lines with specific field ordering per ICAO standards.
// Example: (FPL-CCA1532-IS\n-A332/H\n-SDE3FGHIJ4J5M1RWY/LB101\n-ZSSS2035\n-K0859S1040 PIAKS G330...\n-ZBAA0153 ZBYN\n-PBN/A1B2... RMK/TCAS EQUIPPED)
// Capture groups:
// - category: Message type (FPL)
// - number: Flight number (e.g., CCA1532)
// - indicator: Flight rules and type (2 letters, e.g., IS)
// - aircraft: Aircraft type and wake turbulence (e.g., A332/H)
// - surve: Surveillance equipment codes
// - dep: Departure airport (4-letter ICAO)
// - dep_time: Departure time (4 digits HHMM)
// - speed: Cruising speed (e.g., K0859)
// - level: Flight level (e.g., S1040)
// - route: Flight route (can span multiple lines)
// - dest: Destination airport (4-letter ICAO)
// - estt: Estimated elapsed time (4 digits)
// - alter: Alternate airports (space-separated ICAO codes)
// - other: Other information fields (PBN, NAV, REG, EET, SEL, PER, RIF, RMK)
FplPatternString = `\((?P<category>[A-Z]{3})-(?P<number>[A-Z]+\d+)-(?P<indicator>[A-Z]{2})\n-(?P<aircraft>[A-Z]+\d+\/?[A-Z]?)\n?-(?P<surve>.*)\n?-(?P<dep>[A-Z]{4})(?P<dep_time>\d{4})\n?-(?P<speed>[A-Z]+\d+)(?P<level>[A-Z0-9]+)\s+(?P<route>(.|\n)+)\n-(?P<dest>[A-Z]{4})(?P<estt>\d{4})\s?(?P<alter>(\s[A-Z]{4})+)\n?-([A-Z]{3}\/(?:[A-Z]{4}\d{4}\s?)+)?(?P<other>(?m)[A-Z]{3}\/(.|\n)*)\)$`
// CnlPatternString matches CNL (Cancellation) messages.
// Format: (CNL-FLIGHTNUM-[DEPICAO]-ARRICAO)
// Example: (CNL-YZR7979-ZSPD-ZBTJ)
// Capture groups:
// - category: Message type (CNL)
// - number: Flight number (alphanumeric, e.g., YZR7979)
// - dep: Departure airport (4-letter ICAO, optional)
// - arr: Destination airport (4-letter ICAO)
CnlPatternString = `^\((?P<category>[A-Z]{3})-(?P<number>\w+\d+)-?(?P<dep>[A-Z]{4})?-?(?<arr>[A-Z]{4})\)$`
// DlaPatternString matches DLA (Delay) messages.
// Format: (DLA-FLIGHTNUM-DEPICAO[TIME]-ARRICAO[TIME])
// Example: (DLA-CSN3133-ZGGG0110-ZBTJ)
// Capture groups:
// - category: Message type (DLA)
// - number: Flight number (alphanumeric, e.g., CSN3133)
// - dep: Departure airport (4-letter ICAO)
// - dep_time: New departure time (4 digits HHMM, optional)
// - arr: Arrival airport (4-letter ICAO)
// - arr_time: Arrival time (4 digits HHMM, optional)
DlaPatternString = `^\((?P<category>[A-Z]{3})-(?P<number>\w+\d+)-?(?P<dep>[A-Z]{4})(?P<dep_time>\d{4})?-?(?<arr>[A-Z]{4})(?<arr_time>\d{4})?\)$`
)
// Compiled regular expressions
var (
ArrPatternExpression = regexp.MustCompile(ArrPatternString)
DepPatternExpression = regexp.MustCompile(DepPatternString)
FplPatternExpression = regexp.MustCompile(FplPatternString)
CnlPatternExpression = regexp.MustCompile(CnlPatternString)
DlaPatternExpression = regexp.MustCompile(DlaPatternString)
BodyTypePattern = regexp.MustCompile(`^\(([A-Z]{3})(.*\n?)+\)$`)
categoryRegex = regexp.MustCompile(`\((?P<category>[A-Z]+)-`)
emptyLineRemove = regexp.MustCompile(`(?m)^\s*$`)
bodyOnly = regexp.MustCompile(`(.|\n)?(ZCZC(.|\n)*)NNNN(.|\n)?$`)
originator = regexp.MustCompile(`(?P<originatorDateTime>[0-9]+)\s(?P<originator>[A-Z]+)`)
navPattern = regexp.MustCompile(`(?m)NAV\/(?P<nav>\w+)`)
remarkPattern = regexp.MustCompile(`(?s)RMK\/(?P<remark>.*)`)
selPattern = regexp.MustCompile(`(?m)SEL\/(?P<sel>\w+)`)
regPattern = regexp.MustCompile(`(?m)REG\/(?P<reg>[A-Z0-9]+)`)
pbnPattern = regexp.MustCompile(`(?m)PBN\/(?P<pbn>[A-Z0-9]+)`)
eetPattern = regexp.MustCompile(`(?s)(-?EET\/(?P<eet>(?:[A-Z]{4}\d{4}\s*)+))`)
performancePattern = regexp.MustCompile(`(?s)-?PER\/(?P<per>\w)`)
reroutePattern = regexp.MustCompile(`(?m)RIF\/(?P<rif>.*)[A-Z]{3}\/`)
)