Skip to content

Commit 1913066

Browse files
fix(security): restore EL7 support and make the credential tests able to fail
RHEL/CentOS 7 ships curl 7.29.0, which does not know oauth2-bearer. It does not reject the unknown configuration key either: it ignores it, exits 0, and sends the request with no credential at all. The previous commit therefore broke EL7 silently rather than loudly. Each script now detects the capability positively and falls back to a raw Authorization header when it is missing. The credential still travels on curl's configuration input in both modes, so it never reaches the command line. The gate moves from 7.55 to 7.33, the version that added oauth2-bearer, which also stops blocking curl 7.33 through 7.54 for no reason, and it is restored in the uninstall and migrate scripts where it had been dropped. Warning text now names what is actually unverified on very old curl, which is redirect handling, and names the control that bounds it. Two latent defects turned up while doing this. The command -v curl guard sat inside a command substitution, where die only exits the subshell and leaves the variable empty; in falcon-linux-uninstall.sh that guard also called die 39 lines before die was defined, so a host without curl got "die: command not found" instead of the message. Both are fixed. The credential handling suite could not fail. It called rg, which is not installed on ubuntu-latest, so "if rg ...; then fail; fi" read exit 127 as no match and three checks quietly did nothing. Replaced with grep behind a helper that inspects the exit status, so a missing or broken tool fails instead of passing. The mode loop now exercises both credential mechanisms rather than looping over a variable nothing read. The container matrix passed its setup command through a variable that the container shell expanded after parsing, turning && and > into literal arguments, which is why all five legs errored. A dependency script that detects the package manager replaces it, and Oracle Linux 7 and CentOS 7 legs are added, since the oldest curl exercised before this was 7.81. Also: aws_ssm_parameter no longer prints the decrypted response body on failure, since that body can carry the client secret; the live curl test runs on python2 and cannot hang; and the PowerShell test reports every collected failure instead of throwing on the first one.
1 parent cd7a9c8 commit 1913066

13 files changed

Lines changed: 510 additions & 156 deletions
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/bin/sh
2+
3+
# Installs the packages that .github/scripts/test-curl-oauth-live.sh needs:
4+
# curl and a python interpreter. Only missing packages are installed, so the
5+
# script does nothing on images that already have both. RHEL/CentOS 7 images
6+
# supply curl and python2 already, so no package manager runs there.
7+
8+
set -eu
9+
10+
have() {
11+
command -v "$1" >/dev/null 2>&1
12+
}
13+
14+
have_python() {
15+
have python3 || have python2 || have python
16+
}
17+
18+
if have curl && have_python; then
19+
echo 'curl and python are already present; no packages to install.'
20+
exit 0
21+
fi
22+
23+
if have apk; then
24+
packages=""
25+
have curl || packages="$packages curl"
26+
have_python || packages="$packages python3"
27+
# shellcheck disable=SC2086 # deliberate word splitting into package names
28+
apk add --no-cache $packages
29+
elif have dnf; then
30+
packages=""
31+
have curl || packages="$packages curl"
32+
have_python || packages="$packages python3"
33+
# shellcheck disable=SC2086 # deliberate word splitting into package names
34+
dnf install -y -q $packages
35+
elif have yum; then
36+
packages=""
37+
have curl || packages="$packages curl"
38+
have_python || packages="$packages python"
39+
# shellcheck disable=SC2086 # deliberate word splitting into package names
40+
yum install -y -q $packages
41+
elif have apt-get; then
42+
packages=""
43+
have curl || packages="$packages curl"
44+
have_python || packages="$packages python3"
45+
apt-get update -qq
46+
# shellcheck disable=SC2086 # deliberate word splitting into package names
47+
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq $packages
48+
else
49+
echo 'ERROR: no supported package manager found (apk, dnf, yum, apt-get).' >&2
50+
exit 1
51+
fi
52+
53+
echo 'Test dependencies are installed.'

.github/scripts/test-credential-handling.sh

Lines changed: 83 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,56 @@ fail() {
1111
exit 1
1212
}
1313

