Coverage Report

Created: 2025-08-05 08:11

/src/osquery/osquery/tables/system/posix/crontab.cpp
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * Copyright (c) 2014-present, The osquery authors
3
 *
4
 * This source code is licensed as defined by the LICENSE file found in the
5
 * root directory of this source tree.
6
 *
7
 * SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
8
 */
9
10
#include <vector>
11
12
#include <boost/algorithm/string/trim.hpp>
13
14
#include <osquery/core/core.h>
15
#include <osquery/core/tables.h>
16
#include <osquery/filesystem/filesystem.h>
17
#include <osquery/logger/logger.h>
18
#include <osquery/utils/conversions/split.h>
19
#include <osquery/worker/ipc/platform_table_container_ipc.h>
20
#include <osquery/worker/logging/glog/glog_logger.h>
21
22
namespace osquery {
23
namespace tables {
24
25
const std::string kSystemCron = "/etc/crontab";
26
27
const std::vector<std::string> kCronSearchDirs = {
28
    "/etc/cron.d/", // system all
29
    "/var/at/tabs/", // user mac:lion
30
    "/var/spool/cron/", // user linux:centos
31
    "/var/spool/cron/crontabs/", // user linux:debian
32
};
33
34
6
std::vector<std::string> cronFromFile(const std::string& path, Logger& logger) {
35
6
  std::string content;
36
6
  std::vector<std::string> cron_lines;
37
6
  if (!isReadable(path).ok()) {
38
3
    return cron_lines;
39
3
  }
40
41
3
  auto s = readFile(path, content);
42
3
  if (!s.ok()) {
43
0
    logger.log(google::GLOG_WARNING, s.getMessage());
44
0
    return cron_lines;
45
0
  }
46
47
3
  auto lines = split(content, "\n");
48
49
  // Only populate the lines that are not comments or blank.
50
6
  for (auto& line : lines) {
51
    // Cheat and use a non-const iteration, to inline trim.
52
6
    boost::trim(line);
53
6
    if (line.size() > 0 && line.at(0) != '#') {
54
6
      cron_lines.push_back(line);
55
6
    }
56
6
  }
57
58
3
  return cron_lines;
59
3
}
60
61
void genCronLine(const std::string& path,
62
                 const std::string& line,
63
6
                 QueryData& results) {
64
6
  Row r;
65
66
6
  r["path"] = path;
67
6
  auto columns = split(line, " \t");
68
69
6
  size_t index = 0;
70
6
  auto iterator = columns.begin();
71
84
  for (; iterator != columns.end(); ++iterator) {
72
78
    if (index == 0) {
73
6
      if ((*iterator).at(0) == '@') {
74
        // If the first value is an 'at' then skip to the command.
75
0
        r["event"] = *iterator;
76
0
        index = 5;
77
0
        continue;
78
0
      }
79
6
      r["minute"] = *iterator;
80
72
    } else if (index == 1) {
81
6
      r["hour"] = *iterator;
82
66
    } else if (index == 2) {
83
6
      r["day_of_month"] = *iterator;
84
60
    } else if (index == 3) {
85
6
      r["month"] = *iterator;
86
54
    } else if (index == 4) {
87
6
      r["day_of_week"] = *iterator;
88
48
    } else if (index == 5) {
89
6
      r["command"] = *iterator;
90
42
    } else {
91
      // Long if switch to handle command breaks from space delim.
92
42
      r["command"] += " " + *iterator;
93
42
    }
94
78
    index++;
95
78
  }
96
97
6
  if (r["command"].size() == 0) {
98
    // The line was not well-formed, perhaps it was a variable?
99
0
    return;
100
0
  }
101
6
  r["pid_with_namespace"] = "0";
102
103
6
  results.push_back(r);
104
6
}
105
106
3
QueryData genCronTabImpl(QueryContext& context, Logger& logger) {
107
3
  QueryData results;
108
3
  std::vector<std::string> file_list;
109
110
3
  file_list.push_back(kSystemCron);
111
112
12
  for (const auto& cron_dir : kCronSearchDirs) {
113
12
    osquery::listFilesInDirectory(cron_dir, file_list);
114
12
  }
115
116
6
  for (const auto& file_path : file_list) {
117
6
    auto lines = cronFromFile(file_path, logger);
118
6
    for (const auto& line : lines) {
119
6
      genCronLine(file_path, line, results);
120
6
    }
121
6
  }
122
123
3
  return results;
124
3
}
125
126
3
QueryData genCronTab(QueryContext& context) {
127
3
  if (hasNamespaceConstraint(context)) {
128
0
    return generateInNamespace(context, "crontab", genCronTabImpl);
129
3
  } else {
130
3
    GLOGLogger logger;
131
3
    return genCronTabImpl(context, logger);
132
3
  }
133
3
}
134
} // namespace tables
135
} // namespace osquery