# 扫描语言说明

# 【C#】

C#项目扫描需要对项目进行编译。根据C#项目所使用的.Net框架版本不同,扫描环境有一定区别和要求:

1, 若采用的是经典.Net Framework框架开发(如微软已经停止维护的.Net Framework4.6或者更低的版本, 不支持跨平台)扫描环境要求采用Windows主机模式进行扫描,具体说明可参考帮助文档【使用说明 > 扫描.Net Framework项目

2, 若采用.Net5.0或6.0框架开发,则扫描环境默认支持Docker模式(扫描器镜像已内置.Net5.0, 6.0环境,扫描时计算资源选择K8s集群即可),具体sdk版本如下:

   sdk 5.0.408
   sdk 6.0.100
  • 由于扫描时需要对项目进行编译,系统会默认采用dotnet build xxx.sln指令进行编译(若是基于Windows系统的.Net Framework项目默认编译指令是MSBuild.exe xxx.sln /t:Rebuild)。

  • 默认指令可能编译失败,若需根据自己的项目自定义编译指令,可以在项目根目录自己添加一个命名为scan.build的文件并写入自定义编译指令。

    根目录中存在这个文件,扫描程序则不会再执行默认指令,而执行文件中自定义的指令。文件里面的指令内容可以比较灵活,比如先调用一个封装好的脚本或程序来执行一些编译准备工作,然后再执行项目的编译指令。

3, 若采用的是.Net Core更低的框架开发,目前扫描环境暂时还不支持,若有需求可以联系平台方寻求支持。

下图可以帮助理解不同.Net版本之间扫描环境的区别: An image

# 【C++】

在扫描配置页高级配置中,支持宏相关的扫描参数配置,demo如下:\

[tool.cppcheck]
# 定义的宏,多个宏用英文逗号分隔 
def=宏1,宏2,...
# 配合def使用,且前提是def配置内容不为空,可选值为true/false。 若为true将使用--force参数扫描,表示定义了宏X时,检测所有配置,反之只检查配置X 
def_force=true
# 未定义的宏,多个宏用英文逗号分隔
ndef=宏3,宏4,...

# 【Objective-C】

基于OCLint、Infer等工具进行编译型代码扫描,扫描基本原理:根据项目编译过程中产生的编译日志生成compile_commands.json,结合编译日志进行分析。

# - 扫描环境准备

1, 扫描过程依赖项目编译环境,所以需要基于macOS主机模式进行扫描,可以将macOS开发机接入平台作为计算资源或者也可以复用流水线编译构建任务计算资源。

2, macOS机器作为计算资源接入后,下载扫描工具包(命令中的域名地址已脱敏,请替换命令中的地址),然后解压到指定目录下: /Applications/Scan/

下载命令:curl -o scanner_macOS_amd64.tar "https://{此处替换平台Saas版地址}/pkg/scan/raw/file/snapshot?version=v1.0.0&pkgName=scan/scanner_macOS_amd64" -k -u scan:55804412da4f4ba4bcfb4e0fb418bd8a1672909786203

解压后在/Applications/Scan/下会有两个子目录:scanner和tools

  • /Applications/Scan/scanner目录下是扫描终端程序 (基于python3.7.8 macOS amd64环境打包)
  • /Applications/Scan/tools目录下是依赖的扫描工具 (其中pmd进行代码重复分析,依赖Java8以上运行环境)

# - 编译指令说明

  • 项目中若没有自定义的编译指令,扫描工具将采用系统默认指令编译项目并进行分析:
xcodebuild clean && xcodebuild build | xcpretty -r json-compilation-database -o compile_commands.json
  • 系统默认指令可能编译项目失败从而无法继续分析,可以在项目根目录下添加命名为scan.build的文件(注意是在根目录添加),
    里面写入当前项目的编译指令,在扫描时将根据自定义指令进行编译扫描。如果待编译的项目在子目录下,下面自定义的命令请先cd到子目录。
    具体的编译参数请根据项目实际情况进行设置,注意要先执行clean,一条指令占一行,例如:
xcodebuild [编译参数...] clean
xcodebuild [编译参数...] build | xcpretty -r json-compilation-database -o compile_commands.json

说明: 分析的前提是编译,如果待扫描的文件不在编译范围内,该文件将不会被分析。
举例:用户编辑了代码TestApp/test.m,触发了增量扫描任务,但是由于项目编译指令指定的Target并没有编译当前文件,此时该文件将不会被分析。即使该文件中存在代码问题,扫描结果中也不会扫出这些问题。

# - 触发扫描

在代码库扫描配置中(流水线扫描插件配置类似),计算资源请选择"主机集群",自定义集群选择上面已接入的macOS计算资源,并勾选Xcode项目参数

