DMS定义了一套领域专用语言DSL(Domain Specific Language)用来描述安全规则。DSL语法非常灵活,理论上可以表述任意安全规则,从而帮助不同企业定义符合自己的研发规范。

DSL语法概述

DSL语法非常简单,由分支判断(IF-ELSE)、条件和动作三部分组成,基本形式如下。

if
    条件1
then
    动作1
end
说明 当满足条件1,系统就执行动作1。

还有增强形式,即else后面还可以跟着if条件。只有if语句是必须的,elseif可以有0个或多个,else可以有0个或1个。

if
    条件1
then
    动作1
elseif
    条件2
then
    动作2
[else 动作3]
end
说明 如果满足条件1,就执行动作1,不满足条件1时如果满足条件2,就执行动作2,否则就执行动作3,[else 动作3]也可以没有,不满足条件2时,不执行任何操作。

DSL语法详细介绍

  • 条件语句

    条件就是判断语句,用来判定truefalse。条件语句由连接符(andor)、操作符、因子(系统变量)组成。如下示例都是合法的条件语句。

    true                    // 最简单的条件语句,结果就是true。
    1 > 0
    1 > 0 and 2 > 1
    1 <= 0 or 1 == 1
    说明 以上结果都是true。
    • 连接符

      和其它语言一样,andor分别是与和或连接运算,andor优先级高,但它们都比操作符的优先级低。

      例如 1 <= 0 or 1 == 1语句:最先执行 1 <= 0判断,然后执行 1 == 1判断,最后执行or判断。

    • 操作符

      操作符用于连接因子(系统变更)、常量进行相关逻辑运算,目前支持的操作符如下。

      操作符 说明 示例
      == 等于 1 == 1
      != 不等于 1 != 2
      > 大于 1 > 2
      >= 大于等于 1 >= 2
      < 小于 1 < 2
      <= 小于等于 1 <= 2
      in 包含于 ‘a’ in [‘a’, ‘b’, ‘c’]
      not in 不包含于 ‘a’ not in [‘a’, ‘b’, ‘c’]
      matchs(验证工具) 正则匹配 'idx_aa' matchs 'idx_\\w+'
      not matchs 不匹配 'idx_aa' not matchs 'idx_\\w+'
      isBlank 为空 ‘’ isBlank
      isNotBlank 不为空 ‘’ isNotBlank

      注意:正则表达式中(\)需要另外一个(\)转义,例如:idx_\w+需要写成:idx_\\w+

      说明 操作符虽然有优先级,但不建议依赖优先级。如果条件语句较为复杂时,建议将需要先判断的部分放在括号内。例如:1 <= 2 == true执行顺序不明确,可改为:(1 <= 2) == true,此时一定先执行(1 <= 2)
    • 因子

      因子是系统内置变量,可用来获取安全规则校验的上下文信息,如获取SQL类型、影响行数等。因子全部以@fac.开头,后接因子名称。每个模块的不同检测点均提供不同因子,如下为部分因子及其说明。

      因子名 说明
      @fac.env_type 环境类型,值为环境标识,如devproduct。详情请参见实例环境说明
      @fac.sql_type SQL脚本的类型,如UPDATEINSERT
      @fac.detail_type 数据变更的种类:
      • COMMON:普通数据变更
      • CHUNK_DML:无锁数据变更
      • PROCEDURE:存储过程
      • CRON_CLEAR_DATA:定时清理表
      • BIG_FILE:批量数据导入
      @fac.is_logic 是否为逻辑库。
      @fac.extra_info 其他变更信息(暂无用途)。
      @fac.is_ignore_affect_rows 是否跳过校验。
      @fac.insert_rows 插入数据的影响行数。
      @fac.update_delete_rows 更新数据的影响行数。
      @fac.max_alter_table_size 修改表中,最大的表空间大小。
      @fac.is_has_security_column SQL脚本中是否包含敏感列。
      @fac.security_column_list SQL脚本中包含的敏感列列表。
      @fac.risk_level 识别到的风险级别。
      @fac.risk_reason 识别为该风险的原因。

      在条件语句中,可直接引用因子,例如:@fac.sql_type == 'DML',判断SQL类型是不是DML。

  • 动作语句

    动作是满足if条件之后系统执行的行为,例如:禁止提交工单、选择工作流、允许执行、拒绝执行等,这些动作表达了安全规则的主要目的。动作全部以@act.开头,后接动作名称。每个模块的不同检测点均提供不同动作,如下为部分动作及其说明。

    动作名 说明
    @act.allow_submit 必须提交工单执行。
    @act.allow_execute_direct 允许直接在SQL控制台执行。
    @act.forbid_execute 禁止执行。
    @act.mark_risk 标记风险。用法:@act.mark_risk 'middle' '中风险:线上环境'
    @act.do_not_approve 指定审批模板ID。详情请参见设置审批流程
    @act.choose_approve_template
    @act.choose_approve_template_with_reason
  • 内置函数

    安全规则内置部分函数,在条件语句和动作语句均可以使用。函数全部以 @fun. 开头,后面是函数名。

    函数名 说明 示例
    @fun.concat 拼接字符串。

    返回值:字符串。

    参数:任意多个字符串。
    @fun.concat(‘d’, ‘m’, ‘s’) // ‘dms’

    @fun.concat(‘[研发规范]字段[‘,@fac.column_name, ‘]必须要填写备注信息’) // 拼接友好的提示信息返回给用户。

    @fun.char_length 计算字符串的长度。

    返回值:整数。

    参数:一个字符串。
    @fun.char_length(‘dms’) // 3

    @fun.char_length(@fac.table_name) // 计算表名长度。

    @fun.is_char_lower 判断字符串是否都是小写。

    返回值:true或false。

    参数:一个字符串。
    @fun.is_char_lower(‘dms’) // true

    @fun.is_char_lower(@fac.table_name) // 返回true的话,表名都是小写。

    @fun.is_char_upper 判断字符串是否都是大写。

    返回值:true或false。

    参数:一个字符串。
    @fun.is_char_upper(‘dms’) // false

    @fun.is_char_upper(@fac.table_name) // 返回true的话,表名都是大写。

    @fun.array_size 计算集合的大小。

    返回值:整数。

    参数:一个集合。
    @fun.array_size([1, 2, 3]) // 3

    @fun.array_size(@fac.table_index_array) // 计算表索引的个数。

    @fun.add 多个数相加。

    返回值:数值。

    参数:任意多个数。
    @fun.add(1, 2, 3) // 6
    @fun.sub 两个数相减。

    返回值:数值。

    参数:2个数值。
    @fun.sub(6, 1) // 5
    @fun.between 是否在某区间(支持数值、日期时间比较),判断时包含边界值。

    返回值:true或false。

    参数:3个参数,第一个是目标值,第二个是左区间,第三个是右区间。
    @fun.between(1, 1, 3) // 判断1是否在1~3区间内,返回true。

    @fun.between(2, 1, 3) // 判断2是否在1~3区间内,返回true。

    @fun.between(7, 1, 3) // 判断7是否在1~3区间内,返回false。

    @fun.between(@fac.export_rows, 2001, 100000) // 判断导出行数的范围。

    @fun.between(@fun.current_datetime(), ‘2019-10-31 00:00:00’, ‘2019-11-04 00:00:00’) // 判断当前时间是否在10.31~11.04号之间。

    @fun.between(@fun.current_date(), ‘2019-10-31’, ‘2019-11-04’) // 判断当前时间是否在10.31~11.04号之间。

    @fun.current_datetime 获取当前日期时间,格式:yyyy-MM-dd HH:mm:ss。

    返回值:字符串。

    参数:无。
    @fun.current_datetime() // 当前日期时间,例如:2019-10-31 00:00:00。
    @fun.current_date 获取当前日期,格式:yyyy-MM-dd。

    返回值:字符串。

    参数:无。
    @fun.current_date() // 当前日期,例如:2020-01-13。
    @fun.current_time 获取当前时间,格式:HH:mm:ss。

    返回值:字符串。

    参数:无。
    @fun.current_time() // 当前时间,例如:19:43:20。
    @fun.is_contain_str 判断第一个字符串是否包含第二个字符串。

    返回值:true或false。

    参数:2个参数,第一个是源字符串(Source),第二个是目标字符串(Target)。
    @fun.is_contain_str('abcd', 'ab') // 判断"abcd"是否包含"ab",返回true。
    @fun.listEqualIgnoreOrder 判断两个字符串List是否相同(忽略元素顺序和元素的大小写)。

    返回值:true或false。

    参数:2个参数,第一个是字符串List1,第二个是字符串List2。
    @fun.listEqualIgnoreOrder(['ab','cd'], ['Cd','ab']) // 判断是否相同,返回true。

    @fun.listEqualIgnoreOrder(@fac.perm_type, ['QUERY']) // 判断是否仅申请了查询权限。

    @fun.listEqualIgnoreOrder(@fac.perm_type, ['CORRECT','EXPORT']) // 判断是否同时申请了变更和导出权限。

示例

  • 控制单次执行SQL个数。
    if
        @fac.sql_count > 1000
    then
        @act.reject_execute '单次执行SQL个数不能超过1000'
    else
        @act.allow_execute
    end
    说明 如果SQL的数量大于1000个时,DMS即拒绝执行,并且返回相应的理由给用户,否则就允许执行。
  • 允许提交DML语句。
    if
        @fac.sql_type in [ 'UPDATE','DELETE','INSERT','INSERT_SELECT']
    then
        @act.allow_submit
    end
    说明 如果提交的SQL是UPDATEDELETEINSERTINSERT_SELECT类型就允许执行。