Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/WASMTIME_MAINTENANCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Wasmtime maintenance automation

The default executor branch coordinates Wasmtime maintenance for every line in
`wasmtime-maintenance.json`. GitHub schedules only execute from the repository's
default branch, so the workflow reads the configured `vX-dev` and `vX.x` refs
through the GitHub API instead of assuming its own checkout represents every
supported line.

## Ownership

Set the repository Actions variable `WASMTIME_REBASE_OWNER` to the GitHub login
of the person accountable for Wasmtime rebases. Advisory and monthly review
issues are assigned to that login. If the variable is absent, monitoring still
runs and creates issues, but emits an Actions warning and leaves them unassigned.

## Policies

- `monthly`: published-advisory monitoring plus one scheduled rebase review
issue per calendar month.
- `security-only`: published-advisory monitoring without routine upgrade churn.

Both the development and release refs are checked. This distinguishes a line
that is vulnerable everywhere from one that is fixed on `vX-dev` but still
needs promotion to `vX.x`.

## Remediation flow

The automation never creates branches, pushes commits, or merges changes.
Remediation follows the existing manager-linked branch strategy:

1. Create a feature branch from the current manager `vX-dev`.
2. Rebase the affected executor line in its submodule.
3. Push the executor mirror as `pr/<executor-line>/<manager-feature>`.
4. Open the manager PR and its linked executor PR into `<executor-line>-dev`.
5. Run the full executor, upstream Wasmtime, and cross-repository checks.
6. Promote the fixed development line through the standing release gate into
`<executor-line>.x`.

Do not patch a protected development or release branch directly.
11 changes: 11 additions & 0 deletions .github/wasmtime-maintenance.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"schema_version": 1,
"lines": {
"v0.2": {
"policy": "security-only"
},
"v0.3": {
"policy": "monthly"
}
}
}
64 changes: 64 additions & 0 deletions .github/workflows/wasmtime-maintenance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: maintenance / Wasmtime

# This workflow is deliberately a coordinator, not a branch-writing bot.
# GitHub runs schedules from the default branch (currently the latest release
# line), so the script reads every configured vX-dev and vX.x ref through the
# API. Remediation still follows the manager-linked pr/vX/<feature> flow.

on:
schedule:
# Daily, off the hour. Monthly review issues are deduplicated by line/month.
- cron: "17 5 * * *"
workflow_dispatch:
inputs:
dry_run:
description: "Report findings without creating or updating issues"
type: boolean
required: false
default: false
pull_request:
branches: ["v*-dev"]
paths:
- ".git-third-party/config.json"
- ".git-third-party/patches/executor/third-party/wasmtime/**"
- "executor/Cargo.lock"
- ".github/wasmtime-maintenance.json"
- ".github/workflows/wasmtime-maintenance.yml"
- "support/scripts/wasmtime_maintenance.py"

concurrency:
group: wasmtime-maintenance-${{ github.event_name == 'pull_request' && github.event.pull_request.number || 'scheduled' }}
cancel-in-progress: false

defaults:
run:
shell: bash -exo pipefail {0}

jobs:
audit-pr:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Check the proposed Wasmtime pin
run: python3 support/scripts/wasmtime_maintenance.py audit-local

reconcile:
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
issues: write
steps:
- uses: actions/checkout@v4
- name: Reconcile advisories and scheduled reviews
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
WASMTIME_REBASE_OWNER: ${{ vars.WASMTIME_REBASE_OWNER }}
run: >-
python3 support/scripts/wasmtime_maintenance.py reconcile
${{ inputs.dry_run && '--dry-run' || '' }}
137 changes: 137 additions & 0 deletions support/scripts/tests/test_wasmtime_maintenance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import importlib.util
import json
import sys
import tempfile
import unittest
from pathlib import Path


SCRIPT = Path(__file__).parents[1] / 'wasmtime_maintenance.py'
SPEC = importlib.util.spec_from_file_location('wasmtime_maintenance', SCRIPT)
MODULE = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = MODULE
SPEC.loader.exec_module(MODULE)


class MaintenanceTests(unittest.TestCase):
def test_load_lines_sorts_and_preserves_policy(self):
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / 'config.json'
path.write_text(
json.dumps(
{
'schema_version': 1,
'lines': {
'v0.10': {'policy': 'monthly'},
'v0.2': {'policy': 'security-only'},
},
}
)
)
lines = MODULE.load_lines(path)

self.assertEqual([line.name for line in lines], ['v0.2', 'v0.10'])
self.assertEqual(lines[0].refs['development'], 'v0.2-dev')
self.assertEqual(lines[0].refs['release'], 'v0.2.x')
self.assertEqual(lines[1].policy, 'monthly')

def test_parse_pin_reads_custom_commit_and_lock_version(self):
manifest = json.dumps(
{
'repos': {
'executor/third-party/wasmtime': {
'commit': 'a' * 40,
'patches': 6,
}
}
}
)
lock = '''
[[package]]
name = "wasmtime"
version = "42.0.2"

[[package]]
name = "wasmtime-environ"
version = "42.0.2"
'''

pin = MODULE.parse_pin('v0.3', 'development', 'v0.3-dev', manifest, lock)

self.assertEqual(pin.version, '42.0.2')
self.assertEqual(pin.commit, 'a' * 40)
self.assertEqual(pin.ref, 'v0.3-dev')

def test_parse_pin_rejects_ambiguous_versions(self):
manifest = json.dumps(
{
'repos': {
'executor/third-party/wasmtime': {
'commit': 'b' * 40,
}
}
}
)
lock = '''
[[package]]
name = "wasmtime"
version = "41.0.0"

[[package]]
name = "wasmtime"
version = "42.0.2"
'''

with self.assertRaisesRegex(ValueError, 'exactly one'):
MODULE.parse_pin('v0.3', 'development', 'v0.3-dev', manifest, lock)

def test_marker_round_trips(self):
key = 'advisory:v0.3:GHSA-abcd-1234-5678'
body = f'prefix\n{MODULE.marker(key)}\nsuffix'
self.assertEqual(MODULE.marker_key(body), key)

def test_advisory_aliases_prefer_ghsa(self):
advisory = MODULE.advisory_from_osv(
{
'id': 'RUSTSEC-2026-0114',
'aliases': ['CVE-2026-44216', 'GHSA-p8xm-42r7-89xg'],
'summary': 'panic',
}
)

self.assertEqual(advisory.id, 'GHSA-p8xm-42r7-89xg')
self.assertEqual(
advisory.url,
'https://osv.dev/vulnerability/GHSA-p8xm-42r7-89xg',
)

def test_advisories_body_groups_findings_and_names_branch_flow(self):
line = MODULE.Line(name='v0.3', policy='monthly')
pin = MODULE.Pin(
line='v0.3',
channel='development',
ref='v0.3-dev',
version='42.0.2',
commit='c' * 40,
)
advisory = MODULE.Advisory(
id='GHSA-test',
summary='test advisory',
url='https://example.invalid',
)

body = MODULE.advisories_body(
line,
{advisory.id: (advisory, [pin])},
'genvm-owner',
)

self.assertIn('pr/v0.3/<manager-feature>', body)
self.assertIn('v0.3-dev', body)
self.assertIn('v0.3.x', body)
self.assertIn('@genvm-owner', body)
self.assertIn('<!-- wasmtime-maintenance:advisories:v0.3 -->', body)


if __name__ == '__main__':
unittest.main()
Loading