Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Update LiquibaseCommandLineTest expectedHelpOutput for the three new ALLOW_* audit flags#7760

Merged
v-petrovych merged 1 commit into
liquibase:mainfrom
v-petrovych:cli-help-output-fixture-for-audit-allow-flags
May 25, 2026
Merged

Update LiquibaseCommandLineTest expectedHelpOutput for the three new ALLOW_* audit flags#7760
v-petrovych merged 1 commit into
liquibase:mainfrom
v-petrovych:cli-help-output-fixture-for-audit-allow-flags

Conversation

@v-petrovych
Copy link
Copy Markdown
Contributor

TL;DR

Three audit-batch PRs that landed in main recently — #7747 (CWE-78 allowExecuteCommand), #7748 (CWE-470 allowCustomChange), #7750 (CWE-22 allowParentDirectoryReferences) — each added a new GlobalConfiguration.builder.define() entry that picocli auto-renders into --help output. None of those PRs updated the byte-exact expectedHelpOutput snapshot in LiquibaseCommandLineTest.groovy. This PR adds the three missing stanzas at the alphabetical positions picocli actually emits.

Captured the stanzas verbatim from a local mvn test -Dtest='LiquibaseCommandLineTest#help output' run; not hand-written, so the wrap points exactly match what picocli renders.

Why this regression wasn't caught at PR-author time

picocli reflects over LiquibaseConfiguration.getRegisteredDefinitions() at CLI startup (see LiquibaseCommandLine.addGlobalArguments line 1203) and adds an OptionSpec per registered definition automatically. Authors of new flags get the --help rendering "for free" without touching CLI code, which is exactly when it's easy to forget there's a downstream snapshot test pinning every byte of that output.

The snapshot is a single ~691-line inline string literal in LiquibaseCommandLineTest, which doesn't show up in IDE call-graphs of GlobalConfiguration.ALLOW_CUSTOM_CHANGE (it's literally just a string). Each of the three authors (same author, three different PRs) missed the linkage three times in a row.

Why it surfaced in CI now, not before

