• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OISF / suricata / 22553697083

01 Mar 2026 09:58PM UTC coverage: 75.673% (+2.0%) from 73.687%
22553697083

Pull #14925

github

web-flow
Merge 288827f07 into 90823fa90
Pull Request #14925: hs: false positive coverity warning in a reference string v1

241615 of 319288 relevant lines covered (75.67%)

1333554.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.7
/rust/src/pgsql/logger.rs
1
/* Copyright (C) 2022 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17

18
// Author: Juliana Fajardini <jufajardini@gmail.com>
19

20
//! PostgreSQL parser json logger
21

22
use crate::jsonbuilder::{JsonBuilder, JsonError};
23
use crate::pgsql::parser::*;
24
use crate::pgsql::pgsql::*;
25
use std;
26

27
pub const PGSQL_LOG_PASSWORDS: u32 = BIT_U32!(0);
28

29
fn log_pgsql(tx: &PgsqlTransaction, flags: u32, js: &mut JsonBuilder) -> Result<(), JsonError> {
75✔
30
    js.open_object("pgsql")?;
75✔
31
    js.set_uint("tx_id", tx.tx_id)?;
75✔
32
    if !tx.requests.is_empty() {
75✔
33
        // For now, even if 'requests' is an array, we don't need to log it as such, as
34
        // there should be no duplicated messages, and there should be no more than 2 requests per tx
35
        debug_validate_bug_on!(tx.requests.len() > 2);
36
        js.open_object("request")?;
75✔
37
        log_request(tx, flags, js)?;
75✔
38
        js.close()?;
75✔
39
    } else if tx.responses.is_empty() {
×
40
        SCLogDebug!("Suricata created an empty PGSQL transaction");
41
        // TODO Log anomaly event?
42
        // if there are no transactions, there's nothing more to be logged
43
        js.close()?;
×
44
        return Ok(());
×
45
    }
×
46

47
    if !tx.responses.is_empty() {
75✔
48
        SCLogDebug!("Responses length: {}", tx.responses.len());
49
        js.set_object("response", &log_response_object(tx)?)?;
65✔
50
    }
10✔
51
    js.close()?;
75✔
52

53
    Ok(())
75✔
54
}
75✔
55

56
fn log_request(tx: &PgsqlTransaction, flags: u32, js: &mut JsonBuilder) -> Result<(), JsonError> {
75✔
57
    // CopyFail, ConsolidatedCopyDataIn, CopyDone
75✔
58
    let mut duplicated_messages: [u8; 3] = [0, 0, 0];
75✔
59
    for req in &tx.requests {
151✔
60
        SCLogDebug!("Suricata requests length: {}", tx.requests.len());
61
        match req {
76✔
62
            PgsqlFEMessage::StartupMessage(StartupPacket {
63
                length: _,
64
                proto_major,
12✔
65
                proto_minor,
12✔
66
                params,
12✔
67
            }) => {
12✔
68
                let proto = format!("{}.{}", proto_major, proto_minor);
12✔
69
                js.set_string("protocol_version", &proto)?;
12✔
70
                js.set_object("startup_parameters", &log_startup_parameters(params)?)?;
12✔
71
            }
72
            PgsqlFEMessage::SSLRequest(_) => {
73
                js.set_string("message", "SSL Request")?;
11✔
74
            }
75
            PgsqlFEMessage::SASLInitialResponse(SASLInitialResponsePacket {
76
                identifier: _,
77
                length: _,
78
                auth_mechanism,
1✔
79
                param_length: _,
1✔
80
                sasl_param,
1✔
81
            }) => {
1✔
82
                js.set_string("sasl_authentication_mechanism", auth_mechanism.to_str())?;
1✔
83
                js.set_string_from_bytes("sasl_param", sasl_param)?;
1✔
84
            }
85
            PgsqlFEMessage::PasswordMessage(RegularPacket {
86
                identifier: _,
87
                length: _,
88
                payload,
7✔
89
            }) => {
7✔
90
                if flags & PGSQL_LOG_PASSWORDS != 0 {
7✔
91
                    js.set_string_from_bytes(req.to_str(), payload)?;
×
92
                } else {
93
                    js.set_bool("password_redacted", true)?;
7✔
94
                }
95
            }
96
            PgsqlFEMessage::SASLResponse(RegularPacket {
97
                identifier: _,
98
                length: _,
99
                payload,
1✔
100
            }) => {
1✔
101
                js.set_string_from_bytes("sasl_response", payload)?;
1✔
102
            }
103
            PgsqlFEMessage::SimpleQuery(RegularPacket {
104
                identifier: _,
105
                length: _,
106
                payload,
32✔
107
            }) => {
32✔
108
                js.set_string_from_bytes(req.to_str(), payload)?;
32✔
109
            }
110
            PgsqlFEMessage::CopyFail(RegularPacket {
111
                identifier: _,
112
                length: _,
113
                payload,
×
114
            }) => {
×
115
                js.set_string_from_bytes(req.to_str(), payload)?;
×
116
                duplicated_messages[0] += 1;
×
117
                debug_validate_bug_on!(duplicated_messages[0] > 1);
118
            }
119
            PgsqlFEMessage::CancelRequest(CancelRequestMessage { pid, backend_key }) => {
1✔
120
                js.set_string("message", "cancel_request")?;
1✔
121
                js.set_uint("process_id", *pid)?;
1✔
122
                js.set_uint("secret_key", *backend_key)?;
1✔
123
            }
124
            PgsqlFEMessage::ConsolidatedCopyDataIn(ConsolidatedDataRowPacket {
125
                identifier: _,
126
                row_cnt,
1✔
127
                data_size,
1✔
128
            }) => {
1✔
129
                js.open_object(req.to_str())?;
1✔
130
                js.set_uint("msg_count", *row_cnt)?;
1✔
131
                js.set_uint("data_size", *data_size)?;
1✔
132
                js.close()?;
1✔
133
                duplicated_messages[1] += 1;
1✔
134
                debug_validate_bug_on!(duplicated_messages[1] > 1);
135
            }
136
            PgsqlFEMessage::CopyDone(_) => {
137
                js.set_string("message", req.to_str())?;
1✔
138
                duplicated_messages[2] += 1;
1✔
139
                debug_validate_bug_on!(duplicated_messages[2] > 1);
140
            }
141
            PgsqlFEMessage::Terminate(_) => {
142
                js.set_string("message", req.to_str())?;
9✔
143
            }
144
            PgsqlFEMessage::UnknownMessageType(RegularPacket {
145
                identifier: _,
146
                length: _,
147
                payload: _,
148
            }) => {
×
149
                // We don't want to log these, for now. Cf redmine: #6576
×
150
            }
×
151
        }
152
    }
153
    Ok(())
75✔
154
}
75✔
155

156
fn log_response_object(tx: &PgsqlTransaction) -> Result<JsonBuilder, JsonError> {
65✔
157
    let mut jb = JsonBuilder::try_new_object()?;
65✔
158
    let mut array_open = false;
65✔
159
    for response in &tx.responses {
306✔
160
        if let PgsqlBEMessage::ParameterStatus(msg) = response {
241✔
161
            if !array_open {
106✔
162
                jb.open_array("parameter_status")?;
10✔
163
                array_open = true;
10✔
164
            }
96✔
165
            jb.append_object(&log_pgsql_param(&msg.param)?)?;
106✔
166
        } else {
167
            if array_open {
135✔
168
                jb.close()?;
10✔
169
                array_open = false;
10✔
170
            }
125✔
171
            log_response(response, &mut jb)?;
135✔
172
        }
173
    }
174
    jb.close()?;
65✔
175
    Ok(jb)
65✔
176
}
65✔
177

178
fn log_response(res: &PgsqlBEMessage, jb: &mut JsonBuilder) -> Result<(), JsonError> {
135✔
179
    match res {
135✔
180
        PgsqlBEMessage::SSLResponse(message) => {
11✔
181
            if let SSLResponseMessage::SSLAccepted = message {
11✔
182
                jb.set_bool("ssl_accepted", true)?;
1✔
183
            } else {
184
                jb.set_bool("ssl_accepted", false)?;
10✔
185
            }
186
        }
187
        PgsqlBEMessage::NoticeResponse(ErrorNoticeMessage {
188
            identifier: _,
189
            length: _,
190
            message_body,
×
191
        })
192
        | PgsqlBEMessage::ErrorResponse(ErrorNoticeMessage {
193
            identifier: _,
194
            length: _,
195
            message_body,
6✔
196
        }) => {
197
            log_error_notice_field_types(message_body, jb)?;
6✔
198
        }
199
        PgsqlBEMessage::AuthenticationMD5Password(AuthenticationMessage {
200
            identifier: _,
201
            length: _,
202
            auth_type: _,
203
            payload,
8✔
204
        })
205
        | PgsqlBEMessage::AuthenticationSSPI(AuthenticationMessage {
206
            identifier: _,
207
            length: _,
208
            auth_type: _,
209
            payload,
×
210
        })
211
        | PgsqlBEMessage::AuthenticationSASLFinal(AuthenticationMessage {
212
            identifier: _,
213
            length: _,
214
            auth_type: _,
215
            payload,
1✔
216
        })
217
        | PgsqlBEMessage::CommandComplete(RegularPacket {
218
            identifier: _,
219
            length: _,
220
            payload,
26✔
221
        }) => {
222
            jb.set_string_from_bytes(res.to_str(), payload)?;
35✔
223
        }
224
        PgsqlBEMessage::UnknownMessageType(RegularPacket {
225
            identifier: _,
226
            length: _,
227
            payload: _,
228
        }) => {
×
229
            // We don't want to log these, for now. Cf redmine: #6576
×
230
        }
×
231
        PgsqlBEMessage::AuthenticationOk(_)
232
        | PgsqlBEMessage::AuthenticationCleartextPassword(_)
233
        | PgsqlBEMessage::AuthenticationSASL(_)
234
        | PgsqlBEMessage::AuthenticationSASLContinue(_)
235
        | PgsqlBEMessage::CopyDone(_) => {
236
            jb.set_string("message", res.to_str())?;
14✔
237
        }
238
        PgsqlBEMessage::ParameterStatus(ParameterStatusMessage {
239
            identifier: _,
240
            length: _,
241
            param: _,
242
        }) => {
×
243
            // We take care of these elsewhere
×
244
        }
×
245
        PgsqlBEMessage::CopyOutResponse(CopyResponse {
246
            identifier: _,
247
            length: _,
248
            column_cnt,
1✔
249
        })
250
        | PgsqlBEMessage::CopyInResponse(CopyResponse {
251
            identifier: _,
252
            length: _,
253
            column_cnt,
1✔
254
        }) => {
255
            jb.open_object(res.to_str())?;
2✔
256
            jb.set_uint("columns", *column_cnt)?;
2✔
257
            jb.close()?;
2✔
258
        }
259
        PgsqlBEMessage::BackendKeyData(BackendKeyDataMessage {
260
            identifier: _,
261
            length: _,
262
            backend_pid,
10✔
263
            secret_key,
10✔
264
        }) => {
10✔
265
            jb.set_uint("process_id", *backend_pid)?;
10✔
266
            jb.set_uint("secret_key", *secret_key)?;
10✔
267
        }
268
        PgsqlBEMessage::ReadyForQuery(ReadyForQueryMessage {
269
            identifier: _,
270
            length: _,
271
            transaction_status: _,
272
        }) => {
42✔
273
            // We don't want to log this one
42✔
274
        }
42✔
275
        PgsqlBEMessage::ConsolidatedCopyDataOut(ConsolidatedDataRowPacket {
276
            identifier: _,
277
            row_cnt,
1✔
278
            data_size,
1✔
279
        }) => {
1✔
280
            jb.open_object(res.to_str())?;
1✔
281
            jb.set_uint("row_count", *row_cnt)?;
1✔
282
            jb.set_uint("data_size", *data_size)?;
1✔
283
            jb.close()?;
1✔
284
        }
285
        PgsqlBEMessage::RowDescription(RowDescriptionMessage {
286
            identifier: _,
287
            length: _,
288
            field_count,
7✔
289
            fields: _,
7✔
290
        }) => {
7✔
291
            jb.set_uint("field_count", *field_count)?;
7✔
292
        }
293
        PgsqlBEMessage::ConsolidatedDataRow(ConsolidatedDataRowPacket {
294
            identifier: _,
295
            row_cnt,
7✔
296
            data_size,
7✔
297
        }) => {
7✔
298
            jb.set_uint("data_rows", *row_cnt)?;
7✔
299
            jb.set_uint("data_size", *data_size)?;
7✔
300
        }
301
        PgsqlBEMessage::NotificationResponse(NotificationResponse {
302
            identifier: _,
303
            length: _,
304
            pid,
×
305
            channel_name,
×
306
            payload,
×
307
        }) => {
×
308
            jb.set_uint("pid", *pid)?;
×
309
            jb.set_string_from_bytes("channel_name", channel_name)?;
×
310
            jb.set_string_from_bytes("payload", payload)?;
×
311
        }
312
    }
313
    Ok(())
135✔
314
}
135✔
315

316
fn log_error_notice_field_types(
6✔
317
    error_fields: &Vec<PgsqlErrorNoticeMessageField>, jb: &mut JsonBuilder,
6✔
318
) -> Result<(), JsonError> {
6✔
319
    for field in error_fields {
48✔
320
        jb.set_string_from_bytes(field.field_type.to_str(), &field.field_value)?;
42✔
321
    }
322
    Ok(())
6✔
323
}
6✔
324

325
fn log_startup_parameters(params: &PgsqlStartupParameters) -> Result<JsonBuilder, JsonError> {
12✔
326
    let mut jb = JsonBuilder::try_new_object()?;
12✔
327
    // User is a mandatory field in a pgsql message
328
    jb.set_string_from_bytes("user", &params.user.value)?;
12✔
329
    if let Some(parameters) = &params.optional_params {
12✔
330
        jb.open_array("optional_parameters")?;
12✔
331
        for parameter in parameters {
49✔
332
            jb.append_object(&log_pgsql_param(parameter)?)?;
37✔
333
        }
334
        jb.close()?;
12✔
335
    }
×
336

337
    jb.close()?;
12✔
338
    Ok(jb)
12✔
339
}
12✔
340

341
fn log_pgsql_param(param: &PgsqlParameter) -> Result<JsonBuilder, JsonError> {
143✔
342
    let mut jb = JsonBuilder::try_new_object()?;
143✔
343
    jb.set_string_from_bytes(param.name.to_str(), &param.value)?;
143✔
344
    jb.close()?;
143✔
345
    Ok(jb)
143✔
346
}
143✔
347

348
#[no_mangle]
349
pub unsafe extern "C" fn SCPgsqlLogger(
75✔
350
    tx: *mut std::os::raw::c_void, flags: u32, js: &mut JsonBuilder,
75✔
351
) -> bool {
75✔
352
    let tx_pgsql = cast_pointer!(tx, PgsqlTransaction);
75✔
353
    SCLogDebug!(
75✔
354
        "----------- PGSQL rs_pgsql_logger call. Tx id is {:?}",
75✔
355
        tx_pgsql.tx_id
75✔
356
    );
75✔
357
    log_pgsql(tx_pgsql, flags, js).is_ok()
75✔
358
}
75✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc