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:
@@ -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
@@ -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"
|
||||
Reference in New Issue
Block a user