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
134 changes: 134 additions & 0 deletions .github/workflows-disabled/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
name: Release Build

# Build xcframework on a GitHub-hosted macOS runner, resolving Swift package
# dependencies through the curated Artifactory pull-through cache (ADR Rule 3).
# Produces a build attestation binding the artifact digest to the source commit.

on:
push:
pull_request:
workflow_dispatch:

permissions:
contents: read
id-token: write
attestations: write

jobs:
build_xcframework:
runs-on: macos-15
env:
ARTIFACT_NAME: Segment.xcframework.zip
ARTIFACTORY_URL: https://twilio.jfrog.io
SWIFT_VIRTUAL_REPO: virtual-swift-thirdparty
outputs:
artifact-digest: ${{ steps.digest.outputs.sha256 }}

steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Select Xcode
run: sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer

# Exchange GitHub OIDC token for a short-lived Artifactory read token.
# The segmentio org uses provider_name "github-actions-segmentio".
# Token lands in sdk-build-readers group (read access to virtual repos).
- name: Get Artifactory token via OIDC
run: |
set -euo pipefail
OIDC_JWT=$(curl -sS "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=${ARTIFACTORY_URL}" \
-H "Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" | jq -r '.value')
ART_TOKEN=$(curl -sS "${ARTIFACTORY_URL}/access/api/v1/oidc/token" \
-H 'Content-Type: application/json' \
-d "{\"grant_type\":\"urn:ietf:params:oauth:grant-type:token-exchange\",
\"subject_token_type\":\"urn:ietf:params:oauth:token-type:id_token\",
\"subject_token\":\"${OIDC_JWT}\",
\"provider_name\":\"github-actions-segmentio\"}" | jq -r '.access_token')
echo "::add-mask::$ART_TOKEN"
echo "ART_TOKEN=$ART_TOKEN" >> "$GITHUB_ENV"

# Configure SPM to resolve dependencies through the Artifactory Swift
# pull-through cache instead of hitting GitHub directly.
- name: Configure SPM to resolve through Artifactory
run: |
set -euo pipefail
MIRROR_BASE="${ARTIFACTORY_URL}/artifactory/api/swift/${SWIFT_VIRTUAL_REPO}"

# Set up netrc for git authentication to Artifactory
echo "machine twilio.jfrog.io login token password ${ART_TOKEN}" > ~/.netrc
chmod 600 ~/.netrc

# Mirror each dependency through Artifactory
swift package config set-mirror \
--original-url https://github.com/segmentio/sovran-swift.git \
--mirror-url "${MIRROR_BASE}/segmentio/sovran-swift.git"
swift package config set-mirror \
--original-url https://github.com/segmentio/jsonsafeencoding-swift.git \
--mirror-url "${MIRROR_BASE}/segmentio/jsonsafeencoding-swift.git"

- name: Resolve dependencies
run: swift package resolve

# Build xcframework for all supported platforms
- name: Build xcframework
run: |
set -euo pipefail

ARCHIVE_DIR="$(pwd)/.build/archives"
XCFRAMEWORK_DIR="$(pwd)/.build/xcframework"
mkdir -p "$ARCHIVE_DIR" "$XCFRAMEWORK_DIR"

declare -a PLATFORMS=(
"iOS:generic/platform=iOS"
"iOS-Simulator:generic/platform=iOS Simulator"
"macOS:generic/platform=macOS"
"tvOS:generic/platform=tvOS"
"tvOS-Simulator:generic/platform=tvOS Simulator"
"watchOS:generic/platform=watchOS"
"watchOS-Simulator:generic/platform=watchOS Simulator"
)

FRAMEWORK_ARGS=()
for entry in "${PLATFORMS[@]}"; do
NAME="${entry%%:*}"
DEST="${entry#*:}"
ARCHIVE_PATH="$ARCHIVE_DIR/$NAME.xcarchive"

xcodebuild archive \
-scheme Segment \
-destination "$DEST" \
-archivePath "$ARCHIVE_PATH" \
-derivedDataPath .build/derived-data \
SKIP_INSTALL=NO \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
SWIFT_SERIALIZE_DEBUGGING_OPTIONS=NO

FRAMEWORK_ARGS+=("-framework" "$ARCHIVE_PATH/Products/Library/Frameworks/Segment.framework")
done

xcodebuild -create-xcframework \
"${FRAMEWORK_ARGS[@]}" \
-output "$XCFRAMEWORK_DIR/Segment.xcframework"

cd "$XCFRAMEWORK_DIR"
ditto -c -k --sequesterRsrc --keepParent Segment.xcframework "${{ env.ARTIFACT_NAME }}"
mv "${{ env.ARTIFACT_NAME }}" "$GITHUB_WORKSPACE/"

