def test_load_signature_map(self): sigmap = maps.SignatureMap() with open("tests/sid-msg.map") as infile: sigmap.load_signature_map(infile) # Get a basic signature. sig = sigmap.get(1, 2000356) self.assertTrue(sig is not None) self.assertEqual(1, sig["gid"]) self.assertEqual(2000356, sig["sid"]) self.assertEqual("ET POLICY IRC connection", sig["msg"]) self.assertEqual(len(sig["ref"]), 1) self.assertEqual("url,doc.emergingthreats.net/2000356", sig["ref"][0]) # Try again but with a gid of 3. self.assertEqual(sig, sigmap.get(3, 2000356)) # This signature has multiple refs. sig = sigmap.get(1, 2000373) self.assertEqual(3, len(sig["ref"])) sig = sigmap.get(1, 71918985) self.assertEqual( "SN: Inbound TCP traffic from suspect network (AS29073 - NL)", sig["msg"])
def rehash(self): try: self.sigmap = maps.SignatureMap() self.sigmap.load_generator_map( open(self.config['global']['generator_map'])) self.sigmap.load_signature_map( open(self.config['global']['signature_map'])) self.sigmap_timestamp = os.path.getmtime( self.config['global']['signature_map']) self.genmap_timestamp = os.path.getmtime( self.config['global']['generator_map']) except Exception, e: raise CharlotteConfigError("Error reading signature maps: %s" % e)
def test_load_signature_v2_map(self): sigmap = maps.SignatureMap() sigmap.load_signature_map(open("tests/sid-msg-v2.map")) sig = sigmap.get(1, 2495) self.assertEquals(1, sig["gid"]) self.assertEquals(2495, sig["sid"]) self.assertEquals("misc-attack", sig["classification"]) self.assertEquals(0, sig["priority"]) self.assertEquals( "GPL NETBIOS SMB DCEPRC ORPCThis request flood attempt", sig["msg"]) self.assertEquals(4, len(sig["ref"]))
def test_load_generator_map(self): sigmap = maps.SignatureMap() sigmap.load_generator_map(open("tests/gen-msg.map")) sig = sigmap.get(1, 1) self.assertTrue(sig is not None) self.assertEquals(1, sig["gid"]) self.assertEquals(1, sig["sid"]) self.assertEquals("snort general alert", sig["msg"]) sig = sigmap.get(139, 1) self.assertTrue(sig is not None) self.assertEquals(139, sig["gid"]) self.assertEquals(1, sig["sid"]) self.assertEquals( "sensitive_data: sensitive data global threshold exceeded", sig["msg"])
def get_events(cls, path=None, file=None): if path is None: path = cls.CONFIG['DEFAULT_LOG_PATH'] if file is None: file = cls.CONFIG['DEFAULT_LOG_FILE'] events = [] try: sigmap = maps.SignatureMap() sigmap.load_generator_map(open(cls.CONFIG.get('GEN_MAP_PATH'))) sigmap.load_signature_map(open(cls.CONFIG.get('SIG_MAP_PATH'))) reader = unified2.SpoolRecordReader(path, file, follow=False) for record in reader: if isinstance(record, unified2.Event): event_details = sigmap.get(record['generator-id'], record['signature-id']) event = NetworkEvent(cls.__name__) events.append(event) event.src_ip = record['source-ip'] event.dest_ip = record['destination-ip'] event.protocol = record['protocol'] event.src_port = record['sport-itype'] event.dest_port = record['dport-icode'] event.signature = event_details[ 'msg'] if event_details else 'SID: {}'.format( record['signature-id']) if event_details: event.reference = json.dumps(event_details['ref']) except Exception as e: flash(e.__class__.__name__, "error") return events
def test_load_signature_map(self): sigmap = maps.SignatureMap() sigmap.load_signature_map(open("tests/sid-msg.map")) # Get a basic signature. sig = sigmap.get(1, 2000356) self.assertTrue(sig is not None) self.assertEquals(1, sig["gid"]) self.assertEquals(2000356, sig["sid"]) self.assertEquals("ET POLICY IRC connection", sig["msg"]) self.assertEquals(len(sig["ref"]), 1) self.assertEquals("url,doc.emergingthreats.net/2000356", sig["ref"][0]) # Try again but with a gid of 3. self.assertEquals(sig, sigmap.get(3, 2000356)) # This signature has multiple refs. sig = sigmap.get(1, 2000373) print(sig) self.assertEquals(3, len(sig["ref"]))
def main(): msgmap = maps.SignatureMap() classmap = maps.ClassificationMap() parser = argparse.ArgumentParser( fromfile_prefix_chars='@', epilog=epilog) parser.add_argument( "-C", dest="classification_path", metavar="<classification.config>", help="path to classification config") parser.add_argument( "-S", dest="sidmsgmap_path", metavar="<msg-msg.map>", help="path to sid-msg.map") parser.add_argument( "-G", dest="genmsgmap_path", metavar="<gen-msg.map>", help="path to gen-msg.map") parser.add_argument( "--snort-conf", dest="snort_conf", metavar="<snort.conf>", help="attempt to load classifications and map files based on the " "location of the snort.conf") parser.add_argument( "--directory", metavar="<spool directory>", help="spool directory (eg: /var/log/snort)") parser.add_argument( "--prefix", metavar="<spool file prefix>", help="spool filename prefix (eg: unified2.log)") parser.add_argument( "--bookmark", action="store_true", default=False, help="enable bookmarking") parser.add_argument( "--follow", action="store_true", default=False, help="follow files/continuous mode (spool mode only)") parser.add_argument( "--delete", action="store_true", default=False, help="delete spool files") parser.add_argument( "-o", "--output", metavar="<filename>", help="output filename (eg: /var/log/snort/alerts.json") parser.add_argument( "--stdout", action="store_true", default=False, help="also log to stdout if --output is a file") parser.add_argument( "--packet-printable", action="store_true", default=False, help="add packet_printable field to events") parser.add_argument( "--packet-hex", action="store_true", default=False, help="add packet_hex field to events") parser.add_argument( "filenames", nargs="*") args = parser.parse_args() if args.snort_conf: load_from_snort_conf(args.snort_conf, classmap, msgmap) if args.classification_path: classmap.load_from_file( open(os.path.expanduser(args.classification_path))) if args.genmsgmap_path: msgmap.load_generator_map(open(os.path.expanduser(args.genmsgmap_path))) if args.sidmsgmap_path: msgmap.load_signature_map(open(os.path.expanduser(args.sidmsgmap_path))) if msgmap.size() == 0: LOG.warning("WARNING: No alert message map entries loaded.") else: LOG.info("Loaded %s rule message map entries.", msgmap.size()) if classmap.size() == 0: LOG.warning("WARNING: No classifications loaded.") else: LOG.info("Loaded %s classifications.", classmap.size()) eve_filter = EveFilter( msgmap, classmap, packet_printable=args.packet_printable, packet_hex=args.packet_hex) outputs = [] if args.output: outputs.append(OutputWrapper(args.output)) if args.stdout: outputs.append(OutputWrapper("-", sys.stdout)) else: outputs.append(OutputWrapper("-", sys.stdout)) writer = Writer(outputs, eve_filter) bookmark = None if args.directory and args.prefix: init_filename, init_offset = None, None if args.bookmark: bookmark = unified2.Unified2Bookmark( args.directory, args.prefix) init_filename, init_offset = bookmark.get() rollover_handler = RolloverHandler(args.delete) reader = unified2.SpoolRecordReader( directory=args.directory, prefix=args.prefix, follow=args.follow, init_filename=init_filename, init_offset=init_offset, rollover_hook=rollover_handler.on_rollover) elif args.filenames: if args.bookmark: LOG.error("Bookmarking not supported in file mode, exiting.") return 1 reader = unified2.FileRecordReader(*args.filenames) else: print("nothing to do.") return event = None last_record_time = time.time() queue = [] while True: flush = False record = reader.next() done = False if not record: if event and time.time() - last_record_time > 1.0: queue.append(event) event = None flush = True else: if args.follow: time.sleep(0.01) else: if event: queue.append(event) flush = True done = True else: last_record_time = time.time() if isinstance(record, unified2.Event): if event is not None: queue.append(event) flush = True event = record elif isinstance(record, unified2.ExtraData): if not event: continue event["extra-data"].append(record) elif isinstance(record, unified2.Packet): if not event: queue.append(record) flush = True else: if "packet" in event: queue.append(record) else: event["packet"] = record if flush: for record in queue: writer.write(record) if args.bookmark and bookmark: location = reader.tell() bookmark.update(*location) queue = [] if done: break
def main(): msgmap = maps.SignatureMap() classmap = maps.ClassificationMap() parser = argparse.ArgumentParser(fromfile_prefix_chars='@', epilog=epilog) parser.add_argument("-C", dest="classification_path", metavar="<classification.config>", help="path to classification config") parser.add_argument("-S", dest="sidmsgmap_path", metavar="<msg-msg.map>", help="path to sid-msg.map") parser.add_argument("-G", dest="genmsgmap_path", metavar="<gen-msg.map>", help="path to gen-msg.map") parser.add_argument( "--snort-conf", dest="snort_conf", metavar="<snort.conf>", help="attempt to load classifications and map files based on the " "location of the snort.conf") parser.add_argument("--directory", metavar="<spool directory>", help="spool directory (eg: /var/log/snort)") parser.add_argument("--prefix", metavar="<spool file prefix>", help="spool filename prefix (eg: unified2.log)") parser.add_argument("--bookmark", metavar="<filename>", help="enable bookmarking") parser.add_argument("--follow", action="store_true", default=False, help="follow files/continuous mode (spool mode only)") parser.add_argument("--cs", metavar="<cybersift ip>", help="Specify the CyberSift Server IP Address") parser.add_argument("--delete", action="store_true", default=False, help="delete spool files") parser.add_argument("--output", metavar="<filename>", help="output filename (eg: /var/log/snort/alerts.json") parser.add_argument("--stdout", action="store_true", default=False, help="also log to stdout if --output is a file") parser.add_argument( "--sort-keys", dest="sort_keys", action="store_true", default=False, help="the output of dictionaries will be sorted by key") parser.add_argument("--verbose", action="store_true", default=False, help="be more verbose") parser.add_argument("filenames", nargs="*") args = parser.parse_args() if args.verbose: LOG.setLevel(logging.DEBUG) if args.snort_conf: load_from_snort_conf(args.snort_conf, classmap, msgmap) if args.cs: elastic_ip = args.cs es = Elasticsearch( ["http://" + elastic_ip + ":80/cybersift_elasticsearch/"], timeout=600) else: LOG.error("Cannot proceed without a valid CyberSift IP") sys.exit(1) if args.classification_path: classmap.load_from_file( open(os.path.expanduser(args.classification_path))) if args.genmsgmap_path: msgmap.load_generator_map(open(os.path.expanduser( args.genmsgmap_path))) if args.sidmsgmap_path: msgmap.load_signature_map(open(os.path.expanduser( args.sidmsgmap_path))) if msgmap.size() == 0: LOG.warn("No alert message map entries loaded.") else: LOG.info("Loaded %s rule message map entries.", msgmap.size()) if classmap.size() == 0: LOG.warn("No classifications loaded.") else: LOG.info("Loaded %s classifications.", classmap.size()) outputs = [] if args.output: outputs.append(OutputWrapper(args.output)) if args.stdout: outputs.append(OutputWrapper("-", sys.stdout)) else: outputs.append(OutputWrapper("-", sys.stdout)) bookmark = None if args.filenames: if args.bookmark: LOG.error("Bookmarking not valid in file mode.") return 1 if args.follow: LOG.error("Follow not valid in file mode.") return 1 if args.delete: LOG.error("Delete not valid in file mode.") return 1 reader = unified2.FileRecordReader(*args.filenames) elif args.directory and args.prefix: if args.bookmark: current_snort_pid = str(check_output(["pgrep", "-u", "snort"])).strip() bookmark = unified2.Unified2Bookmark(filename=args.bookmark + '_' + current_snort_pid) init_filename, init_offset = bookmark.get() else: init_filename = None init_offset = None reader = unified2.SpoolRecordReader( directory=args.directory, prefix=args.prefix, follow=args.follow, rollover_hook=rollover_hook if args.delete else None, init_filename=init_filename, init_offset=init_offset) else: LOG.error("No spool or files provided.") return 1 formatter = Formatter(msgmap=msgmap, classmap=classmap) count = 0 record = True try: while record is not None: record = reader.next() if record is not None: try: as_json = formatter.format(record) if 'event' in as_json: create_snort_module_alert(as_json, es) count += 1 except Exception as err: LOG.error("Failed to encode record as JSON: %s: %s" % (str(err), str(record))) if bookmark: filename, offset = reader.tell() bookmark.update(filename, offset) except unified2.UnknownRecordType as err: if count == 0: LOG.error("%s: Is this a unified2 file?" % (err)) else: LOG.error(err)
def main(): msgmap = maps.SignatureMap() classmap = maps.ClassificationMap() parser = argparse.ArgumentParser( fromfile_prefix_chars='@', epilog=epilog) parser.add_argument( "-C", dest="classification_path", metavar="<classification.config>", help="path to classification config") parser.add_argument( "-S", dest="sidmsgmap_path", metavar="<msg-msg.map>", help="path to sid-msg.map") parser.add_argument( "-G", dest="genmsgmap_path", metavar="<gen-msg.map>", help="path to gen-msg.map") parser.add_argument( "--snort-conf", dest="snort_conf", metavar="<snort.conf>", help="attempt to load classifications and map files based on the " "location of the snort.conf") parser.add_argument( "--directory", metavar="<spool directory>", help="spool directory (eg: /var/log/snort)") parser.add_argument( "--prefix", metavar="<spool file prefix>", help="spool filename prefix (eg: unified2.log)") parser.add_argument( "--bookmark", metavar="<filename>", help="enable bookmarking") parser.add_argument( "--follow", action="store_true", default=False, help="follow files/continuous mode (spool mode only)") parser.add_argument( "--delete", action="store_true", default=False, help="delete spool files") parser.add_argument( "--output", metavar="<filename>", help="output filename (eg: /var/log/snort/alerts.json") parser.add_argument( "--stdout", action="store_true", default=False, help="also log to stdout if --output is a file") parser.add_argument( "--verbose", action="store_true", default=False, help="be more verbose") parser.add_argument( "filenames", nargs="*") args = parser.parse_args() if args.verbose: LOG.setLevel(logging.DEBUG) if args.snort_conf: load_from_snort_conf(args.snort_conf, classmap, msgmap) if args.classification_path: classmap.load_from_file( open(os.path.expanduser(args.classification_path))) if args.genmsgmap_path: msgmap.load_generator_map(open(os.path.expanduser(args.genmsgmap_path))) if args.sidmsgmap_path: msgmap.load_signature_map(open(os.path.expanduser(args.sidmsgmap_path))) if msgmap.size() == 0: LOG.warn("No alert message map entries loaded.") else: LOG.info("Loaded %s rule message map entries.", msgmap.size()) if classmap.size() == 0: LOG.warn("No classifications loaded.") else: LOG.info("Loaded %s classifications.", classmap.size()) outputs = [] if args.output: outputs.append(OutputWrapper(args.output)) if args.stdout: outputs.append(OutputWrapper("-", sys.stdout)) else: outputs.append(OutputWrapper("-", sys.stdout)) bookmark = None if args.filenames: if args.bookmark: LOG.error("Bookmarking not valid in file mode.") return 1 if args.follow: LOG.error("Follow not valid in file mode.") return 1 if args.delete: LOG.error("Delete not valid in file mode.") return 1 reader = unified2.FileRecordReader(*args.filenames) elif args.directory and args.prefix: if args.bookmark: bookmark = unified2.Unified2Bookmark(filename=args.bookmark) init_filename, init_offset = bookmark.get() else: init_filename = None init_offset = None reader = unified2.SpoolRecordReader( directory=args.directory, prefix=args.prefix, follow=args.follow, rollover_hook=rollover_hook if args.delete else None, init_filename=init_filename, init_offset=init_offset) else: LOG.error("No spool or files provided.") return 1 formatter = Formatter(msgmap=msgmap, classmap=classmap) count = 0 try: for record in reader: try: as_json = json.dumps(formatter.format(record)) for out in outputs: out.write(as_json) count += 1 except Exception as err: LOG.error("Failed to encode record as JSON: %s: %s" % ( str(err), str(record))) if bookmark: filename, offset = reader.tell() bookmark.update(filename, offset) except unified2.UnknownRecordType as err: if count == 0: LOG.error("%s: Is this a unified2 file?" % (err)) else: LOG.error(err)
def main(): msgmap = maps.SignatureMap() classmap = maps.ClassificationMap() parser = argparse.ArgumentParser(fromfile_prefix_chars='@') parser.add_argument("-C", dest="classification_path", metavar="<classification.config>", help="path to classification config") parser.add_argument("-S", dest="sidmsgmap_path", metavar="<msg-msg.map>", help="path to sid-msg.map") parser.add_argument("-G", dest="genmsgmap_path", metavar="<gen-msg.map>", help="path to gen-msg.map") parser.add_argument( "--snort-conf", dest="snort_conf", metavar="<snort.conf>", help="attempt to load classifications and map files based on the " "location of the snort.conf") parser.add_argument("--directory", metavar="<spool directory>", help="spool directory (eg: /var/log/snort)") parser.add_argument("--prefix", metavar="<spool file prefix>", help="spool filename prefix (eg: unified2.log)") parser.add_argument("--bookmark", action="store_true", default=False, help="enable bookmarking") parser.add_argument("--follow", action="store_true", default=False, help="follow files/continuous mode (spool mode only)") parser.add_argument("filenames", nargs="*") args = parser.parse_args() if args.snort_conf: load_from_snort_conf(args.snort_conf, classmap, msgmap) if args.classification_path: classmap.load_from_file( open(os.path.expanduser(args.classification_path))) if args.genmsgmap_path: msgmap.load_generator_map(open(os.path.expanduser( args.genmsgmap_path))) if args.sidmsgmap_path: msgmap.load_signature_map(open(os.path.expanduser( args.sidmsgmap_path))) if msgmap.size() == 0: LOG.warn("WARNING: No alert message map entries loaded.") else: LOG.info("Loaded %s rule message map entries.", msgmap.size()) if classmap.size() == 0: LOG.warn("WARNING: No classifications loaded.") else: LOG.info("Loaded %s classifications.", classmap.size()) if args.directory and args.prefix: reader = unified2.SpoolEventReader(directory=args.directory, prefix=args.prefix, follow=args.follow, bookmark=args.bookmark) for event in reader: print_event(event, msgmap, classmap) elif args.filenames: reader = unified2.FileEventReader(*args.filenames) for event in reader: print_event(event, msgmap, classmap) else: parser.print_help() return 1
def __init__(self, config_path): self.classmap = maps.ClassificationMap() self.msgmap = maps.SignatureMap() self.load_from_config_path(config_path)
required=False, type=int, help="Spool to the output file every threshold_seconds, 20 by default") return parser parser = create_parser() args = parser.parse_args() assert os.path.exists( args.directory), args.directory + ': no such file or directory' assert os.path.exists(os.path.dirname( args.output)), os.path.dirname(args.output) + ': no such file or directory' msgmap = maps.SignatureMap() classmap = maps.ClassificationMap() formatter = Formatter(msgmap=msgmap, classmap=classmap) reader = unified2.SpoolRecordReader(directory=args.directory, prefix=args.prefix, follow=True, init_filename=None, init_offset=None) labels = [ 'generator-id', 'signature-id', 'blocked', 'source-ip', 'dport-icode' ] alert_count = {} text_collector = {}
def main(): msgmap = maps.SignatureMap() classmap = maps.ClassificationMap() parser = argparse.ArgumentParser(fromfile_prefix_chars='@', epilog=epilog) parser.add_argument("-C", dest="classification_path", metavar="<classification.config>", help="path to classification config") parser.add_argument("-S", dest="sidmsgmap_path", metavar="<msg-msg.map>", help="path to sid-msg.map") parser.add_argument("-G", dest="genmsgmap_path", metavar="<gen-msg.map>", help="path to gen-msg.map") parser.add_argument( "--snort-conf", dest="snort_conf", metavar="<snort.conf>", help="attempt to load classifications and map files based on the " "location of the snort.conf") parser.add_argument("--directory", metavar="<spool directory>", help="spool directory (eg: /var/log/snort)") parser.add_argument("--prefix", metavar="<spool file prefix>", help="spool filename prefix (eg: unified2.log)") parser.add_argument("--bookmark", action="store_true", default=False, help="enable bookmarking") parser.add_argument("--follow", action="store_true", default=False, help="follow files/continuous mode (spool mode only)") parser.add_argument("--delete", action="store_true", default=False, help="delete spool files") parser.add_argument("--output", metavar="<filename>", help="output filename (eg: /var/log/snort/alerts.json") parser.add_argument("--stdout", action="store_true", default=False, help="also log to stdout if --output is a file") parser.add_argument("filenames", nargs="*") args = parser.parse_args() if args.snort_conf: load_from_snort_conf(args.snort_conf, classmap, msgmap) if args.classification_path: classmap.load_from_file( open(os.path.expanduser(args.classification_path))) if args.genmsgmap_path: msgmap.load_generator_map(open(os.path.expanduser( args.genmsgmap_path))) if args.sidmsgmap_path: msgmap.load_signature_map(open(os.path.expanduser( args.sidmsgmap_path))) if msgmap.size() == 0: LOG.warn("WARNING: No alert message map entries loaded.") else: LOG.info("Loaded %s rule message map entries.", msgmap.size()) if classmap.size() == 0: LOG.warn("WARNING: No classifications loaded.") else: LOG.info("Loaded %s classifications.", classmap.size()) eve_filter = EveFilter(msgmap, classmap) outputs = [] if args.output: outputs.append(OutputWrapper(args.output)) if args.stdout: outputs.append(OutputWrapper("-", sys.stdout)) else: outputs.append(OutputWrapper("-", sys.stdout)) if args.directory and args.prefix: reader = unified2.SpoolEventReader(directory=args.directory, prefix=args.prefix, follow=args.follow, delete=args.delete, bookmark=args.bookmark) elif args.filenames: reader = unified2.FileEventReader(*args.filenames) else: print("nothing to do.") return for event in reader: try: encoded = json.dumps(eve_filter.filter(event)) for out in outputs: out.write(encoded) except Exception as err: LOG.error("Failed to encode record as JSON: %s: %s" % (str(err), str(event)))
def main(): module = AnsibleModule(argument_spec=dict( rule=dict(required=True, default=None), state=dict(choices=['present', 'absent'], required=True), rules_file=dict(required=False, default='/etc/snort/rules/ansible_managed.rules'), ), supports_check_mode=True) if not HAS_IDSTOOLS: module.fail_json( msg= "Python module idstools not found on host, but is required for snort_rule Ansible module" ) try: matched_rules = [ snort_rule for snort_rule in rule.parse_file(module.params['rules_file']) if to_text(snort_rule) == to_text(rule.parse(module.params['rule'])) ] except IOError: module.fail_json( msg= "rule file {} not found or permission was denied attempting access it" .format(module.params['rules_file'])) rule_found = True if matched_rules else False sigmap = maps.SignatureMap() try: sigmap.load_generator_map(open(GENMAP_FILE, 'r')) except IOError: module.fail_json( msg= "generator file {} not found or permission was denied attempting to access it" .format(GENMAP_FILE)) try: sigmap.load_signature_map(open(SIGMAP_FILE, 'r')) except IOError: module.fail_json( msg= "signature file {} not found or permission was denied attempting to access it" .format(SIGMAP_FILE)) if module.params['state'] == 'present' and rule_found: module.exit_json( msg="Rule '{}' already present in rules_file {}".format( module.params['rule'], module.params['rules_file']), changed=False) elif module.params['state'] == 'present' and not rule_found: if module.check_mode: module.exit_json( msg="Rule '{}' would be added to rules_file {}".format( module.params['rule'], module.params['rules_file']), changed=True) new_snort_rule = rule.parse(module.params['rule']) with open(module.params['rules_file'], 'a') as rules_file: rules_file.write(to_text("\n{}".format(new_snort_rule))) with open(SIGMAP_FILE, 'a') as sigmap_file: if "ref" in new_snort_rule and len(new_snort_rule["ref"]) > 0: sigmap_file.write( to_text("\n{} || {} || {}".format( new_snort_rule['sid'], new_snort_rule['msg'], " || ".join(new_snort_rule['ref'])))) else: sigmap_file.write( to_text("\n{} || {}".format(new_snort_rule['sid'], new_snort_rule['msg']))) module.exit_json(msg="Rule '{}' added to rules_file {}".format( module.params['rule'], module.params['rules_file']), changed=True) if module.params['state'] == 'absent' and not rule_found: module.exit_json( msg="Rule '{}' does not exist in rules_file {}".format( module.params['rule'], module.params['rules_file']), changed=False) elif module.params['state'] == 'absent' and rule_found: new_snort_rule = rule.parse(module.params['rule']) changed = False orig_rulefile_contents = [] orig_sigmapfile_contents = [] new_rulefile_contents = [] new_sigmapfile_contents = [] with open(module.params['rules_file'], 'r') as rules_file: orig_rulefile_contents = rules_file.readlines() with open(SIGMAP_FILE, 'r') as sigmap_file: orig_sigmapfile_contents = sigmap_file.readlines() new_rulefile_contents = [ line for line in orig_rulefile_contents if new_snort_rule != rule.parse(line) ] if "ref" in new_snort_rule and len(new_snort_rule["ref"]) > 0: new_sigmapfile_contents = [ line for line in orig_sigmapfile_contents if "{} || {} || {}".format( new_snort_rule['sid'], new_snort_rule['msg'], ' || '.join( new_snort_rule['ref'])) != line.strip() ] else: new_sigmapfile_contents = [ line for line in orig_sigmapfile_contents if "{} || {}".format(new_snort_rule['sid'], new_snort_rule['msg']) != line.strip() ] if module.check_mode: if len(orig_rulefile_contents) != len(new_rulefile_contents): module.exit_json( msg="Rule '{}' would have been removed from rules_file {}". format(module.params['rule'], module.params['rules_file']), changed=True) if len(orig_rulefile_contents) != len(new_rulefile_contents): changed = True with open(module.params['rules_file'], 'w') as rules_file: for line in new_rulefile_contents: rules_file.write(line) if new_sigmapfile_contents and (len(orig_sigmapfile_contents) != len(new_sigmapfile_contents)): changed = True with open(SIGMAP_FILE, 'w') as sigmap_file: for line in new_sigmapfile_contents: sigmap_file.write(line) module.exit_json( msg="Rule '{}' has been removed from rules_file {}".format( module.params['rule'], module.params['rules_file']), changed=changed)