Managing Secrets
Secrets provide secure handling of sensitive data like API keys, passwords, and tokens. QIT validates, injects, and redacts secrets automatically.
How Secrets Work
- Declaration: Packages declare required secrets in manifest
- Validation: QIT checks all secrets exist before execution
- Injection: Secrets passed as environment variables
- Redaction: Values automatically hidden from output
Declaring Secrets
In qit-test.json:
{
"requires": {
"secrets": [
"STRIPE_TEST_KEY",
"STRIPE_TEST_SECRET",
"WEBHOOK_SIGNING_SECRET"
]
}
}
Setting Secrets
Environment Variables
# Set individually
export STRIPE_TEST_KEY="sk_test_..."
export STRIPE_TEST_SECRET="..."
export WEBHOOK_SIGNING_SECRET="whsec_..."
# Run tests
qit run:e2e woocommerce --config=test.json
From .env File
QIT has built-in support for .env files via the --env_file flag:
qit run:e2e woocommerce --env_file=.env
.env file:
STRIPE_TEST_KEY=sk_test_...
STRIPE_TEST_SECRET=...
WEBHOOK_SIGNING_SECRET=whsec_...
CI/CD Systems
GitHub Actions
- name: Run Tests
env:
STRIPE_TEST_KEY: ${{ secrets.STRIPE_TEST_KEY }}
STRIPE_TEST_SECRET: ${{ secrets.STRIPE_TEST_SECRET }}
run: qit run:e2e woocommerce
GitLab CI
test:
variables:
STRIPE_TEST_KEY: $STRIPE_TEST_KEY
STRIPE_TEST_SECRET: $STRIPE_TEST_SECRET
script:
- qit run:e2e woocommerce
Jenkins
withCredentials([
string(credentialsId: 'stripe-key', variable: 'STRIPE_TEST_KEY'),
string(credentialsId: 'stripe-secret', variable: 'STRIPE_TEST_SECRET')
]) {
sh 'qit run:e2e woocommerce'
}
Validation
Early Validation
Secrets are validated before any execution:
Missing required secrets:
- STRIPE_TEST_KEY (required by: payment-tests)
- WEBHOOK_SECRET (required by: webhook-tests)
Set these environment variables:
export STRIPE_TEST_KEY='your-key'
export WEBHOOK_SECRET='your-secret'
Cross-Package Validation
QIT collects secrets from ALL packages:
Package A declares API_KEY, Package B declares API_SECRET:
{ "requires": { "secrets": ["API_KEY"] } }
{ "requires": { "secrets": ["API_SECRET"] } }
Both must be set before execution starts.
Using Secrets
In Commands
Secrets are available as environment variables:
{
"run": [
"API_KEY=$API_KEY npm test"
]
}
In Test Code
JavaScript:
const apiKey = process.env.STRIPE_TEST_KEY;
const apiSecret = process.env.STRIPE_TEST_SECRET;
test('process payment', async () => {
const stripe = new Stripe(apiKey);
// Use stripe client
});
PHP:
$apiKey = getenv('STRIPE_TEST_KEY');
$apiSecret = getenv('STRIPE_TEST_SECRET');
$stripe = new \Stripe\StripeClient($apiKey);
Python:
import os
api_key = os.environ['STRIPE_TEST_KEY']
api_secret = os.environ['STRIPE_TEST_SECRET']
Automatic Redaction
Secret values are automatically hidden from output:
Before Redaction
Setting up Stripe with key sk_test_51234567890abcdef...
Connection established to webhook whsec_abcdef123456...
After Redaction
Setting up Stripe with key [REDACTED:STRIPE_TEST_KEY]...
Connection established to webhook [REDACTED:WEBHOOK_SECRET]...
Redaction Rules
- Only actual secret values are redacted
- Secrets shorter than 4 characters are not redacted
- Secret names are preserved for debugging
- Redaction happens in real-time
Secret Patterns
API Keys
{
"requires": {
"secrets": [
"API_KEY",
"API_SECRET",
"API_ENDPOINT"
]
}
}
Usage:
const client = new APIClient({
key: process.env.API_KEY,
secret: process.env.API_SECRET,
endpoint: process.env.API_ENDPOINT
});
Database Credentials
{
"requires": {
"secrets": [
"TEST_DB_HOST",
"TEST_DB_USER",
"TEST_DB_PASS"
]
}
}
Usage:
const connection = mysql.createConnection({
host: process.env.TEST_DB_HOST,
user: process.env.TEST_DB_USER,
password: process.env.TEST_DB_PASS
});
OAuth Tokens
{
"requires": {
"secrets": [
"OAUTH_CLIENT_ID",
"OAUTH_CLIENT_SECRET",
"OAUTH_REDIRECT_URI"
]
}
}
Webhook Secrets
{
"requires": {
"secrets": [
"WEBHOOK_SIGNING_SECRET",
"WEBHOOK_ENDPOINT_SECRET"
]
}
}
Best Practices
1. Use Descriptive Names
Good:
"secrets": [
"STRIPE_TEST_PUBLISHABLE_KEY",
"STRIPE_TEST_SECRET_KEY",
"STRIPE_WEBHOOK_SIGNING_SECRET"
]
Bad:
"secrets": [
"KEY1",
"SECRET",
"TOKEN"
]
2. Document Required Secrets
In package README:
## Required Secrets
- `STRIPE_TEST_KEY`: Stripe test mode publishable key
- `STRIPE_TEST_SECRET`: Stripe test mode secret key
- `WEBHOOK_SECRET`: Stripe webhook signing secret
Get these from your Stripe dashboard.
3. Provide Examples
.env.example:
# Stripe Test Keys (get from https://dashboard.stripe.com/test/apikeys)
STRIPE_TEST_KEY=pk_test_...
STRIPE_TEST_SECRET=sk_test_...
WEBHOOK_SECRET=whsec_...
4. Group Related Secrets
{
"requires": {
"secrets": [
"PAYMENT_GATEWAY_API_KEY",
"PAYMENT_GATEWAY_API_SECRET",
"PAYMENT_GATEWAY_MERCHANT_ID",
"SHIPPING_API_KEY",
"SHIPPING_API_SECRET"
]
}
}
5. Validate Format
In setup phase:
{
"setup": [
"node ./scripts/validate-secrets.js"
]
}
validate-secrets.js:
if (!process.env.STRIPE_TEST_KEY?.startsWith('pk_test_')) {
throw new Error('STRIPE_TEST_KEY must be a test mode key');
}
Security Considerations
Don't Commit Secrets
.gitignore:
.env
.env.local
.env.*.local
secrets/
*.key
*.pem
Use Test/Sandbox Credentials
Always use test mode credentials:
- Stripe:
sk_test_...notsk_live_... - PayPal: Sandbox not Production
- AWS: Test account not Production
Rotate Regularly
- Change test credentials periodically
- Update CI/CD systems when rotated
- Document rotation procedures
Limit Scope
Use credentials with minimal permissions:
- Read-only where possible
- Restricted to test resources
- Time-limited tokens
Troubleshooting
Secret Not Found
Error:
Missing required secrets:
- API_KEY
Solution:
export API_KEY="your-key"
Secret Not Redacted
Check:
- Secret value is longer than 3 characters
- Secret is actually being used
- Not using pattern-based redaction
Secret in Wrong Format
Validate format in tests:
if (!process.env.API_KEY || process.env.API_KEY.length < 10) {
throw new Error('Invalid API_KEY format');
}
Secret Not Available in Test
Ensure:
- Declared in qit-test.json
- Set before running tests
- Correct variable name
Advanced Patterns
Dynamic Secrets
Load from external source:
export API_KEY=$(vault read -field=key secret/api)
qit run:e2e woocommerce
Environment-Specific Secrets
# Development
export STRIPE_TEST_KEY=$DEV_STRIPE_KEY
# Staging
export STRIPE_TEST_KEY=$STAGING_STRIPE_KEY
Secret Validation Script
validate-env.sh:
#!/bin/bash
required_secrets=(
"STRIPE_TEST_KEY"
"STRIPE_TEST_SECRET"
"WEBHOOK_SECRET"
)
missing=()
for secret in "${required_secrets[@]}"; do
if [ -z "${!secret}" ]; then
missing+=($secret)
fi
done
if [ ${#missing[@]} -gt 0 ]; then
echo "Missing secrets: ${missing[*]}"
exit 1
fi
Conditional Secrets
Only require if feature enabled:
const secrets = ['BASE_API_KEY'];
if (process.env.ENABLE_STRIPE === 'true') {
secrets.push('STRIPE_KEY', 'STRIPE_SECRET');
}
// qit-test.json would list all possible secrets