- name: Compute artifact digest
id: digest
run: |
SHA256=$(shasum -a 256 "${{ env.ARTIFACT_NAME }}" | awk '{print $1}')
echo "sha256=$SHA256" >> "$GITHUB_OUTPUT"
echo "Artifact digest: $SHA256"

- name: Attest build provenance
uses: actions/attest-build-provenance@1c608d11d69870c2092266b3f9a6f3abbf17002c # v1.4.3
with:
subject-path: ${{ env.ARTIFACT_NAME }}

- name: Upload artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: xcframework-release
path: ${{ env.ARTIFACT_NAME }}
retention-days: 30
109 changes: 109 additions & 0 deletions .github/workflows-disabled/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
name: Publish

# Verify clearance and publish the exact cleared bytes to GitHub Release.
#
# This workflow:
# 1. Downloads the built artifact from the release-build run
# 2. Recomputes its sha256 digest
# 3. Verifies the build attestation (signer-workflow pinned)
# 4. Verifies internal clearance (placeholder — not yet built by Platform)
# 5. On all checks pass: uploads to GitHub Release
# 6. The sha256 becomes the checksum in Package.swift's binaryTarget
#
# FAILS CLOSED on any mismatch.

on:
workflow_dispatch:
inputs:
version:
description: 'Version to publish (e.g., 1.9.5)'
required: true
run-id:
description: 'The release-build workflow run ID'
required: true
artifact-digest:
description: 'The sha256 digest from the release build'
required: true

permissions:
contents: write
id-token: write
attestations: write

env:
ARTIFACT_NAME: Segment.xcframework.zip
BUILD_WORKFLOW_REF: ".github/workflows/build.yml"

jobs:
verify_and_publish:
runs-on: macos-26
environment: production

steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Download release artifact
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: xcframework-release
run-id: ${{ inputs.run-id }}
github-token: ${{ secrets.GITHUB_TOKEN }}

# Gate 1: Digest verification — detect substitution between build and publish.
- name: Verify artifact digest
run: |
set -euo pipefail
COMPUTED_DIGEST=$(shasum -a 256 "${{ env.ARTIFACT_NAME }}" | awk '{print $1}')
EXPECTED_DIGEST="${{ inputs.artifact-digest }}"

if [ "$COMPUTED_DIGEST" != "$EXPECTED_DIGEST" ]; then
echo "::error::DIGEST MISMATCH — artifact may have been tampered with"
exit 1
fi

echo "Digest verified: $COMPUTED_DIGEST"
echo "ARTIFACT_DIGEST=$COMPUTED_DIGEST" >> "$GITHUB_ENV"

Comment on lines +54 to +66

@semgrep-code-segmentio-2 semgrep-code-segmentio-2 Bot Jun 12, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Using variable interpolation ${{...}} with github context data in a run: step could allow an attacker to inject their own code into the runner. This would allow them to steal secrets and code. github context data can have arbitrary user input and should be treated as untrusted. Instead, use an intermediate environment variable with env: to store the data and use the environment variable in the run: script. Be sure to use double-quotes the environment variable, like this: "$ENVVAR".

🎉 Fixed in commit 1ddac56 🎉

Comment on lines +54 to +66

@semgrep-code-segmentio-2 semgrep-code-segmentio-2 Bot Jul 2, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Using variable interpolation ${{...}} with github context data in a run: step could allow an attacker to inject their own code into the runner. This would allow them to steal secrets and code. github context data can have arbitrary user input and should be treated as untrusted. Instead, use an intermediate environment variable with env: to store the data and use the environment variable in the run: script. Be sure to use double-quotes the environment variable, like this: "$ENVVAR".

Removed in commit b46b44a

Comment on lines +54 to +66

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Semgrep identified an issue in your code:

User-controlled workflow input inputs.artifact-digest is directly interpolated into a shell script, allowing command injection. An attacker could inject arbitrary commands to steal secrets or compromise the workflow.

More details about this

The run: step directly interpolates ${{ inputs.artifact-digest }} into the shell script without any sanitization. Since inputs.artifact-digest is passed from user input through the workflow input parameter, an attacker could inject arbitrary shell commands into the EXPECTED_DIGEST variable.

Exploit scenario:

  1. An attacker submits a workflow dispatch request with artifact-digest set to something like "; rm -rf /; #
  2. This malicious value gets interpolated directly into the script: EXPECTED_DIGEST="${{ inputs.artifact-digest }}" becomes EXPECTED_DIGEST=""; rm -rf /; #"
  3. The shell interprets the injected commands and executes them with the runner's full permissions
  4. The attacker can now steal secrets from $GITHUB_TOKEN, exfiltrate the repository code, or modify the artifact before publishing

