Skip to content

Configure tenant hygiene for a release

The source readiness workflow scans frozen Git blobs and ten candidate archives. Repository secret TENANT_HYGIENE_POLICY contains the maintainer's deny list, not a PyPI credential. Configure it under GitHub repository settings, Secrets and variables → Actions. Never commit the real policy or print it.

Policy formats

Existing newline-delimited policies retain exact, case-sensitive UTF-8 byte matching. Each nonempty line is a literal; there are no comments or regexes. A version header explicitly selects structured JSON. Existing literal lines, including lines beginning with {, retain their meaning. The header family #!dpone.tenant-hygiene-policy. is reserved; unknown versions fail closed:

#!dpone.tenant-hygiene-policy.v2
{
  "schema": "dpone.tenant-hygiene-policy.v2",
  "terms": ["ExampleTenant", "example_pipeline"],
  "jira_prefixes": ["ABC"],
  "detectors": ["rfc1918"]
}

Replace fictional terms with the approved inventory directly in GitHub. All four fields are required; unknown or duplicate fields are rejected. Terms must be nonempty. Prefixes and detectors may be empty lists. Each list allows 1024 entries, each entry 256 Unicode characters; the complete policy is bounded to 1 MiB. Invalid or unavailable policy means UNABLE_TO_CERTIFY.

Matching rules and coverage

Terms use Unicode NFC and case folding, then remove ASCII spaces, tabs, newlines, carriage returns, underscores, hyphens, dots, slashes and backslashes. The remaining term must contain only letters or digits. Matching is substring based, including inside hostnames, filesystem paths and configuration values. Other characters, including invalid UTF-8 bytes, remain barriers. Arbitrary Unicode lookalikes and encoded/obfuscated strings are outside this contract.

Jira prefixes contain ASCII letters. Matching permits the same separators between prefix letters and requires at least one separator before numeric issue digits. A key cannot be embedded inside a longer word. A bare prefix is not a key.

The optional rfc1918 detector validates complete dotted IPv4 addresses against exact RFC1918 networks. Loopback, documentation networks, invalid octets and longer dotted strings do not match. Private addresses in examples and tests also produce findings; their location does not exempt them.

Provide concrete cloud project/namespace values and mobile application IDs in terms. Company-name terms also match identifiers containing those names. Unspecified, unrelated identifiers are outside verified coverage; the scanner cannot infer a private inventory from generic identifier syntax.

Corporate paths embedded in compiled Python bytes are scanned with UTF-8 surrogate escape. No bytecode is deserialized or executed. Compiled artifacts without matching values are not automatically classified as corporate material.

Execution and failure handling

Source paths, blob bodies, archive member paths and bodies share one matcher. Structured policies read each size-bounded archive member completely before matching, preventing missed Unicode/separator matches at read boundaries. The 64 MiB member limit admits the supported SqlClient native dependency while bounding decoded and normalized representations. The 1 GiB per-archive aggregate limit and compression-ratio guard remain in force. Legacy policies retain streaming literal matching.

Wheel metadata *.dist-info/RECORD is interpreted as strict UTF-8 CSV rather than arbitrary prose. The scanner computes SHA-256 and byte counts while scanning every regular member. It excludes only the checksum column after verifying exact equality with those observations, complete membership, unique rows, three columns, and the empty checksum/size of RECORD itself. Paths and sizes remain subject to the policy; every referenced member's full body is still scanned. Ordinary files named RECORD, Git blobs and sdists have no checksum exemption. This prevents random base64 checksum substrings from being mistaken for tenant identifiers.

Malformed, incomplete or inconsistent RECORD metadata fails closed. The supported release-builder format is SHA-256; other algorithms and signed wheel layouts are not certified by this path. This is not a general wheel-format validator: archive fragments without RECORD retain full content scanning. Existing archive size, compression, path, link and replacement guards still apply. No private deny-list values are needed to validate this behavior. The layout follows the PyPA wheel specification.

Frozen source identity, no-follow archive access, replacement detection, the remaining archive limits, report schema and exit codes are unchanged. Reports contain safe codes and paths, never matched values. Protected paths are redacted. A finding returns FAIL / exit 2; invalid or unsafe input returns UNABLE_TO_CERTIFY / exit 3.

After integrating the scanner, dispatch source-release-readiness.yml on protected master. Retain both successful jobs and their original artifacts as described in Agent release protocol. Local scans are diagnostic and do not replace CI-owned release evidence. Investigate without printing sensitive snippets; remove accidental material through normal review rather than relaxing the policy to make a release pass.

Implementation and validation scope

This additive correction implements the approved tenant hygiene design and the maintainer's request for case/separator variants and private addresses. The policy module owns parsing/matching; the scanner retains Git/archive boundaries and reports. The CLI loads its fixed sibling in isolated stdlib-only execution. No runtime API or publication authority changes.

Regressions cover legacy behavior, Cyrillic, case folding, separators, malformed policies, address and Jira boundaries, protected paths, compiled-path bytes and archive read boundaries. Tests use fictional identifiers. Record actual checks and remaining coverage gaps in release evidence.