Bug fixes: - Fix nil-pointer panic in sendMessage SOAP fault handler (used err.Error() on nil) - Fix missing return after SOAP fault response (caused fall-through to 200 OK) - Fix timeout=0 shadowing: removed package-level constant, use configured value - Fix receiveMessage not checking SOAP faults from CIIMS Codec hardening: - Replace fragile regex parsing with encoding/xml.Decoder for namespace-agnostic XML - XML-escape all user inputs (user, pass, event) — not just message body - Remove magic-number-based string slicing (split, GetErrMsg) Transport improvements: - Extract Client type with connection pooling (reuse http.Client across requests) - Add CIIMSResponse domain type with IsFault(), ErrorMessage(), Messages() - Add HTTPStatusError and ResponseTooLargeError typed errors - Replace deprecated ioutil.ReadAll with io.ReadAll - Add response body size limit (64 MiB) Security hardening: - Add MaxBytesReader (1MB request body limit) on both endpoints - Add URL allowlist via CIIMS_ALLOWED_SERVERS env var - Add normalizeBaseURL with scheme/host/query validation - Add count bounds validation (1-1000) on /receive - Add server-level timeouts (ReadHeaderTimeout, ReadTimeout, IdleTimeout) Configuration: - Introduce Config struct to replace package-level globals - Add loadConfig() with full validation and error propagation - Add getIntEnv() with positive-value enforcement Test coverage (75 tests, 35 new): - Phase 1: 8 internal tests (nil receiver, error types, SOAP+HTTP500, malformed XML) - Phase 2: 19 pure function tests (normalizeBaseURL, parseAllowedServers, getIntEnv) - Phase 3: 16 handler tests (send/receive success, SOAP fault, network error, HTTP 500, body too large, invalid JSON, missing fields, URL allowlist) - Phase 4: 8 config/router tests (loadConfig, newServer, newRouter) Toolchain: - Upgrade Go 1.13 → 1.22, gin 1.6.3 → 1.10.0, testify 1.5.1 → 1.10.0 Cleanup: - Remove dead code (unused post() function, commented-out defaults) - Replace println with structured logging - Add .gitignore - Rewrite README with API docs, env vars, security considerations
8.7 KiB
CIIMS Proxy Remediation Plan
Objective
Fix all correctness, security, maintainability, and testing issues identified in the code review so the proxy is reliable, safe, and maintainable. Expected outcomes:
- Request timeouts are honored and configurable via
CIIMS_TIMEOUT. sendMessageno longer panics on SOAP faults and returns a proper error response.- All user-supplied values are safely XML-escaped before being embedded in SOAP envelopes.
- SOAP response parsing is robust against namespace/formatting changes.
- Dead code and inconsistent logging are removed or unified.
- HTTP handlers have unit/integration tests.
- Dependencies and Go toolchain are upgraded to supported versions.
Implementation Plan
1. Fix Timeout Configuration and Request Handling
- Remove the package-level
timeoutconstant incmd/main/main.gothat always evaluates to0. - Introduce an application configuration struct (or closure) to hold the resolved timeout value so handlers receive the configured timeout instead of the global zero value.
- Ensure
http.Client.Timeoutis set to the configured duration; treat0as a validation error or explicitly default todefaultTimeoutbefore constructing the client. - Validate that
CIIMS_TIMEOUT, when provided, parses as a positive integer and fail fast with a clear message on invalid input instead of panicking.
2. Fix sendMessage Error Path
- In
cmd/main/main.go, after callinginternal.GetErrMsg(resp), use the returnederrMsgstring for logging and the JSON error payload instead oferr.Error(). - Add the missing
returnstatement inside thelen(errMsg) > 0branch so the handler does not return200 OK {"error":""}after detecting a SOAP fault. - Ensure consistent error response shape across all handler error paths (e.g.,
{"error": "..."}).
3. Harden SOAP Message Construction
- XML-escape all user-provided fields inserted into the SOAP templates (
user,pass,event,message) usingxml.EscapeTextor equivalent. - Audit the
sendtplandreceivetpltemplates ininternal/codec.goto confirm no raw interpolation remains. - Consider replacing string-template-based SOAP construction with typed
encoding/xmlstructs for the envelope, header, and body, while still embedding the inner message as escaped text. - Add unit tests that verify payloads containing XML metacharacters (
<,>,&,",') are escaped correctly.
4. Replace Fragile Response Parsing
- Replace regex-based extraction in
GetMsgsandGetErrMsgwith XML unmarshaling into properly typed structs, or at minimum use namespace-aware parsing. - If regex is retained as a short-term fix, switch to non-greedy patterns (e.g.,
<ns1:string>(.*?)</ns1:string>) and validate slice indices before substring operations. - Remove hardcoded magic numbers (
12,13,54,15) fromsplitandGetErrMsg. - Add tests covering responses with different namespace prefixes, extra attributes, whitespace variations, and missing elements.
5. Remove Dead Code and Unify Logging
- Delete the unused
postfunction ininternal/http.go. - Remove commented-out default URLs and the commented
r.Run()line incmd/main/main.go. - Decide on a single logging approach: either use Gin's built-in logger and standard
logpackage, or keep a structured logger consistently; remove the mixed use ofgithub.com/labstack/gommon/logunless it provides required features. - Replace
printlnstartup messages with structured log calls or remove them.
6. Improve Error Handling
- Handle errors from
regexp.Compileexplicitly; if regex remains, compile patterns once at package init and panic only on init failure, or prefer compile-time-safe approaches. - Refactor
getIntEnvto return(int, error)instead of panicking, and letmaindecide how to report invalid configuration. - Ensure all HTTP client errors (network, timeout, non-2xx status) are logged and returned to the client without leaking internal details.
7. Add Security Hardening
- Add configurable authentication for the
/sendand/receiveendpoints (e.g., API key header, basic auth, or TLS client certificates) if the proxy is exposed beyond localhost. - Document that the proxy should run behind TLS when handling credentials.
- Avoid logging request bodies or credentials; if URL logging is required, log only the host or a sanitized version.
- Add request body size limits and input validation (e.g., max
count, maxmsglength) to prevent abuse.
8. Expand Test Coverage
- Add HTTP handler tests for
/sendand/receiveusingnet/http/httptestand a mock CIIMS backend. - Add tests for timeout behavior, including verification that
http.Client.Timeoutis set correctly. - Add tests for SOAP fault handling in
sendMessageandreceiveMessage. - Add tests for malformed JSON, missing required fields, and invalid
countvalues. - Ensure existing tests in
internal/codec_test.gocontinue to pass after refactoring, updating expected strings only if the XML format changes intentionally.
9. Upgrade Toolchain and Dependencies
- Update
go.modto a supported Go version (e.g.,1.22or later) and rungo mod tidy. - Upgrade
gin-gonic/gin,labstack/gommon, andstretchr/testifyto current stable versions. - Review release notes for breaking changes in Gin and adjust handler code if necessary.
- Verify the build and all tests pass on the upgraded toolchain.
10. Documentation and Deployment Notes
- Update
README.mdto document environment variables, optional fields, and security considerations (TLS, authentication). - Remove the committed
mainbinary from the repository and add it to.gitignoreif not already ignored. - Add a
Makefileor build script for consistent compilation and testing (optional but recommended).
Verification Criteria
CIIMS_TIMEOUT=30results in outbound requests timing out after 30 seconds;CIIMS_TIMEOUT=0falls back to the default240seconds or fails validation as designed.- Sending a request that causes a CIIMS SOAP fault returns
500 Internal Server Errorwith{"error":"<fault text>"}and does not panic. - A
sendrequest withuser,pass,event, ormsgcontaining XML metacharacters produces a valid SOAP envelope without breaking XML structure. receiveresponses with different namespace prefixes or whitespace still return the correct decoded messages.- All existing and new unit tests pass (
go test ./...). go vet ./...andgo build ./...produce no errors on the upgraded Go version.- The committed
mainbinary is removed from version control.
Potential Risks and Mitigations
-
Regression in SOAP format Mitigation: Keep the existing tests as a baseline and add new tests before refactoring. Compare generated XML with the current expected output to ensure backward compatibility with CIIMS.
-
Namespace changes in CIIMS responses break parsing Mitigation: Move to XML unmarshaling or namespace-agnostic parsing. Add test fixtures covering multiple namespace prefix styles.
-
Authentication requirement breaks existing clients Mitigation: Make authentication optional via environment variable, defaulting to disabled for local development, and document enablement for production.
-
Go/dependency upgrade introduces breaking changes Mitigation: Upgrade dependencies incrementally, run the full test suite after each change, and review Gin migration guides.
-
Timeout behavior change affects long-running CIIMS operations Mitigation: Set a sensible default (e.g.,
240seconds as currently intended) and allow operators to tuneCIIMS_TIMEOUTbased on observed backend latency.
Alternative Approaches
-
Template-based SOAP vs. struct-based XML marshaling
- Template approach: Simpler to read and matches the current implementation, but requires careful escaping. Keep if escaping is added and tested.
- Struct approach: Type-safe and eliminates string-replacement bugs, but more verbose due to mixed namespaces. Recommended for long-term maintainability.
-
Regex parsing vs. XML unmarshaling
- Regex: Quick to implement and matches current behavior, but fragile. Acceptable only as a short-term fix with non-greedy patterns and bounds checks.
- XML unmarshaling: Robust and self-documenting. Recommended for production use.
-
Global variables vs. dependency injection for configuration
- Global variables: Minimal code change, but hard to test. Current code uses this pattern.
- Dependency injection: Pass a config/handler struct to route registration. Enables better testing and removes hidden state. Recommended during refactoring.