The comparison logic in the conditional doesn't execute unless the attacker's injected code is valid shell syntax, but they can still inject commands before it completes or use shell metacharacters to break out of the string and execute arbitrary code.

To resolve this comment:

✨ Commit fix suggestion

Suggested change
run: |
set -euo pipefail
COMPUTED_DIGEST=$(shasum -a 256 "${{ env.ARTIFACT_NAME }}" | awk '{print $1}')
EXPECTED_DIGEST="${{ inputs.artifact-digest }}"
if [ "$COMPUTED_DIGEST" != "$EXPECTED_DIGEST" ]; then
echo "::error::DIGEST MISMATCH — artifact may have been tampered with"
exit 1
fi
echo "Digest verified: $COMPUTED_DIGEST"
echo "ARTIFACT_DIGEST=$COMPUTED_DIGEST" >> "$GITHUB_ENV"
env:
EXPECTED_DIGEST: ${{ inputs.artifact-digest }}
run: |
set -euo pipefail
COMPUTED_DIGEST=$(shasum -a 256 "${{ env.ARTIFACT_NAME }}" | awk '{print $1}')
if [ "$COMPUTED_DIGEST" != "$EXPECTED_DIGEST" ]; then
echo "::error::DIGEST MISMATCH — artifact may have been tampered with"
exit 1
fi
echo "Digest verified: $COMPUTED_DIGEST"
echo "ARTIFACT_DIGEST=$COMPUTED_DIGEST" >> "$GITHUB_ENV"
View step-by-step instructions
  1. Move the untrusted workflow input out of the shell script and into the step env: block.
    Add something like env: EXPECTED_DIGEST: ${{ inputs.artifact-digest }} on the Verify artifact digest step.

  2. Stop interpolating ${{ inputs.artifact-digest }} inside run:.
    Replace EXPECTED_DIGEST="${{ inputs.artifact-digest }}" with EXPECTED_DIGEST="$EXPECTED_DIGEST" or compare directly against "$EXPECTED_DIGEST" in the if statement.

  3. Use the environment variable with double quotes everywhere it is read in the shell.
    Keep the comparison in the form if [ "$COMPUTED_DIGEST" != "$EXPECTED_DIGEST" ]; then ... fi so the value is treated as data, not shell syntax.

  4. Apply the same pattern to other run: steps that interpolate ${{ ... }} values from inputs or github context.
    For example, move values such as ${{ inputs.version }}, ${{ github.repository }}, and ${{ github.sha }} into step-level env: entries, then reference them as "$VERSION", "$GITHUB_REPOSITORY", or "$GITHUB_SHA" inside the script.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by run-shell-injection.

Need help with this issue? Consult our appsec team or ask in #help-appsec on Slack.

You can view more details about this finding in the Semgrep AppSec Platform.

# Gate 2: Build attestation — verify it was signed by the expected workflow.
- name: Verify build attestation
run: |
set -euo pipefail
gh attestation verify "${{ env.ARTIFACT_NAME }}" \
-R "${{ github.repository }}" \
--signer-workflow "${{ env.BUILD_WORKFLOW_REF }}"

# Gate 3: Internal clearance — placeholder until Platform delivers the gate.
- name: Verify internal clearance
run: |
echo "PLACEHOLDER: Internal clearance gate not yet available (PEA-1323)"
echo "Once delivered, this step will:"
echo " 1. Fetch KMS-signed clearance attestation for digest ${{ env.ARTIFACT_DIGEST }}"
echo " 2. Verify signature against the clearance KMS public key"
echo " 3. Assert clearance subject.digest.sha256 == ${{ env.ARTIFACT_DIGEST }}"
echo " 4. Assert clearance predicate.sourceCommit == ${{ github.sha }}"
echo " 5. Fail closed on any mismatch"

# All gates passed — publish to GitHub Release.
- name: Create GitHub Release
run: |
set -euo pipefail
VERSION="${{ inputs.version }}"

gh release create "$VERSION" \
"${{ env.ARTIFACT_NAME }}" \
--title "Version $VERSION" \
--notes "## Segment.xcframework ${VERSION}

Binary Swift package release.

### Consumer verification

