// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

package mandiant.capa;

message APIFeature {
  string type = 1;
  string api = 2;
  optional string description = 3;
}

message Address {
  AddressType type = 1;
  oneof value {
    Integer v = 2;
    Token_Offset token_offset = 3;
    Ppid_Pid ppid_pid = 4;
    Ppid_Pid_Tid ppid_pid_tid = 5;
    Ppid_Pid_Tid_Id ppid_pid_tid_id = 6;
  };
}

enum AddressType {
  ADDRESSTYPE_UNSPECIFIED = 0;
  ADDRESSTYPE_ABSOLUTE = 1;
  ADDRESSTYPE_RELATIVE = 2;
  ADDRESSTYPE_FILE = 3;
  ADDRESSTYPE_DN_TOKEN = 4;
  ADDRESSTYPE_DN_TOKEN_OFFSET = 5;
  ADDRESSTYPE_NO_ADDRESS = 6;
  ADDRESSTYPE_PROCESS = 7;
  ADDRESSTYPE_THREAD = 8;
  ADDRESSTYPE_CALL = 9;
}

message Analysis {
  string format = 1;
  string arch = 2;
  string os = 3;
  string extractor = 4;
  repeated string rules = 5;
  Address base_address = 6;
  Layout layout = 7;
  FeatureCounts feature_counts = 8;
  repeated LibraryFunction library_functions = 9;
}

message ArchFeature {
  string type = 1;
  string arch = 2;
  optional string description = 3;
}

message AttackSpec {
  repeated string parts = 1;
  string tactic = 2;
  string technique = 3;
  string subtechnique = 4;
  string id = 5;
}

message BasicBlockFeature {
  string type = 1;
  optional string description = 2;
}

message BasicBlockLayout {
  Address address = 1;
}

message BytesFeature {
  string type = 1;
  string bytes = 2;
  optional string description = 3;
}

message CharacteristicFeature {
  string type = 1;
  string characteristic = 2;
  optional string description = 3;
}

message ClassFeature {
  string type = 1;
  string class_ = 2;  // class is protected Python keyword
  optional string description = 3;
}

message CompoundStatement {
  string type = 1;
  optional string description = 2;
}

message DynamicAnalysis {
  string format = 1;
  string arch = 2;
  string os = 3;
  string extractor = 4;
  repeated string rules = 5;
  DynamicLayout layout = 6;
  DynamicFeatureCounts feature_counts = 7;
}

message DynamicFeatureCounts {
  uint64 file = 1;
  repeated ProcessFeatureCount processes = 2;
}

message DynamicLayout {
  repeated ProcessLayout processes = 1;
}

message ExportFeature {
  string type = 1;
  string export = 2;
  optional string description = 3;
}

message FeatureCounts {
  uint64 file = 1;
  repeated FunctionFeatureCount functions = 2;
}

message FeatureNode {
  string type = 1;
  oneof feature {
    OSFeature os = 2;
    ArchFeature arch = 3;
    FormatFeature format = 4;
    MatchFeature match = 5;
    CharacteristicFeature characteristic = 6;
    ExportFeature export = 7;
    ImportFeature import_ = 8;  // import is Python keyword
    SectionFeature section = 9;
    FunctionNameFeature function_name = 10;
    SubstringFeature substring = 11;
    RegexFeature regex = 12;
    StringFeature string = 13;
    ClassFeature class_ = 14;
    NamespaceFeature namespace = 15;
    APIFeature api = 16;
    PropertyFeature property_ = 17;  // property is a Python top-level decorator name
    NumberFeature number = 18;
    BytesFeature bytes = 19;
    OffsetFeature offset = 20;
    MnemonicFeature mnemonic = 21;
    OperandNumberFeature operand_number = 22;
    OperandOffsetFeature operand_offset = 23;
    BasicBlockFeature basic_block = 24;
  };
}

message FormatFeature {
  string type = 1;
  string format = 2;
  optional string description = 3;
}

message FunctionFeatureCount {
  Address address = 1;
  uint64 count = 2;
}

message FunctionLayout {
  Address address = 1;
  repeated BasicBlockLayout matched_basic_blocks = 2;
}

message FunctionNameFeature {
  string type = 1;
  string function_name = 2;
  optional string description = 3;
}

message ImportFeature {
  string type = 1;
  string import_ = 2;
  optional string description = 3;
}

message Layout {
  repeated FunctionLayout functions = 1;
}

message LibraryFunction {
  Address address = 1;
  string name = 2;
}

message MBCSpec {
  repeated string parts = 1;
  string objective = 2;
  string behavior = 3;
  string method = 4;
  string id = 5;
}

message MaecMetadata {
  string analysis_conclusion = 1;
  string analysis_conclusion_ov = 2;
  string malware_family = 3;
  string malware_category = 4;
  string malware_category_ov = 5;
}

message Match {
  bool success = 1;
  oneof node {
    StatementNode statement = 2;
    FeatureNode feature = 3;
  };
  repeated Match children = 5;
  repeated Address locations = 6;
  map <string, Addresses> captures = 7;
}

message MatchFeature {
  string type = 1;
  string match = 2;
  optional string description = 3;
}

enum Flavor {
  FLAVOR_UNSPECIFIED = 0;
  FLAVOR_STATIC = 1;
  FLAVOR_DYNAMIC = 2;
}

