schema

package
v0.0.0-...-5ab47d9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 21, 2024 License: MIT Imports: 9 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// NVDOutTimeFormat is the time format for NVD API
	NVDOutTimeFormat = "2006-01-02T15:04:05.000"
)

Variables

Functions

func GetLastSpecificIndex

func GetLastSpecificIndex(cpeName string) int

GetLastSpecificIndex gets the last item that is non "*" or "-"

Usage:

// LastSpecificIndex = CPEVersion = 5
fmt.Println(GetLastSpecificIndex("cpe:2.3:o:freebsd:freebsd:4.4:*:*:*:*:*:*:*"))  // 5

func IsAll

func IsAll(val string) bool

IsAll return if value is represents 'all' in query

func IsSpecific

func IsSpecific(val string) bool

IsSpecific return if value is represents specific version in query

func IsUnknown

func IsUnknown(val string) bool

IsUnknown return if value is represents 'unknown' in query

func IsValidCVE

func IsValidCVE(cve string) bool

func VersionInRange

func VersionInRange(start, end versionBound, v *version.Version) bool

VersionInRange checks whether version is in 'versionStart(Including|Excluding)' and 'versionEnd(Including|Excluding)'

Types

type APIResp

type APIResp[T APIRespItem] struct {
	APIRespInfo
	Items []T
}

APIResp is the response from NVD's CVE and CPE API 'Items' stores the list of CVE or CPE * CVE with json tag: "vulnerabilities" * CPE with json tag: "products"

func (*APIResp[T]) Init

func (ar *APIResp[T]) Init()

Init set default value for APIResp

func (APIResp[T]) IsNotFound

func (ar APIResp[T]) IsNotFound() bool

func (APIResp[T]) MarshalJSON

func (ar APIResp[T]) MarshalJSON() ([]byte, error)

MarshalJSON implements custom json.Marshaler for APIResp due to dynamic json tag and type of Items

func (*APIResp[T]) Sort

func (ar *APIResp[T]) Sort(fn func(i, j int) bool)

func (*APIResp[T]) UnmarshalJSON

func (ar *APIResp[T]) UnmarshalJSON(data []byte) error

UnmarshalJSON implements custom json.Unmarshaler for APIResp due to dynamic json tag and type of Items To avoid infinite loop, it use temp struct and json.RawMessage to unmarshal 'Items' field ref. https://stackoverflow.com/questions/62951510/unmarshal-remaining-json-after-performing-custom-unmarshalling

type APIRespInfo

type APIRespInfo struct {
	ResultsPerPage int    `json:"resultsPerPage"`
	StartIndex     int    `json:"startIndex"`
	TotalResults   int    `json:"totalResults"`
	Format         string `json:"format"`
	Version        string `json:"version"`
	Timestamp      string `json:"timestamp"`
}

APIRespInfo is the common info of API response from NVD's CVE and CPE API

type APIRespItem

type APIRespItem interface {
	CveItem | CpeItem
}

APIRespItem is the common interface of CVE and CPE

type CPEField

type CPEField int
const (
	CPEPart CPEField = iota + 2
	CPEVendor
	CPEProduct
	CPEVersion
	CPEUpdate
	CPEEdition
	CPELang
	CPESWEdition
	CPETargetSW
	CPETargetHW
	CPEOther
)

func (CPEField) String

func (f CPEField) String() string

type CPEParsed

type CPEParsed struct {
	// Ori is the original cpe string
	Ori string

	// Parsed is the map to store all parts in cpeName
	Parsed CPEParsedMap

	// PrefixToProduct is the prefix to product
	// E.g., prefix of "cpe:2.3:o:freebsd:freebsd:4.4:*:*:*:*:*:*:*" is "cpe:2.3:o:freebsd:freebsd"
	PrefixToProduct string
}

CPEParsed is the structure to store value parsed from CPE 2.3 string. The format is as below:

cpe:2.3:part:vendor:product:version:update:edition:language:sw_edition:target_sw:target_hw:other

func NewCPEParsed

func NewCPEParsed(cpeName string) *CPEParsed

NewCPEParsed parses FULL cpeName to struct

func (CPEParsed) Get

func (p CPEParsed) Get(fnum CPEField) string

func (CPEParsed) IsOthersMatch