# - 补充说明

  • Xcode项目目前仅扫描.m,.mm文件,项目中的其他文件如.py,.js将不会进行缺陷分析。
  • 目前主机编译扫描尚不支持对组件进行分析,在扫描配置页开启组件漏洞和组件许可证扫描两个选项也不会生效。
  • 当前集成的OCLint 22.02工具版本,在Xcode14.3,15项目扫描时存在官方已知的问题:oclint: error: compilation contains multiple jobs, 需等待OCLint官方升级
  • 若扫描构建日志中有错误信息:oclint: error: cannot open dynamic library: xxx,可能是程序被系统安全拦截了,需开启信任,先执行命令sudo spctl --master-disable,然后在安全与隐私中选择信任来源

# 【Java】

平台支持对Java项目进行源码扫描或编译扫描。

  • 编译扫描与流水线编译构建插件做了集成,需要在插件配置中开启编译产出扫描开关(参考SpotBugs扫描)
  • 源码扫描在扫描设置页进行常规配置即可,若需要指定Java JDK的版本可在扫描设置页右侧高级配置中调整参数:
[tool.sonar]
# 说明: 扫描Java项目时采用的JDK版本,扫描环境中目前内置11和17(默认采用11),若要使用17请修改参数并取消注释
java.source=11

# 【Android】

代码扫描服务是基于编程语言来管理扫描规则的,Android实际上并不是一门语言(Android项目采用Java或Kotlin语言进行开发),所以在扫描规则列表页并没有单独列出"Android语言"。

扫描Android项目时根据使用的开发语言进行对应的配置即可:

1, 如果使用Java开发,可以使用Java规则进行扫描,或者在流水线Java编译构建插件中开启编译产出扫描(会使用SpotBugs对编译产出jar包进行扫描)

2, 如果使用Kotlin开发,可以使用Kotlin规则进行扫描

3, 扫描环境中集成了AndroidLint (为方便规则管理,AndroidLint规则被默认归类在了Java语言下), 支持对Java和Kotlin以及Android资源、配置文件进行分析,扫描时系统会自动识别语言进行工具调度扫描。

AndroidLint支持高级配置项,可在扫描配置页 > 高级配置中进行设置:

[tool.android_lint]
# 说明: 扫描时已经默认开启检查的问题分类,英文逗号分割。
#enable_issue_categories=Correctness,Security,Performance,Productivity,Usability,Accessibility
# 说明: 针对已经开启的问题分类,忽略某些具体问题的规则,扫描时将不会再检查这些问题。所有的规则可通过lint --list命令查看,或者在浏览器控制台查看某个规则的key
#ignore_issue_rules=UnusedResources,MissingPrefix

AndroidLint支持的所有问题分类如下,也可以通过lint --list命令查看

Valid issue categories:
    Correctness
    Correctness:Messages
    Correctness:Chrome OS
    Security
    Compliance
    Performance
    Performance:Application Size
    Usability:Typography
    Usability:Icons
    Usability
    Productivity
    Accessibility
    Internationalization
    Internationalization:Bidirectional Text
    Testing
    Interoperability
    Interoperability:Kotlin Interoperability
    Lint Implementation Issues

# 【TypeScript】

TypeScript项目扫描工具采用ESLint+TypeScript插件,由于ESLint支持在代码根目录中指定配置文件(声明使用的解析器,插件,规则开启、关闭以及参数设置等),平台在扫描时也会遵循这一特性,并且会结合关联的规则集处理扫描结果,最终的扫描结果是配置文件规则与规则集中规则的交集。

# - 扫描配置策略

  • 若项目根目录中已经有ESLint扫描配置文件,形如:.eslintrc.js, .eslintrc.cjs, .eslintrc.json, .eslintrc, 则平台在扫描时会优先采用代码库中的配置文件进行扫描,注意:如果配置文件不正确或者指定的插件在扫描器镜像中并未安装,则有可能会引起扫描失败。

  • 若项目根目录中未指定上述扫描配置文件,则平台会使用内置的基本配置进行扫描,基本配置内容见下方。

  • 配置文件优先级>规则集优先级:若扫描时关联的规则集中包含某条规则A,但是配置文件中并未开启该规则A,则不会扫出该规则对应的代码问题。

# - 结果处理策略

  • 最终的扫描结果会基于ESLint扫描的原始结果结合关联的规则集进行二次过滤处理:若配置文件中开启了规则A并且也扫描出了代码问题, 但是扫描时关联的规则集中并不包含该规则,则平台在处理原始扫描结果时也会过滤掉这类代码问题。

内置基本扫描配置:每条规则的说明可以参考官方文档: https://typescript-eslint.io/rules/

module.exports = {
  root: true,
  parser: '@typescript-eslint/parser',
  plugins: [
    '@typescript-eslint',
  ],
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
  ],
//  rules: {
//    //note you must disable the base rule as it can report incorrect errors
//    //重载ESLint的扩展规则,要先关闭ESLint同名规则才能生效
//    //"brace-style": "off",
//    //"@typescript-eslint/brace-style": ["error"],
//
//    //"comma-dangle": "off",
//    //"@typescript-eslint/comma-dangle": ["error"],
//
//    //"comma-spacing": "off",
//    //"@typescript-eslint/comma-spacing": ["error"],
//
//    //"default-param-last": "off",
//    //"@typescript-eslint/default-param-last": ["error"],
//
//    //"dot-notation": "off",
//    //"@typescript-eslint/dot-notation": ["error"], // need parse options
//
//    //"func-call-spacing": "off",
//    //"@typescript-eslint/func-call-spacing": ["error"],
//
//    //"indent": "off",
//    //"@typescript-eslint/indent": ["error"],
//
//    //"init-declarations": "off",
//    //"@typescript-eslint/init-declarations": ["error"],
//
//    //"keyword-spacing": "off",
//    //"@typescript-eslint/keyword-spacing": ["error"],
//
//    //"lines-between-class-members": "off",
//    //"@typescript-eslint/lines-between-class-members": ["error"],
//
//    //"no-array-constructor": "off",
//    //"@typescript-eslint/no-array-constructor": ["error"],
//
//    //"no-dupe-class-members": "off",
//    //"@typescript-eslint/no-dupe-class-members": ["error"],
//
//    //"no-duplicate-imports": "off",
//    //"@typescript-eslint/no-duplicate-imports": ["error"], // deprecated
//
//    //"no-empty-function": "off",
//    //"@typescript-eslint/no-empty-function": ["error"],
//
//    //"no-extra-parens": "off",
//    //"@typescript-eslint/no-extra-parens": ["error"],
//
//    //"no-extra-semi": "off",
//    //"@typescript-eslint/no-extra-semi": ["error"],
//
//    //"no-implied-eval": "off",
//    //"@typescript-eslint/no-implied-eval": ["error"],
//
//    //"no-invalid-this": "off",
//    //"@typescript-eslint/no-invalid-this": ["error"],
//
//    //"no-loop-func": "off",
//    //"@typescript-eslint/no-loop-func": ["error"],
//
//    //"no-loss-of-precision": "off",
//    //"@typescript-eslint/no-loss-of-precision": ["error"],
//
//    //"no-magic-numbers": "off",
//    //"@typescript-eslint/no-magic-numbers": ["error"],
//
//    //"no-redeclare": "off",
//    //"@typescript-eslint/no-redeclare": ["error"],
//
//    //"no-restricted-imports": "off",
//    //"@typescript-eslint/no-restricted-imports": ["error"],
//
//    //"no-shadow": "off",
//    //"@typescript-eslint/no-shadow": ["error"],
//
//    //"no-throw-literal": "off",
//    //"@typescript-eslint/no-throw-literal": ["error"],
//
//    //"no-unused-expressions": "off",
//    //"@typescript-eslint/no-unused-expressions": ["error"],
//
//    //"no-unused-vars": "off",
//    //"@typescript-eslint/no-unused-vars": ["error"],
//
//    //"no-use-before-define": "off",
//    //"@typescript-eslint/no-use-before-define": ["error"],
//
//    //"no-useless-constructor": "off",
//    //"@typescript-eslint/no-useless-constructor": ["error"],
//
//    //"object-curly-spacing": "off",
//    //"@typescript-eslint/object-curly-spacing": ["error"],
//
//    //"padding-line-between-statements": "off",
//    //"@typescript-eslint/padding-line-between-statements": [
//    //    "error",
//    //    {
//    //      "blankLine": "always",
//    //      "prev": "*",
//    //      "next": ["interface", "type"]
//    //    }
//    //],
//
//    //"quotes": "off",
//    //"@typescript-eslint/quotes": ["error"],
//
//    //"require-await": "off",
//    //"@typescript-eslint/require-await": "error",
//
//    //"no-return-await": "off",
//    //"@typescript-eslint/return-await": "error",
//
//    //"semi": "off",
//    //"@typescript-eslint/semi": ["error"],
//
//    //"space-before-blocks": "off",
//    //"@typescript-eslint/space-before-blocks": ["error"],
//
//    //"space-before-function-paren": "off",
//    //"@typescript-eslint/space-before-function-paren": ["error"],
//
//    //"space-infix-ops": "off",
//    //"@typescript-eslint/space-infix-ops": ["error", { "int32Hint": false }]
//  }
};

# 【PL/SQL】

PL/SQL支持的文件后缀:.sql,.pkg,.pks,.pkb,.fun,.pcd,.tgg,.prc,.tpb,.trg,.typ,.tab,.tps

其他的SQL文件(如: Mysql, T-SQL, PostgreSql)文件后缀: .sql

注意: 由于不同语法的文件后缀都可能是.sql,目前扫描工具尚不能对sql dialect进行自动区分, 若关联的规则集中包含PL/SQL规则,扫描时有可能引起Mysql .sql文件语法解析错误从而产生大量误报。

解决方案:非PL/SQL项目在扫描项目中的.sql文件时,指定的规则集中不要包含PL/SQL规则,此时普通sql文件不会当作PL/SQL进行扫描.

说明:

  • 在系统生成的默认全量规则集中不包含SQL,PL/SQL相关的规则, 用户可根据项目实际情况在规则集中自己勾选适合的规则进行扫描,

  • 若规则集中同时选择了PL/SQL规则和常规的SQL规则,此时系统只会采用常规SQL规则进行扫描,目前默认仅支持Mysql dialect