From 0ff68170254ca1d5e29147650ab32d2b5b1792ee Mon Sep 17 00:00:00 2001 From: Gregory Duchatelet Date: Wed, 15 May 2019 10:31:15 +0200 Subject: [PATCH 1/5] new option: -u, parse a user crontab --- chkcrontab | 2 ++ chkcrontab_lib.py | 12 +++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/chkcrontab b/chkcrontab index 3ad4291..f09ca3c 100755 --- a/chkcrontab +++ b/chkcrontab @@ -46,6 +46,8 @@ def main(argv): dest='whitelisted_users', help='user to ignore when warning of unrecognized users; may be passed multiple times', default=['postgres', 'buildbot']) + parser.add_argument('-u', '--user', action='store_true', + dest='user', help='crontab user') parser.add_argument('-n', '--no-check-passwd', action='store_false', dest='check_passwd', help='disable user lookup') parser.add_argument('-q', '--quiet', action='store_true', diff --git a/chkcrontab_lib.py b/chkcrontab_lib.py index 344dbd7..880f98d 100755 --- a/chkcrontab_lib.py +++ b/chkcrontab_lib.py @@ -716,6 +716,8 @@ def ValidateAndLog(self, log): # User checks. if self.user in self.whitelisted_users: pass + elif self.user is False: + pass elif len(self.user) > 31: log.LineError(log.MSG_INVALID_USER, 'Username too long "%s"' % self.user) @@ -806,7 +808,8 @@ def ValidateAndLog(self, log): class CronLineFactory(object): """Classify a line in a cron field by what type of line it is.""" - def __init__(self): + def __init__(self, is_user_crontab=False): + self.is_user_crontab = is_user_crontab pass def ParseLine(self, line, options ): @@ -857,7 +860,10 @@ def ParseLine(self, line, options ): 'month': match.groups()[3], 'day of week': match.groups()[4], } - return CronLineTime(field, match.groups()[5], match.groups()[6], options) + if self.is_user_crontab: + return CronLineTime(field, False, match.groups()[5] + " " + match.groups()[6], options) + else: + return CronLineTime(field, match.groups()[5], match.groups()[6], options) return CronLineUnknown() @@ -1100,7 +1106,7 @@ def check_crontab(arguments, log): ' [A-Za-z0-9_-]+ .') line_no = 0 - cron_line_factory = CronLineFactory() + cron_line_factory = CronLineFactory(arguments.user) with open(arguments.crontab, 'r') as crontab_f: for line in crontab_f: missing_newline = line[-1] != "\n" From 9ba82c5c18296982a32ba9cd1cc34489a98af6ee Mon Sep 17 00:00:00 2001 From: Gregory Duchatelet Date: Thu, 28 Jan 2021 16:20:56 +0100 Subject: [PATCH 2/5] warn only, do not fail --- chkcrontab | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chkcrontab b/chkcrontab index f09ca3c..a7f640c 100755 --- a/chkcrontab +++ b/chkcrontab @@ -59,4 +59,5 @@ def main(argv): return check.check_crontab(args, check.LogCounter(args.quiet)) if __name__ == '__main__': - sys.exit(main(sys.argv)) + main(sys.argv) + sys.exit(0) From 3cb72e3206670463f4a5bb8a5b7337d61ee54f77 Mon Sep 17 00:00:00 2001 From: Gregory Duchatelet Date: Tue, 2 Aug 2022 08:41:15 +0200 Subject: [PATCH 3/5] moved to python3 --- chkcrontab | 8 ++------ chkcrontab_lib.py | 2 +- tests/test_check.py | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/chkcrontab b/chkcrontab index a7f640c..513ed10 100755 --- a/chkcrontab +++ b/chkcrontab @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # # Copyright 2011 Google Inc. All Rights Reserved. # @@ -31,16 +31,12 @@ import chkcrontab_lib as check from optparse import OptionParser from argparse import ArgumentParser -try: - from _version import __version__ -except ImportError: - __version__ = 'unknown' def main(argv): """main program.""" parser = ArgumentParser( description='Parse crontab files; check for potential syntax errors.', - version=__version__) + ) parser.add_argument('crontab', help='crontab file to parse') parser.add_argument('-w', '--whitelist', action='append', dest='whitelisted_users', diff --git a/chkcrontab_lib.py b/chkcrontab_lib.py index 880f98d..57a7c1f 100755 --- a/chkcrontab_lib.py +++ b/chkcrontab_lib.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # # Copyright 2011 Google Inc. All Rights Reserved. # diff --git a/tests/test_check.py b/tests/test_check.py index 642f7fc..930a48d 100755 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # # Copyright 2011 Google Inc. All Rights Reserved. # From 29b390388cd1997ec99b4d7675cd7a75a13447a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20B=C3=B6ck?= <990588+hannob@users.noreply.github.com> Date: Thu, 18 Jan 2024 11:18:13 +0100 Subject: [PATCH 4/5] Use r strings to avoid SyntaxWarning in python 3.12 --- chkcrontab_lib.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/chkcrontab_lib.py b/chkcrontab_lib.py index 57a7c1f..6334a77 100755 --- a/chkcrontab_lib.py +++ b/chkcrontab_lib.py @@ -822,12 +822,12 @@ def ParseLine(self, line, options ): Returns: A CronLine* class (must have a ValidateAndLog method). """ - chkcrontab_cmd = re.compile('##*\s*chkcrontab:\s*(.*)=(.*)') - assignment_line_re = re.compile('[a-zA-Z_][a-zA-Z0-9_]*\s*=(.*)') - at_line_re = re.compile('@(\S+)\s+(\S+)\s+(.*)') - cron_time_field_re = '[\*0-9a-zA-Z,/-]+' + chkcrontab_cmd = re.compile(r'##*\s*chkcrontab:\s*(.*)=(.*)') + assignment_line_re = re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*\s*=(.*)') + at_line_re = re.compile(r'@(\S+)\s+(\S+)\s+(.*)') + cron_time_field_re = r'[\*0-9a-zA-Z,/-]+' time_field_job_line_re = re.compile( - '^\s*(%s)\s+(%s)\s+(%s)\s+(%s)\s+(%s)\s+(\S+)\s+(.*)' % + r'^\s*(%s)\s+(%s)\s+(%s)\s+(%s)\s+(%s)\s+(\S+)\s+(.*)' % (cron_time_field_re, cron_time_field_re, cron_time_field_re, cron_time_field_re, cron_time_field_re)) From d92ae2267b5ac8cbd13e23b4438eb2413162b519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20B=C3=B6ck?= <990588+hannob@users.noreply.github.com> Date: Fri, 19 Jan 2024 13:44:16 +0100 Subject: [PATCH 5/5] Fix user crontab regular expression The existing code causes a LINE_ERROR for cronjobs that consist of a single command without any parameters. This is due to the fact that the regular expression expects a username string and a command after that, and the conditional is_user_crontab code concats these two strings. Instead of doing that, this commit introduces a separate regular expression for user crontab lines. --- chkcrontab_lib.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/chkcrontab_lib.py b/chkcrontab_lib.py index 6334a77..bfe56c8 100755 --- a/chkcrontab_lib.py +++ b/chkcrontab_lib.py @@ -826,10 +826,16 @@ def ParseLine(self, line, options ): assignment_line_re = re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*\s*=(.*)') at_line_re = re.compile(r'@(\S+)\s+(\S+)\s+(.*)') cron_time_field_re = r'[\*0-9a-zA-Z,/-]+' - time_field_job_line_re = re.compile( - r'^\s*(%s)\s+(%s)\s+(%s)\s+(%s)\s+(%s)\s+(\S+)\s+(.*)' % - (cron_time_field_re, cron_time_field_re, cron_time_field_re, - cron_time_field_re, cron_time_field_re)) + if self.is_user_crontab: + time_field_job_line_re = re.compile( + r'^\s*(%s)\s+(%s)\s+(%s)\s+(%s)\s+(%s)\s+(.*)' % + (cron_time_field_re, cron_time_field_re, cron_time_field_re, + cron_time_field_re, cron_time_field_re)) + else: + time_field_job_line_re = re.compile( + r'^\s*(%s)\s+(%s)\s+(%s)\s+(%s)\s+(%s)\s+(\S+)\s+(.*)' % + (cron_time_field_re, cron_time_field_re, cron_time_field_re, + cron_time_field_re, cron_time_field_re)) if not line: return CronLineEmpty() @@ -861,7 +867,7 @@ def ParseLine(self, line, options ): 'day of week': match.groups()[4], } if self.is_user_crontab: - return CronLineTime(field, False, match.groups()[5] + " " + match.groups()[6], options) + return CronLineTime(field, False, match.groups()[5], options) else: return CronLineTime(field, match.groups()[5], match.groups()[6], options)