func (candidate CPEParsed) IsOthersMatch(qParsed CPEParsed) bool

IsOthersMatch match criteria (CPE Name 2.3) with update:edition:language:sw_edition:target_sw:target_hw:other

type CPEParsedMap

type CPEParsedMap map[string]string

func NewCPEParsedMap

func NewCPEParsedMap(cpeName string) CPEParsedMap

NewCPEParsedMap parses full or partial cpeName to struct

type CVSSV2

type CVSSV2 struct {
	Source                  string     `json:"source" bson:"source"`
	Type                    string     `json:"type" bson:"type"`
	CvssData                CVSSV2Data `json:"cvssData" bson:"cvssData"`
	BaseSeverity            string     `json:"baseSeverity" bson:"baseSeverity"`
	ExploitabilityScore     float64    `json:"exploitabilityScore" bson:"exploitabilityScore"`
	ImpactScore             float64    `json:"impactScore" bson:"impactScore"`
	AcInsufInfo             bool       `json:"acInsufInfo" bson:"acInsufInfo"`
	ObtainAllPrivilege      bool       `json:"obtainAllPrivilege" bson:"obtainAllPrivilege"`
	ObtainUserPrivilege     bool       `json:"obtainUserPrivilege" bson:"obtainUserPrivilege"`
	ObtainOtherPrivilege    bool       `json:"obtainOtherPrivilege" bson:"obtainOtherPrivilege"`
	UserInteractionRequired bool       `json:"userInteractionRequired" bson:"userInteractionRequired"`
}

CVSSV2 stores V2 of CVSS which is no longer generates new data as of July 13th, 2022

type CVSSV2Data

type CVSSV2Data struct {
	Version               string  `json:"version" bson:"version"` // "2.0"
	VectorString          string  `json:"vectorString" bson:"vectorString"`
	AccessVector          string  `json:"accessVector" bson:"accessVector"`
	AccessComplexity      string  `json:"accessComplexity" bson:"accessComplexity"`
	Authentication        string  `json:"authentication" bson:"authentication"`
	ConfidentialityImpact string  `json:"confidentialityImpact" bson:"confidentialityImpact"`
	IntegrityImpact       string  `json:"integrityImpact" bson:"integrityImpact"`
	AvailabilityImpact    string  `json:"availabilityImpact" bson:"availabilityImpact"`
	BaseScore             float64 `json:"baseScore" bson:"baseScore"`
}

CVSSV2Data is data of CVSSV2

type CVSSV3

type CVSSV3 struct {
	Source              string     `json:"source" bson:"source"`
	Type                string     `json:"type" bson:"type"`
	CvssData            CVSSV3Data `json:"cvssData" bson:"cvssData"`
	ExploitabilityScore float64    `json:"exploitabilityScore" bson:"exploitabilityScore"`
	ImpactScore         float64    `json:"impactScore" bson:"impactScore"`
}

CVSSV3 stores V3.0 and V3.1 of CVSS

type CVSSV3Data

type CVSSV3Data struct {
	Version               string  `json:"version" bson:"version"` // "3.0", "3.1"
	VectorString          string  `json:"vectorString" bson:"vectorString"`
	AttackVector          string  `json:"attackVector" bson:"attackVector"`
	AttackComplexity      string  `json:"attackComplexity" bson:"attackComplexity"`
	PrivilegesRequired    string  `json:"privilegesRequired" bson:"privilegesRequired"`
	UserInteraction       string  `json:"userInteraction" bson:"userInteraction"`
	Scope                 string  `json:"scope" bson:"scope"`
	ConfidentialityImpact string  `json:"confidentialityImpact" bson:"confidentialityImpact"`
	IntegrityImpact       string  `json:"integrityImpact" bson:"integrityImpact"`
	AvailabilityImpact    string  `json:"availabilityImpact" bson:"availabilityImpact"`
	BaseScore             float64 `json:"baseScore" bson:"baseScore"`
	BaseSeverity          string  `json:"baseSeverity" bson:"baseSeverity"`
}

CVSSV3Data is data of CVSSV3

type Config

type Config struct {
	Operator string `json:"operator,omitempty" bson:"operator,omitempty"` // "AND", "OR"
	Negate   bool   `json:"negate,omitempty" bson:"negate,omitempty"`
	Nodes    []Node `json:"nodes" bson:"nodes"`
}

