Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/api-verity.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
with:
fetch-depth: 0

- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v5.6.0
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"
- run: pip install -e .
Expand Down
43 changes: 32 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v5.6.0
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"
cache: pip
Expand Down Expand Up @@ -54,6 +54,13 @@ jobs:
# fails if the committed README stops matching what the code produces.
run: python scripts/capture_readme_examples.py --check

- name: Secret scan
# The only secret check here was a local pre-commit `detect-private-key`
# hook, which `git commit --no-verify` skips and CI never ran. This is
# the scanner tooltrace-bench uses, plus the Slack, Google, Stripe and
# npm rules a planted-secret test showed it was missing.
run: python scripts/secret_scan.py

# pyproject declares "Operating System :: OS Independent" and Python 3.11 and
# 3.12, but CI ran ubuntu-latest on 3.12 only -- so a 3.11 user, or anyone on
# Windows or macOS, was running wholly unvalidated code against a classifier
Expand Down Expand Up @@ -82,7 +89,7 @@ jobs:
python-version: "3.12"
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v5.6.0
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}
cache: pip
Expand All @@ -100,19 +107,27 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v5.6.0
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"
- run: pip install jsonschema pyyaml && pip install -e .
- name: Validate bundled result artifacts against schemas/result-v1
run: |
python - <<'PY'
import json, pathlib
from jsonschema import Draft202012Validator
schema = json.loads(pathlib.Path("schemas/result-v1.schema.json").read_text())
Draft202012Validator.check_schema(schema)
print("result-v1 schema is valid")
PY
# This step used to load the schema, call `check_schema` on it, and
# print "result-v1 schema is valid". That validates the schema
# *document*; it never loaded an artifact. It was green, required and
# branch-protected while proving nothing about its own name.
#
# Running it for real immediately found five disagreements between the
# schema and the emitters, including `changes` and `operations` each
# carrying an integer in one command and an array in another.
run: python scripts/validate_result_artifacts.py
- name: Pinned actions match their version comments
# Five `uses:` lines pinned setup-python@5fda3b95a4ea and called it
# "v5.6.0". That SHA is v7.0.0. The pin is the control; the comment is
# what a reviewer reads.
env:
GITHUB_TOKEN: ${{ github.token }}
run: python scripts/check_action_pins.py

