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
10 changes: 6 additions & 4 deletions .castiron.stats.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
schema_version: 1
generation_id: 04cb977a-ba1d-438c-a56e-94f269e79218
openapi_spec_hash: dd725fb7d43ceec7fb2de6f8713d14b6
openapi_transformed_spec_hash: 10930179c5f116288e24e0c6fda46559
generation_id: 14743cd4-a53e-4fed-ba72-d5e23592153e
openapi_spec_hash: a99ded1ea34cf528a9cd5f064167f26a
openapi_transformed_spec_hash: e24c9d9339620c3cce8bbdd80e9ef8ed
config_hash: 85382dd94c503b5d225adc7636a77c9f
codegen_sha: 6e990f52e3cbdeaae602710a1b0f2a2c944a5c35
codegen_sha: 66cd6dedd5d1b60732d891f911deb07347a8f068
codegen_hash: 0e1cb892e3631438e55b55edd14899551f458be8d073e13d2c191730297562d6
public_codegen_sha: 6356986f823c01fd602da9f64ef414c01db4619c
87 changes: 87 additions & 0 deletions .github/workflows/castiron-custom-code-comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
name: Castiron custom code comment

on:
workflow_run:
workflows: [Castiron custom code]
types: [completed]

permissions: {}

concurrency:
group: castiron-custom-code-comment-${{ github.event.workflow_run.head_repository.id }}-${{ github.event.workflow_run.head_branch }}
cancel-in-progress: false

jobs:
comment:
name: Update custom-code comment
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.path == '.github/workflows/castiron-custom-code.yml'
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
actions: read
pull-requests: write
steps:
- name: Check out the trusted publisher
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: ${{ github.workflow_sha }}
persist-credentials: false

- name: Download the completed run's report
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id }}
name: castiron-custom-code-${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}
path: ${{ runner.temp }}/castiron-custom-code

- name: Validate report context
id: context
env:
REPOSITORY: ${{ github.repository }}
RUN_ID: ${{ github.event.workflow_run.id }}
RUN_ATTEMPT: ${{ github.event.workflow_run.run_attempt }}
run: |
number=$(jq -er --arg repository "$REPOSITORY" --argjson run "$RUN_ID" \
--argjson attempt "$RUN_ATTEMPT" \
'select(.repository == $repository and .run == $run and .attempt == $attempt) | .pr | select(type == "number" and . > 0 and . == floor)' \
"$RUNNER_TEMP/castiron-custom-code/context.json")
printf 'number=%s\n' "$number" >> "$GITHUB_OUTPUT"

- name: Create or update the single report comment
id: publish
env:
GH_TOKEN: ${{ github.token }}
REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ steps.context.outputs.number }}
RUN_ID: ${{ github.event.workflow_run.id }}
RUN_ATTEMPT: ${{ github.event.workflow_run.run_attempt }}
run: |
python3 -I scripts/castiron/custom_code_report.py comment \
--report "$RUNNER_TEMP/castiron-custom-code/report.json" \
--repository "$REPOSITORY" --pr "$PR_NUMBER" --run-id "$RUN_ID" \
--run-attempt "$RUN_ATTEMPT"

- name: Publish a trusted failure status
if: always() && !cancelled() && steps.publish.outcome != 'success'
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
with:
script: |
const marker = '<!-- castiron:custom-code-report:v1 -->';
const run = context.payload.workflow_run;
if (run.event !== 'pull_request' || run.path !== '.github/workflows/castiron-custom-code.yml') return;
const pulls = run.pull_requests?.length ? run.pull_requests : await github.paginate(github.rest.repos.listPullRequestsAssociatedWithCommit, {...context.repo, commit_sha: run.head_sha});
for (const pull of pulls) {
const {data: current} = await github.rest.pulls.get({...context.repo, pull_number: pull.number});
if (current.state !== 'open' || current.head.sha !== run.head_sha) continue;
const comments = await github.paginate(github.rest.issues.listComments, {...context.repo, issue_number: pull.number});
const previous = comments.find(c => c.user?.type === 'Bot' && c.user?.login === 'github-actions[bot]' && c.body?.startsWith(marker));
const prior = previous?.body?.match(/<!-- castiron:run:v1:(\d+):(\d+) -->/);
if (prior && (Number(prior[1]) > run.id || (Number(prior[1]) === run.id && Number(prior[2]) > run.run_attempt))) continue;
const url = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${run.id}`;
const body = `${marker}\n\n## Castiron custom code\n\n⚠️ Report unavailable for \`${run.head_sha.slice(0, 12)}\`.\n\nThe report setup or validation failed. [Inspect the workflow run](${url}).\n\n<!-- castiron:run:v1:${run.id}:${run.run_attempt} -->`;
if (previous) await github.rest.issues.updateComment({...context.repo, comment_id: previous.id, body});
else await github.rest.issues.createComment({...context.repo, issue_number: pull.number, body});
}
84 changes: 84 additions & 0 deletions .github/workflows/castiron-custom-code.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details.
name: Castiron custom code

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
Comment on lines +5 to +6

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Rerun the report when the pull request base changes

When a pull request is retargeted to another base branch, GitHub emits an edited activity rather than any of the activity types listed here. The head SHA can remain unchanged while its merge base and generated baseline change, so no replacement report is produced and the existing bot comment continues to describe the old base; include the base-change event and recompute the report.

Useful? React with 👍 / 👎.


permissions:
contents: read

concurrency:
group: castiron-custom-code-${{ github.event.pull_request.number }}
cancel-in-progress: false

env:
REPORTER_SHA256: 73ecd6290e9803b0d0a93af4ca4dccdbf8648cbd5c0cce51ea65fce28c7da79f
Comment on lines +15 to +16

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Verify the reporter against a trusted checksum

