Description
Contentstack CLI export/backup fails when attempting to download certain assets from https://images.contentstack.io, reporting an SSRF prevention error.
The same backup pipeline and Contentstack content successfully export when using Contentstack CLI 1.38.0.
The issue occurs with both:
- Contentstack CLI
1.46.1
- Contentstack CLI
2.0.1
CLI 1.38.0 does not produce the SSRF error and successfully completes the asset download.
Based on a comparison of the packages included with the working and failing CLI versions, this appears to potentially be related to a change in the asset download implementation introduced after CLI 1.38.0, rather than an issue with Bash or basic network connectivity.
Error
The CLI reports an error similar to:
Error: SSRF Prevention: URL "https://images.contentstack.io/v3/assets/<redacted>/bltda70d33c86925cd4/6596d62c14e77e3cde117f02/Logo_(Dar...)" is not allowed
The rejected URL is hosted on Contentstack's own images.contentstack.io domain.
An example of the URL pattern being rejected is:
https://images.contentstack.io/v3/assets/<redacted>/bltda70d33c86925cd4/6596d62c14e77e3cde117f02/Logo_(Dar...)
Some portions of the URL have been redacted.
Environment
- Contentstack CLI failing versions:
- Contentstack CLI working version:
- Node.js:
- Execution environment:
- Azure DevOps release pipeline
- Bash shell
The same backup pipeline succeeds when the Contentstack CLI is downgraded to 1.38.0.
Version Comparison
Contentstack CLI 1.38.0
Contentstack CLI 1.38.0 includes:
@contentstack/cli-cm-export ~1.15.2
@contentstack/management ~1.18.4
Inspection of the distributed @contentstack/cli-cm-export 1.15.2 package shows that assets were downloaded directly through the package's HTTP client.
The relevant implementation resembles:
self.httpClient
.options({ responseType: 'stream' })
.get(self.assetStream.url)
In simplified terms, the download path appears to be:
CLI
-> HTTP client
-> https://images.contentstack.io/...
-> asset download
This version successfully downloads the affected assets in our Azure DevOps backup pipeline.
Contentstack CLI 1.46.1
Contentstack CLI 1.46.1 includes:
@contentstack/cli-cm-export ~1.20.1
@contentstack/management ~1.22.0
Inspection of @contentstack/cli-cm-export 1.20.1 shows that the asset download implementation changed.
The export package now calls:
this.stack
.asset()
.download({ url, responseType: 'stream' })
The asset URL passed to this method is prepared using:
This is invoked through the download-asset path in the export module.
In simplified terms, the newer download path appears to be:
CLI
-> Contentstack Management SDK
-> stack.asset().download()
-> https://images.contentstack.io/...
-> asset download
This is materially different from the direct HTTP GET used by the older export implementation.
Suspected Regression
The behavioral change appears to have been introduced somewhere between:
Contentstack CLI 1.38.0
@contentstack/cli-cm-export 1.15.2
@contentstack/management 1.18.4
and:
Contentstack CLI 1.46.1
@contentstack/cli-cm-export 1.20.1
@contentstack/management 1.22.0
The significant implementation difference we identified is the change from a direct HTTP asset download to:
The behavior we are seeing is:
- CLI
1.38.0 succeeds.
- CLI
1.46.1 fails with the SSRF prevention error.
- CLI
2.0.1 also fails with the same SSRF prevention error.
- All versions are being used against the same Contentstack content and the same Azure DevOps backup pipeline.
This suggests that the SSRF rejection may be associated with the newer Management SDK-based asset download path.
We have not identified the exact commit that introduced the SSRF validation, so this should be considered a suspected regression based on the package implementation and observed behavior rather than a confirmed root cause.
Why the SSRF Rejection Appears Unexpected
The rejected URL is not an arbitrary external URL. It is an asset hosted on Contentstack's own image service:
https://images.contentstack.io/...
We therefore would not expect this URL to be rejected during a normal Contentstack stack export.
Possible areas that may be worth investigating include:
- SSRF validation performed by
stack.asset().download()
- Host/domain validation or allowlisting for
images.contentstack.io
- DNS/IP validation performed as part of SSRF prevention
- Redirect validation during asset download
- URL parsing or normalization
- Interaction between
encodeURI() and the Management SDK
- Asset filenames containing spaces, parentheses, or other special characters
- Differences between the previous direct HTTP download implementation and the newer Management SDK download implementation
The particular URL currently producing the failure contains a filename beginning with:
Because of this, URL parsing or encoding differences may also be relevant, although we have not confirmed that the filename itself contributes to the SSRF rejection.
Operational Impact
We currently cannot identify a Contentstack CLI version that reliably handles both sides of our backup/restore workflow.
CLI 2.0.1
We need to use the newer CLI because it avoids import/restore problems encountered when using older CLI versions.
However, the backup/export process encounters the SSRF prevention error when downloading certain assets.
CLI 1.46.1
This version also encounters the same SSRF prevention error during backup/export.
CLI 1.38.0
Backup/export works with this version, including downloading the affected assets.
However, we have encountered other issues with the older CLI during restore/import, including what appear to be rate-limit-related failures where some content may not be restored successfully.
As a result, remaining on 1.38.0 is not a viable long-term workaround.
Effectively, we currently have:
CLI 1.38.0
Backup/export: Works
Restore/import: Problems
CLI 1.46.1
Backup/export: SSRF failure
CLI 2.0.1
Backup/export: SSRF failure
Restore/import: Needed to avoid problems with older CLI
This prevents us from moving the backup/restore pipeline cleanly onto the current CLI release.
Expected Behavior
A Contentstack stack export should be able to download valid asset URLs hosted on:
https://images.contentstack.io
without triggering SSRF prevention.
If a Contentstack-hosted asset URL genuinely fails an SSRF security check, it would also be helpful for the CLI to report the specific reason the URL was classified as unsafe.
Actual Behavior
CLI 1.46.1 and 2.0.1 reject the Contentstack-hosted asset URL with:
SSRF Prevention: URL "<Contentstack asset URL>" is not allowed
CLI 1.38.0 successfully downloads the same asset when run through the same backup pipeline.
Reproduction Summary
At a high level:
- Run the Contentstack stack backup/export through the Azure DevOps pipeline using CLI
1.38.0.
- The affected
images.contentstack.io asset downloads successfully.
- Run the same backup/export against the same stack using CLI
1.46.1.
- The asset download fails with the SSRF prevention error.
- Repeat using CLI
2.0.1.
- The same SSRF prevention error occurs.
No Contentstack content or pipeline configuration changes are required to reproduce the difference other than changing the Contentstack CLI version.
Questions for the Contentstack Team
Could the Contentstack team please confirm:
-
Was SSRF validation added or changed in the asset download path between @contentstack/cli-cm-export 1.15.2 and 1.20.1?
-
Does stack.asset().download() perform SSRF validation that was not performed by the previous direct HTTP download implementation?
-
Should https://images.contentstack.io always be considered a valid asset download source by the Management SDK?
-
Is the SSRF validation performed locally by the Management SDK/CLI, or is the URL submitted to a Contentstack API that performs the SSRF validation server-side?
-
Can DNS resolution, IP validation, HTTP redirects, URL encoding, or particular filename characters cause a legitimate images.contentstack.io URL to fail the SSRF validation?
-
Could the use of:
before passing the URL to:
affect how certain Contentstack asset URLs are validated?
-
Is there additional debug logging we can enable that would show exactly why the URL is being classified as unsafe?
-
Is there a supported workaround that would allow us to remain on Contentstack CLI 2.0.1 while avoiding this SSRF failure?
Additional Information
We can provide the complete affected asset URL privately if required for troubleshooting, as well as additional Azure DevOps pipeline logs.
We can also perform additional diagnostics against the affected asset URL if helpful, including:
- DNS resolution from the Azure DevOps execution environment
- Redirect-chain inspection
- HTTP response/header inspection
- Testing the asset URL directly with
curl
- Testing other assets to determine whether the problem is specific to certain filenames or URLs
- Running the Contentstack CLI with additional debug/verbose logging
- Comparing behavior using the same Node.js version and execution environment across different Contentstack CLI releases
The strongest indication of a regression is currently the reproducible version-dependent behavior:
Same stack
Same assets
Same Azure DevOps pipeline
Same general execution environment
CLI 1.38.0 -> asset download succeeds
CLI 1.46.1 -> SSRF Prevention error
CLI 2.0.1 -> SSRF Prevention error
Combined with the change in the export package from a direct HTTP download to stack.asset().download(), this appears to provide a relatively narrow area for investigation.
Description
Contentstack CLI export/backup fails when attempting to download certain assets from
https://images.contentstack.io, reporting an SSRF prevention error.The same backup pipeline and Contentstack content successfully export when using Contentstack CLI
1.38.0.The issue occurs with both:
1.46.12.0.1CLI
1.38.0does not produce the SSRF error and successfully completes the asset download.Based on a comparison of the packages included with the working and failing CLI versions, this appears to potentially be related to a change in the asset download implementation introduced after CLI
1.38.0, rather than an issue with Bash or basic network connectivity.Error
The CLI reports an error similar to:
The rejected URL is hosted on Contentstack's own
images.contentstack.iodomain.An example of the URL pattern being rejected is:
Some portions of the URL have been redacted.
Environment
1.46.12.0.11.38.022.23.1The same backup pipeline succeeds when the Contentstack CLI is downgraded to
1.38.0.Version Comparison
Contentstack CLI 1.38.0
Contentstack CLI
1.38.0includes:Inspection of the distributed
@contentstack/cli-cm-export1.15.2package shows that assets were downloaded directly through the package's HTTP client.The relevant implementation resembles:
In simplified terms, the download path appears to be:
This version successfully downloads the affected assets in our Azure DevOps backup pipeline.
Contentstack CLI 1.46.1
Contentstack CLI
1.46.1includes:Inspection of
@contentstack/cli-cm-export1.20.1shows that the asset download implementation changed.The export package now calls:
The asset URL passed to this method is prepared using:
This is invoked through the
download-assetpath in the export module.In simplified terms, the newer download path appears to be:
This is materially different from the direct HTTP GET used by the older export implementation.
Suspected Regression
The behavioral change appears to have been introduced somewhere between:
and:
The significant implementation difference we identified is the change from a direct HTTP asset download to:
The behavior we are seeing is:
1.38.0succeeds.1.46.1fails with the SSRF prevention error.2.0.1also fails with the same SSRF prevention error.This suggests that the SSRF rejection may be associated with the newer Management SDK-based asset download path.
We have not identified the exact commit that introduced the SSRF validation, so this should be considered a suspected regression based on the package implementation and observed behavior rather than a confirmed root cause.
Why the SSRF Rejection Appears Unexpected
The rejected URL is not an arbitrary external URL. It is an asset hosted on Contentstack's own image service:
We therefore would not expect this URL to be rejected during a normal Contentstack stack export.
Possible areas that may be worth investigating include:
stack.asset().download()images.contentstack.ioencodeURI()and the Management SDKThe particular URL currently producing the failure contains a filename beginning with:
Because of this, URL parsing or encoding differences may also be relevant, although we have not confirmed that the filename itself contributes to the SSRF rejection.
Operational Impact
We currently cannot identify a Contentstack CLI version that reliably handles both sides of our backup/restore workflow.
CLI 2.0.1
We need to use the newer CLI because it avoids import/restore problems encountered when using older CLI versions.
However, the backup/export process encounters the SSRF prevention error when downloading certain assets.
CLI 1.46.1
This version also encounters the same SSRF prevention error during backup/export.
CLI 1.38.0
Backup/export works with this version, including downloading the affected assets.
However, we have encountered other issues with the older CLI during restore/import, including what appear to be rate-limit-related failures where some content may not be restored successfully.
As a result, remaining on
1.38.0is not a viable long-term workaround.Effectively, we currently have:
This prevents us from moving the backup/restore pipeline cleanly onto the current CLI release.
Expected Behavior
A Contentstack stack export should be able to download valid asset URLs hosted on:
without triggering SSRF prevention.
If a Contentstack-hosted asset URL genuinely fails an SSRF security check, it would also be helpful for the CLI to report the specific reason the URL was classified as unsafe.
Actual Behavior
CLI
1.46.1and2.0.1reject the Contentstack-hosted asset URL with:CLI
1.38.0successfully downloads the same asset when run through the same backup pipeline.Reproduction Summary
At a high level:
1.38.0.images.contentstack.ioasset downloads successfully.1.46.1.2.0.1.No Contentstack content or pipeline configuration changes are required to reproduce the difference other than changing the Contentstack CLI version.
Questions for the Contentstack Team
Could the Contentstack team please confirm:
Was SSRF validation added or changed in the asset download path between
@contentstack/cli-cm-export1.15.2and1.20.1?Does
stack.asset().download()perform SSRF validation that was not performed by the previous direct HTTP download implementation?Should
https://images.contentstack.ioalways be considered a valid asset download source by the Management SDK?Is the SSRF validation performed locally by the Management SDK/CLI, or is the URL submitted to a Contentstack API that performs the SSRF validation server-side?
Can DNS resolution, IP validation, HTTP redirects, URL encoding, or particular filename characters cause a legitimate
images.contentstack.ioURL to fail the SSRF validation?Could the use of:
before passing the URL to:
affect how certain Contentstack asset URLs are validated?
Is there additional debug logging we can enable that would show exactly why the URL is being classified as unsafe?
Is there a supported workaround that would allow us to remain on Contentstack CLI
2.0.1while avoiding this SSRF failure?Additional Information
We can provide the complete affected asset URL privately if required for troubleshooting, as well as additional Azure DevOps pipeline logs.
We can also perform additional diagnostics against the affected asset URL if helpful, including:
curlThe strongest indication of a regression is currently the reproducible version-dependent behavior:
Combined with the change in the export package from a direct HTTP download to
stack.asset().download(), this appears to provide a relatively narrow area for investigation.