feat(config): add cobra-probe-enabled toggle option and safeguard cobra probing (#133)

currently, every command that doesn't have a completion spec is assumed
to be a cobra cli and it gets ran with a `__complete` argument. for non
cobra clis this can cause problems if those non-cobra clis have
sideeffects even when ran with the `__complete` argument, and there is
no way to disable this.
one of these problems was solved at #111, but a full solution would
probably require actual sandboxing which is i think is an overkill just
for attempting to get suggestions for unrecognized commands. instead of
sandboxing, this pr adds a config option for allowing only selected
commands to be probed and disallowing the rest.
currently the default is still allowing all (`["*"]`) so default
behavior is the same, but it might be better to just have a long list of
known cobra clis as the default instead.

here's a demo of a side effect caused by the probing, in this case
"deleting" a file with rmtrash without actually trying to run the
command:

https://github.com/user-attachments/assets/5256d363-5291-42e8-bb4f-cf7467927246

---------

Co-authored-by: shemishtamesh <shemishtamail@gmail.com>
This commit is contained in:
shemishtamesh
2026-08-11 19:24:47 +07:00
committed by GitHub
co-authored by shemishtamesh
parent 0f173f78dc
commit 89fcd6f830
8 changed files with 103 additions and 18 deletions
+31
View File
@@ -2,6 +2,7 @@ package spec
import (
"context"
"debug/buildinfo"
"os"
"os/exec"
"strconv"
@@ -9,8 +10,12 @@ import (
"sync"
"syscall"
"time"
"github.com/versenilvis/iris/internal/config"
)
const cobraModulePath = "github.com/spf13/cobra"
type cobraCacheEntry struct {
suggestions []Suggestion
}
@@ -91,6 +96,25 @@ func buildCobraCacheKey(binKey string, args []string, partial string) string {
return sb.String()
}
// isLikelyCobraBinary reports whether binName is a Go binary linking Cobra.
// only does static analysis so can produce false negatives and positives.
func isLikelyCobraBinary(binName string) bool {
path, err := exec.LookPath(binName)
if err != nil {
return false
}
info, err := buildinfo.ReadFile(path)
if err != nil {
return false
}
for _, dep := range info.Deps {
if dep.Path == cobraModulePath {
return true
}
}
return false
}
// newProbeCmd builds and isolates the `__complete` probe command.
// starts the child in its own session so it has no controlling terminal
// and therefore won't affect the user's tty in the case of programs that
@@ -108,6 +132,9 @@ func QueryCobraComplete(binName string, args []string, partial string) []Suggest
if strings.ContainsAny(binName, `/\`) {
return nil
}
if !config.Get().Core.CobraProbeEnabled {
return nil
}
binKey := cobraBinKey(binName)
argKey := buildCobraCacheKey(binKey, args, partial)
@@ -119,6 +146,10 @@ func QueryCobraComplete(binName string, args []string, partial string) []Suggest
}
cobraCacheMu.Unlock()
if !isLikelyCobraBinary(binName) {
return nil
}
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond)
defer cancel()