Skip to content

fix(audit): close the five PR #84 review follow-ups as a five-layer stack - #90

Merged
getdownandcode merged 7 commits into
mainfrom
chore/repo-uv-lock
Oct 26, 2026
Merged

getdownandcode merged 7 commits into
mainfrom
chore/repo-uv-lock

Conversation

@getdownandcode

@getdownandcode getdownandcode commented Oct 26, 2026 •

Copy link
Copy Markdown
Owner

Summary

Closes the five follow-up issues filed from the PR #84 review (#85–#89) as a linear five-layer stack, one commit per layer, plus a progress-bookkeeping commit that records the stack in docs/PROGRESS.md and a review-fix commit for the finding Codex raised on this PR. This PR is the tip, so reviewing commit-by-commit walks the layers in dependency order and merging lands all six commits on main at once.

# Issue Branch Commit Fix
1 #86 fix/deploy-credential-url-passwords 3242d6d The credential guard audits passwords embedded in explicit DATABASE_URL / DATABASE_READONLY_URL / REDIS_URL with the same policy as components, requires a password in an explicit URL, reports malformed URLs instead of raising, and stops demanding component variables an explicit URL does not use.
2 #87 refactor/deploy-credential-logging 1eefc8b The guard logs its problems through logging instead of print(), with handler setup confined to __main__.
3 #85 test/real-infra-provisioning-failures 06c20dd The live SQL-isolation fixture no longer converts a provisioning failure into a skip; the upstream fixture already fails loudly when PostgreSQL is unreachable, so a broken provisioner now turns CI red.
4 #88 docs/audit-history-consistency a202693 docs/AUDIT.md records the canonical 12-layer / 14-commit history, and the stale "Strict SELECT AST validation" row in docs/PROGRESS.md is corrected; both are guarded by the docs-accuracy test.
5 #89 chore/repo-uv-lock d3d3e95 uv.lock is ignored instead of appearing as untracked noise; CI installs from pyproject.toml with pip, so a lockfile is not part of the current build.
— bookkeeping — 59c1499 docs/PROGRESS.md gains one row per follow-up pointing at this PR and a combined Detailed Task Notes section.
— review fix — 713735b Codex finding: a malformed Redis URL such as redis://[broken made urlsplit() raise before validation, so the guard crashed instead of reporting it.

What changed, per issue

  • fix(deploy): reject weak passwords embedded in explicit datastore URLs #86 (guard correctness) — audit_credentials() still resolves URLs through resolve_postgres_url() / apply_redis_password(), so it audits exactly what Settings uses. URL-supplied secrets now go through _is_weak_password() (defaults, placeholders, <12 chars), an explicit URL with no password is reported, and make_url() failures become "<VAR> is not a valid connection URL." without echoing SQLAlchemy's parse error or the secret. Component passwords are validated when present but only required when they are the source of the resolved URL.
  • refactor(scripts): use logging instead of print in the deploy credential guard #87 (logging) — main() emits the header and one ERROR record per problem; basicConfig() lives in the if __name__ == "__main__" block. Exit code and message text are unchanged.
  • test(real-infra): fail instead of skipping when read-only role provisioning errors #85 (test reliability) — readonly_engine calls provision_readonly_role() directly. real_database_url already applies migrations and fails with a clear message on an unreachable database, so the skip was masking real defects.
  • docs(audit): align the stack counts and remove the stale AST claim in PROGRESS #88 (docs) — canonical wording: "a linear 12-layer stack, merged as 14 commits (12 layer commits plus two progress-bookkeeping commits)". M4-T04 now describes the lexer + dedicated role instead of an AST parser. tests/unit/test_docs_accuracy.py pins both.
  • chore(repo): commit or ignore the untracked uv.lock #89 (repo hygiene) — .gitignore gains uv.lock with a comment explaining that the pip-based CI does not consume it. Adopting uv properly remains a separate decision.
  • Review fix (713735b) — _resolve_redis_url() wraps apply_redis_password(), which calls urlsplit() before the audit parses anything. A malformed IPv6 authority (redis://[broken) now yields "REDIS_URL is not a valid connection URL." instead of a traceback, and a failed split suppresses the follow-on "no password" message because there is no URL to inspect. Parametrised tests cover the unsplittable authority and a bad port; the guard was also run by hand and exits 1 with the problem line only.

Test plan

Run on the tip, all green:

ruff check .                      # All checks passed
ruff format --check .             # 125 files already formatted
mypy agentkit tests               # Success: no issues found in 107 source files
pytest -q --cov=agentkit --cov-fail-under=85
                                  # passed, coverage 93.32%

The guard was also run by hand against a mixed weak configuration and exits 1 with one line per problem; the live PostgreSQL/Redis job exercises the changed #85 fixture.

Merge note

Use a merge commit, not squash, so the five layer commits (plus the bookkeeping and review commits) stay individually reviewable in main history.

Closes #85
Closes #86
Closes #87
Closes #88
Closes #89

The credential guard applied the weak-password policy only to the POSTGRES_* and REDIS_PASSWORD components. A URL that carries its own userinfo bypassed it: DATABASE_URL was only checked for exact membership in the known-password set, the read-only URL was never policy-checked, and REDIS_URL was checked for presence alone, so 'redis://:short@host' passed the 12-character rule. Malformed URLs also crashed the guard with a traceback instead of reporting a deployment problem, and a URL-only deployment was scolded for component variables it does not use.

Audit the credentials actually used: resolve each URL exactly as Settings does, apply the same weak-password policy to URL-supplied secrets, require a password in an explicit URL, and validate a component only when it is the resolution source (still checking any component that is set). Parse each URL once through a helper that reports 'is not a valid connection URL' without echoing the SQLAlchemy parse error or the secret. Add regression tests for weak URL passwords, URL-only configuration, missing passwords and malformed URLs.
AGENTS.md forbids print() in favour of module logging. The deployment guard wrote its failure report straight to stderr, which also made the message untestable through caplog and bypassed any logging configuration the caller had installed.

Emit the header and each problem through the module logger at ERROR, leaving handler configuration to the __main__ block so importing the module still has no side effects. The compose migrations service keeps stderr output via basicConfig, the exit code is unchanged, and the test asserts the log records; a second test pins the no-print rule.
The readonly_engine fixture treated every exception from provision_readonly_role() as 'environment unavailable' and skipped the test. A regression in the security-critical provisioner would therefore turn most of the SQL isolation tier green-by-skip, even though the upstream real_database_url fixture already fails loudly when PostgreSQL is unreachable (it applies migrations first).

Let the provisioning error propagate so CI goes red. The upstream fixture still skips when AGENTKIT_REAL_INFRA is unset, so local development ergonomics are unchanged.
AUDIT-16 corrected the README but left two inconsistent claims behind. docs/AUDIT.md still described the remediation as 'a linear stack of nine branches', while the merged PR contains a 12-layer stack (14 commits once the two progress-bookkeeping commits are counted); the title, body and docs each used a different number. The historical M4-T04 row in docs/PROGRESS.md still said the SQL tool uses 'Strict SELECT AST validation', although the implementation is a lexer with a dedicated read-only role as the primary control — exactly the claim the README correction removed.

Record the canonical 12-layer/14-commit description in AUDIT.md, rewrite the M4-T04 row to match the code, and extend the docs-accuracy test so neither claim can drift back.
uv.lock was untracked and not ignored, so every checkout reported it as noise and contributors could not tell whether dependency resolution was meant to be reproducible. The project installs from pyproject.toml with pip in all three CI jobs, and nothing consumes a uv lock, so tracking one would be misleading.

Ignore it explicitly and document why in the ignore file. Adopting uv properly (commit the lock and switch CI to 'uv sync --locked') remains a separate decision.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-26T17:10:42.408926Z 713735b Manual request
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

The five PR #84 follow-ups (#85-#89) landed as a stacked PR but were missing from the execution log, so the table still ended at AUDIT-18-FU and the detailed notes stopped at AUDIT-20.

Add one row per follow-up pointing at #90 and a combined Detailed Task Notes section that records what each layer changed and why.

@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: d3d3e958cc

ℹ️ 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 scripts/check_deploy_credentials.py Outdated
or ""
)
raw_redis = (env.get("REDIS_URL") or "").strip()
redis_url = apply_redis_password(raw_redis, env.get("REDIS_PASSWORD"))

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 Catch Redis URL splitting errors before applying credentials

