Skip to content
Closed
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
142 changes: 142 additions & 0 deletions .github/workflows/downstream-semgrep.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
name: Downstream Semgrep candidate

on:
pull_request:
paths:
- 'scripts/downstream_semgrep.py'
- 'scripts/verify_downstream_semgrep.py'
- 'ci/downstream*'
- 'tools/downstream_semgrep/**'
- '.github/workflows/downstream-semgrep.yml'
- 'tests/unit/scripts/test_downstream_semgrep.py'
workflow_dispatch:

permissions:
contents: read

jobs:
matrix:
runs-on: ubuntu-24.04
outputs:
cells: ${{ steps.matrix.outputs.cells }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
with:
python-version: '3.13'
- id: matrix
shell: bash
run: |
cells="$(python scripts/verify_downstream_semgrep.py matrix)"
echo "cells=$cells" >> "$GITHUB_OUTPUT"

candidate:
needs: matrix
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.matrix.outputs.cells) }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 60
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
with:
python-version: ${{ matrix.python_version }}
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e
with:
enable-cache: false
version: '0.12.19'
- name: Execute native candidate cell
if: matrix.container_image == ''
shell: bash
env:
CELL_PLATFORM: ${{ matrix.platform }}
CELL_PYTHON: ${{ matrix.python_version }}
run: |
python scripts/verify_downstream_semgrep.py cell --platform "$CELL_PLATFORM" --python-version "$CELL_PYTHON" --output "$RUNNER_TEMP/downstream-cell"
- name: Execute native musl candidate cell
if: matrix.container_image != ''
shell: bash
env:
CELL_PLATFORM: ${{ matrix.platform }}
CELL_PYTHON: ${{ matrix.python_version }}
CELL_IMAGE: ${{ matrix.container_image }}
run: |
case "$CELL_IMAGE" in python:*@sha256:*) ;; *) echo 'Unpinned musl image' >&2; exit 1 ;; esac
mkdir -p "$RUNNER_TEMP/downstream-musl"
status=0
docker run --rm \
--mount "type=bind,src=$GITHUB_WORKSPACE,dst=/workspace,readonly" \
--mount "type=bind,src=$RUNNER_TEMP/downstream-musl,dst=/evidence" \
-e CELL_PLATFORM -e CELL_PYTHON \
-w /workspace "$CELL_IMAGE" sh -euc '
apk add --no-cache git build-base libffi-dev openssl-dev cargo
git config --global --add safe.directory /workspace
python scripts/verify_downstream_semgrep.py uv-requirements > /evidence/uv-bootstrap.txt
python -m pip install --require-hashes --only-binary=:all: --index-url https://pypi.org/simple -r /evidence/uv-bootstrap.txt
python scripts/verify_downstream_semgrep.py cell --platform "$CELL_PLATFORM" --python-version "$CELL_PYTHON" --output /evidence/downstream-cell
' || status=$?
if test -d "$RUNNER_TEMP/downstream-musl/downstream-cell"; then
cp -R "$RUNNER_TEMP/downstream-musl/downstream-cell" "$RUNNER_TEMP/downstream-cell"
fi
exit "$status"
- name: Retain candidate evidence on success or failure
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: downstream-${{ matrix.platform }}-${{ matrix.python_version }}
path: |
${{ runner.temp }}/downstream-cell
!${{ runner.temp }}/downstream-cell/*-env/**
!${{ runner.temp }}/downstream-cell/build-two/**
if-no-files-found: error
retention-days: 14

aggregate:
permissions:
contents: read
actions: read
if: always()
needs: [matrix, candidate]
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
with:
python-version: '3.13'
- name: Download immutable job artifacts
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
shell: bash
run: |
gh run download "$RUN_ID" --pattern 'downstream-*' --dir "$RUNNER_TEMP/downstream-reports"
- name: Require complete successful candidate matrix
env:
MATRIX_RESULT: ${{ needs.matrix.result }}
CANDIDATE_RESULT: ${{ needs.candidate.result }}
shell: bash
run: |
test "$MATRIX_RESULT" = success
test "$CANDIDATE_RESULT" = success
python scripts/verify_downstream_semgrep.py aggregate --reports "$RUNNER_TEMP/downstream-reports"

protected-producer:
if: github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/dev'
needs: aggregate
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Refuse production bootstrap until separately approved roots exist
shell: bash
run: |
echo 'Candidate evidence only. Production producer bootstrap is unconfigured; no signing, attestation or publication is permitted.' >&2
exit 1
4 changes: 4 additions & 0 deletions ci/downstream-semgrep-admissions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"schema_version": 1,
"admissions": []
}
105 changes: 105 additions & 0 deletions ci/downstream-semgrep-recipe.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
{
"schema_version": 1,
"package": "semgrep",
"upstream_version": "1.179.0",
"downstream_version": "1.179.0+specfact.1",
"mcp_from": "1.29.0",
"mcp_to": "1.30.0",
"python_versions": [
"3.11",
"3.12",
"3.13"
],
"build_runtime_policy": {
"implementation": "CPython",
"python_versions": [
"3.11",
"3.12",
"3.13"
],
"wheel_compression": "ZIP_STORED",
"description": "Runtime-policy digest; actual executable and environment identities are bound separately by each cell report."
},
"build_runtime_digest": "31432b8918f5a5aef72fbdb8f5aefe74090f5002917c33e7f4f78478998c0f51",
"wheels": [
{
"filename": "semgrep-1.179.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-macosx_10_14_x86_64.whl",
"platform_tag": "macosx_10_14_x86_64",
"upstream_sha256": "34b5e801e3f4325c93014124ccfab6da96857f74aae68458d2a07defd4727b0f",
"upstream_url": "https://files.pythonhosted.org/packages/85/4b/4cbaa0c3c124ae7d522e688c01f06013038ff59cc818c21dfa44c96380b3/semgrep-1.179.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-macosx_10_14_x86_64.whl",
"runner": "macos-15-intel",
"containers": {}
},
{
"filename": "semgrep-1.179.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-macosx_11_0_arm64.whl",
"platform_tag": "macosx_11_0_arm64",
"upstream_sha256": "9b57026f587a4bc7833bca2b6e4d6778072a79dcf0b9f2e5e01d3d5df39c7759",
"upstream_url": "https://files.pythonhosted.org/packages/bd/77/b573a0faa5a5b0848dc884a0f8595fcb5c550f55fa5f455a233b29a52c0c/semgrep-1.179.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-macosx_11_0_arm64.whl",
"runner": "macos-15",
"containers": {}
},
{
"filename": "semgrep-1.179.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-manylinux_2_34_x86_64.whl",
"platform_tag": "manylinux_2_34_x86_64",
"upstream_sha256": "b529c4db63f293918989b84ce4640f12f1b1e4680d9e7fcab5f3554f9113dfc5",
"upstream_url": "https://files.pythonhosted.org/packages/cc/6c/621944b4843c7484aeaefd5b232d93780bdf95259567c10a9a8129f08dc6/semgrep-1.179.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-manylinux_2_34_x86_64.whl",
"runner": "ubuntu-24.04",
"containers": {}
},
{
"filename": "semgrep-1.179.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-manylinux_2_34_aarch64.whl",
"platform_tag": "manylinux_2_34_aarch64",
"upstream_sha256": "c23d79c4839540372acc39b59aad167c1c7f3dc21bacacb27f19aa2edd79d271",
"upstream_url": "https://files.pythonhosted.org/packages/e3/ba/b9f1afbed852a21b6f7e72c064343161c5ab249954c46db1796b59314290/semgrep-1.179.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-manylinux_2_34_aarch64.whl",
"runner": "ubuntu-24.04-arm",
"containers": {}
},
{
"filename": "semgrep-1.179.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-musllinux_1_2_x86_64.whl",
"platform_tag": "musllinux_1_2_x86_64",
"upstream_sha256": "69bce348296374df6c51e72758801ce06dd18d6dfb307bc73d24b9de53bbe5f1",
"upstream_url": "https://files.pythonhosted.org/packages/ee/c1/59096c02408630b79be19c5b97d145786141e254782ab0e62939c1eea52d/semgrep-1.179.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-musllinux_1_2_x86_64.whl",
"runner": "ubuntu-24.04",
"containers": {
"3.11": "python:3.11-alpine3.22@sha256:a4fc589b32e824f3f02ed9d7e7be19518aa47e105b80416336af9f202275a489",
"3.12": "python:3.12-alpine3.22@sha256:a190708a2dec1bd18b1decb539f8e8f5407abaa9bf39cacda583f7f8c11db322",
"3.13": "python:3.13-alpine3.22@sha256:e81548ac35b07a3bd4805f275107592ef458b1e893c0e04d45aedaa19416cca5"
}
},
{
"filename": "semgrep-1.179.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-musllinux_1_2_aarch64.whl",
"platform_tag": "musllinux_1_2_aarch64",
"upstream_sha256": "dc6317fbfad49b1dbe396df7da7d9702cffc5ea9ea62cb3b4b2209824496b425",
"upstream_url": "https://files.pythonhosted.org/packages/1d/41/dc51c4e2f2cd730c1512739db792f70f7e9c665f6a0fb40fef62ff4b8eb3/semgrep-1.179.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-musllinux_1_2_aarch64.whl",
"runner": "ubuntu-24.04-arm",
"containers": {
"3.11": "python:3.11-alpine3.22@sha256:a4fc589b32e824f3f02ed9d7e7be19518aa47e105b80416336af9f202275a489",
"3.12": "python:3.12-alpine3.22@sha256:a190708a2dec1bd18b1decb539f8e8f5407abaa9bf39cacda583f7f8c11db322",
"3.13": "python:3.13-alpine3.22@sha256:e81548ac35b07a3bd4805f275107592ef458b1e893c0e04d45aedaa19416cca5"
}
},
{
"filename": "semgrep-1.179.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-win_amd64.whl",
"platform_tag": "win_amd64",
"upstream_sha256": "6c6cf104ad0959c0b11de56d15c5d31f56949c620b4bd130c5d1fb899f2bb5a3",
"upstream_url": "https://files.pythonhosted.org/packages/ec/3e/dbf54b028afb807f26dc1e1c94489faac5926a8fa745b41bf505b4c53a7e/semgrep-1.179.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-win_amd64.whl",
"runner": "windows-2022",
"containers": {}
}
],
"uv_bootstrap": {
"version": "0.12.19",
"wheels": [
{
"filename": "uv-0.12.19-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl",
"sha256": "b466eb0f74645883df52446d54474905e8515d75fdf0b313bf099dedc3237896",
"url": "https://files.pythonhosted.org/packages/49/5c/4690490a19726b44d8fd01cdbdc56061fdebf99bb3e466215f276eefbd5d/uv-0.12.19-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl"
},
{
"filename": "uv-0.12.19-py3-none-musllinux_1_1_x86_64.whl",
"sha256": "a6512549e6bf12013190639bd5d9ef780f197d7adf4770a628c5e681577543d8",
"url": "https://files.pythonhosted.org/packages/28/cd/e57952eef1e24d52403a73c25c73d5697791eb72debe4a2facf9006fcba5/uv-0.12.19-py3-none-musllinux_1_1_x86_64.whl"
}
]
}
}
11 changes: 10 additions & 1 deletion openspec/CHANGE_ORDER.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ This document is the **single source of truth for active work** in this
repository. It lists what is in flight, what is paused, and the order in which
active changes should be implemented.

## Downstream Semgrep admission (#772, 2026-10-16)

The owner authorized `downstream-semgrep-admission` as a separate implementation
PR under #747. Build metadata-only downstream wheels with fixed MCP, execute the
seven-platform / three-Python matrix, and require exact protected provenance
before production adoption. Candidate tests do not authorize their own artifacts
or replace the currently blocked frozen audit. Preserve every existing gate,
provider pin and signature boundary. No merge or main promotion is authorized.

## Authorized #747 dependency stack (2026-10-10)

The owner approved `dev <- #744 <- #727 <- #752 <- #754 <- #753`, retaining
Expand Down Expand Up @@ -73,7 +82,7 @@ Recovered from uncommitted proposal-only feature worktrees into local `dev`; rem

| Bucket | Count | Location |
|---|---:|---|
| **Active-tree entries** | 41 | [`openspec/changes/`](changes/) |
| **Active-tree entries** | 47 | [`openspec/changes/`](changes/) |
| **Parking-lot entries** | 21 | [`openspec/parking-lot/`](parking-lot/) |
| **Archived** | 120 | [`openspec/changes/archive/`](changes/archive/) |

Expand Down
47 changes: 47 additions & 0 deletions openspec/changes/downstream-semgrep-admission/REVIEW_EXCEPTIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Preinstallation review exceptions

The unchanged SpecFact review reports nine `MISSING_ICONTRACT` warnings and one
`kiss.parameter-count.warning`. These are explicit, scoped exceptions under
`docs/agent-rules/50-quality-gates-and-review.md`. The review scorer, thresholds,
configuration and contract checker remain unchanged.

## Standard-library preinstallation boundary

The nine affected entry points are `build_candidate`, `verify_candidate`,
`required_matrix`, `aggregate_candidate_reports`, `admit_artifact`,
`CandidateWheel.candidate_audit_input`, `AdmittedWheel.audit_input`,
`BoundAuditInput.assert_complete_result`, and the verification driver's `main`.

These scripts run before an environment or a downstream artifact is trusted.
Importing icontract or beartype here would require a third-party installation
before the boundary can check it. A fallback decorator would falsely imply an
enforced contract. Instead, the core validates its external JSON, paths,
identities, archive members, signatures and audit inventory with explicit
fail-closed checks. The driver validates its arguments and invokes the same
boundary. The forty mapped regressions exercise observable success and rejection
behavior. The independent final security review accepted this limited rationale.

This exception applies only to these preinstallation entry points. It does not
permit application APIs to omit contracts, or permit a caller to bypass admission.
Reassess it if this code moves into an authenticated installed environment.

## External MCP factory signature

`_FixedManagerFactory.__call__` mirrors the MCP 1.30.0 factory arguments exactly.
The reported six parameters include `self`. The regression checks the installed
SDK signature before casting it to the protocol. Reducing the signature to five
parameters would lose a tested control or misrepresent the external API. This
exception applies only to that external protocol declaration.

No other current review finding is covered by these exceptions. Real native
platform proof and protected producer provenance remain separate obligations.

## Credential fixture readability

The FULL review also reports `ai-bloat.loc-vs-complexity` on the 53-line private
`_authenticated_fixture`. Manual inspection retains that fixture. The same
function defines ephemeral signed credentials, their signature verifier, and the
server's audience policy. Splitting these single-use pieces into more helpers
would spread the security assumptions across callers without removing logic.
The HTTP test itself now contains only the ownership request sequence. This is
an explicit retained readability advisory, not a change to the clean-code gate.
Loading
Loading