Skip to content

Fix: BoxLang signature failure — use isNull() instead of structKeyExists(arguments,...) for optional date overrides - #47

Merged
lmajano merged 20 commits into
developmentfrom
fix/boxlang-signature-date-null-check
Sep 15, 2026
Merged

lmajano merged 20 commits into
developmentfrom
fix/boxlang-signature-date-null-check

Conversation

@lmajano

@lmajano lmajano commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Every signed S3 request (PUT/GET/DELETE/etc.) fails on the boxlang@1 engine with:

Error: Required argument dateStamp is missing for function buildCredentialScope
  at models/Sv4Util.cfc:105

Root cause

In generateSignatureData() (Sv4Util.cfc and the equivalent in Sv2Util.cfc), amzDate and dateStamp are optional parameters with no default:

string amzDate,
string dateStamp

Both call sites in AmazonS3.cfc (s3Request() and getAuthenticatedURL()) omit both entirely, relying on the function to generate them internally. The override check was:

if ( structKeyExists( arguments, "amzDate" ) || structKeyExists( arguments, "dateStamp" ) ) {
    props.dateStamp = arguments.dateStamp;
    props.amzDate   = arguments.amzDate;
}

structKeyExists(arguments, "x") is not a safe way to test "was this optional, no-default argument passed" across engines. On engines with full null support — which is BoxLang's default (unlike Lucee's default partial null support) — an unpassed, no-default argument still exists as a key holding null, so structKeyExists() returns true for it. That makes the if branch run even though neither argument was actually passed, so props.dateStamp = arguments.dateStamp assigns a real null. That null then flows into buildCredentialScope( required string dateStamp, ... ), which BoxLang correctly rejects as a missing required argument.

Lucee/Adobe don't hit this because with their default null handling, an unpassed no-default argument simply isn't a key in arguments at all, so structKeyExists() correctly returns false.

Fix

Check each optional argument independently with isNull(), which works correctly regardless of an engine's null-support model, and also correctly handles the case where only one of the two is passed (the original code would have blindly read the other as null in that case too).

Applied the identical fix to MiniLogBox.cfc's debug()/error()/warn(), which had the same structKeyExists( arguments, "data" ) pattern for its optional data parameter — same root cause, lower-severity symptom (silently logs a spurious null entry instead of throwing, since data isn't a required downstream argument).

Other BoxLang findings

This repo's own CI matrix (.github/workflows/tests.yml) tests boxlang-cfml@1 (BoxLang's CFML-compatibility mode) but not the native boxlang@1 engine, which is why this went uncaught — boxlang-cfml@1's null-handling compatibility shim apparently masks it. A grep across models/**/*.cfc for the structKeyExists( arguments, ... ) anti-pattern turned up no other occurrences beyond the ones fixed here.

