Skip to content

Add cpf-lgpd scanner package with CLI, docs, tests and CI - #1

Merged
analistarj merged 9 commits into
mainfrom
codex/realizar-diagnostico-do-repositorio-cpf_lgpd
Aug 21, 2026
Merged

analistarj merged 9 commits into
mainfrom
codex/realizar-diagnostico-do-repositorio-cpf_lgpd

Conversation

@analistarj

Copy link
Copy Markdown
Owner

Motivation

  • Replace an ad-hoc Active Directory script with a packaged, local scanner that discovers mathematically valid CPFs while minimizing exposure and never persisting raw CPF values.
  • Provide explainable contextual classification, risk scoring and safe remediation guidance to support privacy and security workflows.
  • Add robust extractors, safe handling for archives/OCR/office formats, and atomic, permissioned report writing to reduce operational risk.
  • Improve project hygiene with tests, documentation, security policy and a CI pipeline.

Description

  • Removed the legacy CPF_indevidos script and introduced a new cpf_lgpd package with modules for classification, extractors, scanner, risk, reporting, permissions, configuration and a CLI in cli.py.
  • Implemented deterministic CPF validation and HMAC-based deduplication, contextual analyzers that do not retain original excerpts, and redact_cpfs to mask CPF candidates in paths and output.
  • Added robust extraction logic for text, PDF, Office XML, legacy formats, images (OCR) and archives with extraction limits and error classes to avoid zip-bombs and unsafe XML constructs.
  • Added reporting helpers that write JSON and CSV atomically with restrictive permissions, a docs/METODOLOGIA.md, SECURITY.md, an expanded README.md, and a pyproject.toml exposing the cpf-lgpd CLI entry point.
  • Introduced unit tests under tests/ covering classification, scanning, extractors and CLI behavior, and added a GitHub Actions workflow .github/workflows/ci.yml that runs linting, unit tests and coverage.

Testing

  • Linting was run with ruff check . as part of CI and the project includes coverage settings in pyproject.toml.
  • Unit tests were executed with python -m unittest discover -v (covered by coverage run -m unittest discover) and the repository includes tests/test_classification.py and tests/test_scanner.py.
  • The CI steps (ruff, unit tests and coverage report) were configured and ran successfully in the validation environment.

Codex Task

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 90db5da4c7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread cpf_lgpd/classification.py Outdated
Comment on lines +145 to +146
self._analyze_record(self._buffer[:boundary])
self._buffer = self._buffer[boundary:]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve matches across internal buffer splits

When a newline-free record exceeds maximum_buffer, this analyzes the prefix and retains only the configured context tail without any regex overlap. A valid 11-digit CPF that straddles boundary is split into two non-matching fragments and is silently omitted; for example, with the defaults, placing a CPF at the internal cutoff of a text line longer than 64 KiB produces zero findings. Retain enough overlap for the longest CPF representation or defer parsing the boundary fragment.

Useful? React with 👍 / 👎.

Comment thread cpf_lgpd/extractors.py Outdated
Comment on lines +172 to +173
total = 0
members = 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Share archive limits across nested archives

For nested archives, these counters are reset on every recursive _archive call, so max_archive_size and max_archive_members apply independently to each inner archive rather than to the top-level input. An outer ZIP can therefore contain many small, highly compressed inner ZIPs, each expanding up to the full limit; the scanner may process hundreds of gigabytes despite a 200 MiB configured cap, and the processing deadline is only checked after the entire top-level file returns. Track a shared extraction budget through recursion.

Useful? React with 👍 / 👎.

Comment thread cpf_lgpd/cli.py Outdated
print("Relatorio protegido gravado com sucesso.")
if args.csv_report:
print("Relatorio CSV protegido gravado com sucesso.")
return 1 if result.valid_cpfs else 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Report timed-out scans as incomplete

When max_processing_seconds expires before any CPF is encountered, scan_directory sets scan_timed_out and stops early, but this return expression still exits with status 0. Without an optional JSON report, the terminal also shows no timeout indication and zero failed files, so automation and operators can interpret a partial scan as a completed scan with no CPFs. Return an operational/incomplete status when result.scan_timed_out is true and surface that condition.

Useful? React with 👍 / 👎.

Comment thread cpf_lgpd/extractors.py Outdated
Comment on lines +97 to +101
for info in archive.infolist():
if info.file_size > limits.max_member_size:
raise ExtractionLimitError("parte Office excede limite")
if info.filename in names or info.filename.startswith(prefixes):
yield _xml_text(archive.read(info)) + "\n"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Enforce aggregate limits for Office archives

DOCX and XLSX inputs only enforce max_member_size; they never apply max_archive_members or max_archive_size. A crafted workbook containing many worksheet XML entries just below the per-member limit can therefore force unbounded aggregate decompression and XML parsing, bypassing the zip-bomb limits used for ordinary archives. Count members and cumulative uncompressed bytes before reading selected Office entries.

Useful? React with 👍 / 👎.

Comment thread cpf_lgpd/configuration.py Outdated
Comment on lines +39 to +40
weights = ScoreWeights(**_known_values(ScoreWeights, values.pop("weights", {})))
governance_values = _known_values(GovernanceMetadata, values.pop("governance", {}))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject invalid configuration types without a traceback

If a user supplies syntactically valid JSON with a wrong nested type, such as "weights": null, _known_values receives a non-mapping and raises TypeError. The CLI only catches OSError and ValueError, so this configuration error escapes as a traceback instead of the documented operational exit code 2; similar failures occur for scalar extensions and string numeric fields. Validate the JSON value types and raise ValueError before constructing the dataclasses.

Useful? React with 👍 / 👎.

Harden local and UNC path handling, assess NTFS and SMB ACLs, protect reports, add corporate preflight, and validate the 2.2.0rc1 package across Linux and Windows CI.
Compare canonical paths by file identity, set the executing user as the protected report owner, and cover the native ACL call contract.
@analistarj
analistarj merged commit 8d612b0 into main Aug 21, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant