# 扫描语言说明

# 【C#】

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

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

2, 若采用.Net Core3.1, .Net5.0或6.0框架开发,则扫描环境默认支持Docker模式扫描(扫描器镜像已内置Core3.1, .Net5.0, 6.0环境),具体sdk版本如下:

   sdk 3.1.423
   sdk 5.0.408
   sdk 6.0.100

由于扫描时需要对项目进行编译,系统会默认采用dotnet build xxx.sln指令进行编译。默认指令可能编译失败,若需自定义编译指令,可以在项目根目录添加build.ezscan文件写入自定义编译指令。

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

# 【C++】

本平台支持对C++进行代码扫描时配置宏,具体操作如下:
1、在被检项目的根目录中引入一个scan.settings文件
2、在setting文件中写入自定义配置项,下图scan.settings配置文件demo

[tool.cppcheck]
def=宏1,宏2,...
def_force=true
ndef=宏3,宏4,...

3、具体配置项说明:
3.1、[]来表示自定义配置的section:包括tool, language, rule, ruleset等,本次仅开放tool.cppcheck配置,其他作为预留。section名称可忽略大小写
3.2、[tool.cppcheck]目前开放的自定义配置项,变量名需和说明保持一致:

  • def: 定义的宏,多个宏用英文逗号分隔
  • ef_force: 配合def使用,且前提是def配置内容不为空,可选值为true/false。 若为true将使用--force参数扫描,表示定义了宏X时,检测所有配置,反之只检查配置X
  • ndef:未定义的宏,多个宏用英文逗号分隔

# 【Objective-C】

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

# - 扫描环境准备

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

2, macOS机器作为计算资源接入后,下载扫描工具包解压到指定目录下: /Applications/ezscan/

下载命令:curl -o scanner_macOS_amd64.tar "https://ezone.work/pkg/ezscan/raw/file/snapshot?version=v1.0.0&pkgName=ezscan/scanner_macOS_amd64" -k -u ezscan:3e2e0cf2188347738d1f33819e3853b81652426652079

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

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

# - 编译指令说明

  • 项目中若没有自定义的编译指令,扫描工具将采用系统默认指令编译项目并进行分析:
xcodebuild clean; xcodebuild build | xcpretty -r json-compilation-database -o compile_commands.json
  • 系统默认指令可能编译项目失败从而无法继续分析,可以在项目根目录下添加命名为build.ezscan的文件,里面写入当前项目的编译指令,在扫描时将根据自定义指令进行编译扫描。

具体的编译参数请根据项目实际情况进行设置,注意要先执行clean,一条指令占一行,例如:

xcodebuild [编译参数...] clean
xcodebuild [编译参数...] | xcpretty -r json-compilation-database -o compile_commands.json

说明: 上面提到分析的前提是编译,如果待扫描的文件不在编译范围内,该文件将不会被分析。

场景举例:用户编辑了代码TestApp/test.m,触发了流水线配置的增量扫描任务,但是由于项目编译指令指定的Target并没有编译当前文件,此时该文件将不会被分析。

即使该文件中存在代码问题,扫描结果中也不会扫出这些问题。

# - 触发扫描

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

说明: Xcode项目目前仅扫描.m,.mm文件,若项目中有其他如.py脚本文件将不会进行缺陷分析。

# 【Android】

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

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

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

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

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

# 【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