14+
# Report an absent or broken search tool as a failure instead of a pass. grep
15+
# returns 0 for a match, 1 for no match, and 2 or more for an error. Only 1 is
16+
# an acceptable result here.
17+
assert_no_match() {
18+
local description=$1 pattern=$2 tree=$3 include=$4
19+
local output status
20+
21+
set +e
22+
output=$(grep -rnE "$pattern" "$repo_root/$tree" --include="$include")
23+
status=$?
24+
set -e
25+
26+
if [ "$status" -eq 0 ]; then
27+
echo "$output" >&2
28+
fail "$description"
29+
fi
30+
if [ "$status" -ne 1 ]; then
31+
fail "grep failed with status $status while checking: $description"
32+
fi
33+
}
34+
1435
curl() {
1536
printf '%s\n' "$@" >"$CURL_ARGS_FILE"
1637
cat >"$CURL_STDIN_FILE"
1738
}
1839

19-
export CURL_ARGS_FILE CURL_STDIN_FILE
20-
2140
test_curl_helper() {
2241
local script=$1 token_mode=$2
23-
local helper
42+
local helper mode expected_key
2443

2544
helper=$(awk '/^curl_command\(\)/,/^}/' "$repo_root/$script")
2645
[ -n "$helper" ] || fail "curl_command not found in $script"
2746
eval "$helper"
2847

29-
for old_curl in 0 1; do
48+
# Mode 1 uses the oauth2-bearer configuration key. Mode 0 is the fallback
49+
# for curl older than 7.33.0 and uses a raw Authorization header. Both must
50+
# keep the credential on stdin.
51+
for mode in 1 0; do
52+
# shellcheck disable=SC2034 # read by the curl_command body under eval
53+
curl_has_oauth2_bearer=$mode
54+
if [ "$mode" -eq 1 ]; then
55+
expected_key='oauth2-bearer = "REGRESSION_SECRET_TOKEN"'
56+
else
57+
expected_key='header = "Authorization: Bearer REGRESSION_SECRET_TOKEN"'
58+
fi
59+
3060
CURL_ARGS_FILE="$work_dir/args"
3161
CURL_STDIN_FILE="$work_dir/stdin"
62+
# shellcheck disable=SC2034 # read by the curl_command body under eval
3263
proxy=""
33-
export proxy
3464
cs_falcon_oauth_token="REGRESSION_SECRET_TOKEN"
3565

3666
if [ "$token_mode" = "argument" ]; then
@@ -40,16 +70,18 @@ test_curl_helper() {
4070
fi
4171

4272
if grep -qF "$cs_falcon_oauth_token" "$CURL_ARGS_FILE"; then
43-
fail "$script exposed the bearer token in curl arguments (old_curl=$old_curl)"
73+
fail "$script exposed the bearer token in curl arguments (mode=$mode)"
4474
fi
4575
grep -qF 'https://api.example.invalid/resource' "$CURL_ARGS_FILE" ||
46-
fail "$script did not pass the expected URL (old_curl=$old_curl)"
76+
fail "$script did not pass the expected URL (mode=$mode)"
4777
grep -qF "$cs_falcon_oauth_token" "$CURL_STDIN_FILE" ||
48-
fail "$script did not provide the bearer token through stdin (old_curl=$old_curl)"
78+
fail "$script did not provide the bearer token through stdin (mode=$mode)"
79+
grep -qF "$expected_key" "$CURL_STDIN_FILE" ||
80+
fail "$script did not use the expected credential mechanism (mode=$mode)"
4981
grep -qF -- '--proto' "$CURL_ARGS_FILE" ||
50-
fail "$script did not restrict the request protocol (old_curl=$old_curl)"
82+
fail "$script did not restrict the request protocol (mode=$mode)"
5183
grep -qF -- '--proto-redir' "$CURL_ARGS_FILE" ||
52-
fail "$script did not restrict the redirect protocol (old_curl=$old_curl)"
84+
fail "$script did not restrict the redirect protocol (mode=$mode)"
5385
done
5486
}
5587

@@ -85,37 +117,51 @@ test_hash_verification() {
85117

86118
helper=$(awk '/^verify_sha256\(\)/,/^}/' "$repo_root/$script")
87119
[ -n "$helper" ] || fail "verify_sha256 not found in $script"
88-
eval "$helper"
89-
die() { exit 1; }
90-
91-
test_file="$work_dir/installer"
92-
printf '%s' 'verified installer content' >"$test_file"
93-
expected_sha=$(openssl dgst -sha256 "$test_file" | awk '{ print $NF }')
94-
verify_sha256 "$test_file" "$expected_sha" ||
95-
fail "$script rejected a valid installer hash"
96-
97-
if (verify_sha256 "$test_file" '0000000000000000000000000000000000000000000000000000000000000000'); then
98-
fail "$script accepted an invalid installer hash"
99-
fi
100-
[ ! -e "$test_file" ] || fail "$script retained an installer with an invalid hash"
120+
(
121+
eval "$helper"
122+
# verify_sha256 calls die on a mismatch; keep the stub inside the subshell.
123+
# shellcheck disable=SC2329 # invoked indirectly by verify_sha256
124+
die() { exit 1; }
125+
126+
test_file="$work_dir/installer"
127+
printf '%s' 'verified installer content' >"$test_file"
128+
expected_sha=$(openssl dgst -sha256 "$test_file" | awk '{ print $NF }')
129+
verify_sha256 "$test_file" "$expected_sha" ||
130+
fail "$script rejected a valid installer hash"
131+
132+
if (verify_sha256 "$test_file" '0000000000000000000000000000000000000000000000000000000000000000'); then
133+
fail "$script accepted an invalid installer hash"
134+
fi
135+
[ ! -e "$test_file" ] || fail "$script retained an installer with an invalid hash"
136+
)
101137
}
102138

103139
test_hash_verification bash/install/falcon-linux-install.sh
104140
test_hash_verification bash/migrate/falcon-linux-migrate.sh
105141

106-
if rg -n 'Invalid Access Token:.*\$cs_falcon_oauth_token|Failed to retrieve maintenance token\. Response:' \
107-
"$repo_root/bash" --glob '*.sh'; then
108-
fail 'a Bash error path exposes a credential or raw maintenance-token response'
109-
fi
110-
111-
if rg -n 'curl .*X-aws-ec2-metadata-token:.*\$token' \
112-
"$repo_root/bash" --glob '*.sh'; then
113-
fail 'an EC2 metadata token is exposed in curl arguments'
114-
fi
115-
116-
if rg -n '(Invoke-FalconAuth|GetToken) - \$content:|Retrieved maintenance token:|Starting .*parameters.*\$(Install|Uninstall)Params' \
117-
"$repo_root/powershell" --glob '*.ps1'; then
118-
fail 'a PowerShell log statement exposes an authentication or installer token'
119-
fi
142+
# The arguments below are grep patterns, not shell expansions.
143+
# shellcheck disable=SC2016
144+
assert_no_match \
145+
'a Bash error path exposes a credential or raw maintenance-token response' \
146+
'Invalid Access Token:.*\$cs_falcon_oauth_token|Failed to retrieve maintenance token\. Response:' \
147+
bash '*.sh'
148+
149+
# shellcheck disable=SC2016
150+
assert_no_match \
151+
'an EC2 metadata token is exposed in curl arguments' \
152+
'curl .*X-aws-ec2-metadata-token:.*\$token' \
153+
bash '*.sh'
154+
155+
# shellcheck disable=SC2016
156+
assert_no_match \
157+
'an AWS SSM Parameter Store error path prints the decrypted response body' \
158+
'AWS SSM Parameter Store[^"]*\$response' \
159+
bash '*.sh'
160+
161+
# shellcheck disable=SC2016
162+
assert_no_match \
163+
'a PowerShell log statement exposes an authentication or installer token' \
164+
'(Invoke-FalconAuth|GetToken) - \$content:|Retrieved maintenance token:|Starting .*parameters.*\$(Install|Uninstall)Params' \
165+
powershell '*.ps1'
120166

121167
echo 'PASS: credential handling regression checks'

0 commit comments

Comments
 (0)