/ common / fingerprints / parser / parser.go
parser.go
  1  // Copyright (c) 2024-2026 Tencent Zhuque Lab. All rights reserved.
  2  //
  3  // Licensed under the Apache License, Version 2.0 (the "License");
  4  // you may not use this file except in compliance with the License.
  5  // You may obtain a copy of the License at
  6  //
  7  //     http://www.apache.org/licenses/LICENSE-2.0
  8  //
  9  // Unless required by applicable law or agreed to in writing, software
 10  // distributed under the License is distributed on an "AS IS" BASIS,
 11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  // See the License for the specific language governing permissions and
 13  // limitations under the License.
 14  //
 15  // Requirement: Any integration or derivative work must explicitly attribute
 16  // Tencent Zhuque Lab (https://github.com/Tencent/AI-Infra-Guard) in its
 17  // documentation or user interface, as detailed in the NOTICE file.
 18  
 19  // Package parser 实现了指纹规则的解析和评估功能。
 20  // 该包提供了将YAML格式的指纹规则转换为可执行规则对象的功能,
 21  // 并支持对HTTP请求响应进行指纹匹配评估。
 22  package parser
 23  
 24  import (
 25  	"fmt"
 26  
 27  	"gopkg.in/yaml.v2"
 28  )
 29  
 30  // FingerPrintInfo 定义了指纹的基本信息
 31  type FingerPrintInfo struct {
 32  	Name           string            `yaml:"name" json:"name"`
 33  	Author         string            `yaml:"author" json:"author"`
 34  	Example        []string          `yaml:"example,omitempty" json:"example,omitempty"`
 35  	Desc           string            `yaml:"desc,omitempty" json:"desc,omitempty"`
 36  	Severity       string            `yaml:"severity" json:"severity"`
 37  	Metadata       map[string]string `yaml:"metadata" json:"metadata"`
 38  	Recommendation int               `yaml:"recommendation,omitempty" json:"recommendation,omitempty"`
 39  }
 40  
 41  // Extractor 定义了从响应中提取信息的规则
 42  type Extractor struct {
 43  	Part  string `yaml:"part" json:"part"`
 44  	Group string `yaml:"group" json:"group"`
 45  	Regex string `yaml:"regex" json:"regex"`
 46  }
 47  
 48  // HttpRule 定义了HTTP请求匹配规则
 49  type HttpRule struct {
 50  	Method       string    `yaml:"method" json:"method"`
 51  	Path         string    `yaml:"path" json:"path"`
 52  	Matchers     []string  `yaml:"matchers" json:"matchers"`
 53  	Data         string    `yaml:"data,omitempty" json:"data,omitempty"`
 54  	dsl          []*Rule   `yaml:"-" json:"-"`
 55  	VersionRange string    `yaml:"versionrange,omitempty" json:"versionrange,omitempty"`
 56  	Extractor    Extractor `yaml:"extractor,omitempty" json:"extractor,omitempty"`
 57  }
 58  
 59  // GetDsl 返回解析后的DSL规则列表
 60  func (h *HttpRule) GetDsl() []*Rule {
 61  	return h.dsl
 62  }
 63  
 64  // FingerPrint 定义了完整的指纹规则结构
 65  type FingerPrint struct {
 66  	Info    FingerPrintInfo `yaml:"info" json:"info"`
 67  	Http    []HttpRule      `yaml:"http" json:"http"`
 68  	Version []HttpRule      `yaml:"version,omitempty" json:"version,omitempty"`
 69  }
 70  
 71  // FingerPrints 表示多个指纹规则的集合
 72  type FingerPrints []FingerPrint
 73  
 74  // Config 定义了进行指纹匹配时需要的配置信息
 75  type Config struct {
 76  	Body   string
 77  	Header string
 78  	Icon   int32
 79  	Hash   string
 80  }
 81  
 82  // AdvisoryConfig 提供漏洞配置信息
 83  type AdvisoryConfig struct {
 84  	Version    string
 85  	IsInternal bool
 86  }
 87  
 88  // transfromRule 将规则字符串转换为规则对象
 89  // 参数:
 90  //   - rule: 规则字符串
 91  //
 92  // 返回:
 93  //   - *Rule: 解析后的规则对象
 94  //   - error: 解析过程中的错误
 95  func transfromRule(rule string) (*Rule, error) {
 96  	tokens, err := ParseTokens(rule)
 97  	if err != nil {
 98  		return nil, err
 99  	}
100  	if err = CheckBalance(tokens); err != nil {
101  		return nil, err
102  	}
103  	return TransFormExp(tokens)
104  }
105  
106  // InitFingerPrintFromData 从字节数据初始化指纹配置
107  // 参数:
108  //   - reader: 包含YAML格式指纹配置的字节数据
109  //
110  // 返回:
111  //   - *FingerPrint: 解析后的指纹对象
112  //   - error: 解析过程中的错误
113  func InitFingerPrintFromData(reader []byte) (*FingerPrint, error) {
114  	var fp FingerPrint
115  	err := yaml.Unmarshal(reader, &fp)
116  	if err != nil {
117  		return nil, err
118  	}
119  	if err := compileMatchers(fp.Http); err != nil {
120  		return nil, err
121  	}
122  	if err := compileMatchers(fp.Version); err != nil {
123  		return nil, err
124  	}
125  	return &fp, err
126  }
127  
128  // compileMatchers compiles textual matchers into executable DSL rules.
129  func compileMatchers(rules []HttpRule) error {
130  	for i := range rules {
131  		dsls := make([]*Rule, 0, len(rules[i].Matchers))
132  		hasHashMatcher := false
133  		hasNonHashMatcher := false
134  		for _, matcher := range rules[i].Matchers {
135  			dsl, err := transfromRule(matcher)
136  			if err != nil {
137  				return err
138  			}
139  			usesHash, hashOnly := dsl.hashUsage()
140  			if usesHash {
141  				if !hashOnly {
142  					return fmt.Errorf("hash matcher cannot be combined with other fields: %s %s -> %s", rules[i].Method, rules[i].Path, matcher)
143  				}
144  				hasHashMatcher = true
145  			} else {
146  				hasNonHashMatcher = true
147  			}
148  			dsls = append(dsls, dsl)
149  		}
150  		if hasHashMatcher && hasNonHashMatcher {
151  			return fmt.Errorf("hash matcher cannot coexist with other matcher types: %s %s", rules[i].Method, rules[i].Path)
152  		}
153  		rules[i].dsl = dsls
154  	}
155  	return nil
156  }
157  
158  // FpResult 指纹结构体
159  type FpResult struct {
160  	Name    string `json:"name"`
161  	Version string `json:"version,omitempty"`
162  }
163  
164  // Eval 评估配置是否匹配规则
165  // 参数:
166  //   - config: 配置对象,包含请求体、请求头和图标信息
167  //   - dsl: 要评估的规则对象
168  //
169  // 返回:
170  //   - bool: 是否匹配规则
171  func Eval(config *Config, dsl *Rule) bool {
172  	return dsl.Eval(config)
173  }