TeamCity Integration

Integrate Code Pathfinder into TeamCity build configurations using Kotlin DSL and the built-in Docker Container Wrapper. The Container Wrapper runs any build step inside a Docker container without needing a separate Docker runner step.

Quick Start

Add a build configuration to your .teamcity/settings.kts. The dockerImage property on the script step activates the Container Wrapper, which pulls the image and runs your script inside it:

kotlin
import jetbrains.buildServer.configs.kotlin.*
import jetbrains.buildServer.configs.kotlin.buildSteps.script
import jetbrains.buildServer.configs.kotlin.triggers.vcs

version = "2024.12"

project {
    buildType(CodePathfinderSAST)
}

object CodePathfinderSAST : BuildType({
    name = "Code Pathfinder SAST"

    vcs {
        root(DslContext.settingsRoot)
    }

    steps {
        script {
            name = "Run Code Pathfinder SAST"
            scriptContent = """
                pathfinder ci \
                    --project . \
                    --ruleset python/all \
                    --ruleset docker/all \
                    --ruleset docker-compose/all \
                    --output sarif \
                    --output-file pathfinder-results.sarif \
                    --fail-on critical,high
            """.trimIndent()
            dockerImage = "shivasurya/code-pathfinder:stable-latest"
        }
    }

    artifactRules = "pathfinder-results.sarif => security-reports"

    triggers {
        vcs { }
    }
})

Container Wrapper vs Docker runner

TeamCity has two ways to run Docker. The Container Wrapper (shown above) adds a dockerImage property to any existing step — TeamCity automatically overrides the entrypoint to /bin/sh and mounts the checkout directory at the same path inside the container. The separate Docker runner step (dockerCommand {}) is for explicit docker build or raw docker run commands. Use the Container Wrapper for running pathfinder.

Version pinning

Pin to a specific tag like shivasurya/code-pathfinder:v2.0.2 instead of stable-latest for reproducible builds.

File Structure

TeamCity reads Kotlin DSL from the .teamcity/ directory in your repository root. For small projects a single file is sufficient:

text
.teamcity/
  pom.xml          # Maven descriptor for IDE support and dependency resolution
  settings.kts     # Project settings: version declaration + all build configurations

For larger projects, split into separate files under .teamcity/_Self/buildTypes/. The pom.xml file is auto-generated by TeamCity when you first enable Kotlin DSL — you do not need to create it manually.

To enable Kotlin DSL: go to Project Settings → Versioned Settings → Enable, choose use settings from VCS, and select Kotlin as the format. TeamCity will commit an initial settings.kts to your repository.

Configuration Options

All pathfinder ci flags are available inside the scriptContent block.

FlagDescriptionDefault
--rulesPath to local Python SDK rules file or directory
--rulesetRemote ruleset(s) from registry. Can be specified multiple times.
--projectPath to source code to scan.
--skip-testsSkip scanning test filestrue
--outputOutput format: sarif, json, or csvsarif
--output-fileWrite output to file instead of stdout
--fail-onFail build on severities: critical, high, medium, low (comma-separated)
--no-diffScan all files, disabling diff-aware modefalse
--verboseShow statistics and timing informationfalse
--debugShow detailed debug diagnostics with timestampsfalse
--refresh-rulesForce refresh of cached rulesetsfalse
--disable-metricsDisable anonymous usage metricsfalse

Common Use Cases

Scan Python projects

kotlin
steps {
    script {
        name = "Python Security Scan"
        scriptContent = """
            pathfinder ci \
                --project . \
                --ruleset python/all \
                --fail-on critical,high \
                --output sarif \
                --output-file results.sarif
        """.trimIndent()
        dockerImage = "shivasurya/code-pathfinder:stable-latest"
    }
}
artifactRules = "results.sarif => security-reports"

Scan Dockerfiles and docker-compose

kotlin
steps {
    script {
        name = "Docker Security Scan"
        scriptContent = """
            pathfinder ci \
                --project . \
                --ruleset docker/all \
                --ruleset docker-compose/all \
                --verbose \
                --output sarif \
                --output-file results.sarif
        """.trimIndent()
        dockerImage = "shivasurya/code-pathfinder:stable-latest"
    }
}
artifactRules = "results.sarif => security-reports"

