From db8aea2b1511290bc2af010c1e2131e720762094 Mon Sep 17 00:00:00 2001 From: chanel-y Date: Wed, 26 Aug 2026 10:03:21 -0700 Subject: [PATCH 1/4] Expand Azure Pipelines model foundation Add structural model coverage for Azure Pipelines documents and templates, including parameters, stages, jobs, checkout/template steps, script variants, and repository/pipeline resources. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8eab11c3-3153-4036-9847-28a2f615835f --- iac/ql/lib/codeql/iac/azure/Pipelines.qll | 314 +++++++++++++++++- .../test/library-tests/azure/pipelines/AST.ql | 16 + .../azure/pipelines/azure-pipelines.yml | 52 +++ .../azure/pipelines/build-template.yml | 11 + 4 files changed, 379 insertions(+), 14 deletions(-) create mode 100644 iac/ql/test/library-tests/azure/pipelines/build-template.yml diff --git a/iac/ql/lib/codeql/iac/azure/Pipelines.qll b/iac/ql/lib/codeql/iac/azure/Pipelines.qll index 1a7269b3679b..09daf2187a6a 100644 --- a/iac/ql/lib/codeql/iac/azure/Pipelines.qll +++ b/iac/ql/lib/codeql/iac/azure/Pipelines.qll @@ -2,22 +2,43 @@ private import codeql.iac.YAML private import codeql.files.FileSystem module AzurePipelines { + private predicate hasPipelineBaseName(YamlDocument doc) { + doc.getFile().getBaseName() = ["azure-pipelines.yml", "azure-pipelines.yaml"] + } + + private predicate hasPipelineShape(YamlMapping doc) { + exists(doc.lookup("steps")) or + exists(doc.lookup("jobs")) or + exists(doc.lookup("stages")) or + exists(doc.lookup("extends")) + } + /** - * Azure DevOps Pipeline file. + * Azure DevOps Pipeline file or referenced template. */ class Document extends YamlNode, YamlDocument, YamlMapping { Document() { - // Check the filename - this.getFile().getBaseName() = ["azure-pipelines.yml", "azure-pipelines.yaml"] + this.getFile().getExtension() = ["yml", "yaml"] and + (hasPipelineBaseName(this) or hasPipelineShape(this)) } override string toString() { result = "Azure DevOps Pipeline" } + /** + * Gets a top-level trigger-like entry. + */ + YamlValue getTrigger(string name) { result = this.lookup(name) } + /** * Get the pipeline pool. */ Pool getPool() { result = this.lookup("pool") } + /** + * Gets the pipeline parameters. + */ + Parameter getParameters() { result = this.lookup("parameters").getAChild() } + /** * Get the pipeline variables. */ @@ -35,7 +56,31 @@ module AzurePipelines { /** * Get the pipeline steps. */ - Step getSteps() { result = this.lookup("steps").getAChild() } + Step getSteps() { result.getEnclosingDocument() = this } + + /** + * Gets the pipeline stages. + */ + Stage getStages() { result = this.lookup("stages").getAChild() } + + /** + * Gets the pipeline jobs. + */ + Job getJobs() { + result = this.lookup("jobs").getAChild() + or + result = this.getStages().getJobs() + } + + /** + * Gets the pipeline repository resources. + */ + RepositoryResource getRepositoryResources() { result.getEnclosingDocument() = this } + + /** + * Gets the pipeline resources. + */ + PipelineResource getPipelineResources() { result.getEnclosingDocument() = this } /** * Get the pipeline task steps. @@ -48,15 +93,114 @@ module AzurePipelines { Script getScriptSteps() { result = this.getSteps().(Script) } } + /** + * Azure DevOps Pipeline parameter. + */ + class Parameter extends YamlNode, YamlMapping { + Parameter() { exists(Document document | document.lookup("parameters").getChild(_) = this) } + + override string toString() { result = "Parameter '" + this.getName() + "'" } + + /** + * Gets the parameter name. + */ + string getName() { result = yamlToString(this.lookup("name")) } + + /** + * Gets the parameter type. + */ + string getType() { result = yamlToString(this.lookup("type")) } + + /** + * Gets the parameter default value. + */ + YamlValue getDefault() { result = this.lookup("default") } + + /** + * Gets an allowed value for the parameter. + */ + YamlValue getAllowedValue() { result = this.lookup("values").getAChild() } + } + + /** + * Azure DevOps Pipeline stage. + */ + class Stage extends YamlNode, YamlMapping { + Stage() { exists(Document document | document.lookup("stages").getAChildNode() = this) } + + override string toString() { result = "Stage '" + this.getName() + "'" } + + /** + * Gets the stage name. + */ + string getName() { result = yamlToString(this.lookup("stage")) } + + /** + * Gets a job in the stage. + */ + Job getJobs() { result = this.lookup("jobs").getAChild() } + + /** + * Gets the stage condition. + */ + YamlValue getCondition() { result = this.lookup("condition") } + } + + /** + * Azure DevOps Pipeline job. + */ + class Job extends YamlNode, YamlMapping { + Job() { + exists(Document document | document.lookup("jobs").getAChildNode() = this) + or + exists(Stage stage | stage.lookup("jobs").getAChildNode() = this) + } + + override string toString() { result = "Job '" + this.getName() + "'" } + + /** + * Gets the job name. + */ + string getName() { + result = yamlToString(this.lookup("job")) + or + result = yamlToString(this.lookup("deployment")) + } + + /** + * Gets the job pool. + */ + Pool getPool() { result = this.lookup("pool") } + + /** + * Gets a step in the job. + */ + Step getSteps() { result = this.lookup("steps").getAChild() } + + /** + * Gets the job condition. + */ + YamlValue getCondition() { result = this.lookup("condition") } + } + + /** + * Azure DevOps Pipeline deployment job. + */ + class DeploymentJob extends Job { + DeploymentJob() { exists(this.lookup("deployment")) } + } + /** * Azure DevOps Pipeline pool. * * https://learn.microsoft.com/en-us/azure/devops/pipelines/yaml-schema/pool */ class Pool extends YamlNode, YamlMapping { - private Document pipeline; - - Pool() { pipeline.lookup("pool") = this } + Pool() { + exists(Document document | document.lookup("pool") = this) + or + exists(Job job | job.lookup("pool") = this) + } /** * Get the pool name. @@ -80,9 +224,13 @@ module AzurePipelines { * https://learn.microsoft.com/en-us/azure/devops/pipelines/process/variables */ class Variable extends YamlNode, YamlMapping { - private Document document; - - Variable() { document.lookup("variables").getChild(_) = this } + Variable() { + exists(Document document | document.lookup("variables").getChild(_) = this) + or + exists(Stage stage | stage.lookup("variables").getChild(_) = this) + or + exists(Job job | job.lookup("variables").getChild(_) = this) + } override string toString() { result = "Variable '" + this.getName() + "'" } @@ -103,12 +251,23 @@ module AzurePipelines { * https://learn.microsoft.com/en-us/azure/devops/pipelines/yaml-schema/steps */ class Step extends YamlNode, YamlMapping { - private Document pipeline; - - Step() { pipeline.lookup("steps").getAChildNode() = this } + Step() { + exists(Document document | document.lookup("steps").getAChildNode() = this) + or + exists(Job job | job.lookup("steps").getAChildNode() = this) + } override string toString() { result = "Azure DevOps Pipeline step" } + /** + * Gets the enclosing Azure DevOps Pipeline document. + */ + Document getEnclosingDocument() { + exists(Document document | document.lookup("steps").getAChildNode() = this | result = document) + or + exists(Document document | this.getFile() = document.getFile() | result = document) + } + /** * Get the step display name. */ @@ -121,6 +280,16 @@ module AzurePipelines { exists(this.lookup("task")) and result = "task" or exists(this.lookup("script")) and result = "script" + or + exists(this.lookup("bash")) and result = "bash" + or + exists(this.lookup("powershell")) and result = "powershell" + or + exists(this.lookup("pwsh")) and result = "pwsh" + or + exists(this.lookup("checkout")) and result = "checkout" + or + exists(this.lookup("template")) and result = "template" } } @@ -148,6 +317,123 @@ module AzurePipelines { * Azure DevOps Pipeline script step. */ class Script extends Step { - Script() { this.getType() = "script" } + Script() { this.getType() = ["script", "bash", "powershell", "pwsh"] } + + /** + * Gets the script step kind. + */ + string getScriptKind() { result = this.getType() } + + /** + * Gets the inline script content. + */ + YamlValue getScriptContent() { result = this.lookup(this.getScriptKind()) } + } + + /** + * Azure DevOps Pipeline checkout step. + */ + class Checkout extends Step { + Checkout() { this.getType() = "checkout" } + + /** + * Gets the checkout target. + */ + string getRepository() { result = yamlToString(this.lookup("checkout")) } + + /** + * Gets the persistCredentials setting. + */ + YamlValue getPersistCredentials() { result = this.lookup("persistCredentials") } + } + + /** + * Azure DevOps Pipeline template step. + */ + class TemplateStep extends Step { + TemplateStep() { this.getType() = "template" } + + /** + * Gets the referenced template path. + */ + string getTemplate() { result = yamlToString(this.lookup("template")) } + } + + /** + * Azure DevOps repository resource. + */ + class RepositoryResource extends YamlNode, YamlMapping { + RepositoryResource() { + exists(Document document | + document.lookup("resources").(YamlMapping).lookup("repositories").getAChildNode() = this + ) + } + + override string toString() { result = "Repository resource '" + this.getAlias() + "'" } + + /** + * Gets the enclosing Azure DevOps Pipeline document. + */ + Document getEnclosingDocument() { + exists(Document document | + document.lookup("resources").(YamlMapping).lookup("repositories").getAChildNode() = this + | + result = document + ) + } + + /** + * Gets the resource alias. + */ + string getAlias() { result = yamlToString(this.lookup("repository")) } + + /** + * Gets the repository name. + */ + string getName() { result = yamlToString(this.lookup("name")) } + + /** + * Gets the referenced revision. + */ + string getRef() { result = yamlToString(this.lookup("ref")) } + } + + /** + * Azure DevOps pipeline resource. + */ + class PipelineResource extends YamlNode, YamlMapping { + PipelineResource() { + exists(Document document | + document.lookup("resources").(YamlMapping).lookup("pipelines").getAChildNode() = this + ) + } + + override string toString() { result = "Pipeline resource '" + this.getAlias() + "'" } + + /** + * Gets the enclosing Azure DevOps Pipeline document. + */ + Document getEnclosingDocument() { + exists(Document document | + document.lookup("resources").(YamlMapping).lookup("pipelines").getAChildNode() = this + | + result = document + ) + } + + /** + * Gets the resource alias. + */ + string getAlias() { result = yamlToString(this.lookup("pipeline")) } + + /** + * Gets the source pipeline. + */ + string getSource() { result = yamlToString(this.lookup("source")) } + + /** + * Gets the branch selector. + */ + string getBranch() { result = yamlToString(this.lookup("branch")) } } } diff --git a/iac/ql/test/library-tests/azure/pipelines/AST.ql b/iac/ql/test/library-tests/azure/pipelines/AST.ql index ca02be0e106d..2f558452fb1e 100644 --- a/iac/ql/test/library-tests/azure/pipelines/AST.ql +++ b/iac/ql/test/library-tests/azure/pipelines/AST.ql @@ -2,8 +2,16 @@ private import iac query predicate adopipeline(AzurePipelines::Document n) { any() } +query predicate adopipelineParameter(AzurePipelines::Parameter n) { any() } + query predicate adopipelineVariable(AzurePipelines::Variable n) { any() } +query predicate adopipelineStage(AzurePipelines::Stage n) { any() } + +query predicate adopipelineJob(AzurePipelines::Job n) { any() } + +query predicate adopipelineDeploymentJob(AzurePipelines::DeploymentJob n) { any() } + query predicate adopipelineSteps(AzurePipelines::Step n) { any() } query predicate adopipelinePool(AzurePipelines::Pool n) { any() } @@ -11,3 +19,11 @@ query predicate adopipelinePool(AzurePipelines::Pool n) { any() } query predicate adopipelineTask(AzurePipelines::Task n) { any() } query predicate adopipelineScript(AzurePipelines::Script n) { any() } + +query predicate adopipelineCheckout(AzurePipelines::Checkout n) { any() } + +query predicate adopipelineTemplateStep(AzurePipelines::TemplateStep n) { any() } + +query predicate adopipelineRepositoryResource(AzurePipelines::RepositoryResource n) { any() } + +query predicate adopipelinePipelineResource(AzurePipelines::PipelineResource n) { any() } diff --git a/iac/ql/test/library-tests/azure/pipelines/azure-pipelines.yml b/iac/ql/test/library-tests/azure/pipelines/azure-pipelines.yml index 47741c915d24..13d9493adacf 100644 --- a/iac/ql/test/library-tests/azure/pipelines/azure-pipelines.yml +++ b/iac/ql/test/library-tests/azure/pipelines/azure-pipelines.yml @@ -1,6 +1,28 @@ trigger: - main +pr: + - main + +parameters: + - name: target + type: string + default: dev + values: + - dev + - prod + +resources: + repositories: + - repository: templates + type: git + name: Shared/Templates + ref: refs/heads/main + pipelines: + - pipeline: build + source: BuildPipeline + branch: refs/heads/main + pool: vmImage: ubuntu-latest strategy: @@ -17,6 +39,13 @@ variables: value: initialValue steps: + - checkout: self + persistCredentials: true + + - template: build-template.yml + parameters: + target: ${{ parameters.target }} + - task: UsePythonVersion@0 inputs: versionSpec: "$(python.version)" @@ -31,3 +60,26 @@ steps: pip install pytest pytest-azurepipelines pytest displayName: "pytest" + +stages: + - stage: deploy + jobs: + - deployment: deploy_web + pool: + name: PrivatePool + environment: prod + strategy: + runOnce: + deploy: + steps: + - pwsh: | + ./deploy.ps1 -Target ${{ parameters.target }} + displayName: "Deploy" + +jobs: + - job: lint + pool: + vmImage: windows-latest + steps: + - bash: echo "lint" + - powershell: Write-Host "lint" diff --git a/iac/ql/test/library-tests/azure/pipelines/build-template.yml b/iac/ql/test/library-tests/azure/pipelines/build-template.yml new file mode 100644 index 000000000000..2f83ac5d0df0 --- /dev/null +++ b/iac/ql/test/library-tests/azure/pipelines/build-template.yml @@ -0,0 +1,11 @@ +parameters: + - name: target + type: string + default: dev + +steps: + - script: echo "building ${{ parameters.target }}" + - task: Bash@3 + inputs: + targetType: inline + script: echo "task script" From c51ec22f8d9a33a5d24bf2876df6753950f5e9d1 Mon Sep 17 00:00:00 2001 From: chanel-y Date: Wed, 26 Aug 2026 10:50:15 -0700 Subject: [PATCH 2/4] Exclude GitHub Actions workflows from ADO pipeline model GitHub Actions workflows share `jobs:`/`steps:` keys with Azure DevOps pipelines, so the shape-based Document match misclassified them. Exclude documents that live under .github/workflows/ or declare a top-level `on:` trigger, and regenerate the AST test expectations. Adds a github-workflow.yml regression input. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- iac/ql/lib/codeql/iac/azure/Pipelines.qll | 19 ++++++- .../azure/pipelines/AST.expected | 49 +++++++++++++++---- .../azure/pipelines/github-workflow.yml | 11 +++++ 3 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 iac/ql/test/library-tests/azure/pipelines/github-workflow.yml diff --git a/iac/ql/lib/codeql/iac/azure/Pipelines.qll b/iac/ql/lib/codeql/iac/azure/Pipelines.qll index 09daf2187a6a..50a7ac09b050 100644 --- a/iac/ql/lib/codeql/iac/azure/Pipelines.qll +++ b/iac/ql/lib/codeql/iac/azure/Pipelines.qll @@ -13,13 +13,30 @@ module AzurePipelines { exists(doc.lookup("extends")) } + /** + * Holds if `doc` is a GitHub Actions workflow rather than an Azure DevOps + * pipeline. + * + * GitHub Actions workflows live under `.github/workflows/` and are required + * to declare an `on:` trigger, whereas Azure DevOps pipelines are triggered + * with `trigger:`/`pr:` and never use a top-level `on:` key. Both formats + * share `jobs:`/`steps:` keys, so without this exclusion a workflow would be + * misclassified as a pipeline by `hasPipelineShape`. + */ + private predicate isGitHubActionsWorkflow(YamlDocument doc) { + doc.getFile().getRelativePath().matches("%.github/workflows/%") + or + exists(doc.(YamlMapping).lookup("on")) + } + /** * Azure DevOps Pipeline file or referenced template. */ class Document extends YamlNode, YamlDocument, YamlMapping { Document() { this.getFile().getExtension() = ["yml", "yaml"] and - (hasPipelineBaseName(this) or hasPipelineShape(this)) + (hasPipelineBaseName(this) or hasPipelineShape(this)) and + not isGitHubActionsWorkflow(this) } override string toString() { result = "Azure DevOps Pipeline" } diff --git a/iac/ql/test/library-tests/azure/pipelines/AST.expected b/iac/ql/test/library-tests/azure/pipelines/AST.expected index f6c44af8a897..4044767857e3 100644 --- a/iac/ql/test/library-tests/azure/pipelines/AST.expected +++ b/iac/ql/test/library-tests/azure/pipelines/AST.expected @@ -1,15 +1,46 @@ adopipeline -| azure-pipelines.yml:1:1:33:26 | Azure DevOps Pipeline | +| azure-pipelines.yml:1:1:85:38 | Azure DevOps Pipeline | +| build-template.yml:1:1:11:33 | Azure DevOps Pipeline | +adopipelineParameter +| azure-pipelines.yml:8:5:13:13 | Parameter 'target' | +| build-template.yml:2:5:4:17 | Parameter 'target' | adopipelineVariable -| azure-pipelines.yml:16:5:17:24 | Variable 'one' | +| azure-pipelines.yml:38:5:39:24 | Variable 'one' | +adopipelineStage +| azure-pipelines.yml:65:5:77:40 | Stage 'deploy' | +adopipelineJob +| azure-pipelines.yml:67:9:77:40 | Job 'deploy_web' | +| azure-pipelines.yml:80:5:85:38 | Job 'lint' | +adopipelineDeploymentJob +| azure-pipelines.yml:67:9:77:40 | Job 'deploy_web' | adopipelineSteps -| azure-pipelines.yml:20:5:25:2 | Azure DevOps Pipeline step | -| azure-pipelines.yml:25:5:30:2 | Azure DevOps Pipeline step | -| azure-pipelines.yml:30:5:33:26 | Azure DevOps Pipeline step | +| azure-pipelines.yml:42:5:45:2 | Azure DevOps Pipeline step | +| azure-pipelines.yml:45:5:49:2 | Azure DevOps Pipeline step | +| azure-pipelines.yml:49:5:54:2 | Azure DevOps Pipeline step | +| azure-pipelines.yml:54:5:59:2 | Azure DevOps Pipeline step | +| azure-pipelines.yml:59:5:62:26 | Azure DevOps Pipeline step | +| azure-pipelines.yml:84:9:85:6 | Azure DevOps Pipeline step | +| azure-pipelines.yml:85:9:85:38 | Azure DevOps Pipeline step | +| build-template.yml:7:5:8:2 | Azure DevOps Pipeline step | +| build-template.yml:8:5:11:33 | Azure DevOps Pipeline step | adopipelinePool -| azure-pipelines.yml:5:3:5:25 | vmImage ... -latest | +| azure-pipelines.yml:27:3:27:25 | vmImage ... -latest | +| azure-pipelines.yml:69:11:70:8 | name: PrivatePool | +| azure-pipelines.yml:82:7:83:4 | vmImage ... -latest | adopipelineTask -| azure-pipelines.yml:20:5:25:2 | Azure DevOps Pipeline step | +| azure-pipelines.yml:49:5:54:2 | Azure DevOps Pipeline step | +| build-template.yml:8:5:11:33 | Azure DevOps Pipeline step | adopipelineScript -| azure-pipelines.yml:25:5:30:2 | Azure DevOps Pipeline step | -| azure-pipelines.yml:30:5:33:26 | Azure DevOps Pipeline step | +| azure-pipelines.yml:54:5:59:2 | Azure DevOps Pipeline step | +| azure-pipelines.yml:59:5:62:26 | Azure DevOps Pipeline step | +| azure-pipelines.yml:84:9:85:6 | Azure DevOps Pipeline step | +| azure-pipelines.yml:85:9:85:38 | Azure DevOps Pipeline step | +| build-template.yml:7:5:8:2 | Azure DevOps Pipeline step | +adopipelineCheckout +| azure-pipelines.yml:42:5:45:2 | Azure DevOps Pipeline step | +adopipelineTemplateStep +| azure-pipelines.yml:45:5:49:2 | Azure DevOps Pipeline step | +adopipelineRepositoryResource +| azure-pipelines.yml:17:7:21:2 | Repository resource 'templates' | +adopipelinePipelineResource +| azure-pipelines.yml:22:7:24:30 | Pipeline resource 'build' | diff --git a/iac/ql/test/library-tests/azure/pipelines/github-workflow.yml b/iac/ql/test/library-tests/azure/pipelines/github-workflow.yml new file mode 100644 index 000000000000..8a9c64405d4b --- /dev/null +++ b/iac/ql/test/library-tests/azure/pipelines/github-workflow.yml @@ -0,0 +1,11 @@ +name: CI +on: + push: + branches: + - main +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: echo "build" From 824d44b25b6119e03a87902634780f46c2423a3c Mon Sep 17 00:00:00 2001 From: Chanel <102255874+chanel-y@users.noreply.github.com> Date: Mon, 14 Sep 2026 10:28:07 -0700 Subject: [PATCH 3/4] Apply batched suggestions from code review Co-authored-by: Mathias Vorreiter Pedersen --- iac/ql/lib/codeql/iac/azure/Pipelines.qll | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/iac/ql/lib/codeql/iac/azure/Pipelines.qll b/iac/ql/lib/codeql/iac/azure/Pipelines.qll index 50a7ac09b050..d7db2fc2c52e 100644 --- a/iac/ql/lib/codeql/iac/azure/Pipelines.qll +++ b/iac/ql/lib/codeql/iac/azure/Pipelines.qll @@ -42,7 +42,7 @@ module AzurePipelines { override string toString() { result = "Azure DevOps Pipeline" } /** - * Gets a top-level trigger-like entry. + * Gets the top-level trigger-like entry named `name`, if any. */ YamlValue getTrigger(string name) { result = this.lookup(name) } @@ -280,9 +280,9 @@ module AzurePipelines { * Gets the enclosing Azure DevOps Pipeline document. */ Document getEnclosingDocument() { - exists(Document document | document.lookup("steps").getAChildNode() = this | result = document) + result.lookup("steps").getAChildNode() = this or - exists(Document document | this.getFile() = document.getFile() | result = document) + this.getFile() = result.getFile() } /** @@ -392,11 +392,7 @@ module AzurePipelines { * Gets the enclosing Azure DevOps Pipeline document. */ Document getEnclosingDocument() { - exists(Document document | - document.lookup("resources").(YamlMapping).lookup("repositories").getAChildNode() = this - | - result = document - ) + result.lookup("resources").(YamlMapping).lookup("repositories").getAChildNode() = this } /** @@ -431,11 +427,7 @@ module AzurePipelines { * Gets the enclosing Azure DevOps Pipeline document. */ Document getEnclosingDocument() { - exists(Document document | - document.lookup("resources").(YamlMapping).lookup("pipelines").getAChildNode() = this - | - result = document - ) + result.lookup("resources").(YamlMapping).lookup("pipelines").getAChildNode() = this } /** From 36077dc205551aa63c3dc9cc5dbe00800fe989f9 Mon Sep 17 00:00:00 2001 From: chanelyoung Date: Mon, 14 Sep 2026 10:59:58 -0700 Subject: [PATCH 4/4] update get predicates to align with qldoc standards, update toString instances to use getName --- iac/ql/lib/codeql/iac/azure/Pipelines.qll | 99 ++++++++++++----------- 1 file changed, 51 insertions(+), 48 deletions(-) diff --git a/iac/ql/lib/codeql/iac/azure/Pipelines.qll b/iac/ql/lib/codeql/iac/azure/Pipelines.qll index d7db2fc2c52e..f5b029b95ec7 100644 --- a/iac/ql/lib/codeql/iac/azure/Pipelines.qll +++ b/iac/ql/lib/codeql/iac/azure/Pipelines.qll @@ -47,67 +47,67 @@ module AzurePipelines { YamlValue getTrigger(string name) { result = this.lookup(name) } /** - * Get the pipeline pool. + * Gets the pipeline pool, if any. */ Pool getPool() { result = this.lookup("pool") } /** - * Gets the pipeline parameters. + * Gets a pipeline parameter, if any. */ - Parameter getParameters() { result = this.lookup("parameters").getAChild() } + Parameter getAParameter() { result = this.lookup("parameters").getAChild() } /** - * Get the pipeline variables. + * Gets a pipeline variable, if any. */ - Variable getVariables() { result = this.lookup("variables").getAChild() } + Variable getAVariable() { result = this.lookup("variables").getAChild() } /** - * Get the pipeline variable with the given name. + * Gets the pipeline variable with the given name, if any. */ YamlValue getVariable(string name) { - exists(Variable var | var = this.getVariables() and var.getName() = name | + exists(Variable var | var = this.getAVariable() and var.getName() = name | result = var.getValue() ) } /** - * Get the pipeline steps. + * Gets a pipeline step, if any. */ - Step getSteps() { result.getEnclosingDocument() = this } + Step getAStep() { result.getEnclosingDocument() = this } /** - * Gets the pipeline stages. + * Gets a pipeline stage, if any. */ - Stage getStages() { result = this.lookup("stages").getAChild() } + Stage getAStage() { result = this.lookup("stages").getAChild() } /** - * Gets the pipeline jobs. + * Gets a pipeline job, if any. */ - Job getJobs() { + Job getAJob() { result = this.lookup("jobs").getAChild() or - result = this.getStages().getJobs() + result = this.getAStage().getAJob() } /** - * Gets the pipeline repository resources. + * Gets a pipeline repository resource, if any. */ - RepositoryResource getRepositoryResources() { result.getEnclosingDocument() = this } + RepositoryResource getARepositoryResource() { result.getEnclosingDocument() = this } /** - * Gets the pipeline resources. + * Gets a pipeline resource, if any. */ - PipelineResource getPipelineResources() { result.getEnclosingDocument() = this } + PipelineResource getAPipelineResource() { result.getEnclosingDocument() = this } /** - * Get the pipeline task steps. + * Gets a pipeline task step, if any. */ - Task getTaskSteps() { result = this.getSteps().(Task) } + Task getATaskStep() { result = this.getAStep().(Task) } /** - * Get the pipeline script steps. + * Gets a pipeline script step, if any. */ - Script getScriptSteps() { result = this.getSteps().(Script) } + Script getAScriptStep() { result = this.getAStep().(Script) } } /** @@ -116,7 +116,7 @@ module AzurePipelines { class Parameter extends YamlNode, YamlMapping { Parameter() { exists(Document document | document.lookup("parameters").getChild(_) = this) } - override string toString() { result = "Parameter '" + this.getName() + "'" } + override string toString() { result = this.getName() } /** * Gets the parameter name. @@ -124,19 +124,19 @@ module AzurePipelines { string getName() { result = yamlToString(this.lookup("name")) } /** - * Gets the parameter type. + * Gets the parameter type, if any. */ string getType() { result = yamlToString(this.lookup("type")) } /** - * Gets the parameter default value. + * Gets the parameter default value, if any. */ YamlValue getDefault() { result = this.lookup("default") } /** - * Gets an allowed value for the parameter. + * Gets an allowed value for the parameter, if any. */ - YamlValue getAllowedValue() { result = this.lookup("values").getAChild() } + YamlValue getAnAllowedValue() { result = this.lookup("values").getAChild() } } /** @@ -145,7 +145,7 @@ module AzurePipelines { class Stage extends YamlNode, YamlMapping { Stage() { exists(Document document | document.lookup("stages").getAChildNode() = this) } - override string toString() { result = "Stage '" + this.getName() + "'" } + override string toString() { result = this.getName() } /** * Gets the stage name. @@ -153,12 +153,12 @@ module AzurePipelines { string getName() { result = yamlToString(this.lookup("stage")) } /** - * Gets a job in the stage. + * Gets a job in the stage, if any. */ - Job getJobs() { result = this.lookup("jobs").getAChild() } + Job getAJob() { result = this.lookup("jobs").getAChild() } /** - * Gets the stage condition. + * Gets the stage condition, if any. */ YamlValue getCondition() { result = this.lookup("condition") } } @@ -173,7 +173,7 @@ module AzurePipelines { exists(Stage stage | stage.lookup("jobs").getAChildNode() = this) } - override string toString() { result = "Job '" + this.getName() + "'" } + override string toString() { result = this.getName() } /** * Gets the job name. @@ -185,17 +185,17 @@ module AzurePipelines { } /** - * Gets the job pool. + * Gets the job pool, if any. */ Pool getPool() { result = this.lookup("pool") } /** - * Gets a step in the job. + * Gets a step in the job, if any. */ - Step getSteps() { result = this.lookup("steps").getAChild() } + Step getAStep() { result = this.lookup("steps").getAChild() } /** - * Gets the job condition. + * Gets the job condition, if any. */ YamlValue getCondition() { result = this.lookup("condition") } } @@ -220,19 +220,19 @@ module AzurePipelines { } /** - * Get the pool name. + * Gets the pool name, if any. */ string getName() { result = yamlToString(this.lookup("name")) } /** - * Get the pool VM image. + * Gets the pool VM image, if any. */ string getVmImage() { result = yamlToString(this.lookup("vmImage")) } /** - * Get the pool demands. + * Gets the pool demands, if any. */ - string getDemands() { result = yamlToString(this.lookup("demands")) } + string getADemand() { result = yamlToString(this.lookup("demands")) } } /** @@ -249,7 +249,7 @@ module AzurePipelines { exists(Job job | job.lookup("variables").getChild(_) = this) } - override string toString() { result = "Variable '" + this.getName() + "'" } + override string toString() { result = this.getName() } /** * Get the variable name. @@ -257,7 +257,7 @@ module AzurePipelines { string getName() { result = yamlToString(this.lookup("name")) } /** - * Get the variable value. + * Gets the variable value, if any. */ YamlValue getValue() { result = this.lookup("value") } } @@ -274,7 +274,7 @@ module AzurePipelines { exists(Job job | job.lookup("steps").getAChildNode() = this) } - override string toString() { result = "Azure DevOps Pipeline step" } + override string toString() { result = this.getDisplayName() } /** * Gets the enclosing Azure DevOps Pipeline document. @@ -286,12 +286,12 @@ module AzurePipelines { } /** - * Get the step display name. + * Gets the step display name, if any. */ - string displayName() { result = yamlToString(this.lookup("displayName")) } + string getDisplayName() { result = yamlToString(this.lookup("displayName")) } /** - * Get the step type based on the presence of a `task` or `script` key. + * Gets the step type based on its defining key, if any. */ string getType() { exists(this.lookup("task")) and result = "task" @@ -327,6 +327,9 @@ module AzurePipelines { TaskInputs() { task.lookup("inputs") = this } + /** + * Gets the input named `name`, if any. + */ YamlValue getInput(string name) { result = this.lookup(name) } } @@ -359,7 +362,7 @@ module AzurePipelines { string getRepository() { result = yamlToString(this.lookup("checkout")) } /** - * Gets the persistCredentials setting. + * Gets the `persistCredentials` setting, if any. */ YamlValue getPersistCredentials() { result = this.lookup("persistCredentials") } } @@ -441,7 +444,7 @@ module AzurePipelines { string getSource() { result = yamlToString(this.lookup("source")) } /** - * Gets the branch selector. + * Gets the branch selector */ string getBranch() { result = yamlToString(this.lookup("branch")) } }