Add end-to-end smoke and rewrite the README for v0

The smoke boots the fake Secrets Manager in a container and drives the
real binary from the host through a 0600 env file: login, listings, get
by name and uuid, failure paths, and a redaction sweep over every
captured output. README documents the implemented wire protocol, the
library surface keyproxy will call, the verified quickstart, and the
config table.
This commit is contained in:
2026-08-29 00:08:38 -05:00
parent edf056eb9f
commit 9b730afd12
4 changed files with 329 additions and 38 deletions
+50
View File
@@ -0,0 +1,50 @@
// Command fakesm boots the fake Bitwarden Secrets Manager on a plain TCP
// listener for the smoke script (tests use httptest via fakesm.Server
// directly). The credential identity below is the published SDK sample;
// the smoke script configures the CLI with the exact same string, and the
// organization key is random per boot so the full decrypt chain runs.
package main
import (
"crypto/rand"
"encoding/base64"
"flag"
"log"
"time"
"git.knownelement.com/ukrrs/mopac-bitwarden-go/internal/fakesm"
)
const smokeCredential = "0.ec2c1d46-6a4b-4751-a310-af9601317f2d.C2IgxjjLF7qSshsbwe8JGcbM075YXw:X8vbvA0bduihIDe/qrzIQQ=="
func main() {
addr := flag.String("addr", ":8600", "listen address")
flag.Parse()
tokenKey, err := base64.StdEncoding.DecodeString("X8vbvA0bduihIDe/qrzIQQ==")
if err != nil || len(tokenKey) != 16 {
log.Fatal("fakesm: bad token key part")
}
orgKey := make([]byte, 64)
if _, err := rand.Read(orgKey); err != nil {
log.Fatal("fakesm: entropy")
}
s := &fakesm.Server{
ClientID: "ec2c1d46-6a4b-4751-a310-af9601317f2d",
ClientSecret: "C2IgxjjLF7qSshsbwe8JGcbM075YXw",
TokenKey: tokenKey,
OrgKey: orgKey,
OrgID: "3fb1c0de-0000-4000-8000-000000000000",
TokenTTL: time.Hour,
Projects: []fakesm.Project{
{ID: "ac1d0000-0000-4000-8000-000000000001", Name: "harness"},
},
Secrets: []fakesm.Secret{
{ID: "5ec1e700-0000-4000-8000-00000000000a", Name: "smoke-redmine-key", Value: "smoke-redmine-value-0123456789abcdef"},
{ID: "5ec1e700-0000-4000-8000-00000000000b", Name: "smoke-litellm-key", Value: "smoke-litellm-value-fedcba9876543210"},
},
}
log.Printf("fakesm: listening addr=%s org=%s", *addr, s.OrgID)
if err := s.ListenAndServe(*addr); err != nil {
log.Fatal(err)
}
}
Executable
+113
View File
@@ -0,0 +1,113 @@
#!/bin/sh
# End-to-end smoke for bitwarden-go: builds the CLI in the Docker builder,
# boots the FAKE Secrets Manager in a container on 127.0.0.1:8600, drives
# the real binary from the host through a 0600 env file, and asserts the
# happy paths plus redaction. No real vault is ever contacted. Only exact
# container IDs / PIDs spawned here are killed.
set -e
cd "$(dirname "$0")/.."
IMAGE="golang@sha256:e8c859f5632dcfde7b32d2012b4351728f6437930887c2f6a91ea242459e5514"
PORT=8600
CID=""
cleanup() {
if [ -n "$CID" ]; then
docker rm -f "$CID" >/dev/null 2>&1 || true
fi
rm -rf .smoke
}
trap cleanup EXIT INT TERM
mkdir -p .smoke
umask 077
echo "--- build CLI (docker builder)"
docker run --rm -v "$PWD:/h" -w /h \
-u "$(id -u):$(id -g)" -e HOME=/tmp -e GOFLAGS=-buildvcs=false \
"$IMAGE" go build -o bin/bitwarden-go ./cmd/bitwarden-go
echo "--- boot fake Secrets Manager (container, port $PORT)"
CID=$(docker run -d --rm \
-v "$PWD:/h" -w /h \
-u "$(id -u):$(id -g)" -e HOME=/tmp -e GOFLAGS=-buildvcs=false \
-p 127.0.0.1:$PORT:8600 \
"$IMAGE" go run ./smoke/fakesm -addr :8600)
# wait for the fake to answer (any HTTP response, even 400, proves it is up)
i=0
until [ -n "$CID" ] && [ "$(docker inspect -f '{{.State.Running}}' "$CID" 2>/dev/null)" = "true" ] && \
printf 'grant_type=client_credentials&client_id=probe&client_secret=probe&scope=api.secrets' \
| python3 -c "
import sys, urllib.request, urllib.error
req = urllib.request.Request('http://127.0.0.1:$PORT/identity/connect/token',
data=sys.stdin.buffer.read(), headers={'Content-Type':'application/x-www-form-urlencoded'})
try:
urllib.request.urlopen(req, timeout=2)
except urllib.error.HTTPError:
sys.exit(0) # got an HTTP answer: server is up
except Exception:
sys.exit(1) # not yet
sys.exit(0)
"; do
i=$((i+1))
if [ "$i" -ge 60 ]; then
echo "smoke: fake server did not come up; logs:" >&2
docker logs "$CID" >&2 || true
exit 1
fi
sleep 1
done
CRED='0.ec2c1d46-6a4b-4751-a310-af9601317f2d.C2IgxjjLF7qSshsbwe8JGcbM075YXw:X8vbvA0bduihIDe/qrzIQQ=='
printf 'BW_SERVER_URL=http://127.0.0.1:%s\nBW_ACCESS_TOKEN=%s\n' "$PORT" "$CRED" > .smoke/env
chmod 600 .smoke/env
export BITWARDENGO_CONFIG="$PWD/.smoke/env"
echo "--- login (credential check; prints summary only)"
./bin/bitwarden-go login | tee .smoke/login.out
grep -q "authenticated: account ec2c1d46-6a4b-4751-a310-af9601317f2d" .smoke/login.out
grep -q "expires" .smoke/login.out
echo "--- projects"
./bin/bitwarden-go projects > .smoke/projects.out
grep -q "ac1d0000-0000-4000-8000-000000000001 harness" .smoke/projects.out
echo "--- secrets list"
./bin/bitwarden-go secrets list > .smoke/secrets.out
grep -q "5ec1e700-0000-4000-8000-00000000000a smoke-redmine-key" .smoke/secrets.out
grep -q "5ec1e700-0000-4000-8000-00000000000b smoke-litellm-key" .smoke/secrets.out
echo "--- get (bare value, no newline)"
V="$(./bin/bitwarden-go get smoke-redmine-key)"
[ "$V" = "smoke-redmine-value-0123456789abcdef" ] || { echo "smoke: wrong value: $V" >&2; exit 1; }
echo "--- get by uuid"
V="$(./bin/bitwarden-go get 5ec1e700-0000-4000-8000-00000000000b)"
[ "$V" = "smoke-litellm-value-fedcba9876543210" ] || { echo "smoke: wrong value: $V" >&2; exit 1; }
echo "--- failure path: missing secret (exit code + redacted stderr)"
if ./bin/bitwarden-go get no-such-secret > .smoke/missing.out 2> .smoke/missing.err; then
echo "smoke: missing secret should fail" >&2; exit 1
fi
grep -q "secret not found" .smoke/missing.err
[ ! -s .smoke/missing.out ] || { echo "smoke: stdout not empty on failure" >&2; exit 1; }
echo "--- failure path: bad credential (exit code)"
printf 'BW_SERVER_URL=http://127.0.0.1:%s\nBW_ACCESS_TOKEN=0.ec2c1d46-6a4b-4751-a310-af9601317f2d.wrong-secret:X8vbvA0bduihIDe/qrzIQQ==\n' "$PORT" > .smoke/bad.env
chmod 600 .smoke/bad.env
if BITWARDENGO_CONFIG="$PWD/.smoke/bad.env" ./bin/bitwarden-go login 2> .smoke/bad.err; then
echo "smoke: bad credential should fail" >&2; exit 1
fi
grep -q "auth failed" .smoke/bad.err
echo "--- redaction: no material in any captured output"
for f in .smoke/login.out .smoke/projects.out .smoke/secrets.out .smoke/missing.err .smoke/bad.err; do
grep -qF 'C2IgxjjLF7qSshsbwe8JGcbM075YXw' "$f" && { echo "smoke: client secret leaked into $f" >&2; exit 1; }
grep -qF 'X8vbvA0bduihIDe/qrzIQQ==' "$f" && { echo "smoke: credential key leaked into $f" >&2; exit 1; }
done
grep -qF 'smoke-litellm-value' .smoke/secrets.out && { echo "smoke: secret value leaked into listing" >&2; exit 1; }
echo "smoke: OK"