# Python 编码规范 Gate 规则
# 来源: 华为Python语言编程规范V3.4.pdf + 华为云计算最小规则集（Python）.xlsx

rules:
  # === 命名规范 ===

  - id: py-module-naming
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-命名"
    message: "模块名应使用小写字母，下划线分隔"
    pattern: "\\bimport\\s+[A-Z]"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/__init__.py"

  - id: py-class-naming
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-命名"
    message: "类名必须使用PascalCase"
    pattern: "^class\\s+[a-z]"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/__init__.py"

  - id: py-function-naming
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-命名"
    message: "函数名必须使用snake_case"
    pattern: "^def\\s+[A-Z]"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/__init__.py"

  - id: py-constant-naming
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-命名"
    message: "常量必须使用UPPER_SNAKE_CASE"
    pattern: "^[A-Z_]+\\s*=\\s*(?!\\d+)"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/__init__.py"

  - id: py-boolean-negative-name
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-命名"
    message: "避免使用否定含义的布尔变量名"
    pattern: "\\b(is_not|isNo|isNot|not_)\\w*"
    match_mode: absent
    includes:
      - "**/*.py"

  # === 格式规范 ===

  - id: py-no-tab-indent
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-缩进"
    message: "禁止使用制表符缩进，使用4个空格"
    pattern: "\t"
    match_mode: absent
    includes:
      - "**/*.py"

  - id: py-no-trailing-whitespace
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-格式"
    message: "删除行尾多余空格"
    pattern: " +$"
    match_mode: absent
    includes:
      - "**/*.py"

  - id: py-max-line-length
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-格式"
    message: "单行代码建议不超过120字符"
    pattern: ".{121,}$"
    match_mode: present
    includes:
      - "**/*.py"

  # === 注释规范 ===

  - id: py-docstring-format
    type: regex-check
    blocking: false
    severity: suggestion
    scope: incremental
    source: "华为Python规范-注释"
    message: "公共函数应使用三重引号编写docstring"
    pattern: "^def\\s+\\w+\\([^)]*\\):\\s*\\n\\s*\"\"\""
    match_mode: present
    includes:
      - "**/*.py"
    excludes:
      - "**/test*.py"
      - "**/*_test.py"
      - "**/__init__.py"

  - id: py-no-todo-in-prod
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-注释"
    message: "正式交付代码不应包含TODO/FIXME注释"
    pattern: "(TODO|FIXME)"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/test*.py"
      - "**/*_test.py"

  # === 异常处理 ===

  - id: py-no-bare-except
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-异常"
    message: "禁止使用裸except，必须指定具体异常类型"
    pattern: "^\\s*except\\s*:"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/test*.py"
      - "**/*_test.py"

  - id: py-no-catch-except-base
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-异常"
    message: "禁止直接捕获Exception基类"
    pattern: "except\\s+(Exception|BaseException)\\s*:"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/test*.py"
      - "**/*_test.py"

  # === 空值比较 ===

  - id: py-use-is-none
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-表达式"
    message: "使用is/is not比较None，禁止使用==或!="
    pattern: "==\\s*None|!=\\s*None"
    match_mode: absent
    includes:
      - "**/*.py"

  # === 导入规范 ===

  - id: py-no-wildcard-import
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-导入"
    message: "禁止使用from module import *"
    pattern: "from\\s+\\w+\\s+import\\s+\\*"
    match_mode: absent
    includes:
      - "**/*.py"

  - id: py-import-order
    type: regex-check
    blocking: false
    severity: suggestion
    scope: incremental
    source: "华为Python规范-导入"
    message: "导入顺序应遵循：标准库、第三方库、本地库"
    pattern: "^from\\s+\\.\\s+import"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/__init__.py"

  # === 函数规范 ===

  - id: py-no-mutable-default-args
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-函数"
    message: "禁止使用可变对象作为函数默认参数"
    pattern: "def\\s+\\w+\\([^)]*=\\s*(\\[|\\{|set\\(\\))"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/test*.py"
      - "**/*_test.py"

  # === 文件操作 ===

  - id: py-use-with
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-IO"
    message: "文件操作须使用with语句"
    pattern: "open\\([^)]+\\)\\s*[^\\s]"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/test*.py"
      - "**/*_test.py"
      - "**/conftest.py"

  # === 日志规范 ===

  - id: py-no-print-in-tests
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-测试"
    message: "测试中禁止使用print调试"
    pattern: "\\bprint\\s*\\("
    match_mode: absent
    includes:
      - "**/test*.py"
      - "**/*_test.py"
      - "**/tests/**"

  # === 安全规范 - Flask ===

  - id: py-flask-debug-false
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为云Python规则-BD_flask_debug_true"
    message: "禁止设置Flask debug=True，生产环境会导致任意代码执行漏洞"
    pattern: "app\\.run\\([^)]*debug\\s*=\\s*True"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/test*.py"
      - "**/*_test.py"

  # === 安全规范 - Jinja2 ===

  - id: py-jinja2-autoescape-true
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为云Python规则-BD_jinja2_autoescape_false"
    message: "Jinja2必须启用autoescape防止XSS攻击"
    pattern: "jinja2\\.Environment\\([^)]*autoescape\\s*=\\s*False"
    match_mode: absent
    includes:
      - "**/*.py"

  # === 安全规范 - SSL/TLS ===

  - id: py-ssl-bad-version
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为云Python规则-BD_ssl_with_bad_version"
    message: "禁止使用不安全的SSL/TLS协议版本(SSLv2, SSLv3, TLSv1, TLSv1.1)"
    pattern: "PROTOCOL_SSLv|PROTOCOL_TLSv1(_0)?\\b"
    match_mode: absent
    includes:
      - "**/*.py"

  - id: py-ssl-no-version
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为云Python规则-BD_ssl_with_no_version"
    message: "SSL连接应明确指定安全的协议版本"
    pattern: "ssl\\.wrap_socket\\s*\\("
    match_mode: absent
    includes:
      - "**/*.py"

  # === 安全规范 - 证书验证 ===

  - id: py-no-unverified-ssl-context
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为云Python规则-CR_HWCloud_Python_Security_Blacklist_Unverified_context"
    message: "禁止使用ssl._create_unverified_context()，必须验证服务端证书"
    pattern: "ssl\\._create_unverified_context\\s*\\("
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/test*.py"
      - "**/*_test.py"

  # === 安全规范 - 反序列化 ===

  - id: py-no-pickle-load
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为云Python规则-CR_HWCloud_Python_Security_Deserialization_Injection"
    message: "禁止直接使用pickle.load()，反序列化不可信数据会导致远程代码执行"
    pattern: "pickle\\.(load|loads)\\s*\\("
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/test*.py"
      - "**/*_test.py"

  - id: py-no-torch-load
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为云Python规则-CR_HWCloud_Python_Security_Deserialization_Injection"
    message: "torch.load()必须添加weights_only=True参数防止反序列化攻击"
    pattern: "torch\\.load\\s*\\([^)]*\\)"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/test*.py"
      - "**/*_test.py"

  # === 安全规范 - CSRF ===

  - id: py-django-csrf-exempt
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为云Python规则-CR_HWCloud_Python_Security_Django_Csrf_Exempt"
    message: "@csrf_exempt存在安全风险，建议只在必要时使用并添加额外安全措施"
    pattern: "@csrf_exempt"
    match_mode: absent
    includes:
      - "**/*.py"

  # === 安全规范 - 私钥 ===

  - id: py-no-unencrypted-private-key
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为云Python规则-CR_HWCloud_Python_Security_Dump_Unencrypted_Private_Key"
    message: "禁止明文存储或打印私钥"
    pattern: "(private[_\\s]key|PRIVATE[_\\s]KEY|-----BEGIN PRIVATE KEY-----)"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/test*.py"
      - "**/*_test.py"
      - "**/*.pem"
      - "**/*.key"

  # === 安全规范 - 不安全协议 ===

  - id: py-no-ftp
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为云Python规则-CR_HWCloud_Python_Security_FTP"
    message: "禁止使用FTP、TFTP等不安全协议，改用SFTP/SCP"
    pattern: "(pyftpdlib|tftpy|ftplib)"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/test*.py"
      - "**/*_test.py"

  # === 安全规范 - 硬编码凭据 ===

  - id: py-no-hardcoded-appcode
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为云Python规则-CR_HWCloud_Python_Security_Hardcoded_Appcode"
    message: "禁止在代码中硬编码AppCode"
    pattern: "(AppCode|appcode)\\s*=\\s*['\"][^'\"]{16,}"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/test*.py"
      - "**/*_test.py"

  - id: py-no-hardcoded-encryption-key
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为云Python规则-CR_HWCloud_Python_Security_Hardcoded_Encryption_Key"
    message: "禁止在代码中硬编码加密密钥"
    pattern: "(key|secret|aes|rsa)\\s*=\\s*['\"][^'\"]{16,}"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/test*.py"
      - "**/*_test.py"

  # === 安全规范 - 随机数 ===

  - id: py-secure-random-seed
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为云Python规则-CR_HWCloud_Python_Security_Hardcoded_Random_seed"
    message: "安全场景下必须使用secrets模块生成随机数，禁止使用random模块"
    pattern: "random\\.(randint|random|randrange|choice|sample|seed)"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/test*.py"
      - "**/*_test.py"
      - "**/conftest.py"

  # === 硬编码敏感信息 ===

  - id: py-no-hardcoded-credential
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-安全"
    message: "禁止硬编码凭据，使用os.getenv()"
    pattern: "(password|secret|api_key|token)\\s*=\\s*['\"][^'\"]{8,}"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/test*.py"
      - "**/*_test.py"
      - "**/migrations/**"

  # === SQL注入防护 ===

  - id: py-sql-injection
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-安全"
    message: "使用参数化查询，禁止字符串拼接SQL"
    pattern: "cursor\\.execute\\([^,]+%"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/test*.py"
      - "**/*_test.py"

  # === 类型注解 ===

  - id: py-use-type-annotation
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-类型"
    message: "公共函数建议添加类型注解"
    pattern: "^def\\s+\\w+\\([^)]*\\):\\s*(?!->)"
    match_mode: absent
    includes:
      - "**/*.py"
    excludes:
      - "**/test*.py"
      - "**/*_test.py"
      - "**/__init__.py"

  # === 测试命名 ===

  - id: py-test-naming
    type: regex-check
    blocking: false
    severity: warning
    scope: incremental
    source: "华为Python规范-测试"
    message: "测试函数须以test_开头，类以Test开头"
    pattern: "^def\\s+[^t]|^class\\s+[^T]"
    match_mode: absent
    includes:
      - "**/test*.py"
      - "**/*_test.py"
      - "**/tests/**"