Config is a container that holds a set of nodes which then contain CPE Name Match Criteria

type Cpe

type Cpe struct {
	// Id is the auto generated ID for document DB
	Id *primitive.ObjectID `json:"-" bson:"_id,omitempty"`

	// Name is the cpe 2.3 format of CPE, which is also the primary key in cpe collection (index)
	Name string `json:"cpeName" bson:"cpeName"`

	// NameID is the UUID given from NVD, mitre does not contain this field
	NameID string `json:"cpeNameId" bson:"cpeNameId"`

	// NameParsed is the parsed result for 'Name' which is generated by ourselves to improve search performance
	NameParsed CPEParsedMap `json:"-" bson:"cpeNameParsed"`

	// LastModified is the last time this CPE has been modified
	// Format: 2022-08-11T15:15:10.593
	// Note: mitre does not contain this field
	LastModified *string `json:"lastModified" bson:"lastModified"`

	// Created is the time when CPE is created
	// Format: 2022-08-11T15:15:10.593
	// Note: mitre does not contain this field
	Created *string `json:"created" bson:"created"`

	// Titles are Human readable title for CPE
	Titles []Title `json:"titles,omitempty" bson:"titles"`

	// References are Internet resource for CPE
	References []ReferenceCpe `json:"refs,omitempty" bson:"refs"`

	// Deprecated shows whether the cpe is deprecated due to other cpes
	// Note: mitre does not contain this field
	Deprecated   bool        `json:"deprecated" bson:"deprecated"`
	DeprecatedBy []Deprecate `json:"deprecatedBy,omitempty" bson:"deprecatedBy"`
	Deprecates   []Deprecate `json:"deprecates,omitempty" bson:"deprecates"`
}

Cpe is also the unit struct for each cpe record in db

func (*Cpe) SetParsed

func (c *Cpe) SetParsed()

SetParsed parses the cpe to parts and store into struct, which is expected to write to database when -db-type=mongo to provide querying cpe with cpe match string

E.g. 1, cpeNameMatchString=cpe:2.3:*:Microsoft, matches CPEs with vendor=Microsoft
E.g. 2, cpeNameMatchString=cpe:2.3:o:microsoft:windows_10, matches CPEs with part=o, vendor=microsoft and product=windows_10

type CpeItem

type CpeItem struct {
	Cpe Cpe `json:"cpe"`
}

type CpeMatch

type CpeMatch struct {
	Vulnerable bool `json:"vulnerable" bson:"vulnerable"`

	// Criteria is cpe 2.3 format string as one of the condition that might contains this CVE
	// E.g., cpe:2.3:a:apache:http_server:*:*:*:*:*:*:*:*
	Criteria string `json:"criteria" bson:"criteria"` // cpe2.3

	// CriteriaProductPrefix is generated from field 'Criteria', which is not in either NVD or Mitre response
	// We generate this field as one of the index which benefits the performance when query with cpe name
	//
	// For example, when Criteria is "cpe:2.3:a:apache:http_server:*:*:*:*:*:*:*:*", then
	// CriteriaProductPrefix will be "cpe:2.3:a:apache:http_server".
	//
	// Since user can query with many kinds of version formats (E.g., '*', '-', '2.0', '3', ...),
	// When trying to match CVEs from CPE, we search with ProductPrefix first, then filter the result before return
	CriteriaProductPrefix string `json:"-" bson:"criteriaProductPrefix"`

	// MatchCriteriaID is the unique ID in NVD response which is used as query parameter for other API.
	// Mitre does not contains this field
	MatchCriteriaID string `json:"matchCriteriaId" bson:"matchCriteriaId"`

	// When Criteria does not specify the version, the range of the version is given by below 4 fields
	VersionStartIncluding string `json:"versionStartIncluding,omitempty" bson:"versionStartIncluding"`
	VersionEndIncluding   string `json:"versionEndIncluding,omitempty" bson:"versionEndIncluding"`
	VersionStartExcluding string `json:"versionStartExcluding,omitempty" bson:"versionStartExcluding"`
	VersionEndExcluding   string `json:"versionEndExcluding,omitempty" bson:"versionEndExcluding"`
}

CpeMatch describes how this cpe (Criteria) matches to the CVE