Maven's reactor stops at the first module-level test failure. On the liquibase-pro subtree-sync runs, liquibase-standard's MSSQLDatabaseTest.getTargetUniquenessAttributes_allAuthMethods_produceSameUrl was failing first (the regression fixed in #7756). The reactor never advanced to liquibase-cli, so this fixture gap stayed invisible. After #7756 merged and the subtree-sync was retried, the reactor advanced past liquibase-standard, and LiquibaseCommandLineTest.'help output':854 immediately tripped.

The fix

Three new stanzas inserted at the alphabetical positions picocli emits:

Position New stanza
Before --allow-duplicated-changeset-identifiers --allow-custom-change=PARAM (#7748 / CWE-470)
Between --allow-duplicated-… and --allow-inherit-logical-file-path --allow-execute-command=PARAM (#7747 / CWE-78)
Between --allow-inherit-logical-file-path and --always-drop-instead-of-replace --allow-parent-directory-references=PARAM (#7750 / CWE-22)

Alphabetical ordering rationale:

  • Within allow-*: c < d < e < i < p — so custom-change comes first, parent-directory-references comes last.
  • allow-* (a-l-l-o-w) < always-* (a-l-w-a-y-s) because l < w at position 2 of the suffix.

Each stanza is the verbatim output of picocli's .usage() renderer for that flag, including picocli's specific line-wrap points. The stanzas are NOT hand-written. Hand-editing risks drifting from picocli's rendering on the next build — capturing them as picocli emits them is the only way to keep this snapshot stable.

Captured-output methodology (for future fixture updates)

For anyone adding a new GlobalConfiguration flag and needing to update this fixture:

  1. Temporarily add new File("target/help-output-actual.txt").text = bytes.toString() to the 'help output' test's when: block.
  2. Run mvn test -Dtest='LiquibaseCommandLineTest#help output' — it'll fail, but target/help-output-actual.txt will contain the full picocli output.
  3. Extract the new flag's stanza from that file verbatim (option header + description + DEFAULT + defaults-file/env-var trailer).
  4. Insert at the alphabetical position in expectedHelpOutput.
  5. Revert the diagnostic line.
  6. Re-run the test to confirm green.

This is the procedure I used to generate this PR.

Test plan

[INFO] Tests run: 115, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS  (liquibase-cli reactor)

Full liquibase-cli reactor passes (115/115 specs across ParameterUtilTest, CommandLineArgumentValueProviderTest, LiquibaseCommandLineProCommandTest, LiquibaseCommandLineTest 40 incl. 'help output', LiquibaseCommandLineThreadingTest, LiquibaseLauncherTest, ProCommandErrorMessageTest).

Things to be aware of

Coordination

  • liquibase-pro#3860 (the subtree-sync PR currently red on this test) will go green on LiquibaseCommandLineTest.'help output' after this PR merges and a follow-up subtree-sync pulls it in.
  • The remaining red check on Upgrade installer JVM to 17.0.6+10  #3860 (PostgreSQLIntegrationTest.testStatusRunDuringUpdate NPE in AlternateConnectionExecutor.<init>) is a pre-existing test-fixture wiring issue, unrelated to this PR.

Related

Part of the May-2026 OSS credential-handling-and-changelog-injection audit slice (sdou label).

…ree new ALLOW_* flags

Three upstream PRs from the May-2026 audit batch each added a new
GlobalConfiguration.builder.define() entry that picocli auto-renders into
the CLI --help output as a global option, but none of the three PRs
updated the byte-exact snapshot fixture in
LiquibaseCommandLineTest.expectedHelpOutput. Surfaces in liquibase-pro
subtree-sync CI (LiquibaseCommandLineTest.'help output':854) once the
sibling MSSQL regression (DAT-23093 / #7756) cleared and the Maven
reactor advanced past liquibase-standard into liquibase-cli.

Missed PRs (all merged into upstream main, all by the same author):

  #7747  CWE-78  allowExecuteCommand                — declared at GlobalConfiguration:57
  #7748  CWE-470 allowCustomChange                  — declared at GlobalConfiguration:56
  #7750  CWE-22  allowParentDirectoryReferences     — declared at GlobalConfiguration:58

Add three picocli-rendered stanzas to expectedHelpOutput at the
alphabetical insertion points the renderer actually emits (captured
verbatim from a local 'mvn test -Dtest=LiquibaseCommandLineTest#help
output' run after the missing flags were declared):

  - --allow-custom-change=PARAM
      inserted BEFORE --allow-duplicated-changeset-identifiers
      (alphabetical: 'c' < 'd' within 'allow-*')

  - --allow-execute-command=PARAM
      inserted BETWEEN --allow-duplicated-changeset-identifiers
      and --allow-inherit-logical-file-path
      (alphabetical: 'd' < 'e' < 'i')

  - --allow-parent-directory-references=PARAM
      inserted BETWEEN --allow-inherit-logical-file-path
      and --always-drop-instead-of-replace
      (alphabetical: 'i' < 'p' within 'allow-*'; 'allow-*' < 'always-*'
      since 'l' < 'w' at position 2 of the suffix)

Each stanza copies the description text VERBATIM from picocli's
.usage() renderer — including picocli's specific line-wrap points,
which depend on description length, terminal width assumptions, and
where word boundaries fall. Hand-editing the stanzas would risk
drifting from picocli's rendering on the next build; capturing them
as picocli emits is the only way to keep the snapshot stable.

Test: full liquibase-cli reactor passes (115 / 115 specs across
ParameterUtilTest, CommandLineArgumentValueProviderTest,
LiquibaseCommandLineProCommandTest, LiquibaseCommandLineTest (40 incl.
'help output'), LiquibaseCommandLineThreadingTest, LiquibaseLauncherTest,
ProCommandErrorMessageTest).

Once this merges and a follow-up subtree-sync pulls it into
liquibase-pro, the 'help output' assertion at line 854 of the synced
fixture file flips green.

Note for the audit-batch follow-up: a similar fixture-drift gap will
appear when the remaining open PRs in this batch (#7749 customPrecondition,
#7753 sqlCheck, #7754 includeAll filter/comparator classes, #7755
external changelog paths) merge into main, because each declares a new
GlobalConfiguration entry. Each of those PRs should ideally include its
own LiquibaseCommandLineTest fixture update before merging; if any merge
without one, a follow-up of this PR's shape will be needed for it.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 684cfdac-4eae-4d93-b4a4-c44a7764f924

📥 Commits

Reviewing files that changed from the base of the PR and between 0bff9f7 and 75ac557.

📒 Files selected for processing (1)
  • liquibase-cli/src/test/groovy/liquibase/integration/commandline/LiquibaseCommandLineTest.groovy

📝 Walkthrough

Walkthrough

The pull request updates LiquibaseCommandLineTest.groovy to verify expanded help text output for three global command-line options: --allow-custom-change, --allow-execute-command, and --allow-parent-directory-references. Each section includes behavior descriptions, defaults, and corresponding configuration keys.

Changes

CLI help text for security-related options

Layer / File(s) Summary
Expected help output for security options
liquibase-cli/src/test/groovy/liquibase/integration/commandline/LiquibaseCommandLineTest.groovy
Test assertion expectedHelpOutput is expanded with full parameter documentation for three security-related global options, including descriptions of load-time risks, shell command validation, path containment checks, DEFAULT values, and associated defaults-file/environment-variable keys.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Possibly related PRs

  • liquibase/liquibase#7748: Introduces the --allow-custom-change global configuration flag and validation logic that this test's expanded help text documents.
  • liquibase/liquibase#7747: Adds the --allow-execute-command global option and validation that the test now verifies in CLI help output.
  • liquibase/liquibase#7750: Implements the --allow-parent-directory-references configuration that this test's expanded help text reflects.

Suggested labels

sdou

Suggested reviewers

  • wwillard7800
  • filipelautert
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: updating the expectedHelpOutput snapshot for three new ALLOW_* audit flags.
Description check ✅ Passed The PR description provides comprehensive context including root cause analysis, methodology, test results, and forward-looking risks, though it does not follow the template structure with explicit Impact/Release note/Things to be aware of sections.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@v-petrovych v-petrovych requested a review from abrackx May 25, 2026 11:42
@v-petrovych v-petrovych merged commit 8ed8669 into liquibase:main May 25, 2026
8 of 12 checks passed
@v-petrovych v-petrovych deleted the cli-help-output-fixture-for-audit-allow-flags branch May 25, 2026 13:26
v-petrovych added a commit that referenced this pull request May 28, 2026
…w allowExternalChangelogPaths flag

This PR introduces ALLOW_EXTERNAL_CHANGELOG_PATHS in GlobalConfiguration
(allowExternalChangelogPaths=true by default). The help-output snapshot
reflects over all registered config definitions, so the new flag appears in
--help and the fixture needs the matching block.

Block captured byte-exact from picocli's actual --help output. Mirrors the
#7760 pattern for the three already-merged ALLOW_* audit flags (this branch's
merge from origin/main brought those in; this commit adds the fourth).

Note on the UNC double-backslash: the description references the leading
'\\' UNC path prefix. In Groovy triple-quoted source the inserted text is
escaped as '\\\\' (four source backslashes → two runtime backslashes) so
the runtime fixture matches picocli's literal output. The other 35 block
lines apply verbatim.

LiquibaseCommandLineTest 40/40 green locally.
v-petrovych added a commit that referenced this pull request May 28, 2026
…put for the new allowIncludeAllClasses flag

This PR introduces ALLOW_INCLUDE_ALL_CLASSES in GlobalConfiguration
(allowIncludeAllClasses=true by default), governing the includeAll
resourceFilter/resourceComparator class-name attributes. The help-output
snapshot reflects over all registered config definitions, so the new flag
appears in --help and the fixture needs the matching block.

Block captured byte-exact from picocli's actual --help output, inserted
alphabetically between --allow-execute-command and --allow-inherit-logical-
file-path. Mirrors the #7760 pattern for the three already-merged ALLOW_*
audit flags (the merge from origin/main brought those in; this commit adds
the fourth).

LiquibaseCommandLineTest 40/40 green locally.
v-petrovych added a commit that referenced this pull request May 28, 2026
…w allowSqlPrecondition flag

This PR introduces ALLOW_SQL_PRECONDITION in GlobalConfiguration
(allowSqlPrecondition=true by default), governing the <sqlCheck> changelog
precondition. The help-output snapshot reflects over all registered config
definitions, so the new flag appears in --help and the fixture needs the
matching block.

Block captured byte-exact from picocli's actual --help output, inserted
alphabetically after --allow-parent-directory-references and before
--always-drop-instead-of-replace. Mirrors the #7760 pattern for the three
already-merged ALLOW_* audit flags (merge from origin/main brought those in;
this commit adds the fourth).

LiquibaseCommandLineTest 40/40 green locally.
filipelautert pushed a commit that referenced this pull request May 28, 2026
…7761) (#7764)

* CWE-316: make CommandScope.clearCredentialArguments() opt-in — fixes #7761

The original CWE-316 fix (PR #7741, commit 4850342) added a
`finally { clearCredentialArguments(); }` block at the end of
CommandScope.execute() to overwrite credential-bearing argument values
with "*****" so the original Strings would become GC-eligible after the
command ran.

That broke every caller that calls execute() more than once on the same
CommandScope: the second call reads "*****" from argumentValues, passes
it to the JDBC driver, and the driver rejects the connection with
"FATAL: password authentication failed for user '<lbuser>'".

Surfaced by liquibase.dbtest.pgsql.PostgreSQLIntegrationTest
.testStatusRunDuringUpdate, which executes the same CommandScope three
times (before / during / after a concurrent liquibase.update()). The
first execution succeeded, the second failed with the password auth
error.  Upstream main has been red on this test since PR #7741 landed
(2026-05-21 12:15 UTC); the regression became visible after the May-22+
subtree-sync attempts into liquibase-pro PR #3860 and again on upstream
main after PR #7760 landed and freed the reactor to advance past
liquibase-standard.

Why the regression sat in main for four days
============================================

PR #7741 was authored from a fork. Upstream's run-tests.yml workflow
gates its `authorize` job on `environment: external` whenever the PR's
head repo differs from github.repository. That gate requires a
maintainer to click "Review deployments → Approve and run" before the
workflow can proceed. The gate was never approved on #7741's pre-merge
build; the workflow sat in `status: waiting`; no Run Test for (Java N
<OS>) or Integration Test (<db>) job ever materialised; the PR's 9
green checks were all from non-gated workflows (CodeQL / label /
claude-review). The merge button did not block on a workflow that
never produced any check at all.

After merge, the post-merge `push` to main triggered the workflow with
the `internal` environment (no manual approval needed). By that point
the merge was already in. Same shape for PR #7760 (the DAT-23096
fixture fix that finally let the reactor advance past liquibase-cli to
liquibase-integration-tests on the subtree-sync).

Option A — opt-in clearing
==========================

Three options were discussed in the upstream issue:

  A. Make the clearing opt-in: add clearCredentialArguments() as a
     public method on CommandScope; remove the finally block; call it
     explicitly at single-use call sites (e.g. Main's CLI runners).
     Preserves CWE-316 intent for CLI sessions without breaking long-
     lived / re-used scopes elsewhere. Recommended.

  B. Gate the clearing on a markSingleUse() flag (backwards-compatible
     default). Possible, but adds API surface and a state machine
     that's easy to forget.

  C. Clear on scope close rather than per-execute. Requires
     CommandScope to be AutoCloseable; bigger refactor; same forget-
     to-close risk as B.

This PR implements Option A. The other two are not pursued.

Changes
=======

1) CommandScope.java

   - Removed the outer `finally { clearCredentialArguments(); }` block
     from execute() (between the second-to-last catch and the closing
     brace at the method's bottom).

   - Changed clearCredentialArguments() from package-private to public
     so external callers can invoke it explicitly.

   - Updated both the CREDENTIAL_KEY_TOKENS doc comment and the
     clearCredentialArguments() method doc to document the new
     caller-invoked contract, including the specific re-use scenario
     that the auto-finally broke and the recommended try-finally
     pattern for single-use callers.

2) Main.java

   - Added a private static helper executeAndClearCredentials(CommandScope)
     that wraps execute() in try-finally + clearCredentialArguments()
     and returns CommandResults. Used in place of bare execute() at
     every CLI command-runner site (15 sites: snapshotCommand,
     dropAllCommand, calculateChecksumCommand, historyCommand,
     diffCommand, diffChangelogCommand, updateCommand,
     rollbackOneChangeSet (x2), rollbackOneUpdate (x2),
     executeSql, three bare commandScope.execute() sites).

   - Net effect: CLI users get the same CWE-316 protection they had
     before #7741's regression-causing fix landed, but the protection
     now lives at the call site (where the single-use assumption is
     true) rather than inside execute() (where the assumption was
     false for the integration tests and any library / programmatic
     caller that re-uses a scope).

3) CommandScopeTest.groovy

   - Updated the two existing specs that asserted auto-clearing in
     execute()'s finally to assert the new opt-in semantics (execute()
     alone does NOT touch credential values; explicit
     clearCredentialArguments() does).

   - Added a new regression spec
     "execute called twice on the same scope reads original credentials
     both times — regression for PostgreSQLIntegrationTest
     .testStatusRunDuringUpdate" that calls execute() three times on
     the same scope and uses a MockCommandStep that captures
     argumentValues["password"] each time the pipeline runs. All three
     captured values must equal the original credential. This is the
     unit-level regression coverage for the integration-test scenario
     that surfaced the bug — running it at unit speed means future
     re-introductions are caught at PR-author time without needing a
     live postgres in the loop.

   - Added a new spec
     "caller-invoked try-finally pattern around execute clears
     credentials post-execution (CWE-316 wiring documented for CLI
     integrators)" that demonstrates and pins the recommended usage
     pattern: the pipeline sees the original credential during
     execute(), and the credential is wiped immediately after the call
     returns. This is what Main.executeAndClearCredentials() does
     internally; the spec documents the contract so external callers
     have a reference.

Tests
=====

All passing locally on this branch:

  CommandScopeTest                      22 / 22  (3 new specs)
  LiquibaseCommandLineTest              40 / 40  (incl. 'help output')
  liquibase-cli full reactor           115 / 115
  liquibase-standard Command*-related  204 / 204
  MainTest                              32 / 32

(Total 413 specs across the directly-impacted set. Did not run the live-
postgres integration tests locally — Docker not available; see the
"Validation" section in the PR description.)

Validation
==========

Per the validation checklist filed alongside this fix (Pro internal
DAT-23097 last comment, mirrored into the upstream issue), this PR
needs explicit confirmation that the run-tests.yml workflow actually
ran pre-merge. If reviewers see the workflow stuck in `status: waiting`
on the `authorize` job, please click "Review deployments → Approve and
run" before merging — that's the gate that previously hid both PR #7741
and PR #7760 regressions for days.

Closes #7761.

* CWE-316: empty commit to re-trigger gated CI workflows after maintainer review approval (#7763)

* CWE-316: fix pre-existing no-op in runGenerateChangelogCommandStep

`Main.runGenerateChangelogCommandStep()` built the CommandScope for the
legacy CLI `generateChangelog` command but never called .execute() on it,
making the command a silent no-op via the Main.java entry point. The bug
predates this PR (also present on upstream main at 8ed8669) but CodeRabbit
flagged it while reviewing the executeAndClearCredentials helper this PR
introduces, so applying the fix here with that helper is the natural
landing place.
filipelautert pushed a commit that referenced this pull request May 28, 2026
…put for the new allowIncludeAllClasses flag

This PR introduces ALLOW_INCLUDE_ALL_CLASSES in GlobalConfiguration
(allowIncludeAllClasses=true by default), governing the includeAll
resourceFilter/resourceComparator class-name attributes. The help-output
snapshot reflects over all registered config definitions, so the new flag
appears in --help and the fixture needs the matching block.

Block captured byte-exact from picocli's actual --help output, inserted
alphabetically between --allow-execute-command and --allow-inherit-logical-
file-path. Mirrors the #7760 pattern for the three already-merged ALLOW_*
audit flags (the merge from origin/main brought those in; this commit adds
the fourth).

LiquibaseCommandLineTest 40/40 green locally.
filipelautert pushed a commit that referenced this pull request May 28, 2026
…w allowSqlPrecondition flag

This PR introduces ALLOW_SQL_PRECONDITION in GlobalConfiguration
(allowSqlPrecondition=true by default), governing the <sqlCheck> changelog
precondition. The help-output snapshot reflects over all registered config
definitions, so the new flag appears in --help and the fixture needs the
matching block.

Block captured byte-exact from picocli's actual --help output, inserted
alphabetically after --allow-parent-directory-references and before
--always-drop-instead-of-replace. Mirrors the #7760 pattern for the three
already-merged ALLOW_* audit flags (merge from origin/main brought those in;
this commit adds the fourth).

LiquibaseCommandLineTest 40/40 green locally.
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.

2 participants