Skip to content

Commit d393f38

Browse files
committed
core: Build _GO_PLUGINS_PATHS up, not down
I realized that the previous way `_GO_PLUGINS_PATHS` was getting built would allow nested plugin paths to leak up into the environment of plugins closer to the `_GO_PLUGINS_DIR` root. After considering how to filter `_GO_PLUGINS_PATHS`, I realized the best solution was to build it by walking up the directory tree whenever a plugin is executed. Ironically, this is similar to another commit I'm working on in another branch to find plugin files (specifically plugin modules, though the mechanism is generic) in a similar fashion.
1 parent af3cb36 commit d393f38

File tree

4 files changed

+85
-20
lines changed

4 files changed

+85
-20
lines changed

go-core.bash

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ declare _GO_INJECT_MODULE_PATH="$_GO_INJECT_MODULE_PATH"
259259
return 1
260260
fi
261261

262-
if [[ "$__go_cmd_path" =~ ^$_GO_SCRIPTS_DIR/plugins ]]; then
262+
if [[ "$__go_cmd_path" =~ ^$_GO_PLUGINS_DIR/ ]]; then
263263
_@go.run_plugin_command_script "$__go_cmd_path" "${__go_argv[@]}"
264264
else
265265
_@go.run_command_script "$__go_cmd_path" "${__go_argv[@]}"
@@ -275,8 +275,24 @@ _@go.source_builtin() {
275275
_@go.run_plugin_command_script() {
276276
local _GO_SCRIPTS_DIR="${__go_cmd_path%/*}"
277277
local _GO_ROOTDIR="${_GO_SCRIPTS_DIR%/*}"
278-
local _GO_PLUGINS_PATHS=("${_GO_PLUGINS_PATHS[@]}")
279278
local _GO_SEARCH_PATHS=()
279+
local _GO_PLUGINS_PATHS=()
280+
local plugins_dir="$_GO_SCRIPTS_DIR/plugins"
281+
local plugin_paths=()
282+
283+
# A plugin's own local plugin paths will appear before inherited ones. If
284+
# there is a version incompatibility issue with other installed plugins, this
285+
# allows a plugin's preferred version to take precedence.
286+
while true; do
287+
plugin_paths=("$plugins_dir"/*/bin)
288+
if [[ "${plugin_paths[0]}" != "$plugins_dir/*/bin" ]]; then
289+
_GO_PLUGINS_PATHS+=("${plugin_paths[@]}")
290+
fi
291+
if [[ "$plugins_dir" == "$_GO_PLUGINS_DIR" ]]; then
292+
break
293+
fi
294+
plugins_dir="${plugins_dir%/plugins/*}/plugins"
295+
done
280296

281297
_@go.set_search_paths
282298
_@go.run_command_script "$__go_cmd_path" "${__go_argv[@]}"

lib/internal/path

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,13 @@
11
#! /bin/bash
22

33
_@go.set_search_paths() {
4-
local plugins_paths=("$_GO_SCRIPTS_DIR"/plugins/*/bin)
54
local plugin_path
65

76
if [[ -n "$_GO_INJECT_SEARCH_PATH" ]]; then
87
_GO_SEARCH_PATHS+=("$_GO_INJECT_SEARCH_PATH")
98
fi
109
_GO_SEARCH_PATHS+=("$_GO_CORE_DIR/libexec" "$_GO_SCRIPTS_DIR")
1110

12-
# A plugin's own local plugin paths will appear before inherited ones. If
13-
# there is a version incompatibility issue with other installed plugins, this
14-
# allows a plugin's preferred version to take precedence.
15-
if [[ "${plugins_paths[0]}" != "$_GO_SCRIPTS_DIR/plugins/*/bin" ]]; then
16-
_GO_PLUGINS_PATHS=("${plugins_paths[@]}" "${_GO_PLUGINS_PATHS[@]}")
17-
fi
18-
1911
# A plugin's _GO_SCRIPTS_DIR may continue to appear in _GO_PLUGINS_PATHS so
2012
# that it's available to other plugins that depend upon it as a circular
2113
# dependency (though such dependencies are strongly discouraged). However, we
@@ -28,8 +20,13 @@ _@go.set_search_paths() {
2820
}
2921

3022
if [[ "${#_GO_SEARCH_PATHS[@]}" -eq 0 ]]; then
31-
_@go.set_search_paths
3223
_GO_PLUGINS_DIR="$_GO_SCRIPTS_DIR/plugins"
24+
_GO_PLUGINS_PATHS=("$_GO_PLUGINS_DIR"/*/bin)
25+
26+
if [[ "${_GO_PLUGINS_PATHS[0]}" == "$_GO_PLUGINS_DIR/*/bin" ]]; then
27+
unset '_GO_PLUGINS_PATHS[0]'
28+
fi
29+
_@go.set_search_paths
3330
fi
3431

3532
_@go.list_available_commands() {

tests/core/plugin-scope-execution.bats

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,15 @@ teardown() {
140140
run "$TEST_GO_SCRIPT" 'foo'
141141
assert_success "$TEST_GO_SCRIPTS_DIR/plugins/baz/bin/baz"
142142
}
143+
144+
@test "$SUITE: nested plugins dir doesn't leak to sibling plugin" {
145+
@go.create_test_command_script 'plugins/foo/bin/foo' '@go bar'
146+
@go.create_test_command_script 'plugins/foo/bin/plugins/bar/bin/bar' '@go baz'
147+
@go.create_test_command_script 'plugins/foo/bin/plugins/quux/bin/quux' \
148+
"$PRINT_SOURCE"
149+
@go.create_test_command_script 'plugins/baz/bin/baz' '@go quux'
150+
151+
run "$TEST_GO_SCRIPT" 'foo'
152+
assert_failure
153+
assert_line_equals 0 'Unknown command: quux'
154+
}

tests/core/plugin-scope-variable-values.bats

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,16 @@ setup() {
2525
'printf "TOP LEVEL SCOPE:\n"' \
2626
"${print_scope_implementation[@]}"
2727
@go.create_test_command_script 'plugins/first/bin/first' \
28-
'printf "FIRST LEVEL PLUGIN SCOPE:\n"' \
28+
'printf "FIRST PLUGIN SCOPE:\n"' \
2929
"${print_scope_implementation[@]}"
30+
31+
# This script will execute one of the other plugins based on its argument.
3032
@go.create_test_command_script 'plugins/second/bin/second' \
31-
"@go third" \
32-
'printf "FIRST LEVEL PLUGIN SCOPE:\n"' \
33+
'@go "$1"' \
34+
'printf "SECOND PLUGIN SCOPE:\n"' \
3335
"${print_scope_implementation[@]}"
3436
@go.create_test_command_script 'plugins/second/bin/plugins/third/bin/third' \
35-
'printf "SECOND LEVEL PLUGIN SCOPE:\n"' \
37+
'printf "THIRD PLUGIN SCOPE:\n"' \
3638
"${print_scope_implementation[@]}"
3739

3840
EXPECTED_ROOT_SCOPE_PLUGINS_PATHS=("$TEST_GO_PLUGINS_DIR/first/bin"
@@ -96,7 +98,7 @@ assert_scope_values_equal() {
9698
# common plugin in the top-level _GO_PLUGINS_DIR. The plugin's _GO_SCRIPTS_DIR
9799
# will still appear in _GO_PLUGINS_PATHS, but won't be duplicated in
98100
# _GO_SEARCH_PATHS.
99-
assert_scope_values_equal 'FIRST LEVEL PLUGIN' \
101+
assert_scope_values_equal 'FIRST PLUGIN' \
100102
'_GO_ROOTDIR:' \
101103
"$TEST_GO_PLUGINS_DIR/first" \
102104
'_GO_SCRIPTS_DIR:' \
@@ -112,8 +114,8 @@ assert_scope_values_equal() {
112114
assert_scope_values_equal 'ROOT LEVEL' "${EXPECTED_ROOT_SCOPE_VALUES[@]}"
113115
}
114116

115-
@test "$SUITE: second-level plugin script" {
116-
run "$TEST_GO_SCRIPT" second
117+
@test "$SUITE: first-level plugin script invoking own second-level plugin" {
118+
run "$TEST_GO_SCRIPT" second third
117119
assert_success
118120

119121
# As with the previous test case, _GO_PLUGINS_PATHS is inherited, and the
@@ -123,7 +125,7 @@ assert_scope_values_equal() {
123125
# Its parent's _GO_SCRIPTS_DIR is in both _GO_PLUGINS_PATHS and
124126
# _GO_SEARCH_PATHS, since its parent is also a plugin. This could support a
125127
# circular dependency, though such dependencies are strongly discouraged.
126-
assert_scope_values_equal 'SECOND LEVEL PLUGIN' \
128+
assert_scope_values_equal 'THIRD PLUGIN' \
127129
'_GO_ROOTDIR:' \
128130
"$TEST_GO_PLUGINS_DIR/second/bin/plugins/third" \
129131
'_GO_SCRIPTS_DIR:' \
@@ -141,7 +143,45 @@ assert_scope_values_equal() {
141143
# The first level plugin's own plugin paths will appear before any inherited
142144
# _GO_PLUGINS_PATHS, to support a plugin's own installed plugin version to
143145
# take precedence over other installed versions.
144-
assert_scope_values_equal 'FIRST LEVEL PLUGIN' \
146+
assert_scope_values_equal 'SECOND PLUGIN' \
147+
'_GO_ROOTDIR:' \
148+
"$TEST_GO_PLUGINS_DIR/second" \
149+
'_GO_SCRIPTS_DIR:' \
150+
"$TEST_GO_PLUGINS_DIR/second/bin" \
151+
'_GO_PLUGINS_PATHS:' \
152+
"$TEST_GO_PLUGINS_DIR/second/bin/plugins/third/bin" \
153+
"$TEST_GO_PLUGINS_DIR/first/bin" \
154+
"$TEST_GO_PLUGINS_DIR/second/bin" \
155+
'_GO_SEARCH_PATHS:' \
156+
"$_GO_CORE_DIR/libexec" \
157+
"$TEST_GO_PLUGINS_DIR/second/bin" \
158+
"$TEST_GO_PLUGINS_DIR/second/bin/plugins/third/bin" \
159+
"$TEST_GO_PLUGINS_DIR/first/bin"
160+
161+
assert_scope_values_equal 'ROOT LEVEL' "${EXPECTED_ROOT_SCOPE_VALUES[@]}"
162+
}
163+
164+
@test "$SUITE: first-level plugin script invoking sibling first-level plugin" {
165+
run "$TEST_GO_SCRIPT" second first
166+
assert_success
167+
168+
# Since `second` is invoking its sibling `first`, its plugin directory
169+
# shouldn't leak into the environment of `first`.
170+
assert_scope_values_equal 'FIRST PLUGIN' \
171+
'_GO_ROOTDIR:' \
172+
"$TEST_GO_PLUGINS_DIR/first" \
173+
'_GO_SCRIPTS_DIR:' \
174+
"$TEST_GO_PLUGINS_DIR/first/bin" \
175+
'_GO_PLUGINS_PATHS:' \
176+
"$TEST_GO_PLUGINS_DIR/first/bin" \
177+
"$TEST_GO_PLUGINS_DIR/second/bin" \
178+
'_GO_SEARCH_PATHS:' \
179+
"$_GO_CORE_DIR/libexec" \
180+
"$TEST_GO_PLUGINS_DIR/first/bin" \
181+
"$TEST_GO_PLUGINS_DIR/second/bin"
182+
183+
# `second`'s plugin environment should be the same as it ever was.
184+
assert_scope_values_equal 'SECOND PLUGIN' \
145185
'_GO_ROOTDIR:' \
146186
"$TEST_GO_PLUGINS_DIR/second" \
147187
'_GO_SCRIPTS_DIR:' \

0 commit comments

Comments
 (0)