Skip to content

Commit b372d61

Browse files
authored
fix(interpreter): expand assoc array keys with command substitutions (#878)
Add async expand_assoc_key() that uses full word expansion for associative array keys containing $() or backtick substitutions. Fixes #872
1 parent 9752fe0 commit b372d61

File tree

3 files changed

+86
-48
lines changed

3 files changed

+86
-48
lines changed

crates/bashkit/src/interpreter/mod.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3129,7 +3129,7 @@ impl Interpreter {
31293129
if let Some(index_str) = &assignment.index {
31303130
let resolved_name = self.resolve_nameref(&assignment.name).to_string();
31313131
if self.assoc_arrays.contains_key(&resolved_name) {
3132-
let key = self.expand_variable_or_literal(index_str);
3132+
let key = self.expand_assoc_key(index_str).await?;
31333133
let is_new_entry = self
31343134
.assoc_arrays
31353135
.get(&resolved_name)
@@ -7791,6 +7791,18 @@ impl Interpreter {
77917791
s.to_string()
77927792
}
77937793

7794+
/// Expand an associative array key with full word expansion.
7795+
/// Unlike `expand_variable_or_literal`, this handles command substitutions
7796+
/// (`$(...)`, backticks) and all other expansion types. (Issue #872)
7797+
async fn expand_assoc_key(&mut self, s: &str) -> Result<String> {
7798+
if s.contains('$') || s.contains('`') {
7799+
let word = crate::parser::Parser::parse_word_string(s);
7800+
self.expand_word(&word).await
7801+
} else {
7802+
Ok(s.to_string())
7803+
}
7804+
}
7805+
77947806
/// THREAT[TM-INJ-009]: Check if a variable name is an internal marker.
77957807
fn is_internal_variable(name: &str) -> bool {
77967808
is_internal_variable(name)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//! Test for issue #872: Associative array keys with command substitutions
2+
//! expand to empty string.
3+
4+
use bashkit::Bash;
5+
6+
#[tokio::test]
7+
async fn assoc_key_command_substitution() {
8+
let mut bash = Bash::new();
9+
let result = bash
10+
.exec(
11+
r#"
12+
declare -A m=()
13+
m["$(echo hello)"]="world"
14+
echo "count: ${#m[@]}"
15+
for k in "${!m[@]}"; do echo "key=[$k] val=[${m[$k]}]"; done
16+
"#,
17+
)
18+
.await
19+
.unwrap();
20+
assert!(
21+
result.stdout.contains("key=[hello] val=[world]"),
22+
"expected key=[hello], got: {}",
23+
result.stdout
24+
);
25+
}
26+
27+
#[tokio::test]
28+
async fn assoc_key_variable_expansion() {
29+
let mut bash = Bash::new();
30+
let result = bash
31+
.exec(
32+
r#"
33+
declare -A m=()
34+
key="mykey"
35+
m[$key]="myval"
36+
echo "${m[mykey]}"
37+
"#,
38+
)
39+
.await
40+
.unwrap();
41+
assert!(
42+
result.stdout.contains("myval"),
43+
"expected myval, got: {}",
44+
result.stdout
45+
);
46+
}
47+
48+
#[tokio::test]
49+
async fn assoc_key_literal_unchanged() {
50+
let mut bash = Bash::new();
51+
let result = bash
52+
.exec(
53+
r#"
54+
declare -A m=()
55+
m[literal]="val"
56+
echo "${m[literal]}"
57+
"#,
58+
)
59+
.await
60+
.unwrap();
61+
assert!(
62+
result.stdout.contains("val"),
63+
"expected val, got: {}",
64+
result.stdout
65+
);
66+
}

supply-chain/config.toml

Lines changed: 7 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,6 @@ criteria = "safe-to-run"
154154
version = "0.2.4"
155155
criteria = "safe-to-deploy"
156156

157-
[[exemptions.cc]]
158-
version = "1.2.57"
159-
criteria = "safe-to-deploy"
160-
161157
[[exemptions.cc]]
162158
version = "1.2.58"
163159
criteria = "safe-to-deploy"
@@ -206,10 +202,6 @@ criteria = "safe-to-deploy"
206202
version = "1.1.0"
207203
criteria = "safe-to-deploy"
208204

209-
[[exemptions.cmake]]
210-
version = "0.1.57"
211-
criteria = "safe-to-deploy"
212-
213205
[[exemptions.cmake]]
214206
version = "0.1.58"
215207
criteria = "safe-to-deploy"
@@ -238,10 +230,6 @@ criteria = "safe-to-deploy"
238230
version = "0.9.0"
239231
criteria = "safe-to-deploy"
240232

241-
[[exemptions.console]]
242-
version = "0.15.11"
243-
criteria = "safe-to-run"
244-
245233
[[exemptions.console]]
246234
version = "0.16.3"
247235
criteria = "safe-to-run"
@@ -578,10 +566,6 @@ criteria = "safe-to-deploy"
578566
version = "2.13.0"
579567
criteria = "safe-to-deploy"
580568

581-
[[exemptions.insta]]
582-
version = "1.46.3"
583-
criteria = "safe-to-run"
584-
585569
[[exemptions.insta]]
586570
version = "1.47.0"
587571
criteria = "safe-to-run"
@@ -598,10 +582,6 @@ criteria = "safe-to-deploy"
598582
version = "2.12.0"
599583
criteria = "safe-to-deploy"
600584

601-
[[exemptions.iri-string]]
602-
version = "0.7.10"
603-
criteria = "safe-to-deploy"
604-
605585
[[exemptions.iri-string]]
606586
version = "0.7.11"
607587
criteria = "safe-to-deploy"
@@ -642,10 +622,6 @@ criteria = "safe-to-deploy"
642622
version = "0.21.1"
643623
criteria = "safe-to-deploy"
644624

645-
[[exemptions.jni-sys]]
646-
version = "0.3.0"
647-
criteria = "safe-to-deploy"
648-
649625
[[exemptions.jni-sys]]
650626
version = "0.3.1"
651627
criteria = "safe-to-deploy"
@@ -663,7 +639,7 @@ version = "0.1.34"
663639
criteria = "safe-to-deploy"
664640

665641
[[exemptions.js-sys]]
666-
version = "0.3.91"
642+
version = "0.3.92"
667643
criteria = "safe-to-deploy"
668644

669645
[[exemptions.leb128fmt]]
@@ -726,10 +702,6 @@ criteria = "safe-to-deploy"
726702
version = "0.8.9"
727703
criteria = "safe-to-deploy"
728704

729-
[[exemptions.mio]]
730-
version = "1.1.1"
731-
criteria = "safe-to-deploy"
732-
733705
[[exemptions.mio]]
734706
version = "1.2.0"
735707
criteria = "safe-to-deploy"
@@ -1190,10 +1162,6 @@ criteria = "safe-to-deploy"
11901162
version = "0.9.1"
11911163
criteria = "safe-to-deploy"
11921164

1193-
[[exemptions.simd-adler32]]
1194-
version = "0.3.8"
1195-
criteria = "safe-to-deploy"
1196-
11971165
[[exemptions.simd-adler32]]
11981166
version = "0.3.9"
11991167
criteria = "safe-to-deploy"
@@ -1390,10 +1358,6 @@ criteria = "safe-to-deploy"
13901358
version = "0.1.25"
13911359
criteria = "safe-to-deploy"
13921360

1393-
[[exemptions.unicode-segmentation]]
1394-
version = "1.13.1"
1395-
criteria = "safe-to-deploy"
1396-
13971361
[[exemptions.unicode-segmentation]]
13981362
version = "1.13.2"
13991363
criteria = "safe-to-deploy"
@@ -1463,23 +1427,23 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
14631427
criteria = "safe-to-run"
14641428

14651429
[[exemptions.wasm-bindgen]]
1466-
version = "0.2.114"
1430+
version = "0.2.115"
14671431
criteria = "safe-to-deploy"
14681432

14691433
[[exemptions.wasm-bindgen-futures]]
1470-
version = "0.4.64"
1434+
version = "0.4.65"
14711435
criteria = "safe-to-deploy"
14721436

14731437
[[exemptions.wasm-bindgen-macro]]
1474-
version = "0.2.114"
1438+
version = "0.2.115"
14751439
criteria = "safe-to-deploy"
14761440

14771441
[[exemptions.wasm-bindgen-macro-support]]
1478-
version = "0.2.114"
1442+
version = "0.2.115"
14791443
criteria = "safe-to-deploy"
14801444

14811445
[[exemptions.wasm-bindgen-shared]]
1482-
version = "0.2.114"
1446+
version = "0.2.115"
14831447
criteria = "safe-to-deploy"
14841448

14851449
[[exemptions.wasm-encoder]]
@@ -1499,7 +1463,7 @@ version = "0.244.0"
14991463
criteria = "safe-to-deploy"
15001464

15011465
[[exemptions.web-sys]]
1502-
version = "0.3.91"
1466+
version = "0.3.92"
15031467
criteria = "safe-to-deploy"
15041468

15051469
[[exemptions.web-time]]
@@ -1562,10 +1526,6 @@ criteria = "safe-to-deploy"
15621526
version = "0.52.0"
15631527
criteria = "safe-to-deploy"
15641528

1565-
[[exemptions.windows-sys]]
1566-
version = "0.59.0"
1567-
criteria = "safe-to-run"
1568-
15691529
[[exemptions.windows-sys]]
15701530
version = "0.60.2"
15711531
criteria = "safe-to-deploy"

0 commit comments

Comments
 (0)