@@ -203,25 +203,33 @@ def test_password_not_logged_when_line_invalid(caplog: pytest.LogCaptureFixture)
203203 assert secret not in combined , f"Password leaked into logs on invalid row: { combined !r} "
204204
205205
206- def test_password_with_comma_partially_masks (caplog : pytest .LogCaptureFixture ) -> None :
207- """A password containing commas is misaligned by the naive split parser: only the
208- portion that lands in column 1 gets masked. The remaining fragments still leak.
209- This documents the limitation — fully protecting passwords with embedded commas
210- requires a proper CSV parser — but confirms that the column-1 mask holds even
211- when the password value contains a comma."""
212- line = "jsmith,hunter2,SECRETTAIL,creator,site,yes,jsmith@example.com"
206+ def test_password_with_comma_is_fully_redacted (caplog : pytest .LogCaptureFixture ) -> None :
207+ """A quoted comma in a password must not leak from invalid row output or logs."""
208+ line = 'jsmith,"hunter2,SECRETTAIL",John Smith,not-a-real-license,site,yes,jsmith@example.com'
209+ test_data = _mock_file_content ([line ])
213210 with caplog .at_level (logging .DEBUG , logger = logger .name ):
214- try :
215- TSC .UserItem .CSVImport ._validate_import_line_or_throw (line , logger )
216- except Exception :
217- pass # misaligned columns are expected to fail validation
211+ valid , invalid = TSC .UserItem .CSVImport .validate_file_for_import (test_data , logger )
212+ assert valid == 0
213+ assert len (invalid ) == 1
214+ assert "hunter2" not in invalid [0 ]
215+ assert "SECRETTAIL" not in invalid [0 ]
218216 combined = "\n " .join (record .getMessage () for record in caplog .records )
219- # Column 1 ("hunter2") is masked; the fragment that spilled into column 2
220- # ("SECRETTAIL") is not — this is the documented limitation.
221217 assert "hunter2" not in combined
218+ assert "SECRETTAIL" not in combined
222219 assert _mask_present (caplog .records )
223220
224221
222+ def test_malformed_csv_row_is_not_returned_unredacted (caplog : pytest .LogCaptureFixture ) -> None :
223+ secret = "SECRETTAIL"
224+ test_data = _mock_file_content ([f'jsmith,"{ secret } ' ])
225+ with caplog .at_level (logging .DEBUG , logger = logger .name ):
226+ valid , invalid = TSC .UserItem .CSVImport .validate_file_for_import (test_data , logger )
227+ assert valid == 0
228+ assert invalid == ["<invalid CSV row redacted>" ]
229+ combined = "\n " .join (record .getMessage () for record in caplog .records )
230+ assert secret not in combined
231+
232+
225233def test_password_not_in_validate_attribute_value_error (
226234 caplog : pytest .LogCaptureFixture , monkeypatch : pytest .MonkeyPatch
227235) -> None :
@@ -310,6 +318,12 @@ def test_create_user_with_auth_column() -> None:
310318 assert user .auth_setting == "SAML" , f"Expected SAML, got { user .auth_setting } "
311319
312320
321+ def test_create_user_with_comma_in_password () -> None :
322+ user = TSC .UserItem .CSVImport .create_user_from_line ('username,"pass,word",fname,creator,none,yes,email' )
323+ assert user is not None
324+ assert user .fullname == "fname"
325+
326+
313327def test_too_many_columns_raises () -> None :
314328 with pytest .raises (ValueError ):
315329 TSC .UserItem .CSVImport .create_user_from_line ("u, p, n, creator, none, yes, email, SAML, extra" )
0 commit comments