Skip to content

Commit 6358fb5

Browse files
Mossakaclaude
andcommitted
fix: resolve merge conflicts with main
Accept main's C-only one-shot-token library (with XOR obfuscation from PR #776). The Rust implementation (src/lib.rs, Cargo.toml) was deleted on main and is no longer needed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2 parents 9ef62e5 + 844a011 commit 6358fb5

10 files changed

Lines changed: 251 additions & 503 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ coverage/
99
.nyc_output/
1010
*.swp
1111
*.swo
12+
*.so
1213
*~
1314
.idea/
1415

containers/agent/Dockerfile

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,6 @@
77
# NOTE: ARG declared before first FROM is global and available in all FROM statements
88
ARG BASE_IMAGE=ubuntu:22.04
99

10-
# Multi-stage build: Use official Rust image to build one-shot-token library
11-
# SECURITY: Using official rust:1.77-slim image prevents executing unverified
12-
# scripts from the internet during build time (supply chain attack mitigation)
13-
# NOTE: Rust 1.77+ required for C string literal syntax (c"...") used in src/lib.rs
14-
FROM rust:1.77-slim AS rust-builder
15-
16-
# Copy one-shot-token source files
17-
COPY one-shot-token/Cargo.toml /tmp/one-shot-token/Cargo.toml
18-
COPY one-shot-token/src/ /tmp/one-shot-token/src/
19-
20-
# Build the one-shot-token library
21-
WORKDIR /tmp/one-shot-token
22-
RUN cargo build --release
23-
24-
# Main stage
2510
FROM ${BASE_IMAGE}
2611

2712
# Install required packages and Node.js 22
@@ -85,9 +70,21 @@ RUN chmod +x /usr/local/bin/setup-iptables.sh /usr/local/bin/entrypoint.sh /usr/
8570

