Skip to content

Commit

Permalink
kitty +open: Ask for permission before executing script files that ar…
Browse files Browse the repository at this point in the history
…e not marked as executable

This prevents accidental execution of script files via MIME type
association from programs that unconditionally "open"
attachments/downloaded files via MIME type associations.
  • Loading branch information
kovidgoyal committed May 7, 2023
1 parent 79c1956 commit 537cabc
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 3 deletions.
3 changes: 3 additions & 0 deletions docs/changelog.rst
Expand Up @@ -54,6 +54,9 @@ Detailed list of changes

- Fix re-using an image id for an animated image for a still image causing a crash (:iss:`6244`)

- kitty +open: Ask for permission before executing script files that are not marked as executable. This prevents accidental execution
of script files via MIME type association from programs that unconditionally "open" attachments/downloaded files via MIME type associations.

0.28.1 [2023-04-21]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
2 changes: 1 addition & 1 deletion kittens/ask/choices.go
Expand Up @@ -60,7 +60,7 @@ func extra_for(width, screen_width int) int {
return utils.Max(0, screen_width-width)/2 + 1
}

func choices(o *Options) (response string, err error) {
func GetChoices(o *Options) (response string, err error) {
response = ""
lp, err := loop.New()
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion kittens/ask/main.go
Expand Up @@ -33,7 +33,7 @@ func main(_ *cli.Command, o *Options, args []string) (rc int, err error) {
}
switch o.Type {
case "yesno", "choices":
result.Response, err = choices(o)
result.Response, err = GetChoices(o)
if err != nil {
return 1, err
}
Expand Down
3 changes: 2 additions & 1 deletion kitty/entry_points.py
Expand Up @@ -94,6 +94,7 @@ def edit(args: List[str]) -> None:


def shebang(args: List[str]) -> None:
from kitty.constants import kitten_exe
script_path = args[1]
cmd = args[2:]
if cmd == ['__ext__']:
Expand All @@ -111,7 +112,7 @@ def shebang(args: List[str]) -> None:
cmd = line.split(' ')
else:
cmd = line.split(' ', maxsplit=1)
os.execvp(cmd[0], cmd + [script_path])
os.execvp(kitten_exe(), ['kitten', '__confirm_and_run_shebang__'] + cmd + [script_path])


def run_kitten(args: List[str]) -> None:
Expand Down
48 changes: 48 additions & 0 deletions tools/cmd/tool/confirm_and_run_shebang.go
@@ -0,0 +1,48 @@
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>

package tool

import (
"fmt"
"os"

"golang.org/x/sys/unix"

"kitty/kittens/ask"
"kitty/tools/cli/markup"
"kitty/tools/utils"
)

var _ = fmt.Print

func ask_for_permission(script_path string) (allowed bool, err error) {
opts := &ask.Options{Type: "yesno", Default: "n"}

ctx := markup.New(true)
opts.Message = ctx.Prettify(fmt.Sprintf(
"Attempting to execute the script: :yellow:`%s`\nExecuting untrusted scripts can be dangerous. Proceed anyway?", script_path))
response, err := ask.GetChoices(opts)
return response == "y", err
}

func confirm_and_run_shebang(args []string) (rc int, err error) {
script_path := args[len(args)-1]
if unix.Access(script_path, unix.X_OK) != nil {
allowed, err := ask_for_permission(script_path)
if err != nil {
return 1, err
}
if !allowed {
return 1, fmt.Errorf("Execution permission refused by user")
}
}
exe := utils.FindExe(args[0])
if exe == "" {
return 1, fmt.Errorf("Failed to find the script interpreter: %s", args[0])
}
err = unix.Exec(exe, args, os.Environ())
if err != nil {
rc = 1
}
return
}
9 changes: 9 additions & 0 deletions tools/cmd/tool/main.go
Expand Up @@ -67,4 +67,13 @@ func KittyToolEntryPoints(root *cli.Command) {
return
},
})
// __confirm_and_run_shebang__
root.AddSubCommand(&cli.Command{
Name: "__confirm_and_run_shebang__",
Hidden: true,
OnlyArgsAllowed: true,
Run: func(cmd *cli.Command, args []string) (rc int, err error) {
return confirm_and_run_shebang(args)
},
})
}

0 comments on commit 537cabc

Please sign in to comment.