message Metadata {
  string timestamp = 1;  // iso8601 format, like: 2019-01-01T00:00:00Z 
  string version = 2;
  repeated string argv = 3;
  Sample sample = 4;
  // deprecated in v7.0.
  // use analysis2 instead.
  Analysis analysis = 5 [deprecated = true];
  Flavor flavor = 6;
  oneof analysis2 {
    // use analysis2 instead of analysis (deprecated in v7.0).
    StaticAnalysis static_analysis = 7;
    DynamicAnalysis dynamic_analysis = 8;
  };
}

message MnemonicFeature {
  string type = 1;
  string mnemonic = 2;
  optional string description = 3;
}

message NamespaceFeature {
  string type = 1;
  string namespace = 2;
  optional string description = 3;
}

message NumberFeature {
  string type = 1;
  Number number = 2;  // this can be positive (range: u64), negative (range: i64), or a double.
  optional string description = 5;
}

message OSFeature {
  string type = 1;
  string os = 2;
  optional string description = 3;
}

message OffsetFeature {
  string type = 1;
  Integer offset = 2;  // offset can be negative
  optional string description = 3;
}

message OperandNumberFeature {
  string type = 1;
  uint32 index = 2;
  Integer operand_number = 3;  // this can be positive (range: u64), negative (range: i64), or a double.
  optional string description = 4;
}

message OperandOffsetFeature {
  string type = 1;
  uint32 index = 2;
  Integer operand_offset = 3;
  optional string description = 4;
}

message ProcessFeatureCount {
  Address address = 1;
  uint64 count = 2;
}

message ProcessLayout {
  Address address = 1;
  repeated ThreadLayout matched_threads = 2;
  string name = 3;
}

message PropertyFeature {
  string type = 1;
  string property_ = 2;  // property is a Python top-level decorator name
  optional string access = 3;
  optional string description = 4;
}

message RangeStatement {
  string type = 1;
  uint64 min = 2;
  uint64 max = 3;
  // reusing FeatureNode here to avoid duplication and list all features OSFeature, ArchFeature, ... again.
  FeatureNode child = 4;
  optional string description = 5;
}

message RegexFeature {
  string type = 1;
  string regex = 2;
  optional string description = 3;
}

message ResultDocument {
  Metadata meta = 1;
  map <string, RuleMatches> rules = 2;
}

message RuleMatches {
  RuleMetadata meta = 1;
  string source = 2;
  repeated Pair_Address_Match matches = 3;
}

message RuleMetadata {
  string name = 1;
  string namespace = 2;
  repeated string authors = 3;
  // deprecated in v7.0.
  // use scopes instead.
  Scope scope = 4 [deprecated = true];
  repeated AttackSpec attack = 5;
  repeated MBCSpec mbc = 6;
  repeated string references = 7;
  repeated string examples = 8;
  string description = 9;
  bool lib = 10;
  MaecMetadata maec = 11;
  bool is_subscope_rule = 12;
  // use scopes over scope (deprecated in v7.0).
  Scopes scopes = 13;
}

message Sample {
  string md5 = 1;
  string sha1 = 2;
  string sha256 = 3;
  string path = 4;
}

enum Scope {
  SCOPE_UNSPECIFIED = 0;
  SCOPE_FILE = 1;
  SCOPE_FUNCTION = 2;
  SCOPE_BASIC_BLOCK = 3;
  SCOPE_INSTRUCTION = 4;
  SCOPE_PROCESS = 5;
  SCOPE_THREAD = 6;
  SCOPE_CALL = 7;
  SCOPE_SPAN_OF_CALLS = 8;
}

message Scopes {
  optional Scope static = 1;
  optional Scope dynamic = 2;
}

message SectionFeature {
  string type = 1;
  string section = 2;
  optional string description = 3;
}

message SomeStatement {
  string type = 1;
  uint32 count = 2;
  optional string description = 3;
}

message StatementNode {
  string type = 1;
  oneof statement {
    RangeStatement range = 2;
    SomeStatement some = 3;
    SubscopeStatement subscope = 4;
    CompoundStatement compound = 5;
  };
}

message StaticAnalysis {
  string format = 1;
  string arch = 2;
  string os = 3;
  string extractor = 4;
  repeated string rules = 5;
  Address base_address = 6;
  StaticLayout layout = 7;
  StaticFeatureCounts feature_counts = 8;
  repeated LibraryFunction library_functions = 9;
}

message StaticFeatureCounts {
  uint64 file = 1;
  repeated FunctionFeatureCount functions = 2;
}

message StaticLayout {
  repeated FunctionLayout functions = 1;
}

message StringFeature {
  string type = 1;
  string string = 2;
  optional string description = 3;
}

message SubscopeStatement {
  string type = 1;
  Scope scope = 2;
  optional string description = 3;
}

message SubstringFeature {
  string type = 1;
  string substring = 2;
  optional string description = 3;
}

message CallLayout {
  Address address = 1;
  string name = 2;
}

message ThreadLayout {
  Address address = 1;
  repeated CallLayout matched_calls = 2;
}

message Addresses { repeated Address address = 1; }

message Pair_Address_Match {
  Address address = 1;
  Match match = 2;
}

message Token_Offset {
  Integer token = 1;
  uint64 offset = 2;  // offset is always >= 0
}

message Ppid_Pid {
  Integer ppid = 1;
  Integer pid = 2;
}

message Ppid_Pid_Tid {
  Integer ppid = 1;
  Integer pid = 2;
  Integer tid = 3;
}

message Ppid_Pid_Tid_Id {
  Integer ppid = 1;
  Integer pid = 2;
  Integer tid = 3;
  Integer id = 4;
}

message Integer { oneof value { uint64 u = 1; sint64 i = 2; } }  // unsigned or signed int

message Number { oneof value { uint64 u = 1; sint64 i = 2; double f = 3; } }
