Skip to content

Commit

Permalink
Use github artifact attestations for the prebuilt artifacts (#503)
Browse files Browse the repository at this point in the history
* Add optional verification of prebuilt artifacts

Signed-off-by: Jonathan Schwender <[email protected]>

f

Signed-off-by: Jonathan Schwender <[email protected]>
Signed-off-by: Jonathan Schwender <[email protected]>

* CI: Enable strict attestation check in release-check

Signed-off-by: Jonathan Schwender <[email protected]>

* CI: Verify ohos archives in release-check.

Signed-off-by: Jonathan Schwender <[email protected]>

* Disable artifact verification by default

Signed-off-by: Jonathan Schwender <[email protected]>

* Move attestation into function

Signed-off-by: Jonathan Schwender <[email protected]>

* fix ohos archive ci verification

Signed-off-by: Jonathan Schwender <[email protected]>

* Bump version to 128.3-3

Signed-off-by: Jonathan Schwender <[email protected]>

---------

Signed-off-by: Jonathan Schwender <[email protected]>
Signed-off-by: Jonathan Schwender <[email protected]>
  • Loading branch information
jschwe authored Nov 1, 2024
1 parent 1179f5b commit 3a0a5f0
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 2 deletions.
41 changes: 41 additions & 0 deletions .github/workflows/release-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,47 @@ jobs:
cargo test --tests --examples --verbose --features streams
- name: Build from auto-download
if: ${{ env.RELEASE_TAG != '' }}
env:
MOZJS_ATTESTATION: strict
run: |
cargo build --verbose --features streams
cargo test --tests --examples --verbose --features streams
verify-archive-ohos:
name: "Verify archive OpenHarmony"
runs-on: ubuntu-latest
strategy:
matrix:
target: ["aarch64-unknown-linux-ohos", "x86_64-unknown-linux-ohos"]
env:
RELEASE_TAG: ${{ github.event_name == 'release' && github.ref_name || inputs.release-tag }}
steps:
- uses: actions/checkout@v4
- name: Setup OpenHarmony SDK
id: setup_sdk
uses: openharmony-rs/[email protected]
with:
version: "4.1"
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Download prebuilt mozjs from artifact
if: ${{ env.RELEASE_TAG == '' }}
uses: actions/download-artifact@v4
with:
name: libmozjs-${{ matrix.target }}.tar.gz
- name: Build from archive
if: ${{ env.RELEASE_TAG == '' }}
env:
OHOS_SDK_NATIVE: ${{ steps.setup_sdk.outputs.ohos_sdk_native }}
MOZJS_ARCHIVE: libmozjs-${{ matrix.target }}.tar.gz
run: |
./ohos-build cargo build --target="${{ matrix.target }}" --verbose --features streams
- name: Build from auto-download (arch ${{ matrix.target }})
if: ${{ env.RELEASE_TAG != '' }}
env:
OHOS_SDK_NATIVE: ${{ steps.setup_sdk.outputs.ohos_sdk_native }}
MOZJS_ATTESTATION: strict
run: |
./ohos-build cargo build --target="${{ matrix.target }}" --verbose --features "streams"
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ this feature:
archive. The base URL should be similar to `https://github.com/servo/mozjs/releases`.
The build script will append the version and target accordingly. See the files at the example
URL for more details.
- `MOZJS_ATTESTATION` allows uses [Github Attestations] to verify the integrity of the prebuilt archive
and that the archive was built by in CI, for a valid commit on the main branch of the servo/mozjs repo.
Attestation verification requires having a recent version of the github cli tool [gh] installed.
If artifact verification is enabled and reports an error, the prebuilt archive will be discarded and
mozjs will be built from source instead.
Available values are:
- unset (default): Equivalent to `off`.
- `MOZJS_ATTESTATION=<0|false|off>`: Disable artifact verification.
- `MOZJS_ATTESTATION=<1|true|on|lenient>`: Enable artifact verification and fallback to compiling from source if
verification fails or is not possible.
- `MOZJS_ATTESTATION=<2|strict|force>`: Fail the build if artifact verification fails.

[Github Attestations]: https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds
[gh]: https://cli.github.com/

## Building from Source

Expand Down
2 changes: 1 addition & 1 deletion mozjs-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "mozjs_sys"
description = "System crate for the Mozilla SpiderMonkey JavaScript engine."
repository.workspace = true
version = "0.128.3-2"
version = "0.128.3-3"
authors = ["Mozilla"]
links = "mozjs"
build = "build.rs"
Expand Down
114 changes: 113 additions & 1 deletion mozjs-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ use flate2::read::GzDecoder;
use flate2::write::GzEncoder;
use flate2::Compression;
use std::env;
use std::env::VarError;
use std::ffi::{OsStr, OsString};
use std::fs;
use std::fs::File;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str;
use std::sync::LazyLock;
use std::time::Instant;
use tar::Archive;
use walkdir::WalkDir;

Expand Down Expand Up @@ -905,6 +908,106 @@ fn decompress_static_lib(archive: &Path, build_dir: &Path) -> Result<(), std::io
Ok(())
}

