feat: Add pdsh-style hostlist expression support#107
Conversation
Implement range expansion syntax for specifying multiple hosts: - Simple ranges: node[1-5] -> node1, node2, node3, node4, node5 - Zero-padded: node[01-05] -> node01, node02, node03, node04, node05 - Comma-separated: node[1,3,5] -> node1, node3, node5 - Mixed: node[1-3,7] -> node1, node2, node3, node7 - Cartesian product: rack[1-2]-node[1-3] -> 6 hosts - File input: ^/path/to/file reads hosts from file Integrate with -H option, --filter, and --exclude options. Add 52 unit tests for hostlist module.
Security & Performance ReviewAnalysis Summary
Prioritized Issue RoadmapHIGH - Potential Resource Exhaustion via HostfileFile: Issue: The Current code: pub fn parse_hostfile(path: &Path) -> Result<Vec<String>, HostlistError> {
let content = std::fs::read_to_string(path).map_err(|e| {
// ... error handling
})?;
// No size check before processingRecommendation: Add a maximum file size check before reading: const MAX_HOSTFILE_SIZE: u64 = 1_048_576; // 1MB limit
let metadata = std::fs::metadata(path).map_err(/* ... */)?;
if metadata.len() > MAX_HOSTFILE_SIZE {
return Err(HostlistError::FileTooLarge { path: path.display().to_string(), limit: MAX_HOSTFILE_SIZE });
}MEDIUM - Code DuplicationFiles:
Issue: The functions Recommendation: Keep only the implementation in // In src/hostlist/mod.rs
pub fn is_hostlist_expression(pattern: &str) -> bool { ... }MEDIUM - Unused Sign VariableFile: Issue: The Current code: let (sign, digits) = if let Some(rest) = s.strip_prefix('-') {
(-1, rest)
} else {
(1, s)
};
// ...
let _ = sign; // value already includes sign from parseRecommendation: Remove the unnecessary sign calculation since let digits = s.strip_prefix('-').unwrap_or(s);
let padding = if digits.len() > 1 && digits.starts_with('0') {
digits.len()
} else {
0
};
let value: i64 = s.parse().map_err(/* ... */)?;MEDIUM - Missing Overflow Protection in Cartesian ProductFile: Issue: The Current protection: // Only checks final count before starting expansion
let expansion_count = pattern.expansion_count();
if expansion_count > MAX_EXPANSION_SIZE { ... }Recommendation: Add a check within the expansion loop or use let new_capacity = results.len()
.checked_mul(values.len())
.ok_or_else(|| HostlistError::RangeTooLarge { /* ... */ })?;
if new_capacity > MAX_EXPANSION_SIZE {
return Err(HostlistError::RangeTooLarge { /* ... */ });
}LOW - Potential Off-by-One Edge Case in Range CountFile: Issue: The Current code: fn count(&self) -> usize {
match self {
RangeItem::Single(_) => 1,
RangeItem::Range { start, end } => {
if end >= start {
(end - start + 1) as usize
} else {
0 // This code path is dead since parser rejects reversed ranges
}
}
}
}Recommendation: Consider using RangeItem::Range { start, end } => {
debug_assert!(end >= start, "Reversed ranges should be rejected by parser");
(*end - *start + 1) as usize
}Positive FindingsThe implementation demonstrates several security best practices:
Test Coverage AssessmentWell covered:
Could benefit from additional tests:
SummaryThis is a well-implemented feature with good test coverage and security considerations. The main issues are:
Overall, the PR is in good shape for merge after addressing the HIGH severity hostfile size limit issue. |
) * fix: Address PR review findings - resource limits and code cleanup HIGH Priority Fixes: - Add resource exhaustion protection in parse_hostfile(): * Maximum file size limit of 1 MB * Maximum line count limit of 100,000 lines * Check file size before reading to prevent DoS attacks MEDIUM Priority Fixes: - Remove code duplication: * Move is_hostlist_expression() and looks_like_hostlist_range() from src/main.rs and src/app/nodes.rs to src/hostlist/mod.rs * Export functions as public API from hostlist module * Update all call sites to use the exported functions - Remove unused variable: * Remove unused 'sign' variable from parse_number() in parser.rs - Add overflow protection: * Use checked_mul() for cartesian product allocations in expander.rs * Return RangeTooLarge error if overflow would occur * Prevent integer overflow in intermediate calculations All changes compile successfully and pass tests (cargo test, cargo clippy). * docs: Add HOSTLIST EXPRESSIONS section and update option docs in manpage
Summary
src/hostlist/module with parser, expander, and error types-H,--filter, and--excludeoptionsFeatures
node[1-5]node[01-05]node[1,3,5]node[1-3,7]rack[1-2]-node[1-3]web[1-3].example.com^/path/to/fileTest plan
Closes #98