Multiple rulesets in one step

kotlin
steps {
    script {
        name = "Full SAST Scan"
        scriptContent = """
            pathfinder ci \
                --project . \
                --ruleset python/all \
                --ruleset docker/all \
                --ruleset docker-compose/all \
                --fail-on critical,high \
                --output sarif \
                --output-file pathfinder-results.sarif
        """.trimIndent()
        dockerImage = "shivasurya/code-pathfinder:stable-latest"
    }
}
artifactRules = "pathfinder-results.sarif => security-reports"
// Build fails automatically when pathfinder exits non-zero (--fail-on triggered)

Parallel scans in a monorepo

Create separate build configurations for each project component and run them in parallel using TeamCity snapshot dependencies or build chains:

kotlin
import jetbrains.buildServer.configs.kotlin.*
import jetbrains.buildServer.configs.kotlin.buildSteps.script
import jetbrains.buildServer.configs.kotlin.triggers.vcs

version = "2024.12"

project {
    buildType(ScanBackend)
    buildType(ScanInfrastructure)
}

object ScanBackend : BuildType({
    name = "Scan Backend (Python)"
    vcs { root(DslContext.settingsRoot) }
    steps {
        script {
            name = "Python SAST"
            scriptContent = """
                pathfinder ci \
                    --project ./backend \
                    --ruleset python/all \
                    --output sarif \
                    --output-file backend-results.sarif
            """.trimIndent()
            dockerImage = "shivasurya/code-pathfinder:stable-latest"
        }
    }
    artifactRules = "backend-results.sarif => security-reports"
    triggers { vcs { } }
})

object ScanInfrastructure : BuildType({
    name = "Scan Infrastructure (Docker)"
    vcs { root(DslContext.settingsRoot) }
    steps {
        script {
            name = "Docker SAST"
            scriptContent = """
                pathfinder ci \
                    --project ./infrastructure \
                    --ruleset docker/all \
                    --output sarif \
                    --output-file infra-results.sarif
            """.trimIndent()
            dockerImage = "shivasurya/code-pathfinder:stable-latest"
        }
    }
    artifactRules = "infra-results.sarif => security-reports"
    triggers { vcs { } }
})

Pull Request Scanning

TeamCity detects pull requests through the Pull Requests build feature, which filters branches matching the PR naming convention of your VCS provider. Enable it in Kotlin DSL with the features {} block, then restrict the VCS trigger to PR branches using a branch filter:

kotlin
import jetbrains.buildServer.configs.kotlin.*
import jetbrains.buildServer.configs.kotlin.buildFeatures.pullRequests
import jetbrains.buildServer.configs.kotlin.buildSteps.script
import jetbrains.buildServer.configs.kotlin.triggers.vcs

version = "2024.12"

project {
    buildType(CodePathfinderPR)
}

object CodePathfinderPR : BuildType({
    name = "Code Pathfinder SAST (PR)"

    vcs {
        root(DslContext.settingsRoot)
    }

    steps {
        script {
            name = "Run Code Pathfinder SAST"
            scriptContent = """
                pathfinder ci \
                    --project . \
                    --ruleset python/all \
                    --ruleset docker/all \
                    --ruleset docker-compose/all \
                    --output sarif \
                    --output-file pathfinder-results.sarif \
                    --fail-on critical,high
            """.trimIndent()
            dockerImage = "shivasurya/code-pathfinder:stable-latest"
        }
    }

    artifactRules = "pathfinder-results.sarif => security-reports"

    // Trigger on PRs and the default branch
    triggers {
        vcs {
            branchFilter = """
                +:<default>
                +:pull/*
            """.trimIndent()
        }
    }

    // Pull Requests feature: detects PR branches and sets build parameters
    features {
        pullRequests {
            vcsRootExtId = "${DslContext.settingsRoot.id}"
            provider = github {
                authType = token {
                    token = "credentialsJSON:your-github-token-id"
                }
                filterAuthorRole = PullRequests.GitHubRoleFilter.MEMBER
            }
        }
    }
})

GitHub token for PR detection