When REDIS_PASSWORD is set and REDIS_URL contains a malformed IPv6 authority such as redis://[broken, apply_redis_password() raises ValueError in urlsplit() before _parse_url() can report the malformed URL. The guard then exits with a traceback instead of returning the promised actionable validation error.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 713735b.

apply_redis_password() does split the URL before the audit ever reaches _parse_url(), so the finding was real: redis://[broken with REDIS_PASSWORD set killed the guard with ValueError: Invalid IPv6 URL.

  • _resolve_redis_url() now wraps the call and turns a split failure into REDIS_URL is not a valid connection URL.
  • A failed split also suppresses the follow-on "has no password" line, since there is no URL left to inspect.
  • test_malformed_redis_url_is_reported_instead_of_raising covers both redis://[broken (split error) and redis://host:notaport/0 (parse error).
  • By hand: the guard exits 1 with that single problem line and no traceback.

All four CI jobs are green on 713735b.

Codex review of #90: with REDIS_PASSWORD set, apply_redis_password() splits REDIS_URL with urlsplit() before the audit parses it. A malformed IPv6 authority such as redis://[broken makes urlsplit raise ValueError, so the guard died with a traceback instead of returning the actionable problem line the rest of the audit produces.

Resolve the Redis URL through a helper that catches the split error and reports 'REDIS_URL is not a valid connection URL.' A failed split also suppresses the follow-on 'has no password' message, since there is no URL to inspect. Add regression tests for the unsplittable authority and for a bad port, and verified by hand that the guard exits 1 with the problem line and no traceback.
@getdownandcode

Copy link
Copy Markdown
Owner Author

Review fix pushed as 713735b — "fix(deploy): report malformed Redis URLs instead of crashing".

What Codex caught: with REDIS_PASSWORD set, apply_redis_password() calls urlsplit() on REDIS_URL before _parse_url() runs, so redis://[broken raised ValueError: Invalid IPv6 URL and the guard died with a traceback instead of printing a problem line.

What changed:

  • scripts/check_deploy_credentials.py — new _resolve_redis_url() wraps apply_redis_password(), catches the split ValueError, and reports REDIS_URL is not a valid connection URL.; it also suppresses the redundant "has no password" message when the URL could not be split.
  • tests/unit/test_deploy_credentials.py — test_malformed_redis_url_is_reported_instead_of_raising parametrised over redis://[broken (split error) and redis://host:notaport/0 (parse error), asserting the exact single problem.

Verification on 713735b:

ruff check . / ruff format --check . / mypy agentkit tests   all clean
pytest --cov-fail-under=85                                   passed, 93.32%
REDIS_URL='redis://[broken' python scripts/check_deploy_credentials.py
  ERROR __main__:   - REDIS_URL is not a valid connection URL.   exit=1

CI: Lint & Type Check, Test Hermetic (3.11, 3.12) and Test Against Live PostgreSQL and Redis all pass. PR body updated with the review-fix row.

@codex review

@getdownandcode
getdownandcode merged commit 2d85e7d into main Oct 26, 2026
4 checks passed
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: 713735b4d4

ℹ️ 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".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant