Skip to content

Commit 52f41cc

Browse files
committed
agents/sec-rev: final tweaks to agent code after review
Signed-off-by: Jacob Howard <[email protected]>
1 parent 8cb53f4 commit 52f41cc

File tree

4 files changed

+198
-184
lines changed

4 files changed

+198
-184
lines changed

agents/security-reviewer/Dockerfile

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ RUN useradd --create-home --shell /bin/bash agent \
5454
&& install -d -o agent -g agent /workspace/input \
5555
&& install -d -o agent -g agent /workspace/output
5656

57-
# Copy the reviewer assets and report template.
57+
# Copy the reviewer entrypoint and assets.
5858
COPY --from=gobuilder /security-reviewer/build/reviewer /opt/security-reviewer/reviewer
5959
COPY --chown=agent:agent prompt-template.md /opt/security-reviewer/prompt-template.md
6060
COPY --chown=agent:agent report-template.md /opt/security-reviewer/report-template.md
@@ -67,17 +67,22 @@ WORKDIR /workspace
6767
ENTRYPOINT ["/opt/security-reviewer/reviewer"]
6868

6969

70-
# Lightweight image hosting the API proxy.
70+
# Use a debian image to host the proxy.
7171
FROM debian:trixie-slim AS proxy-image
7272

73+
# Install dependencies, including those needed for healthchecks.
7374
ENV DEBIAN_FRONTEND=noninteractive
7475
RUN apt-get update \
7576
&& apt-get install -y --no-install-recommends \
7677
ca-certificates \
7778
wget \
7879
&& rm -rf /var/lib/apt/lists/*
7980

81+
# Copy the proxy entrypoint.
8082
COPY --from=gobuilder /security-reviewer/build/proxy /opt/security-reviewer/proxy
8183

84+
# Expose the proxy's default port.
8285
EXPOSE 4000
86+
87+
# Set the proxy entrypoint.
8388
ENTRYPOINT ["/opt/security-reviewer/proxy"]

agents/security-reviewer/proxy/main.go

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"context"
5+
"crypto/subtle"
56
"errors"
67
"fmt"
78
"log"
@@ -10,27 +11,44 @@ import (
1011
"net/http/httputil"
1112
"net/url"
1213
"os"
14+
"os/signal"
1315
"strings"
16+
"syscall"
1417
"time"
1518
)
1619

1720
const (
18-
defaultListenAddr = ":4000"
19-
defaultOpenAIBaseURL = "https://api.openai.com/v1"
21+
// defaultListenAddr is the fallback bind address when none is provided via PROXY_LISTEN_ADDR.
22+
defaultListenAddr = ":4000"
23+
// defaultOpenAIBaseURL is the upstream OpenAI API base path used when none is provided.
24+
defaultOpenAIBaseURL = "https://api.openai.com/v1"
25+
// defaultAnthropicBaseURL is the upstream Anthropic API base path used when none is provided.
26+
// NOTE: Anthropic clients are expected to include /v1 in their requests, so
27+
// it is not idiomatic to include it in the base URL.
2028
defaultAnthropicBaseURL = "https://api.anthropic.com"
21-
openAIInboundPrefix = "/openai/"
22-
anthropicInboundPrefix = "/anthropic/"
23-
healthPath = "/health/liveliness"
24-
headerAuthorization = "Authorization"
25-
headerAnthropicAPIKey = "X-Api-Key"
29+
// openAIInboundPrefix is the path prefix used to route requests to OpenAI.
30+
openAIInboundPrefix = "/openai/"
31+
// anthropicInboundPrefix is the path prefix used to route requests to Anthropic.
32+
anthropicInboundPrefix = "/anthropic/"
33+
// healthPath is the HTTP endpoint used for container health checks.
34+
healthPath = "/health/liveliness"
35+
// headerAuthorization is the inbound HTTP header that carries bearer tokens.
36+
headerAuthorization = "Authorization"
37+
// headerAnthropicAPIKey is the Anthropic-specific header carrying API keys.
38+
headerAnthropicAPIKey = "X-Api-Key"
2639
)
2740

2841
// providerProxy defines how to forward requests to a specific upstream API.
2942
type providerProxy struct {
30-
Prefix string
31-
Target *url.URL
32-
HeaderName string
43+
// Prefix is the inbound path prefix handled by the provider.
44+
Prefix string
45+
// Target is the upstream endpoint used to service requests for the provider.
46+
Target *url.URL
47+
// HeaderName is the outbound header carrying the provider-specific credential.
48+
HeaderName string
49+
// HeaderValue is the credential value set on outbound requests.
3350
HeaderValue string
51+
// DisplayName is the human-readable name of the provider used in logs.
3452
DisplayName string
3553
}
3654

@@ -41,6 +59,9 @@ func main() {
4159
log.Fatalf("proxy configuration error: %v", err)
4260
}
4361

62+
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
63+
defer stop()
64+
4465
mux := http.NewServeMux()
4566
mux.HandleFunc(healthPath, handleHealth)
4667

@@ -63,6 +84,15 @@ func main() {
6384
log.Printf("proxy listening on %s (OpenAI -> %s, Anthropic -> %s)",
6485
cfg.listenAddr, cfg.openAIProxy.Target.String(), cfg.anthropicProxy.Target.String())
6586

87+
go func() {
88+
<-ctx.Done()
89+
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
90+
defer cancel()
91+
if err := server.Shutdown(shutdownCtx); err != nil {
92+
log.Printf("proxy shutdown error: %v", err)
93+
}
94+
}()
95+
6696
if err = server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
6797
log.Fatalf("proxy server error: %v", err)
6898
}
@@ -289,6 +319,8 @@ func firstNonEmpty(values ...string) string {
289319
return ""
290320
}
291321

322+
// validateClientToken ensures inbound requests present the proxy bearer secret using
323+
// a constant-time comparison to avoid leaking timing information.
292324
func validateClientToken(headerValue, expectedToken string) bool {
293325
if expectedToken == "" {
294326
return false
@@ -300,5 +332,6 @@ func validateClientToken(headerValue, expectedToken string) bool {
300332
if !strings.EqualFold(parts[0], "bearer") {
301333
return false
302334
}
303-
return strings.TrimSpace(parts[1]) == expectedToken
335+
provided := strings.TrimSpace(parts[1])
336+
return subtle.ConstantTimeCompare([]byte(provided), []byte(expectedToken)) == 1
304337
}

0 commit comments

Comments
 (0)