Because this checksum and the reporter are both loaded from the pull-request head, a contributor can modify custom_code_report.py to fabricate a clean status: ok report and update this value so the verification step still passes. The separate trusted publisher then renders that fabricated artifact as a github-actions[bot] comment, so the custom-code check cannot be trusted to detect deliberate customizations; the expected checksum or executable must come from outside the PR-controlled checkout.

Useful? React with 👍 / 👎.


jobs:
report:
name: Castiron / baseline consistency
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Record the pull request
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
REPOSITORY: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
RUN_ATTEMPT: ${{ github.run_attempt }}
run: |
mkdir -p "$RUNNER_TEMP/castiron-custom-code"
jq -n --argjson pr "$PR_NUMBER" --arg repository "$REPOSITORY" \
--argjson run "$RUN_ID" --argjson attempt "$RUN_ATTEMPT" \
'{pr: $pr, repository: $repository, run: $run, attempt: $attempt}' \
> "$RUNNER_TEMP/castiron-custom-code/context.json"

- name: Check out the pull request
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
persist-credentials: false

- name: Verify the reporter matches its generated workflow
run: printf '%s %s\n' "$REPORTER_SHA256" scripts/castiron/custom_code_report.py | sha256sum --check --strict

- name: Test hash mismatch and snapshot isolation
run: python3 -m unittest discover -s scripts/castiron -p test_custom_code_report.py

- name: Validate the codegen hash and report custom code
env:
GH_TOKEN: ${{ github.token }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove the token from the PR-controlled report step

For fork-originated pull requests, this job checks out the contributor's head and then executes their version of scripts/castiron/custom_code_report.py while GH_TOKEN is present in its environment. A contributor can therefore modify that script to exfiltrate the short-lived repository credential or exercise its contents: read permissions; the token must not be exposed to code taken from the untrusted PR checkout.

AGENTS.md reference: AGENTS.md:L25-L27

Useful? React with 👍 / 👎.

GIT_CONFIG_COUNT: '2'
GIT_CONFIG_KEY_0: credential.helper
GIT_CONFIG_VALUE_0: ''
GIT_CONFIG_KEY_1: credential.https://github.com.helper
GIT_CONFIG_VALUE_1: '!gh auth git-credential'
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
PUBLIC_REPOSITORY: ${{ !github.event.repository.private }}
run: |
git fetch --quiet --no-tags origin "$BASE_SHA" "$HEAD_SHA"
mode=()
if [[ "$PUBLIC_REPOSITORY" == true ]]; then mode=(--public); fi
python3 -I scripts/castiron/custom_code_report.py report \
--base "$BASE_SHA" --head "$HEAD_SHA" \
--fetch --require-head-hash "${mode[@]}" \
--out "$RUNNER_TEMP/castiron-custom-code"

- name: Add the report to the run summary
if: always()
run: |
if test -f "$RUNNER_TEMP/castiron-custom-code/summary.md"; then
cat "$RUNNER_TEMP/castiron-custom-code/summary.md" >> "$GITHUB_STEP_SUMMARY"
fi

- name: Upload report and current custom-code patch
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: castiron-custom-code-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/castiron-custom-code/
if-no-files-found: error
retention-days: 7
16 changes: 11 additions & 5 deletions api_reference/openapi.transformed.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31738,6 +31738,12 @@ components:
model:
type: string
description: The model to generate the completion.
obfuscation:
type: string
description: |
An obfuscation string added to normalize the size of streamed chunks as a
mitigation to certain side-channel attacks. The field is included by
default and omitted when `stream_options.include_obfuscation` is `false`.
service_tier:
$ref: '#/components/schemas/ServiceTier'
system_fingerprint:
Expand Down Expand Up @@ -31781,13 +31787,13 @@ components:
name: The chat completion chunk object
group: chat
example: |
{"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}]}
{"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"obfuscation":"r4N7vQ2m"}

{"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}]}
{"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}],"obfuscation":"p9K3xT6w"}

....

{"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]}
{"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"obfuscation":""}
CreateCompletionRequest:
type: object
properties:
Expand Down Expand Up @@ -64185,7 +64191,7 @@ components:
x-stainless-const: true
encrypted_content:
type: string
maxLength: 10485760
maxLength: 20971520
description: The encrypted content of the compaction summary.
type: object
required:
Expand Down Expand Up @@ -70602,7 +70608,7 @@ components:
x-stainless-const: true
encrypted_content:
type: string
maxLength: 10485760
maxLength: 20971520
description: The encrypted content of the compaction summary.
type: object
required:
Expand Down
23 changes: 23 additions & 0 deletions scripts/castiron/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!-- File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. -->

# Castiron custom-code reporting

Castiron maintains shared templates for these files. Prefer changing those templates
for cross-SDK improvements; repository-specific customizations use the normal
three-way merge and are allowed.
The reporter uses Python 3.10+, Git, and `gh`; it does not import SDK code.

Run `python3 scripts/castiron/test_custom_code_report.py` for focused tests.
The report comment includes commands to inspect the exact custom-code patch.
Public reporting uses only public snapshots and needs no private repository access.

The workflow validates the recorded `codegen_hash`.
Its hash format is documented in the reporter. Only `.github/actions/` and
`.github/workflows/` are excluded from the content hash.

The read-only pull-request workflow runs on every branch, including drafts and
forks. A separate `workflow_run` publisher reads its report as untrusted data and
uses only code from the trusted default branch to update the PR comment. The
publisher becomes active once its workflow is on the default branch. No branch
allowlist or repository variable is needed. Never execute PR-controlled code with
write credentials. Changing either workflow may require one-time AM permission.
Loading
Loading