Test plan

  • Confirm signed S3 requests succeed under boxlang@1 (this was discovered via coldbox-modules/cbfs#57, which pulls this module and can be used to verify)

🤖 Generated with Claude Code

https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk


Generated by Claude Code

claude and others added 5 commits September 14, 2026 20:04
…nal date overrides

structKeyExists(arguments, "x") is not a safe way to test whether an
optional, no-default argument was passed: on engines with full null
support (BoxLang defaults to this, unlike Lucee's default partial null
support), an unpassed argument still exists as a key holding null, so
structKeyExists() returns true.

In generateSignatureData() (Sv4Util.cfc and Sv2Util.cfc), both callers
in AmazonS3.cfc omit amzDate and dateStamp entirely, but on BoxLang
structKeyExists(arguments, "amzDate") is still true, so the code reads
arguments.dateStamp (a real null) and passes it into
buildCredentialScope()'s required dateStamp parameter, which BoxLang
correctly rejects as missing:

  Error: Required argument dateStamp is missing for function buildCredentialScope

This breaks every signed S3 request (PUT/GET/DELETE/etc.) on the
boxlang@1 engine. Check each optional argument independently with
isNull() instead, which is null-support-safe on every engine and
degrades correctly when only one of the two is actually passed.

Applied the same fix to MiniLogBox.cfc's debug/error/warn(), which had
the identical structKeyExists(arguments, "data") pattern for its
optional data parameter.

Note: this repo's own CI matrix (.github/workflows/tests.yml) tests
boxlang-cfml@1 but not the native boxlang@1 engine, which is why this
went uncaught.
…ng support, refresh docs and CI matrix

- copyObject()/renameObject(): remove manually-set Content-Length header
  that duplicated CFHTTP's own header on bodyless requests, causing
  SignatureDoesNotMatch on Adobe CF and BoxLang
- Sv4Util.cfc/Sv2Util.cfc: fix dateFormat() mask from "yyyymmdd" (minutes)
  to "yyyyMMdd" (month) for cross-engine correctness
- Fix server-boxlang-cfml@1.json leftover aliases copied from another module
- Add native BoxLang (boxlang@1) server config and CI matrix entry
- CI matrix: drop lucee@5, adobe@2018, adobe@2021; add lucee@6,
  adobe@2023, adobe@2025; bump ColdBox to ^8
- box.json: swap commandbox-dotenv/commandbox-cfconfig devDependencies
  for commandbox-boxlang; update shortDescription
- test-harness/box.json: bump coldbox dependency to ^8
- Rewrite readme.md (BoxLang-first, expanded usage/config/testing docs)
  and update changelog.md
- Add AGENTS.md with engine-portability guidance for coding agents
- Run cfformat --overwrite across the project

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk
Breaking changes in this branch (dropped Adobe 2018/2021, Lucee 5,
ColdBox 7 support) warrant a major version bump per semver.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk
@github-actions

github-actions Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

adobe@2025 Test Results - Coldbox be

  1 files   12 suites   34s ⏱️
109 tests 108 ✅ 1 💤 0 ❌
110 runs  109 ✅ 1 💤 0 ❌

Results for commit eb23954.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

adobe@2025 Test Results - Coldbox ^8

  1 files   12 suites   12s ⏱️
109 tests 108 ✅ 1 💤 0 ❌
110 runs  109 ✅ 1 💤 0 ❌

Results for commit eb23954.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

boxlang-cfml@1 Test Results - Coldbox ^8

  1 files   12 suites   14s ⏱️
109 tests 108 ✅ 1 💤 0 ❌
110 runs  109 ✅ 1 💤 0 ❌

Results for commit eb23954.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

boxlang@1 Test Results - Coldbox be

  1 files   12 suites   35s ⏱️
109 tests 108 ✅ 1 💤 0 ❌
110 runs  109 ✅ 1 💤 0 ❌

Results for commit eb23954.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

lucee@6 Test Results - Coldbox ^8

  1 files   12 suites   13s ⏱️
109 tests 108 ✅ 1 💤 0 ❌
110 runs  109 ✅ 1 💤 0 ❌

Results for commit eb23954.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

boxlang@1 Test Results - Coldbox ^8

  1 files   12 suites   34s ⏱️
109 tests 108 ✅ 1 💤 0 ❌
110 runs  109 ✅ 1 💤 0 ❌

Results for commit eb23954.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

lucee@6 Test Results - Coldbox be

  1 files   12 suites   13s ⏱️
109 tests 108 ✅ 1 💤 0 ❌
110 runs  109 ✅ 1 💤 0 ❌

Results for commit eb23954.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

boxlang-cfml@1 Test Results - Coldbox be

  1 files  ±0   12 suites  ±0   25s ⏱️ -3s
109 tests ±0  108 ✅ +8  1 💤 ±0  0 ❌ ±0 
110 runs  ±0  109 ✅ +8  1 💤 ±0  0 ❌ ±0 

Results for commit eb23954. ± Comparison against base commit c43bc3f.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

adobe@2023 Test Results - Coldbox ^8

  1 files   12 suites   17s ⏱️
109 tests 108 ✅ 1 💤 0 ❌
110 runs  109 ✅ 1 💤 0 ❌

Results for commit eb23954.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

adobe@2023 Test Results - Coldbox be

  1 files   12 suites   1m 35s ⏱️
109 tests 108 ✅ 1 💤 0 ❌
110 runs  109 ✅ 1 💤 0 ❌

Results for commit eb23954.

♻️ This comment has been updated with latest results.

- AmazonS3Spec.cfc isOldACF() unconditionally read
  server.coldfusion.productVersion, which doesn't exist on native
  BoxLang's server scope, crashing the whole test bundle on boxlang@1.
  Guard with structKeyExists( server, "coldfusion" ) first.
- CI: force-install latest commandbox-cfconfig before starting any
  server, since the version bundled with the pinned CommandBox CLI has
  no config provider for adobe@2025 yet ("Sorry, no config provider
  could be found for [adobe@2025...]").

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk

lmajano commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

Remaining CI failure: not fixable from this PR

After the fixes pushed here (Content-Length duplication, dateFormat() month/minutes mask, isNull() null-checks, native BoxLang isOldACF() crash, and the Adobe 2025 CFConfig provider gap), the only failures left on lucee@6 and boxlang-cfml@1 are the 6 "encryption" specs in AmazonS3Spec.cfc (customer-provided-key / SSE-C tests): can put encrypted with custom encryption key, can copy/rename encrypted file with custom encryption key, can get presigned URL for encrypted file with custom encrypted key, can put encrypted with custom encryption key and custom algorithm, can use default encryption key.

All 6 fail identically with:

Code: AccessDenied
Message: User: arn:aws:iam::233317242204:user/s3sdk is not authorized to perform: s3:PutObject on resource: "...bucket.../encrypted.txt" because this bucket has blocked upload requests that specify Server Side Encryption with Customer provided keys (SSE-C). Please specify a different server-side encryption type.

This is an AWS bucket policy (or account-level policy) that now explicitly denies SSE-C uploads, applied uniformly across every dynamically-named test bucket this suite creates. It's not a code bug in this repo — there's no client-side fix that makes AWS accept an upload the bucket policy explicitly denies. It will need one of:

  • the bucket/account policy updated to allow SSE-C for the s3sdk IAM user's test buckets, or
  • the SSE-C test cases reworked to use SSE-S3/SSE-KMS instead (a real scope/coverage change, not a bug fix)

I haven't made that call since it affects test coverage/security posture and I don't have AWS console access to check the policy. Flagging it here rather than leaving it silently red — let me know which direction you'd like and I can push the test-suite change.


Generated by Claude Code


Generated by Claude Code

claude and others added 13 commits September 14, 2026 20:37
- test-harness/box.json: bump testbox devDependency from "be" to "*"
  to get TestBox's isBoxLang()/isLucee()/isAdobe() helpers on a stable
  release (7.1.0 also fixes isLucee() incorrectly returning true on
  BoxLang).
- AmazonS3Spec.cfc: replace structKeyExists(server, "lucee"),
  isNull(server.lucee) and server.keyExists("boxlang") with TestBox's
  isAdobe()/isLucee()/isBoxLang().
- The bucket used by CI test runs blocks SSE-C (customer-provided key)
  uploads at the policy level. The 6 "customer encryption key" specs
  now exercise SSE-S3 (encryptionAlgorithm) instead of SSE-C
  (encryptionKey) so they can run against this bucket. The SDK's SSE-C
  support itself is unchanged for callers whose bucket allows it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk
…O array

- Sv4UtilSpec.cfc: .listToArray() member-function syntax isn't resolved
  on Adobe ColdFusion ("The listToArray method was not found"), only on
  Lucee/BoxLang. Switched to the portable listToArray(string, delimiter)
  function call. This was pre-existing and unrelated to this PR's
  changes, but promoting adobe@2023 from experimental to a required
  matrix entry surfaced it as a blocking failure.
- AmazonS3.cfc putObjectFile(): the multi-part upload path passed an
  untyped, empty CFML array to Files.newByteChannel()'s varargs
  OpenOption... parameter. Adobe's stricter Java overload resolution
  couldn't match it, throwing inside the surrounding try/catch, which
  silently fell back to a non-multipart upload ("can perform a
  multi-part upload on a file over 5MB" failing with the response not
  containing "multipart"). Now explicitly
  javacast("java.nio.file.OpenOption[]", []).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk
…ne throw() type

- CI: bump Setup Java from 11 to Temurin 17. Adobe ColdFusion 2025's
  cfpm tooling requires Java 17+ and was failing with
  UnsupportedClassVersionError (class file version 61.0 vs the
  supported 55.0) during onServerInstall.
- server-boxlang@1.json (native BoxLang): install the bx-esapi module,
  matching server-boxlang-cfml@1.json. Without it, encodeForURL()
  (used by Sv4Util.cfc's urlEncodePath()) isn't available, which
  crashed the entire AmazonS3Spec bundle at beforeAll() with
  "Function [encodeForURL] not found".
- AmazonS3.cfc: several throw() calls omitted an explicit type.
  Adobe/Lucee default an untyped throw() to type "Application", but
  BoxLang defaults it to "Custom", breaking tests that assert
  toThrow( type = "application" ). All now pass type = "Application"
  explicitly for consistent behavior across engines.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk
- server-adobe@2023.json, server-adobe@2025.json: pin javaVersion to
  openjdk21_jre, matching the BoxLang server configs, so the engine's
  own JVM is consistent and modern across the matrix.
- Sv4UtilSpec.cfc: rename the `file` parameter (in
  headersFromRequestFile()/urlParamsFromRequestFile()) to
  `requestContent`. Adobe ColdFusion treats `file` specially in some
  contexts, throwing "Complex object types cannot be converted to
  simple values" when it was passed into listToArray().

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk
Ported this fix from the already-working coldbox-modules/cbfs server
configs. On Java 17+, the module system blocks reflective access to
internal JDK packages like sun.nio.fs (which backs
java.nio.file.Files) unless explicitly opened via --add-opens. This is
very likely the real root cause of two previously-unexplained Adobe
failures: the multi-part upload path (which calls
java.nio.file.Files.newByteChannel()) silently failing inside a
try/catch and falling back to a non-multipart upload, and possibly
contributing to slow/flaky adobe@2025 startup.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk
Replace the separate Ortus-Solutions/setup-commandbox step plus manual
box install --force commandbox-boxlang/commandbox-cfconfig steps with
a single ortus-boxlang/setup-boxlang@main step (with-commandbox: true,
commandbox_modules: commandbox-boxlang,commandbox-cfconfig,
testbox-cli), matching the already-working setup in
coldbox-modules/cbfs. Also bump Setup Java from 17 to 21 to match cbfs
and the server JVM pins.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk
The multi-part upload test on Adobe fails with the response not
containing "multipart", meaning putObjectFile() silently fell back
to the non-multipart path. The actual exception is caught by an
internal try/catch and only passed to a fully-mocked logger in this
test, so it's invisible in CI output. Temporarily print the mock's
captured error() call log via systemOutput() to see the real
exception on the next CI run. To be reverted once the root cause is
confirmed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk
systemOutput() isn't recognized in this Adobe scripting context
("Variable SYSTEMOUTPUT is undefined"), so the previous diagnostic
attempt itself errored instead of printing anything. Use TestBox's
fail() helper instead, which surfaces its message through the same
"Failure: ..." reporting we already see reliably in CI output.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk
Root cause finally confirmed via a temporary diagnostic (now removed):

  coldfusion.runtime.UndefinedElementException: Element UPLOADID is
  undefined in PART.

putObjectFile()'s multi-part upload path routed concurrent part
uploads through variables.asyncManager.allApply(). On Adobe, ColdBox's
async cbproxies Function wrapper does not correctly marshal the "part"
struct argument across the async boundary, so part.uploadId is missing
inside the closure. This exception was silently caught by the
surrounding try/catch and fell back to a non-multipart upload, with no
visible error - "can perform a multi-part upload on a file over 5MB"
only failed with the response not containing "multipart", giving no
clue to the real cause.

Always use the synchronous part-upload path (the code already had a
working, near-identical fallback for engines without an async
manager) until this ColdBox/Adobe async interop issue is fixed
upstream.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk
The async-marshaling fix got the multi-part upload path actually
running on Adobe now (adobe@2023 is fully green), surfacing a
different, smaller issue on adobe@2025: the test asserted the
uploaded object's Content-Length against a file size computed BEFORE
fileWrite() ran ("Expected [6291456] but received [6291457]"). Read
the actual on-disk size via getFileInfo() after writing instead, so
the assertion holds regardless of any engine-specific fileWrite()
byte-count behavior.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk
@lmajano
lmajano merged commit 2b9d311 into development Sep 15, 2026
22 checks passed
@lmajano
lmajano deleted the fix/boxlang-signature-date-null-check branch September 15, 2026 10:22
lmajano added a commit that referenced this pull request Sep 15, 2026
* Fix: BoxLang signature failure — use isNull() instead of structKeyExists(arguments,...) for optional date overrides (#47)

* fix: use isNull() instead of structKeyExists(arguments,...) for optional date overrides

structKeyExists(arguments, "x") is not a safe way to test whether an
optional, no-default argument was passed: on engines with full null
support (BoxLang defaults to this, unlike Lucee's default partial null
support), an unpassed argument still exists as a key holding null, so
structKeyExists() returns true.

In generateSignatureData() (Sv4Util.cfc and Sv2Util.cfc), both callers
in AmazonS3.cfc omit amzDate and dateStamp entirely, but on BoxLang
structKeyExists(arguments, "amzDate") is still true, so the code reads
arguments.dateStamp (a real null) and passes it into
buildCredentialScope()'s required dateStamp parameter, which BoxLang
correctly rejects as missing:

  Error: Required argument dateStamp is missing for function buildCredentialScope

This breaks every signed S3 request (PUT/GET/DELETE/etc.) on the
boxlang@1 engine. Check each optional argument independently with
isNull() instead, which is null-support-safe on every engine and
degrades correctly when only one of the two is actually passed.

Applied the same fix to MiniLogBox.cfc's debug/error/warn(), which had
the identical structKeyExists(arguments, "data") pattern for its
optional data parameter.

Note: this repo's own CI matrix (.github/workflows/tests.yml) tests
boxlang-cfml@1 but not the native boxlang@1 engine, which is why this
went uncaught.

* Apply cfformat changes

* Fix Content-Length duplication and date format bugs, add native BoxLang support, refresh docs and CI matrix

- copyObject()/renameObject(): remove manually-set Content-Length header
  that duplicated CFHTTP's own header on bodyless requests, causing
  SignatureDoesNotMatch on Adobe CF and BoxLang
- Sv4Util.cfc/Sv2Util.cfc: fix dateFormat() mask from "yyyymmdd" (minutes)
  to "yyyyMMdd" (month) for cross-engine correctness
- Fix server-boxlang-cfml@1.json leftover aliases copied from another module
- Add native BoxLang (boxlang@1) server config and CI matrix entry
- CI matrix: drop lucee@5, adobe@2018, adobe@2021; add lucee@6,
  adobe@2023, adobe@2025; bump ColdBox to ^8
- box.json: swap commandbox-dotenv/commandbox-cfconfig devDependencies
  for commandbox-boxlang; update shortDescription
- test-harness/box.json: bump coldbox dependency to ^8
- Rewrite readme.md (BoxLang-first, expanded usage/config/testing docs)
  and update changelog.md
- Add AGENTS.md with engine-portability guidance for coding agents
- Run cfformat --overwrite across the project

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk

* Bump version to 6.0.0 to target this as a major release

Breaking changes in this branch (dropped Adobe 2018/2021, Lucee 5,
ColdBox 7 support) warrant a major version bump per semver.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk

* Fix native BoxLang test crash and Adobe 2025 CFConfig gap in CI

- AmazonS3Spec.cfc isOldACF() unconditionally read
  server.coldfusion.productVersion, which doesn't exist on native
  BoxLang's server scope, crashing the whole test bundle on boxlang@1.
  Guard with structKeyExists( server, "coldfusion" ) first.
- CI: force-install latest commandbox-cfconfig before starting any
  server, since the version bundled with the pinned CommandBox CLI has
  no config provider for adobe@2025 yet ("Sorry, no config provider
  could be found for [adobe@2025...]").

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk

* Use TestBox engine-detection helpers; switch SSE-C tests to SSE-S3

- test-harness/box.json: bump testbox devDependency from "be" to "*"
  to get TestBox's isBoxLang()/isLucee()/isAdobe() helpers on a stable
  release (7.1.0 also fixes isLucee() incorrectly returning true on
  BoxLang).
- AmazonS3Spec.cfc: replace structKeyExists(server, "lucee"),
  isNull(server.lucee) and server.keyExists("boxlang") with TestBox's
  isAdobe()/isLucee()/isBoxLang().
- The bucket used by CI test runs blocks SSE-C (customer-provided key)
  uploads at the policy level. The 6 "customer encryption key" specs
  now exercise SSE-S3 (encryptionAlgorithm) instead of SSE-C
  (encryptionKey) so they can run against this bucket. The SDK's SSE-C
  support itself is unchanged for callers whose bucket allows it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk

* Fix Adobe-specific failures: listToArray member syntax and untyped NIO array

- Sv4UtilSpec.cfc: .listToArray() member-function syntax isn't resolved
  on Adobe ColdFusion ("The listToArray method was not found"), only on
  Lucee/BoxLang. Switched to the portable listToArray(string, delimiter)
  function call. This was pre-existing and unrelated to this PR's
  changes, but promoting adobe@2023 from experimental to a required
  matrix entry surfaced it as a blocking failure.
- AmazonS3.cfc putObjectFile(): the multi-part upload path passed an
  untyped, empty CFML array to Files.newByteChannel()'s varargs
  OpenOption... parameter. Adobe's stricter Java overload resolution
  couldn't match it, throwing inside the surrounding try/catch, which
  silently fell back to a non-multipart upload ("can perform a
  multi-part upload on a file over 5MB" failing with the response not
  containing "multipart"). Now explicitly
  javacast("java.nio.file.OpenOption[]", []).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk

* Fix Adobe 2025 Java version, native BoxLang ESAPI gap, and cross-engine throw() type

- CI: bump Setup Java from 11 to Temurin 17. Adobe ColdFusion 2025's
  cfpm tooling requires Java 17+ and was failing with
  UnsupportedClassVersionError (class file version 61.0 vs the
  supported 55.0) during onServerInstall.
- server-boxlang@1.json (native BoxLang): install the bx-esapi module,
  matching server-boxlang-cfml@1.json. Without it, encodeForURL()
  (used by Sv4Util.cfc's urlEncodePath()) isn't available, which
  crashed the entire AmazonS3Spec bundle at beforeAll() with
  "Function [encodeForURL] not found".
- AmazonS3.cfc: several throw() calls omitted an explicit type.
  Adobe/Lucee default an untyped throw() to type "Application", but
  BoxLang defaults it to "Custom", breaking tests that assert
  toThrow( type = "application" ). All now pass type = "Application"
  explicitly for consistent behavior across engines.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk

* Pin Adobe 2023/2025 to JRE 21; fix reserved-name param on Adobe

- server-adobe@2023.json, server-adobe@2025.json: pin javaVersion to
  openjdk21_jre, matching the BoxLang server configs, so the engine's
  own JVM is consistent and modern across the matrix.
- Sv4UtilSpec.cfc: rename the `file` parameter (in
  headersFromRequestFile()/urlParamsFromRequestFile()) to
  `requestContent`. Adobe ColdFusion treats `file` specially in some
  contexts, throwing "Complex object types cannot be converted to
  simple values" when it was passed into listToArray().

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk

* Add --add-opens java.base/sun.nio.fs JVM arg to Adobe server configs

Ported this fix from the already-working coldbox-modules/cbfs server
configs. On Java 17+, the module system blocks reflective access to
internal JDK packages like sun.nio.fs (which backs
java.nio.file.Files) unless explicitly opened via --add-opens. This is
very likely the real root cause of two previously-unexplained Adobe
failures: the multi-part upload path (which calls
java.nio.file.Files.newByteChannel()) silently failing inside a
try/catch and falling back to a non-multipart upload, and possibly
contributing to slow/flaky adobe@2025 startup.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk

* CI: use ortus-boxlang/setup-boxlang action, matching cbfs

Replace the separate Ortus-Solutions/setup-commandbox step plus manual
box install --force commandbox-boxlang/commandbox-cfconfig steps with
a single ortus-boxlang/setup-boxlang@main step (with-commandbox: true,
commandbox_modules: commandbox-boxlang,commandbox-cfconfig,
testbox-cli), matching the already-working setup in
coldbox-modules/cbfs. Also bump Setup Java from 17 to 21 to match cbfs
and the server JVM pins.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk

* TEMP: print swallowed multipart exception for CI diagnosis

The multi-part upload test on Adobe fails with the response not
containing "multipart", meaning putObjectFile() silently fell back
to the non-multipart path. The actual exception is caught by an
internal try/catch and only passed to a fully-mocked logger in this
test, so it's invisible in CI output. Temporarily print the mock's
captured error() call log via systemOutput() to see the real
exception on the next CI run. To be reverted once the root cause is
confirmed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk

* TEMP: fix diagnostic - systemOutput() undefined on Adobe, use fail()

systemOutput() isn't recognized in this Adobe scripting context
("Variable SYSTEMOUTPUT is undefined"), so the previous diagnostic
attempt itself errored instead of printing anything. Use TestBox's
fail() helper instead, which surfaces its message through the same
"Failure: ..." reporting we already see reliably in CI output.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk

* Apply cfformat changes

* Fix multi-part upload on Adobe: bypass broken async closure marshaling

Root cause finally confirmed via a temporary diagnostic (now removed):

  coldfusion.runtime.UndefinedElementException: Element UPLOADID is
  undefined in PART.

putObjectFile()'s multi-part upload path routed concurrent part
uploads through variables.asyncManager.allApply(). On Adobe, ColdBox's
async cbproxies Function wrapper does not correctly marshal the "part"
struct argument across the async boundary, so part.uploadId is missing
inside the closure. This exception was silently caught by the
surrounding try/catch and fell back to a non-multipart upload, with no
visible error - "can perform a multi-part upload on a file over 5MB"
only failed with the response not containing "multipart", giving no
clue to the real cause.

Always use the synchronous part-upload path (the code already had a
working, near-identical fallback for engines without an async
manager) until this ColdBox/Adobe async interop issue is fixed
upstream.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk

* Fix off-by-one Content-Length assertion in multipart test on Adobe 2025

The async-marshaling fix got the multi-part upload path actually
running on Adobe now (adobe@2023 is fully green), surfacing a
different, smaller issue on adobe@2025: the test asserted the
uploaded object's Content-Length against a file size computed BEFORE
fileWrite() ran ("Expected [6291456] but received [6291457]"). Read
the actual on-disk size via getFileInfo() after writing instead, so
the assertion holds regardless of any engine-specific fileWrite()
byte-count behavior.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MY8vKDaEubRApVMT9BXSYk

* finalized ai integrations

* more updates

* updated to module standards

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: lmajano <lmajano@users.noreply.github.com>

* fix markdown issues

* updates

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: lmajano <lmajano@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants