diff --git a/.github/workflows/pal-ci.yml b/.github/workflows/pal-ci.yml new file mode 100644 index 0000000..b1307e2 --- /dev/null +++ b/.github/workflows/pal-ci.yml @@ -0,0 +1,48 @@ +name: pal +on: + push: + branches: + - main + - develop + pull_request: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set Up Golang + uses: actions/setup-go@v5 + with: + go-version: ">=1.23.2" + + - name: Run Linters + run: | + make install-linters + echo + make lint + + - name: Build pal Binary + run: | + make linux + + - name: Build pal Docker Container + run: | + make certs + echo + make docker + + - name: Run Tests Using pal Docker Container + run: | + make test + + - name: Run Vulnerability Scanner On Filesystem + uses: aquasecurity/trivy-action@0.20.0 + with: + scan-type: "fs" + scan-ref: "." + severity: HIGH,CRITICAL diff --git a/Makefile b/Makefile index 804a085..95be551 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,11 @@ run: test: ./test/test.sh +install-linters: + go install -u honnef.co/go/tools/cmd/staticcheck@2024.1.1 + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.61.0 + curl -sfL https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.21.4 + update-deps: go get -u ./... go mod tidy @@ -42,7 +47,7 @@ update-deps: certs: openssl req -x509 -newkey rsa:4096 -nodes -keyout localhost.key -out localhost.pem -days 365 -sha256 -subj '/CN=localhost' -addext 'subjectAltName=IP:127.0.0.1' -docker: #linux certs +docker: sudo docker build -t pal:latest . sudo docker rm -f pal || true mkdir -p ./pal.db diff --git a/README.md b/README.md index 22261d7..57418d9 100644 --- a/README.md +++ b/README.md @@ -103,9 +103,9 @@ deploy: # Set command timeout in seconds (default: 600 seconds/10 mins) timeout: 600 # Set custom HTTP Response Headers - resp_headers: + headers: - header: - - value: + value: # Validate input provided to run, valid options can be found here https://github.com/go-playground/validator?tab=readme-ov-file#baked-in-validations input_validate: required on_error: diff --git a/config/config.go b/config/config.go index e9b07c2..3e9f9d7 100644 --- a/config/config.go +++ b/config/config.go @@ -77,7 +77,7 @@ func InitConfig(location string) error { configMap.Set("http_auth_header", config.HTTP.AuthHeader) configMap.Set("db_path", config.DB.Path) configMap.Set("db_encrypt_key", config.DB.EncryptKey) - configMap.Set("db_resp_headers", config.DB.ResponseHeaders) + configMap.Set("db_headers", config.DB.ResponseHeaders) // Set default value for notifications.max to 100 if config.Notifications.Max == 0 { configMap.Set("notifications_max", 100) @@ -121,7 +121,7 @@ func GetConfigInt(key string) int { } func GetConfigResponseHeaders() []data.ResponseHeaders { - val, _ := configMap.Get("db_resp_headers") + val, _ := configMap.Get("db_headers") return val.([]data.ResponseHeaders) } diff --git a/data/data.go b/data/data.go index 9380817..5dcf697 100644 --- a/data/data.go +++ b/data/data.go @@ -25,7 +25,7 @@ type ActionData struct { Output bool `yaml:"output" json:"output" validate:"boolean"` Timeout int `yaml:"timeout" json:"timeout" validate:"number"` Cmd string `yaml:"cmd" json:"cmd" validate:"required"` - ResponseHeaders []ResponseHeaders `yaml:"resp_headers" json:"resp_headers"` + ResponseHeaders []ResponseHeaders `yaml:"headers" json:"headers"` Cron string `yaml:"cron" json:"cron"` OnError OnError `yaml:"on_error" json:"on_error"` InputValidate string `yaml:"input_validate" json:"input_validate"` @@ -65,7 +65,7 @@ type Config struct { } `yaml:"http"` DB struct { EncryptKey string `yaml:"encrypt_key" validate:"gte=16"` - ResponseHeaders []ResponseHeaders `yaml:"resp_headers"` + ResponseHeaders []ResponseHeaders `yaml:"headers"` Path string `yaml:"path" validate:"dir"` } `yaml:"db"` Notifications struct { diff --git a/go.mod b/go.mod index 3b93a3b..e7a26c1 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/dgraph-io/ristretto v1.0.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.5 // indirect + github.com/gabriel-vasile/mimetype v1.4.6 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect diff --git a/go.sum b/go.sum index b1b3ed4..fbe03db 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= -github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4= +github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc= +github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc= github.com/go-co-op/gocron/v2 v2.12.1 h1:dCIIBFbzhWKdgXeEifBjHPzgQ1hoWhjS4289Hjjy1uw= github.com/go-co-op/gocron/v2 v2.12.1/go.mod h1:xY7bJxGazKam1cz04EebrlP4S9q4iWdiAylMGP3jY9w= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= diff --git a/main.go b/main.go index 7e9ccd7..8ed03bd 100644 --- a/main.go +++ b/main.go @@ -210,7 +210,7 @@ Documentation: https://github.com/marshyski/pal filesFuncMap := template.FuncMap{ "fileSize": func(file fs.DirEntry) string { info, _ := file.Info() - return humanize.Bytes(uint64(info.Size())) + return humanize.Bytes(uint64(info.Size())) // #nosec G115 }, "fileModTime": func(file fs.DirEntry) string { info, _ := file.Info() diff --git a/pal.yml b/pal.yml index 5111495..640de35 100644 --- a/pal.yml +++ b/pal.yml @@ -35,7 +35,7 @@ db: # BadgerDB SECRET DO NOT SHARE encrypt_key: "8c755319-fd2a-4a89-b0d9-ae7b8d26" # Optional Response Headers - resp_headers: + headers: - header: Access-Control-Allow-Origin value: "*" # Local path to database file diff --git a/routes/routes.go b/routes/routes.go index 8445c8c..a400009 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -818,7 +818,7 @@ func GetFilesPage(c echo.Context) error { tmpl, err := template.New("files.tmpl").Funcs(template.FuncMap{ "fileSize": func(file fs.DirEntry) string { info, _ := file.Info() - return humanize.Bytes(uint64(info.Size())) + return humanize.Bytes(uint64(info.Size())) // #nosec G115 }, "fileModTime": func(file fs.DirEntry) string { info, _ := file.Info() diff --git a/test/pal-actions.yml b/test/pal-actions.yml index a8a3ebd..ec14ac7 100644 --- a/test/pal-actions.yml +++ b/test/pal-actions.yml @@ -79,7 +79,7 @@ test: background: true concurrent: true output: true - resp_headers: + headers: - header: Access-Control-Allow-Origin value: "*" - header: X-Pal @@ -127,7 +127,7 @@ json: desc: Run jq On Input JSON output: true concurrent: true - resp_headers: + headers: - header: Content-Type value: application/json # input_validate: required,json @@ -141,7 +141,7 @@ json: desc: Run jq On PAL_REQUEST JSON output: true concurrent: true - resp_headers: + headers: - header: Content-Type value: application/json tags: @@ -156,7 +156,7 @@ html: desc: Sets Max-Age Cache Control & Renders HTML Content-Type output: true concurrent: true - resp_headers: + headers: - header: Cache-Control value: max-age=3600 - header: Content-Type diff --git a/test/test.sh b/test/test.sh index c54a7be..79508a3 100755 --- a/test/test.sh +++ b/test/test.sh @@ -11,83 +11,84 @@ else echo "[fail] auth" && exit 1 fi -# unauth -OUT=$(curl -sk -H 'X-Pal-Auth: PaLLy!@#890-' "$URL/test/unauth") +# no_auth +OUT=$(curl -sk -H 'X-Pal-Auth: PaLLy!@#890-' "$URL/test/no_auth") -if [ "$(echo "$OUT" | grep -c "helloworld")" = 1 ]; then - echo "[pass] unauth" +if [ "$(echo "$OUT" | grep -c "no_auth")" = 1 ]; then + echo "[pass] no_auth" else - echo "[fail] unauth" && exit 1 + echo "[fail] no_auth" && exit 1 fi -# emptycmd -OUT=$(curl -sk "$URL/test/emptycmd") +# fail +OUT=$(curl -sk "$URL/test/fail") -if [ "$(echo "$OUT" | grep -c "empty")" = 1 ]; then - echo "[pass] emptycmd" +if [ "$(echo "$OUT" | grep -c "exit")" = 1 ]; then + echo "[pass] fail" else - echo "[fail] emptycmd" && exit 1 + echo "[fail] fail" && exit 1 fi # block curl -sk $URL/test/block 1>/dev/null & sleep 1 -OUT=$(curl -sk "$URL/test/block") +OUT=$(curl -sk "$URL/test/block?input=1") -if [ "$(echo "$OUT" | grep -c "ready")" = 1 ]; then +if [ "$(echo "$OUT" | grep -c "block")" = 1 ]; then echo "[pass] block" else echo "[fail] block" && exit 1 fi -# noblock -curl -sk $URL/test/noblock 1>/dev/null & +# no_block +curl -sk $URL/test/no_block 1>/dev/null & sleep 1 -OUT=$(curl -sk "$URL/test/noblock") +OUT=$(curl -sk "$URL/test/no_block") -if [ "$(echo "$OUT" | grep -c "noblock")" = 1 ]; then - echo "[pass] noblock" +if [ "$(echo "$OUT" | grep -c "no_block")" = 1 ]; then + echo "[pass] no_block" else - echo "[fail] noblock" && exit 1 + echo "[fail] no_block" && exit 1 fi -# norc -OUT=$(curl -sk "$URL/test/norc") +# no_output +OUT=$(curl -sk "$URL/test/no_output") if [ "$(echo "$OUT" | grep -c "done")" = 1 ]; then - echo "[pass] norc" + echo "[pass] no_output" else - echo "[fail] norc" && exit 1 + echo "[fail] no_output" && exit 1 fi -# json -OUT=$(curl -sk "$URL/json/newres?input=%7B%22hello%22%3A%22world%22%7D") -if [ "$(echo "$OUT" | grep -c "hello")" = 1 ]; then - echo "[pass] json" +# input_validate +OUT=$(curl -sk "$URL/test/input_validate?input=123") + +if [ "$(echo "$OUT" | grep -c "input_validate")" = 1 ]; then + echo "[pass] input_validate" else - echo "[fail] json" && exit 1 + echo "[fail] input_validate" && exit 1 fi -# invalidaction -OUT=$(curl -sk "$URL/test2/invalidaction") -if [ "$(echo "$OUT" | grep -c "invalid")" = 1 ]; then - echo "[pass] invalidaction" +# json/parse +OUT=$(curl -sk "$URL/json/parse?input=%7B%22hello%22%3A%22world%22%7D") +if [ "$(echo "$OUT" | grep -c "hello")" = 1 ]; then + echo "[pass] json/parse" else - echo "[fail] invalidaction" && exit 1 + echo "[fail] json/parse" && exit 1 fi -# emptyaction -OUT=$(curl -sk "$URL/test2/") -if [ "$(echo "$OUT" | grep -c "empty")" = 1 ]; then - echo "[pass] emptyaction" +# invalid +OUT=$(curl -sk "$URL/test/invalid") +if [ "$(echo "$OUT" | grep -c "invalid")" = 1 ]; then + echo "[pass] invalid" else - echo "[fail] emptyaction" && exit 1 + echo "[fail] invalid" && exit 1 fi -# contenttype -OUT=$(curl -sk -o /dev/null -w '%{content_type}' "$URL/test2/contenttype") +# html/index_cache +OUT=$(curl -sk -o /dev/null -w '%{content_type}' "$URL/html/index_cache") if [ "$(echo "$OUT" | grep -c 'text/html')" = 1 ]; then - echo "[pass] contenttype" + echo "[pass] html/index_cache" else - echo "[fail] contenttype" && exit 1 + echo "[fail] html/index_cache" && exit 1 fi