Knowledge Hub
Comprehensive guides and references for the OpenFrame platform
OpenFrame Gen1 is Here · The AI platform for autonomous IT is out of beta and ready for production.
Comprehensive guides and references for the OpenFrame platform
This document describes the security patterns, practices, and mitigations built into the OpenFrame CLI, along with guidelines for contributors to maintain these standards.
The CLI authenticates to Kubernetes clusters using standard kubeconfig files. The internal/k8s package handles context loading and rest.Config construction:
// Contexts are loaded from the standard kubeconfig path (~/.kube/config)
// or from the KUBECONFIG environment variable.
// rest.Config is constructed per-operation, not stored globally.
Guidelines:
clientcmd.BuildConfigFromFlagsAccessor type for cluster health checks rather than raw API callsrest.Config through function arguments, not global variablesThe self-update and download subsystems authenticate with GitHub using tokens:
| Variable | Priority | Description |
|---|---|---|
OPENFRAME_GITHUB_TOKEN |
High | OpenFrame-specific token (takes precedence) |
GITHUB_TOKEN |
Standard | Standard GitHub Actions token |
Guidelines:
redact.RegisterSecret() at startupps output)ArgoCD is managed via the native Kubernetes dynamic client (client-go) rather than the ArgoCD HTTP API. This means:
All potentially sensitive values must be registered with the redact package before any logging or command execution:
import "github.com/flamingo-stack/openframe-cli/internal/shared/redact"
// Register a secret for automatic scrubbing
redact.RegisterSecret(githubToken)
redact.RegisterSecret(registryPassword)
// All log output and command strings are automatically scrubbed
// redact.Redact("helm upgrade --set auth.token=mysecret")
// → "helm upgrade --set auth.token=***"
Key behaviors:
user:pass@host) are scrubbed unconditionally without explicit registrationsync.RWMutexredact.ClearSecrets() in teardown to prevent cross-test contaminationContribution rule: Any value read from environment variables, configuration files, or user prompts that could be a credential must be passed through redact.RegisterSecret() before being used in any executor call or log statement.
Cluster names are validated against RFC1123 rules at the command boundary before reaching any shell-out:
// Validation is enforced in cmd/bootstrap/bootstrap.go and cmd/cluster/create.go
// before any subprocess execution — prevents injection via cluster names
if err := clustermodels.ValidateClusterName(name); err != nil {
return err
}
This ensures that a cluster name like ; rm -rf / cannot reach the k3d subprocess.
The openframe-helm-values.yaml file is validated via a "preflight" check before cluster creation — the cheapest gate in the pipeline:
// internal/chart/services/preflight.go
if err := services.ValidateHelmValuesFile(); err != nil {
// Fails fast before any expensive cluster operations
return err
}
Guidelines:
CommandExecutor interfaceThe CommandExecutor interface uses os/exec with argv arrays (not shell invocation):
// SAFE: argv array — no shell injection possible
result, err := exec.Execute(ctx, "k3d", "cluster", "list", "--output", "json")
// NEVER do this — shell injection risk:
// exec.Execute(ctx, "sh", "-c", "k3d cluster list --output " + userInput)
Contribution rule: Never pass user input to sh -c or any shell interpreter. Always use direct os/exec with separate argument lists.
The self-update mechanism uses Sigstore/cosign for supply chain security:
graph LR
A["openframe update"] --> B["Fetch latest release from GitHub"]
B --> C["Download checksums.txt + bundle.json"]
C --> D["Verify cosign signature"]
D --> E{"Signature valid?"}
E -->|Yes| F["Download binary archive"]
E -->|No| G["REJECT — abort update"]
F --> H["Verify SHA256 checksum"]
H --> I["Smoke-test new binary"]
I --> J["Atomic binary swap"]
J --> K[".bak rollback saved"]
Pinned identity checks:
https://token.actions.githubusercontent.com (GitHub Actions only)flamingo-stack/openframe-cli's release.yml workflow on main or tag refsEmergency escape hatch (for testing/development only — never in production):
export OPENFRAME_UPDATE_INSECURE_SKIP_VERIFY=1
Warning: Setting
OPENFRAME_UPDATE_INSECURE_SKIP_VERIFY=1disables all cryptographic verification. Only use this in isolated development environments.
All binary downloads (k3d, mkcert, Helm) use pinned versions and SHA256 checksum verification:
// internal/shared/download/pins.go
// Each tool has a pinned version and expected SHA256 checksum
// Downloads are rejected if the checksum doesn't match
Guidelines:
On Windows, the CLI forwards execution into WSL2:
// Only forward if ShouldForward() returns true
// ShouldForward() returns false if:
// - running on Linux (prevents infinite recursion)
// - OPENFRAME_NO_WSL_FORWARD=1 is set
if wsllauncher.ShouldForward() {
code, err := wsllauncher.Forward(version, os.Args[1:])
os.Exit(code)
}
Environment variables GITHUB_TOKEN and OPENFRAME_GITHUB_TOKEN are forwarded into WSL via WSLENV — ensure these are not set to high-privilege tokens in shared environments.
redact.RegisterSecret() immediately on ingestionps aux). Use environment variablesOPENFRAME_GITHUB_TOKEN should be scoped to the minimum required permissionsFor OPENFRAME_GITHUB_TOKEN / GITHUB_TOKEN:
| Scope | Required? | Reason |
|---|---|---|
read:packages |
Optional | Accessing private container images |
repo (public read) |
No | Public repos are accessible without auth |
| No special scopes | Sufficient | For rate-limit bypass only (public repos) |
| Vulnerability | Mitigation |
|---|---|
| Command injection | os/exec with argv arrays; cluster name RFC1123 validation |
| Secret leakage in logs | redact package with automatic URL credential scrubbing |
| Malicious update binary | Cosign signature verification against pinned GitHub Actions identity |
| Checksum bypass | SHA256 verification before any binary execution |
| Stale kubeconfig | Context validated before use; Accessor.Reachable() check |
| YAML injection | Structured Helm values parsing, not raw string interpolation |
| Token exposure in env | Tokens forwarded via WSLENV mechanism, not command arguments |
The MockCommandExecutor records all argv arrays for security assertions:
mock := executor.MockCommandExecutor{}
// After execution:
calls := mock.RecordedCalls()
for _, call := range calls {
// Assert no user input leaked into command args without validation
assert.NotContains(t, call.Args, userInput)
}
Security test checklist for new commands:
ValidateClusterNameredact.RegisterSecret()Please report security vulnerabilities via the OpenMSP Slack community using a direct message to the maintainers rather than public channels. Do not open public GitHub issues for security vulnerabilities.