Skip to content

feat(httpclient): add proxy and TLS support (OD-30)#205

Merged
andrzej-janczak merged 11 commits into
mainfrom
cliv2-verify-and-add-proxy-support-od-30
Jun 11, 2026
Merged

feat(httpclient): add proxy and TLS support (OD-30)#205
andrzej-janczak merged 11 commits into
mainfrom
cliv2-verify-and-add-proxy-support-od-30

Conversation

@andrzej-janczak

@andrzej-janczak andrzej-janczak commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds utils/httpclient package: New(opts...) factory that sets http.ProxyFromEnvironment, loads system cert pool + optional SSL_CERT_FILE custom CA bundle, and honours CODACY_CLI_INSECURE=1 (with stderr warning)
  • Migrates all 7 HTTP callsites (codacy-client, tools/patterns, utils/download, cmd/upload ×4, cmd/upload_sbom) to the new factory
  • Adds README "Proxy & TLS" section documenting the three env vars
  • Adds hermetic unit tests (utils/httpclient/httpclient_test.go, 9 tests, no network) and a real-life integration harness (integration-tests/proxy-tls/run.sh) using mitmproxy against app.codacy.com

OD-30 Proxy/TLS — Manual Test Results

Real cli-v2 init → mitmproxy (:8899, MITM) → real app.codacy.com.

A — proxy + custom CA (SSL_CERT_FILE=$CA)

ℹ️ No project token was specified, fetching codacy default configurations
PMD7 configuration created based on Codacy settings
...
✅ Successfully initialized Codacy configuration!
PASS — traffic through proxy, custom CA trusted, success.

B — proxy, no CA

Warning: Failed to get default patterns for : ... tls: failed to verify
certificate: x509: "app.codacy.com" certificate is not trusted
(repeated per tool)
failed to build languages config from API: ... certificate is not trusted
PASS — TLS correctly rejected MITM cert (fails loud, no insecure default).

C — proxy + CODACY_CLI_INSECURE=1

WARNING: TLS certificate verification is DISABLED (CODACY_CLI_INSECURE set)...
(repeated per HTTP call)

✅ Successfully initialized Codacy configuration!
PASS — insecure toggle works, stderr warning shown, success.

D — NO_PROXY=app.codacy.com,api.codacy.com

ℹ️ No project token was specified, fetching codacy default configurations

✅ Successfully initialized Codacy configuration!
PASS — bypassed proxy (no MITM in path), success.

Test plan

  • go test ./utils/httpclient/ -count=5 — all 9 unit tests pass (stable)
  • go build ./... + go vet ./... — clean
  • integration-tests/proxy-tls/run.sh — all 4 cases (custom CA, no-CA-fails, insecure, no-proxy bypass) PASS with real mitmproxy → real app.codacy.com
  • Confirmed: before this change cases A (custom CA) and C (insecure) failed with x509: certificate is not trusted; after: both PASS

Closes OD-30

🤖 Generated with Claude Code

andrzej-janczak and others added 8 commits June 9, 2026 16:04
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Runs the actual cli-v2 binary through a real mitmproxy against app.codacy.com.
Baseline confirms SSL_CERT_FILE is ignored on macOS and CODACY_CLI_INSECURE is
unimplemented; both cases flip green once OD-30 lands.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-30)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ction (OD-30)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codacy-production

codacy-production Bot commented Jun 10, 2026

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 1 critical · 4 medium · 4 minor

Alerts:
⚠ 5 issues (≤ 1 issue of at least medium severity)
⚠ 2 issues (≤ 0 issues of at least minor severity)

Results:
9 new issues

Category Results
UnusedCode 2 medium (2 false positives)
BestPractice 1 medium
Documentation 4 minor
Security 1 critical (1 false positive)
1 medium

View in Codacy

🟢 Metrics 26 complexity · 4 duplication

Metric Results
Complexity 26
Duplication 4

View in Codacy

🟢 Coverage 62.14% diff coverage · +0.59% coverage variation

Metric Results
Coverage variation +0.59% coverage variation (-0.50%)
Diff coverage 62.14% diff coverage (50.00%)

View coverage diff in Codacy

Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (ebec24b) Report Missing Report Missing Report Missing
Head commit (1c30605) 6301 (+90) 1561 (+59) 24.77% (+0.59%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#205) 103 64 62.14%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

1 Codacy didn't receive coverage data for the commit, or there was an error processing the received data. Check your integration for errors and validate that your coverage setup is correct.

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces a centralized HTTP client factory in utils/httpclient that honors standard proxy environment variables, supports custom CA bundles via SSL_CERT_FILE, and allows disabling TLS verification via CODACY_CLI_INSECURE. Multiple callsites across the codebase have been migrated to use this new factory. Feedback on the changes suggests optimizing the TLS configuration by only loading the system cert pool when a custom CA bundle is specified to avoid performance overhead and potential handshake failures. Additionally, it is recommended to clone http.DefaultTransport instead of creating a new transport from scratch to preserve optimized default settings such as connection pooling and HTTP/2 support.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread utils/httpclient/tls.go Outdated
Comment on lines +25 to +41
pool, err := x509.SystemCertPool()
if err != nil || pool == nil {
pool = x509.NewCertPool()
}

if path := caBundlePath(); path != "" {
pemBytes, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read CA bundle from %s (%s): %w", EnvCABundle, path, err)
}
if !pool.AppendCertsFromPEM(pemBytes) {
return nil, fmt.Errorf("no valid certificates found in CA bundle %s (%s)", EnvCABundle, path)
}
}

cfg.RootCAs = pool
return cfg, nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Loading the system cert pool on every client creation even when no custom CA bundle (SSL_CERT_FILE) is specified is inefficient and can be slow on some operating systems. Furthermore, if x509.SystemCertPool() fails and no custom CA is provided, cfg.RootCAs is set to an empty cert pool, which will cause all subsequent TLS handshakes to fail instead of falling back to Go's default system verification behavior. We should only load the system cert pool and set cfg.RootCAs if a custom CA bundle path is actually configured.

Suggested change
pool, err := x509.SystemCertPool()
if err != nil || pool == nil {
pool = x509.NewCertPool()
}
if path := caBundlePath(); path != "" {
pemBytes, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read CA bundle from %s (%s): %w", EnvCABundle, path, err)
}
if !pool.AppendCertsFromPEM(pemBytes) {
return nil, fmt.Errorf("no valid certificates found in CA bundle %s (%s)", EnvCABundle, path)
}
}
cfg.RootCAs = pool
return cfg, nil
if path := caBundlePath(); path != "" {
pool, err := x509.SystemCertPool()
if err != nil || pool == nil {
pool = x509.NewCertPool()
}
pemBytes, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read CA bundle from %s (%s): %w", EnvCABundle, path, err)
}
if !pool.AppendCertsFromPEM(pemBytes) {
return nil, fmt.Errorf("no valid certificates found in CA bundle %s (%s)", EnvCABundle, path)
}
cfg.RootCAs = pool
}
return cfg, nil

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Applied — only build the cert pool and set RootCAs when SSL_CERT_FILE is configured; otherwise leave RootCAs nil so Go uses default system verification. Avoids the empty-pool-rejects-everything failure mode.

Comment thread utils/httpclient/client.go Outdated
Comment on lines +23 to +26
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: tlsCfg,
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Creating a new http.Transport from scratch without cloning http.DefaultTransport or copying its configuration means you lose important default settings, such as connection pooling limits (MaxIdleConns), idle timeouts (IdleConnTimeout), TLS handshake timeouts (TLSHandshakeTimeout), and HTTP/2 support (ForceAttemptHTTP2). To preserve these optimized defaults, clone http.DefaultTransport and then apply your custom TLSClientConfig.

	transport := http.DefaultTransport.(*http.Transport).Clone()
	transport.TLSClientConfig = tlsCfg

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Applied — now clone http.DefaultTransport and override only Proxy + TLSClientConfig, preserving connection pooling, idle/handshake timeouts, and HTTP/2.

andrzej-janczak and others added 3 commits June 10, 2026 09:33
…m CA (OD-30)

Address PR #205 review:
- Clone http.DefaultTransport to preserve connection pooling, idle/handshake
  timeouts, and HTTP/2 instead of building a bare Transport.
- Only set tls.Config.RootCAs when SSL_CERT_FILE is configured; leave nil
  otherwise so Go uses default system verification. Prevents an empty pool
  (when SystemCertPool fails) from rejecting all TLS handshakes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ing to file (OD-30)

Proxy/TLS warnings (and all log.Printf/log.Fatalf output) reached only the
terminal, never codacy-cli.log. Initialize now redirects Go's standard logger
to io.MultiWriter(stderr, logfile), and the CODACY_CLI_INSECURE warning is
emitted via log.Println so it lands in the file too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@andrzej-janczak andrzej-janczak merged commit 27e119a into main Jun 11, 2026
10 checks passed
@andrzej-janczak andrzej-janczak deleted the cliv2-verify-and-add-proxy-support-od-30 branch June 11, 2026 09:53
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.

3 participants