gx
chenyc
2025-06-12 7b72ac13a83764a662159d4a49b7fffb90476ecb
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
'use strict';
const Msg = require('./core/connection/msg').Msg;
const KillCursor = require('./core/connection/commands').KillCursor;
const GetMore = require('./core/connection/commands').GetMore;
const deepCopy = require('./utils').deepCopy;
 
/** Commands that we want to redact because of the sensitive nature of their contents */
const SENSITIVE_COMMANDS = new Set([
  'authenticate',
  'saslStart',
  'saslContinue',
  'getnonce',
  'createUser',
  'updateUser',
  'copydbgetnonce',
  'copydbsaslstart',
  'copydb'
]);
 
const HELLO_COMMANDS = new Set(['hello', 'ismaster', 'isMaster']);
 
const LEGACY_FIND_QUERY_MAP = {
  $query: 'filter',
  $orderby: 'sort',
  $hint: 'hint',
  $comment: 'comment',
  $maxScan: 'maxScan',
  $max: 'max',
  $min: 'min',
  $returnKey: 'returnKey',
  $showDiskLoc: 'showRecordId',
  $maxTimeMS: 'maxTimeMS',
  $snapshot: 'snapshot'
};
 
const LEGACY_FIND_OPTIONS_MAP = {
  numberToSkip: 'skip',
  numberToReturn: 'batchSize',
  returnFieldsSelector: 'projection'
};
 
const OP_QUERY_KEYS = [
  'tailable',
  'oplogReplay',
  'noCursorTimeout',
  'awaitData',
  'partial',
  'exhaust'
];
 
const collectionName = command => command.ns.split('.')[1];
 
const shouldRedactCommand = (commandName, cmd) =>
  SENSITIVE_COMMANDS.has(commandName) ||
  (HELLO_COMMANDS.has(commandName) && !!cmd.speculativeAuthenticate);
 
/**
 * Extract the actual command from the query, possibly upconverting if it's a legacy
 * format
 *
 * @param {Object} command the command
 */
const extractCommand = command => {
  let extractedCommand;
  if (command instanceof GetMore) {
    extractedCommand = {
      getMore: deepCopy(command.cursorId),
      collection: collectionName(command),
      batchSize: command.numberToReturn
    };
  } else if (command instanceof KillCursor) {
    extractedCommand = {
      killCursors: collectionName(command),
      cursors: deepCopy(command.cursorIds)
    };
  } else if (command instanceof Msg) {
    extractedCommand = deepCopy(command.command);
  } else if (command.query && command.query.$query) {
    let result;
    if (command.ns === 'admin.$cmd') {
      // upconvert legacy command
      result = Object.assign({}, command.query.$query);
    } else {
      // upconvert legacy find command
      result = { find: collectionName(command) };
      Object.keys(LEGACY_FIND_QUERY_MAP).forEach(key => {
        if (typeof command.query[key] !== 'undefined')
          result[LEGACY_FIND_QUERY_MAP[key]] = deepCopy(command.query[key]);
      });
    }
 
    Object.keys(LEGACY_FIND_OPTIONS_MAP).forEach(key => {
      if (typeof command[key] !== 'undefined')
        result[LEGACY_FIND_OPTIONS_MAP[key]] = deepCopy(command[key]);
    });
 
    OP_QUERY_KEYS.forEach(key => {
      if (command[key]) result[key] = command[key];
    });
 
    if (typeof command.pre32Limit !== 'undefined') {
      result.limit = command.pre32Limit;
    }
 
    if (command.query.$explain) {
      extractedCommand = { explain: result };
    } else {
      extractedCommand = result;
    }
  } else {
    extractedCommand = deepCopy(command.query || command);
  }
 
  const commandName = Object.keys(extractedCommand)[0];
  return {
    cmd: extractedCommand,
    name: commandName,
    shouldRedact: shouldRedactCommand(commandName, extractedCommand)
  };
};
 
module.exports = {
  extractCommand
};