8671
# Copy pre-built one-shot-token library from rust-builder stage
8772
# This prevents tokens from being read multiple times (e.g., by malicious code)
88-
# SECURITY: Using multi-stage build with official Rust image avoids executing
89-
# unverified scripts from the internet during build time
90-
COPY --from=rust-builder /tmp/one-shot-token/target/release/libone_shot_token.so /usr/local/lib/one-shot-token.so
73+
# Build flags: -fvisibility=hidden hides internal symbols, -s strips at link time
74+
COPY one-shot-token/one-shot-token.c /tmp/one-shot-token.c
75+
RUN set -eux; \
76+
BUILD_PKGS="gcc libc6-dev binutils"; \
77+
apt-get update && \
78+
( apt-get install -y --no-install-recommends $BUILD_PKGS || \
79+
(rm -rf /var/lib/apt/lists/* && apt-get update && \
80+
apt-get install -y --no-install-recommends $BUILD_PKGS) ) && \
81+
gcc -shared -fPIC -fvisibility=hidden -O2 -Wall -s \
82+
-o /usr/local/lib/one-shot-token.so /tmp/one-shot-token.c -ldl -lpthread && \
83+
strip --strip-unneeded /usr/local/lib/one-shot-token.so && \
84+
rm /tmp/one-shot-token.c && \
85+
apt-get remove -y $BUILD_PKGS && \
86+
apt-get autoremove -y && \
87+
rm -rf /var/lib/apt/lists/*
9188

9289
# Install Docker stub script that shows helpful error message
9390
# Docker-in-Docker support was removed in v0.9.1

containers/agent/one-shot-token/Cargo.toml

Lines changed: 0 additions & 19 deletions
This file was deleted.

containers/agent/one-shot-token/README.md

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,14 @@ In chroot mode, the library must be accessible from within the chroot (host file
158158

159159
### In Docker (automatic)
160160

161-
The Dockerfile compiles the Rust library during image build:
161+
The Dockerfile compiles the library during image build with hardened flags:
162162

163163
```dockerfile
164-
RUN cargo build --release && \
165-
cp target/release/libone_shot_token.so /usr/local/lib/one-shot-token.so
164+
RUN gcc -shared -fPIC -fvisibility=hidden -O2 -Wall -s \
165+
-o /usr/local/lib/one-shot-token.so \
166+
/tmp/one-shot-token.c \
167+
-ldl -lpthread && \
168+
strip --strip-unneeded /usr/local/lib/one-shot-token.so
166169
```
167170

168171
### Locally (for testing)
@@ -175,6 +178,24 @@ Requires Rust toolchain (install via [rustup](https://rustup.rs/)):
175178

176179
This builds `target/release/libone_shot_token.so` and creates a symlink `one-shot-token.so` for backwards compatibility.
177180

181+
### Binary Hardening
182+
183+
The build applies several hardening measures to reduce reconnaissance value:
184+
185+
- **XOR-obfuscated token names**: Default token names are stored as XOR-encoded byte arrays
186+
and decoded at runtime. This prevents extraction via `strings` or `objdump -s -j .rodata`.
187+
- **Hidden symbol visibility**: `-fvisibility=hidden` hides all internal symbols by default.
188+
Only `getenv` and `secure_getenv` are exported (required for LD_PRELOAD interposition).
189+
- **Stripped binary**: `-s` flag and `strip --strip-unneeded` remove the symbol table,
190+
debug sections, and build metadata.
191+
192+
To regenerate the obfuscated byte arrays after changing default token names:
193+
194+
```bash
195+
./encode-tokens.sh
196+
# Paste the output into one-shot-token.c, replacing the OBFUSCATED_DEFAULTS section
197+
```
198+
178199
## Testing
179200

180201
### Basic Test (Default Tokens)
@@ -305,7 +326,7 @@ This library is one layer in AWF's security model:
305326

306327
## Files
307328

308-
- `src/lib.rs` - Library source code (Rust)
309-
- `Cargo.toml` - Rust package configuration
310-
- `build.sh` - Local build script
329+
- `one-shot-token.c` - Library source code (token names are XOR-obfuscated)
330+
- `build.sh` - Local build script (includes hardening flags and verification)
331+
- `encode-tokens.sh` - Generates XOR-encoded byte arrays for default token names
311332
- `README.md` - This documentation

containers/agent/one-shot-token/build.sh

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,27 @@ LINK_FILE="${SCRIPT_DIR}/one-shot-token.so"
99

1010
echo "[build] Building one-shot-token with Cargo..."
1111

12-
cd "${SCRIPT_DIR}"
13-
14-
# Build the release version
15-
cargo build --release
16-
17-
# Determine the output file based on platform
18-
if [[ "$(uname)" == "Darwin" ]]; then
19-
OUTPUT_FILE="${SCRIPT_DIR}/target/release/libone_shot_token.dylib"
20-
echo "[build] Successfully built: ${OUTPUT_FILE} (macOS)"
21-
else
22-
OUTPUT_FILE="${SCRIPT_DIR}/target/release/libone_shot_token.so"
23-
echo "[build] Successfully built: ${OUTPUT_FILE}"
24-
25-
# Create symlink for backwards compatibility (Linux only)
26-
if [[ -L "${LINK_FILE}" ]]; then
27-
rm "${LINK_FILE}"
28-
fi
29-
ln -sf "target/release/libone_shot_token.so" "${LINK_FILE}"
30-
echo "[build] Created symlink: ${LINK_FILE} -> target/release/libone_shot_token.so"
31-
fi
12+
# Compile as a shared library with hardened build flags:
13+
# -shared: create a shared library
14+
# -fPIC: position-independent code (required for shared libs)
15+
# -fvisibility=hidden: hide all symbols by default (only getenv/secure_getenv
16+
# are exported via __attribute__((visibility("default"))))
17+
# -ldl: link with libdl for dlsym
18+
# -lpthread: link with pthread for mutex
19+
# -O2: optimize for performance
20+
# -Wall -Wextra: enable warnings
21+
# -s: strip symbol table and relocation info at link time
22+
gcc -shared -fPIC \
23+
-fvisibility=hidden \
24+
-O2 -Wall -Wextra -s \
25+
-o "${OUTPUT_FILE}" \
26+
"${SOURCE_FILE}" \
27+
-ldl -lpthread
28+
29+
# Remove remaining unneeded symbols (debug sections, build metadata)
30+
strip --strip-unneeded "${OUTPUT_FILE}"
31+
32+
echo "[build] Successfully built: ${OUTPUT_FILE}"
3233

3334
# Verify it's a valid shared library
3435
if file "${OUTPUT_FILE}" | grep -qE "shared object|dynamically linked"; then
@@ -37,3 +38,11 @@ else
3738
echo "[build] ERROR: Output is not a valid shared library"
3839
exit 1
3940
fi
41+
42+
# Verify hardening: token names should NOT appear in binary
43+
if strings -a "${OUTPUT_FILE}" | grep -qE '(COPILOT_GITHUB_TOKEN|OPENAI_API_KEY|ANTHROPIC_API_KEY)'; then
44+
echo "[build] WARNING: Cleartext token names still present in binary"
45+
exit 1
46+
else
47+
echo "[build] Verified: no cleartext token names in binary"
48+
fi
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/bin/bash
2+
# Generate XOR-obfuscated byte arrays for default token names.
3+
# Run this script whenever the default token list changes, then paste
4+
# the output into one-shot-token.c (replacing the OBFUSCATED_DEFAULTS section).
5+
#
6+
# The obfuscation prevents token names from appearing as cleartext strings
7+
# in the .rodata section of the compiled binary. This is NOT cryptographic
8+
# security -- a determined attacker can reverse the XOR. The goal is to
9+
# defeat casual reconnaissance via strings(1) / objdump.
10+
11+
set -euo pipefail
12+
13+
KEY=0x5A
14+
15+
TOKENS=(
16+
"COPILOT_GITHUB_TOKEN"
17+
"GITHUB_TOKEN"
18+
"GH_TOKEN"
19+
"GITHUB_API_TOKEN"
20+
"GITHUB_PAT"
21+
"GH_ACCESS_TOKEN"
22+
"OPENAI_API_KEY"
23+
"OPENAI_KEY"
24+
"ANTHROPIC_API_KEY"
25+
"CLAUDE_API_KEY"
26+
"CODEX_API_KEY"
27+
)
28+
29+
echo "/* --- BEGIN GENERATED OBFUSCATED DEFAULTS (key=0x$(printf '%02X' $KEY)) --- */"
30+
echo "/* Re-generate with: containers/agent/one-shot-token/encode-tokens.sh */"
31+
echo "#define NUM_DEFAULT_TOKENS ${#TOKENS[@]}"
32+
echo ""
33+
34+
for i in "${!TOKENS[@]}"; do
35+
token="${TOKENS[$i]}"
36+
printf "static const unsigned char OBF_%d[] = { " "$i"
37+
for ((j=0; j<${#token}; j++)); do
38+
byte=$(printf '%d' "'${token:$j:1}")
39+
encoded=$((byte ^ KEY))
40+
if ((j > 0)); then
41+
printf ", "
42+
fi
43+
printf "0x%02x" "$encoded"
44+
done
45+
printf " }; /* length=%d */\n" "${#token}"
46+
done
47+
48+
echo ""
49+
echo "static const struct obf_entry OBFUSCATED_DEFAULTS[${#TOKENS[@]}] = {"
50+
for i in "${!TOKENS[@]}"; do
51+
echo " { OBF_${i}, sizeof(OBF_${i}) },"
52+
done
53+
echo "};"
54+
echo "/* --- END GENERATED OBFUSCATED DEFAULTS --- */"

0 commit comments

Comments
 (0)