Replace credentialsJSON:your-github-token-id with the ID of a GitHub token stored in TeamCity's credential store (Project Settings → Connections or Parameters → credentialsJSON). The token needs repo scope (or public_repo for public repositories). For GitLab or Bitbucket VCS roots, change the provider block to gitlab {} or bitbucketServer {}.

Scheduled Scans

Use the schedule trigger with a cron expression to run a weekly security scan. TeamCity uses Quartz cron format (six fields: seconds minutes hours day month weekday):

kotlin
import jetbrains.buildServer.configs.kotlin.*
import jetbrains.buildServer.configs.kotlin.buildSteps.script
import jetbrains.buildServer.configs.kotlin.triggers.schedule

version = "2024.12"

project {
    buildType(CodePathfinderWeeklyScan)
}

object CodePathfinderWeeklyScan : BuildType({
    name = "Code Pathfinder Weekly SAST"

    vcs {
        root(DslContext.settingsRoot)
    }

    steps {
        script {
            name = "Weekly Security Scan"
            scriptContent = """
                pathfinder ci \
                    --project . \
                    --ruleset python/all \
                    --ruleset docker/all \
                    --ruleset docker-compose/all \
                    --no-diff \
                    --output sarif \
                    --output-file pathfinder-results.sarif
            """.trimIndent()
            dockerImage = "shivasurya/code-pathfinder:stable-latest"
        }
    }

    artifactRules = "pathfinder-results.sarif => security-reports"

    triggers {
        schedule {
            // Every Monday at 02:00 AM UTC (Quartz cron: sec min hour day month weekday)
            schedulingPolicy = cron {
                seconds = "0"
                minutes = "0"
                hours = "2"
                dayOfMonth = "?"
                month = "*"
                dayOfWeek = "MON"
            }
            branchFilter = "+:<default>"
            triggerBuild = always()
        }
    }
})

--no-diff for scheduled full scans

Scheduled scans should use --no-diff to scan the entire codebase rather than only files changed since the last build. Diff-aware mode is more useful for commit-triggered builds where you only need to check what changed.

Troubleshooting

No vulnerabilities detected

Enable debug and verbose output to see detailed scan progress:

kotlin
steps {
    script {
        scriptContent = """
            pathfinder ci \
                --project . \
                --ruleset python/all \
                --debug \
                --verbose \
                --output sarif \
                --output-file results.sarif
        """.trimIndent()
        dockerImage = "shivasurya/code-pathfinder:stable-latest"
    }
}

Scan full codebase instead of changed files

By default, Code Pathfinder uses diff-aware scanning in CI. Use --no-diff to scan everything:

kotlin
scriptContent = """
    pathfinder ci \
        --project . \
        --ruleset python/all \
        --no-diff \
        --output sarif \
        --output-file results.sarif
""".trimIndent()

Cache issues with remote rulesets

Force refresh cached rulesets:

kotlin
scriptContent = """
    pathfinder ci \
        --project . \
        --ruleset python/all \
        --refresh-rules \
        --output sarif \
        --output-file results.sarif
""".trimIndent()

Artifacts not published when build fails

By default, TeamCity publishes artifacts even when a build step fails (EVEN_IF_FAILED mode). If your SARIF file is not appearing after a failed build, explicitly set the publish mode on the build configuration:

kotlin
object CodePathfinderSAST : BuildType({
    name = "Code Pathfinder SAST"

    // Publish artifacts even when the build fails (e.g. --fail-on threshold hit)
    publishArtifacts = PublishMode.EVEN_IF_FAILED

    artifactRules = "pathfinder-results.sarif => security-reports"

    steps {
        script {
            scriptContent = """
                pathfinder ci \
                    --project . \
                    --ruleset python/all \
                    --fail-on critical,high \
                    --output sarif \
                    --output-file pathfinder-results.sarif
            """.trimIndent()
            dockerImage = "shivasurya/code-pathfinder:stable-latest"
        }
    }
})

SARIF viewing in TeamCity

No native SARIF viewer

TeamCity does not have a built-in SARIF viewer. Download the pathfinder-results.sarif artifact from the build page and open it in VS Code with the SARIF Viewer extension, or upload it to GitHub Code Scanning if your repository is hosted on GitHub.