Verify the build attestation:
\`\`\`
gh attestation verify Segment.xcframework.zip -R ${{ github.repository }} --signer-workflow ${{ env.BUILD_WORKFLOW_REF }}
\`\`\`

### Checksum (for Package.swift binaryTarget)
\`\`\`
${{ env.ARTIFACT_DIGEST }}
\`\`\`"
Comment on lines +88 to +109

@semgrep-code-segmentio-2 semgrep-code-segmentio-2 Bot Jul 2, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Using variable interpolation ${{...}} with github context data in a run: step could allow an attacker to inject their own code into the runner. This would allow them to steal secrets and code. github context data can have arbitrary user input and should be treated as untrusted. Instead, use an intermediate environment variable with env: to store the data and use the environment variable in the run: script. Be sure to use double-quotes the environment variable, like this: "$ENVVAR".

🎉 Removed in commit b46b44a 🎉

Comment on lines +88 to +109

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Semgrep identified an issue in your code:

Untrusted workflow input inputs.version is interpolated directly into shell command in a run: step, allowing arbitrary code execution. Attackers can inject shell metacharacters via workflow dispatch to execute malicious commands on the runner.

More details about this

The run: step directly interpolates ${{ inputs.version }} into a shell command without protection. An attacker could exploit this by submitting a workflow dispatch with a malicious version input containing shell metacharacters. For example, if an attacker provides version as 1.0.0"; rm -rf /; echo ", the shell would execute:

VERSION="1.0.0"; rm -rf /; echo ""
gh release create ...

This allows arbitrary command execution on the runner, potentially enabling theft of GITHUB_TOKEN and other repository secrets, code exfiltration, or runner compromise.

The vulnerable code stores the untrusted inputs.version directly into VERSION without escaping, then uses it unquoted in the gh release create command and in string interpolation for the release title. While env.ARTIFACT_DIGEST, env.ARTIFACT_NAME, and env.BUILD_WORKFLOW_REF are safe (they come from environment variables, not user input), the inputs.version variable poses a direct injection risk.

To resolve this comment:

✨ Commit fix suggestion

Suggested change
run: |
set -euo pipefail
VERSION="${{ inputs.version }}"
gh release create "$VERSION" \
"${{ env.ARTIFACT_NAME }}" \
--title "Version $VERSION" \
--notes "## Segment.xcframework ${VERSION}
Binary Swift package release.
### Consumer verification
Verify the build attestation:
\`\`\`
gh attestation verify Segment.xcframework.zip -R ${{ github.repository }} --signer-workflow ${{ env.BUILD_WORKFLOW_REF }}
\`\`\`
### Checksum (for Package.swift binaryTarget)
\`\`\`
${{ env.ARTIFACT_DIGEST }}
\`\`\`"
env:
VERSION: ${{ inputs.version }}
REPOSITORY: ${{ github.repository }}
ARTIFACT_NAME: ${{ env.ARTIFACT_NAME }}
BUILD_WORKFLOW_REF: ${{ env.BUILD_WORKFLOW_REF }}
ARTIFACT_DIGEST: ${{ env.ARTIFACT_DIGEST }}
run: |
set -euo pipefail
gh release create "$VERSION" \
"$ARTIFACT_NAME" \
--title "Version $VERSION" \
--notes "## Segment.xcframework $VERSION
Binary Swift package release.
### Consumer verification
Verify the build attestation:
\`\`\`
gh attestation verify Segment.xcframework.zip -R $REPOSITORY --signer-workflow $BUILD_WORKFLOW_REF
\`\`\`
### Checksum (for Package.swift binaryTarget)
\`\`\`
$ARTIFACT_DIGEST
\`\`\`"
View step-by-step instructions
  1. Move the untrusted workflow input out of the shell script and into the step env: block. For example, set VERSION: ${{ inputs.version }} on the Create GitHub Release step instead of assigning VERSION="${{ inputs.version }}" inside run:.

  2. Remove the direct ${{ inputs.version }} interpolation from the run: script and use the environment variable instead. Keep all shell references quoted, such as "$VERSION".

  3. If you need the repository value inside the release notes text, pass it through env: as well and reference it from the shell, for example REPOSITORY: ${{ github.repository }} and then use "$REPOSITORY" in the command text. This avoids mixing GitHub expression expansion directly into a shell script.

  4. Update the gh release create command so the shell only reads environment variables inside run:. For example, use values like "$VERSION", "$ARTIFACT_NAME", "$BUILD_WORKFLOW_REF", and "$ARTIFACT_DIGEST" in the command arguments and notes body.

  5. Keep the multi-line --notes argument as a shell string that expands only environment variables, not ${{ ... }} expressions. This prevents user-controlled workflow input from being interpreted by the runner before the shell handles quoting.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by run-shell-injection.

Need help with this issue? Consult our appsec team or ask in #help-appsec on Slack.

You can view more details about this finding in the Semgrep AppSec Platform.

39 changes: 0 additions & 39 deletions .github/workflows/create_jira.yml

This file was deleted.

73 changes: 0 additions & 73 deletions .github/workflows/e2e-tests.yml

This file was deleted.

36 changes: 0 additions & 36 deletions .github/workflows/publish-e2e-cli.yml

This file was deleted.

Loading
Loading