README ¶
Gosh
Gosh is a secure shell and scripting language. It features a fined-grain permission system and enforces a strong isolation of dependencies.
Gosh is not production ready yet : if you find a bug or want to suggest a feature create an issue please ! Expect breaking changes :).
Have a look at the features section and the wiki !
Installation
You can use the gosh
executable to execute scripts and launch the shell:
go install github.com/gosh-project/gosh/cmd/gosh@0.1.0
Editor support
If you use VSCode you can install the extension of ID gosh.gosh
.
Shell
Launch the REPL with the shell
subcommand:
gosh shell
Before starting the REPL gosh
will execute $HOME/startup.gosh
and grant the required permissions by the script to the shell.
No additional permissions will be granted. You can copy the file named startup.gosh
in this repository and modify it.
Execute commands
ex echo "hello" # 'ex echo hello' will not work
ex go help
NOTE: Almost no commands are allowed by default, edit your startup.gosh
file to allow more commands (and subcommands).
Read, Create, Update, Delete, Provide resources
From now on we will references files, HTTP servers and endpoints as "resources".
You can easily manipulate resources using read | create | update | delete
followed by the resource's name.
Read
Read the entries of a folder: read ./dir/
Read a file: read ./file.txt
Read an HTTP resource with: read https://jsonplaceholder.typicode.com/posts/1
Create
Create a dir: create ./dir/
Create a file: create ./file.txt [optional string content]
Update
Append to a file: update ./file.txt append <string>
Patch an HTTP resource: update <url> <string | object>
Delete
Use delete <resource>
for deletion. The deletion is recursive for folders.
Scripting
# hello.gosh
require {
use: {
globals: "*"
}
}
log "hello world !"
Execute the script with the run
subcommand:
gos run hello.gosh -p=required
Language features
The most important features are described in this section. If you want to learn Gosh or want to know more details about specific features you can go on the wiki.
integer = 1
float = 1.0
if true {
log 1 2
} else {
log(3,4)
}
slice1 = ["a", "b", 3, $integer]
slice2 = [
"a"
"b"
3
$integer
]
object = {name: "Foo"}
Permissions
Required permissions are specified at the top of each module (file).
require {
read: {
# access to all HTTPS hosts (GET, HEAD, QUERY)
: https://*
# access to all paths prefixed with /home/project
: /home/project/...
}
use: {
globals: "*"
}
create: {
globals: ["myvar"]
: https://* # POST to all HTTPS hosts
}
}
Gosh also provides limitations to prevent a script to take all resources. In the example below writing to the filesystem is limited to 100 kB/s.
require {
[...]
limits: {
"fs/write": 100kB/s
}
}
Permissions can also be dropped.
drop-perms {
read: {
: https://*
}
}
Special literals & expressions
# Path literals
/home/user/
./file.json
# Path expressions
/home/user/$dirpath$
# Path patterns support basic globbing (*, [set], ?) and prefixing (not both at the same time).
./data/*.json
/app/logs/...
/app/*/... # invalid (this might be allowed in the future though)
# HTTP host literals
https://example.com
https://example.com:443
# HTTP host pattern literals
https://* # any HTTPS host
https://*.com # any domain with .com as TLD, will not match against subdomains
https://*.example.com # any subdomain of example.com
# URL literals
https://example.com/
https://example.com/index.html
https://localhost/
# URL expressions
https://example.com/users/$id$
@loc/index.html # @loc is a host alias : the expression resolves to https://localhost/index.html if @loc = https://localhost
# URL pattern literals (only prefix patterns supported)
https://example.com/users/...
Quantity literals
10s # time.Duration
10ms # time.Duration
10% # 0.10
sleep 100ms
Imports
Syntax:
import <varname> <url> <file-content-sha256> { <global variables> } allow { <permission> }
Importing a module is like executing a script with the passed globals and granted permissions.
# content of https://example.com/return_1.gosh
return 1
Script:
import modresult https://example.com/return_1.gosh "SG2a/7YNuwBjsD2OI6bM9jZM4gPcOp9W8g51DrQeyt4=" {MY_GLOBVAR: "a"} allow {}
Routines
Routines are mainly used for concurrent work and isolation. Each routine has its own goroutine and state.
Embedded module:
routine = sr {http: $$http} {
return http.get(https://example.com/)!
} allow {
use: {globals: ["http"]}
}
Call syntax (all permissions are inherited).
routine = sr nil f()
Routines can optionally be part of a "routine group" that allows easier control of multiple routines. The group variable is defined (and updated) when the spawn expression is evaluated.
for (1 .. 10) {
sr req_group nil read(https://jsonplaceholder.typicode.com/posts)!
}
results = $req_group.WaitAllResults()!
For more details about the different features you can read the repository's wiki.
Implementation
- Why use a tree walk interpreter instead of a bytecode interpreter ?
-> Tree walk interpreters are slower but simpler : that means less bugs and vulnerabilities. An implementation of Gosh that uses a bytecode interpreter might be developed in the future though.