Class: Yara::Rule

Inherits:
Object
  • Object
show all
Defined in:
lib/yara/rule.rb

Overview

Public: Represents a YARA rule from compiled rules.

A Rule provides access to a YARA rule’s metadata, tags, and patterns without needing to scan any data. This is useful for inspecting compiled rules, extracting metadata, and understanding rule structure.

Examples

# Typically created by Scanner#each_rule
scanner.each_rule do |rule|
  puts "Rule: #{rule.identifier}"
  puts "Namespace: #{rule.namespace}"
  puts "Tags: #{rule.tags.join(', ')}"
  rule..each { |k, v| puts "  #{k}: #{v}" }
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(rule_ptr) ⇒ Rule

Public: Initialize a new Rule from a YRX_RULE pointer.

This constructor extracts the rule identifier, namespace, metadata, and tags using the YARA-X C API.

rule_ptr - An FFI Pointer to the YRX_RULE structure

Examples

# Typically created internally by Scanner#each_rule
rule = Rule.new(rule_ptr)


40
41
42
43
44
45
46
# File 'lib/yara/rule.rb', line 40

def initialize(rule_ptr)
  @rule_ptr = rule_ptr
  @identifier = extract_identifier
  @namespace = extract_namespace
  @metadata_cache = nil
  @tags_cache = nil
end

Instance Attribute Details

#identifierObject (readonly)

Public: The identifier (name) of the rule.



21
22
23
# File 'lib/yara/rule.rb', line 21

def identifier
  @identifier
end

#namespaceObject (readonly)

Public: The namespace of the rule.



24
25
26
# File 'lib/yara/rule.rb', line 24

def namespace
  @namespace
end

#rule_ptrObject (readonly)

Public: FFI pointer to the underlying YRX_RULE structure.



27
28
29
# File 'lib/yara/rule.rb', line 27

def rule_ptr
  @rule_ptr
end

Instance Method Details

#extract_identifierObject

Internal: Extract the rule identifier using YARA-X API.

Returns a String containing the rule name.



183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/yara/rule.rb', line 183

def extract_identifier
  ident_ptr = ::FFI::MemoryPointer.new(:pointer)
  len_ptr = ::FFI::MemoryPointer.new(:size_t)

  result = Yara::FFI.yrx_rule_identifier(@rule_ptr, ident_ptr, len_ptr)
  if result != Yara::FFI::YRX_SUCCESS
    raise "Failed to extract rule identifier: #{Yara::FFI.yrx_last_error}"
  end

  ident = ident_ptr.read_pointer
  length = len_ptr.read(:size_t)
  ident.read_bytes(length).force_encoding("UTF-8")
end

#extract_metadataObject

Internal: Extract metadata from the rule using YARA-X API.

Returns a Hash mapping metadata keys to their values.



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/yara/rule.rb', line 220

def 
   = {}

   = proc do |, _user_data|
    begin
      # Read identifier (first field, pointer at offset 0)
      identifier_ptr = .get_pointer(0)
      next if identifier_ptr.null?
      identifier = identifier_ptr.read_string.to_sym

      # Read value_type (int at offset 8, after the 8-byte pointer)
      value_type = .get_int32(8)

      # The value union starts at offset 16 (pointer:8 + int:4 + padding:4)
      # This is due to struct alignment requirements
      value_offset = 16

      value = case value_type
      when Yara::FFI::YRX_METADATA_TYPE_I64
        .get_int64(value_offset)
      when Yara::FFI::YRX_METADATA_TYPE_F64
        .get_double(value_offset)
      when Yara::FFI::YRX_METADATA_TYPE_BOOLEAN
        .get_uint8(value_offset) != 0
      when Yara::FFI::YRX_METADATA_TYPE_STRING
        str_ptr = .get_pointer(value_offset)
        str_ptr.null? ? nil : str_ptr.read_string
      when Yara::FFI::YRX_METADATA_TYPE_BYTES
        length = .get_size_t(value_offset)
        data_ptr = .get_pointer(value_offset + 8)
        (data_ptr.null? || length == 0) ? nil : data_ptr.read_bytes(length)
      else
        nil
      end

      [identifier] = value unless value.nil?
    rescue => e
      # Skip problematic metadata entries to ensure partial extraction works
    end
  end

  result = Yara::FFI.(@rule_ptr, , nil)
  if result != Yara::FFI::YRX_SUCCESS
    raise "Failed to iterate rule metadata: #{Yara::FFI.yrx_last_error}"
  end

  
end

#extract_namespaceObject

Internal: Extract the rule namespace using YARA-X API.

Returns a String containing the namespace, or nil if default namespace.



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/yara/rule.rb', line 200

def extract_namespace
  ns_ptr = ::FFI::MemoryPointer.new(:pointer)
  len_ptr = ::FFI::MemoryPointer.new(:size_t)

  result = Yara::FFI.yrx_rule_namespace(@rule_ptr, ns_ptr, len_ptr)
  if result != Yara::FFI::YRX_SUCCESS
    raise "Failed to extract rule namespace: #{Yara::FFI.yrx_last_error}"
  end

  ns = ns_ptr.read_pointer
  length = len_ptr.read(:size_t)
  namespace_str = ns.read_bytes(length).force_encoding("UTF-8")

  # Return nil for default namespace
  namespace_str.empty? ? nil : namespace_str
end

#extract_tagsObject

Internal: Extract tags from the rule using YARA-X API.

Returns an Array of Strings containing the rule’s tags.



272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/yara/rule.rb', line 272

def extract_tags
  tags = []

  tag_callback = proc do |tag_ptr, _user_data|
    tags << tag_ptr.read_string unless tag_ptr.null?
  end

  result = Yara::FFI.yrx_rule_iter_tags(@rule_ptr, tag_callback, nil)
  if result != Yara::FFI::YRX_SUCCESS
    raise "Failed to iterate rule tags: #{Yara::FFI.yrx_last_error}"
  end

  tags
end

#has_tag?(tag) ⇒ Boolean

Public: Check if the rule has a specific tag.

tag - A String or Symbol representing the tag to check

Returns true if the tag exists, false otherwise.

Examples

rule.has_tag?("malware")
# => true

Returns:

  • (Boolean)


102
103
104
# File 'lib/yara/rule.rb', line 102

def has_tag?(tag)
  tags.include?(tag.to_s)
end

#metadataObject

Public: Get the rule’s metadata as a Hash.

Metadata is extracted from the rule’s meta section and includes various types: strings, integers, floats, booleans, and bytes.

Returns a Hash mapping metadata keys (Symbols) to their values.

Examples

 = rule.
# => { author: "test", severity: 5, is_malware: true }


59
60
61
# File 'lib/yara/rule.rb', line 59

def 
  @metadata_cache ||= 
end

#metadata_bool(key) ⇒ Object

Public: Get a Boolean metadata value with type validation.

key - A Symbol or String representing the metadata key

Returns the Boolean value if found and is a Boolean, nil otherwise.

Examples

rule.(:is_malware)
# => true


160
161
162
163
# File 'lib/yara/rule.rb', line 160

def (key)
  value = (key)
  [true, false].include?(value) ? value : nil
end

#metadata_float(key) ⇒ Object

Public: Get a Float metadata value with type validation.

key - A Symbol or String representing the metadata key

Returns the Float value if found and is a Float, nil otherwise.

Examples

rule.(:confidence)
# => 0.95


175
176
177
178
# File 'lib/yara/rule.rb', line 175

def (key)
  value = (key)
  value.is_a?(Float) ? value : nil
end

#metadata_int(key) ⇒ Object

Public: Get an Integer metadata value with type validation.

key - A Symbol or String representing the metadata key

Returns the Integer value if found and is an Integer, nil otherwise.

Examples

rule.(:severity)
# => 5


145
146
147
148
# File 'lib/yara/rule.rb', line 145

def (key)
  value = (key)
  value.is_a?(Integer) ? value : nil
end

#metadata_string(key) ⇒ Object

Public: Get a String metadata value with type validation.

key - A Symbol or String representing the metadata key

Returns the String value if found and is a String, nil otherwise.

Examples

rule.(:author)
# => "test_author"


130
131
132
133
# File 'lib/yara/rule.rb', line 130

def (key)
  value = (key)
  value.is_a?(String) ? value : nil
end

#metadata_value(key) ⇒ Object

Public: Get a metadata value by key with type checking.

key - A Symbol or String representing the metadata key

Returns the metadata value if found, nil otherwise.

Examples

rule.(:author)
# => "test_author"


116
117
118
# File 'lib/yara/rule.rb', line 116

def (key)
  [key.to_sym]
end

#qualified_nameObject

Public: Get a qualified name combining namespace and identifier.

Returns a String in the format “namespace.identifier”.

Examples

rule.qualified_name
# => "malware.trojan_detector"


86
87
88
89
90
# File 'lib/yara/rule.rb', line 86

def qualified_name
  return identifier if namespace.nil? || namespace.empty?

  "#{namespace}.#{identifier}"
end

#tagsObject

Public: Get the rule’s tags as an Array.

Tags are labels used to categorize and organize rules, defined after the rule name in the rule definition.

Returns an Array of Strings containing the rule’s tags.

Examples

tags = rule.tags
# => ["malware", "trojan"]


74
75
76
# File 'lib/yara/rule.rb', line 74

def tags
  @tags_cache ||= extract_tags
end