File: sql.go

package info (click to toggle)
fever 1.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 920 kB
  • sloc: sh: 41; makefile: 18
file content (141 lines) | stat: -rw-r--r-- 5,282 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package db

// DCSO FEVER
// Copyright (c) 2017, DCSO GmbH

// SQLTrigramFunction is a plpgsql function to pull out indexable content from event JSON
const SQLTrigramFunction = `CREATE OR REPLACE FUNCTION trigram_string(payload jsonb)
RETURNS text
AS $$
DECLARE
	buffer varchar := '';
BEGIN
  -- trying in typical order of frequency
  IF payload->>'event_type' = 'dns'
  THEN
    RETURN payload->'dns'->>'rdata';
  END IF;
  IF payload->>'event_type' = 'http'
  THEN
    RETURN (payload->'http'->>'hostname') || '|' || (payload->'http'->>'url') || '|' || (payload->'http'->>'http_user_agent');
  END IF;
  IF payload->>'event_type' = 'tls'
  THEN
    RETURN (payload->'tls'->>'subject') ||'|' || (payload->'tls'->>'issuerdn') || '|' || (payload->'tls'->>'fingerprint');
  END IF;
  IF payload->>'event_type' = 'alert'
  THEN
    RETURN (payload->'alert'->>'payload_printable')  || '|' || (payload->'alert'->>'payload');
  END IF;
  IF payload->>'event_type' = 'smtp'
  THEN
    RETURN (payload->'smtp'->>'helo') || '|' || (payload->'smtp'->>'mail_from') || '|' || (payload->'smtp'->>'rcpt_to') || '|' || (payload->'email'->>'from') || '|' || (payload->'email'->>'to') || '|' || (payload->'email'->>'attachment');
  END IF;
  IF payload->>'event_type' = 'fileinfo'
  THEN
    RETURN (payload->'fileinfo'->>'filename') || '|' || (payload->'fileinfo'->>'md5');
  END IF;
	RETURN buffer;
END;
$$
LANGUAGE plpgsql
IMMUTABLE;`

// SQLCheckForTrigramExtension is an SQL query to check whether the trigram extension is available.
const SQLCheckForTrigramExtension = `SELECT COUNT(*) FROM pg_available_extensions WHERE name = 'pg_trgm';`

// SQLCreate is an SQL/DDL clause to create a new event table
const SQLCreate = `CREATE UNLOGGED TABLE IF NOT EXISTS "%s"
  (ts timestamp without time zone default now(),
   payload jsonb);
GRANT ALL PRIVILEGES ON TABLE "%s" to %s;`

// SQLCopy is an SQL/DDL clause to bulk insert a chunk of JSON into the database
const SQLCopy = `COPY "%s" (ts, payload) FROM STDIN WITH CSV DELIMITER E'\t' QUOTE E'\b'`

// SQLIndex is an SQL/DDL clause to create indexes on event tables
const SQLIndex = `CREATE INDEX ON "%s" (((payload->>'src_ip')::INET), ((payload->>'src_port')::INT));
CREATE INDEX ON "%s" (ts);
CREATE INDEX ON "%s" (((payload->>'dest_ip')::INET), ((payload->>'dest_port')::INT));
CREATE INDEX ON "%s" ((payload->>'event_type'));
CREATE INDEX ON "%s" using GIN (trigram_string(payload) gin_trgm_ops)`

// SQLGetTableSizes is an SQL query to obtain the names of tables in the current schema and their size in bytes.
const SQLGetTableSizes = `SELECT relname as table,
 pg_total_relation_size(relid) as size
 FROM pg_catalog.pg_statio_user_tables
 ORDER BY 1 DESC;`

// SQLGenericQuery is the main kind of query used to pull out event metadata.
const SQLGenericQuery = `SELECT * FROM all_events_query($1::text, $2::timestamp, $3::timestamp, $4::text[], $5::inet, $6::int, $7::inet, $8::int, $9::int);`

// SQLQueryAllEvents is a plpgsql function to enable queries over all hourly tables
// Example: SELECT COUNT(*) FROM all_events_query('WHERE trigram_string(payload) LIKE ''%%foo%%''');
const SQLQueryAllEvents = `CREATE OR REPLACE FUNCTION all_events_query(keyword text,
                                            start_time timestamp with time zone,
                                            end_time timestamp with time zone,
                                            event_type text[],
                                            ipsrc inet, portsrc int,
                                            ipdest inet, portdest int,
                                            mlimit int)
 RETURNS TABLE (ts timestamp, payload jsonb)
 AS $$
 DECLARE
   clause text;
   t RECORD;
   tables CURSOR FOR
        SELECT * FROM information_schema.tables
        WHERE table_name LIKE 'event%';
 BEGIN
  clause := '';
  OPEN tables;

  LOOP
    FETCH tables INTO t;
    EXIT WHEN NOT FOUND;
    IF clause != '' THEN
      clause := clause || ' UNION ALL ';
    END IF;
    clause := clause
      || 'SELECT * FROM ' || quote_ident(t.table_name)
      || ' WHERE ts BETWEEN ' || quote_literal(start_time)
      || ' AND ' || quote_literal(end_time);
    IF keyword IS NOT NULL THEN
      clause := clause
        || ' AND trigram_string(payload) LIKE ' || quote_literal(keyword);
    END IF;
    IF event_type IS NOT NULL THEN
      clause := clause
        || ' AND payload->>''event_type'' = ANY(' || quote_literal(event_type) || ')';
    END IF;
    IF ipsrc IS NOT NULL THEN
      clause := clause
        || ' AND (payload->>''src_ip'')::inet <<= inet ' || quote_literal(ipsrc);
    END IF;
    IF portsrc IS NOT NULL THEN
      clause := clause
        || ' AND payload->>''src_port'' = ' || quote_literal(portsrc);
    END IF;
    IF ipdest IS NOT NULL THEN
      clause := clause
        || ' AND (payload->>''dest_ip'')::inet <<= inet ' || quote_literal(ipdest);
    END IF;
    IF portdest IS NOT NULL THEN
      clause := clause
        || ' AND payload->>''dest_port'' = ' || quote_literal(portdest);
    END IF;
  END LOOP;

  IF mlimit IS NOT NULL THEN
    clause := clause || ' LIMIT ' || quote_literal(mlimit);
  END IF;

  RAISE NOTICE '%', clause;

  CLOSE tables;
  RETURN QUERY EXECUTE clause;
 END;
 $$
 LANGUAGE plpgsql
 STABLE;
`