static ATTESTATION_AVAILABLE: LazyLock<bool> = LazyLock::new(|| {
Command::new("gh")
.arg("attestation")
.arg("--help")
.output()
.is_ok_and(|output| output.status.success())
});

enum AttestationType {
/// Fallback to compiling from source on failure
Lenient,
/// Abort the build on failure
Strict,
}

enum ArtifactAttestation {
/// Do not verify the attestation artifact.
Disabled,
/// Verify the attestation artifact
Enabled(AttestationType),
}

impl ArtifactAttestation {
const ENV_VAR_NAME: &'static str = "MOZJS_ATTESTATION";

fn from_env_str(value: &str) -> Self {
match value {
"0" | "off" | "false" => ArtifactAttestation::Disabled,
"1" | "on" | "true" | "lenient" => {
ArtifactAttestation::Enabled(AttestationType::Lenient)
}
"2" | "strict" | "force" => ArtifactAttestation::Enabled(AttestationType::Strict),
other => {
println!(
"cargo:warning=`{}` set to unsupported value: {other}",
Self::ENV_VAR_NAME
);
ArtifactAttestation::Enabled(AttestationType::Lenient)
}
}
}

fn from_env() -> Self {
match env::var(Self::ENV_VAR_NAME) {
Ok(value) => {
let lower = value.to_lowercase();
return Self::from_env_str(&lower);
}
Err(VarError::NotPresent) => {}
Err(VarError::NotUnicode(_)) => {
println!(
"cargo:warning={} value must be valid unicode.",
Self::ENV_VAR_NAME
);
}
}
ArtifactAttestation::Disabled
}
}

/// Use GitHub artifact attestation to verify the artifact is not corrupt.
fn attest_artifact(kind: AttestationType, archive_path: &Path) -> Result<(), std::io::Error> {
let start = Instant::now();
if !*ATTESTATION_AVAILABLE {
println!(
"cargo:warning=Artifact attestation enabled, but not available. \
Please refer to the documentation for available values for {}",
ArtifactAttestation::ENV_VAR_NAME
);
}
let mut attestation_cmd = Command::new("gh");
attestation_cmd
.arg("attestation")
.arg("verify")
.arg(&archive_path)
.arg("-R")
.arg("servo/mozjs");

let attestation_duration = start.elapsed();
eprintln!(
"Artifact evaluation took {} ms",
attestation_duration.as_millis()
);

if let Err(output) = attestation_cmd.output() {
println!("cargo:warning=Failed to verify the artifact downloaded from CI: {output:?}");
// Remove the file so the build-script will redownload next time.
let _ = fs::remove_file(&archive_path).inspect_err(|e| {
println!("cargo:warning=Failed to delete archive: {e}");
});
match kind {
AttestationType::Strict => panic!("Artifact verification failed!"),
AttestationType::Lenient => {
return Err(std::io::Error::from(std::io::ErrorKind::InvalidData));
}
}
}
Ok(())
}

/// Download the SpiderMonkey archive with cURL using the provided base URL. If it's None,
/// it will use `servo/mozjs`'s release page as the base URL.
fn download_archive(base: Option<&str>) -> Result<PathBuf, std::io::Error> {
Expand All @@ -913,6 +1016,8 @@ fn download_archive(base: Option<&str>) -> Result<PathBuf, std::io::Error> {
let target = env::var("TARGET").unwrap();
let archive_path = PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("libmozjs.tar.gz");
if !archive_path.exists() {
eprintln!("Trying to download prebuilt mozjs static library from Github Releases");
let curl_start = Instant::now();
if !Command::new("curl")
.arg("-L")
.arg("-f")
Expand All @@ -927,8 +1032,15 @@ fn download_archive(base: Option<&str>) -> Result<PathBuf, std::io::Error> {
{
return Err(std::io::Error::from(std::io::ErrorKind::NotFound));
}
eprintln!(
"Successfully downloaded mozjs archive in {} ms",
curl_start.elapsed().as_millis()
);
let attestation = ArtifactAttestation::from_env();
if let ArtifactAttestation::Enabled(kind) = attestation {
attest_artifact(kind, &archive_path)?;
}
}

Ok(archive_path)
}

Expand Down

0 comments on commit 3a0a5f0

Please sign in to comment.