This repository is the source-of-truth monorepo for reusable GitHub application security workflows, custom policy actions, governance automation, and example projects.
Primary focus:
- Fortify on Demand (FoD) SAST workflow patterns
- Sonatype SCA workflow patterns
- GitHub Enterprise Server (GHES)-first compatibility
- Reusable workflow governance and rollout
Top-level platform directories:
platform-workflows/: centralized reusable workflows and orchestrator/detector examplesplatform-actions/: centralized fcli custom actions for policy checks and security controlsplatform-governance/: compliance checks, rollout templates, and governance automationplatform-docs/: architecture, workflow contracts, governance docs, and publishing guidesexamples/*/: minimal example projects for Java, Node, Python, .NET, and monorepo patternssample_workflows/andsample_actions/: standalone workflow/action examplesscripts/: helper scripts for example bootstrapping and subtree publishing
Useful scripts:
scripts/publish-platform-directories.ps1: publish top-level platform directories to split repositoriesscripts/preview-platform-docs.ps1: build/preview platform docs locally through a transient Docusaurus scaffold
Use this repository as a central authoring repo, then publish subdirectories into dedicated repositories in your organization (for example under fortify-presales).
Typical target repositories:
platform-workflowsplatform-actionsplatform-governanceplatform-docs
Optional example repositories:
ref-app-javaref-app-noderef-app-pythonref-app-dotnetref-app-monorepo
- Git is installed and available on PATH.
- You have create and push access to your target GitHub organization.
- Authentication is configured using one of:
- SSH keys (default path for publishing script)
- HTTPS credentials (Git Credential Manager or PAT)
git subtreeis available in your local git installation.- Destination repositories exist in advance (empty is recommended for first publish).
The first-time setup has two phases:
- Bootstrap this source repository (
appsec-platform). - Create destination platform repositories that subtree publishing will push into.
Run these commands from repository root:
git init -b main
git add .
git commit -m "Initial platform workflows, docs, and examples"
git remote add origin https://github.com/<org>/appsec-platform.git
git push -u origin mainIf your organization or host differs, update remote URL accordingly.
For GitHub Enterprise Server, use your enterprise host:
git remote add origin https://<ghes-host>/<org>/appsec-platform.git
git push -u origin mainThe publishing script pushes to these repositories by default:
platform-workflowsplatform-actionsplatform-governanceplatform-docs
Optional repositories when publishing examples:
ref-app-monoreporef-app-javaref-app-noderef-app-pythonref-app-dotnet
Create repositories manually in GitHub UI, or with GitHub CLI:
$repos = @("platform-workflows", "platform-actions", "platform-governance", "platform-docs")
foreach ($r in $repos) { gh repo create "<org>/$r" --private -y }Preflight check before publish (must succeed for each target):
git ls-remote https://<git-host>/<org>/platform-workflows.gitIf this returns Repository not found, create the repository first or confirm your org permissions.
Use the helper script:
scripts/publish-platform-directories.ps1
Recommended defaults:
- Prefer HTTPS mode unless your organization has SSH keys configured on all publisher runners.
- Set
-RepoHostexplicitly for GitHub Enterprise Server. - Run a dry run first for each new organization/host setup.
Default behavior publishes these mappings:
platform-workflows-><org>/platform-workflowsplatform-actions-><org>/platform-actionsplatform-governance-><org>/platform-governanceplatform-docs-><org>/platform-docs
Important:
- The target repositories must already exist.
- You must have write access to each target repository.
- First publish uses force push to set target
mainfrom subtree history.
Command patterns by host:
- GitHub.com dry run:
.\scripts\publish-platform-directories.ps1 -Org <org> -SourceRef main -UseHttps -DryRun- GitHub.com publish:
.\scripts\publish-platform-directories.ps1 -Org <org> -SourceRef main -UseHttps- GHES dry run:
.\scripts\publish-platform-directories.ps1 -Org <org> -SourceRef main -UseHttps -RepoHost <ghes-host> -DryRun- GHES publish:
.\scripts\publish-platform-directories.ps1 -Org <org> -SourceRef main -UseHttps -RepoHost <ghes-host>- HTTPS + PAT (GitHub.com or GHES):
.\scripts\publish-platform-directories.ps1 -Org <org> -SourceRef main -UseHttps -RepoHost <git-host> -Pat "<TOKEN>"Dry run first:
.\scripts\publish-platform-directories.ps1 -Org fortify-presales -SourceRef main -DryRunPublish:
.\scripts\publish-platform-directories.ps1 -Org fortify-presales -SourceRef main -UseHttpsInclude example projects:
.\scripts\publish-platform-directories.ps1 -Org fortify-presales -SourceRef main -UseHttps -IncludeExamplesUse HTTPS + PAT:
.\scripts\publish-platform-directories.ps1 -Org fortify-presales -SourceRef main -UseHttps -Pat "<TOKEN>"Use a custom Git host (GHES):
.\scripts\publish-platform-directories.ps1 -Org <org> -SourceRef main -UseHttps -RepoHost <ghes-host>How publishing works:
git subtree splitbuilds a branch per subdirectory.- Script force-pushes that split branch to
mainof target repository. - Temporary split branches are deleted locally.
Application repositories should keep thin caller workflows and reference centralized reusable workflows by version tag (not @main).
For Sonatype Lifecycle -> FoD SBOM synchronization, prefer a separate scheduled/manual caller workflow (for example lifecycle-fod-sync.yml) instead of mixing it into branch-protection security.yml jobs.
Example:
jobs:
fortify:
uses: fortify-presales/platform-workflows/.github/workflows/reusable-fortify-fod.yml@v1
secrets: inheritPermissions baseline in caller repositories:
permissions:
contents: read
security-events: write
pull-requests: writeMinimum shared configuration for most examples:
- Fortify:
- Variable:
FOD_URL - Secret:
FOD_CLIENT_ID - Secret:
FOD_CLIENT_SECRET
- Variable:
- Sonatype:
- Variable:
LIFECYCLE_SERVER_URL - Variable (optional):
LIFECYCLE_ORGANIZATION_ID(recommended if Sonatype org name differs from GitHub org/repository owner) - Secret:
LIFECYCLE_USERNAME - Secret:
LIFECYCLE_PASSWORD - Variable (optional):
LIFECYCLE_APPLICATION_ID(defaults to the repository name when unset) - Variable (optional):
FOD_RELEASE(used by Lifecycle -> FoD SBOM sync; defaults to<owner>/<repo>:<default_branch>) - Variable (optional):
PLATFORM_ACTIONS_REPOSITORY(for examplemy-org/platform-actions) - Variable (optional):
PLATFORM_ACTIONS_REF(for examplev1or pinned SHA) - Variable (optional):
PLATFORM_ACTIONS_ACTION_PATH(defaultactions/import-lifecycle-sbom-to-fod.yaml)
- Variable:
Yes, these values can be managed at the organization level and inherited by repositories that are allowed to access them.
Recommended split:
- Organization Variables (non-sensitive):
FOD_URLLIFECYCLE_SERVER_URLLIFECYCLE_APPLICATION_ID(optional, only if shared; set per-repo if app-specific)
- Organization Secrets (sensitive):
FOD_CLIENT_IDFOD_CLIENT_SECRETLIFECYCLE_USERNAMELIFECYCLE_PASSWORD
Scope model:
- Store common defaults at organization level.
- Override at repository level only when a specific app/repo needs different values.
- Restrict org secrets/variables to selected repositories where possible.
UI paths:
- GitHub.com:
- Organization
Settings->Secrets and variables->Actions
- Organization
- GitHub Enterprise Server:
- Organization
Settings->Secrets and variables->Actions - Exact labels can vary slightly by GHES version, but location is the same in org settings.
- Organization
Workflow behavior notes:
secrets: inheritin caller workflows passes accessible secrets to reusable workflow jobs.${{ vars.NAME }}resolves repository variables first, then organization-level variables when not overridden.LIFECYCLE_USERNAMEis consumed as a secret in reusable Sonatype workflow calls.
Example project set in this repository:
Location: examples/
ref-app-javaref-app-noderef-app-pythonref-app-dotnetref-app-monorepo
What each example includes:
- Minimal source code and build file(s).
- A
.github/workflows/security.ymlcaller workflow wired to reusable platform workflows.
ref-app-monorepo additionally demonstrates detector + matrix fanout for polyglot services.
These are intended as starter repositories. Copy each example folder into its own repository for end-to-end validation.
Use:
scripts/new-ref-app.ps1
Example:
.\scripts\new-ref-app.ps1 -TemplateName ref-app-node -DestinationPath C:\repos\my-ref-app-node -NewName my-ref-app-nodeSupported template names:
ref-app-javaref-app-noderef-app-pythonref-app-dotnetref-app-monorepo
Use scripts/new-security-workflow.ps1 to generate .github/workflows/security.yml in an existing repository.
Node.js example for sandbox-application:
.\scripts\new-security-workflow.ps1 -RepositoryPath C:\Users\klee2\repos\sandbox-application -Language nodeAuto-detect language from repository files:
.\scripts\new-security-workflow.ps1 -RepositoryPath C:\Users\klee2\repos\sandbox-applicationOverwrite an existing security.yml:
.\scripts\new-security-workflow.ps1 -RepositoryPath C:\Users\klee2\repos\sandbox-application -Language node -ForceGenerate for GitHub Enterprise Server reusable workflows:
.\scripts\new-security-workflow.ps1 -RepositoryPath C:\Users\klee2\repos\sandbox-application -Language node -PlatformOrg <org> -PlatformRepo platform-workflows -WorkflowRef v1Main docs index:
platform-docs/README.md
High-value docs:
platform-docs/intro.mdplatform-docs/architecture.mdplatform-docs/workflows/fortify-fod-reusable.mdplatform-docs/workflows/sonatype-sca-reusable.mdplatform-docs/workflows/security-gate.mdplatform-docs/governance/compliance-rollout.mdplatform-docs/publishing/repository-split.mdplatform-docs/publishing/template-sync.mdplatform-docs/publishing/release-and-tagging.md
See:
platform-governance/README.mdplatform-governance/scripts/check-compliance.ps1platform-governance/templates/README.md
These assets are intended for organization-wide rollout checks, template distribution, and enforcement support.
- Prefer GHES-compatible artifact upload actions where required.
- Keep reusable workflows and custom actions version-pinned with tags.
- Avoid direct
@mainconsumption in downstream repositories. - Document and control any TLS/proxy exceptions used in workflows.
- Keep this monorepo as the editing source of truth.
- Publish updates with subtree script to platform repositories.
- After publishing, create an immutable release tag (for example
v1.2.0) and move major aliasv1to that release commit so repositories using@v1receive updates. - Update callers to consume tagged workflow/action releases.
Release/tagging command runbook:
platform-docs/publishing/release-and-tagging.md
This repository is licensed under Apache-2.0. See LICENSE.