diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..c43a22c --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,53 @@ +name: "CodeQL Advanced" + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + schedule: + - cron: "28 19 * * 0" + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + security-events: write + packages: read + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: go + build-mode: autobuild + - language: javascript-typescript + build-mode: none + + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Initialize CodeQL + uses: github/codeql-action/init@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: CodeQL Analysis + uses: github/codeql-action/analyze@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 4d8ce78..e026d9a 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -10,7 +10,7 @@ permissions: read-all jobs: analysis: - name: Scorecard analysis + name: Scorecard Analysis runs-on: ubuntu-latest permissions: security-events: write @@ -18,25 +18,25 @@ jobs: steps: - name: Checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - name: Analysis - uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 + uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 with: results_file: results.sarif results_format: sarif publish_results: true - name: Upload - uses: actions/upload-artifact@97a0fba1372883ab732affbe8f94b823f91727db # v3.pre.node20 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: SARIF file path: results.sarif retention-days: 5 - name: CodeQL - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0 with: sarif_file: results.sarif diff --git a/.github/workflows/pal-ci.yml b/.github/workflows/pal-ci.yml index 0d12572..ce976cf 100644 --- a/.github/workflows/pal-ci.yml +++ b/.github/workflows/pal-ci.yml @@ -5,7 +5,9 @@ on: - main - develop pull_request: - branches: [main] + branches: ["main"] + +permissions: read-all jobs: build-test-scan: @@ -16,10 +18,10 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set Up Golang - uses: actions/setup-go@v5 + uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0 with: go-version: ">=1.23.3" @@ -47,7 +49,7 @@ jobs: rm -f ./localhost.* - name: Run Vulnerability Scanner On Filesystem - uses: aquasecurity/trivy-action@0.20.0 + uses: aquasecurity/trivy-action@18f2510ee396bbf400402947b394f2dd8c87dbb0 # v0.29.0 with: scan-type: "fs" scan-ref: "." diff --git a/Dockerfile b/Dockerfile index 05ac00a..6db992f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:stable-slim +FROM debian:stable-slim@sha256:5f21ebd358442f40099c997a3f4db906a7b1bd872249e67559f55de654b55d3b COPY ./pal /pal/ COPY ./entrypoint.sh ./localhost.key ./localhost.pem /etc/pal/ diff --git a/Dockerfile-alpine b/Dockerfile-alpine index 8f714e3..4475403 100644 --- a/Dockerfile-alpine +++ b/Dockerfile-alpine @@ -1,4 +1,4 @@ -FROM alpine:latest +FROM alpine:latest@sha256:21dc6063fd678b478f57c0e13f47560d0ea4eeba26dfc947b2a4f81f686b9f45 COPY ./pal /pal/ COPY ./entrypoint.sh ./localhost.key ./localhost.pem /etc/pal/ diff --git a/README.md b/README.md index 12b2b02..f4c4fa7 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ ![ci] ![license] [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/marshyski/pal/badge)](https://scorecard.dev/viewer/?uri=github.com/marshyski/pal) +[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/9863/badge)](https://www.bestpractices.dev/projects/9863) ![OS](https://img.shields.io/badge/OS-linux-0078D4) ![CPU](https://img.shields.io/badge/CPU-x64%2C%20ARM64-0078D4) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..3b8a037 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,47 @@ +# Security Policy + +## Supported Versions + +The pal project is currently under active development. The following table provides information about the status of each release branch: + +| Version | Supported | +| ----------- | ------------------ | +| `v2025.*.*` | :heavy_check_mark: | + +**Note:** We only provide security updates for the current year CalVer releases or the latest release. Older versions/release branches are not actively supported. + +## Reporting a Vulnerability + +**DO NOT CREATE A PUBLIC GITHUB ISSUE FOR SECURITY VULNERABILITIES.** + +Instead, please report any security vulnerabilities directly to GitHub Advisories on the pal GitHub page here: https://github.com/marshyski/pal/security/advisories/new + +**When reporting a vulnerability, please provide the following information:** + +- **Description:** A clear and concise description of the vulnerability. +- **Steps to Reproduce:** Detailed steps to reproduce the vulnerability, including any necessary code snippets, environment setup, or specific inputs. +- **Affected Version(s):** The version(s) of the project affected by the vulnerability (if known). +- **Potential Impact:** An assessment of the potential impact of the vulnerability. +- **Possible Mitigation:** If you have any suggestions for mitigating the vulnerability, please include them. + +**Vulnerability Handling Process:** + +1. **Acknowledgement:** We will acknowledge your report within 48 hours. +2. **Verification:** We will investigate and verify the reported vulnerability. +3. **Fix Development:** If the vulnerability is confirmed, we will develop a fix as soon as possible. +4. **Release:** We will release a new version of the project that includes the fix. +5. **Disclosure:** We will publicly disclose the vulnerability after the fix has been released. We will credit you for the discovery (unless you prefer to remain anonymous). + +**We kindly request that you refrain from publicly disclosing the vulnerability until we have had sufficient time to address it and release a fix.** + +## Security Best Practices + +This project uses Go (Golang) for the backend and JavaScript for the frontend. We strive to follow security best practices for both languages: + +**Go (Golang):** + +- **Dependency Management:** We use Go modules to manage dependencies and keep them up-to-date. Dependabot is used for automated dependency updates. +- **Input Validation:** All user inputs are carefully validated and sanitized to prevent common vulnerabilities like Cross-Site Scripting (XSS), SQL injection, and command injection. +- **Secure Coding Practices:** We adhere to secure coding guidelines for Go, including those outlined in the [OWASP Secure Coding Practices Quick Reference Guide](https://owasp.org/www-project-secure-coding-practices-quick-reference-guide/migrated_content) +- **Error Handling:** Proper error handling is implemented to avoid leaking sensitive information. +- **Cryptography:** We use strong, well-established cryptographic libraries (e.g., Go's `crypto` diff --git a/config/config.go b/config/config.go index 0f00b8e..62b773b 100644 --- a/config/config.go +++ b/config/config.go @@ -245,3 +245,7 @@ func SetActionsDir(dir string) { func SetConfigFile(file string) { configMap.Set("global_config_file", file) } + +func SetVersion(ver string) { + configMap.Set("global_version", ver) +} diff --git a/main.go b/main.go index ed3d292..7cf3924 100644 --- a/main.go +++ b/main.go @@ -127,6 +127,7 @@ Documentation: https://github.com/marshyski/pal config.SetActionsDir(actionsDir) config.SetConfigFile(configFile) + config.SetVersion(version) groups := config.ReadConfig(actionsDir) diff --git a/routes/routes.go b/routes/routes.go index 5967b04..7b601f6 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -25,6 +25,7 @@ import ( "log" "net/http" "os" + "path/filepath" "strconv" "strings" "time" @@ -888,6 +889,7 @@ func GetSystemPage(c echo.Context) error { }{} uiData.Configs = make(map[string]string) uiData.Configs["global_timezone"] = config.GetConfigStr("global_timezone") + uiData.Configs["global_version"] = config.GetConfigStr("global_version") uiData.Configs["global_working_dir"] = config.GetConfigStr("global_working_dir") uiData.Configs["global_debug"] = strconv.FormatBool(config.GetConfigBool("global_debug")) uiData.Configs["global_cmd_prefix"] = config.GetConfigStr("global_cmd_prefix") @@ -1139,7 +1141,14 @@ func GetFilesDownload(c echo.Context) error { if !sessionValid(c) { return c.Redirect(http.StatusSeeOther, "/v1/pal/ui/login") } - return c.File(config.GetConfigUI().UploadDir + "/" + c.Param("file")) + + path := config.GetConfigUI().UploadDir + "/" + c.Param("file") + absPath, err := filepath.Abs(path) + if err != nil || strings.HasPrefix(absPath, config.GetConfigUI().UploadDir) { + return echo.NewHTTPError(http.StatusInternalServerError, "error downloading file from path "+path) + } + + return c.File(absPath) } func GetFavicon(c echo.Context) error { @@ -1166,7 +1175,14 @@ func GetFilesDelete(c echo.Context) error { return c.Redirect(http.StatusSeeOther, "/v1/pal/ui/login") } file := c.Param("file") - err := os.Remove(config.GetConfigUI().UploadDir + "/" + file) + + path := config.GetConfigUI().UploadDir + "/" + file + absPath, err := filepath.Abs(path) + if err != nil || strings.HasPrefix(absPath, config.GetConfigUI().UploadDir) { + return echo.NewHTTPError(http.StatusInternalServerError, "error deleting file "+path) + } + + err = os.Remove(absPath) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "error deleting file "+file) }