{"id":13531852,"url":"https://github.com/spectralops/preflight","last_synced_at":"2025-07-15T10:33:23.030Z","repository":{"id":48354523,"uuid":"362780817","full_name":"SpectralOps/preflight","owner":"SpectralOps","description":"preflight helps you verify scripts and executables to mitigate chain of supply attacks such as the recent Codecov hack.","archived":false,"fork":false,"pushed_at":"2022-11-27T08:52:15.000Z","size":3478,"stargazers_count":153,"open_issues_count":0,"forks_count":45,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-11-02T18:41:40.553Z","etag":null,"topics":["devops","devsecops","golang","security"],"latest_commit_sha":null,"homepage":"https://github.com/spectralops/preflight","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SpectralOps.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-04-29T10:37:33.000Z","updated_at":"2024-09-16T13:36:48.000Z","dependencies_parsed_at":"2022-09-10T06:51:22.282Z","dependency_job_id":null,"html_url":"https://github.com/SpectralOps/preflight","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SpectralOps%2Fpreflight","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SpectralOps%2Fpreflight/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SpectralOps%2Fpreflight/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SpectralOps%2Fpreflight/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SpectralOps","download_url":"https://codeload.github.com/SpectralOps/preflight/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226033251,"owners_count":17563126,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["devops","devsecops","golang","security"],"created_at":"2024-08-01T07:01:06.275Z","updated_at":"2024-11-23T11:13:47.217Z","avatar_url":"https://github.com/SpectralOps.png","language":"Go","readme":"\u003cp align=\"center\"\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n   \u003cimg src=\"media/preflight-logo.png\" width=\"330\"/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n\u003cb\u003e:bomb: Mitigate chain of supply attacks\u003c/b\u003e\n\u003cbr/\u003e\n\u003cb\u003e:ok_hand: Verify your curl scripts and executables\u003c/b\u003e\n\u003cbr/\u003e\n\u003chr/\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/spectralops/preflight/actions/workflows/ci.yml/badge.svg\"/\u003e\n\n\u003cbr/\u003e\n\u003cimg src=\"media/pf-intro-long.gif\"/\u003e\n\u003c/p\u003e\n\n# :thinking: What is it?\n`preflight` helps you verify scripts and executables to mitigate chain of supply attacks such as the recent [Codecov hack](https://spectralops.io/blog/credentials-risk-supply-chain-lessons-from-the-codecov-breach/).\n# :gift: Getting Preflight\n\nFirst of all, it's the chicken and the egg. How do you pull a legit `preflight` binary from us without verifying it with `preflight`? having that `preflight` is solving this exact problem?\n\nThe best way, is that you grab the source, compile it yourself, and use your own binary which you put in a place _that you trust_. People usually have several options of how to do that safely:\n\n* Put it on your own S3 bucket\n* Drop it on your own [Artifactory](https://jfrog.com/artifactory/) or similar\n* Push it directly into your repos (it should be as small as 4mb, and almost never change so, Git should work nicely with it)\n* Build from source into your containers directly:\n\n```Dockerfile\nFROM golang:1.16-alpine AS preflight_builder\nRUN apk add --no-cache git\nWORKDIR /builds\nRUN GOBIN=`pwd` go get -u github.com/spectralops/preflight\n\n# Build from a bare image, copy built binary\nFROM alpine:3.9 \nRUN apk add ca-certificates\nCOPY --from=preflight_builder /builds/preflight /usr/local/bin\n\n# use preflight as you wish\nRUN curl https://.. | preflight run \u003cdigest\u003e\n```\n\n\n\nIf you want to just get started quickly on your workstation, you can [download a release](https://github.com/spectralops/preflight/releases) or install `preflight` with homebrew:\n\n```bash\n$ brew tap spectralops/tap \u0026\u0026 brew install preflight\n````\n\n\n# :rocket: Quick Run\n\n\n**Someone changed the script or binary you're running. Abort!**\n\n```bash\n$ curl -L https://XXX | preflight run sha256=1ce...2244a6e86\n⌛️ Preflight starting\n❌ Preflight failed:\nDigest does not match.\n\nExpected:\n\u003c...\u003e\n\nActual: \n\u003c...\u003e\n  \n   Information:\n   It is recommended to inspect the modified file contents.\n```\n\n**A hash is verified, but it is actually vulnerable. Abort!**\n\n```bash\n$ curl -L https://XXX | preflight run sha256=1ce...2244a6e86\n⌛️ Preflight starting using file lookup: malshare.current.sha256.txt\n❌ Preflight failed: Digest matches but marked as vulnerable.\n   Digest matches but marked as vulnerable.\n\nInformation:\n  Vulnerability: Hash was found in a vulnerable digest list\n  More: malshare.current.sha256.txt\n```\n\nAll ok, let's fly.\n\n```bash\n$ curl -L https://XXX | preflight run sha256=1ce...2244a6e86\n⌛️ Preflight starting\n✅ Preflight verified\n\n... actual script output ...\n\n```\n\n# :sparkles:  Examples\n\n## :octocat: Github action\n\nYou can install Preflight with a [Github action](https://github.com/marketplace/actions/setup-preflight), or use it like this:\n\n```yaml\n   - name: Setup Preflight\n     uses: spectralops/setup-preflight@v1\n```\n\nAnd now, you have a `preflight` binary to play with.\n## :golf: Running codecov safely in your CI\n\nFirst, let's create a hash (before creating it, review the script manually and see that it's not doing anything funny):\n\n```bash\n$ curl -s https://codecov.io/bash | ./preflight create\nsha256=d6aa3207c4908d123bd8af62ec0538e3f2b9f257c3de62fad4e29cd3b59b41d9\n```\n\nNow, we're going to take\n\n```\nsha256=d6aa3207c4908d123bd8af62ec0538e3f2b9f257c3de62fad4e29cd3b59b41d9\n```\n\nAnd use this to secure our pulls from Codecov. In this case, `preflight` is checked safely into your repo under `ci/preflight`.\n\n----\nBEFORE (insecure):\n\n\n```yaml\nsteps:\n   - curl -s https://codecov.io/bash | sh\n```\n\nAFTER (safe, yay!):\n\n```yaml\nsteps:\n   - curl -s https://codecov.io/bash | ./ci/preflight run sha256=d6aa3207c4908d123bd8af62ec0538e3f2b9f257c3de62fad4e29cd3b59b41d9\n```\n\n----\n\n## :golf: Building Docker images in a secure way\n\nIt's recommended to use `preflight` when you're building Docker images, and are installing via `curl | sh` scripts that vendors give you.\n\nBefore:\n\n```Dockerfile\nFROM alpine:3.9 \nRUN apk add ca-certificates\nRUN apk add curl coreutils\n\nRUN cd /opt \u0026\u0026 curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.7.2 sh\n```\n\nAfter (securely building preflight from source + validating Istio with preflight):\n\n```Dockerfile\nFROM golang:1.16-alpine AS preflight_builder\n\nRUN apk add --no-cache git\nWORKDIR /builds\n\nRUN GOBIN=`pwd` go get -u github.com/spectralops/preflight\n\n# Build from a bare image, copy built binary\nFROM alpine:3.9 \nRUN apk add ca-certificates\nRUN apk add curl coreutils\n\nCOPY --from=preflight_builder /builds/preflight /usr/local/bin\n\n# create a hash with:\n# curl -L https://istio.io/downloadIstio | preflight create\nRUN cd /opt \u0026\u0026 curl -L https://istio.io/downloadIstio | \\\n    ISTIO_VERSION=1.7.2 \\\n    preflight run sha256=e826fb57c6705cca0b6464edf4c1701d4bd5fd5879f5820fca78941c0a83ce64\n```\n\nUsing `preflight` we're also getting a nice confirmation badge during the build process:\n\n\n```\n... docker build log...\n\n⌛️ Preflight starting\n✅ Preflight verified\n\nDownloading istio-1.7.2 from https://github.com/istio/istio/releases/download/1.7.2/istio-1.7.2-linux-amd64.tar.gz ...\n\nIstio 1.7.2 Download Complete!\n```\n\n\n\n## :bulb: Dealing with changing runnables \u0026 auto updates\n\nWhen updating an old binary or script to a new updated version, there will be at least two (2) valid digests \"live\" and just replacing the single digest used will fail for the older runnable which may still be running somewhere.\n\n```bash\n$ preflight \u003chash list|https://url/to/hash-list\u003e\n```\n\nTo support updates and rolling/auto updates of scripts and binaries we basically need to validate against `\u003cold hash\u003e` + `\u003cnew hash\u003e` at all times, until everyone upgrades to the new script. Preflight validates against a `list of hashes` or better, give it a _live_ URL of `valid hashes` and it will validate against it.\n\n\n```bash\ncurl .. | ./ci/preflight run sha256=d6aa3207c4908d123bd8af62ec0538e3f2b9f257c3de62fad4e29cd3b59b41d9,sha256=\u003cnew hash\u003e,...\n```\n\nOr to a live URL:\n```bash\ncurl .. | ./ci/preflight run https://dl.example.com/hashes.txt\n```\n\n\nUse this when:\n\n* Use multiple digests verbatim, when your runnables change often, but not too often\n* Use a URL when your runnables change often. Remember to follow the chain of trust. This will now mean that:\n  * Your hash list URL is now a source of trust\n  * Visually: we're swapping the chain of trust like so `curl \u003cforeign trust\u003e | ./ci/preflight \u003cown trust\u003e`\n\n## :running: Running scripts and binaries\n\n**Piping:**\n\n```bash\n$ curl -s https://example.com/some-script | preflight run sha256=d6aa3207c4\u003c...\u003eb4\n```\n\n**Executables:**\n\n```bash\n$ preflight run sha256=d6aa3207c4\u003c...\u003eb4 ./my-script.sh\n```\n## :mag_right: Checking scripts and binaries\n\n**Piping:**\n\n```bash\n$ curl -s https://example.com/some-script | preflight check sha256=d6aa3207c4\u003c...\u003eb4 | sh\n```\n\nNot that `preflight check` is built in a way that you could continue to pipe to the next process:\n\n* If a check passes, the checked script or binary content will be dumped to `STDOUT`\n* If a check fails, you'll get an `exit(1)`, and an error message\n\n**Executables:**\n\n```bash\n$ preflight check sha256=d6aa3207c4\u003c...\u003eb4 ./my-script.sh\n```\n\nIn this case:\n\n* If a check passes, you'll get an `exit(0)` and no output (so you can compose with other tooling)\n* If a check fails, you'll get an `exit(1)` and an error message\n\n\n\n\n## :round_pushpin: Creating new hashes\n\nYou can easily create new hashes with `preflight`. The default is a SHA256 hash, but you could also create a `sha256`, `sha1`, and `md5` hash.\n\n\n```bash\n$ preflight create test.sh\nsha256=fe6d02cf15642ff8d5f61cad6d636a62fd46a5e5a49c06733fece838f5fa9d85\n```\n\nThough not recommended, you can create other kinds (weaker kinds) of hashes for legacy/compatibility reasons:\n\n```bash\n$ preflight create test.sh --digest md5\nmd5=cb62874fea06458b2b0cabf2322c9d55\n```\n\n# :see_no_evil: Using optional malware lookup\n\n`preflight` comes with lookup providers, which is optional -- you can enable them by using environment variables:\n\n## File Lookup\n\nYou can download a daily list of malware signatures from [malshare.com](https://www.malshare.com) or any equivalent service. Here is a [direct link to such a list](https://www.malshare.com/daily/malshare.current.sha256.txt).\n\nThen:\n\n* Set `PF_FILE_LOOKUP=./path/to/text/file`\n\nWith this configured `preflight` will search for all digest types in this file before approving.\n\n\nHere is a full example for your CI, combining `preflight` with Malshare:\n\n```yaml\nenv:\n   PF_FILE_LOOKUP: malshare.current.sha256.txt\n\nsteps: \n- wget https://www.malshare.com/daily/malshare.current.sha256.txt\n- curl https://... | preflight \u003csha\u003e\n```\n\n**Result:**\n\n```bash\n$ PF_FILE_LOOKUP=malshare.current.sha256.txt preflight run fe6d02cf15642ff8d5f61cad6d636a62fd46a5e5a49c06733fece838f5fa9d85 test.sh\n⌛️ Preflight starting using file lookup: malshare.current.sha256.txt\n❌ Preflight failed: Digest matches but marked as vulnerable.\n\nInformation:\n  Vulnerability: Hash was found in a vulnerable digest list\n  More: malshare.current.sha256.txt\n\n```\n## VirusTotal Lookup\n\nYou can use the [virus total community]() API access to lookup your hashes.\n\n\n\n* Set `PF_VT_TOKEN=your-virustotal-api-key`\n\nWith this configured `preflight` will automatically create the VirusTotal lookup provider and validate digest with it.\n\n\nHere is a full example for your CI, combining `preflight` with VirusTotal:\n\n```yaml\nenv:\n   PF_VT_TOKEN: {{secrets.PF_VT_TOKEN}}\n\nsteps: \n- curl https://... | preflight \u003csha\u003e\n```\n\n\n**Result:**\n\n```bash\n$ PF_VT_TOKEN=xxx preflight check e86d4eb1e888bd625389f2e50644be67a6bdbd77ff3bceaaf182d45860b88d80 kx-leecher.exe\n⌛️ Preflight starting using VirusTotal\n❌ Preflight failed: Digest matches but marked as vulnerable.\n\nInformation:\n  Vulnerability: VirusTotal stats - malicious: 40, suspicious 0\n  More: https://www.virustotal.com/gui/file/e86d4eb1e888bd625389f2e50644be67a6bdbd77ff3bceaaf182d45860b88d80/detection\n```\n## Other lookup types?\n\nWe've established that a _file lookup_ is universal and general enough to be useful to everyone. However, you might prefer your own vendor, or a service such as VirusTotal -- `preflight`'s architecture is pluggable and we're accepting [pull requests](https://github.com/spectralops/preflight).\n\n# Thanks\n\nTo all [Contributors](https://github.com/spectralops/preflight/graphs/contributors) - you make this happen, thanks!\n\n\n# Copyright\n\nCopyright (c) 2021 [@jondot](http://twitter.com/jondot). See [LICENSE](LICENSE.txt) for further details.\n","funding_links":[],"categories":["DevOps","Tools","Dependency intelligence"],"sub_categories":["Development","Supply Chain Security"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspectralops%2Fpreflight","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspectralops%2Fpreflight","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspectralops%2Fpreflight/lists"}