Severity / Confidence
P3, high confidence. Chore. Duplicated SSRF-defense helper across two modules; no behavior change pending refactor.
Affected files
internal/secrets/azure_resolver.go:17-60 (duplicate imdsAddresses, blockIMDSDialer, imdsBlockingTransport)
providers/azure/internal/httpclient/httpclient.go:14-53 (original imdsAddresses, blockIMDSDialer, New)
Evidence
PR LeanerCloud/cloud-commitments-cli#1224 added an IMDS-blocking transport to NewAzureResolver (imdsBlockingTransport() at internal/secrets/azure_resolver.go:46). The implementation is bit-for-bit equivalent to httpclient.New() at providers/azure/internal/httpclient/httpclient.go:38: same imdsAddresses map (169.254.169.254, fd00:ec2::254), same blockIMDSDialer struct + DialContext, same 10s/30s/30s timeouts, same http.Transport fields.
The duplication is currently forced by Go's internal/ import rule plus the module boundary (providers/azure/go.mod is a separate module from the root). The root-module internal/secrets package cannot import github.com/LeanerCloud/CUDly/providers/azure/internal/httpclient.
Memory feedback_azure_use_httpclient_new.md explicitly says: "Every Azure service client must use httpclient.New(), not &http.Client{Timeout: ...}. Any new Azure service client that omits this step loses the SSRF defense-in-depth without any compile-time signal." Two parallel implementations defeat the grep-based regression check the memory recommends (grep '&http.Client{' in providers/azure/).
Recommendation
Extract imdsAddresses, blockIMDSDialer, and the New() constructor into a shared, non-internal package importable by both modules:
pkg/httpclient/httpclient.go (the pkg/ module already has its own go.mod, so it can be required by both root and providers/azure/).
- Replace the contents of
providers/azure/internal/httpclient/httpclient.go and internal/secrets/azure_resolver.go IMDS helpers with a re-export / direct call into the new pkg/httpclient.New().
- Keep the regression test pattern: each consuming module retains a small test that calls
pkg/httpclient.New() and asserts a request to http://169.254.169.254/... errors with "blocked".
This collapses the duplication and gives the grep guard a single canonical answer.
Triage labels
type/chore, priority/p3, severity/low, urgency/eventually, impact/internal, effort/s, triaged.
Source: PR LeanerCloud/cloud-commitments-cli#1224 adversarial review.
Severity / Confidence
P3, high confidence. Chore. Duplicated SSRF-defense helper across two modules; no behavior change pending refactor.
Affected files
internal/secrets/azure_resolver.go:17-60(duplicateimdsAddresses,blockIMDSDialer,imdsBlockingTransport)providers/azure/internal/httpclient/httpclient.go:14-53(originalimdsAddresses,blockIMDSDialer,New)Evidence
PR LeanerCloud/cloud-commitments-cli#1224 added an IMDS-blocking transport to
NewAzureResolver(imdsBlockingTransport()atinternal/secrets/azure_resolver.go:46). The implementation is bit-for-bit equivalent tohttpclient.New()atproviders/azure/internal/httpclient/httpclient.go:38: sameimdsAddressesmap (169.254.169.254,fd00:ec2::254), sameblockIMDSDialerstruct +DialContext, same 10s/30s/30s timeouts, samehttp.Transportfields.The duplication is currently forced by Go's
internal/import rule plus the module boundary (providers/azure/go.modis a separate module from the root). The root-moduleinternal/secretspackage cannot importgithub.com/LeanerCloud/CUDly/providers/azure/internal/httpclient.Memory
feedback_azure_use_httpclient_new.mdexplicitly says: "Every Azure service client must usehttpclient.New(), not&http.Client{Timeout: ...}. Any new Azure service client that omits this step loses the SSRF defense-in-depth without any compile-time signal." Two parallel implementations defeat the grep-based regression check the memory recommends (grep '&http.Client{' in providers/azure/).Recommendation
Extract
imdsAddresses,blockIMDSDialer, and theNew()constructor into a shared, non-internalpackage importable by both modules:pkg/httpclient/httpclient.go(thepkg/module already has its owngo.mod, so it can be required by both root andproviders/azure/).providers/azure/internal/httpclient/httpclient.goandinternal/secrets/azure_resolver.goIMDS helpers with a re-export / direct call into the newpkg/httpclient.New().pkg/httpclient.New()and asserts a request tohttp://169.254.169.254/...errors with "blocked".This collapses the duplication and gives the grep guard a single canonical answer.
Triage labels
type/chore,priority/p3,severity/low,urgency/eventually,impact/internal,effort/s,triaged.Source: PR LeanerCloud/cloud-commitments-cli#1224 adversarial review.