Skip to content

Commit 5e785b7

Browse files
authored
fix: redact secret values from env (#252)
1 parent 8a7168b commit 5e785b7

6 files changed

Lines changed: 228 additions & 5 deletions

File tree

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
name: Test Redacted Environment Variables
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
workflow_dispatch:
9+
10+
jobs:
11+
test-redacted-env:
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v4
16+
17+
- name: Create test mise config with sensitive values
18+
run: |
19+
cat > .mise.toml << 'EOF'
20+
[env]
21+
PUBLIC_VAR = "this-is-public"
22+
API_KEY = {value = "secret-api-key-12345", redact = true}
23+
SECRET_TOKEN = {value = "supersecret-token-xyz", redact = true}
24+
DATABASE_PASSWORD = {value = "db-pass-789", redact = true}
25+
EOF
26+
27+
- name: Setup mise
28+
uses: ./
29+
30+
- name: Verify environment variables are exported
31+
run: |
32+
echo "Checking if environment variables are set..."
33+
34+
# Check that public var is set
35+
if [ "$PUBLIC_VAR" != "this-is-public" ]; then
36+
echo "ERROR: PUBLIC_VAR not set correctly"
37+
exit 1
38+
fi
39+
echo "✓ PUBLIC_VAR is set correctly"
40+
41+
# Check that sensitive vars are set (but their values should be masked in logs)
42+
if [ -z "$API_KEY" ]; then
43+
echo "ERROR: API_KEY not set"
44+
exit 1
45+
fi
46+
echo "✓ API_KEY is set"
47+
48+
if [ -z "$SECRET_TOKEN" ]; then
49+
echo "ERROR: SECRET_TOKEN not set"
50+
exit 1
51+
fi
52+
echo "✓ SECRET_TOKEN is set"
53+
54+
if [ -z "$DATABASE_PASSWORD" ]; then
55+
echo "ERROR: DATABASE_PASSWORD not set"
56+
exit 1
57+
fi
58+
echo "✓ DATABASE_PASSWORD is set"
59+
60+
- name: Test that sensitive values are masked (will show *** if properly masked)
61+
run: |
62+
echo "Testing value masking..."
63+
echo "API_KEY value: $API_KEY"
64+
echo "SECRET_TOKEN value: $SECRET_TOKEN"
65+
echo "DATABASE_PASSWORD value: $DATABASE_PASSWORD"
66+
echo "PUBLIC_VAR value: $PUBLIC_VAR"
67+
68+
# This should show the actual values in the step output,
69+
# but GitHub Actions should mask them if core.setSecret was called
70+
71+
- name: Verify mise version
72+
run: mise --version

dist/index.js

Lines changed: 67 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package-lock.json

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"@actions/core": "^1.11.1",
3434
"@actions/exec": "^1.1.1",
3535
"@actions/glob": "^0.5.0",
36+
"@types/handlebars": "^4.0.40",
3637
"handlebars": "^4.7.8"
3738
},
3839
"devDependencies": {

src/index.ts

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,93 @@ async function run(): Promise<void> {
7171
await miseLs()
7272
const loadEnv = core.getBooleanInput('env')
7373
if (loadEnv) {
74-
const output = await exec.getExecOutput('mise', ['env', '--dotenv'])
75-
fs.appendFileSync(process.env.GITHUB_ENV!, output.stdout)
74+
await exportMiseEnv()
7675
}
7776
} catch (err) {
7877
if (err instanceof Error) core.setFailed(err.message)
7978
else throw err
8079
}
8180
}
8281

82+
async function exportMiseEnv(): Promise<void> {
83+
core.startGroup('Exporting mise environment variables')
84+
85+
// Check if mise supports --redacted flags based on version input
86+
const supportsRedacted = checkMiseSupportsRedacted()
87+
88+
if (supportsRedacted) {
89+
try {
90+
// First, get the redacted values to identify what needs masking
91+
const redactedOutput = await exec.getExecOutput(
92+
'mise',
93+
['env', '--redacted', '--json'],
94+
{ silent: true }
95+
)
96+
const redactedVars = JSON.parse(redactedOutput.stdout)
97+
98+
// Mask sensitive values in GitHub Actions
99+
for (const [key, actualValue] of Object.entries(redactedVars)) {
100+
core.setSecret(actualValue as string)
101+
core.info(`Masked sensitive value for: ${key}`)
102+
}
103+
104+
// Then get the actual values
105+
const actualOutput = await exec.getExecOutput('mise', ['env', '--json'])
106+
const actualVars = JSON.parse(actualOutput.stdout)
107+
108+
// Export all environment variables
109+
for (const [key, value] of Object.entries(actualVars)) {
110+
if (typeof value === 'string') {
111+
core.exportVariable(key, value)
112+
}
113+
}
114+
} catch {
115+
// Fall back to dotenv format if the redacted command fails
116+
core.info('Falling back to dotenv format')
117+
const output = await exec.getExecOutput('mise', ['env', '--dotenv'])
118+
fs.appendFileSync(process.env.GITHUB_ENV!, output.stdout)
119+
}
120+
} else {
121+
// Fall back to the old --dotenv format for older versions
122+
const output = await exec.getExecOutput('mise', ['env', '--dotenv'])
123+
fs.appendFileSync(process.env.GITHUB_ENV!, output.stdout)
124+
}
125+
126+
core.endGroup()
127+
}
128+
129+
function checkMiseSupportsRedacted(): boolean {
130+
const version = core.getInput('version')
131+
132+
// If no version is specified, assume latest which supports redacted
133+
if (!version) {
134+
return true
135+
}
136+
137+
// Parse the version string (remove 'v' prefix if present)
138+
const cleanVersion = version.replace(/^v/, '')
139+
const versionMatch = cleanVersion.match(/^(\d+)\.(\d+)\.(\d+)/)
140+
141+
if (!versionMatch) {
142+
// If we can't parse the version, assume it supports redacted
143+
return true
144+
}
145+
146+
const [, year, month, patch] = versionMatch
147+
const yearNum = parseInt(year, 10)
148+
const monthNum = parseInt(month, 10)
149+
const patchNum = parseInt(patch, 10)
150+
151+
// Check if version is >= 2025.8.17
152+
if (yearNum > 2025) return true
153+
if (yearNum === 2025) {
154+
if (monthNum > 8) return true
155+
if (monthNum === 8 && patchNum >= 17) return true
156+
}
157+
158+
return false
159+
}
160+
83161
async function setEnvVars(): Promise<void> {
84162
core.startGroup('Setting env vars')
85163
const set = (k: string, v: string): void => {

0 commit comments

Comments
 (0)