frontend:
name: Frontend lint · typecheck · build
Expand All @@ -130,6 +145,12 @@ jobs:
- run: npm ci || npm install
- run: npm run lint
- run: npm test
# local-ai-hardware-bench has audited its frontend dependencies from the
# start; this repository ran pip-audit and nothing on the npm side, so
# half its dependency surface was unexamined while a job named
# "Dependency scan" was green.
- name: npm audit (fail on high+)
run: npm audit --audit-level=high
- run: npm run build
# Code splitting is invisible when it breaks: a static import of a page
# module collapses every chunk back into the entry and nothing fails.
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ jobs:
- uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
with:
languages: ${{ matrix.language }}
# The default suite skips a lot that matters for a tool which parses
# untrusted specs and serves HTTP: it was `security-extended` that
# surfaced the ReDoS in a sibling repository's own guard regex.
# local-ai-hardware-bench has run this suite for a while; the four
# projects should not disagree about how hard they look.
queries: security-extended
- uses: github/codeql-action/autobuild@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
- uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v5.6.0
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"
- name: Install build tool
Expand Down
18 changes: 16 additions & 2 deletions apiverity/cli/commands/governance.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,14 @@ def cmd_validate(args: argparse.Namespace) -> int:
"protocol": plugin.protocol().value,
"title": service.title,
"version": service.version,
"operations": len(service.operations),
# `operations` in a result-v1 artifact is the array of per-operation
# stats that performance/engine.py reads back from a baseline
# (`{o["operation_key"]: o for o in baseline["operations"]}`). Emitting
# an integer under the same key meant a `validate` artifact and a
# `baseline` artifact disagreed about the type of the same field, and
# handing the former to the performance engine would raise rather than
# report. The count is still useful, under a name that says so.
"operation_count": len(service.operations),
"findings": all_findings,
"errors": errors,
}
Expand Down Expand Up @@ -86,7 +93,14 @@ def cmd_breaking(args: argparse.Namespace) -> int:
"command": "breaking",
"old_version": old.version,
"new_version": new.version,
"changes": len(changes),
# `diff` emits `changes` as the array of changes; this emitted the
# same key as an integer count, so a consumer reading `changes`
# got a list or a number depending on which command produced the
# artifact -- and schemas/result-v1 declares it an array, which
# made every `breaking` artifact silently non-conformant. Renamed
# rather than converted: the count is genuinely useful here, and
# `breaking` reports findings, not the changes themselves.
"change_count": len(changes),
"findings": findings,
"errors": errors,
},
Expand Down
4 changes: 3 additions & 1 deletion apiverity/cli/commands/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ def cmd_self_test(args: argparse.Namespace) -> int:
"tool": "apiverity",
"command": "self-test",
"ok": ok,
"operations": len(service.operations),
# See governance.py: `operations` is the per-operation array in a
# result-v1 artifact, not a count.
"operation_count": len(service.operations),
"spec_findings": len(findings),
},
args.json,
Expand Down
155 changes: 130 additions & 25 deletions schemas/result-v1.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,149 @@
"$id": "https://github.com/webdevsamran/api-verity-lab/schemas/result-v1.schema.json",
"title": "apiverity result artifact (v1)",
"type": "object",
"required": ["tool", "tool_version", "command", "contract_hash"],
"required": [
"tool",
"tool_version",
"command",
"contract_hash"
],
"properties": {
"tool": { "const": "apiverity" },
"tool_version": { "type": "string" },
"result_schema_version": { "const": 1 },
"tool": {
"const": "apiverity"
},
"tool_version": {
"type": "string"
},
"result_schema_version": {
"const": 1
},
"command": {
"enum": ["validate", "diff", "breaking", "test", "workflow", "drift",
"replay", "baseline", "regression", "coverage"]
},
"contract_hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
"protocol": { "enum": ["openapi", "graphql", "grpc"] },
"target": { "type": "string" },
"seed": { "type": "integer" },
"duration_ms": { "type": "integer" },
"enum": [
"baseline",
"breaking",
"changelog",
"coverage",
"diff",
"drift",
"plugins",
"regression",
"replay",
"rules",
"self-test",
"test",
"validate",
"workflow"
]
},
"contract_hash": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"protocol": {
"enum": [
"openapi",
"graphql",
"grpc"
]
},
"target": {
"type": [
"string",
"null"
],
"description": "Base URL contacted, or null when nothing was contacted."
},
"seed": {
"type": [
"integer",
"null"
],
"description": "Generation seed, or null when the command is not seeded."
},
"duration_ms": {
"type": "integer"
},
"redaction": {
"type": "object",
"properties": {
"applied": { "type": "boolean" },
"sensitive_field_count": { "type": "integer" }
"applied": {
"type": "boolean"
},
"sensitive_field_count": {
"type": "integer"
}
}
},
"findings": {
"type": "array",
"items": {
"type": "object",
"required": ["rule_id", "severity", "message"],
"required": [
"rule_id",
"severity",
"message"
],
"properties": {
"rule_id": { "type": "string" },
"severity": { "enum": ["ERROR", "WARN", "INFO"] },
"message": { "type": "string" },
"operation_key": { "type": "string" },
"old_location": { "type": "object" },
"new_location": { "type": "object" }
"rule_id": {
"type": "string"
},
"severity": {
"enum": [
"ERROR",
"WARN",
"INFO"
]
},
"message": {
"type": "string"
},
"operation_key": {
"type": [
"string",
"null"
],
"description": "Operation the finding belongs to, or null for contract-level findings."
},
"old_location": {
"type": [
"object",
"null"
]
},
"new_location": {
"type": [
"object",
"null"
]
}
}
}
},
"changes": { "type": "array", "items": { "type": "object" } },
"results": { "type": "array", "items": { "type": "object" } },
"operations": { "type": "array", "items": { "type": "object" } }
"changes": {
"type": "array",
"items": {
"type": "object"
}
},
"results": {
"type": "array",
"items": {
"type": "object"
}
},
"operations": {
"type": "array",
"items": {
"type": "object"
}
},
"change_count": {
"type": "integer",
"description": "Number of changes considered (breaking)."
},
"operation_count": {
"type": "integer",
"description": "Number of operations in the contract (validate, self-test)."
}
}
}
}
Loading
Loading