What language is this for?
TypeScript
Which rule?
The relevant rule should be related to detecting SQL Injection vulnerabilities in database queries. I believe this falls under security hotspot or vulnerability detection related to dynamically constructed SQL queries.
Why do you believe it’s a false-positive/false-negative?
False-negative:
SonarQube should ideally detect and flag SQL injection in cases where untrusted input (e.g., workflowDefinitionId
) is directly concatenated into the query string without input sanitization or parameterized queries. Here’s why I believe this is a false-negative:
- The
workflowDefinitionId
comes directly from a function argument and is concatenated without validation or escaping. - The query is dynamically constructed, making it vulnerable to SQL injection.
- This is a classic SQL injection pattern that is commonly flagged by static analyzers and security tools.
SonarQube Server
Version: SonarQube v2025.1 Developer Edition
How can we reproduce the problem?
You can reproduce this by scanning a TypeScript project containing the following function:
async getWorkflowDefinition(context: ContextModel, workflowDefinitionId: string): Promise<WorkflowDefinition> {
const client = await getReadClient();
const result = await client.query(`SELECT * FROM "${context.clientId}".workflow_definitions WHERE id = ${workflowDefinitionId}`);
return toCamelCase(workflow.rows[0]);
}
Scanner context
Plugins:
Bundled analyzers:
- JaCoCo 1.3.0.1538 (jacoco)
- IaC Code Quality and Security 1.41.0.14206 (iacenterprise)
- IaC Code Quality and Security 1.41.0.14206 (iac)
- Text Code Quality and Security 2.20.0.5038 (textdeveloper)
- Clean as You Code 2.4.0.2018 (cayc)
Global server settings:
- sonar.abap.file.suffixes=.abap,.ab4,.flow,.asprog
- sonar.auth.github.appId=238585
- sonar.auth.github.enabled=true
- sonar.auth.github.organizations=....
- sonar.auth.saml.enabled=false
- sonar.auth.saml.loginUrl=.......
- sonar.azureresourcemanager.file.suffixes=.bicep
- sonar.c.file.suffixes=.c,.h
- sonar.core.id=.......
- sonar.core.serverBaseURL=https://..../
- sonar.core.startTime=2025-01-30T16:44:14+0000
- sonar.cpp.file.suffixes=.cc,.cpp,.cxx,.c++,.hh,.hpp,.hxx,.h++,.ipp,.ixx,.mxx,.cppm,.ccm,.cxxm,.c++m
- sonar.cs.file.suffixes=.cs,.razor
- sonar.css.file.suffixes=.css,.less,.scss,.sass
- sonar.dart.file.suffixes=.dart
- sonar.dbcleaner.branchesToKeepWhenInactive=master,develop,rc/*
- sonar.dbcleaner.daysBeforeDeletingAnticipatedTransitions=7
- sonar.dbcleaner.daysBeforeDeletingClosedIssues=7
- sonar.dbcleaner.daysBeforeDeletingInactiveBranchesAndPRs=5
- sonar.dbcleaner.weeksBeforeDeletingAllSnapshots=53
- sonar.dbcleaner.weeksBeforeKeepingOnlyOneSnapshotByWeek=2
- sonar.docker.file.patterns=Dockerfile,*.dockerfile
- sonar.flex.file.suffixes=as
- sonar.forceAuthentication=true
- sonar.global.exclusions=/src/tests,**/*.spec.ts,@types/*,**/.storybook,public/*,uitests/*,**/*.html,**/*.css
- sonar.global.test.exclusions=*.spec.ts,*.spec.js,**/tests/*,**/uitests/*
- sonar.go.file.suffixes=.go
- sonar.html.file.suffixes=.html,.xhtml,.cshtml,.vbhtml,.aspx,.ascx,.rhtml,.erb,.shtm,.shtml,.cmp,.twig
- sonar.ipynb.file.suffixes=ipynb
- sonar.java.file.suffixes=.java,.jav
- sonar.java.jvmframeworkconfig.file.patterns=**/src/main/resources/**/*app*.properties,**/src/main/resources/**/*app*.yaml,**/src/main/resources/**/*app*.yml
- sonar.javascript.file.suffixes=.js,.jsx,.cjs,.mjs,.vue
- sonar.json.file.suffixes=.json
- sonar.jsp.file.suffixes=.jsp,.jspf,.jspx
- sonar.kotlin.file.suffixes=.kt,.kts
- sonar.lf.enableGravatar=true
- sonar.lf.logoUrl=https://...../images/...../u4027.png
- sonar.multi-quality-mode.enabled=true
- sonar.objc.file.suffixes=.m
- sonar.php.file.suffixes=php,php3,php4,php5,phtml,inc
- sonar.plsql.file.suffixes=sql,pks,pkb
- sonar.plugins.risk.consent=ACCEPTED
- sonar.projectCreation.mainBranchName=develop
- sonar.python.file.suffixes=py
- sonar.qualityProfiles.allowDisableInheritedRules=false
- sonar.qualitygate.ignoreSmallChanges=false
- sonar.ruby.file.suffixes=.rb
- sonar.scala.file.suffixes=.scala
- sonar.swift.file.suffixes=.swift
- sonar.technicalDebt.developmentCost=15
- sonar.terraform.file.suffixes=.tf
- sonar.tsql.file.suffixes=.tsql
- sonar.typescript.file.suffixes=.ts,.tsx,.cts,.mts
- sonar.vbnet.file.suffixes=.vb
- sonar.xml.file.suffixes=.xml,.xsd,.xsl,.config
- sonar.yaml.file.suffixes=.yaml,.yml
Project server settings:
- sonar.abap.file.suffixes=.abap,.ab4,.flow,.asprog
- sonar.azureresourcemanager.file.suffixes=.bicep
- sonar.c.file.suffixes=.c,.h
- sonar.cpp.file.suffixes=.cc,.cpp,.cxx,.c++,.hh,.hpp,.hxx,.h++,.ipp,.ixx,.mxx,.cppm,.ccm,.cxxm,.c++m
- sonar.cs.file.suffixes=.cs,.razor
- sonar.css.file.suffixes=.css,.less,.scss,.sass
- sonar.dart.file.suffixes=.dart
- sonar.docker.file.patterns=Dockerfile,*.dockerfile
- sonar.flex.file.suffixes=as
- sonar.go.file.suffixes=.go
- sonar.html.file.suffixes=.html,.xhtml,.cshtml,.vbhtml,.aspx,.ascx,.rhtml,.erb,.shtm,.shtml,.cmp,.twig
- sonar.ipynb.file.suffixes=ipynb
- sonar.java.file.suffixes=.java,.jav
- sonar.java.jvmframeworkconfig.file.patterns=**/src/main/resources/**/*app*.properties,**/src/main/resources/**/*app*.yaml,**/src/main/resources/**/*app*.yml
- sonar.javascript.file.suffixes=.js,.jsx,.cjs,.mjs,.vue
- sonar.json.file.suffixes=.json
- sonar.jsp.file.suffixes=.jsp,.jspf,.jspx
- sonar.kotlin.file.suffixes=.kt,.kts
- sonar.objc.file.suffixes=.m
- sonar.php.file.suffixes=php,php3,php4,php5,phtml,inc
- sonar.plsql.file.suffixes=sql,pks,pkb
- sonar.python.file.suffixes=py
- sonar.ruby.file.suffixes=.rb
- sonar.scala.file.suffixes=.scala
- sonar.swift.file.suffixes=.swift
- sonar.terraform.file.suffixes=.tf
- sonar.tsql.file.suffixes=.tsql
- sonar.typescript.file.suffixes=.ts,.tsx,.cts,.mts
- sonar.vbnet.file.suffixes=.vb
- sonar.xml.file.suffixes=.xml,.xsd,.xsl,.config
- sonar.yaml.file.suffixes=.yaml,.yml
Project scanner properties:
- sonar.coverage.exclusions=src/**/*.spec.ts,**/*.test.ts,node_modules/*,reports/*
- sonar.eslint.eslintconfigpath=./.eslintrc.cjs
- sonar.eslint.reportPaths=reports/eslint.json
- sonar.exclusions=src/types/**/*,src/schema/**/*,src/seed/**/*,src/models/**/*,src/handlers/migrations/*,src/proxies/**/*.ts,src/docs.ts,src/app.ts,src/handlers/local-migrations.ts,src/handlers/**/*.ts,src/shared/**/*.ts,src/middleware/**/*.ts,src/utils/**/*.ts,src/helpers/**/*.ts,src/db/**/*.ts,src/config.ts,src/migrations.ts,src/services/events/**/*.ts
- sonar.host.url=https://....../
- sonar.javascript.file.suffixes=.js
- sonar.language=ts
- sonar.links.issue=https://github.com/......./issues
- sonar.links.scm=https://github.com/......
- sonar.organization=.....
- sonar.projectBaseDir=/github/workspace
- sonar.projectKey=workflow-triggers
- sonar.pullrequest.github.summary_comment=true
- sonar.qualitygate.wait=false
- sonar.scanner.apiBaseUrl=https://....../api/v2
- sonar.scanner.app=ScannerCLI
- sonar.scanner.appVersion=6.2.1.4610
- sonar.scanner.arch=x86_64
- sonar.scanner.bootstrapStartTime=1738763331495
- sonar.scanner.home=/opt/sonar-scanner
- sonar.scanner.os=linux
- sonar.scanner.wasEngineCacheHit=false
- sonar.scanner.wasJreCacheHit=MISS
- sonar.sourceEncoding=UTF-8
- sonar.sources=./src
- sonar.testExecutionReportPaths=./reports/test-report.xml
- sonar.tests=test/unit
- sonar.token=******
- sonar.typescript.file.suffixes=.ts
- sonar.typescript.lcov.reportPaths=./reports/coverage/lcov.info
- sonar.typescript.tsconfigPath=tsconfig.json
- sonar.userHome=/opt/sonar-scanner/.sonar
- sonar.working.directory=/github/workspace/.scannerwork