func (CpeMatch) IsVersionMatch

func (cpeToMatch CpeMatch) IsVersionMatch(qryVerStr string) bool

IsVersionMatch match criteria (CPE Name 2.3) with query version

type Cve

type Cve struct {
	// Id is the auto generated ID for document DB
	Id *primitive.ObjectID `json:"-" bson:"_id,omitempty"`

	// CVEID is the ID of CVE, E.g., CVE-2022-45197
	CVEID string `json:"id" bson:"cveId"`

	// SourceIdentifier is the source name, values can be 'nvd@nist.gov', 'cve@mitre.org', ...
	SourceIdentifier string `json:"sourceIdentifier" bson:"sourceIdentifier"`

	// Published is the time when this CVE is published.
	// Format: 2022-08-11T15:15:10.593
	Published string `json:"published" bson:"published"`

	// LastModified is the last time this CVE has been modified
	// Format: 2022-08-11T15:15:10.593
	LastModified string `json:"lastModified" bson:"lastModified"`

	// VulnStatus is the new field in NVD to describe the analysis status
	// Value includes: Analyzed, Undergoing Analysis, Modified.
	// All the data from Mitre is given with 'Analyzed'
	VulnStatus string `json:"vulnStatus" bson:"vulnStatus"`

	// Descriptions describe the vulnerability with sufficient detail
	// NVD contains more description than mitre
	Descriptions []Description `json:"descriptions" bson:"descriptions"`

	// Metrics contains multiple version of CVSS scores
	// In NVD, there might be multiple scores with same version which is given from different sources
	// It can be differenciate by Source and Type (Primary, Secondary) fields
	Metrics Metrics `json:"metrics"`

	// Weaknesses is Common Weakness Enumeration (CWE), which was created to identify
	// common software security weaknesses.
	Weaknesses []Weaknesses `json:"weaknesses" bson:"weaknesses"`

	// Configurations is a container that holds a set of nodes which then contain CPE Name Match Criteria
	Configurations []Config `json:"configurations" bson:"configurations"`

	// References are supplemental information relevant to the vulnerability
	References []Reference `json:"references" bson:"references"`
}

Cve is the cve part of one CVE record, while looks like all the information is stored in cve It is also the unit struct for each cve record in db

func (*Cve) AddCriteriaProductPrefixs

func (c *Cve) AddCriteriaProductPrefixs()

AddCriteriaProductPrefixs fill value to 'CriteriaProductPrefix' for every CpeMatchItems, which should be invoked before upserting data to database (when -db-type=mongo) since 'CriteriaProductPrefix' is set as index to enhance the performance when query with CPE name

func (Cve) Match

func (c Cve) Match(qParsed CPEParsed) bool

Match checks whether incoming query (cpe) matches this CVE

type CveItem

type CveItem struct {
	Cve Cve `json:"cve"`
}

CveItem is the unit of one CVE record in NVD

type Deprecate

type Deprecate struct {
	CpeName   string `json:"cpeName"`
	CpeNameId string `json:"cpeNameId"`
}

type Description

type Description struct {
	Lang  string `json:"lang" bson:"lang"`
	Value string `json:"value" bson:"value"`
}

Description is plain language field that should describe the vulnerability with sufficient detail as to demonstrate that the vulnerability is unique

type Metrics

type Metrics struct {
	CvssMetricV31 []CVSSV3 `json:"cvssMetricV31,omitempty" bson:"cvssMetricV31,omitempty"`
	CvssMetricV30 []CVSSV3 `json:"cvssMetricV30,omitempty" bson:"cvssMetricV30,omitempty"`
	CvssMetricV2  []CVSSV2 `json:"cvssMetricV2,omitempty" bson:"cvssMetricV2,omitempty"`
}

Metrics is a collection of multiple version of CVSS scores.

type MitreCVSSV2

type MitreCVSSV2 struct {
	CvssData                CVSSV2Data `json:"cvssV2"`
	BaseSeverity            string     `json:"baseSeverity"`
	ExploitabilityScore     float64    `json:"exploitabilityScore"`
	ImpactScore             float64    `json:"impactScore"`
	AcInsufInfo             bool       `json:"acInsufInfo"`
	ObtainAllPrivilege      bool       `json:"obtainAllPrivilege"`
	ObtainUserPrivilege     bool       `json:"obtainUserPrivilege"`
	ObtainOtherPrivilege    bool       `json:"obtainOtherPrivilege"`
	UserInteractionRequired bool       `json:"userInteractionRequired"`
}

