Repository navigation
Conversation
…e import on one bad row CsvLoader::from_string validated `type` and `category` independently (validation::validate_type / validate_category) but never called validate_type_category_pair, so a syntactically valid but incompatible pair like type=md5,category=Person was accepted client-side and would only be rejected later by the MISP server. Confirmed against the pinned MISP checkout: app/Model/MispAttribute.php checks categoryDefinitions[$category]['types'] and rejects a type not listed for that category (see the pair check around line 997-998, backed by categoryDefinitions built at line ~4168). Separately, every per-row validation error and the CSV parse error used `?`, so one malformed or invalid row discarded every attribute already parsed from earlier rows in the same call. Fix: call validate_type_category_pair once per row (it also covers the individual type/category checks), and collect per-row errors instead of propagating them immediately. A row that fails is skipped and logged via log::warn!; the good rows before and after it are preserved. The existing MispResult<Vec<MispAttribute>> API shape is unchanged - an error is returned only when every attempted row failed (aggregating all row errors) or the CSV structure itself is invalid (e.g. missing required columns), matching the two existing single-row error tests. Added csv_rejects_mismatched_type_category_pair (fails before the fix: md5/Person was previously accepted) and csv_skips_bad_rows_but_keeps_good_ones (fails before the fix: the good row was previously discarded along with the bad one). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JoSButbVaWEtGMXwTx73BZ
elhoim
marked this pull request as draft
September 3, 2026 05:05
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
BLUF —
CsvLoader::from_stringnow validates type/category pairs and skips bad rows instead of aborting.CsvLoader::from_stringinsrc/tools/csv_loader.rsvalidated type and category independently, never callingvalidate_type_category_pair, and every per-row error used?, discarding already-parsed rows.validate_type_category_pairper row and collects row errors withcontinuepluslog::warn!; it errors only when every row failed or the CSV structure is invalid.type=md5,category=Personare rejected client-side rather than by the server, and one malformed line no longer loses a good import. Two tests added.ubuntu-22.04runner lacks php8.3mod_php;test_event_crud_lifecycleraces the publish worker), fixed in unmerged PR fix: [ci] Run integration tests on ubuntu-24.04 to match php8.3 #53 and unrelated to this change.Finding
type/category validated independently; pair validator never called, and one bad row aborts the import
validate_type_category_pair exists for exactly this, so type=md5,category=Person is accepted and rejected later by the server. Separately at :66 one malformed row discards every successfully parsed row.
Change
CsvLoader::from_string validated type and category independently and never called validate_type_category_pair, so type=md5,category=Person passed client-side validation despite the server rejecting that pair. Separately, every per-row error (parse error, type error, category error) used
?, aborting the whole import and discarding every already-parsed row. Fixed by calling validate_type_category_pair once per row (it subsumes the individual checks) and by collecting per-row errors with continue instead of propagating them via?; bad rows are skipped and logged with log::warn!, good rows before/after are preserved. The public MispResult<Vec> signature is unchanged: an error is returned only when every attempted row failed or the CSV structure itself is invalid, so the two existing single-row-error tests (csv_invalid_type, csv_invalid_category) still pass unmodified.Upstream evidence
MISP core app/Model/MispAttribute.php:997-998 checks
in_array($fields['type'], $this->categoryDefinitions[$category]['types'], true)and rejects the attribute if the type is not listed for that category; categoryDefinitions is built from category_type_mappings-equivalent data at line ~4168-4170. This confirms a syntactically valid type + syntactically valid category can still be an invalid pair the server rejects, exactly the case validate_type_category_pair (src/validation.rs:103-119) exists to catch client-side. PyMISP's api.py has no bearing here since this is a purely client-side CSV convenience loader with no corresponding upstream PyMISP method to compare against.Verified against MISP core
843b6744and PyMISPe7debb4f.Tests
csv_rejects_mismatched_type_category_paircsv_skips_bad_rows_but_keeps_good_onesNote on the original finding
Accurate on both counts. (1) validate_type_category_pair existed but was never called - confirmed by reading validation.rs and csv_loader.rs; type and category were checked with the standalone validate_type/validate_category functions only. (2) One malformed row did abort the whole import via
?inside the row loop, discarding all previously-parsed rows in that call - confirmed by reading the original loop body. No overstatement or understatement found; the fix matches the suggested approach (call the pair validator, collect per-row errors, preserve API shape).Caveats
Did not add a test for the CSV-parse-error path at the original :66
map_err(...)?(now also collect-and-continue) - withflexible(true)and guaranteed-UTF-8 string input, that branch is effectively unreachable from from_string's public surface, so csv_skips_bad_rows_but_keeps_good_ones (which exercises the invalid-type per-row skip path) is the representative regression test for the "one bad row aborts everything" half of the finding. Both new tests were verified by construction to fail on the pre-fix code (mismatched pair was previously Ok(1); mixed valid/invalid rows previously returned Err) - could not run cargo test in this environment per the task's build/test restriction, so this is a by-inspection guarantee, not a captured red/green run.Verified locally:
cargo fmt --check,cargo clippy --all-targets --all-features -- -D warnings,cargo test --all-features --lib,--test integration_tests --no-run, andcargo check --examples, each in an isolated target directory.🤖 Generated with Claude Code