MitreCVSSV2 is the CVSS V2 score structure

type MitreCVSSV3

type MitreCVSSV3 struct {
	CvssData            CVSSV3Data `json:"cvssV3"`
	ExploitabilityScore float64    `json:"exploitabilityScore"`
	ImpactScore         float64    `json:"impactScore"`
}

MitreCVSSV3 is the CVSS V3 score structure which includes V3.0 and V3.1

type MitreCpe

type MitreCpe struct {
	Text  string `xml:",chardata"`
	Name  string `xml:"name,attr"`
	Title struct {
		Text string `xml:",chardata"`
		Lang string `xml:"lang,attr"`
	} `xml:"title"`
	References struct {
		Text      string `xml:",chardata"`
		Reference []struct {
			Text string `xml:",chardata"`
			Href string `xml:"href,attr"`
		} `xml:"reference"`
	} `xml:"references"`
	Cpe23Item struct {
		Text string `xml:",chardata"`
		Name string `xml:"name,attr"`
	} `xml:"cpe23-item"`
}

MitreCpe is the stale format for each cpe.

E.g.,

<cpe-item name="cpe:/a:01org:tpm2.0-tools:1.1.0">
    <title xml:lang="en-US">01org Tpm2.0-tools 1.1.0</title>
    <references>
        <reference href="https://github.com/01org/tpm2.0-tools">Product</reference>
        <reference href="https://github.com/01org">Vendor</reference>
    </references>
    <cpe-23:cpe23-item name="cpe:2.3:a:01org:tpm2.0-tools:1.1.0:*:*:*:*:*:*:*"/>
 </cpe-item>

func (MitreCpe) ToAPIStruct

func (c MitreCpe) ToAPIStruct() Cpe

ToAPIStruct converts mitre format to nvd cpe format

type MitreCpeList

type MitreCpeList struct {
	XMLName        xml.Name `xml:"cpe-list"`
	Text           string   `xml:",chardata"`
	Config         string   `xml:"config,attr"`
	Xmlns          string   `xml:"xmlns,attr"`
	Xsi            string   `xml:"xsi,attr"`
	ScapCore       string   `xml:"scap-core,attr"`
	Cpe23          string   `xml:"cpe-23,attr"`
	Ns6            string   `xml:"ns6,attr"`
	Meta           string   `xml:"meta,attr"`
	SchemaLocation string   `xml:"schemaLocation,attr"`
	Generator      struct {
		Text           string `xml:",chardata"`
		ProductName    string `xml:"product_name"`
		ProductVersion string `xml:"product_version"`
		SchemaVersion  string `xml:"schema_version"`
		Timestamp      string `xml:"timestamp"`
	} `xml:"generator"`
	CpeItem []MitreCpe `xml:"cpe-item"`
}

MitreCpeList is the xml format for stale CPE xml files

ref. https://nvd.nist.gov/products/cpe

type MitreCve

type MitreCve struct {
	// DataType is the type of data, while most of them is "cve"
	DataType string `json:"data_type"`

	// CVEDataMeta is the meta for CVE, includes ID and sources
	CVEDataMeta struct {
		// ID is the ID of CVE, E.g., CVE-2022-45197
		ID string `json:"ID"`
		// SourceIdentifier is the source name, while most of them is 'cve@mitre.org'
		SourceIdentifier string `json:"ASSIGNER"` // maps to sourceIdentifier
	} `json:"CVE_data_meta"`

	// Description describes the vulnerability with sufficient detail
	Description struct {
		Data []Description `json:"description_data"`
	} `json:"description"`

	// Weaknesses is Common Weakness Enumeration (CWE), which was created to identify
	// common software security weaknesses.
	Weaknesses struct {
		Data []struct {
			Descr []Description `json:"description"` // E.g., value: "CWE-668"
		} `json:"problemtype_data"`
	} `json:"problemtype"`

	// References are supplemental information relevant to the vulnerability
	References struct {
		Data []Reference `json:"reference_data"`
	} `json:"references"`
}

MitreCve is the cve part of MitreCveItem

type MitreCveItem

type MitreCveItem struct {
	Cve    MitreCve `json:"cve"`
	Config struct {
		Nodes []NodeWChild `json:"nodes"`
	} `json:"configurations"`
	Impact struct {
		CvssMetricV3 MitreCVSSV3 `json:"baseMetricV3"`
		CvssMetricV2 MitreCVSSV2 `json:"baseMetricV2"`
	} `json:"impact"`
	Published    string `json:"publishedDate"`    // format: "2022-12-25T05:15Z"
	LastModified string `json:"lastModifiedDate"` // format: "2022-12-25T05:15Z"
}

MitreCveItem is the unit of each CVE In NVD, 'configurations' and 'impact' are also moved into 'cve'

func (MitreCveItem) ToAPIStruct

func (m MitreCveItem) ToAPIStruct() (*CveItem, error)

ToAPIStruct converts stale format to new API reponse format while some of the fields is filled with fixed value since the old format does not provide them

type MitreData

type MitreData struct {
	Timestamp    string         `json:"CVE_data_timestamp"` // "2022-12-25T06:00Z"
	NumberOfCVEs string         `json:"CVE_data_numberOfCVEs"`
	Items        []MitreCveItem `json:"CVE_Items"`
}

MitreData is the data feed structure provided from https://nvd.nist.gov/vuln/data-feeds Which is going to retire in late 2023. While it is needed to import stale CVE records

Schema: https://csrc.nist.gov/schema/nvd/feed/1.1/nvd_cve_feed_json_1.1.schema

type Node

type Node struct {
	Operator string     `json:"operator" bson:"operator"` // "AND", "OR"
	Negate   bool       `json:"negate" bson:"negate"`
	CpeMatch []CpeMatch `json:"cpeMatch" bson:"cpeMatch"`
}

Node is the unit of configuration to decide the conditions how cpe match to this cve

type NodeWChild

type NodeWChild struct {
	Operator string       `json:"operator"` // "AND", "OR"
	Children []NodeWChild `json:"children"`
	CpeMatch []struct {
		Vulnerable            bool   `json:"vulnerable"`
		Criteria              string `json:"cpe23Uri"` // cpe2.3
		CPEV22                string `json:"cpe22Uri"` // cpe2.2
		VersionStartIncluding string `json:"versionStartIncluding,omitempty"`
		VersionEndIncluding   string `json:"versionEndIncluding,omitempty"`
		VersionStartExcluding string `json:"versionStartExcluding,omitempty"`
		VersionEndExcluding   string `json:"versionEndExcluding,omitempty"`
	} `json:"cpe_match"`
}

NodeWChild is the recursive structure to store configuration which maps cpe to cve It is used only in mitre, and nvd has removed the recursive part

type Reference

type Reference struct {
	URL    string   `json:"url" bson:"url"`
	Source string   `json:"source" bson:"source"` // vulnerability@ncsc.ch. Mitre does not contains this field (there's refsource but not the same)
	Tags   []string `json:"tags,omitempty" bson:"tags,omitempty"`
}

References are supplemental information relevant to the vulnerability

type ReferenceCpe

type ReferenceCpe struct {
	// Ref is the URL link that related to the product
	Ref string `json:"ref" bson:"ref"`

	// Type is the type of the reference.
	// All possible value: "Advisory", "Change Log", "Product", "Project", "Vendor", "Version"
	// ref. https://csrc.nist.gov/schema/nvd/api/2.0/cpe_api_json_2.0.schema
	Type string `json:"type" bson:"type"`
}

ReferenceCpe is Internet resource for CPE

type Title

type Title struct {
	// Title is the name of the product. E.g., McAfee e-Business Server 8.5.2, GNU Emacs 22.1
	Title string `json:"title" bson:"title"`

	// Lang is the language of product. E.g., en
	Lang string `json:"lang" bson:"lang"`
}

Title is Human readable title for CPE

type Weaknesses

type Weaknesses struct {
	Source      string        `json:"source" bson:"source"`
	Type        string        `json:"type" bson:"type"`
	Description []Description `json:"description" bson:"description"` // E.g., value: "CWE-668"
}

Weaknesses is Common Weakness Enumeration (CWE), which was created to identify common software security weaknesses.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL