diff -Nru clamav-0.103.2+dfsg/clamav-milter/clamav-milter.c clamav-0.103.5+dfsg/clamav-milter/clamav-milter.c --- clamav-0.103.2+dfsg/clamav-milter/clamav-milter.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamav-milter/clamav-milter.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * Author: aCaB @@ -96,7 +96,7 @@ sigset_t sigset; struct sigaction act; - const char * user_name = NULL; + const char *user_name = NULL; cl_initialize_crypto(); @@ -122,7 +122,7 @@ printf("\n"); printf(" Clam AntiVirus: Milter Mail Scanner %s\n", get_version()); printf(" By The ClamAV Team: https://www.clamav.net/about.html#credits\n"); - printf(" (C) 2021 Cisco Systems, Inc.\n"); + printf(" (C) 2022 Cisco Systems, Inc.\n"); printf("\n"); printf(" %s [-c ]\n\n", argv[0]); printf("\n"); @@ -158,7 +158,7 @@ } free(pt); - if ((opt = optget(opts, "User"))->enabled){ + if ((opt = optget(opts, "User"))->enabled) { user_name = opt->strarg; } @@ -419,7 +419,7 @@ if ((opt = optget(opts, "PidFile"))->enabled) { FILE *fd; mode_t old_umask = umask(0002); - int err = 0; + int err = 0; if ((fd = fopen(opt->strarg, "w")) == NULL) { logg("!Can't save PID in file %s\n", opt->strarg); @@ -434,14 +434,14 @@ umask(old_umask); #ifndef _WIN32 - if (0 == err){ + if (0 == err) { /*If the file has already been created by a different user, it will just be * rewritten by us, but not change the ownership, so do that explicitly. */ - if (0 == geteuid()){ - struct passwd * pw = getpwuid(0); - int ret = lchown(opt->strarg, pw->pw_uid, pw->pw_gid); - if (ret){ + if (0 == geteuid()) { + struct passwd *pw = getpwuid(0); + int ret = lchown(opt->strarg, pw->pw_uid, pw->pw_gid); + if (ret) { logg("!Can't change ownership of PID file %s '%s'\n", opt->strarg, strerror(errno)); err = 1; } @@ -449,7 +449,7 @@ } #endif /*_WIN32*/ - if (err){ + if (err) { localnets_free(); whitelist_free(); logg_close(); @@ -460,7 +460,7 @@ #ifndef _WIN32 dropPrivRet = drop_privileges(user_name, logg_file); - if (dropPrivRet){ + if (dropPrivRet) { optfree(opts); return dropPrivRet; } @@ -468,7 +468,7 @@ /* We have been daemonized, and initialization is done. Signal * the parent process so that it can exit cleanly. */ - if (parentPid != getpid()){ //we have been daemonized + if (parentPid != getpid()) { //we have been daemonized daemonize_signal_parent(parentPid); } #endif diff -Nru clamav-0.103.2+dfsg/clamav-milter/clamfi.c clamav-0.103.5+dfsg/clamav-milter/clamfi.c --- clamav-0.103.2+dfsg/clamav-milter/clamfi.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamav-milter/clamfi.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * Author: aCaB diff -Nru clamav-0.103.2+dfsg/clamav-milter/clamfi.h clamav-0.103.5+dfsg/clamav-milter/clamfi.h --- clamav-0.103.2+dfsg/clamav-milter/clamfi.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamav-milter/clamfi.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * Author: aCaB diff -Nru clamav-0.103.2+dfsg/clamav-milter/CMakeLists.txt clamav-0.103.5+dfsg/clamav-milter/CMakeLists.txt --- clamav-0.103.2+dfsg/clamav-milter/CMakeLists.txt 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamav-milter/CMakeLists.txt 2022-01-10 23:17:45.000000000 +0000 @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. cmake_minimum_required( VERSION 3.12...3.13 ) diff -Nru clamav-0.103.2+dfsg/clamav-milter/connpool.c clamav-0.103.5+dfsg/clamav-milter/connpool.c --- clamav-0.103.2+dfsg/clamav-milter/connpool.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamav-milter/connpool.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * Author: aCaB diff -Nru clamav-0.103.2+dfsg/clamav-milter/connpool.h clamav-0.103.5+dfsg/clamav-milter/connpool.h --- clamav-0.103.2+dfsg/clamav-milter/connpool.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamav-milter/connpool.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * Author: aCaB diff -Nru clamav-0.103.2+dfsg/clamav-milter/Makefile.am clamav-0.103.5+dfsg/clamav-milter/Makefile.am --- clamav-0.103.2+dfsg/clamav-milter/Makefile.am 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamav-milter/Makefile.am 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ # -# Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. # Copyright (C) 2007-2013 Sourcefire, Inc. # Copyright (C) 2003-2007 Tomasz Kojm # diff -Nru clamav-0.103.2+dfsg/clamav-milter/Makefile.in clamav-0.103.5+dfsg/clamav-milter/Makefile.in --- clamav-0.103.2+dfsg/clamav-milter/Makefile.in 2021-04-06 19:04:43.000000000 +0000 +++ clamav-0.103.5+dfsg/clamav-milter/Makefile.in 2022-01-10 23:18:12.000000000 +0000 @@ -15,7 +15,7 @@ @SET_MAKE@ # -# Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. # Copyright (C) 2007-2013 Sourcefire, Inc. # Copyright (C) 2003-2007 Tomasz Kojm # diff -Nru clamav-0.103.2+dfsg/clamav-milter/netcode.c clamav-0.103.5+dfsg/clamav-milter/netcode.c --- clamav-0.103.2+dfsg/clamav-milter/netcode.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamav-milter/netcode.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * Author: aCaB diff -Nru clamav-0.103.2+dfsg/clamav-milter/netcode.h clamav-0.103.5+dfsg/clamav-milter/netcode.h --- clamav-0.103.2+dfsg/clamav-milter/netcode.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamav-milter/netcode.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * Author: aCaB diff -Nru clamav-0.103.2+dfsg/clamav-milter/whitelist.c clamav-0.103.5+dfsg/clamav-milter/whitelist.c --- clamav-0.103.2+dfsg/clamav-milter/whitelist.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamav-milter/whitelist.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * Author: aCaB diff -Nru clamav-0.103.2+dfsg/clamav-milter/whitelist.h clamav-0.103.5+dfsg/clamav-milter/whitelist.h --- clamav-0.103.2+dfsg/clamav-milter/whitelist.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamav-milter/whitelist.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * Author: aCaB diff -Nru clamav-0.103.2+dfsg/clamav-types.h.in clamav-0.103.5+dfsg/clamav-types.h.in --- clamav-0.103.2+dfsg/clamav-types.h.in 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamav-types.h.in 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm, Micah Snyder diff -Nru clamav-0.103.2+dfsg/clamav-version.h.in clamav-0.103.5+dfsg/clamav-version.h.in --- clamav-0.103.2+dfsg/clamav-version.h.in 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamav-version.h.in 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2019-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * * Authors: Micah Snyder * diff -Nru clamav-0.103.2+dfsg/clambc/bcrun.c clamav-0.103.5+dfsg/clambc/bcrun.c --- clamav-0.103.2+dfsg/clambc/bcrun.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clambc/bcrun.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,7 +1,7 @@ /* * ClamAV bytecode handler tool. * - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2009-2013 Sourcefire, Inc. * * Authors: Török Edvin @@ -52,7 +52,7 @@ printf("\n"); printf(" Clam AntiVirus: Bytecode Testing Tool %s\n", get_version()); printf(" By The ClamAV Team: https://www.clamav.net/about.html#credits\n"); - printf(" (C) 2021 Cisco Systems, Inc.\n"); + printf(" (C) 2022 Cisco Systems, Inc.\n"); printf("\n"); printf(" clambc [function] [param1 ...]\n"); printf("\n"); @@ -367,6 +367,7 @@ struct cl_engine *engine = cl_engine_new(); fmap_t *map = NULL; memset(&cctx, 0, sizeof(cctx)); + if (!engine) { fprintf(stderr, "Unable to create engine\n"); optfree(opts); @@ -394,11 +395,21 @@ } ctx->ctx = &cctx; cctx.engine = engine; - cctx.fmap = cli_calloc(sizeof(fmap_t *), engine->maxreclevel + 2); - if (!cctx.fmap) { + + cctx.recursion_stack_size = cctx.engine->max_recursion_level; + cctx.recursion_stack = cli_calloc(sizeof(recursion_level_t), cctx.recursion_stack_size); + if (!cctx.recursion_stack) { fprintf(stderr, "Out of memory\n"); exit(3); } + + // ctx was memset, so recursion_level starts at 0. + cctx.recursion_stack[cctx.recursion_level].fmap = map; + cctx.recursion_stack[cctx.recursion_level].type = CL_TYPE_ANY; /* ANY for the top level, because we don't yet know the type. */ + cctx.recursion_stack[cctx.recursion_level].size = map->len; + + cctx.fmap = cctx.recursion_stack[cctx.recursion_level].fmap; + memset(&dbg_state, 0, sizeof(dbg_state)); dbg_state.file = ""; dbg_state.line = 0; @@ -466,7 +477,7 @@ if (map) funmap(map); cl_engine_free(engine); - free(cctx.fmap); + free(cctx.recursion_stack); } cli_bytecode_destroy(bc); cli_bytecode_done(&bcs); diff -Nru clamav-0.103.2+dfsg/clambc/CMakeLists.txt clamav-0.103.5+dfsg/clambc/CMakeLists.txt --- clamav-0.103.2+dfsg/clambc/CMakeLists.txt 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clambc/CMakeLists.txt 2022-01-10 23:17:45.000000000 +0000 @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. cmake_minimum_required( VERSION 3.12...3.13 ) diff -Nru clamav-0.103.2+dfsg/clamconf/clamconf.c clamav-0.103.5+dfsg/clamconf/clamconf.c --- clamav-0.103.2+dfsg/clamconf/clamconf.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamconf/clamconf.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2009-2013 Sourcefire, Inc. * * Author: Tomasz Kojm @@ -207,7 +207,7 @@ printf("\n"); printf(" Clam AntiVirus: Configuration Tool %s\n", get_version()); printf(" By The ClamAV Team: https://www.clamav.net/about.html#credits\n"); - printf(" (C) 2021 Cisco Systems, Inc.\n"); + printf(" (C) 2022 Cisco Systems, Inc.\n"); printf("\n"); printf(" --help -h Show this help\n"); printf(" --version -V Show version\n"); diff -Nru clamav-0.103.2+dfsg/clamconf/CMakeLists.txt clamav-0.103.5+dfsg/clamconf/CMakeLists.txt --- clamav-0.103.2+dfsg/clamconf/CMakeLists.txt 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamconf/CMakeLists.txt 2022-01-10 23:17:45.000000000 +0000 @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. cmake_minimum_required( VERSION 3.12...3.13 ) diff -Nru clamav-0.103.2+dfsg/clamconf/Makefile.am clamav-0.103.5+dfsg/clamconf/Makefile.am --- clamav-0.103.2+dfsg/clamconf/Makefile.am 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamconf/Makefile.am 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ # -# Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. # Copyright (C) 2007-2013 Sourcefire, Inc. # Copyright (C) 2006-2007 Tomasz Kojm # diff -Nru clamav-0.103.2+dfsg/clamconf/Makefile.in clamav-0.103.5+dfsg/clamconf/Makefile.in --- clamav-0.103.2+dfsg/clamconf/Makefile.in 2021-04-06 19:04:43.000000000 +0000 +++ clamav-0.103.5+dfsg/clamconf/Makefile.in 2022-01-10 23:18:12.000000000 +0000 @@ -15,7 +15,7 @@ @SET_MAKE@ # -# Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. # Copyright (C) 2007-2013 Sourcefire, Inc. # Copyright (C) 2006-2007 Tomasz Kojm # diff -Nru clamav-0.103.2+dfsg/clamd/clamav-daemon.service.in clamav-0.103.5+dfsg/clamd/clamav-daemon.service.in --- clamav-0.103.2+dfsg/clamd/clamav-daemon.service.in 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/clamav-daemon.service.in 2022-01-10 23:17:45.000000000 +0000 @@ -1,6 +1,6 @@ [Unit] Description=Clam AntiVirus userspace daemon -Documentation=man:clamd(8) man:clamd.conf(5) https://www.clamav.net/documents/ +Documentation=man:clamd(8) man:clamd.conf(5) https://docs.clamav.net/ Requires=clamav-daemon.socket # Check for database existence ConditionPathExistsGlob=@DBDIR@/main.{c[vl]d,inc} diff -Nru clamav-0.103.2+dfsg/clamd/clamav-daemon.socket.in clamav-0.103.5+dfsg/clamd/clamav-daemon.socket.in --- clamav-0.103.2+dfsg/clamd/clamav-daemon.socket.in 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/clamav-daemon.socket.in 2022-01-10 23:17:45.000000000 +0000 @@ -1,6 +1,6 @@ [Unit] Description=Socket for Clam AntiVirus userspace daemon -Documentation=man:clamd(8) man:clamd.conf(5) https://www.clamav.net/documents/ +Documentation=man:clamd(8) man:clamd.conf(5) https://docs.clamav.net/ # Check for database existence ConditionPathExistsGlob=@DBDIR@/main.{c[vl]d,inc} ConditionPathExistsGlob=@DBDIR@/daily.{c[vl]d,inc} diff -Nru clamav-0.103.2+dfsg/clamd/clamd.c clamav-0.103.5+dfsg/clamd/clamd.c --- clamav-0.103.2+dfsg/clamd/clamd.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/clamd.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm @@ -80,7 +80,6 @@ #include #endif - short debug_mode = 0, logok = 0; short foreground = -1; @@ -89,7 +88,7 @@ printf("\n"); printf(" Clam AntiVirus: Daemon %s\n", get_version()); printf(" By The ClamAV Team: https://www.clamav.net/about.html#credits\n"); - printf(" (C) 2021 Cisco Systems, Inc.\n"); + printf(" (C) 2022 Cisco Systems, Inc.\n"); printf("\n"); printf(" clamd [options]\n"); printf("\n"); @@ -144,9 +143,9 @@ #ifdef C_LINUX STATBUF sb; #endif - pid_t mainpid = 0; - mode_t old_umask = 0; - const char * user_name = NULL; + pid_t mainpid = 0; + mode_t old_umask = 0; + const char *user_name = NULL; if (check_flevel()) exit(1); @@ -156,8 +155,8 @@ sa.sa_handler = SIG_IGN; sigaction(SIGHUP, &sa, NULL); sigaction(SIGUSR2, &sa, NULL); - if(!setlocale(LC_CTYPE, "")) { - mprintf("^Failed to set locale\n"); + if (!setlocale(LC_CTYPE, "")) { + mprintf("^Failed to set locale\n"); } #endif @@ -214,7 +213,7 @@ } free(pt); - if ((opt = optget(opts, "User"))->enabled){ + if ((opt = optget(opts, "User"))->enabled) { user_name = opt->strarg; } @@ -252,48 +251,47 @@ logg_file = NULL; } - #ifndef WIN32 - /* fork into background */ - if (foreground == -1) { - if (optget(opts, "Foreground")->enabled) { - foreground = 1; - } else { - foreground = 0; - } + /* fork into background */ + if (foreground == -1) { + if (optget(opts, "Foreground")->enabled) { + foreground = 1; + } else { + foreground = 0; } - if (foreground == 0) { - int daemonizeRet = 0; + } + if (foreground == 0) { + int daemonizeRet = 0; #ifdef C_BSD - /* workaround for OpenBSD bug, see https://wwws.clamav.net/bugzilla/show_bug.cgi?id=885 */ - for (ret = 0; (unsigned int)ret < nlsockets; ret++) { - if (fcntl(lsockets[ret], F_SETFL, fcntl(lsockets[ret], F_GETFL) | O_NONBLOCK) == -1) { - logg("!fcntl for lsockets[] failed\n"); - close(lsockets[ret]); - ret = 1; - break; - } + /* workaround for OpenBSD bug, see https://wwws.clamav.net/bugzilla/show_bug.cgi?id=885 */ + for (ret = 0; (unsigned int)ret < nlsockets; ret++) { + if (fcntl(lsockets[ret], F_SETFL, fcntl(lsockets[ret], F_GETFL) | O_NONBLOCK) == -1) { + logg("!fcntl for lsockets[] failed\n"); + close(lsockets[ret]); + ret = 1; + break; } + } #endif - gengine = engine; - atexit(free_engine); - daemonizeRet = daemonize_parent_wait(user_name, logg_file); - if (daemonizeRet < 0){ - logg("!daemonize() failed: %s\n", strerror(errno)); - return 1; - } - gengine = NULL; + gengine = engine; + atexit(free_engine); + daemonizeRet = daemonize_parent_wait(user_name, logg_file); + if (daemonizeRet < 0) { + logg("!daemonize() failed: %s\n", strerror(errno)); + return 1; + } + gengine = NULL; #ifdef C_BSD - for (ret = 0; (unsigned int)ret < nlsockets; ret++) { - if (fcntl(lsockets[ret], F_SETFL, fcntl(lsockets[ret], F_GETFL) & ~O_NONBLOCK) == -1) { - logg("!fcntl for lsockets[] failed\n"); - close(lsockets[ret]); - ret = 1; - break; - } + for (ret = 0; (unsigned int)ret < nlsockets; ret++) { + if (fcntl(lsockets[ret], F_SETFL, fcntl(lsockets[ret], F_GETFL) & ~O_NONBLOCK) == -1) { + logg("!fcntl for lsockets[] failed\n"); + close(lsockets[ret]); + ret = 1; + break; } -#endif } +#endif + } #endif @@ -321,10 +319,10 @@ /*If the file has already been created by a different user, it will just be * rewritten by us, but not change the ownership, so do that explicitly. */ - if (0 == geteuid()){ - struct passwd * pw = getpwuid(0); - int ret = lchown(opt->strarg, pw->pw_uid, pw->pw_gid); - if (ret){ + if (0 == geteuid()) { + struct passwd *pw = getpwuid(0); + int ret = lchown(opt->strarg, pw->pw_uid, pw->pw_gid); + if (ret) { logg("!Can't change ownership of PID file %s '%s'\n", opt->strarg, strerror(errno)); exit(2); } @@ -782,7 +780,7 @@ * now, since everything is initialized.*/ /*signal the parent process.*/ - if (parentPid != getpid()){ + if (parentPid != getpid()) { daemonize_signal_parent(parentPid); } #endif diff -Nru clamav-0.103.2+dfsg/clamd/clamd_others.c clamav-0.103.5+dfsg/clamd/clamd_others.c --- clamav-0.103.2+dfsg/clamd/clamd_others.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/clamd_others.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm, Trog, Török Edvin diff -Nru clamav-0.103.2+dfsg/clamd/clamd_others.h clamav-0.103.5+dfsg/clamd/clamd_others.h --- clamav-0.103.2+dfsg/clamd/clamd_others.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/clamd_others.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm, Trog, Török Edvin diff -Nru clamav-0.103.2+dfsg/clamd/CMakeLists.txt clamav-0.103.5+dfsg/clamd/CMakeLists.txt --- clamav-0.103.2+dfsg/clamd/CMakeLists.txt 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/CMakeLists.txt 2022-01-10 23:17:45.000000000 +0000 @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. cmake_minimum_required( VERSION 3.12...3.13 ) diff -Nru clamav-0.103.2+dfsg/clamd/localserver.c clamav-0.103.5+dfsg/clamd/localserver.c --- clamav-0.103.2+dfsg/clamd/localserver.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/localserver.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm diff -Nru clamav-0.103.2+dfsg/clamd/localserver.h clamav-0.103.5+dfsg/clamd/localserver.h --- clamav-0.103.2+dfsg/clamd/localserver.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/localserver.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm diff -Nru clamav-0.103.2+dfsg/clamd/Makefile.am clamav-0.103.5+dfsg/clamd/Makefile.am --- clamav-0.103.2+dfsg/clamd/Makefile.am 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/Makefile.am 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ # -# Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. # Copyright (C) 2007-2013 Sourcefire, Inc. # Copyright (C) 2002-2007 Tomasz Kojm # diff -Nru clamav-0.103.2+dfsg/clamd/Makefile.in clamav-0.103.5+dfsg/clamd/Makefile.in --- clamav-0.103.2+dfsg/clamd/Makefile.in 2021-04-06 19:04:43.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/Makefile.in 2022-01-10 23:18:12.000000000 +0000 @@ -15,7 +15,7 @@ @SET_MAKE@ # -# Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. # Copyright (C) 2007-2013 Sourcefire, Inc. # Copyright (C) 2002-2007 Tomasz Kojm # diff -Nru clamav-0.103.2+dfsg/clamd/scanner.c clamav-0.103.5+dfsg/clamd/scanner.c --- clamav-0.103.2+dfsg/clamd/scanner.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/scanner.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm, Török Edvin @@ -146,8 +146,8 @@ if (NULL != filename) { if (CL_SUCCESS != cli_realpath((const char *)filename, &real_filename)) { - conn_reply_errno(scandata->conn, msg, "Failed to determine real path:"); - logg("^Failed to determine real path for: %s\n", filename); + conn_reply_errno(scandata->conn, msg, "File path check failure:"); + logg("^File path check failure for: %s\n", filename); logg("*Quarantine of the file may fail if file path contains symlinks.\n"); } else { free(filename); @@ -180,25 +180,30 @@ else logg("!Memory allocation failed during cli_ftw()\n"); scandata->errors++; + free(filename); return CL_EMEM; case error_stat: - conn_reply_errno(scandata->conn, msg, "lstat() failed:"); - logg("^lstat() failed on: %s\n", msg); + conn_reply_errno(scandata->conn, msg, "File path check failure:"); + logg("^File path check failure on: %s\n", msg); scandata->errors++; + free(filename); return CL_SUCCESS; case warning_skipped_dir: - logg("^Directory recursion limit reached, skipping %s\n", - msg); + logg("^Directory recursion limit reached, skipping %s\n", msg); + free(filename); return CL_SUCCESS; case warning_skipped_link: logg("$Skipping symlink: %s\n", msg); + free(filename); return CL_SUCCESS; case warning_skipped_special: if (msg == scandata->toplevel_path) conn_reply(scandata->conn, msg, "Not supported file type", "ERROR"); logg("*Not supported file type: %s\n", msg); + free(filename); return CL_SUCCESS; case visit_directory_toplev: + free(filename); return CL_SUCCESS; case visit_file: break; diff -Nru clamav-0.103.2+dfsg/clamd/scanner.h clamav-0.103.5+dfsg/clamd/scanner.h --- clamav-0.103.2+dfsg/clamd/scanner.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/scanner.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm, Török Edvin diff -Nru clamav-0.103.2+dfsg/clamd/server.h clamav-0.103.5+dfsg/clamd/server.h --- clamav-0.103.2+dfsg/clamd/server.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/server.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm, Trog, Török Edvin diff -Nru clamav-0.103.2+dfsg/clamd/server-th.c clamav-0.103.5+dfsg/clamd/server-th.c --- clamav-0.103.2+dfsg/clamd/server-th.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/server-th.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm, Trog, Török Edvin @@ -137,7 +137,8 @@ if (conn->filename) free(conn->filename); logg("$Finished scanthread\n"); - if (thrmgr_group_finished(conn->group, virus ? EXIT_OTHER : errors ? EXIT_ERROR : EXIT_OK)) { + if (thrmgr_group_finished(conn->group, virus ? EXIT_OTHER : errors ? EXIT_ERROR + : EXIT_OK)) { logg("$Scanthread: connection shut down (FD %d)\n", conn->sd); /* close connection if we were last in group */ shutdown(conn->sd, 2); diff -Nru clamav-0.103.2+dfsg/clamd/session.c clamav-0.103.5+dfsg/clamd/session.c --- clamav-0.103.2+dfsg/clamd/session.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/session.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm, Török Edvin diff -Nru clamav-0.103.2+dfsg/clamd/session.h clamav-0.103.5+dfsg/clamd/session.h --- clamav-0.103.2+dfsg/clamd/session.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/session.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm, Török Edvin diff -Nru clamav-0.103.2+dfsg/clamd/shared.h clamav-0.103.5+dfsg/clamd/shared.h --- clamav-0.103.2+dfsg/clamd/shared.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/shared.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm diff -Nru clamav-0.103.2+dfsg/clamd/tcpserver.c clamav-0.103.5+dfsg/clamd/tcpserver.c --- clamav-0.103.2+dfsg/clamd/tcpserver.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/tcpserver.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm, Török Edvin diff -Nru clamav-0.103.2+dfsg/clamd/tcpserver.h clamav-0.103.5+dfsg/clamd/tcpserver.h --- clamav-0.103.2+dfsg/clamd/tcpserver.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/tcpserver.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm diff -Nru clamav-0.103.2+dfsg/clamd/thrmgr.c clamav-0.103.5+dfsg/clamd/thrmgr.c --- clamav-0.103.2+dfsg/clamd/thrmgr.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/thrmgr.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Trog, Török Edvin diff -Nru clamav-0.103.2+dfsg/clamd/thrmgr.h clamav-0.103.5+dfsg/clamd/thrmgr.h --- clamav-0.103.2+dfsg/clamd/thrmgr.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamd/thrmgr.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm, Török Edvin diff -Nru clamav-0.103.2+dfsg/clamdscan/clamdscan.c clamav-0.103.5+dfsg/clamdscan/clamdscan.c --- clamav-0.103.2+dfsg/clamdscan/clamdscan.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamdscan/clamdscan.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm, aCaB @@ -225,7 +225,7 @@ mprintf("\n"); mprintf(" Clam AntiVirus: Daemon Client %s\n", get_version()); mprintf(" By The ClamAV Team: https://www.clamav.net/about.html#credits\n"); - mprintf(" (C) 2021 Cisco Systems, Inc.\n"); + mprintf(" (C) 2022 Cisco Systems, Inc.\n"); mprintf("\n"); mprintf(" clamdscan [options] [file/directory/-]\n"); mprintf("\n"); diff -Nru clamav-0.103.2+dfsg/clamdscan/client.c clamav-0.103.5+dfsg/clamdscan/client.c --- clamav-0.103.2+dfsg/clamdscan/client.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamdscan/client.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2009-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm, aCaB @@ -308,7 +308,21 @@ static int client_scan(const char *file, int scantype, int *infected, int *err, int maxlevel, int session, int flags) { int ret; - char *fullpath = makeabs(file); + char *real_path = NULL; + char *fullpath = NULL; + + /* Convert relative path to fullpath */ + fullpath = makeabs(file); + + /* Convert fullpath to the real path (evaluating symlinks and . and ..). + Doing this early on will ensure that the scan results will appear consistent + across regular scans, --fdpass scans, and --stream scans. */ + if (CL_SUCCESS != cli_realpath(fullpath, &real_path)) { + logg("*client_scan: Failed to determine real filename of %s.\n", fullpath); + } else { + free(fullpath); + fullpath = real_path; + } if (!fullpath) return 0; diff -Nru clamav-0.103.2+dfsg/clamdscan/client.h clamav-0.103.5+dfsg/clamdscan/client.h --- clamav-0.103.2+dfsg/clamdscan/client.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamdscan/client.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2009-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm, aCaB diff -Nru clamav-0.103.2+dfsg/clamdscan/CMakeLists.txt clamav-0.103.5+dfsg/clamdscan/CMakeLists.txt --- clamav-0.103.2+dfsg/clamdscan/CMakeLists.txt 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamdscan/CMakeLists.txt 2022-01-10 23:17:45.000000000 +0000 @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. cmake_minimum_required( VERSION 3.12...3.13 ) diff -Nru clamav-0.103.2+dfsg/clamdscan/Makefile.am clamav-0.103.5+dfsg/clamdscan/Makefile.am --- clamav-0.103.2+dfsg/clamdscan/Makefile.am 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamdscan/Makefile.am 2022-01-10 23:17:45.000000000 +0000 @@ -1,4 +1,4 @@ -# Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. # Copyright (C) 2007-2013 Sourcefire, Inc. # Copyright (C) 2002-2007 Tomasz Kojm # diff -Nru clamav-0.103.2+dfsg/clamdscan/Makefile.in clamav-0.103.5+dfsg/clamdscan/Makefile.in --- clamav-0.103.2+dfsg/clamdscan/Makefile.in 2021-04-06 19:04:43.000000000 +0000 +++ clamav-0.103.5+dfsg/clamdscan/Makefile.in 2022-01-10 23:18:12.000000000 +0000 @@ -14,7 +14,7 @@ @SET_MAKE@ -# Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. # Copyright (C) 2007-2013 Sourcefire, Inc. # Copyright (C) 2002-2007 Tomasz Kojm # diff -Nru clamav-0.103.2+dfsg/clamdscan/proto.c clamav-0.103.5+dfsg/clamdscan/proto.c --- clamav-0.103.2+dfsg/clamdscan/proto.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamdscan/proto.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2009-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm, aCaB @@ -236,19 +236,32 @@ /* 0: scan, 1: skip */ static int chkpath(const char *path) { + int status = 0; const struct optstruct *opt; + char *real_path = NULL; + + if (!path) { + status = 1; + goto done; + } if ((opt = optget(clamdopts, "ExcludePath"))->enabled) { while (opt) { if (match_regex(path, opt->strarg) == 1) { if (printinfected != 1) logg("~%s: Excluded\n", path); - return 1; + status = 1; + goto done; } opt = opt->nextarg; } } - return 0; + +done: + if (NULL != real_path) { + free(real_path); + } + return status; } static int ftw_chkpath(const char *path, struct cli_ftw_cbdata *data) @@ -438,6 +451,7 @@ } if (chkpath(path)) { + /* Exclude the path */ status = CL_SUCCESS; goto done; } @@ -492,6 +506,9 @@ status = CL_SUCCESS; done: + if (NULL != real_filename) { + free(real_filename); + } free(filename); return status; } @@ -618,12 +635,14 @@ logg("*Failed to determine real filename of %s.\n", filename); logg("*Quarantine of the file may fail if file path contains symlinks.\n"); } else { - free(filename); + free(filename); /* callback is responsible for free'ing filename parameter. */ filename = real_filename; } } if (chkpath(filename)) { + /* Exclude the path */ + status = CL_SUCCESS; goto done; } c->files++; diff -Nru clamav-0.103.2+dfsg/clamdscan/proto.h clamav-0.103.5+dfsg/clamdscan/proto.h --- clamav-0.103.2+dfsg/clamdscan/proto.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamdscan/proto.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2009-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm, aCaB diff -Nru clamav-0.103.2+dfsg/clamdtop/clamdtop.c clamav-0.103.5+dfsg/clamdtop/clamdtop.c --- clamav-0.103.2+dfsg/clamdtop/clamdtop.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamdtop/clamdtop.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,7 +1,7 @@ /* * ClamdTOP * - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * Authors: Török Edvin @@ -1339,7 +1339,7 @@ printf("\n"); printf(" Clam AntiVirus: Monitoring Tool %s\n", get_version()); printf(" By The ClamAV Team: https://www.clamav.net/about.html#credits\n"); - printf(" (C) 2021 Cisco Systems, Inc.\n"); + printf(" (C) 2022 Cisco Systems, Inc.\n"); printf("\n"); printf(" clamdtop [-hVc] [host[:port] /path/to/clamd.socket ...]\n"); printf("\n"); diff -Nru clamav-0.103.2+dfsg/clamdtop/CMakeLists.txt clamav-0.103.5+dfsg/clamdtop/CMakeLists.txt --- clamav-0.103.2+dfsg/clamdtop/CMakeLists.txt 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamdtop/CMakeLists.txt 2022-01-10 23:17:45.000000000 +0000 @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. cmake_minimum_required( VERSION 3.12...3.13 ) diff -Nru clamav-0.103.2+dfsg/clamonacc/clamav-clamonacc.service.in clamav-0.103.5+dfsg/clamonacc/clamav-clamonacc.service.in --- clamav-0.103.2+dfsg/clamonacc/clamav-clamonacc.service.in 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/clamav-clamonacc.service.in 2022-01-10 23:17:45.000000000 +0000 @@ -3,7 +3,7 @@ [Unit] Description=ClamAV On-Access Scanner -Documentation=man:clamonacc(8) man:clamd.conf(5) https://www.clamav.net/documents +Documentation=man:clamonacc(8) man:clamd.conf(5) https://docs.clamav.net/ Requires=clamav-daemon.service After=clamav-daemon.service syslog.target network.target diff -Nru clamav-0.103.2+dfsg/clamonacc/clamonacc.c clamav-0.103.5+dfsg/clamonacc/clamonacc.c --- clamav-0.103.2+dfsg/clamonacc/clamonacc.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/clamonacc.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2019-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * * Authors: Mickey Sola * @@ -419,7 +419,7 @@ mprintf("\n"); mprintf(" ClamAV: On Access Scanning Application and Client %s\n", get_version()); mprintf(" By The ClamAV Team: https://www.clamav.net/about.html#credits\n"); - mprintf(" (C) 2021 Cisco Systems, Inc.\n"); + mprintf(" (C) 2022 Cisco Systems, Inc.\n"); mprintf("\n"); mprintf(" clamonacc [options] [file/directory/-]\n"); mprintf("\n"); diff -Nru clamav-0.103.2+dfsg/clamonacc/clamonacc.h clamav-0.103.5+dfsg/clamonacc/clamonacc.h --- clamav-0.103.2+dfsg/clamonacc/clamonacc.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/clamonacc.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Mickey Sola diff -Nru clamav-0.103.2+dfsg/clamonacc/client/client.c clamav-0.103.5+dfsg/clamonacc/client/client.c --- clamav-0.103.2+dfsg/clamonacc/client/client.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/client/client.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2015-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2009 Sourcefire, Inc. * * Authors: Tomasz Kojm, aCaB, Mickey Sola @@ -138,6 +138,7 @@ } else { logg("!ClamClient: Could not connect to clamd, %s\n", curl_easy_strerror(curlcode)); } + curl_easy_cleanup(curl); *err = CL_EARG; return ret; } @@ -397,7 +398,6 @@ curl_easy_cleanup(*curl); return curlcode; } - return curlcode; } @@ -509,6 +509,7 @@ curlcode = curl_easy_perform(curl); if (CURLE_OK != curlcode) { logg("*ClamClient: could not connect to clamd, %s\n", curl_easy_strerror(curlcode)); + curl_easy_cleanup(curl); return 2; } @@ -571,6 +572,7 @@ logg("!ClamClient: Connection to clamd failed, %s.\n", curl_easy_strerror(curlcode)); disconnected = true; } + curl_easy_cleanup(curl); return CL_ECREAT; } if (disconnected) { diff -Nru clamav-0.103.2+dfsg/clamonacc/client/client.h clamav-0.103.5+dfsg/clamonacc/client/client.h --- clamav-0.103.2+dfsg/clamonacc/client/client.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/client/client.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2015-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2009 Sourcefire, Inc. * * Authors: Tomasz Kojm, aCaB diff -Nru clamav-0.103.2+dfsg/clamonacc/client/socket.c clamav-0.103.5+dfsg/clamonacc/client/socket.c --- clamav-0.103.2+dfsg/clamonacc/client/socket.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/client/socket.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * * Author: Mickey Sola * @@ -78,7 +78,7 @@ #ifdef HAVE_FD_PASSING - int sockd; + int sockd = 0; if (onas_sock.written && (sockd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0) { if (connect(sockd, (struct sockaddr *)&onas_sock.sock, sizeof(onas_sock.sock)) == 0) return sockd; diff -Nru clamav-0.103.2+dfsg/clamonacc/client/socket.h clamav-0.103.5+dfsg/clamonacc/client/socket.h --- clamav-0.103.2+dfsg/clamonacc/client/socket.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/client/socket.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * * Author: Mickey Sola * diff -Nru clamav-0.103.2+dfsg/clamonacc/CMakeLists.txt clamav-0.103.5+dfsg/clamonacc/CMakeLists.txt --- clamav-0.103.2+dfsg/clamonacc/CMakeLists.txt 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/CMakeLists.txt 2022-01-10 23:17:45.000000000 +0000 @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. cmake_minimum_required( VERSION 3.12...3.13 ) diff -Nru clamav-0.103.2+dfsg/clamonacc/fanotif/fanotif.c clamav-0.103.5+dfsg/clamonacc/fanotif/fanotif.c --- clamav-0.103.2+dfsg/clamonacc/fanotif/fanotif.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/fanotif/fanotif.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2019-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * * Authors: Mickey Sola * diff -Nru clamav-0.103.2+dfsg/clamonacc/fanotif/fanotif.h clamav-0.103.5+dfsg/clamonacc/fanotif/fanotif.h --- clamav-0.103.2+dfsg/clamonacc/fanotif/fanotif.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/fanotif/fanotif.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2019-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * * Authors: Mickey Sola * diff -Nru clamav-0.103.2+dfsg/clamonacc/inotif/hash.c clamav-0.103.5+dfsg/clamonacc/inotif/hash.c --- clamav-0.103.2+dfsg/clamonacc/inotif/hash.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/inotif/hash.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2015-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * * Authors: Mickey Sola * diff -Nru clamav-0.103.2+dfsg/clamonacc/inotif/hash.h clamav-0.103.5+dfsg/clamonacc/inotif/hash.h --- clamav-0.103.2+dfsg/clamonacc/inotif/hash.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/inotif/hash.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2015-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * * Authors: Mickey Sola * diff -Nru clamav-0.103.2+dfsg/clamonacc/inotif/inotif.c clamav-0.103.5+dfsg/clamonacc/inotif/inotif.c --- clamav-0.103.2+dfsg/clamonacc/inotif/inotif.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/inotif/inotif.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2019-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * * Authors: Mickey Sola * @@ -712,7 +712,7 @@ const char *path, const char *child_path, const struct inotify_event *event, int wd, uint64_t in_mask) { - if (!(event->mask & IN_ISDIR)){ + if (!(event->mask & IN_ISDIR)) { return; } diff -Nru clamav-0.103.2+dfsg/clamonacc/inotif/inotif.h clamav-0.103.5+dfsg/clamonacc/inotif/inotif.h --- clamav-0.103.2+dfsg/clamonacc/inotif/inotif.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/inotif/inotif.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2015-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * * Authors: Mickey Sola * diff -Nru clamav-0.103.2+dfsg/clamonacc/Makefile.am clamav-0.103.5+dfsg/clamonacc/Makefile.am --- clamav-0.103.2+dfsg/clamonacc/Makefile.am 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/Makefile.am 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ # -# Copyright (C) 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2018-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. # # Authors: Mickey Sola # diff -Nru clamav-0.103.2+dfsg/clamonacc/Makefile.in clamav-0.103.5+dfsg/clamonacc/Makefile.in --- clamav-0.103.2+dfsg/clamonacc/Makefile.in 2021-04-06 19:04:43.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/Makefile.in 2022-01-10 23:18:12.000000000 +0000 @@ -15,7 +15,7 @@ @SET_MAKE@ # -# Copyright (C) 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2018-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. # # Authors: Mickey Sola # diff -Nru clamav-0.103.2+dfsg/clamonacc/misc/utils.c clamav-0.103.5+dfsg/clamonacc/misc/utils.c --- clamav-0.103.2+dfsg/clamonacc/misc/utils.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/misc/utils.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2019-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * * Authors: Mickey Sola * @@ -104,15 +104,15 @@ break; case EMFILE: case ENFILE: - if (0 == retry) { + if (3 >= retry) { logg("*ClamMisc: waiting for consumer thread to catch up then retrying ...\n"); - sleep(3); - retry = 1; + sleep(6); + retry += 1; continue; } else { logg("*ClamMisc: fds have been exhausted ... attempting to force the consumer thread to catch up ... (excluding for safety)\n"); pthread_cond_signal(&onas_scan_queue_empty_cond); - sleep(3); + sleep(6); return CHK_FOUND; } case ERANGE: diff -Nru clamav-0.103.2+dfsg/clamonacc/misc/utils.h clamav-0.103.5+dfsg/clamonacc/misc/utils.h --- clamav-0.103.2+dfsg/clamonacc/misc/utils.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/misc/utils.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2019-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * * Authors: Mickey Sola * diff -Nru clamav-0.103.2+dfsg/clamonacc/scan/onas_queue.c clamav-0.103.5+dfsg/clamonacc/scan/onas_queue.c --- clamav-0.103.2+dfsg/clamonacc/scan/onas_queue.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/scan/onas_queue.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2019-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * * Authors: Mickey Sola * @@ -197,9 +197,12 @@ static int onas_consume_event(threadpool thpool) { - pthread_mutex_lock(&onas_queue_lock); + while (onas_queue_is_b_empty()) { + pthread_cond_wait(&onas_scan_queue_empty_cond, &onas_queue_lock); + } + struct onas_event_queue_node *popped_node = g_onas_event_queue_head->next; if (onas_queue_is_b_empty()) { diff -Nru clamav-0.103.2+dfsg/clamonacc/scan/onas_queue.h clamav-0.103.5+dfsg/clamonacc/scan/onas_queue.h --- clamav-0.103.2+dfsg/clamonacc/scan/onas_queue.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/scan/onas_queue.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2019-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * * Authors: Mickey Sola * diff -Nru clamav-0.103.2+dfsg/clamonacc/scan/thread.c clamav-0.103.5+dfsg/clamonacc/scan/thread.c --- clamav-0.103.2+dfsg/clamonacc/scan/thread.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/scan/thread.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2015-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * * Authors: Mickey Sola * diff -Nru clamav-0.103.2+dfsg/clamonacc/scan/thread.h clamav-0.103.5+dfsg/clamonacc/scan/thread.h --- clamav-0.103.2+dfsg/clamonacc/scan/thread.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamonacc/scan/thread.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2015-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * * Authors: Mickey Sola * diff -Nru clamav-0.103.2+dfsg/clamscan/clamscan.c clamav-0.103.5+dfsg/clamscan/clamscan.c --- clamav-0.103.2+dfsg/clamscan/clamscan.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamscan/clamscan.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm @@ -232,7 +232,7 @@ mprintf("\n"); mprintf(" Clam AntiVirus: Scanner %s\n", get_version()); printf(" By The ClamAV Team: https://www.clamav.net/about.html#credits\n"); - printf(" (C) 2021 Cisco Systems, Inc.\n"); + printf(" (C) 2022 Cisco Systems, Inc.\n"); mprintf("\n"); mprintf(" clamscan [options] [file/directory/-]\n"); mprintf("\n"); diff -Nru clamav-0.103.2+dfsg/clamscan/CMakeLists.txt clamav-0.103.5+dfsg/clamscan/CMakeLists.txt --- clamav-0.103.2+dfsg/clamscan/CMakeLists.txt 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamscan/CMakeLists.txt 2022-01-10 23:17:45.000000000 +0000 @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. cmake_minimum_required( VERSION 3.12...3.13 ) diff -Nru clamav-0.103.2+dfsg/clamscan/global.h clamav-0.103.5+dfsg/clamscan/global.h --- clamav-0.103.2+dfsg/clamscan/global.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamscan/global.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm diff -Nru clamav-0.103.2+dfsg/clamscan/Makefile.am clamav-0.103.5+dfsg/clamscan/Makefile.am --- clamav-0.103.2+dfsg/clamscan/Makefile.am 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamscan/Makefile.am 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ # -# Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. # Copyright (C) 2007-2013 Sourcefire, Inc. # Copyright (C) 2002-2007 Tomasz Kojm # diff -Nru clamav-0.103.2+dfsg/clamscan/Makefile.in clamav-0.103.5+dfsg/clamscan/Makefile.in --- clamav-0.103.2+dfsg/clamscan/Makefile.in 2021-04-06 19:04:43.000000000 +0000 +++ clamav-0.103.5+dfsg/clamscan/Makefile.in 2022-01-10 23:18:12.000000000 +0000 @@ -15,7 +15,7 @@ @SET_MAKE@ # -# Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. # Copyright (C) 2007-2013 Sourcefire, Inc. # Copyright (C) 2002-2007 Tomasz Kojm # diff -Nru clamav-0.103.2+dfsg/clamscan/manager.c clamav-0.103.5+dfsg/clamscan/manager.c --- clamav-0.103.2+dfsg/clamscan/manager.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamscan/manager.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm diff -Nru clamav-0.103.2+dfsg/clamscan/manager.h clamav-0.103.5+dfsg/clamscan/manager.h --- clamav-0.103.2+dfsg/clamscan/manager.h 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamscan/manager.h 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * Authors: Tomasz Kojm diff -Nru clamav-0.103.2+dfsg/clamsubmit/clamsubmit.c clamav-0.103.5+dfsg/clamsubmit/clamsubmit.c --- clamav-0.103.2+dfsg/clamsubmit/clamsubmit.c 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamsubmit/clamsubmit.c 2022-01-10 23:17:45.000000000 +0000 @@ -1,3 +1,30 @@ +/* + * ClamAV Malware and False Positive Reporting Tool + * + * Copyright (C) 2014-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * Authors: Shawn Webb, Steve Morgan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "clamav-config.h" +#endif + +#include #include #include #if HAVE_UNISTD_H @@ -23,6 +50,7 @@ #include "misc.h" #include "getopt.h" #include "cert_util.h" +#include "output.h" #define OPTS "e:p:n:N:V:H:h?v?d" @@ -32,7 +60,6 @@ typedef struct _header_data { int len; - char *cfduid; char *session; } header_data; @@ -41,14 +68,14 @@ char *str; } write_data; -int g_debug = 0; +bool g_debug = false; void usage(char *name) { printf("\n"); printf(" Clam AntiVirus: Malware and False Positive Reporting Tool %s\n", get_version()); printf(" By The ClamAV Team: https://www.clamav.net/about.html#credits\n"); - printf(" (C) 2021 Cisco Systems, Inc.\n"); + printf(" (C) 2022 Cisco Systems, Inc.\n"); printf("\n"); printf(" %s -hHinpVvd?\n", name); printf("\n"); @@ -85,22 +112,22 @@ sp = ptr + clen + 1; ep = strchr(sp, ';'); if (ep == NULL) { - fprintf(stderr, "header_cb(): malformed cookie\n"); + logg("!header_cb(): malformed cookie\n"); return 0; } mem = malloc(ep - sp + 1); if (mem == NULL) { - fprintf(stderr, "header_cb(): malloc failed\n"); + logg("!header_cb(): malloc failed\n"); return 0; } memcpy(mem, sp, ep - sp); mem[ep - sp] = '\0'; - if (!strncmp(mem, "__cfduid", 8)) - hd->cfduid = mem; - else if (!strncmp(mem, "_clamav-net_session", strlen("_clamav-net_session"))) + if (!strncmp(mem, "_clamav-net_session", strlen("_clamav-net_session"))) hd->session = mem; - else - fprintf(stderr, "header_cb(): unrecognized cookie\n"); + else { + logg("!header_cb(): unrecognized cookie\n"); + free(mem); + } } return len; } @@ -114,7 +141,7 @@ if (len) { str = realloc(wd->str, wd->len + len + 1); if (str == NULL) { - fprintf(stderr, "write_cb() realloc failure\n"); + logg("!write_cb() realloc failure\n"); return 0; } memcpy(str + wd->len, ptr, len); @@ -140,10 +167,10 @@ if (json_object_object_get_ex(ps_json_obj, key, &json_obj)) { json_str = json_object_get_string(json_obj); if (json_str == NULL) { - fprintf(stderr, "Error: json_object_get_string() for %s.\n", key); + logg("!Error: json_object_get_string() for %s.\n", key); } } else { - fprintf(stderr, "Error: json_object_object_get_ex() for %s.\n", key); + logg("!Error: json_object_object_get_ex() for %s.\n", key); } return json_str; } @@ -161,23 +188,28 @@ int setURL = 0, fromStream = 0; const char *json_str; write_data wd = {0, NULL}; - header_data hd_malware = {0, NULL, NULL}; - header_data hd_presigned = {0, NULL, NULL}; + header_data hd_malware = {0, NULL}; + header_data hd_presigned = {0, NULL}; json_object *ps_json_obj = NULL; - int malware = 0; + bool malware = false; int len = 0; char *submissionID = NULL; char *fpvname = NULL; - char *sp, *ep, *str; - char *authenticity_token = NULL; - char *urlp; + char *sp, *ep; + + char *authenticity_token_header = NULL; + char *authenticity_token = NULL; + char *session_cookie = NULL; + + char *url_for_auth_token; + char *url_for_presigned_cookie; curl_global_init(CURL_GLOBAL_ALL); clam_curl = curl_easy_init(); if (clam_curl == NULL) { - fprintf(stderr, "ERROR: Could not initialize libcurl.\n"); - goto cleanup; + logg("!ERROR: Could not initialize libcurl.\n"); + goto done; } memset(userAgent, 0, sizeof(userAgent)); @@ -187,7 +219,7 @@ userAgent[sizeof(userAgent) - 1] = 0; if (CURLE_OK != curl_easy_setopt(clam_curl, CURLOPT_USERAGENT, userAgent)) { - fprintf(stderr, "!create_curl_handle: Failed to set CURLOPT_USERAGENT (%s)!\n", userAgent); + logg("!!create_curl_handle: Failed to set CURLOPT_USERAGENT (%s)!\n", userAgent); } while ((ch = my_getopt(argc, argv, OPTS)) > 0) { @@ -209,14 +241,14 @@ case 'n': if (setURL) usage(argv[0]); - malware = 1; + malware = true; filename = optarg; break; case 'V': fpvname = optarg; break; case 'd': - g_debug = 1; + g_debug = true; break; case 'h': case '?': @@ -228,15 +260,15 @@ if (!(name) || !(email) || !(filename)) usage(argv[0]); - if (malware == 0 && fpvname == NULL) { - fprintf(stderr, "Detected virus name(-V) required for false positive submissions.\n"); + if (malware == false && fpvname == NULL) { + logg("!Detected virus name(-V) required for false positive submissions.\n"); usage(argv[0]); } if (strlen(filename) == 1 && filename[0] == '-') { filename = read_stream(); if (!(filename)) { - fprintf(stderr, "ERROR: Unable to read stream\n"); - goto cleanup; + logg("!ERROR: Unable to read stream\n"); + goto done; } fromStream = 1; } @@ -244,31 +276,34 @@ if (g_debug) { /* ask libcurl to show us the verbose output */ if (CURLE_OK != curl_easy_setopt(clam_curl, CURLOPT_VERBOSE, 1L)) { - fprintf(stderr, "!ERROR: Failed to set CURLOPT_VERBOSE!\n"); + logg("!!ERROR: Failed to set CURLOPT_VERBOSE!\n"); } if (CURLE_OK != curl_easy_setopt(clam_curl, CURLOPT_STDERR, stdout)) { - fprintf(stderr, "!ERROR: Failed to direct curl debug output to stdout!\n"); + logg("!!ERROR: Failed to direct curl debug output to stdout!\n"); } } if (CURLE_OK != curl_easy_setopt(clam_curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1)) { - fprintf(stderr, "ERROR: Failed to set HTTP version to 1.1 (to prevent 2.0 responses which we don't yet parse properly)!\n"); + logg("!ERROR: Failed to set HTTP version to 1.1 (to prevent 2.0 responses which we don't yet parse properly)!\n"); } #if defined(C_DARWIN) || defined(_WIN32) if (CURLE_OK != curl_easy_setopt(clam_curl, CURLOPT_SSL_CTX_FUNCTION, *sslctx_function)) { - fprintf(stderr, "ERROR: Failed to set SSL CTX function!\n"); + logg("!ERROR: Failed to set SSL CTX function!\n"); } #else set_tls_ca_bundle(clam_curl); #endif - /*** The GET malware|fp ***/ - if (malware == 1) - urlp = "https://www.clamav.net/reports/malware"; - else - urlp = "https://www.clamav.net/reports/fp"; - curl_easy_setopt(clam_curl, CURLOPT_URL, urlp); + /* + * GET authenticity token + */ + if (malware == true) { + url_for_auth_token = "https://www.clamav.net/reports/malware"; + } else { + url_for_auth_token = "https://www.clamav.net/reports/fp"; + } + curl_easy_setopt(clam_curl, CURLOPT_URL, url_for_auth_token); curl_easy_setopt(clam_curl, CURLOPT_HTTPGET, 1); curl_easy_setopt(clam_curl, CURLOPT_WRITEDATA, &wd); curl_easy_setopt(clam_curl, CURLOPT_WRITEFUNCTION, write_cb); @@ -276,30 +311,30 @@ curl_easy_setopt(clam_curl, CURLOPT_HEADERFUNCTION, header_cb); res = curl_easy_perform(clam_curl); if (res != CURLE_OK) { - fprintf(stderr, "Error in GET %s: %s\n", urlp, curl_easy_strerror(res)); - goto cleanup; + logg("!Error in GET %s: %s\n", url_for_auth_token, curl_easy_strerror(res)); + goto done; } if (wd.str != NULL) { sp = strstr(wd.str, "name=\"authenticity_token\""); if (sp == NULL) { - fprintf(stderr, "Authenticity token element not found.\n"); - goto cleanup; + logg("!Authenticity token element not found.\n"); + goto done; } sp = strstr(sp, "value="); if (sp == NULL) { - fprintf(stderr, "Authenticity token value not found.\n"); - goto cleanup; + logg("!Authenticity token value not found.\n"); + goto done; } sp += 7; ep = strchr(sp, '"'); if (ep == NULL) { - fprintf(stderr, "Authenticity token malformed.\n"); - goto cleanup; + logg("!Authenticity token malformed.\n"); + goto done; } authenticity_token = malloc(ep - sp + 1); if (authenticity_token == NULL) { - fprintf(stderr, "no memory for authenticity token.\n"); - goto cleanup; + logg("!no memory for authenticity token.\n"); + goto done; } memcpy(authenticity_token, sp, ep - sp); authenticity_token[ep - sp] = '\0'; @@ -307,120 +342,128 @@ wd.str = NULL; } wd.len = 0; - urlp = NULL; - /*** The GET presigned ***/ - if (malware == 1) - curl_easy_setopt(clam_curl, CURLOPT_URL, "https://www.clamav.net/presigned?type=malware"); - else - curl_easy_setopt(clam_curl, CURLOPT_URL, "https://www.clamav.net/presigned?type=fp"); + /* record the session cookie for later use, if exists */ + if (NULL == hd_malware.session) { + logg("!clamav.net/presigned response missing session ID cookie.\nWill try without the cookie.\n"); + // goto done; // Note: unclear if the session cookie is required. Can't hurt to try w/out it? + } else { + len = strlen(hd_malware.session) + 3; + session_cookie = malloc(len); + if (session_cookie == NULL) { + logg("!No memory for GET presigned cookies\n"); + goto done; + } + if (snprintf(session_cookie, len, "%s;", hd_malware.session) > len) { + logg("!snprintf() failed formatting GET presigned cookies\n"); + goto done; + } + } + + /* + * GET presigned cookie + */ + if (malware == true) { + url_for_presigned_cookie = "https://www.clamav.net/presigned?type=malware"; + } else { + url_for_presigned_cookie = "https://www.clamav.net/presigned?type=fp"; + } + + curl_easy_setopt(clam_curl, CURLOPT_URL, url_for_presigned_cookie); curl_easy_setopt(clam_curl, CURLOPT_HTTPGET, 1); - if (NULL == hd_malware.cfduid || NULL == hd_malware.session) { - fprintf(stderr, "invalid cfduid and/or session id values provided by clamav.net/presigned. Unable to continue submission."); - goto cleanup; - } - - len = strlen(hd_malware.cfduid) + strlen(hd_malware.session) + 3; - str = malloc(len); - if (str == NULL) { - fprintf(stderr, "No memory for GET presigned cookies\n"); - goto cleanup; - } - if (snprintf(str, len, "%s; %s;", hd_malware.cfduid, hd_malware.session) > len) { - fprintf(stderr, "snprintf() failed formatting GET presigned cookies\n"); - free(str); - goto cleanup; - } - curl_easy_setopt(clam_curl, CURLOPT_COOKIE, str); - free(str); - len = strlen(authenticity_token) + 15; - str = malloc(len); - if (str == NULL) { - fprintf(stderr, "No memory for GET presigned X-CSRF-Token\n"); - goto cleanup; - } - if (snprintf(str, len, "X-CSRF-Token: %s", authenticity_token) > len) { - fprintf(stderr, "snprintf() failed for GET presigned X-CSRF-Token\n"); - free(str); - goto cleanup; + if (NULL != session_cookie) { + curl_easy_setopt(clam_curl, CURLOPT_COOKIE, session_cookie); } - slist = curl_slist_append(slist, str); - free(str); + + /* Include an X-CSRF-Token header using the authenticity token retrieved with the presigned GET request */ + len = strlen(authenticity_token) + strlen("X-CSRF-Token: ") + 1; + authenticity_token_header = malloc(len); + if (authenticity_token_header == NULL) { + logg("!No memory for GET presigned X-CSRF-Token\n"); + goto done; + } + if (snprintf(authenticity_token_header, len, "X-CSRF-Token: %s", authenticity_token) > len) { + logg("!snprintf() failed for GET presigned X-CSRF-Token\n"); + goto done; + } + slist = curl_slist_append(slist, authenticity_token_header); + free(authenticity_token_header); + authenticity_token_header = NULL; + curl_easy_setopt(clam_curl, CURLOPT_HTTPHEADER, slist); curl_easy_setopt(clam_curl, CURLOPT_HEADERDATA, &hd_presigned); curl_easy_setopt(clam_curl, CURLOPT_HEADERFUNCTION, header_cb); - if (malware == 1) - curl_easy_setopt(clam_curl, CURLOPT_REFERER, "https://www.clamav.net/reports/malware"); - else - curl_easy_setopt(clam_curl, CURLOPT_REFERER, "https://www.clamav.net/reports/fp"); + curl_easy_setopt(clam_curl, CURLOPT_REFERER, url_for_auth_token); res = curl_easy_perform(clam_curl); if (res != CURLE_OK) { - fprintf(stderr, "Error in GET presigned: %s\n", curl_easy_strerror(res)); - goto cleanup; + logg("!Error in GET reports: %s\n", curl_easy_strerror(res)); + goto done; } curl_slist_free_all(slist); slist = NULL; - /*** The POST to AWS ***/ + /* + * POST the report to AWS + */ ps_json_obj = json_tokener_parse(wd.str); if (ps_json_obj == NULL) { - fprintf(stderr, "Error in json_tokener_parse of %.*s\n", wd.len, wd.str); - goto cleanup; + logg("!Error in json_tokener_parse of %.*s\n", wd.len, wd.str); + goto done; } json_str = presigned_get_string(ps_json_obj, "key"); if (json_str == NULL) { - fprintf(stderr, "Error in presigned_get_string parsing key from json object\n"); - goto cleanup; + logg("!Error in presigned_get_string parsing key from json object\n"); + goto done; } sp = strchr(json_str, '/'); if (sp == NULL) { - fprintf(stderr, "Error: malformed 'key' string in GET presigned response (missing '/'.\n"); - goto cleanup; + logg("!Error: malformed 'key' string in GET presigned response (missing '/'.\n"); + goto done; } sp++; ep = strchr(sp, '-'); if (ep == NULL) { - fprintf(stderr, "Error: malformed 'key' string in GET presigned response (missing '-'.\n"); - goto cleanup; + logg("!Error: malformed 'key' string in GET presigned response (missing '-'.\n"); + goto done; } submissionID = malloc(ep - sp + 1); if (submissionID == NULL) { - fprintf(stderr, "Error: malloc submissionID.\n"); - goto cleanup; + logg("!Error: malloc submissionID.\n"); + goto done; } memcpy(submissionID, sp, ep - sp); submissionID[ep - sp] = '\0'; aws_curl = curl_easy_init(); if (!(aws_curl)) { - fprintf(stderr, "ERROR: Could not initialize libcurl POST presigned\n"); - goto cleanup; + logg("!ERROR: Could not initialize libcurl POST presigned\n"); + goto done; } if (CURLE_OK != curl_easy_setopt(aws_curl, CURLOPT_USERAGENT, userAgent)) { - fprintf(stderr, "!create_curl_handle: Failed to set CURLOPT_USERAGENT (%s)!\n", userAgent); + logg("!!create_curl_handle: Failed to set CURLOPT_USERAGENT (%s)!\n", userAgent); } if (g_debug) { /* ask libcurl to show us the verbose output */ if (CURLE_OK != curl_easy_setopt(aws_curl, CURLOPT_VERBOSE, 1L)) { - fprintf(stderr, "!ERROR: Failed to set CURLOPT_VERBOSE!\n"); + logg("!!ERROR: Failed to set CURLOPT_VERBOSE!\n"); } if (CURLE_OK != curl_easy_setopt(aws_curl, CURLOPT_STDERR, stdout)) { - fprintf(stderr, "!ERROR: Failed to direct curl debug output to stdout!\n"); + logg("!!ERROR: Failed to direct curl debug output to stdout!\n"); } } if (CURLE_OK != curl_easy_setopt(aws_curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1)) { - fprintf(stderr, "ERROR: Failed to set HTTP version to 1.1 (to prevent 2.0 responses which we don't yet parse properly)!\n"); + logg("!ERROR: Failed to set HTTP version to 1.1 (to prevent 2.0 responses which we don't yet parse properly)!\n"); } #if defined(C_DARWIN) || defined(_WIN32) if (CURLE_OK != curl_easy_setopt(aws_curl, CURLOPT_SSL_CTX_FUNCTION, *sslctx_function)) { - fprintf(stderr, "ERROR: Failed to set SSL CTX function!\n"); + logg("!ERROR: Failed to set SSL CTX function!\n"); } #else set_tls_ca_bundle(aws_curl); @@ -430,50 +473,50 @@ json_str = presigned_get_string(ps_json_obj, "acl"); if (json_str == NULL) { - fprintf(stderr, "Error in presigned_get_string parsing acl from json object\n"); - goto cleanup; + logg("!Error in presigned_get_string parsing acl from json object\n"); + goto done; } curl_formadd(&post, &last, CURLFORM_COPYNAME, "acl", CURLFORM_COPYCONTENTS, json_str, CURLFORM_END); json_str = presigned_get_string(ps_json_obj, "policy"); if (json_str == NULL) { - fprintf(stderr, "Error in presigned_get_string parsing policy from json object\n"); - goto cleanup; + logg("!Error in presigned_get_string parsing policy from json object\n"); + goto done; } curl_formadd(&post, &last, CURLFORM_COPYNAME, "policy", CURLFORM_COPYCONTENTS, json_str, CURLFORM_END); json_str = presigned_get_string(ps_json_obj, "x-amz-meta-original-filename"); if (json_str == NULL) { - fprintf(stderr, "Error in presigned_get_string parsing x-amz-meta-original-filename from json object\n"); - goto cleanup; + logg("!Error in presigned_get_string parsing x-amz-meta-original-filename from json object\n"); + goto done; } curl_formadd(&post, &last, CURLFORM_COPYNAME, "x-amz-meta-original-filename", CURLFORM_COPYCONTENTS, json_str, CURLFORM_END); json_str = presigned_get_string(ps_json_obj, "x-amz-credential"); if (json_str == NULL) { - fprintf(stderr, "Error in presigned_get_string parsing x-amz-credential from json object\n"); - goto cleanup; + logg("!Error in presigned_get_string parsing x-amz-credential from json object\n"); + goto done; } curl_formadd(&post, &last, CURLFORM_COPYNAME, "x-amz-credential", CURLFORM_COPYCONTENTS, json_str, CURLFORM_END); json_str = presigned_get_string(ps_json_obj, "x-amz-algorithm"); if (json_str == NULL) { - fprintf(stderr, "Error in presigned_get_string parsing x-amz-algorithm from json object\n"); - goto cleanup; + logg("!Error in presigned_get_string parsing x-amz-algorithm from json object\n"); + goto done; } curl_formadd(&post, &last, CURLFORM_COPYNAME, "x-amz-algorithm", CURLFORM_COPYCONTENTS, json_str, CURLFORM_END); json_str = presigned_get_string(ps_json_obj, "x-amz-date"); if (json_str == NULL) { - fprintf(stderr, "Error in presigned_get_string parsing x-amz-date from json object\n"); - goto cleanup; + logg("!Error in presigned_get_string parsing x-amz-date from json object\n"); + goto done; } curl_formadd(&post, &last, CURLFORM_COPYNAME, "x-amz-date", CURLFORM_COPYCONTENTS, json_str, CURLFORM_END); json_str = presigned_get_string(ps_json_obj, "x-amz-signature"); if (json_str == NULL) { - fprintf(stderr, "Error in presigned_get_string parsing x-amz-signature from json object\n"); - goto cleanup; + logg("!Error in presigned_get_string parsing x-amz-signature from json object\n"); + goto done; } curl_formadd(&post, &last, CURLFORM_COPYNAME, "x-amz-signature", CURLFORM_COPYCONTENTS, json_str, CURLFORM_END); @@ -486,8 +529,8 @@ res = curl_easy_perform(aws_curl); if (res != CURLE_OK) { - fprintf(stderr, "Error in POST AWS: %s\n", curl_easy_strerror(res)); - goto cleanup; + logg("!Error in POST AWS: %s\n", curl_easy_strerror(res)); + goto done; } curl_slist_free_all(slist); slist = NULL; @@ -497,36 +540,30 @@ curl_easy_cleanup(aws_curl); aws_curl = NULL; json_object_put(ps_json_obj); - free(wd.str); - wd.str = NULL; + + if (wd.str != NULL) { + free(wd.str); + wd.str = NULL; + } wd.len = 0; /*** The POST submit to clamav.net ***/ slist = curl_slist_append(slist, "Expect:"); - len = strlen(hd_malware.cfduid) + strlen(hd_malware.session) + 3; - str = malloc(len); - if (str == NULL) { - fprintf(stderr, "No memory for POST submit cookies.\n"); - goto cleanup; - } - if (snprintf(str, len, "%s; %s;", hd_malware.cfduid, hd_malware.session) > len) { - fprintf(stderr, "snprintf() failed formatting POST submit cookies\n"); - free(str); - goto cleanup; + + if (NULL != session_cookie) { + curl_easy_setopt(clam_curl, CURLOPT_COOKIE, session_cookie); } - curl_easy_setopt(clam_curl, CURLOPT_COOKIE, str); - free(str); + curl_formadd(&post, &last, CURLFORM_COPYNAME, "utf8", CURLFORM_COPYCONTENTS, "\x27\x13", CURLFORM_END); curl_formadd(&post, &last, CURLFORM_COPYNAME, "authenticity_token", CURLFORM_COPYCONTENTS, authenticity_token, CURLFORM_END); curl_formadd(&post, &last, CURLFORM_COPYNAME, "submissionID", CURLFORM_COPYCONTENTS, submissionID, CURLFORM_END); curl_formadd(&post, &last, CURLFORM_COPYNAME, "type", CURLFORM_COPYCONTENTS, malware ? "malware" : "fp", CURLFORM_END); curl_formadd(&post, &last, CURLFORM_COPYNAME, "sendername", CURLFORM_COPYCONTENTS, name, CURLFORM_END); curl_formadd(&post, &last, CURLFORM_COPYNAME, "email", CURLFORM_COPYCONTENTS, email, CURLFORM_END); - if (malware == 0) { - curl_formadd(&post, &last, CURLFORM_COPYNAME, "virusname", CURLFORM_COPYCONTENTS, fpvname, CURLFORM_END); + if (malware == true) { + curl_formadd(&post, &last, CURLFORM_COPYNAME, "shareSample", CURLFORM_COPYCONTENTS, "on", CURLFORM_END); } else { - if (malware == 1) - curl_formadd(&post, &last, CURLFORM_COPYNAME, "shareSample", CURLFORM_COPYCONTENTS, "on", CURLFORM_END); + curl_formadd(&post, &last, CURLFORM_COPYNAME, "virusname", CURLFORM_COPYCONTENTS, fpvname, CURLFORM_END); } curl_formadd(&post, &last, CURLFORM_COPYNAME, "description", CURLFORM_COPYCONTENTS, "clamsubmit", CURLFORM_END); curl_formadd(&post, &last, CURLFORM_COPYNAME, "notify", CURLFORM_COPYCONTENTS, "on", CURLFORM_END); @@ -537,37 +574,43 @@ curl_easy_setopt(clam_curl, CURLOPT_HEADERFUNCTION, NULL); res = curl_easy_perform(clam_curl); if (res != CURLE_OK) { - fprintf(stderr, "Error in POST submit: %s\n", curl_easy_strerror(res)); - goto cleanup; + logg("!Error in POST submit: %s\n", curl_easy_strerror(res)); + goto done; } else { long response_code; curl_easy_getinfo(clam_curl, CURLINFO_RESPONSE_CODE, &response_code); if (response_code / 100 == 3) { - curl_easy_getinfo(clam_curl, CURLINFO_REDIRECT_URL, &urlp); - if (urlp == NULL) { - fprintf(stderr, "POST submit Location URL is NULL.\n"); - goto cleanup; + curl_easy_getinfo(clam_curl, CURLINFO_REDIRECT_URL, &url_for_auth_token); + if (url_for_auth_token == NULL) { + logg("!POST submit Location URL is NULL.\n"); + goto done; } - sp = strstr(urlp, "/reports/"); + sp = strstr(url_for_auth_token, "/reports/"); if (sp == NULL) { - fprintf(stderr, "POST submit Location URL is malformed.\n"); + logg("!POST submit Location URL is malformed.\n"); } else if (!strcmp(sp, "/reports/success")) { - fprintf(stdout, "Submission success!\n"); + logg("Submission success!\n"); status = 0; } else if (!strcmp(sp, "/reports/failure")) { - fprintf(stdout, "Submission failed\n"); + logg("Submission failed\n"); } else { - fprintf(stdout, "Unknown submission status %s\n", sp); + logg("Unknown submission status %s\n", sp); } } else { - fprintf(stderr, "Unexpected POST submit response code: %li\n", response_code); + logg("!Unexpected POST submit response code: %li\n", response_code); } } -cleanup: +done: /* * Cleanup */ + if (authenticity_token_header != NULL) { + free(authenticity_token_header); + } + if (session_cookie != NULL) { + free(session_cookie); + } if (slist != NULL) { curl_slist_free_all(slist); } @@ -587,15 +630,9 @@ wd.str = NULL; wd.len = 0; } - if (hd_malware.cfduid != NULL) { - free(hd_malware.cfduid); - } if (hd_malware.session != NULL) { free(hd_malware.session); } - if (hd_presigned.cfduid != NULL) { - free(hd_presigned.cfduid); - } if (hd_presigned.session != NULL) { free(hd_presigned.session); } diff -Nru clamav-0.103.2+dfsg/clamsubmit/CMakeLists.txt clamav-0.103.5+dfsg/clamsubmit/CMakeLists.txt --- clamav-0.103.2+dfsg/clamsubmit/CMakeLists.txt 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/clamsubmit/CMakeLists.txt 2022-01-10 23:17:45.000000000 +0000 @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. cmake_minimum_required( VERSION 3.12...3.13 ) diff -Nru clamav-0.103.2+dfsg/CMakeLists.txt clamav-0.103.5+dfsg/CMakeLists.txt --- clamav-0.103.2+dfsg/CMakeLists.txt 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/CMakeLists.txt 2022-01-10 23:17:45.000000000 +0000 @@ -1,4 +1,4 @@ -# Copyright (C) 2019-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2019-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. cmake_minimum_required( VERSION 3.12...3.13 ) set(CMAKE_C_STANDARD 90) @@ -15,7 +15,7 @@ set(VERSION_SUFFIX "") project( ClamAV - VERSION "0.103.2" + VERSION "0.103.5" DESCRIPTION "ClamAV open source email, web, and end-point anti-virus toolkit." ) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) @@ -24,7 +24,7 @@ set(PACKAGE_NAME "${PROJECT_NAME}") set(PACKAGE_VERSION "${PROJECT_VERSION}") set(PACKAGE_STRING "${PROJECT_NAME} ${PROJECT_VERSION}${VERSION_SUFFIX}") -set(PACKAGE_BUGREPORT "https://bugzilla.clamav.net/") +set(PACKAGE_BUGREPORT "https://github.com/Cisco-Talos/clamav/issues") set(PACKAGE_URL "https://www.clamav.net/") HexVersion(PACKAGE_VERSION_NUM ${PROJECT_VERSION_MAJOR} ${PROJECT_VERSION_MINOR} ${PROJECT_VERSION_PATCH}) diff -Nru clamav-0.103.2+dfsg/configure clamav-0.103.5+dfsg/configure --- clamav-0.103.2+dfsg/configure 2021-04-06 19:04:41.000000000 +0000 +++ clamav-0.103.5+dfsg/configure 2022-01-10 23:18:10.000000000 +0000 @@ -1,8 +1,8 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for ClamAV 0.103.2. +# Generated by GNU Autoconf 2.69 for ClamAV 0.103.5. # -# Report bugs to . +# Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -275,8 +275,8 @@ $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and -$0: https://bugzilla.clamav.net/ about your system, -$0: including any error possibly output before this +$0: https://github.com/Cisco-Talos/clamav/issues about your +$0: system, including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi @@ -592,9 +592,9 @@ # Identity of this package. PACKAGE_NAME='ClamAV' PACKAGE_TARNAME='clamav' -PACKAGE_VERSION='0.103.2' -PACKAGE_STRING='ClamAV 0.103.2' -PACKAGE_BUGREPORT='https://bugzilla.clamav.net/' +PACKAGE_VERSION='0.103.5' +PACKAGE_STRING='ClamAV 0.103.5' +PACKAGE_BUGREPORT='https://github.com/Cisco-Talos/clamav/issues' PACKAGE_URL='https://www.clamav.net/' ac_unique_file="clamscan/clamscan.c" @@ -1606,7 +1606,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures ClamAV 0.103.2 to adapt to many kinds of systems. +\`configure' configures ClamAV 0.103.5 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1687,7 +1687,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of ClamAV 0.103.2:";; + short | recursive ) echo "Configuration of ClamAV 0.103.5:";; esac cat <<\_ACEOF --enable-dependency-tracking @@ -1858,7 +1858,7 @@ Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. -Report bugs to . +Report bugs to . ClamAV home page: . _ACEOF ac_status=$? @@ -1922,7 +1922,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -ClamAV configure 0.103.2 +ClamAV configure 0.103.5 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2119,9 +2119,9 @@ $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} -( $as_echo "## ------------------------------------------- ## -## Report this to https://bugzilla.clamav.net/ ## -## ------------------------------------------- ##" +( $as_echo "## ----------------------------------------------------------- ## +## Report this to https://github.com/Cisco-Talos/clamav/issues ## +## ----------------------------------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac @@ -2550,7 +2550,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by ClamAV $as_me 0.103.2, which was +It was created by ClamAV $as_me 0.103.5, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -4308,7 +4308,7 @@ # Define the identity of the package. PACKAGE='clamav' - VERSION='0.103.2' + VERSION='0.103.5' # Some tools Automake needs. @@ -6036,7 +6036,7 @@ $as_echo "#define PACKAGE PACKAGE_NAME" >>confdefs.h -VERSION="0.103.2" +VERSION="0.103.5" major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/^0-9//g"` minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/^0-9//g"` @@ -31896,7 +31896,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by ClamAV $as_me 0.103.2, which was +This file was extended by ClamAV $as_me 0.103.5, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -31956,14 +31956,14 @@ Configuration commands: $config_commands -Report bugs to . +Report bugs to . ClamAV home page: ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -ClamAV config.status 0.103.2 +ClamAV config.status 0.103.5 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -34813,7 +34813,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by ClamAV $as_me 0.103.2, which was +This file was extended by ClamAV $as_me 0.103.5, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -34873,14 +34873,14 @@ Configuration commands: $config_commands -Report bugs to . +Report bugs to . ClamAV home page: ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -ClamAV config.status 0.103.2 +ClamAV config.status 0.103.5 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -37602,6 +37602,6 @@ fi if test "x$clamonacc-curl" = "xdeprecated"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ****** your libcurl (e.g. libcurl-devel) is older than the recommended version. Installing ClamAV with clamonacc requires libcurl 7.40 or higher to use fdpassing. ****** fdpassing with clamonacc will be disabled on your system. ****** for more information on ClamAV's on-access scanner, please read our documentation: https://www.clamav.net/documents/on-access-scanning#on-access-scanning" >&5 -$as_echo "$as_me: WARNING: ****** your libcurl (e.g. libcurl-devel) is older than the recommended version. Installing ClamAV with clamonacc requires libcurl 7.40 or higher to use fdpassing. ****** fdpassing with clamonacc will be disabled on your system. ****** for more information on ClamAV's on-access scanner, please read our documentation: https://www.clamav.net/documents/on-access-scanning#on-access-scanning" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ****** your libcurl (e.g. libcurl-devel) is older than the recommended version. Installing ClamAV with clamonacc requires libcurl 7.40 or higher to use fdpassing. ****** fdpassing with clamonacc will be disabled on your system. ****** for more information on ClamAV's on-access scanner, please read our documentation: https://docs.clamav.net/manual/OnAccess.html" >&5 +$as_echo "$as_me: WARNING: ****** your libcurl (e.g. libcurl-devel) is older than the recommended version. Installing ClamAV with clamonacc requires libcurl 7.40 or higher to use fdpassing. ****** fdpassing with clamonacc will be disabled on your system. ****** for more information on ClamAV's on-access scanner, please read our documentation: https://docs.clamav.net/manual/OnAccess.html" >&2;} fi diff -Nru clamav-0.103.2+dfsg/configure.ac clamav-0.103.5+dfsg/configure.ac --- clamav-0.103.2+dfsg/configure.ac 2021-04-12 18:43:41.000000000 +0000 +++ clamav-0.103.5+dfsg/configure.ac 2022-01-12 19:53:22.000000000 +0000 @@ -1,4 +1,4 @@ -dnl Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +dnl Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. dnl Copyright (C) 2007-2013 Sourcefire, Inc. dnl Copyright (C) 2002-2007 Tomasz Kojm dnl socklen_t check (c) Alexander V. Lukyanov @@ -22,7 +22,7 @@ dnl For a release change [devel] to the real version [0.xy] dnl also change VERSION below -AC_INIT([ClamAV], [0.103.2], [https://bugzilla.clamav.net/], [clamav], [https://www.clamav.net/]) +AC_INIT([ClamAV], [0.103.5], [https://github.com/Cisco-Talos/clamav/issues], [clamav], [https://www.clamav.net/]) dnl put configure auxiliary into config AC_CONFIG_AUX_DIR([config]) @@ -398,6 +398,6 @@ AC_MSG_WARN([m4_normalize([ ****** your libcurl (e.g. libcurl-devel) is older than the recommended version. Installing ClamAV with clamonacc requires libcurl 7.40 or higher to use fdpassing. ****** fdpassing with clamonacc will be disabled on your system. -****** for more information on ClamAV's on-access scanner, please read our documentation: https://www.clamav.net/documents/on-access-scanning#on-access-scanning +****** for more information on ClamAV's on-access scanner, please read our documentation: https://docs.clamav.net/manual/OnAccess.html ])]) fi diff -Nru clamav-0.103.2+dfsg/database/Makefile.am clamav-0.103.5+dfsg/database/Makefile.am --- clamav-0.103.2+dfsg/database/Makefile.am 2021-04-06 19:03:42.000000000 +0000 +++ clamav-0.103.5+dfsg/database/Makefile.am 2022-01-10 23:17:45.000000000 +0000 @@ -1,5 +1,5 @@ # -# Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. # Copyright (C) 2007-2013 Sourcefire, Inc. # Copyright (C) 2002-2007 Tomasz Kojm # Fixes by Arkadiusz Miskiewicz diff -Nru clamav-0.103.2+dfsg/database/Makefile.in clamav-0.103.5+dfsg/database/Makefile.in --- clamav-0.103.2+dfsg/database/Makefile.in 2021-04-06 19:04:43.000000000 +0000 +++ clamav-0.103.5+dfsg/database/Makefile.in 2022-01-10 23:18:12.000000000 +0000 @@ -15,7 +15,7 @@ @SET_MAKE@ # -# Copyright (C) 2013-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. # Copyright (C) 2007-2013 Sourcefire, Inc. # Copyright (C) 2002-2007 Tomasz Kojm # Fixes by Arkadiusz Miskiewicz diff -Nru clamav-0.103.2+dfsg/debian/changelog clamav-0.103.5+dfsg/debian/changelog --- clamav-0.103.2+dfsg/debian/changelog 2021-04-29 11:25:03.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/changelog 2022-01-17 13:19:19.000000000 +0000 @@ -1,21 +1,45 @@ -clamav (0.103.2+dfsg-0ubuntu0.20.04.2) focal-security; urgency=medium +clamav (0.103.5+dfsg-1~20.04.1) focal-security; urgency=medium - * SECURITY REGRESSION: clamdscan - MULTISCAN parameter causes - Segmentation fault. - (LP: #1926300) - - debian/patches/lp_1926300_multiscan_param_segfault.patch: fix - --fdpass -m & ExcludePath crash in clamd/scanner.c, - libclamav/others.h, libclamav/others_common.c, - unit_tests/check_clamd.c. + * Rebuild as security update for focal. - -- Leonidas Da Silva Barbosa Thu, 29 Apr 2021 08:25:03 -0300 + -- Marc Deslauriers Mon, 17 Jan 2022 08:19:19 -0500 -clamav (0.103.2+dfsg-0ubuntu0.20.04.1) focal-security; urgency=medium +clamav (0.103.5+dfsg-1) unstable; urgency=medium - * Updated to version 0.103.2 to fix security issues. - - CVE-2021-1252, CVE-2021-1404, CVE-2021-1405 + * Import 0.103.5 + - CVE-2022-20698 (Fix for invalid pointer read that may cause a crash). + - Update symbol file. - -- Marc Deslauriers Thu, 15 Apr 2021 12:39:30 -0400 + -- Sebastian Andrzej Siewior Wed, 12 Jan 2022 21:31:23 +0100 + +clamav (0.103.4+dfsg-1) unstable; urgency=medium + + * Import 0.103.4 + - Update symbol file. + * Add clamonacc.8. + * Install clamonacc only on Linux. Patch by Laurent Bigonvill + (Closes: #992776). + * Drop unused libidn11-dev dependency, suggested by Simon Josefsson + (Closes: #991976). + + -- Sebastian Andrzej Siewior Tue, 16 Nov 2021 22:03:15 +0100 + +clamav (0.103.3+dfsg-1) unstable; urgency=medium + + * Import 0.103.3 + - Update symbol file. + - Regression: clamdscan segfaults with --fdpass --multipass and + ExcludePath (Closes: #988218). + * Remove clamav user on purge (Closes: #987861). + * Remove freshclam.dat on purge. + + -- Sebastian Andrzej Siewior Fri, 02 Jul 2021 00:06:16 +0200 + +clamav (0.103.2+dfsg-2) unstable; urgency=medium + + * Remove deprecated option SafeBrowsing from debconf templates. + + -- Sebastian Andrzej Siewior Thu, 15 Apr 2021 21:59:11 +0200 clamav (0.103.2+dfsg-1) unstable; urgency=medium diff -Nru clamav-0.103.2+dfsg/debian/clamav-base.postrm clamav-0.103.5+dfsg/debian/clamav-base.postrm --- clamav-0.103.2+dfsg/debian/clamav-base.postrm 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/clamav-base.postrm 2022-01-12 20:02:01.000000000 +0000 @@ -41,6 +41,7 @@ rm -f /var/log/clamav/*.log* /etc/clamav/*.conf.dpkg-old rm -f /var/lib/clamav/*.md5sum || true rm -f $DATABASEDIR/main.cvd $DATABASEDIR/daily.cvd $DATABASEDIR/bytecode.cvd $DATABASEDIR/bytecode.cld + userdel clamav || true ;; remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) ;; diff -Nru clamav-0.103.2+dfsg/debian/clamav-daemon.manpages clamav-0.103.5+dfsg/debian/clamav-daemon.manpages --- clamav-0.103.2+dfsg/debian/clamav-daemon.manpages 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/clamav-daemon.manpages 2022-01-12 20:02:01.000000000 +0000 @@ -2,3 +2,4 @@ debian/tmp/usr/share/man/man1/clamdtop.1 debian/tmp/usr/share/man/man5/clamd.conf.5 debian/tmp/usr/share/man/man8/clamd.8 +debian/tmp/usr/share/man/man8/clamonacc.8 diff -Nru clamav-0.103.2+dfsg/debian/clamav-freshclam.config.in clamav-0.103.5+dfsg/debian/clamav-freshclam.config.in --- clamav-0.103.2+dfsg/debian/clamav-freshclam.config.in 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/clamav-freshclam.config.in 2022-01-12 20:02:01.000000000 +0000 @@ -55,7 +55,6 @@ [ "$NotifyClamd" = "/etc/clamav/clamd.conf" ] && NotifyClamd="true" db_set clamav-freshclam/NotifyClamd "$NotifyClamd" || true fi -set_debconf_value freshclam SafeBrowsing set_debconf_value freshclam Bytecode set_debconf_value freshclam PrivateMirror set_debconf_value freshclam LogRotate @@ -154,13 +153,10 @@ Stateupdate_interval ;; notify_daemon) - StateGeneric medium clamav-freshclam/NotifyClamd safebrowsing update_interval - ;; - safebrowsing) - StateGeneric low clamav-freshclam/SafeBrowsing bytecode notify_daemon + StateGeneric medium clamav-freshclam/NotifyClamd bytecode update_interval ;; bytecode) - StateGeneric low clamav-freshclam/Bytecode private_mirror safebrowsing + StateGeneric low clamav-freshclam/Bytecode private_mirror notify_daemon ;; private_mirror) StateGeneric low clamav-freshclam/PrivateMirror log_rotate bytecode diff -Nru clamav-0.103.2+dfsg/debian/clamav-freshclam.postinst.in clamav-0.103.5+dfsg/debian/clamav-freshclam.postinst.in --- clamav-0.103.2+dfsg/debian/clamav-freshclam.postinst.in 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/clamav-freshclam.postinst.in 2022-01-12 20:02:01.000000000 +0000 @@ -98,8 +98,6 @@ fi db_get clamav-freshclam/NotifyClamd || true [ "$RET" = "true" ] && notify="/etc/clamav/clamd.conf" - db_get clamav-freshclam/SafeBrowsing || true - SafeBrowsing="$RET" db_get clamav-freshclam/Bytecode || true Bytecode="$RET" db_get clamav-freshclam/LogRotate || true @@ -229,7 +227,6 @@ TestDatabases $TestDatabases ScriptedUpdates $ScriptedUpdates CompressLocalDatabase $CompressLocalDatabase -SafeBrowsing $SafeBrowsing Bytecode $Bytecode EOF diff -Nru clamav-0.103.2+dfsg/debian/clamav-freshclam.postrm clamav-0.103.5+dfsg/debian/clamav-freshclam.postrm --- clamav-0.103.2+dfsg/debian/clamav-freshclam.postrm 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/clamav-freshclam.postrm 2022-01-12 20:02:01.000000000 +0000 @@ -45,6 +45,7 @@ ${workdir}/daily.inc/* \ ${workdir}/main.inc/* \ ${workdir}/mirrors.dat \ + ${workdir}/freshclam.dat \ ${workdir}/interface" for i in $RMLIST; do rm -f $i > /dev/null 2>&1 || true diff -Nru clamav-0.103.2+dfsg/debian/clamav-freshclam.templates clamav-0.103.5+dfsg/debian/clamav-freshclam.templates --- clamav-0.103.2+dfsg/debian/clamav-freshclam.templates 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/clamav-freshclam.templates 2022-01-12 20:02:01.000000000 +0000 @@ -88,20 +88,6 @@ that a new virus may slip through even if the database is up to date. Do not use this if you do not use clamd, as it will produce errors. -Template: clamav-freshclam/SafeBrowsing -Type: boolean -Default: false -_Description: Do you want to enable support for Google Safe Browsing? - When activated for the first time, freshclam will download a new database - file (safebrowsing.cvd) which will be automatically loaded by clamd and - clamscan during the next reload, provided that the heuristic phishing - detection is turned on. This database includes information about websites - that may be phishing sites or possible sources of malware. When using this - option, it's mandatory to run freshclam at least every 30 minutes. - Freshclam uses the ClamAV's mirror infrastructure to distribute the - database and its updates but all the contents are provided under Google's - terms of use. - Template: clamav-freshclam/Bytecode Type: boolean Default: true diff -Nru clamav-0.103.2+dfsg/debian/clean clamav-0.103.5+dfsg/debian/clean --- clamav-0.103.2+dfsg/debian/clean 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/clean 2022-01-12 20:02:01.000000000 +0000 @@ -31,6 +31,7 @@ docs/man/clamd.conf.5 docs/man/clamdscan.1 docs/man/clamdtop.1 +docs/man/clamonacc.8 docs/man/clamscan.1 docs/man/freshclam.1 docs/man/freshclam.conf.5 diff -Nru clamav-0.103.2+dfsg/debian/control clamav-0.103.5+dfsg/debian/control --- clamav-0.103.2+dfsg/debian/control 2021-04-15 16:39:30.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/control 2022-01-12 20:02:01.000000000 +0000 @@ -1,8 +1,7 @@ Source: clamav Section: utils Priority: optional -Maintainer: Ubuntu Developers -XSBC-Original-Maintainer: ClamAV Team +Maintainer: ClamAV Team Uploaders: Michael Meskes , Michael Tautschnig , Scott Kitterman , @@ -110,7 +109,6 @@ Depends: libbz2-dev, libc6-dev | libc-dev, libclamav9 (= ${binary:Version}), - libidn11-dev, libssl-dev, libtommath-dev, zlib1g-dev, diff -Nru clamav-0.103.2+dfsg/debian/.git-dpm clamav-0.103.5+dfsg/debian/.git-dpm --- clamav-0.103.2+dfsg/debian/.git-dpm 2021-04-12 18:51:20.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/.git-dpm 2022-01-12 20:02:01.000000000 +0000 @@ -1,8 +1,8 @@ # see git-dpm(1) from git-dpm package -e3fe85c3a7fd4c273b1fe61b871ab9d935bca417 -e3fe85c3a7fd4c273b1fe61b871ab9d935bca417 -21b35cadc5ce6e45c2700201681499bc45eb5419 -21b35cadc5ce6e45c2700201681499bc45eb5419 -clamav_0.103.2+dfsg.orig.tar.xz -461ec3a7b45851e31a1cd9a4458473f9b4dc2677 -5123788 +077fbc57056cf17664c8e3ef04e06ebad2bafbc1 +077fbc57056cf17664c8e3ef04e06ebad2bafbc1 +857db6f7fe6291d39090c77afdefa94d97161cb2 +857db6f7fe6291d39090c77afdefa94d97161cb2 +clamav_0.103.5+dfsg.orig.tar.xz +6b767150c6b8cb9c8c6b11a2ae3df961fd65533f +7121136 diff -Nru clamav-0.103.2+dfsg/debian/libclamav9.symbols clamav-0.103.5+dfsg/debian/libclamav9.symbols --- clamav-0.103.2+dfsg/debian/libclamav9.symbols 2021-04-12 19:14:58.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/libclamav9.symbols 2022-01-12 20:15:54.000000000 +0000 @@ -1,20 +1,20 @@ libclamav.so.9 libclamav9 #MINVER# * Build-Depends-Package: libclamav-dev - CLAMAV_PRIVATE@CLAMAV_PRIVATE 0.103.2 + CLAMAV_PRIVATE@CLAMAV_PRIVATE 0.103.5 CLAMAV_PUBLIC@CLAMAV_PUBLIC 0.101.0 - __cli_strcasestr@CLAMAV_PRIVATE 0.103.2 - __cli_strndup@CLAMAV_PRIVATE 0.103.2 - __cli_strnlen@CLAMAV_PRIVATE 0.103.2 - __cli_strnstr@CLAMAV_PRIVATE 0.103.2 - base64Flush@CLAMAV_PRIVATE 0.103.2 - blobAddData@CLAMAV_PRIVATE 0.103.2 - blobCreate@CLAMAV_PRIVATE 0.103.2 - blobDestroy@CLAMAV_PRIVATE 0.103.2 - cl_ASN1_GetTimeT@CLAMAV_PRIVATE 0.103.2 + __cli_strcasestr@CLAMAV_PRIVATE 0.103.5 + __cli_strndup@CLAMAV_PRIVATE 0.103.5 + __cli_strnlen@CLAMAV_PRIVATE 0.103.5 + __cli_strnstr@CLAMAV_PRIVATE 0.103.5 + base64Flush@CLAMAV_PRIVATE 0.103.5 + blobAddData@CLAMAV_PRIVATE 0.103.5 + blobCreate@CLAMAV_PRIVATE 0.103.5 + blobDestroy@CLAMAV_PRIVATE 0.103.5 + cl_ASN1_GetTimeT@CLAMAV_PRIVATE 0.103.5 cl_always_gen_section_hash@CLAMAV_PUBLIC 0.101.0 - cl_base64_decode@CLAMAV_PRIVATE 0.103.2 - cl_base64_encode@CLAMAV_PRIVATE 0.103.2 - cl_cleanup_crypto@CLAMAV_PRIVATE 0.103.2 + cl_base64_decode@CLAMAV_PRIVATE 0.103.5 + cl_base64_encode@CLAMAV_PRIVATE 0.103.5 + cl_cleanup_crypto@CLAMAV_PRIVATE 0.103.5 cl_countsigs@CLAMAV_PUBLIC 0.101.0 cl_cvdfree@CLAMAV_PUBLIC 0.101.0 cl_cvdhead@CLAMAV_PUBLIC 0.101.0 @@ -54,21 +54,21 @@ cl_fmap_close@CLAMAV_PUBLIC 0.101.0 cl_fmap_open_handle@CLAMAV_PUBLIC 0.101.0 cl_fmap_open_memory@CLAMAV_PUBLIC 0.101.0 - cl_get_pkey_file@CLAMAV_PRIVATE 0.103.2 - cl_get_x509_from_mem@CLAMAV_PRIVATE 0.103.2 - cl_hash_data@CLAMAV_PRIVATE 0.103.2 + cl_get_pkey_file@CLAMAV_PRIVATE 0.103.5 + cl_get_x509_from_mem@CLAMAV_PRIVATE 0.103.5 + cl_hash_data@CLAMAV_PRIVATE 0.103.5 cl_hash_destroy@CLAMAV_PUBLIC 0.101.0 - cl_hash_file_fd@CLAMAV_PRIVATE 0.103.2 - cl_hash_file_fd_ctx@CLAMAV_PRIVATE 0.103.2 - cl_hash_file_fp@CLAMAV_PRIVATE 0.103.2 + cl_hash_file_fd@CLAMAV_PRIVATE 0.103.5 + cl_hash_file_fd_ctx@CLAMAV_PRIVATE 0.103.5 + cl_hash_file_fp@CLAMAV_PRIVATE 0.103.5 cl_hash_init@CLAMAV_PUBLIC 0.101.0 cl_init@CLAMAV_PUBLIC 0.101.0 - cl_initialize_crypto@CLAMAV_PRIVATE 0.103.2 + cl_initialize_crypto@CLAMAV_PRIVATE 0.103.5 cl_load@CLAMAV_PUBLIC 0.101.0 - cl_load_cert@CLAMAV_PRIVATE 0.103.2 - cl_load_crl@CLAMAV_PRIVATE 0.103.2 + cl_load_cert@CLAMAV_PRIVATE 0.103.5 + cl_load_crl@CLAMAV_PRIVATE 0.103.5 cl_retdbdir@CLAMAV_PUBLIC 0.101.0 - cl_retflevel@CLAMAV_PUBLIC 0.103.0 + cl_retflevel@CLAMAV_PUBLIC 0.103.5 cl_retver@CLAMAV_PUBLIC 0.101.0 cl_scandesc@CLAMAV_PUBLIC 0.101.0 cl_scandesc_callback@CLAMAV_PUBLIC 0.101.0 @@ -76,195 +76,196 @@ cl_scanfile_callback@CLAMAV_PUBLIC 0.101.0 cl_scanmap_callback@CLAMAV_PUBLIC 0.101.0 cl_set_clcb_msg@CLAMAV_PUBLIC 0.101.0 - cl_sha1@CLAMAV_PRIVATE 0.103.2 - cl_sha256@CLAMAV_PRIVATE 0.103.2 - cl_sign_data@CLAMAV_PRIVATE 0.103.2 - cl_sign_data_keyfile@CLAMAV_PRIVATE 0.103.2 - cl_sign_file_fd@CLAMAV_PRIVATE 0.103.2 - cl_sign_file_fp@CLAMAV_PRIVATE 0.103.2 + cl_sha1@CLAMAV_PRIVATE 0.103.5 + cl_sha256@CLAMAV_PRIVATE 0.103.5 + cl_sign_data@CLAMAV_PRIVATE 0.103.5 + cl_sign_data_keyfile@CLAMAV_PRIVATE 0.103.5 + cl_sign_file_fd@CLAMAV_PRIVATE 0.103.5 + cl_sign_file_fp@CLAMAV_PRIVATE 0.103.5 cl_statchkdir@CLAMAV_PUBLIC 0.101.0 cl_statfree@CLAMAV_PUBLIC 0.101.0 cl_statinidir@CLAMAV_PUBLIC 0.101.0 cl_strerror@CLAMAV_PUBLIC 0.101.0 cl_update_hash@CLAMAV_PUBLIC 0.101.0 - cl_validate_certificate_chain@CLAMAV_PRIVATE 0.103.2 - cl_validate_certificate_chain_ts_dir@CLAMAV_PRIVATE 0.103.2 - cl_verify_signature@CLAMAV_PRIVATE 0.103.2 - cl_verify_signature_fd@CLAMAV_PRIVATE 0.103.2 - cl_verify_signature_fd_x509@CLAMAV_PRIVATE 0.103.2 - cl_verify_signature_fd_x509_keyfile@CLAMAV_PRIVATE 0.103.2 - cl_verify_signature_hash@CLAMAV_PRIVATE 0.103.2 - cl_verify_signature_hash_x509@CLAMAV_PRIVATE 0.103.2 - cl_verify_signature_hash_x509_keyfile@CLAMAV_PRIVATE 0.103.2 - cl_verify_signature_x509@CLAMAV_PRIVATE 0.103.2 - cl_verify_signature_x509_keyfile@CLAMAV_PRIVATE 0.103.2 - cli_ac_buildtrie@CLAMAV_PRIVATE 0.103.2 - cli_ac_chklsig@CLAMAV_PRIVATE 0.103.2 - cli_ac_free@CLAMAV_PRIVATE 0.103.2 - cli_ac_freedata@CLAMAV_PRIVATE 0.103.2 - cli_ac_init@CLAMAV_PRIVATE 0.103.2 - cli_ac_initdata@CLAMAV_PRIVATE 0.103.2 - cli_ac_scanbuff@CLAMAV_PRIVATE 0.103.2 - cli_basename@CLAMAV_PRIVATE 0.103.2 - cli_bm_free@CLAMAV_PRIVATE 0.103.2 - cli_bm_init@CLAMAV_PRIVATE 0.103.2 - cli_bm_scanbuff@CLAMAV_PRIVATE 0.103.2 - cli_build_regex_list@CLAMAV_PRIVATE 0.103.2 - cli_bytecode_context_alloc@CLAMAV_PRIVATE 0.103.2 - cli_bytecode_context_clear@CLAMAV_PRIVATE 0.103.2 - cli_bytecode_context_destroy@CLAMAV_PRIVATE 0.103.2 - cli_bytecode_context_getresult_int@CLAMAV_PRIVATE 0.103.2 - cli_bytecode_context_set_trace@CLAMAV_PRIVATE 0.103.2 - cli_bytecode_context_setfile@CLAMAV_PRIVATE 0.103.2 - cli_bytecode_context_setfuncid@CLAMAV_PRIVATE 0.103.2 - cli_bytecode_context_setparam_int@CLAMAV_PRIVATE 0.103.2 - cli_bytecode_context_setparam_ptr@CLAMAV_PRIVATE 0.103.2 - cli_bytecode_debug@CLAMAV_PRIVATE 0.103.2 - cli_bytecode_debug_printsrc@CLAMAV_PRIVATE 0.103.2 - cli_bytecode_describe@CLAMAV_PRIVATE 0.103.2 - cli_bytecode_destroy@CLAMAV_PRIVATE 0.103.2 - cli_bytecode_done@CLAMAV_PRIVATE 0.103.2 - cli_bytecode_init@CLAMAV_PRIVATE 0.103.2 - cli_bytecode_load@CLAMAV_PRIVATE 0.103.2 - cli_bytecode_prepare2@CLAMAV_PRIVATE 0.103.2 - cli_bytecode_printversion@CLAMAV_PRIVATE 0.103.2 - cli_bytecode_run@CLAMAV_PRIVATE 0.103.2 - cli_bytefunc_describe@CLAMAV_PRIVATE 0.103.2 - cli_byteinst_describe@CLAMAV_PRIVATE 0.103.2 - cli_bytetype_describe@CLAMAV_PRIVATE 0.103.2 - cli_bytevalue_describe@CLAMAV_PRIVATE 0.103.2 - cli_calloc@CLAMAV_PRIVATE 0.103.2 - cli_check_auth_header@CLAMAV_PRIVATE 0.103.2 - cli_chomp@CLAMAV_PRIVATE 0.103.2 - cli_codepage_to_utf8@CLAMAV_PRIVATE 0.103.2 - cli_ctime@CLAMAV_PRIVATE 0.103.2 - cli_cvdunpack@CLAMAV_PRIVATE 0.103.2 - cli_dbgmsg_internal@CLAMAV_PRIVATE 0.103.2 - cli_dconf_init@CLAMAV_PRIVATE 0.103.2 - cli_debug_flag@CLAMAV_PRIVATE 0.103.2 - cli_detect_environment@CLAMAV_PRIVATE 0.103.2 - cli_disasm_one@CLAMAV_PRIVATE 0.103.2 - cli_errmsg@CLAMAV_PRIVATE 0.103.2 - cli_filecopy@CLAMAV_PRIVATE 0.103.2 - cli_free_vba_project@CLAMAV_PRIVATE 0.103.2 - cli_ftw@CLAMAV_PRIVATE 0.103.2 - cli_genhash_pe@CLAMAV_PRIVATE 0.103.2 - cli_gentemp@CLAMAV_PRIVATE 0.103.2 - cli_gentemp_with_prefix@CLAMAV_PRIVATE 0.103.2 - cli_gentempfd@CLAMAV_PRIVATE 0.103.2 - cli_get_filepath_from_filedesc@CLAMAV_PRIVATE 0.103.2 - cli_gettmpdir@CLAMAV_PRIVATE 0.103.2 - cli_hashfile@CLAMAV_PRIVATE 0.103.2 - cli_hashset_destroy@CLAMAV_PRIVATE 0.103.2 - cli_hashstream@CLAMAV_PRIVATE 0.103.2 - cli_hex2str@CLAMAV_PRIVATE 0.103.2 - cli_hex2ui@CLAMAV_PRIVATE 0.103.2 - cli_initroots@CLAMAV_PRIVATE 0.103.2 - cli_isnumber@CLAMAV_PRIVATE 0.103.2 - cli_js_destroy@CLAMAV_PRIVATE 0.103.2 - cli_js_init@CLAMAV_PRIVATE 0.103.2 - cli_js_output@CLAMAV_PRIVATE 0.103.2 - cli_js_parse_done@CLAMAV_PRIVATE 0.103.2 - cli_js_process_buffer@CLAMAV_PRIVATE 0.103.2 - cli_ldbtokenize@CLAMAV_PRIVATE 0.103.2 - cli_malloc@CLAMAV_PRIVATE 0.103.2 - cli_memstr@CLAMAV_PRIVATE 0.103.2 - cli_ole2_extract@CLAMAV_PRIVATE 0.103.2 - cli_parse_add@CLAMAV_PRIVATE 0.103.2 - cli_pcre_build@CLAMAV_PRIVATE 0.103.2 - cli_pcre_freeoff@CLAMAV_PRIVATE 0.103.2 - cli_pcre_init@CLAMAV_PRIVATE 0.103.2 - cli_pcre_perf_events_destroy@CLAMAV_PRIVATE 0.103.2 - cli_pcre_perf_print@CLAMAV_PRIVATE 0.103.2 - cli_pcre_recaloff@CLAMAV_PRIVATE 0.103.2 - cli_pcre_scanbuf@CLAMAV_PRIVATE 0.103.2 - cli_ppt_vba_read@CLAMAV_PRIVATE 0.103.2 - cli_printcxxver@CLAMAV_PRIVATE 0.103.2 - cli_readn@CLAMAV_PRIVATE 0.103.2 - cli_realloc@CLAMAV_PRIVATE 0.103.2 - cli_realpath@CLAMAV_PRIVATE 0.103.2 - cli_regcomp@CLAMAV_PRIVATE 0.103.2 - cli_regex2suffix@CLAMAV_PRIVATE 0.103.2 - cli_regexec@CLAMAV_PRIVATE 0.103.2 - cli_regfree@CLAMAV_PRIVATE 0.103.2 - cli_rmdirs@CLAMAV_PRIVATE 0.103.2 - cli_rndnum@CLAMAV_PRIVATE 0.103.2 - cli_sanitize_filepath@CLAMAV_PRIVATE 0.103.2 - cli_scan_buff@CLAMAV_PRIVATE 0.103.2 - cli_scan_fmap@CLAMAV_PRIVATE 0.103.2 - cli_sigopts_handler@CLAMAV_PRIVATE 0.103.2 - cli_sigperf_events_destroy@CLAMAV_PRIVATE 0.103.2 - cli_sigperf_print@CLAMAV_PRIVATE 0.103.2 - cli_str2hex@CLAMAV_PRIVATE 0.103.2 - cli_strbcasestr@CLAMAV_PRIVATE 0.103.2 - cli_strdup@CLAMAV_PRIVATE 0.103.2 - cli_strerror@CLAMAV_PRIVATE 0.103.2 - cli_strlcat@CLAMAV_PRIVATE 0.103.2 - cli_strlcpy@CLAMAV_PRIVATE 0.103.2 - cli_strntoul@CLAMAV_PRIVATE 0.103.2 - cli_strrcpy@CLAMAV_PRIVATE 0.103.2 - cli_strtok@CLAMAV_PRIVATE 0.103.2 - cli_strtokbuf@CLAMAV_PRIVATE 0.103.2 - cli_strtokenize@CLAMAV_PRIVATE 0.103.2 - cli_textbuffer_append_normalize@CLAMAV_PRIVATE 0.103.2 - cli_unescape@CLAMAV_PRIVATE 0.103.2 - cli_unlink@CLAMAV_PRIVATE 0.103.2 - cli_url_canon@CLAMAV_PRIVATE 0.103.2 - cli_utf16_to_utf8@CLAMAV_PRIVATE 0.103.2 - cli_utf16toascii@CLAMAV_PRIVATE 0.103.2 - cli_vba_inflate@CLAMAV_PRIVATE 0.103.2 - cli_vba_readdir@CLAMAV_PRIVATE 0.103.2 - cli_versig2@CLAMAV_PRIVATE 0.103.2 - cli_versig@CLAMAV_PRIVATE 0.103.2 - cli_warnmsg@CLAMAV_PRIVATE 0.103.2 - cli_wm_decrypt_macro@CLAMAV_PRIVATE 0.103.2 - cli_wm_readdir@CLAMAV_PRIVATE 0.103.2 - cli_writen@CLAMAV_PRIVATE 0.103.2 - decodeLine@CLAMAV_PRIVATE 0.103.2 - disasmbuf@CLAMAV_PRIVATE 0.103.2 - fmap@CLAMAV_PRIVATE 0.103.2 - fmap_duplicate@CLAMAV_PRIVATE 0.103.2 - free_duplicate_fmap@CLAMAV_PRIVATE 0.103.2 - get_fpu_endian@CLAMAV_PRIVATE 0.103.2 - have_clamjit@CLAMAV_PRIVATE 0.103.2 - have_rar@CLAMAV_PRIVATE 0.103.2 - html_normalise_map@CLAMAV_PRIVATE 0.103.2 - html_normalise_mem@CLAMAV_PRIVATE 0.103.2 - html_screnc_decode@CLAMAV_PRIVATE 0.103.2 - html_tag_arg_free@CLAMAV_PRIVATE 0.103.2 - init_domainlist@CLAMAV_PRIVATE 0.103.2 - init_regex_list@CLAMAV_PRIVATE 0.103.2 - init_whitelist@CLAMAV_PRIVATE 0.103.2 - is_regex_ok@CLAMAV_PRIVATE 0.103.2 - load_regex_matcher@CLAMAV_PRIVATE 0.103.2 + cl_validate_certificate_chain@CLAMAV_PRIVATE 0.103.5 + cl_validate_certificate_chain_ts_dir@CLAMAV_PRIVATE 0.103.5 + cl_verify_signature@CLAMAV_PRIVATE 0.103.5 + cl_verify_signature_fd@CLAMAV_PRIVATE 0.103.5 + cl_verify_signature_fd_x509@CLAMAV_PRIVATE 0.103.5 + cl_verify_signature_fd_x509_keyfile@CLAMAV_PRIVATE 0.103.5 + cl_verify_signature_hash@CLAMAV_PRIVATE 0.103.5 + cl_verify_signature_hash_x509@CLAMAV_PRIVATE 0.103.5 + cl_verify_signature_hash_x509_keyfile@CLAMAV_PRIVATE 0.103.5 + cl_verify_signature_x509@CLAMAV_PRIVATE 0.103.5 + cl_verify_signature_x509_keyfile@CLAMAV_PRIVATE 0.103.5 + cli_ac_buildtrie@CLAMAV_PRIVATE 0.103.5 + cli_ac_chklsig@CLAMAV_PRIVATE 0.103.5 + cli_ac_free@CLAMAV_PRIVATE 0.103.5 + cli_ac_freedata@CLAMAV_PRIVATE 0.103.5 + cli_ac_init@CLAMAV_PRIVATE 0.103.5 + cli_ac_initdata@CLAMAV_PRIVATE 0.103.5 + cli_ac_scanbuff@CLAMAV_PRIVATE 0.103.5 + cli_basename@CLAMAV_PRIVATE 0.103.5 + cli_bm_free@CLAMAV_PRIVATE 0.103.5 + cli_bm_init@CLAMAV_PRIVATE 0.103.5 + cli_bm_scanbuff@CLAMAV_PRIVATE 0.103.5 + cli_build_regex_list@CLAMAV_PRIVATE 0.103.5 + cli_bytecode_context_alloc@CLAMAV_PRIVATE 0.103.5 + cli_bytecode_context_clear@CLAMAV_PRIVATE 0.103.5 + cli_bytecode_context_destroy@CLAMAV_PRIVATE 0.103.5 + cli_bytecode_context_getresult_int@CLAMAV_PRIVATE 0.103.5 + cli_bytecode_context_set_trace@CLAMAV_PRIVATE 0.103.5 + cli_bytecode_context_setfile@CLAMAV_PRIVATE 0.103.5 + cli_bytecode_context_setfuncid@CLAMAV_PRIVATE 0.103.5 + cli_bytecode_context_setparam_int@CLAMAV_PRIVATE 0.103.5 + cli_bytecode_context_setparam_ptr@CLAMAV_PRIVATE 0.103.5 + cli_bytecode_debug@CLAMAV_PRIVATE 0.103.5 + cli_bytecode_debug_printsrc@CLAMAV_PRIVATE 0.103.5 + cli_bytecode_describe@CLAMAV_PRIVATE 0.103.5 + cli_bytecode_destroy@CLAMAV_PRIVATE 0.103.5 + cli_bytecode_done@CLAMAV_PRIVATE 0.103.5 + cli_bytecode_init@CLAMAV_PRIVATE 0.103.5 + cli_bytecode_load@CLAMAV_PRIVATE 0.103.5 + cli_bytecode_prepare2@CLAMAV_PRIVATE 0.103.5 + cli_bytecode_printversion@CLAMAV_PRIVATE 0.103.5 + cli_bytecode_run@CLAMAV_PRIVATE 0.103.5 + cli_bytefunc_describe@CLAMAV_PRIVATE 0.103.5 + cli_byteinst_describe@CLAMAV_PRIVATE 0.103.5 + cli_bytetype_describe@CLAMAV_PRIVATE 0.103.5 + cli_bytevalue_describe@CLAMAV_PRIVATE 0.103.5 + cli_calloc@CLAMAV_PRIVATE 0.103.5 + cli_check_auth_header@CLAMAV_PRIVATE 0.103.5 + cli_chomp@CLAMAV_PRIVATE 0.103.5 + cli_codepage_to_utf8@CLAMAV_PRIVATE 0.103.5 + cli_ctime@CLAMAV_PRIVATE 0.103.5 + cli_cvdunpack@CLAMAV_PRIVATE 0.103.5 + cli_dbgmsg_internal@CLAMAV_PRIVATE 0.103.5 + cli_dconf_init@CLAMAV_PRIVATE 0.103.5 + cli_debug_flag@CLAMAV_PRIVATE 0.103.5 + cli_detect_environment@CLAMAV_PRIVATE 0.103.5 + cli_disasm_one@CLAMAV_PRIVATE 0.103.5 + cli_errmsg@CLAMAV_PRIVATE 0.103.5 + cli_filecopy@CLAMAV_PRIVATE 0.103.5 + cli_free_vba_project@CLAMAV_PRIVATE 0.103.5 + cli_ftw@CLAMAV_PRIVATE 0.103.5 + cli_genhash_pe@CLAMAV_PRIVATE 0.103.5 + cli_gentemp@CLAMAV_PRIVATE 0.103.5 + cli_gentemp_with_prefix@CLAMAV_PRIVATE 0.103.5 + cli_gentempfd@CLAMAV_PRIVATE 0.103.5 + cli_get_filepath_from_filedesc@CLAMAV_PRIVATE 0.103.5 + cli_gettmpdir@CLAMAV_PRIVATE 0.103.5 + cli_hashfile@CLAMAV_PRIVATE 0.103.5 + cli_hashset_destroy@CLAMAV_PRIVATE 0.103.5 + cli_hashstream@CLAMAV_PRIVATE 0.103.5 + cli_hex2str@CLAMAV_PRIVATE 0.103.5 + cli_hex2ui@CLAMAV_PRIVATE 0.103.5 + cli_initroots@CLAMAV_PRIVATE 0.103.5 + cli_isnumber@CLAMAV_PRIVATE 0.103.5 + cli_js_destroy@CLAMAV_PRIVATE 0.103.5 + cli_js_init@CLAMAV_PRIVATE 0.103.5 + cli_js_output@CLAMAV_PRIVATE 0.103.5 + cli_js_parse_done@CLAMAV_PRIVATE 0.103.5 + cli_js_process_buffer@CLAMAV_PRIVATE 0.103.5 + cli_ldbtokenize@CLAMAV_PRIVATE 0.103.5 + cli_malloc@CLAMAV_PRIVATE 0.103.5 + cli_memstr@CLAMAV_PRIVATE 0.103.5 + cli_ole2_extract@CLAMAV_PRIVATE 0.103.5 + cli_parse_add@CLAMAV_PRIVATE 0.103.5 + cli_pcre_build@CLAMAV_PRIVATE 0.103.5 + cli_pcre_freeoff@CLAMAV_PRIVATE 0.103.5 + cli_pcre_init@CLAMAV_PRIVATE 0.103.5 + cli_pcre_perf_events_destroy@CLAMAV_PRIVATE 0.103.5 + cli_pcre_perf_print@CLAMAV_PRIVATE 0.103.5 + cli_pcre_recaloff@CLAMAV_PRIVATE 0.103.5 + cli_pcre_scanbuf@CLAMAV_PRIVATE 0.103.5 + cli_ppt_vba_read@CLAMAV_PRIVATE 0.103.5 + cli_printcxxver@CLAMAV_PRIVATE 0.103.5 + cli_readn@CLAMAV_PRIVATE 0.103.5 + cli_realloc@CLAMAV_PRIVATE 0.103.5 + cli_realpath@CLAMAV_PRIVATE 0.103.5 + cli_regcomp@CLAMAV_PRIVATE 0.103.5 + cli_regex2suffix@CLAMAV_PRIVATE 0.103.5 + cli_regexec@CLAMAV_PRIVATE 0.103.5 + cli_regfree@CLAMAV_PRIVATE 0.103.5 + cli_rmdirs@CLAMAV_PRIVATE 0.103.5 + cli_rndnum@CLAMAV_PRIVATE 0.103.5 + cli_sanitize_filepath@CLAMAV_PRIVATE 0.103.5 + cli_scan_buff@CLAMAV_PRIVATE 0.103.5 + cli_scan_fmap@CLAMAV_PRIVATE 0.103.5 + cli_sigopts_handler@CLAMAV_PRIVATE 0.103.5 + cli_sigperf_events_destroy@CLAMAV_PRIVATE 0.103.5 + cli_sigperf_print@CLAMAV_PRIVATE 0.103.5 + cli_str2hex@CLAMAV_PRIVATE 0.103.5 + cli_strbcasestr@CLAMAV_PRIVATE 0.103.5 + cli_strdup@CLAMAV_PRIVATE 0.103.5 + cli_strerror@CLAMAV_PRIVATE 0.103.5 + cli_strlcat@CLAMAV_PRIVATE 0.103.5 + cli_strlcpy@CLAMAV_PRIVATE 0.103.5 + cli_strntoul@CLAMAV_PRIVATE 0.103.5 + cli_strrcpy@CLAMAV_PRIVATE 0.103.5 + cli_strtok@CLAMAV_PRIVATE 0.103.5 + cli_strtokbuf@CLAMAV_PRIVATE 0.103.5 + cli_strtokenize@CLAMAV_PRIVATE 0.103.5 + cli_textbuffer_append_normalize@CLAMAV_PRIVATE 0.103.5 + cli_unescape@CLAMAV_PRIVATE 0.103.5 + cli_unlink@CLAMAV_PRIVATE 0.103.5 + cli_url_canon@CLAMAV_PRIVATE 0.103.5 + cli_utf16_to_utf8@CLAMAV_PRIVATE 0.103.5 + cli_utf16toascii@CLAMAV_PRIVATE 0.103.5 + cli_vba_inflate@CLAMAV_PRIVATE 0.103.5 + cli_vba_readdir@CLAMAV_PRIVATE 0.103.5 + cli_versig2@CLAMAV_PRIVATE 0.103.5 + cli_versig@CLAMAV_PRIVATE 0.103.5 + cli_warnmsg@CLAMAV_PRIVATE 0.103.5 + cli_wm_decrypt_macro@CLAMAV_PRIVATE 0.103.5 + cli_wm_readdir@CLAMAV_PRIVATE 0.103.5 + cli_writen@CLAMAV_PRIVATE 0.103.5 + decodeLine@CLAMAV_PRIVATE 0.103.5 + disasmbuf@CLAMAV_PRIVATE 0.103.5 + fmap@CLAMAV_PRIVATE 0.103.5 + fmap_dump_to_file@CLAMAV_PRIVATE 0.103.5 + fmap_duplicate@CLAMAV_PRIVATE 0.103.5 + free_duplicate_fmap@CLAMAV_PRIVATE 0.103.5 + get_fpu_endian@CLAMAV_PRIVATE 0.103.5 + have_clamjit@CLAMAV_PRIVATE 0.103.5 + have_rar@CLAMAV_PRIVATE 0.103.5 + html_normalise_map@CLAMAV_PRIVATE 0.103.5 + html_normalise_mem@CLAMAV_PRIVATE 0.103.5 + html_screnc_decode@CLAMAV_PRIVATE 0.103.5 + html_tag_arg_free@CLAMAV_PRIVATE 0.103.5 + init_domainlist@CLAMAV_PRIVATE 0.103.5 + init_regex_list@CLAMAV_PRIVATE 0.103.5 + init_whitelist@CLAMAV_PRIVATE 0.103.5 + is_regex_ok@CLAMAV_PRIVATE 0.103.5 + load_regex_matcher@CLAMAV_PRIVATE 0.103.5 lsig_sub_matched@CLAMAV_PUBLIC 0.101.0 - messageCreate@CLAMAV_PRIVATE 0.103.2 - messageDestroy@CLAMAV_PRIVATE 0.103.2 - mpool_calloc@CLAMAV_PRIVATE 0.103.2 - mpool_create@CLAMAV_PRIVATE 0.103.2 - mpool_destroy@CLAMAV_PRIVATE 0.103.2 - mpool_free@CLAMAV_PRIVATE 0.103.2 - mpool_getstats@CLAMAV_PRIVATE 0.103.2 - phishingScan@CLAMAV_PRIVATE 0.103.2 - phishing_done@CLAMAV_PRIVATE 0.103.2 - phishing_init@CLAMAV_PRIVATE 0.103.2 - regex_list_add_pattern@CLAMAV_PRIVATE 0.103.2 - regex_list_done@CLAMAV_PRIVATE 0.103.2 - regex_list_match@CLAMAV_PRIVATE 0.103.2 - tableCreate@CLAMAV_PRIVATE 0.103.2 - tableDestroy@CLAMAV_PRIVATE 0.103.2 - tableFind@CLAMAV_PRIVATE 0.103.2 - tableInsert@CLAMAV_PRIVATE 0.103.2 - tableIterate@CLAMAV_PRIVATE 0.103.2 - tableRemove@CLAMAV_PRIVATE 0.103.2 - tableUpdate@CLAMAV_PRIVATE 0.103.2 - text_normalize_init@CLAMAV_PRIVATE 0.103.2 - text_normalize_map@CLAMAV_PRIVATE 0.103.2 - text_normalize_reset@CLAMAV_PRIVATE 0.103.2 - uniq_add@CLAMAV_PRIVATE 0.103.2 - uniq_free@CLAMAV_PRIVATE 0.103.2 - uniq_get@CLAMAV_PRIVATE 0.103.2 - uniq_init@CLAMAV_PRIVATE 0.103.2 + messageCreate@CLAMAV_PRIVATE 0.103.5 + messageDestroy@CLAMAV_PRIVATE 0.103.5 + mpool_calloc@CLAMAV_PRIVATE 0.103.5 + mpool_create@CLAMAV_PRIVATE 0.103.5 + mpool_destroy@CLAMAV_PRIVATE 0.103.5 + mpool_free@CLAMAV_PRIVATE 0.103.5 + mpool_getstats@CLAMAV_PRIVATE 0.103.5 + phishingScan@CLAMAV_PRIVATE 0.103.5 + phishing_done@CLAMAV_PRIVATE 0.103.5 + phishing_init@CLAMAV_PRIVATE 0.103.5 + regex_list_add_pattern@CLAMAV_PRIVATE 0.103.5 + regex_list_done@CLAMAV_PRIVATE 0.103.5 + regex_list_match@CLAMAV_PRIVATE 0.103.5 + tableCreate@CLAMAV_PRIVATE 0.103.5 + tableDestroy@CLAMAV_PRIVATE 0.103.5 + tableFind@CLAMAV_PRIVATE 0.103.5 + tableInsert@CLAMAV_PRIVATE 0.103.5 + tableIterate@CLAMAV_PRIVATE 0.103.5 + tableRemove@CLAMAV_PRIVATE 0.103.5 + tableUpdate@CLAMAV_PRIVATE 0.103.5 + text_normalize_init@CLAMAV_PRIVATE 0.103.5 + text_normalize_map@CLAMAV_PRIVATE 0.103.5 + text_normalize_reset@CLAMAV_PRIVATE 0.103.5 + uniq_add@CLAMAV_PRIVATE 0.103.5 + uniq_free@CLAMAV_PRIVATE 0.103.5 + uniq_get@CLAMAV_PRIVATE 0.103.5 + uniq_init@CLAMAV_PRIVATE 0.103.5 libfreshclam.so.2 libclamav9 #MINVER# FRESHCLAM_PRIVATE@FRESHCLAM_PRIVATE 0.103.0 FRESHCLAM_PUBLIC@FRESHCLAM_PUBLIC 0.102.1 diff -Nru clamav-0.103.2+dfsg/debian/patches/0001-Change-paths-in-sample-conf-file-to-match-Debian.patch clamav-0.103.5+dfsg/debian/patches/0001-Change-paths-in-sample-conf-file-to-match-Debian.patch --- clamav-0.103.2+dfsg/debian/patches/0001-Change-paths-in-sample-conf-file-to-match-Debian.patch 2021-04-12 18:51:20.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/patches/0001-Change-paths-in-sample-conf-file-to-match-Debian.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -From a2ae02924bf2fc91d949cd59742ca01a25893816 Mon Sep 17 00:00:00 2001 -From: Scott Kitterman -Date: Mon, 10 Mar 2014 19:20:18 -0400 -Subject: Change paths in sample conf file to match Debian - ---- - etc/clamav-milter.conf.sample | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/etc/clamav-milter.conf.sample b/etc/clamav-milter.conf.sample -index bf46b4f..b1b533b 100644 ---- a/etc/clamav-milter.conf.sample -+++ b/etc/clamav-milter.conf.sample -@@ -64,7 +64,7 @@ Example - # also owned by root to keep other users from tampering with it. - # - # Default: disabled --#PidFile /var/run/clamav-milter.pid -+#PidFile /var/run/clamav/clamav-milter.pid - - # Optional path to the global temporary directory. - # Default: system specific (usually /tmp or /var/tmp). -@@ -91,7 +91,7 @@ Example - # fashion. - # - # Default: no default --#ClamdSocket tcp:scanner.mydomain:7357 -+ClamdSocket /var/run/clamav/clamd - - - ## -@@ -214,7 +214,7 @@ Example - # A full path is required. - # - # Default: disabled --#LogFile /tmp/clamav-milter.log -+#LogFile /var/log/clamav/clamav-milter.log - - # By default the log file is locked for writing - the lock protects against - # running clamav-milter multiple times. diff -Nru clamav-0.103.2+dfsg/debian/patches/0002-add-support-for-system-tomsfastmath.patch clamav-0.103.5+dfsg/debian/patches/0002-add-support-for-system-tomsfastmath.patch --- clamav-0.103.2+dfsg/debian/patches/0002-add-support-for-system-tomsfastmath.patch 2021-04-12 18:51:20.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/patches/0002-add-support-for-system-tomsfastmath.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,108 +0,0 @@ -From d49c808e8d7d7b76e23fcbb20b8da85aef17aea9 Mon Sep 17 00:00:00 2001 -From: Andreas Cadhalpun -Date: Wed, 11 Mar 2015 20:03:15 +0100 -Subject: add support for system tomsfastmath - ---- - configure.ac | 2 ++ - libclamav/Makefile.am | 6 ++++++ - libclamav/bignum.h | 6 +++++- - libclamav/xdp.c | 2 +- - m4/reorganization/libs/tomsfastmath.m4 | 12 ++++++++++++ - 5 files changed, 26 insertions(+), 2 deletions(-) - create mode 100644 m4/reorganization/libs/tomsfastmath.m4 - -diff --git a/configure.ac b/configure.ac -index 6da01a3..1e46398 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -98,6 +98,7 @@ m4_include([m4/reorganization/libs/libmspack.m4]) - if test "x$use_internal_mspack" = "xno"; then - mspack_msg="External, $LIBMSPACK_CFLAGS $LIBMSPACK_LIBS" - fi -+m4_include([m4/reorganization/libs/tomsfastmath.m4]) - - AM_MAINTAINER_MODE - m4_include([m4/reorganization/libs/libz.m4]) -@@ -362,6 +363,7 @@ fi - CL_MSG_STATUS([yara ],[$enable_yara],[$enable_yara]) - CL_MSG_STATUS([fts ],[yes],[$lfs_fts_msg]) - -+CL_MSG_STATUS([tomsfastmath],[yes],[$tomsfastmath_msg]) - - # Yep, downgrading the compiler avoids the bug too: - # 4.0.x, and 4.1.0 are the known buggy versions -diff --git a/libclamav/Makefile.am b/libclamav/Makefile.am -index 0174a92..5547972 100644 ---- a/libclamav/Makefile.am -+++ b/libclamav/Makefile.am -@@ -588,6 +588,7 @@ libclamav_la_SOURCES += yara_arena.c \ - yara_clam.h - endif - -+if !SYSTEM_TOMSFASTMATH - libclamav_la_SOURCES += bignum.h\ - bignum_fast.h\ - tomsfastmath/addsub/fp_add.c\ -@@ -672,6 +673,11 @@ libclamav_la_SOURCES += bignum.h\ - tomsfastmath/sqr/fp_sqr_comba_small_set.c\ - tomsfastmath/sqr/fp_sqrmod.c - -+else -+libclamav_la_CFLAGS += $(TOMSFASTMATH_CFLAGS) -+libclamav_la_LIBADD += $(TOMSFASTMATH_LIBS) -+endif -+ - .PHONY2: version.h.tmp - version.c: version.h - version.h: version.h.tmp -diff --git a/libclamav/bignum.h b/libclamav/bignum.h -index 8fdc956..56dfa95 100644 ---- a/libclamav/bignum.h -+++ b/libclamav/bignum.h -@@ -1,9 +1,13 @@ - #ifndef BIGNUM_H_ - #define BIGNUM_H_ - -+#if HAVE_SYSTEM_TOMSFASTMATH -+#include -+#else - #define TFM_CHECK -- - #include "bignum_fast.h" -+#endif -+ - typedef fp_int mp_int; - #define mp_cmp fp_cmp - #define mp_toradix_n(a, b, c, d) fp_toradix_n(a, b, c, d) -diff --git a/libclamav/xdp.c b/libclamav/xdp.c -index 8742342..6370221 100644 ---- a/libclamav/xdp.c -+++ b/libclamav/xdp.c -@@ -52,7 +52,7 @@ - #include "scanners.h" - #include "conv.h" - #include "xdp.h" --#include "bignum_fast.h" -+#include "bignum.h" - #include "filetypes.h" - - static char *dump_xdp(cli_ctx *ctx, const char *start, size_t sz); -diff --git a/m4/reorganization/libs/tomsfastmath.m4 b/m4/reorganization/libs/tomsfastmath.m4 -new file mode 100644 -index 0000000..2a821a1 ---- /dev/null -+++ b/m4/reorganization/libs/tomsfastmath.m4 -@@ -0,0 +1,12 @@ -+dnl Check for system tomsfastmath -+PKG_CHECK_MODULES([TOMSFASTMATH], [tomsfastmath], [have_system_tomsfastmath=yes], [have_system_tomsfastmath=no]) -+ -+AM_CONDITIONAL([SYSTEM_TOMSFASTMATH], [test "x$have_system_tomsfastmath" = "xyes"]) -+ -+if test "x$have_system_tomsfastmath" = "xyes"; then -+ AC_DEFINE([HAVE_SYSTEM_TOMSFASTMATH], [1], [link against system-wide tomsfastmath library]) -+ tomsfastmath_msg="External, $TOMSFASTMATH_CFLAGS $TOMSFASTMATH_LIBS" -+else -+ AC_DEFINE([HAVE_SYSTEM_TOMSFASTMATH], [0], [don't link against system-wide tomsfastmath library]) -+ tomsfastmath_msg="Internal" -+fi diff -Nru clamav-0.103.2+dfsg/debian/patches/0003-clamd-don-t-depend-on-clamav-demon.socket.patch clamav-0.103.5+dfsg/debian/patches/0003-clamd-don-t-depend-on-clamav-demon.socket.patch --- clamav-0.103.2+dfsg/debian/patches/0003-clamd-don-t-depend-on-clamav-demon.socket.patch 2021-04-12 18:51:20.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/patches/0003-clamd-don-t-depend-on-clamav-demon.socket.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -From 85f632861ea91755a7cc98fc4848183ac9c0ec14 Mon Sep 17 00:00:00 2001 -From: Sebastian Andrzej Siewior -Date: Thu, 11 Aug 2016 21:54:10 +0200 -Subject: clamd: don't depend on clamav-demon.socket - -Let's try to live without it. -This should avoid the endless loop in #824042. Newer systemd have -rate-limiting on (re)starts. This rate-limiting would stop the socket -service. The only purpose for the socket activation is to get clamd -started after the initial freshclam run on installs so I think we can -live without and manually start the daemon after installation. - -Signed-off-by: Sebastian Andrzej Siewior ---- - clamd/clamav-daemon.service.in | 2 -- - 1 file changed, 2 deletions(-) - -diff --git a/clamd/clamav-daemon.service.in b/clamd/clamav-daemon.service.in -index 9a262c3..c13d72f 100644 ---- a/clamd/clamav-daemon.service.in -+++ b/clamd/clamav-daemon.service.in -@@ -1,7 +1,6 @@ - [Unit] - Description=Clam AntiVirus userspace daemon - Documentation=man:clamd(8) man:clamd.conf(5) https://www.clamav.net/documents/ --Requires=clamav-daemon.socket - # Check for database existence - ConditionPathExistsGlob=@DBDIR@/main.{c[vl]d,inc} - ConditionPathExistsGlob=@DBDIR@/daily.{c[vl]d,inc} -@@ -15,4 +14,3 @@ TimeoutStartSec=420 - - [Install] - WantedBy=multi-user.target --Also=clamav-daemon.socket diff -Nru clamav-0.103.2+dfsg/debian/patches/0004-Add-support-for-LLVM-3.7.patch clamav-0.103.5+dfsg/debian/patches/0004-Add-support-for-LLVM-3.7.patch --- clamav-0.103.2+dfsg/debian/patches/0004-Add-support-for-LLVM-3.7.patch 2021-04-12 18:51:20.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/patches/0004-Add-support-for-LLVM-3.7.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,738 +0,0 @@ -From f80c73ea6ca910ff54190329e9037688774f5ad1 Mon Sep 17 00:00:00 2001 -From: Andreas Cadhalpun -Date: Fri, 14 Oct 2016 20:24:39 +0200 -Subject: Add support for LLVM 3.7 - -Main changes: -The DataLayoutPass is no longer necessary. -The LoopInfo pass is now a WrapperPass. -Before creating TargetLibraryInfo one needs to create a -TargetLibraryInfoImpl. -PassManager is now in the legacy:: namespace. -GetElementPtrInst::getIndexedType changed behavior causing segfaults in -the testsuite; emulating the old behavior now. -CreateCallX functions for fixed number X of elements got removed. -JITEmitDebugInfo Option was removed. -DIDescriptor was removed. ---- - libclamav/c++/ClamBCRTChecks.cpp | 34 +++++- - libclamav/c++/PointerTracking.cpp | 44 +++++++- - libclamav/c++/bytecode2llvm.cpp | 181 ++++++++++++++++++++++++++++-- - libclamav/c++/configure.ac | 4 +- - 4 files changed, 244 insertions(+), 19 deletions(-) - -diff --git a/libclamav/c++/ClamBCRTChecks.cpp b/libclamav/c++/ClamBCRTChecks.cpp -index c8a853f..09657b9 100644 ---- a/libclamav/c++/ClamBCRTChecks.cpp -+++ b/libclamav/c++/ClamBCRTChecks.cpp -@@ -201,9 +201,11 @@ namespace llvm { - TD = &getAnalysis(); - #elif LLVM_VERSION < 35 - TD = &getAnalysis(); --#else -+#elif LLVM_VERSION < 37 - DataLayoutPass *DLP = getAnalysisIfAvailable(); - TD = DLP ? &DLP->getDataLayout() : 0; -+#else -+ TD = &F.getEntryBlock().getModule()->getDataLayout(); - #endif - SE = &getAnalysis(); - PT = &getAnalysis(); -@@ -212,7 +214,11 @@ namespace llvm { - #else - DT = &getAnalysis().getDomTree(); - #endif -+#if LLVM_VERSION < 37 - expander = new SCEVExpander(*SE OPT("SCEVexpander")); -+#else -+ expander = new SCEVExpander(*SE, *TD OPT("SCEVexpander")); -+#endif - - std::vector insns; - -@@ -351,8 +357,10 @@ namespace llvm { - AU.addRequired(); - #elif LLVM_VERSION < 35 - AU.addRequired(); --#else -+#elif LLVM_VERSION < 37 - AU.addRequired(); -+#else -+ // No DataLayout pass needed anymore. - #endif - #if LLVM_VERSION < 35 - AU.addRequired(); -@@ -406,7 +414,11 @@ namespace llvm { - if (BaseMap.count(P)) { - return BaseMap[Ptr] = BaseMap[P]; - } -+#if LLVM_VERSION < 37 - Value *P2 = GetUnderlyingObject(P, TD); -+#else -+ Value *P2 = GetUnderlyingObject(P, *TD); -+#endif - if (P2 != P) { - Value *V = getPointerBase(P2); - return BaseMap[Ptr] = V; -@@ -520,7 +532,11 @@ namespace llvm { - } - } - if (LoadInst *LI = dyn_cast(Base)) { -+#if LLVM_VERSION < 37 - Value *V = GetUnderlyingObject(LI->getPointerOperand()->stripPointerCasts(), TD); -+#else -+ Value *V = GetUnderlyingObject(LI->getPointerOperand()->stripPointerCasts(), *TD); -+#endif - if (Argument *A = dyn_cast(V)) { - if (A->getArgNo() == 0) { - // pointers from hidden ctx are trusted to be at least the -@@ -674,7 +690,11 @@ namespace llvm { - } - BasicBlock *BB = I->getParent(); - BasicBlock::iterator It = I; -+#if LLVM_VERSION < 37 - BasicBlock *newBB = SplitBlock(BB, &*It, this); -+#else -+ BasicBlock *newBB = SplitBlock(BB, &*It); -+#endif - PHINode *PN; - unsigned MDDbgKind = I->getContext().getMDKindID("dbg"); - //verifyFunction(*BB->getParent()); -@@ -719,9 +739,15 @@ namespace llvm { - unsigned locationid = 0; - bool Approximate; - if (MDNode *Dbg = getLocation(I, Approximate, MDDbgKind)) { -+#if LLVM_VERSION < 37 - DILocation Loc(Dbg); - locationid = Loc.getLineNumber() << 8; - unsigned col = Loc.getColumnNumber(); -+#else -+ DebugLoc Loc(Dbg); -+ locationid = Loc.getLine() << 8; -+ unsigned col = Loc.getCol(); -+#endif - if (col > 254) - col = 254; - if (Approximate) -@@ -945,7 +971,11 @@ INITIALIZE_PASS_DEPENDENCY(TargetData) - #elif LLVM_VERSION < 35 - INITIALIZE_PASS_DEPENDENCY(DataLayout) - #else -+#if LLVM_VERSION < 37 - INITIALIZE_PASS_DEPENDENCY(DataLayoutPass) -+#else -+// No DataLayout pass needed anymore. -+#endif - #endif - #if LLVM_VERSION < 35 - INITIALIZE_PASS_DEPENDENCY(DominatorTree) -diff --git a/libclamav/c++/PointerTracking.cpp b/libclamav/c++/PointerTracking.cpp -index d5841a1..5567894 100644 ---- a/libclamav/c++/PointerTracking.cpp -+++ b/libclamav/c++/PointerTracking.cpp -@@ -30,7 +30,11 @@ - #include "llvm/IR/InstIterator.h" - #endif - #include "llvm/Support/raw_ostream.h" -+#if LLVM_VERSION < 37 - #include "llvm/Target/TargetLibraryInfo.h" -+#else -+#include -+#endif - - #if LLVM_VERSION < 32 - #include "llvm/Target/TargetData.h" -@@ -70,7 +74,11 @@ INITIALIZE_PASS_DEPENDENCY(DominatorTree) - #else - INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) - #endif -+#if LLVM_VERSION < 37 - INITIALIZE_PASS_DEPENDENCY(LoopInfo) -+#else -+INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) -+#endif - INITIALIZE_PASS_DEPENDENCY(ScalarEvolution) - #if LLVM_VERSION < 35 - INITIALIZE_PASS_DEPENDENCY(DominatorTree) -@@ -96,12 +104,18 @@ bool PointerTracking::runOnFunction(Function &F) { - TD = getAnalysisIfAvailable(); - #elif LLVM_VERSION < 35 - TD = getAnalysisIfAvailable(); --#else -+#elif LLVM_VERSION < 37 - DataLayoutPass *DLP = getAnalysisIfAvailable(); - TD = DLP ? &DLP->getDataLayout() : 0; -+#else -+ TD = &F.getEntryBlock().getModule()->getDataLayout(); - #endif - SE = &getAnalysis(); -+#if LLVM_VERSION < 37 - LI = &getAnalysis(); -+#else -+ LI = &getAnalysis().getLoopInfo(); -+#endif - #if LLVM_VERSION < 35 - DT = &getAnalysis(); - #else -@@ -116,7 +130,11 @@ void PointerTracking::getAnalysisUsage(AnalysisUsage &AU) const { - #else - AU.addRequiredTransitive(); - #endif -+#if LLVM_VERSION < 37 - AU.addRequiredTransitive(); -+#else -+ AU.addRequiredTransitive(); -+#endif - AU.addRequiredTransitive(); - AU.setPreservesAll(); - } -@@ -178,12 +196,19 @@ const SCEV *PointerTracking::computeAllocationCount(Value *P, - if (CallInst *CI = extractMallocCall(V)) { - Value *arraySize = getMallocArraySize(CI, TD); - constType* AllocTy = getMallocAllocatedType(CI); --#else -+#elif LLVM_VERSION < 37 - TargetLibraryInfo* TLI = new TargetLibraryInfo(); - - if (CallInst *CI = extractMallocCall(V, TLI)) { - Value *arraySize = getMallocArraySize(CI, TD, TLI); - constType* AllocTy = getMallocAllocatedType(CI, TLI); -+#else -+ TargetLibraryInfoImpl* TLII = new TargetLibraryInfoImpl(); -+ TargetLibraryInfo* TLI = new TargetLibraryInfo(*TLII); -+ -+ if (CallInst *CI = extractMallocCall(V, TLI)) { -+ Value *arraySize = getMallocArraySize(CI, *TD, TLI); -+ constType* AllocTy = getMallocAllocatedType(CI, TLI); - #endif - if (!AllocTy || !arraySize) return SE->getCouldNotCompute(); - Ty = AllocTy; -@@ -240,7 +265,7 @@ Value *PointerTracking::computeAllocationCountValue(Value *P, constType *&Ty) co - if (!Ty) - return 0; - Value *arraySize = getMallocArraySize(CI, TD); --#else -+#elif LLVM_VERSION < 37 - TargetLibraryInfo* TLI = new TargetLibraryInfo(); - - if (CallInst *CI = extractMallocCall(V, TLI)) { -@@ -248,6 +273,15 @@ Value *PointerTracking::computeAllocationCountValue(Value *P, constType *&Ty) co - if (!Ty) - return 0; - Value *arraySize = getMallocArraySize(CI, TD, TLI); -+#else -+ TargetLibraryInfoImpl* TLII = new TargetLibraryInfoImpl(); -+ TargetLibraryInfo* TLI = new TargetLibraryInfo(*TLII); -+ -+ if (CallInst *CI = extractMallocCall(V, TLI)) { -+ Ty = getMallocAllocatedType(CI, TLI); -+ if (!Ty) -+ return 0; -+ Value *arraySize = getMallocArraySize(CI, *TD, TLI); - #endif - if (!arraySize) { - Ty = Type::getInt8Ty(P->getContext()); -@@ -351,7 +385,11 @@ void PointerTracking::getPointerOffset(Value *Pointer, Value *&Base, - const SCEV *&Offset) const - { - Pointer = Pointer->stripPointerCasts(); -+#if LLVM_VERSION < 37 - Base = GetUnderlyingObject(Pointer, TD); -+#else -+ Base = GetUnderlyingObject(Pointer, *TD); -+#endif - Limit = getAllocationSizeInBytes(Base); - if (isa(Limit)) { - Base = 0; -diff --git a/libclamav/c++/bytecode2llvm.cpp b/libclamav/c++/bytecode2llvm.cpp -index 6f1181a..a0bab63 100644 ---- a/libclamav/c++/bytecode2llvm.cpp -+++ b/libclamav/c++/bytecode2llvm.cpp -@@ -64,7 +64,11 @@ - #include "llvm/Object/ObjectFile.h" - #endif - #include "llvm/ExecutionEngine/JITEventListener.h" -+#if LLVM_VERSION < 37 - #include "llvm/PassManager.h" -+#else -+#include "llvm/IR/LegacyPassManager.h" -+#endif - #include "llvm/Support/Compiler.h" - #include "llvm/Support/Debug.h" - #include "llvm/Support/CommandLine.h" -@@ -232,7 +236,9 @@ namespace { - #define llvm_report_error(x) report_fatal_error(x) - #define llvm_install_error_handler(x) install_fatal_error_handler(x) - #define DwarfExceptionHandling JITExceptionHandling -+#if LLVM_VERSION < 37 - #define SetCurrentDebugLocation(x) SetCurrentDebugLocation(DebugLoc::getFromDILocation(x)) -+#endif - #define DEFINEPASS(passname) passname() : FunctionPass(ID) - #else - #define DEFINEPASS(passname) passname() : FunctionPass(&ID) -@@ -719,7 +725,11 @@ class RuntimeLimits : public FunctionPass { - BBMap[BB] = apicalls; - } - if (!BackedgeTargets.empty()) { -+#if LLVM_VERSION < 37 - LoopInfo &LI = getAnalysis(); -+#else -+ LoopInfo &LI = getAnalysis().getLoopInfo(); -+#endif - ScalarEvolution &SE = getAnalysis(); - - // Now check whether any of these backedge targets are part of a loop -@@ -803,7 +813,11 @@ class RuntimeLimits : public FunctionPass { - #endif - // Load Flag that tells us we timed out (first byte in bc_ctx) - Value *Cond = Builder.CreateLoad(Flag, true); -+#if LLVM_VERSION < 37 - BasicBlock *newBB = SplitBlock(BB, BB->getTerminator(), this); -+#else -+ BasicBlock *newBB = SplitBlock(BB, BB->getTerminator()); -+#endif - TerminatorInst *TI = BB->getTerminator(); - BranchInst::Create(AbrtBB, newBB, Cond, TI); - TI->eraseFromParent(); -@@ -824,7 +838,11 @@ class RuntimeLimits : public FunctionPass { - - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.setPreservesAll(); -+#if LLVM_VERSION < 37 - AU.addRequired(); -+#else -+ AU.addRequired(); -+#endif - AU.addRequired(); - #if LLVM_VERSION < 35 - AU.addRequired(); -@@ -917,7 +935,11 @@ class LLVMCodegen { - Module *M; - LLVMContext &Context; - ExecutionEngine *EE; -+#if LLVM_VERSION < 37 - FunctionPassManager &PM, &PMUnsigned; -+#else -+ legacy::FunctionPassManager &PM, &PMUnsigned; -+#endif - LLVMTypeMapper *TypeMap; - - Function **apiFuncs; -@@ -1090,7 +1112,11 @@ class LLVMCodegen { - Constant *C = ConstantExpr::getPointerCast(GV, IP8Ty); - //TODO: check constant bounds here - return ConstantExpr::getPointerCast( -+#if LLVM_VERSION < 37 - ConstantExpr::getInBoundsGetElementPtr(C, ARRAYREF(Value*, idxs, 1)), -+#else -+ ConstantExpr::getInBoundsGetElementPtr(Ty, C, ARRAYREF(Value*, idxs, 1)), -+#endif - PTy); - } - if (isa(Ty)) { -@@ -1119,15 +1145,21 @@ class LLVMCodegen { - - public: - LLVMCodegen(const struct cli_bc *bc, Module *M, struct CommonFunctions *CF, FunctionMapTy &cFuncs, -+#if LLVM_VERSION < 37 - ExecutionEngine *EE, FunctionPassManager &PM, FunctionPassManager &PMUnsigned, -+#else -+ ExecutionEngine *EE, legacy::FunctionPassManager &PM, legacy::FunctionPassManager &PMUnsigned, -+#endif - Function **apiFuncs, LLVMTypeMapper &apiMap) - : bc(bc), M(M), Context(M->getContext()), EE(EE), - PM(PM),PMUnsigned(PMUnsigned), TypeMap(), apiFuncs(apiFuncs),apiMap(apiMap), - compiledFunctions(cFuncs), BytecodeID("bc"+Twine(bc->id)), - #if LLVM_VERSION < 32 - Folder(EE->getTargetData()), Builder(Context, Folder), Values(), CF(CF) { --#else -+#elif LLVM_VERSION < 37 - Folder(EE->getDataLayout()), Builder(Context, Folder), Values(), CF(CF) { -+#else -+ Folder(*EE->getDataLayout()), Builder(Context, Folder), Values(), CF(CF) { - #endif - - for (unsigned i=0;i - #endif - Value* createGEP(Value *Base, constType *ETy, ARRAYREFPARAM(Value*,InputIterator Start,InputIterator End,ARef)) { -+#if LLVM_VERSION < 37 - constType *Ty = GetElementPtrInst::getIndexedType(Base->getType(),ARRAYREFP(Start,End,ARef)); -+#else -+ Type *Ty = NULL; -+ // This used to be done internally in LLVM's getIndexedTypeInternal. -+ PointerType *PTy = dyn_cast(Base->getType()->getScalarType()); -+ if (PTy) { -+ Type *Agg = PTy->getElementType(); -+ Ty = GetElementPtrInst::getIndexedType(Agg,ARRAYREFP(Start,End,ARef)); -+ } -+#endif - if (!Ty || (ETy && (Ty != ETy && (!isa(Ty) || !isa(ETy))))) { - if (cli_debug_flag) { - std::string str; -@@ -1458,7 +1500,11 @@ class LLVMCodegen { - if (func->dbgnodes[c] != ~0u) { - unsigned j = func->dbgnodes[c]; - assert(j < mdnodes.size()); -+#if LLVM_VERSION < 37 - Builder.SetCurrentDebugLocation(mdnodes[j]); -+#else -+ Builder.SetCurrentDebugLocation(DebugLoc(mdnodes[j])); -+#endif - } else - Builder.SetCurrentDebugLocation(0); - } -@@ -1768,11 +1814,16 @@ class LLVMCodegen { - #if LLVM_VERSION < 29 - CallInst *c = Builder.CreateCall4(CF->FMemset, Dst, Val, Len, - ConstantInt::get(Type::getInt32Ty(Context), 1)); --#else -+#elif LLVM_VERSION < 37 - CallInst *c = Builder.CreateCall5(CF->FMemset, Dst, Val, Len, - ConstantInt::get(Type::getInt32Ty(Context), 1), - ConstantInt::get(Type::getInt1Ty(Context), 0) - ); -+#else -+ Value *args[] = { Dst, Val, Len, -+ ConstantInt::get(Type::getInt32Ty(Context), 1), -+ ConstantInt::get(Type::getInt1Ty(Context), 0)}; -+ CallInst *c = Builder.CreateCall(CF->FMemset, ARRAYREF(Value*, args, args + 5)); - #endif - c->setTailCall(true); - c->setDoesNotThrow(); -@@ -1789,11 +1840,16 @@ class LLVMCodegen { - #if LLVM_VERSION < 29 - CallInst *c = Builder.CreateCall4(CF->FMemcpy, Dst, Src, Len, - ConstantInt::get(Type::getInt32Ty(Context), 1)); --#else -+#elif LLVM_VERSION < 37 - CallInst *c = Builder.CreateCall5(CF->FMemcpy, Dst, Src, Len, - ConstantInt::get(Type::getInt32Ty(Context), 1), - ConstantInt::get(Type::getInt1Ty(Context), 0) - ); -+#else -+ Value *args[] = { Dst, Src, Len, -+ ConstantInt::get(Type::getInt32Ty(Context), 1), -+ ConstantInt::get(Type::getInt1Ty(Context), 0)}; -+ CallInst *c = Builder.CreateCall(CF->FMemcpy, ARRAYREF(Value*, args, args + 5)); - #endif - c->setTailCall(true); - c->setDoesNotThrow(); -@@ -1810,10 +1866,15 @@ class LLVMCodegen { - #if LLVM_VERSION < 29 - CallInst *c = Builder.CreateCall4(CF->FMemmove, Dst, Src, Len, - ConstantInt::get(Type::getInt32Ty(Context), 1)); --#else -+#elif LLVM_VERSION < 37 - CallInst *c = Builder.CreateCall5(CF->FMemmove, Dst, Src, Len, - ConstantInt::get(Type::getInt32Ty(Context), 1), - ConstantInt::get(Type::getInt1Ty(Context), 0)); -+#else -+ Value *args[] = {Dst, Src, Len, -+ ConstantInt::get(Type::getInt32Ty(Context), 1), -+ ConstantInt::get(Type::getInt1Ty(Context), 0)}; -+ CallInst *c = Builder.CreateCall(CF->FMemmove, args); - #endif - c->setTailCall(true); - c->setDoesNotThrow(); -@@ -1831,7 +1892,12 @@ class LLVMCodegen { - #else - Value *Len = convertOperand(func, EE->getDataLayout()->getIntPtrType(Context), inst->u.three[2]); - #endif -+#if LLVM_VERSION < 37 - CallInst *c = Builder.CreateCall3(CF->FRealmemcmp, Dst, Src, Len); -+#else -+ Value *args[] = {Dst, Src, Len}; -+ CallInst *c = Builder.CreateCall(CF->FRealmemcmp, ARRAYREF(Value*, args, args + 3)); -+#endif - c->setTailCall(true); - c->setDoesNotThrow(); - Store(inst->dest, c); -@@ -2212,7 +2278,11 @@ static void addFunctionProtos(struct CommonFunctions *CF, ExecutionEngine *EE, M - } - #if LLVM_VERSION >= 29 - INITIALIZE_PASS_BEGIN(RuntimeLimits, "rl", "Runtime Limits", false, false) -+#if LLVM_VERSION < 37 - INITIALIZE_PASS_DEPENDENCY(LoopInfo) -+#else -+INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) -+#endif - INITIALIZE_PASS_DEPENDENCY(ScalarEvolution) - #if LLVM_VERSION < 35 - INITIALIZE_PASS_DEPENDENCY(DominatorTree) -@@ -2438,8 +2508,10 @@ static void addFPasses(FunctionPassManager &FPM, bool trusted, const TargetData - static void addFPasses(FunctionPassManager &FPM, bool trusted, const DataLayout *TD) - #elif LLVM_VERSION < 36 - static void addFPasses(FunctionPassManager &FPM, bool trusted, const Module *M) --#else -+#elif LLVM_VERSION < 37 - static void addFPasses(FunctionPassManager &FPM, bool trusted, Module *M) -+#else -+static void addFPasses(legacy::FunctionPassManager &FPM, bool trusted, Module *M) - #endif - { - // Set up the optimizer pipeline. Start with registering info about how -@@ -2450,10 +2522,12 @@ static void addFPasses(FunctionPassManager &FPM, bool trusted, Module *M) - FPM.add(new DataLayout(*TD)); - #elif LLVM_VERSION < 36 - FPM.add(new DataLayoutPass(M)); --#else -+#elif LLVM_VERSION < 37 - DataLayoutPass *DLP = new DataLayoutPass(); - DLP->doInitialization(*M); - FPM.add(DLP); -+#else -+ // No DataLayout pass needed anymore. - #endif - // Promote allocas to registers. - FPM.add(createPromoteMemoryToRegisterPass()); -@@ -2483,6 +2557,8 @@ int cli_bytecode_prepare_jit(struct cli_all_bc *bcs) - - #if LLVM_VERSION >= 31 - TargetOptions Options; -+#if LLVM_VERSION < 37 -+ // This option was removed. - #ifdef CL_DEBUG - //disable this for now, it leaks - Options.JITEmitDebugInfo = false; -@@ -2490,6 +2566,7 @@ int cli_bytecode_prepare_jit(struct cli_all_bc *bcs) - #else - Options.JITEmitDebugInfo = false; - #endif -+#endif - #if LLVM_VERSION < 34 - Options.DwarfExceptionHandling = false; - #else -@@ -2526,7 +2603,11 @@ int cli_bytecode_prepare_jit(struct cli_all_bc *bcs) - struct CommonFunctions CF; - addFunctionProtos(&CF, EE, M); - -+#if LLVM_VERSION < 37 - FunctionPassManager OurFPM(M), OurFPMUnsigned(M); -+#else -+ legacy::FunctionPassManager OurFPM(M), OurFPMUnsigned(M); -+#endif - #if LLVM_VERSION < 32 - M->setDataLayout(EE->getTargetData()->getStringRepresentation()); - #else -@@ -2666,17 +2747,23 @@ int cli_bytecode_prepare_jit(struct cli_all_bc *bcs) - break; - } - } -+#if LLVM_VERSION < 37 - PassManager PM; -+#else -+ legacy::PassManager PM; -+#endif - #if LLVM_VERSION < 32 - PM.add(new TargetData(*EE->getTargetData())); - #elif LLVM_VERSION < 35 - PM.add(new DataLayout(*EE->getDataLayout())); - #elif LLVM_VERSION < 36 - PM.add(new DataLayoutPass(M)); --#else -+#elif LLVM_VERSION < 37 - DataLayoutPass *DLP = new DataLayoutPass(); - DLP->doInitialization(*M); - PM.add(DLP); -+#else -+ // No DataLayout pass needed anymore. - #endif - // TODO: only run this on the untrusted bytecodes, not all of them... - if (has_untrusted) -@@ -2988,11 +3075,19 @@ static Metadata *findDbgGlobalDeclare(GlobalVariable *V) { - return 0; - - for (unsigned i = 0, e = NMD->getNumOperands(); i != e; ++i) { -+#if LLVM_VERSION < 37 - DIDescriptor DIG(cast(NMD->getOperand(i))); - if (!DIG.isGlobalVariable()) - continue; - if (DIGlobalVariable(DIG).getGlobal() == V) - return DIG; -+#else -+ MDNode *DIG = NMD->getOperand(i); -+ if (!DIGlobalVariable::classof(DIG)) -+ continue; -+ if ((cast(DIG))->getVariable() == V) -+ return DIG; -+#endif - } - return 0; - } -@@ -3009,11 +3104,19 @@ static Metadata *findDbgSubprogramDeclare(Function *V) { - return 0; - - for (unsigned i = 0, e = NMD->getNumOperands(); i != e; ++i) { -+#if LLVM_VERSION < 37 - DIDescriptor DIG(cast(NMD->getOperand(i))); - if (!DIG.isSubprogram()) - continue; - if (DISubprogram(DIG).getFunction() == V) - return DIG; -+#else -+ MDNode *DIG = NMD->getOperand(i); -+ if (!DISubprogram::classof(DIG)) -+ continue; -+ if ((cast(DIG))->getFunction() == V) -+ return DIG; -+#endif - } - return 0; - } -@@ -3062,22 +3165,39 @@ static bool getLocationInfo(const Value *V, std::string &DisplayName, - Metadata *DIGV = findDbgGlobalDeclare(GV); - #endif - if (!DIGV) return false; -+#if LLVM_VERSION < 37 - DIGlobalVariable Var(cast(DIGV)); -+#else -+ DIGlobalVariable *Var = cast(DIGV); -+#endif - -+#if LLVM_VERSION < 37 - StringRef D = Var.getDisplayName(); -+#else -+ StringRef D = Var->getDisplayName(); -+#endif - if (!D.empty()) - DisplayName = D; -+#if LLVM_VERSION < 37 - LineNo = Var.getLineNumber(); -+#else -+ LineNo = Var->getLine(); -+#endif - #if LLVM_VERSION < 33 - Unit = Var.getCompileUnit(); --#else -+#elif LLVM_VERSION < 37 - G = Var.getFilename(); - H = Var.getDirectory(); -+#else -+ G = Var->getFilename(); -+ H = Var->getDirectory(); - #endif - #if LLVM_VERSION < 35 - TypeD = Var.getType(); --#else -+#elif LLVM_VERSION < 37 - T = Var.getType().getName(); -+#else -+ T = (cast(*Var->getType())).getName(); - #endif - } else if (Function *F = dyn_cast(const_cast(V))){ - #if LLVM_VERSION < 36 -@@ -3086,32 +3206,61 @@ static bool getLocationInfo(const Value *V, std::string &DisplayName, - Metadata *DIF = findDbgSubprogramDeclare(F); - #endif - if (!DIF) return false; -+#if LLVM_VERSION < 37 - DISubprogram Var(cast(DIF)); -+#else -+ DISubprogram *Var = cast(DIF); -+#endif - -+#if LLVM_VERSION < 37 - StringRef D = Var.getDisplayName(); -+#else -+ StringRef D = Var->getDisplayName(); -+#endif - if (!D.empty()) - DisplayName = D; -+#if LLVM_VERSION < 37 - LineNo = Var.getLineNumber(); -+#else -+ LineNo = Var->getLine(); -+#endif - #if LLVM_VERSION < 33 - Unit = Var.getCompileUnit(); --#else -+#elif LLVM_VERSION < 37 - G = Var.getFilename(); - H = Var.getDirectory(); -+#else -+ G = Var->getFilename(); -+ H = Var->getDirectory(); - #endif - #if LLVM_VERSION < 35 - TypeD = Var.getType(); --#else -+#elif LLVM_VERSION < 37 - T = Var.getType().getName(); -+#else -+ T = Var->getType()->getName(); - #endif - } else { - const DbgDeclareInst *DDI = findDbgDeclare(V); - if (!DDI) return false; -+#if LLVM_VERSION < 37 - DIVariable Var(cast(DDI->getVariable())); -+#else -+ DIVariable* Var = DDI->getVariable(); -+#endif - -+#if LLVM_VERSION < 37 - StringRef D = Var.getName(); -+#else -+ StringRef D = Var->getName(); -+#endif - if (!D.empty()) - DisplayName = D; -+#if LLVM_VERSION < 37 - LineNo = Var.getLineNumber(); -+#else -+ LineNo = Var->getLine(); -+#endif - #if LLVM_VERSION < 33 - Unit = Var.getCompileUnit(); - #else -@@ -3121,8 +3270,10 @@ static bool getLocationInfo(const Value *V, std::string &DisplayName, - #endif - #if LLVM_VERSION < 35 - TypeD = Var.getType(); --#else -+#elif LLVM_VERSION < 37 - T = Var.getType().getName(); -+#else -+ T = (cast(*Var->getType())).getName(); - #endif - } - -@@ -3158,9 +3309,15 @@ void printValue(llvm::Value *V, bool a, bool b) { - - void printLocation(llvm::Instruction *I, bool a, bool b) { - if (MDNode *N = I->getMetadata("dbg")) { -+#if LLVM_VERSION < 37 - DILocation Loc(N); - errs() << Loc.getFilename() << ":" << Loc.getLineNumber(); - if (unsigned Col = Loc.getColumnNumber()) { -+#else -+ DebugLoc Loc(N); -+ errs() << Loc.get()->getFilename() << ":" << Loc.getLine(); -+ if (unsigned Col = Loc.getCol()) { -+#endif - errs() << ":" << Col; - } - errs() << ": "; -diff --git a/libclamav/c++/configure.ac b/libclamav/c++/configure.ac -index 14e1ada..0bc8985 100644 ---- a/libclamav/c++/configure.ac -+++ b/libclamav/c++/configure.ac -@@ -90,14 +90,14 @@ elif test $llvmver_test -lt 290; then - elif test $llvmver_test -lt 360; then - llvmcomp="jit nativecodegen scalaropts ipo" - AC_MSG_RESULT([ok ($llvmver)]) --elif test $llvmver_test -lt 370; then -+elif test $llvmver_test -lt 380; then - dnl LLVM 3.6.0 removed jit, so we have to use mcjit - dnl and we're using InitializeNativeTargetAsmParser, so we need the architecture specific parsers - llvmcomp="mcjit nativecodegen scalaropts ipo x86asmparser powerpcasmparser" - AC_MSG_RESULT([ok ($llvmver)]) - else - AC_MSG_RESULT([no ($llvmver)]) -- AC_MSG_ERROR([LLVM < 3.7 required, but "$llvmver"($llvmver_test) found]) -+ AC_MSG_ERROR([LLVM < 3.8 required, but "$llvmver"($llvmver_test) found]) - fi - - dnl acquire the required flags to properly link in external LLVM diff -Nru clamav-0.103.2+dfsg/debian/patches/0005-Add-support-for-LLVM-3.8.patch clamav-0.103.5+dfsg/debian/patches/0005-Add-support-for-LLVM-3.8.patch --- clamav-0.103.2+dfsg/debian/patches/0005-Add-support-for-LLVM-3.8.patch 2021-04-12 18:51:20.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/patches/0005-Add-support-for-LLVM-3.8.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,455 +0,0 @@ -From 6f87c1e75ef32530b38923dcffb74b38a0ea17ef Mon Sep 17 00:00:00 2001 -From: Andreas Cadhalpun -Date: Fri, 14 Oct 2016 20:24:48 +0200 -Subject: Add support for LLVM 3.8 - -Main changes: -llvm/Config/config.h was removed. -The ScalarEvolution pass is now a WrapperPass. -Iterators are no longer automatically converted to and from pointers. -The GVNPass causes the test_bswap_jit test to fail; replaced with -ConstantPropagationPass. -LLVMIsMultithreaded is from the C API, while llvm_is_multithreaded is -the corresponding C++ API. ---- - libclamav/c++/ClamBCRTChecks.cpp | 50 ++++++++++++++++++++++++ - libclamav/c++/PointerTracking.cpp | 12 ++++++ - libclamav/c++/bytecode2llvm.cpp | 65 +++++++++++++++++++++++++------ - libclamav/c++/configure.ac | 4 +- - libclamav/c++/detect.cpp | 2 + - 5 files changed, 119 insertions(+), 14 deletions(-) - -diff --git a/libclamav/c++/ClamBCRTChecks.cpp b/libclamav/c++/ClamBCRTChecks.cpp -index 09657b9..695a32b 100644 ---- a/libclamav/c++/ClamBCRTChecks.cpp -+++ b/libclamav/c++/ClamBCRTChecks.cpp -@@ -54,7 +54,9 @@ - #include "llvm/Analysis/ScalarEvolution.h" - #include "llvm/Analysis/ScalarEvolutionExpressions.h" - #include "llvm/Analysis/ScalarEvolutionExpander.h" -+#if LLVM_VERSION < 38 - #include "llvm/Config/config.h" -+#endif - #include "llvm/Pass.h" - #include "llvm/Support/CommandLine.h" - #if LLVM_VERSION < 35 -@@ -207,7 +209,11 @@ namespace llvm { - #else - TD = &F.getEntryBlock().getModule()->getDataLayout(); - #endif -+#if LLVM_VERSION < 38 - SE = &getAnalysis(); -+#else -+ SE = &getAnalysis().getSE(); -+#endif - PT = &getAnalysis(); - #if LLVM_VERSION < 35 - DT = &getAnalysis(); -@@ -332,7 +338,11 @@ namespace llvm { - AbrtC->setDoesNotThrow(); - #endif - // remove all instructions from entry -+#if LLVM_VERSION < 38 - BasicBlock::iterator BBI = I, BBE=BB->end(); -+#else -+ BasicBlock::iterator BBI = I->getIterator(), BBE=BB->end(); -+#endif - while (BBI != BBE) { - if (!BBI->use_empty()) - BBI->replaceAllUsesWith(UndefValue::get(BBI->getType())); -@@ -367,7 +377,11 @@ namespace llvm { - #else - AU.addRequired(); - #endif -+#if LLVM_VERSION < 38 - AU.addRequired(); -+#else -+ AU.addRequired(); -+#endif - AU.addRequired(); - #if LLVM_VERSION < 35 - AU.addRequired(); -@@ -398,9 +412,17 @@ namespace llvm { - - Instruction *getInsertPoint(Value *V) - { -+#if LLVM_VERSION < 38 - BasicBlock::iterator It = EP; -+#else -+ BasicBlock::iterator It = EP->getIterator(); -+#endif - if (Instruction *I = dyn_cast(V)) { -+#if LLVM_VERSION < 38 - It = I; -+#else -+ It = I->getIterator(); -+#endif - ++It; - } - return &*It; -@@ -427,7 +449,11 @@ namespace llvm { - constType *P8Ty = - PointerType::getUnqual(Type::getInt8Ty(Ptr->getContext())); - if (PHINode *PN = dyn_cast(Ptr)) { -+#if LLVM_VERSION < 38 - BasicBlock::iterator It = PN; -+#else -+ BasicBlock::iterator It = PN->getIterator(); -+#endif - ++It; - PHINode *newPN = PHINode::Create(P8Ty, HINT(PN->getNumIncomingValues()) ".verif.base", &*It); - Changed = true; -@@ -441,7 +467,11 @@ namespace llvm { - return newPN; - } - if (SelectInst *SI = dyn_cast(Ptr)) { -+#if LLVM_VERSION < 38 - BasicBlock::iterator It = SI; -+#else -+ BasicBlock::iterator It = SI->getIterator(); -+#endif - ++It; - Value *TrueB = getPointerBase(SI->getTrueValue()); - Value *FalseB = getPointerBase(SI->getFalseValue()); -@@ -575,7 +605,11 @@ namespace llvm { - } - #endif - if (PHINode *PN = dyn_cast(Base)) { -+#if LLVM_VERSION < 38 - BasicBlock::iterator It = PN; -+#else -+ BasicBlock::iterator It = PN->getIterator(); -+#endif - ++It; - PHINode *newPN = PHINode::Create(I64Ty, HINT(PN->getNumIncomingValues()) ".verif.bounds", &*It); - Changed = true; -@@ -598,7 +632,11 @@ namespace llvm { - return BoundsMap[Base] = newPN; - } - if (SelectInst *SI = dyn_cast(Base)) { -+#if LLVM_VERSION < 38 - BasicBlock::iterator It = SI; -+#else -+ BasicBlock::iterator It = SI->getIterator(); -+#endif - ++It; - Value *TrueB = getPointerBounds(SI->getTrueValue()); - Value *FalseB = getPointerBounds(SI->getFalseValue()); -@@ -655,7 +693,11 @@ namespace llvm { - if (!MDDbgKind) - return 0; - Approximate = true; -+#if LLVM_VERSION < 38 - BasicBlock::iterator It = I; -+#else -+ BasicBlock::iterator It = I->getIterator(); -+#endif - while (It != I->getParent()->begin()) { - --It; - if (MDNode *Dbg = It->getMetadata(MDDbgKind)) -@@ -689,7 +731,11 @@ namespace llvm { - return false; - } - BasicBlock *BB = I->getParent(); -+#if LLVM_VERSION < 38 - BasicBlock::iterator It = I; -+#else -+ BasicBlock::iterator It = I->getIterator(); -+#endif - #if LLVM_VERSION < 37 - BasicBlock *newBB = SplitBlock(BB, &*It, this); - #else -@@ -982,7 +1028,11 @@ INITIALIZE_PASS_DEPENDENCY(DominatorTree) - #else - INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) - #endif -+#if LLVM_VERSION < 38 - INITIALIZE_PASS_DEPENDENCY(ScalarEvolution) -+#else -+INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass) -+#endif - #if LLVM_VERSION < 34 - INITIALIZE_AG_DEPENDENCY(CallGraph) - #elif LLVM_VERSION < 35 -diff --git a/libclamav/c++/PointerTracking.cpp b/libclamav/c++/PointerTracking.cpp -index 5567894..147ad48 100644 ---- a/libclamav/c++/PointerTracking.cpp -+++ b/libclamav/c++/PointerTracking.cpp -@@ -79,7 +79,11 @@ INITIALIZE_PASS_DEPENDENCY(LoopInfo) - #else - INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) - #endif -+#if LLVM_VERSION < 38 - INITIALIZE_PASS_DEPENDENCY(ScalarEvolution) -+#else -+INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass) -+#endif - #if LLVM_VERSION < 35 - INITIALIZE_PASS_DEPENDENCY(DominatorTree) - #else -@@ -110,7 +114,11 @@ bool PointerTracking::runOnFunction(Function &F) { - #else - TD = &F.getEntryBlock().getModule()->getDataLayout(); - #endif -+#if LLVM_VERSION < 38 - SE = &getAnalysis(); -+#else -+ SE = &getAnalysis().getSE(); -+#endif - #if LLVM_VERSION < 37 - LI = &getAnalysis(); - #else -@@ -135,7 +143,11 @@ void PointerTracking::getAnalysisUsage(AnalysisUsage &AU) const { - #else - AU.addRequiredTransitive(); - #endif -+#if LLVM_VERSION < 38 - AU.addRequiredTransitive(); -+#else -+ AU.addRequiredTransitive(); -+#endif - AU.setPreservesAll(); - } - -diff --git a/libclamav/c++/bytecode2llvm.cpp b/libclamav/c++/bytecode2llvm.cpp -index a0bab63..e7093cb 100644 ---- a/libclamav/c++/bytecode2llvm.cpp -+++ b/libclamav/c++/bytecode2llvm.cpp -@@ -171,7 +171,9 @@ void LLVMInitializePowerPCAsmPrinter(); - //#define TIMING - #undef TIMING - -+#if LLVM_VERSION < 38 - #include "llvm/Config/config.h" -+#endif - #ifdef ENABLE_THREADS - #if !ENABLE_THREADS - #error "Thread support was explicitly disabled. Cannot continue" -@@ -730,7 +732,11 @@ class RuntimeLimits : public FunctionPass { - #else - LoopInfo &LI = getAnalysis().getLoopInfo(); - #endif -+#if LLVM_VERSION < 38 - ScalarEvolution &SE = getAnalysis(); -+#else -+ ScalarEvolution &SE = getAnalysis().getSE(); -+#endif - - // Now check whether any of these backedge targets are part of a loop - // with a small constant trip count -@@ -784,7 +790,11 @@ class RuntimeLimits : public FunctionPass { - new UnreachableInst(F.getContext(), AbrtBB); - IRBuilder Builder(F.getContext()); - -+#if LLVM_VERSION < 38 - Value *Flag = F.arg_begin(); -+#else -+ Value *Flag = &*F.arg_begin(); -+#endif - #if LLVM_VERSION < 30 - Function *LSBarrier = Intrinsic::getDeclaration(F.getParent(), - Intrinsic::memory_barrier); -@@ -798,13 +808,21 @@ class RuntimeLimits : public FunctionPass { - #endif - verifyFunction(F); - BasicBlock *BB = &F.getEntryBlock(); -+#if LLVM_VERSION < 38 - Builder.SetInsertPoint(BB, BB->getTerminator()); -+#else -+ Builder.SetInsertPoint(BB, BB->getTerminator()->getIterator()); -+#endif - Flag = Builder.CreatePointerCast(Flag, PointerType::getUnqual( - Type::getInt1Ty(F.getContext()))); - for (BBSetTy::iterator I=needsTimeoutCheck.begin(), - E=needsTimeoutCheck.end(); I != E; ++I) { - BasicBlock *BB = *I; -+#if LLVM_VERSION < 38 - Builder.SetInsertPoint(BB, BB->getTerminator()); -+#else -+ Builder.SetInsertPoint(BB, BB->getTerminator()->getIterator()); -+#endif - #if LLVM_VERSION < 30 - // store-load barrier: will be a no-op on x86 but not other arches - Builder.CreateCall(LSBarrier, ARRAYREF(Value*, MBArgs, MBArgs+5)); -@@ -843,7 +861,11 @@ class RuntimeLimits : public FunctionPass { - #else - AU.addRequired(); - #endif -+#if LLVM_VERSION < 38 - AU.addRequired(); -+#else -+ AU.addRequired(); -+#endif - #if LLVM_VERSION < 35 - AU.addRequired(); - #else -@@ -1158,8 +1180,10 @@ class LLVMCodegen { - Folder(EE->getTargetData()), Builder(Context, Folder), Values(), CF(CF) { - #elif LLVM_VERSION < 37 - Folder(EE->getDataLayout()), Builder(Context, Folder), Values(), CF(CF) { --#else -+#elif LLVM_VERSION < 38 - Folder(*EE->getDataLayout()), Builder(Context, Folder), Values(), CF(CF) { -+#else -+ Folder(EE->getDataLayout()), Builder(Context, Folder), Values(), CF(CF) { - #endif - - for (unsigned i=0;igetTargetData()->getPointerSize() == 8) { --#else -+#elif LLVM_VERSION < 38 - if (EE->getDataLayout()->getPointerSize() == 8) { -+#else -+ if (EE->getDataLayout().getPointerSize() == 8) { - #endif - // eliminate useless trunc, GEP can take i64 too - if (TruncInst *I = dyn_cast(V)) { -@@ -1441,7 +1467,11 @@ class LLVMCodegen { - numArgs = func->numArgs; - - if (FakeGVs.any()) { -+#if LLVM_VERSION < 38 - Argument *Ctx = F->arg_begin(); -+#else -+ Argument *Ctx = &*F->arg_begin(); -+#endif - for (unsigned i=0;inum_globals;i++) { - if (!FakeGVs[i]) - continue; -@@ -1889,8 +1919,10 @@ class LLVMCodegen { - Src = Builder.CreatePointerCast(Src, PointerType::getUnqual(Type::getInt8Ty(Context))); - #if LLVM_VERSION < 32 - Value *Len = convertOperand(func, EE->getTargetData()->getIntPtrType(Context), inst->u.three[2]); --#else -+#elif LLVM_VERSION < 38 - Value *Len = convertOperand(func, EE->getDataLayout()->getIntPtrType(Context), inst->u.three[2]); -+#else -+ Value *Len = convertOperand(func, EE->getDataLayout().getIntPtrType(Context), inst->u.three[2]); - #endif - #if LLVM_VERSION < 37 - CallInst *c = Builder.CreateCall3(CF->FRealmemcmp, Dst, Src, Len); -@@ -2029,6 +2061,7 @@ class LLVMCodegen { - PMUnsigned.run(*F); - PMUnsigned.doFinalization(); - } -+ - apiMap.pmTimer.stopTimer(); - apiMap.irgenTimer.startTimer(); - } -@@ -2261,8 +2294,10 @@ static void addFunctionProtos(struct CommonFunctions *CF, ExecutionEngine *EE, M - args.push_back(PointerType::getUnqual(Type::getInt8Ty(Context))); - #if LLVM_VERSION < 32 - args.push_back(EE->getTargetData()->getIntPtrType(Context)); --#else -+#elif LLVM_VERSION < 38 - args.push_back(EE->getDataLayout()->getIntPtrType(Context)); -+#else -+ args.push_back(EE->getDataLayout().getIntPtrType(Context)); - #endif - FuncTy_5 = FunctionType::get(Type::getInt32Ty(Context), - args, false); -@@ -2283,7 +2318,11 @@ INITIALIZE_PASS_DEPENDENCY(LoopInfo) - #else - INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) - #endif -+#if LLVM_VERSION < 38 - INITIALIZE_PASS_DEPENDENCY(ScalarEvolution) -+#else -+INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass) -+#endif - #if LLVM_VERSION < 35 - INITIALIZE_PASS_DEPENDENCY(DominatorTree) - #else -@@ -2610,8 +2649,10 @@ int cli_bytecode_prepare_jit(struct cli_all_bc *bcs) - #endif - #if LLVM_VERSION < 32 - M->setDataLayout(EE->getTargetData()->getStringRepresentation()); --#else -+#elif LLVM_VERSION < 38 - M->setDataLayout(EE->getDataLayout()->getStringRepresentation()); -+#else -+ M->setDataLayout(EE->getDataLayout().getStringRepresentation()); - #endif - #if LLVM_VERSION < 31 - M->setTargetTriple(sys::getHostTriple()); -@@ -2768,7 +2809,11 @@ int cli_bytecode_prepare_jit(struct cli_all_bc *bcs) - // TODO: only run this on the untrusted bytecodes, not all of them... - if (has_untrusted) - PM.add(createClamBCRTChecks()); --#if LLVM_VERSION >= 36 -+#if LLVM_VERSION >= 38 -+ // With LLVM 3.8 the test_bswap_jit test fails with the GVNPass enabled. -+ // To prevent the segfaults mentioned below, replace it with the ConstantPropagationPass. -+ PM.add(createConstantPropagationPass()); -+#elif LLVM_VERSION >= 36 - // With LLVM 3.6 (MCJIT) this Pass is required to work around - // a crash in LLVM caused by the SCCP Pass: - // Pass 'Sparse Conditional Constant Propagation' is not initialized. -@@ -2842,7 +2887,7 @@ int bytecode_init(void) - return CL_EARG; - } - #else -- if (!LLVMIsMultithreaded()) { -+ if (!llvm_is_multithreaded()) { - cli_warnmsg("bytecode_init: LLVM is compiled without multithreading support\n"); - } - #endif -@@ -2891,11 +2936,7 @@ int bytecode_init(void) - InitializeAllTargets(); - #endif - --#if LLVM_VERSION < 35 - if (!llvm_is_multithreaded()) { --#else -- if (!LLVMIsMultithreaded()) { --#endif - //TODO:cli_dbgmsg - DEBUG(errs() << "WARNING: ClamAV JIT built w/o atomic builtins\n" - << "On x86 for best performance ClamAV should be built for i686, not i386!\n"); -@@ -3114,7 +3155,7 @@ static Metadata *findDbgSubprogramDeclare(Function *V) { - MDNode *DIG = NMD->getOperand(i); - if (!DISubprogram::classof(DIG)) - continue; -- if ((cast(DIG))->getFunction() == V) -+ if ((cast(DIG))->describes(V)) - return DIG; - #endif - } -diff --git a/libclamav/c++/configure.ac b/libclamav/c++/configure.ac -index 0bc8985..cab73e5 100644 ---- a/libclamav/c++/configure.ac -+++ b/libclamav/c++/configure.ac -@@ -90,14 +90,14 @@ elif test $llvmver_test -lt 290; then - elif test $llvmver_test -lt 360; then - llvmcomp="jit nativecodegen scalaropts ipo" - AC_MSG_RESULT([ok ($llvmver)]) --elif test $llvmver_test -lt 380; then -+elif test $llvmver_test -lt 390; then - dnl LLVM 3.6.0 removed jit, so we have to use mcjit - dnl and we're using InitializeNativeTargetAsmParser, so we need the architecture specific parsers - llvmcomp="mcjit nativecodegen scalaropts ipo x86asmparser powerpcasmparser" - AC_MSG_RESULT([ok ($llvmver)]) - else - AC_MSG_RESULT([no ($llvmver)]) -- AC_MSG_ERROR([LLVM < 3.8 required, but "$llvmver"($llvmver_test) found]) -+ AC_MSG_ERROR([LLVM < 3.9 required, but "$llvmver"($llvmver_test) found]) - fi - - dnl acquire the required flags to properly link in external LLVM -diff --git a/libclamav/c++/detect.cpp b/libclamav/c++/detect.cpp -index 42fc3d6..e1c1d11 100644 ---- a/libclamav/c++/detect.cpp -+++ b/libclamav/c++/detect.cpp -@@ -22,7 +22,9 @@ - */ - - #include "llvm/ADT/Triple.h" -+#if LLVM_VERSION < 38 - #include "llvm/Config/config.h" -+#endif - #include "llvm/Support/raw_ostream.h" - #if LLVM_VERSION < 29 - #include "llvm/System/DataTypes.h" diff -Nru clamav-0.103.2+dfsg/debian/patches/0006-Add-support-for-LLVM-3.9.patch clamav-0.103.5+dfsg/debian/patches/0006-Add-support-for-LLVM-3.9.patch --- clamav-0.103.2+dfsg/debian/patches/0006-Add-support-for-LLVM-3.9.patch 2021-04-12 18:51:20.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/patches/0006-Add-support-for-LLVM-3.9.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -From c1a84c5e27582da02e9bdea3aad616625ee9154d Mon Sep 17 00:00:00 2001 -From: Andreas Cadhalpun -Date: Fri, 14 Oct 2016 20:24:56 +0200 -Subject: Add support for LLVM 3.9 - -Changes: -IRBuilder no longer has a preserveNames template argument. -AtomicOrdering is now a strongly typed enum. ---- - libclamav/c++/bytecode2llvm.cpp | 12 +++++++++++- - libclamav/c++/configure.ac | 4 ++-- - 2 files changed, 13 insertions(+), 3 deletions(-) - -diff --git a/libclamav/c++/bytecode2llvm.cpp b/libclamav/c++/bytecode2llvm.cpp -index e7093cb..ad93eae 100644 ---- a/libclamav/c++/bytecode2llvm.cpp -+++ b/libclamav/c++/bytecode2llvm.cpp -@@ -788,7 +788,11 @@ class RuntimeLimits : public FunctionPass { - AbrtC->setDoesNotThrow(); - #endif - new UnreachableInst(F.getContext(), AbrtBB); -+#if LLVM_VERSION < 39 - IRBuilder Builder(F.getContext()); -+#else -+ IRBuilder<> Builder(F.getContext()); -+#endif - - #if LLVM_VERSION < 38 - Value *Flag = F.arg_begin(); -@@ -826,8 +830,10 @@ class RuntimeLimits : public FunctionPass { - #if LLVM_VERSION < 30 - // store-load barrier: will be a no-op on x86 but not other arches - Builder.CreateCall(LSBarrier, ARRAYREF(Value*, MBArgs, MBArgs+5)); --#else -+#elif LLVM_VERSION < 39 - Builder.CreateFence(Release); -+#else -+ Builder.CreateFence(AtomicOrdering::Release); - #endif - // Load Flag that tells us we timed out (first byte in bc_ctx) - Value *Cond = Builder.CreateLoad(Flag, true); -@@ -970,7 +976,11 @@ class LLVMCodegen { - Twine BytecodeID; - - TargetFolder Folder; -+#if LLVM_VERSION < 39 - IRBuilder Builder; -+#else -+ IRBuilder Builder; -+#endif - - std::vector globals; - DenseMap GVoffsetMap; -diff --git a/libclamav/c++/configure.ac b/libclamav/c++/configure.ac -index cab73e5..a2453cd 100644 ---- a/libclamav/c++/configure.ac -+++ b/libclamav/c++/configure.ac -@@ -90,14 +90,14 @@ elif test $llvmver_test -lt 290; then - elif test $llvmver_test -lt 360; then - llvmcomp="jit nativecodegen scalaropts ipo" - AC_MSG_RESULT([ok ($llvmver)]) --elif test $llvmver_test -lt 390; then -+elif test $llvmver_test -lt 400; then - dnl LLVM 3.6.0 removed jit, so we have to use mcjit - dnl and we're using InitializeNativeTargetAsmParser, so we need the architecture specific parsers - llvmcomp="mcjit nativecodegen scalaropts ipo x86asmparser powerpcasmparser" - AC_MSG_RESULT([ok ($llvmver)]) - else - AC_MSG_RESULT([no ($llvmver)]) -- AC_MSG_ERROR([LLVM < 3.9 required, but "$llvmver"($llvmver_test) found]) -+ AC_MSG_ERROR([LLVM < 4.0 required, but "$llvmver"($llvmver_test) found]) - fi - - dnl acquire the required flags to properly link in external LLVM diff -Nru clamav-0.103.2+dfsg/debian/patches/0007-unit-tests-Fix-ck_assert_msg-call.patch clamav-0.103.5+dfsg/debian/patches/0007-unit-tests-Fix-ck_assert_msg-call.patch --- clamav-0.103.2+dfsg/debian/patches/0007-unit-tests-Fix-ck_assert_msg-call.patch 2021-04-12 18:51:20.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/patches/0007-unit-tests-Fix-ck_assert_msg-call.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -From e3fe85c3a7fd4c273b1fe61b871ab9d935bca417 Mon Sep 17 00:00:00 2001 -From: Orion Poplawski -Date: Thu, 17 Sep 2020 22:26:04 -0600 -Subject: unit tests: Fix ck_assert_msg() call - -The first argument to ck_assert_msg() should be a logical condition. ---- - unit_tests/check_jsnorm.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/unit_tests/check_jsnorm.c b/unit_tests/check_jsnorm.c -index f15dce4..7618776 100644 ---- a/unit_tests/check_jsnorm.c -+++ b/unit_tests/check_jsnorm.c -@@ -247,7 +247,7 @@ static void tokenizer_test(const char *in, const char *expected, int split) - fd = open(filename, O_RDONLY); - if (fd < 0) { - jstest_teardown(); -- ck_assert_msg("failed to open output file: %s", filename); -+ ck_assert_msg(0, "failed to open output file: %s", filename); - } - - diff_file_mem(fd, expected, len); diff -Nru clamav-0.103.2+dfsg/debian/patches/Add-support-for-LLVM-3.7.patch clamav-0.103.5+dfsg/debian/patches/Add-support-for-LLVM-3.7.patch --- clamav-0.103.2+dfsg/debian/patches/Add-support-for-LLVM-3.7.patch 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/patches/Add-support-for-LLVM-3.7.patch 2022-01-12 20:02:01.000000000 +0000 @@ -0,0 +1,740 @@ +From 3c59ac840e1af87745bf5ff3bfd59bb4633656ed Mon Sep 17 00:00:00 2001 +From: Andreas Cadhalpun +Date: Fri, 14 Oct 2016 20:24:39 +0200 +Subject: Add support for LLVM 3.7 + +Main changes: +The DataLayoutPass is no longer necessary. +The LoopInfo pass is now a WrapperPass. +Before creating TargetLibraryInfo one needs to create a +TargetLibraryInfoImpl. +PassManager is now in the legacy:: namespace. +GetElementPtrInst::getIndexedType changed behavior causing segfaults in +the testsuite; emulating the old behavior now. +CreateCallX functions for fixed number X of elements got removed. +JITEmitDebugInfo Option was removed. +DIDescriptor was removed. + +Patch-Name: Add-support-for-LLVM-3.7.patch +--- + libclamav/c++/ClamBCRTChecks.cpp | 34 +++++- + libclamav/c++/PointerTracking.cpp | 44 +++++++- + libclamav/c++/bytecode2llvm.cpp | 181 ++++++++++++++++++++++++++++-- + libclamav/c++/configure.ac | 4 +- + 4 files changed, 244 insertions(+), 19 deletions(-) + +diff --git a/libclamav/c++/ClamBCRTChecks.cpp b/libclamav/c++/ClamBCRTChecks.cpp +index 95b4e17..550aa80 100644 +--- a/libclamav/c++/ClamBCRTChecks.cpp ++++ b/libclamav/c++/ClamBCRTChecks.cpp +@@ -201,9 +201,11 @@ namespace llvm { + TD = &getAnalysis(); + #elif LLVM_VERSION < 35 + TD = &getAnalysis(); +-#else ++#elif LLVM_VERSION < 37 + DataLayoutPass *DLP = getAnalysisIfAvailable(); + TD = DLP ? &DLP->getDataLayout() : 0; ++#else ++ TD = &F.getEntryBlock().getModule()->getDataLayout(); + #endif + SE = &getAnalysis(); + PT = &getAnalysis(); +@@ -212,7 +214,11 @@ namespace llvm { + #else + DT = &getAnalysis().getDomTree(); + #endif ++#if LLVM_VERSION < 37 + expander = new SCEVExpander(*SE OPT("SCEVexpander")); ++#else ++ expander = new SCEVExpander(*SE, *TD OPT("SCEVexpander")); ++#endif + + std::vector insns; + +@@ -351,8 +357,10 @@ namespace llvm { + AU.addRequired(); + #elif LLVM_VERSION < 35 + AU.addRequired(); +-#else ++#elif LLVM_VERSION < 37 + AU.addRequired(); ++#else ++ // No DataLayout pass needed anymore. + #endif + #if LLVM_VERSION < 35 + AU.addRequired(); +@@ -406,7 +414,11 @@ namespace llvm { + if (BaseMap.count(P)) { + return BaseMap[Ptr] = BaseMap[P]; + } ++#if LLVM_VERSION < 37 + Value *P2 = GetUnderlyingObject(P, TD); ++#else ++ Value *P2 = GetUnderlyingObject(P, *TD); ++#endif + if (P2 != P) { + Value *V = getPointerBase(P2); + return BaseMap[Ptr] = V; +@@ -520,7 +532,11 @@ namespace llvm { + } + } + if (LoadInst *LI = dyn_cast(Base)) { ++#if LLVM_VERSION < 37 + Value *V = GetUnderlyingObject(LI->getPointerOperand()->stripPointerCasts(), TD); ++#else ++ Value *V = GetUnderlyingObject(LI->getPointerOperand()->stripPointerCasts(), *TD); ++#endif + if (Argument *A = dyn_cast(V)) { + if (A->getArgNo() == 0) { + // pointers from hidden ctx are trusted to be at least the +@@ -674,7 +690,11 @@ namespace llvm { + } + BasicBlock *BB = I->getParent(); + BasicBlock::iterator It = I; ++#if LLVM_VERSION < 37 + BasicBlock *newBB = SplitBlock(BB, &*It, this); ++#else ++ BasicBlock *newBB = SplitBlock(BB, &*It); ++#endif + PHINode *PN; + unsigned MDDbgKind = I->getContext().getMDKindID("dbg"); + //verifyFunction(*BB->getParent()); +@@ -719,9 +739,15 @@ namespace llvm { + unsigned locationid = 0; + bool Approximate; + if (MDNode *Dbg = getLocation(I, Approximate, MDDbgKind)) { ++#if LLVM_VERSION < 37 + DILocation Loc(Dbg); + locationid = Loc.getLineNumber() << 8; + unsigned col = Loc.getColumnNumber(); ++#else ++ DebugLoc Loc(Dbg); ++ locationid = Loc.getLine() << 8; ++ unsigned col = Loc.getCol(); ++#endif + if (col > 254) + col = 254; + if (Approximate) +@@ -945,7 +971,11 @@ INITIALIZE_PASS_DEPENDENCY(TargetData) + #elif LLVM_VERSION < 35 + INITIALIZE_PASS_DEPENDENCY(DataLayout) + #else ++#if LLVM_VERSION < 37 + INITIALIZE_PASS_DEPENDENCY(DataLayoutPass) ++#else ++// No DataLayout pass needed anymore. ++#endif + #endif + #if LLVM_VERSION < 35 + INITIALIZE_PASS_DEPENDENCY(DominatorTree) +diff --git a/libclamav/c++/PointerTracking.cpp b/libclamav/c++/PointerTracking.cpp +index d5841a1..5567894 100644 +--- a/libclamav/c++/PointerTracking.cpp ++++ b/libclamav/c++/PointerTracking.cpp +@@ -30,7 +30,11 @@ + #include "llvm/IR/InstIterator.h" + #endif + #include "llvm/Support/raw_ostream.h" ++#if LLVM_VERSION < 37 + #include "llvm/Target/TargetLibraryInfo.h" ++#else ++#include ++#endif + + #if LLVM_VERSION < 32 + #include "llvm/Target/TargetData.h" +@@ -70,7 +74,11 @@ INITIALIZE_PASS_DEPENDENCY(DominatorTree) + #else + INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) + #endif ++#if LLVM_VERSION < 37 + INITIALIZE_PASS_DEPENDENCY(LoopInfo) ++#else ++INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) ++#endif + INITIALIZE_PASS_DEPENDENCY(ScalarEvolution) + #if LLVM_VERSION < 35 + INITIALIZE_PASS_DEPENDENCY(DominatorTree) +@@ -96,12 +104,18 @@ bool PointerTracking::runOnFunction(Function &F) { + TD = getAnalysisIfAvailable(); + #elif LLVM_VERSION < 35 + TD = getAnalysisIfAvailable(); +-#else ++#elif LLVM_VERSION < 37 + DataLayoutPass *DLP = getAnalysisIfAvailable(); + TD = DLP ? &DLP->getDataLayout() : 0; ++#else ++ TD = &F.getEntryBlock().getModule()->getDataLayout(); + #endif + SE = &getAnalysis(); ++#if LLVM_VERSION < 37 + LI = &getAnalysis(); ++#else ++ LI = &getAnalysis().getLoopInfo(); ++#endif + #if LLVM_VERSION < 35 + DT = &getAnalysis(); + #else +@@ -116,7 +130,11 @@ void PointerTracking::getAnalysisUsage(AnalysisUsage &AU) const { + #else + AU.addRequiredTransitive(); + #endif ++#if LLVM_VERSION < 37 + AU.addRequiredTransitive(); ++#else ++ AU.addRequiredTransitive(); ++#endif + AU.addRequiredTransitive(); + AU.setPreservesAll(); + } +@@ -178,12 +196,19 @@ const SCEV *PointerTracking::computeAllocationCount(Value *P, + if (CallInst *CI = extractMallocCall(V)) { + Value *arraySize = getMallocArraySize(CI, TD); + constType* AllocTy = getMallocAllocatedType(CI); +-#else ++#elif LLVM_VERSION < 37 + TargetLibraryInfo* TLI = new TargetLibraryInfo(); + + if (CallInst *CI = extractMallocCall(V, TLI)) { + Value *arraySize = getMallocArraySize(CI, TD, TLI); + constType* AllocTy = getMallocAllocatedType(CI, TLI); ++#else ++ TargetLibraryInfoImpl* TLII = new TargetLibraryInfoImpl(); ++ TargetLibraryInfo* TLI = new TargetLibraryInfo(*TLII); ++ ++ if (CallInst *CI = extractMallocCall(V, TLI)) { ++ Value *arraySize = getMallocArraySize(CI, *TD, TLI); ++ constType* AllocTy = getMallocAllocatedType(CI, TLI); + #endif + if (!AllocTy || !arraySize) return SE->getCouldNotCompute(); + Ty = AllocTy; +@@ -240,7 +265,7 @@ Value *PointerTracking::computeAllocationCountValue(Value *P, constType *&Ty) co + if (!Ty) + return 0; + Value *arraySize = getMallocArraySize(CI, TD); +-#else ++#elif LLVM_VERSION < 37 + TargetLibraryInfo* TLI = new TargetLibraryInfo(); + + if (CallInst *CI = extractMallocCall(V, TLI)) { +@@ -248,6 +273,15 @@ Value *PointerTracking::computeAllocationCountValue(Value *P, constType *&Ty) co + if (!Ty) + return 0; + Value *arraySize = getMallocArraySize(CI, TD, TLI); ++#else ++ TargetLibraryInfoImpl* TLII = new TargetLibraryInfoImpl(); ++ TargetLibraryInfo* TLI = new TargetLibraryInfo(*TLII); ++ ++ if (CallInst *CI = extractMallocCall(V, TLI)) { ++ Ty = getMallocAllocatedType(CI, TLI); ++ if (!Ty) ++ return 0; ++ Value *arraySize = getMallocArraySize(CI, *TD, TLI); + #endif + if (!arraySize) { + Ty = Type::getInt8Ty(P->getContext()); +@@ -351,7 +385,11 @@ void PointerTracking::getPointerOffset(Value *Pointer, Value *&Base, + const SCEV *&Offset) const + { + Pointer = Pointer->stripPointerCasts(); ++#if LLVM_VERSION < 37 + Base = GetUnderlyingObject(Pointer, TD); ++#else ++ Base = GetUnderlyingObject(Pointer, *TD); ++#endif + Limit = getAllocationSizeInBytes(Base); + if (isa(Limit)) { + Base = 0; +diff --git a/libclamav/c++/bytecode2llvm.cpp b/libclamav/c++/bytecode2llvm.cpp +index 350e8d4..6a9bd7e 100644 +--- a/libclamav/c++/bytecode2llvm.cpp ++++ b/libclamav/c++/bytecode2llvm.cpp +@@ -64,7 +64,11 @@ + #include "llvm/Object/ObjectFile.h" + #endif + #include "llvm/ExecutionEngine/JITEventListener.h" ++#if LLVM_VERSION < 37 + #include "llvm/PassManager.h" ++#else ++#include "llvm/IR/LegacyPassManager.h" ++#endif + #include "llvm/Support/Compiler.h" + #include "llvm/Support/Debug.h" + #include "llvm/Support/CommandLine.h" +@@ -232,7 +236,9 @@ namespace { + #define llvm_report_error(x) report_fatal_error(x) + #define llvm_install_error_handler(x) install_fatal_error_handler(x) + #define DwarfExceptionHandling JITExceptionHandling ++#if LLVM_VERSION < 37 + #define SetCurrentDebugLocation(x) SetCurrentDebugLocation(DebugLoc::getFromDILocation(x)) ++#endif + #define DEFINEPASS(passname) passname() : FunctionPass(ID) + #else + #define DEFINEPASS(passname) passname() : FunctionPass(&ID) +@@ -719,7 +725,11 @@ class RuntimeLimits : public FunctionPass { + BBMap[BB] = apicalls; + } + if (!BackedgeTargets.empty()) { ++#if LLVM_VERSION < 37 + LoopInfo &LI = getAnalysis(); ++#else ++ LoopInfo &LI = getAnalysis().getLoopInfo(); ++#endif + ScalarEvolution &SE = getAnalysis(); + + // Now check whether any of these backedge targets are part of a loop +@@ -803,7 +813,11 @@ class RuntimeLimits : public FunctionPass { + #endif + // Load Flag that tells us we timed out (first byte in bc_ctx) + Value *Cond = Builder.CreateLoad(Flag, true); ++#if LLVM_VERSION < 37 + BasicBlock *newBB = SplitBlock(BB, BB->getTerminator(), this); ++#else ++ BasicBlock *newBB = SplitBlock(BB, BB->getTerminator()); ++#endif + TerminatorInst *TI = BB->getTerminator(); + BranchInst::Create(AbrtBB, newBB, Cond, TI); + TI->eraseFromParent(); +@@ -824,7 +838,11 @@ class RuntimeLimits : public FunctionPass { + + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesAll(); ++#if LLVM_VERSION < 37 + AU.addRequired(); ++#else ++ AU.addRequired(); ++#endif + AU.addRequired(); + #if LLVM_VERSION < 35 + AU.addRequired(); +@@ -917,7 +935,11 @@ class LLVMCodegen { + Module *M; + LLVMContext &Context; + ExecutionEngine *EE; ++#if LLVM_VERSION < 37 + FunctionPassManager &PM, &PMUnsigned; ++#else ++ legacy::FunctionPassManager &PM, &PMUnsigned; ++#endif + LLVMTypeMapper *TypeMap; + + Function **apiFuncs; +@@ -1090,7 +1112,11 @@ class LLVMCodegen { + Constant *C = ConstantExpr::getPointerCast(GV, IP8Ty); + //TODO: check constant bounds here + return ConstantExpr::getPointerCast( ++#if LLVM_VERSION < 37 + ConstantExpr::getInBoundsGetElementPtr(C, ARRAYREF(Value*, idxs, 1)), ++#else ++ ConstantExpr::getInBoundsGetElementPtr(Ty, C, ARRAYREF(Value*, idxs, 1)), ++#endif + PTy); + } + if (isa(Ty)) { +@@ -1119,15 +1145,21 @@ class LLVMCodegen { + + public: + LLVMCodegen(const struct cli_bc *bc, Module *M, struct CommonFunctions *CF, FunctionMapTy &cFuncs, ++#if LLVM_VERSION < 37 + ExecutionEngine *EE, FunctionPassManager &PM, FunctionPassManager &PMUnsigned, ++#else ++ ExecutionEngine *EE, legacy::FunctionPassManager &PM, legacy::FunctionPassManager &PMUnsigned, ++#endif + Function **apiFuncs, LLVMTypeMapper &apiMap) + : bc(bc), M(M), Context(M->getContext()), EE(EE), + PM(PM),PMUnsigned(PMUnsigned), TypeMap(), apiFuncs(apiFuncs),apiMap(apiMap), + compiledFunctions(cFuncs), BytecodeID("bc"+Twine(bc->id)), + #if LLVM_VERSION < 32 + Folder(EE->getTargetData()), Builder(Context, Folder), Values(), CF(CF) { +-#else ++#elif LLVM_VERSION < 37 + Folder(EE->getDataLayout()), Builder(Context, Folder), Values(), CF(CF) { ++#else ++ Folder(*EE->getDataLayout()), Builder(Context, Folder), Values(), CF(CF) { + #endif + + for (unsigned i=0;i + #endif + Value* createGEP(Value *Base, constType *ETy, ARRAYREFPARAM(Value*,InputIterator Start,InputIterator End,ARef)) { ++#if LLVM_VERSION < 37 + constType *Ty = GetElementPtrInst::getIndexedType(Base->getType(),ARRAYREFP(Start,End,ARef)); ++#else ++ Type *Ty = NULL; ++ // This used to be done internally in LLVM's getIndexedTypeInternal. ++ PointerType *PTy = dyn_cast(Base->getType()->getScalarType()); ++ if (PTy) { ++ Type *Agg = PTy->getElementType(); ++ Ty = GetElementPtrInst::getIndexedType(Agg,ARRAYREFP(Start,End,ARef)); ++ } ++#endif + if (!Ty || (ETy && (Ty != ETy && (!isa(Ty) || !isa(ETy))))) { + if (cli_debug_flag) { + std::string str; +@@ -1458,7 +1500,11 @@ class LLVMCodegen { + if (func->dbgnodes[c] != ~0u) { + unsigned j = func->dbgnodes[c]; + assert(j < mdnodes.size()); ++#if LLVM_VERSION < 37 + Builder.SetCurrentDebugLocation(mdnodes[j]); ++#else ++ Builder.SetCurrentDebugLocation(DebugLoc(mdnodes[j])); ++#endif + } else + Builder.SetCurrentDebugLocation(0); + } +@@ -1768,11 +1814,16 @@ class LLVMCodegen { + #if LLVM_VERSION < 29 + CallInst *c = Builder.CreateCall4(CF->FMemset, Dst, Val, Len, + ConstantInt::get(Type::getInt32Ty(Context), 1)); +-#else ++#elif LLVM_VERSION < 37 + CallInst *c = Builder.CreateCall5(CF->FMemset, Dst, Val, Len, + ConstantInt::get(Type::getInt32Ty(Context), 1), + ConstantInt::get(Type::getInt1Ty(Context), 0) + ); ++#else ++ Value *args[] = { Dst, Val, Len, ++ ConstantInt::get(Type::getInt32Ty(Context), 1), ++ ConstantInt::get(Type::getInt1Ty(Context), 0)}; ++ CallInst *c = Builder.CreateCall(CF->FMemset, ARRAYREF(Value*, args, args + 5)); + #endif + c->setTailCall(true); + c->setDoesNotThrow(); +@@ -1789,11 +1840,16 @@ class LLVMCodegen { + #if LLVM_VERSION < 29 + CallInst *c = Builder.CreateCall4(CF->FMemcpy, Dst, Src, Len, + ConstantInt::get(Type::getInt32Ty(Context), 1)); +-#else ++#elif LLVM_VERSION < 37 + CallInst *c = Builder.CreateCall5(CF->FMemcpy, Dst, Src, Len, + ConstantInt::get(Type::getInt32Ty(Context), 1), + ConstantInt::get(Type::getInt1Ty(Context), 0) + ); ++#else ++ Value *args[] = { Dst, Src, Len, ++ ConstantInt::get(Type::getInt32Ty(Context), 1), ++ ConstantInt::get(Type::getInt1Ty(Context), 0)}; ++ CallInst *c = Builder.CreateCall(CF->FMemcpy, ARRAYREF(Value*, args, args + 5)); + #endif + c->setTailCall(true); + c->setDoesNotThrow(); +@@ -1810,10 +1866,15 @@ class LLVMCodegen { + #if LLVM_VERSION < 29 + CallInst *c = Builder.CreateCall4(CF->FMemmove, Dst, Src, Len, + ConstantInt::get(Type::getInt32Ty(Context), 1)); +-#else ++#elif LLVM_VERSION < 37 + CallInst *c = Builder.CreateCall5(CF->FMemmove, Dst, Src, Len, + ConstantInt::get(Type::getInt32Ty(Context), 1), + ConstantInt::get(Type::getInt1Ty(Context), 0)); ++#else ++ Value *args[] = {Dst, Src, Len, ++ ConstantInt::get(Type::getInt32Ty(Context), 1), ++ ConstantInt::get(Type::getInt1Ty(Context), 0)}; ++ CallInst *c = Builder.CreateCall(CF->FMemmove, args); + #endif + c->setTailCall(true); + c->setDoesNotThrow(); +@@ -1831,7 +1892,12 @@ class LLVMCodegen { + #else + Value *Len = convertOperand(func, EE->getDataLayout()->getIntPtrType(Context), inst->u.three[2]); + #endif ++#if LLVM_VERSION < 37 + CallInst *c = Builder.CreateCall3(CF->FRealmemcmp, Dst, Src, Len); ++#else ++ Value *args[] = {Dst, Src, Len}; ++ CallInst *c = Builder.CreateCall(CF->FRealmemcmp, ARRAYREF(Value*, args, args + 3)); ++#endif + c->setTailCall(true); + c->setDoesNotThrow(); + Store(inst->dest, c); +@@ -2212,7 +2278,11 @@ static void addFunctionProtos(struct CommonFunctions *CF, ExecutionEngine *EE, M + } + #if LLVM_VERSION >= 29 + INITIALIZE_PASS_BEGIN(RuntimeLimits, "rl", "Runtime Limits", false, false) ++#if LLVM_VERSION < 37 + INITIALIZE_PASS_DEPENDENCY(LoopInfo) ++#else ++INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) ++#endif + INITIALIZE_PASS_DEPENDENCY(ScalarEvolution) + #if LLVM_VERSION < 35 + INITIALIZE_PASS_DEPENDENCY(DominatorTree) +@@ -2438,8 +2508,10 @@ static void addFPasses(FunctionPassManager &FPM, bool trusted, const TargetData + static void addFPasses(FunctionPassManager &FPM, bool trusted, const DataLayout *TD) + #elif LLVM_VERSION < 36 + static void addFPasses(FunctionPassManager &FPM, bool trusted, const Module *M) +-#else ++#elif LLVM_VERSION < 37 + static void addFPasses(FunctionPassManager &FPM, bool trusted, Module *M) ++#else ++static void addFPasses(legacy::FunctionPassManager &FPM, bool trusted, Module *M) + #endif + { + // Set up the optimizer pipeline. Start with registering info about how +@@ -2450,10 +2522,12 @@ static void addFPasses(FunctionPassManager &FPM, bool trusted, Module *M) + FPM.add(new DataLayout(*TD)); + #elif LLVM_VERSION < 36 + FPM.add(new DataLayoutPass(M)); +-#else ++#elif LLVM_VERSION < 37 + DataLayoutPass *DLP = new DataLayoutPass(); + DLP->doInitialization(*M); + FPM.add(DLP); ++#else ++ // No DataLayout pass needed anymore. + #endif + // Promote allocas to registers. + FPM.add(createPromoteMemoryToRegisterPass()); +@@ -2483,6 +2557,8 @@ int cli_bytecode_prepare_jit(struct cli_all_bc *bcs) + + #if LLVM_VERSION >= 31 + TargetOptions Options; ++#if LLVM_VERSION < 37 ++ // This option was removed. + #ifdef CL_DEBUG + //disable this for now, it leaks + Options.JITEmitDebugInfo = false; +@@ -2490,6 +2566,7 @@ int cli_bytecode_prepare_jit(struct cli_all_bc *bcs) + #else + Options.JITEmitDebugInfo = false; + #endif ++#endif + #if LLVM_VERSION < 34 + Options.DwarfExceptionHandling = false; + #else +@@ -2526,7 +2603,11 @@ int cli_bytecode_prepare_jit(struct cli_all_bc *bcs) + struct CommonFunctions CF; + addFunctionProtos(&CF, EE, M); + ++#if LLVM_VERSION < 37 + FunctionPassManager OurFPM(M), OurFPMUnsigned(M); ++#else ++ legacy::FunctionPassManager OurFPM(M), OurFPMUnsigned(M); ++#endif + #if LLVM_VERSION < 32 + M->setDataLayout(EE->getTargetData()->getStringRepresentation()); + #else +@@ -2666,17 +2747,23 @@ int cli_bytecode_prepare_jit(struct cli_all_bc *bcs) + break; + } + } ++#if LLVM_VERSION < 37 + PassManager PM; ++#else ++ legacy::PassManager PM; ++#endif + #if LLVM_VERSION < 32 + PM.add(new TargetData(*EE->getTargetData())); + #elif LLVM_VERSION < 35 + PM.add(new DataLayout(*EE->getDataLayout())); + #elif LLVM_VERSION < 36 + PM.add(new DataLayoutPass(M)); +-#else ++#elif LLVM_VERSION < 37 + DataLayoutPass *DLP = new DataLayoutPass(); + DLP->doInitialization(*M); + PM.add(DLP); ++#else ++ // No DataLayout pass needed anymore. + #endif + // TODO: only run this on the untrusted bytecodes, not all of them... + if (has_untrusted) +@@ -2988,11 +3075,19 @@ static Metadata *findDbgGlobalDeclare(GlobalVariable *V) { + return 0; + + for (unsigned i = 0, e = NMD->getNumOperands(); i != e; ++i) { ++#if LLVM_VERSION < 37 + DIDescriptor DIG(cast(NMD->getOperand(i))); + if (!DIG.isGlobalVariable()) + continue; + if (DIGlobalVariable(DIG).getGlobal() == V) + return DIG; ++#else ++ MDNode *DIG = NMD->getOperand(i); ++ if (!DIGlobalVariable::classof(DIG)) ++ continue; ++ if ((cast(DIG))->getVariable() == V) ++ return DIG; ++#endif + } + return 0; + } +@@ -3009,11 +3104,19 @@ static Metadata *findDbgSubprogramDeclare(Function *V) { + return 0; + + for (unsigned i = 0, e = NMD->getNumOperands(); i != e; ++i) { ++#if LLVM_VERSION < 37 + DIDescriptor DIG(cast(NMD->getOperand(i))); + if (!DIG.isSubprogram()) + continue; + if (DISubprogram(DIG).getFunction() == V) + return DIG; ++#else ++ MDNode *DIG = NMD->getOperand(i); ++ if (!DISubprogram::classof(DIG)) ++ continue; ++ if ((cast(DIG))->getFunction() == V) ++ return DIG; ++#endif + } + return 0; + } +@@ -3062,22 +3165,39 @@ static bool getLocationInfo(const Value *V, std::string &DisplayName, + Metadata *DIGV = findDbgGlobalDeclare(GV); + #endif + if (!DIGV) return false; ++#if LLVM_VERSION < 37 + DIGlobalVariable Var(cast(DIGV)); ++#else ++ DIGlobalVariable *Var = cast(DIGV); ++#endif + ++#if LLVM_VERSION < 37 + StringRef D = Var.getDisplayName(); ++#else ++ StringRef D = Var->getDisplayName(); ++#endif + if (!D.empty()) + DisplayName = D; ++#if LLVM_VERSION < 37 + LineNo = Var.getLineNumber(); ++#else ++ LineNo = Var->getLine(); ++#endif + #if LLVM_VERSION < 33 + Unit = Var.getCompileUnit(); +-#else ++#elif LLVM_VERSION < 37 + G = Var.getFilename(); + H = Var.getDirectory(); ++#else ++ G = Var->getFilename(); ++ H = Var->getDirectory(); + #endif + #if LLVM_VERSION < 35 + TypeD = Var.getType(); +-#else ++#elif LLVM_VERSION < 37 + T = Var.getType().getName(); ++#else ++ T = (cast(*Var->getType())).getName(); + #endif + } else if (Function *F = dyn_cast(const_cast(V))){ + #if LLVM_VERSION < 36 +@@ -3086,32 +3206,61 @@ static bool getLocationInfo(const Value *V, std::string &DisplayName, + Metadata *DIF = findDbgSubprogramDeclare(F); + #endif + if (!DIF) return false; ++#if LLVM_VERSION < 37 + DISubprogram Var(cast(DIF)); ++#else ++ DISubprogram *Var = cast(DIF); ++#endif + ++#if LLVM_VERSION < 37 + StringRef D = Var.getDisplayName(); ++#else ++ StringRef D = Var->getDisplayName(); ++#endif + if (!D.empty()) + DisplayName = D; ++#if LLVM_VERSION < 37 + LineNo = Var.getLineNumber(); ++#else ++ LineNo = Var->getLine(); ++#endif + #if LLVM_VERSION < 33 + Unit = Var.getCompileUnit(); +-#else ++#elif LLVM_VERSION < 37 + G = Var.getFilename(); + H = Var.getDirectory(); ++#else ++ G = Var->getFilename(); ++ H = Var->getDirectory(); + #endif + #if LLVM_VERSION < 35 + TypeD = Var.getType(); +-#else ++#elif LLVM_VERSION < 37 + T = Var.getType().getName(); ++#else ++ T = Var->getType()->getName(); + #endif + } else { + const DbgDeclareInst *DDI = findDbgDeclare(V); + if (!DDI) return false; ++#if LLVM_VERSION < 37 + DIVariable Var(cast(DDI->getVariable())); ++#else ++ DIVariable* Var = DDI->getVariable(); ++#endif + ++#if LLVM_VERSION < 37 + StringRef D = Var.getName(); ++#else ++ StringRef D = Var->getName(); ++#endif + if (!D.empty()) + DisplayName = D; ++#if LLVM_VERSION < 37 + LineNo = Var.getLineNumber(); ++#else ++ LineNo = Var->getLine(); ++#endif + #if LLVM_VERSION < 33 + Unit = Var.getCompileUnit(); + #else +@@ -3121,8 +3270,10 @@ static bool getLocationInfo(const Value *V, std::string &DisplayName, + #endif + #if LLVM_VERSION < 35 + TypeD = Var.getType(); +-#else ++#elif LLVM_VERSION < 37 + T = Var.getType().getName(); ++#else ++ T = (cast(*Var->getType())).getName(); + #endif + } + +@@ -3158,9 +3309,15 @@ void printValue(llvm::Value *V, bool a, bool b) { + + void printLocation(llvm::Instruction *I, bool a, bool b) { + if (MDNode *N = I->getMetadata("dbg")) { ++#if LLVM_VERSION < 37 + DILocation Loc(N); + errs() << Loc.getFilename() << ":" << Loc.getLineNumber(); + if (unsigned Col = Loc.getColumnNumber()) { ++#else ++ DebugLoc Loc(N); ++ errs() << Loc.get()->getFilename() << ":" << Loc.getLine(); ++ if (unsigned Col = Loc.getCol()) { ++#endif + errs() << ":" << Col; + } + errs() << ": "; +diff --git a/libclamav/c++/configure.ac b/libclamav/c++/configure.ac +index 22b3b26..fcdfc6a 100644 +--- a/libclamav/c++/configure.ac ++++ b/libclamav/c++/configure.ac +@@ -90,14 +90,14 @@ elif test $llvmver_test -lt 290; then + elif test $llvmver_test -lt 360; then + llvmcomp="jit nativecodegen scalaropts ipo" + AC_MSG_RESULT([ok ($llvmver)]) +-elif test $llvmver_test -lt 370; then ++elif test $llvmver_test -lt 380; then + dnl LLVM 3.6.0 removed jit, so we have to use mcjit + dnl and we're using InitializeNativeTargetAsmParser, so we need the architecture specific parsers + llvmcomp="mcjit nativecodegen scalaropts ipo x86asmparser powerpcasmparser" + AC_MSG_RESULT([ok ($llvmver)]) + else + AC_MSG_RESULT([no ($llvmver)]) +- AC_MSG_ERROR([LLVM < 3.7 required, but "$llvmver"($llvmver_test) found]) ++ AC_MSG_ERROR([LLVM < 3.8 required, but "$llvmver"($llvmver_test) found]) + fi + + dnl acquire the required flags to properly link in external LLVM diff -Nru clamav-0.103.2+dfsg/debian/patches/Add-support-for-LLVM-3.8.patch clamav-0.103.5+dfsg/debian/patches/Add-support-for-LLVM-3.8.patch --- clamav-0.103.2+dfsg/debian/patches/Add-support-for-LLVM-3.8.patch 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/patches/Add-support-for-LLVM-3.8.patch 2022-01-12 20:02:01.000000000 +0000 @@ -0,0 +1,457 @@ +From 1086c706710422d3b149924256032a9fe3445a52 Mon Sep 17 00:00:00 2001 +From: Andreas Cadhalpun +Date: Fri, 14 Oct 2016 20:24:48 +0200 +Subject: Add support for LLVM 3.8 + +Main changes: +llvm/Config/config.h was removed. +The ScalarEvolution pass is now a WrapperPass. +Iterators are no longer automatically converted to and from pointers. +The GVNPass causes the test_bswap_jit test to fail; replaced with +ConstantPropagationPass. +LLVMIsMultithreaded is from the C API, while llvm_is_multithreaded is +the corresponding C++ API. + +Patch-Name: Add-support-for-LLVM-3.8.patch +--- + libclamav/c++/ClamBCRTChecks.cpp | 50 ++++++++++++++++++++++++ + libclamav/c++/PointerTracking.cpp | 12 ++++++ + libclamav/c++/bytecode2llvm.cpp | 65 +++++++++++++++++++++++++------ + libclamav/c++/configure.ac | 4 +- + libclamav/c++/detect.cpp | 2 + + 5 files changed, 119 insertions(+), 14 deletions(-) + +diff --git a/libclamav/c++/ClamBCRTChecks.cpp b/libclamav/c++/ClamBCRTChecks.cpp +index 550aa80..949eeec 100644 +--- a/libclamav/c++/ClamBCRTChecks.cpp ++++ b/libclamav/c++/ClamBCRTChecks.cpp +@@ -54,7 +54,9 @@ + #include "llvm/Analysis/ScalarEvolution.h" + #include "llvm/Analysis/ScalarEvolutionExpressions.h" + #include "llvm/Analysis/ScalarEvolutionExpander.h" ++#if LLVM_VERSION < 38 + #include "llvm/Config/config.h" ++#endif + #include "llvm/Pass.h" + #include "llvm/Support/CommandLine.h" + #if LLVM_VERSION < 35 +@@ -207,7 +209,11 @@ namespace llvm { + #else + TD = &F.getEntryBlock().getModule()->getDataLayout(); + #endif ++#if LLVM_VERSION < 38 + SE = &getAnalysis(); ++#else ++ SE = &getAnalysis().getSE(); ++#endif + PT = &getAnalysis(); + #if LLVM_VERSION < 35 + DT = &getAnalysis(); +@@ -332,7 +338,11 @@ namespace llvm { + AbrtC->setDoesNotThrow(); + #endif + // remove all instructions from entry ++#if LLVM_VERSION < 38 + BasicBlock::iterator BBI = I, BBE=BB->end(); ++#else ++ BasicBlock::iterator BBI = I->getIterator(), BBE=BB->end(); ++#endif + while (BBI != BBE) { + if (!BBI->use_empty()) + BBI->replaceAllUsesWith(UndefValue::get(BBI->getType())); +@@ -367,7 +377,11 @@ namespace llvm { + #else + AU.addRequired(); + #endif ++#if LLVM_VERSION < 38 + AU.addRequired(); ++#else ++ AU.addRequired(); ++#endif + AU.addRequired(); + #if LLVM_VERSION < 35 + AU.addRequired(); +@@ -398,9 +412,17 @@ namespace llvm { + + Instruction *getInsertPoint(Value *V) + { ++#if LLVM_VERSION < 38 + BasicBlock::iterator It = EP; ++#else ++ BasicBlock::iterator It = EP->getIterator(); ++#endif + if (Instruction *I = dyn_cast(V)) { ++#if LLVM_VERSION < 38 + It = I; ++#else ++ It = I->getIterator(); ++#endif + ++It; + } + return &*It; +@@ -427,7 +449,11 @@ namespace llvm { + constType *P8Ty = + PointerType::getUnqual(Type::getInt8Ty(Ptr->getContext())); + if (PHINode *PN = dyn_cast(Ptr)) { ++#if LLVM_VERSION < 38 + BasicBlock::iterator It = PN; ++#else ++ BasicBlock::iterator It = PN->getIterator(); ++#endif + ++It; + PHINode *newPN = PHINode::Create(P8Ty, HINT(PN->getNumIncomingValues()) ".verif.base", &*It); + Changed = true; +@@ -441,7 +467,11 @@ namespace llvm { + return newPN; + } + if (SelectInst *SI = dyn_cast(Ptr)) { ++#if LLVM_VERSION < 38 + BasicBlock::iterator It = SI; ++#else ++ BasicBlock::iterator It = SI->getIterator(); ++#endif + ++It; + Value *TrueB = getPointerBase(SI->getTrueValue()); + Value *FalseB = getPointerBase(SI->getFalseValue()); +@@ -575,7 +605,11 @@ namespace llvm { + } + #endif + if (PHINode *PN = dyn_cast(Base)) { ++#if LLVM_VERSION < 38 + BasicBlock::iterator It = PN; ++#else ++ BasicBlock::iterator It = PN->getIterator(); ++#endif + ++It; + PHINode *newPN = PHINode::Create(I64Ty, HINT(PN->getNumIncomingValues()) ".verif.bounds", &*It); + Changed = true; +@@ -598,7 +632,11 @@ namespace llvm { + return BoundsMap[Base] = newPN; + } + if (SelectInst *SI = dyn_cast(Base)) { ++#if LLVM_VERSION < 38 + BasicBlock::iterator It = SI; ++#else ++ BasicBlock::iterator It = SI->getIterator(); ++#endif + ++It; + Value *TrueB = getPointerBounds(SI->getTrueValue()); + Value *FalseB = getPointerBounds(SI->getFalseValue()); +@@ -655,7 +693,11 @@ namespace llvm { + if (!MDDbgKind) + return 0; + Approximate = true; ++#if LLVM_VERSION < 38 + BasicBlock::iterator It = I; ++#else ++ BasicBlock::iterator It = I->getIterator(); ++#endif + while (It != I->getParent()->begin()) { + --It; + if (MDNode *Dbg = It->getMetadata(MDDbgKind)) +@@ -689,7 +731,11 @@ namespace llvm { + return false; + } + BasicBlock *BB = I->getParent(); ++#if LLVM_VERSION < 38 + BasicBlock::iterator It = I; ++#else ++ BasicBlock::iterator It = I->getIterator(); ++#endif + #if LLVM_VERSION < 37 + BasicBlock *newBB = SplitBlock(BB, &*It, this); + #else +@@ -982,7 +1028,11 @@ INITIALIZE_PASS_DEPENDENCY(DominatorTree) + #else + INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) + #endif ++#if LLVM_VERSION < 38 + INITIALIZE_PASS_DEPENDENCY(ScalarEvolution) ++#else ++INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass) ++#endif + #if LLVM_VERSION < 34 + INITIALIZE_AG_DEPENDENCY(CallGraph) + #elif LLVM_VERSION < 35 +diff --git a/libclamav/c++/PointerTracking.cpp b/libclamav/c++/PointerTracking.cpp +index 5567894..147ad48 100644 +--- a/libclamav/c++/PointerTracking.cpp ++++ b/libclamav/c++/PointerTracking.cpp +@@ -79,7 +79,11 @@ INITIALIZE_PASS_DEPENDENCY(LoopInfo) + #else + INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) + #endif ++#if LLVM_VERSION < 38 + INITIALIZE_PASS_DEPENDENCY(ScalarEvolution) ++#else ++INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass) ++#endif + #if LLVM_VERSION < 35 + INITIALIZE_PASS_DEPENDENCY(DominatorTree) + #else +@@ -110,7 +114,11 @@ bool PointerTracking::runOnFunction(Function &F) { + #else + TD = &F.getEntryBlock().getModule()->getDataLayout(); + #endif ++#if LLVM_VERSION < 38 + SE = &getAnalysis(); ++#else ++ SE = &getAnalysis().getSE(); ++#endif + #if LLVM_VERSION < 37 + LI = &getAnalysis(); + #else +@@ -135,7 +143,11 @@ void PointerTracking::getAnalysisUsage(AnalysisUsage &AU) const { + #else + AU.addRequiredTransitive(); + #endif ++#if LLVM_VERSION < 38 + AU.addRequiredTransitive(); ++#else ++ AU.addRequiredTransitive(); ++#endif + AU.setPreservesAll(); + } + +diff --git a/libclamav/c++/bytecode2llvm.cpp b/libclamav/c++/bytecode2llvm.cpp +index 6a9bd7e..3490690 100644 +--- a/libclamav/c++/bytecode2llvm.cpp ++++ b/libclamav/c++/bytecode2llvm.cpp +@@ -171,7 +171,9 @@ void LLVMInitializePowerPCAsmPrinter(); + //#define TIMING + #undef TIMING + ++#if LLVM_VERSION < 38 + #include "llvm/Config/config.h" ++#endif + #ifdef ENABLE_THREADS + #if !ENABLE_THREADS + #error "Thread support was explicitly disabled. Cannot continue" +@@ -730,7 +732,11 @@ class RuntimeLimits : public FunctionPass { + #else + LoopInfo &LI = getAnalysis().getLoopInfo(); + #endif ++#if LLVM_VERSION < 38 + ScalarEvolution &SE = getAnalysis(); ++#else ++ ScalarEvolution &SE = getAnalysis().getSE(); ++#endif + + // Now check whether any of these backedge targets are part of a loop + // with a small constant trip count +@@ -784,7 +790,11 @@ class RuntimeLimits : public FunctionPass { + new UnreachableInst(F.getContext(), AbrtBB); + IRBuilder Builder(F.getContext()); + ++#if LLVM_VERSION < 38 + Value *Flag = F.arg_begin(); ++#else ++ Value *Flag = &*F.arg_begin(); ++#endif + #if LLVM_VERSION < 30 + Function *LSBarrier = Intrinsic::getDeclaration(F.getParent(), + Intrinsic::memory_barrier); +@@ -798,13 +808,21 @@ class RuntimeLimits : public FunctionPass { + #endif + verifyFunction(F); + BasicBlock *BB = &F.getEntryBlock(); ++#if LLVM_VERSION < 38 + Builder.SetInsertPoint(BB, BB->getTerminator()); ++#else ++ Builder.SetInsertPoint(BB, BB->getTerminator()->getIterator()); ++#endif + Flag = Builder.CreatePointerCast(Flag, PointerType::getUnqual( + Type::getInt1Ty(F.getContext()))); + for (BBSetTy::iterator I=needsTimeoutCheck.begin(), + E=needsTimeoutCheck.end(); I != E; ++I) { + BasicBlock *BB = *I; ++#if LLVM_VERSION < 38 + Builder.SetInsertPoint(BB, BB->getTerminator()); ++#else ++ Builder.SetInsertPoint(BB, BB->getTerminator()->getIterator()); ++#endif + #if LLVM_VERSION < 30 + // store-load barrier: will be a no-op on x86 but not other arches + Builder.CreateCall(LSBarrier, ARRAYREF(Value*, MBArgs, MBArgs+5)); +@@ -843,7 +861,11 @@ class RuntimeLimits : public FunctionPass { + #else + AU.addRequired(); + #endif ++#if LLVM_VERSION < 38 + AU.addRequired(); ++#else ++ AU.addRequired(); ++#endif + #if LLVM_VERSION < 35 + AU.addRequired(); + #else +@@ -1158,8 +1180,10 @@ class LLVMCodegen { + Folder(EE->getTargetData()), Builder(Context, Folder), Values(), CF(CF) { + #elif LLVM_VERSION < 37 + Folder(EE->getDataLayout()), Builder(Context, Folder), Values(), CF(CF) { +-#else ++#elif LLVM_VERSION < 38 + Folder(*EE->getDataLayout()), Builder(Context, Folder), Values(), CF(CF) { ++#else ++ Folder(EE->getDataLayout()), Builder(Context, Folder), Values(), CF(CF) { + #endif + + for (unsigned i=0;igetTargetData()->getPointerSize() == 8) { +-#else ++#elif LLVM_VERSION < 38 + if (EE->getDataLayout()->getPointerSize() == 8) { ++#else ++ if (EE->getDataLayout().getPointerSize() == 8) { + #endif + // eliminate useless trunc, GEP can take i64 too + if (TruncInst *I = dyn_cast(V)) { +@@ -1441,7 +1467,11 @@ class LLVMCodegen { + numArgs = func->numArgs; + + if (FakeGVs.any()) { ++#if LLVM_VERSION < 38 + Argument *Ctx = F->arg_begin(); ++#else ++ Argument *Ctx = &*F->arg_begin(); ++#endif + for (unsigned i=0;inum_globals;i++) { + if (!FakeGVs[i]) + continue; +@@ -1889,8 +1919,10 @@ class LLVMCodegen { + Src = Builder.CreatePointerCast(Src, PointerType::getUnqual(Type::getInt8Ty(Context))); + #if LLVM_VERSION < 32 + Value *Len = convertOperand(func, EE->getTargetData()->getIntPtrType(Context), inst->u.three[2]); +-#else ++#elif LLVM_VERSION < 38 + Value *Len = convertOperand(func, EE->getDataLayout()->getIntPtrType(Context), inst->u.three[2]); ++#else ++ Value *Len = convertOperand(func, EE->getDataLayout().getIntPtrType(Context), inst->u.three[2]); + #endif + #if LLVM_VERSION < 37 + CallInst *c = Builder.CreateCall3(CF->FRealmemcmp, Dst, Src, Len); +@@ -2029,6 +2061,7 @@ class LLVMCodegen { + PMUnsigned.run(*F); + PMUnsigned.doFinalization(); + } ++ + apiMap.pmTimer.stopTimer(); + apiMap.irgenTimer.startTimer(); + } +@@ -2261,8 +2294,10 @@ static void addFunctionProtos(struct CommonFunctions *CF, ExecutionEngine *EE, M + args.push_back(PointerType::getUnqual(Type::getInt8Ty(Context))); + #if LLVM_VERSION < 32 + args.push_back(EE->getTargetData()->getIntPtrType(Context)); +-#else ++#elif LLVM_VERSION < 38 + args.push_back(EE->getDataLayout()->getIntPtrType(Context)); ++#else ++ args.push_back(EE->getDataLayout().getIntPtrType(Context)); + #endif + FuncTy_5 = FunctionType::get(Type::getInt32Ty(Context), + args, false); +@@ -2283,7 +2318,11 @@ INITIALIZE_PASS_DEPENDENCY(LoopInfo) + #else + INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) + #endif ++#if LLVM_VERSION < 38 + INITIALIZE_PASS_DEPENDENCY(ScalarEvolution) ++#else ++INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass) ++#endif + #if LLVM_VERSION < 35 + INITIALIZE_PASS_DEPENDENCY(DominatorTree) + #else +@@ -2610,8 +2649,10 @@ int cli_bytecode_prepare_jit(struct cli_all_bc *bcs) + #endif + #if LLVM_VERSION < 32 + M->setDataLayout(EE->getTargetData()->getStringRepresentation()); +-#else ++#elif LLVM_VERSION < 38 + M->setDataLayout(EE->getDataLayout()->getStringRepresentation()); ++#else ++ M->setDataLayout(EE->getDataLayout().getStringRepresentation()); + #endif + #if LLVM_VERSION < 31 + M->setTargetTriple(sys::getHostTriple()); +@@ -2768,7 +2809,11 @@ int cli_bytecode_prepare_jit(struct cli_all_bc *bcs) + // TODO: only run this on the untrusted bytecodes, not all of them... + if (has_untrusted) + PM.add(createClamBCRTChecks()); +-#if LLVM_VERSION >= 36 ++#if LLVM_VERSION >= 38 ++ // With LLVM 3.8 the test_bswap_jit test fails with the GVNPass enabled. ++ // To prevent the segfaults mentioned below, replace it with the ConstantPropagationPass. ++ PM.add(createConstantPropagationPass()); ++#elif LLVM_VERSION >= 36 + // With LLVM 3.6 (MCJIT) this Pass is required to work around + // a crash in LLVM caused by the SCCP Pass: + // Pass 'Sparse Conditional Constant Propagation' is not initialized. +@@ -2842,7 +2887,7 @@ int bytecode_init(void) + return CL_EARG; + } + #else +- if (!LLVMIsMultithreaded()) { ++ if (!llvm_is_multithreaded()) { + cli_warnmsg("bytecode_init: LLVM is compiled without multithreading support\n"); + } + #endif +@@ -2891,11 +2936,7 @@ int bytecode_init(void) + InitializeAllTargets(); + #endif + +-#if LLVM_VERSION < 35 + if (!llvm_is_multithreaded()) { +-#else +- if (!LLVMIsMultithreaded()) { +-#endif + //TODO:cli_dbgmsg + DEBUG(errs() << "WARNING: ClamAV JIT built w/o atomic builtins\n" + << "On x86 for best performance ClamAV should be built for i686, not i386!\n"); +@@ -3114,7 +3155,7 @@ static Metadata *findDbgSubprogramDeclare(Function *V) { + MDNode *DIG = NMD->getOperand(i); + if (!DISubprogram::classof(DIG)) + continue; +- if ((cast(DIG))->getFunction() == V) ++ if ((cast(DIG))->describes(V)) + return DIG; + #endif + } +diff --git a/libclamav/c++/configure.ac b/libclamav/c++/configure.ac +index fcdfc6a..d4fbfed 100644 +--- a/libclamav/c++/configure.ac ++++ b/libclamav/c++/configure.ac +@@ -90,14 +90,14 @@ elif test $llvmver_test -lt 290; then + elif test $llvmver_test -lt 360; then + llvmcomp="jit nativecodegen scalaropts ipo" + AC_MSG_RESULT([ok ($llvmver)]) +-elif test $llvmver_test -lt 380; then ++elif test $llvmver_test -lt 390; then + dnl LLVM 3.6.0 removed jit, so we have to use mcjit + dnl and we're using InitializeNativeTargetAsmParser, so we need the architecture specific parsers + llvmcomp="mcjit nativecodegen scalaropts ipo x86asmparser powerpcasmparser" + AC_MSG_RESULT([ok ($llvmver)]) + else + AC_MSG_RESULT([no ($llvmver)]) +- AC_MSG_ERROR([LLVM < 3.8 required, but "$llvmver"($llvmver_test) found]) ++ AC_MSG_ERROR([LLVM < 3.9 required, but "$llvmver"($llvmver_test) found]) + fi + + dnl acquire the required flags to properly link in external LLVM +diff --git a/libclamav/c++/detect.cpp b/libclamav/c++/detect.cpp +index b0a8616..5dba3f6 100644 +--- a/libclamav/c++/detect.cpp ++++ b/libclamav/c++/detect.cpp +@@ -22,7 +22,9 @@ + */ + + #include "llvm/ADT/Triple.h" ++#if LLVM_VERSION < 38 + #include "llvm/Config/config.h" ++#endif + #include "llvm/Support/raw_ostream.h" + #if LLVM_VERSION < 29 + #include "llvm/System/DataTypes.h" diff -Nru clamav-0.103.2+dfsg/debian/patches/Add-support-for-LLVM-3.9.patch clamav-0.103.5+dfsg/debian/patches/Add-support-for-LLVM-3.9.patch --- clamav-0.103.2+dfsg/debian/patches/Add-support-for-LLVM-3.9.patch 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/patches/Add-support-for-LLVM-3.9.patch 2022-01-12 20:02:01.000000000 +0000 @@ -0,0 +1,76 @@ +From cab0c119bb4042d9b162b8bbfc5e030a6eadbd39 Mon Sep 17 00:00:00 2001 +From: Andreas Cadhalpun +Date: Fri, 14 Oct 2016 20:24:56 +0200 +Subject: Add support for LLVM 3.9 + +Changes: +IRBuilder no longer has a preserveNames template argument. +AtomicOrdering is now a strongly typed enum. + +Patch-Name: Add-support-for-LLVM-3.9.patch +--- + libclamav/c++/bytecode2llvm.cpp | 12 +++++++++++- + libclamav/c++/configure.ac | 4 ++-- + 2 files changed, 13 insertions(+), 3 deletions(-) + +diff --git a/libclamav/c++/bytecode2llvm.cpp b/libclamav/c++/bytecode2llvm.cpp +index 3490690..7e4de54 100644 +--- a/libclamav/c++/bytecode2llvm.cpp ++++ b/libclamav/c++/bytecode2llvm.cpp +@@ -788,7 +788,11 @@ class RuntimeLimits : public FunctionPass { + AbrtC->setDoesNotThrow(); + #endif + new UnreachableInst(F.getContext(), AbrtBB); ++#if LLVM_VERSION < 39 + IRBuilder Builder(F.getContext()); ++#else ++ IRBuilder<> Builder(F.getContext()); ++#endif + + #if LLVM_VERSION < 38 + Value *Flag = F.arg_begin(); +@@ -826,8 +830,10 @@ class RuntimeLimits : public FunctionPass { + #if LLVM_VERSION < 30 + // store-load barrier: will be a no-op on x86 but not other arches + Builder.CreateCall(LSBarrier, ARRAYREF(Value*, MBArgs, MBArgs+5)); +-#else ++#elif LLVM_VERSION < 39 + Builder.CreateFence(Release); ++#else ++ Builder.CreateFence(AtomicOrdering::Release); + #endif + // Load Flag that tells us we timed out (first byte in bc_ctx) + Value *Cond = Builder.CreateLoad(Flag, true); +@@ -970,7 +976,11 @@ class LLVMCodegen { + Twine BytecodeID; + + TargetFolder Folder; ++#if LLVM_VERSION < 39 + IRBuilder Builder; ++#else ++ IRBuilder Builder; ++#endif + + std::vector globals; + DenseMap GVoffsetMap; +diff --git a/libclamav/c++/configure.ac b/libclamav/c++/configure.ac +index d4fbfed..7c8611c 100644 +--- a/libclamav/c++/configure.ac ++++ b/libclamav/c++/configure.ac +@@ -90,14 +90,14 @@ elif test $llvmver_test -lt 290; then + elif test $llvmver_test -lt 360; then + llvmcomp="jit nativecodegen scalaropts ipo" + AC_MSG_RESULT([ok ($llvmver)]) +-elif test $llvmver_test -lt 390; then ++elif test $llvmver_test -lt 400; then + dnl LLVM 3.6.0 removed jit, so we have to use mcjit + dnl and we're using InitializeNativeTargetAsmParser, so we need the architecture specific parsers + llvmcomp="mcjit nativecodegen scalaropts ipo x86asmparser powerpcasmparser" + AC_MSG_RESULT([ok ($llvmver)]) + else + AC_MSG_RESULT([no ($llvmver)]) +- AC_MSG_ERROR([LLVM < 3.9 required, but "$llvmver"($llvmver_test) found]) ++ AC_MSG_ERROR([LLVM < 4.0 required, but "$llvmver"($llvmver_test) found]) + fi + + dnl acquire the required flags to properly link in external LLVM diff -Nru clamav-0.103.2+dfsg/debian/patches/add-support-for-system-tomsfastmath.patch clamav-0.103.5+dfsg/debian/patches/add-support-for-system-tomsfastmath.patch --- clamav-0.103.2+dfsg/debian/patches/add-support-for-system-tomsfastmath.patch 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/patches/add-support-for-system-tomsfastmath.patch 2022-01-12 20:02:01.000000000 +0000 @@ -0,0 +1,109 @@ +From 221f10ef03773457e6997f20207daae1e1284635 Mon Sep 17 00:00:00 2001 +From: Andreas Cadhalpun +Date: Wed, 11 Mar 2015 20:03:15 +0100 +Subject: add support for system tomsfastmath + +Patch-Name: add-support-for-system-tomsfastmath.patch +--- + configure.ac | 2 ++ + libclamav/Makefile.am | 6 ++++++ + libclamav/bignum.h | 6 +++++- + libclamav/xdp.c | 2 +- + m4/reorganization/libs/tomsfastmath.m4 | 12 ++++++++++++ + 5 files changed, 26 insertions(+), 2 deletions(-) + create mode 100644 m4/reorganization/libs/tomsfastmath.m4 + +diff --git a/configure.ac b/configure.ac +index 514a967..32be9e5 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -98,6 +98,7 @@ m4_include([m4/reorganization/libs/libmspack.m4]) + if test "x$use_internal_mspack" = "xno"; then + mspack_msg="External, $LIBMSPACK_CFLAGS $LIBMSPACK_LIBS" + fi ++m4_include([m4/reorganization/libs/tomsfastmath.m4]) + + AM_MAINTAINER_MODE + m4_include([m4/reorganization/libs/libz.m4]) +@@ -362,6 +363,7 @@ fi + CL_MSG_STATUS([yara ],[$enable_yara],[$enable_yara]) + CL_MSG_STATUS([fts ],[yes],[$lfs_fts_msg]) + ++CL_MSG_STATUS([tomsfastmath],[yes],[$tomsfastmath_msg]) + + # Yep, downgrading the compiler avoids the bug too: + # 4.0.x, and 4.1.0 are the known buggy versions +diff --git a/libclamav/Makefile.am b/libclamav/Makefile.am +index 526ad56..7a5185e 100644 +--- a/libclamav/Makefile.am ++++ b/libclamav/Makefile.am +@@ -588,6 +588,7 @@ libclamav_la_SOURCES += yara_arena.c \ + yara_clam.h + endif + ++if !SYSTEM_TOMSFASTMATH + libclamav_la_SOURCES += bignum.h\ + bignum_fast.h\ + tomsfastmath/addsub/fp_add.c\ +@@ -672,6 +673,11 @@ libclamav_la_SOURCES += bignum.h\ + tomsfastmath/sqr/fp_sqr_comba_small_set.c\ + tomsfastmath/sqr/fp_sqrmod.c + ++else ++libclamav_la_CFLAGS += $(TOMSFASTMATH_CFLAGS) ++libclamav_la_LIBADD += $(TOMSFASTMATH_LIBS) ++endif ++ + .PHONY2: version.h.tmp + version.c: version.h + version.h: version.h.tmp +diff --git a/libclamav/bignum.h b/libclamav/bignum.h +index 8fdc956..56dfa95 100644 +--- a/libclamav/bignum.h ++++ b/libclamav/bignum.h +@@ -1,9 +1,13 @@ + #ifndef BIGNUM_H_ + #define BIGNUM_H_ + ++#if HAVE_SYSTEM_TOMSFASTMATH ++#include ++#else + #define TFM_CHECK +- + #include "bignum_fast.h" ++#endif ++ + typedef fp_int mp_int; + #define mp_cmp fp_cmp + #define mp_toradix_n(a, b, c, d) fp_toradix_n(a, b, c, d) +diff --git a/libclamav/xdp.c b/libclamav/xdp.c +index 64a8a7b..8b82945 100644 +--- a/libclamav/xdp.c ++++ b/libclamav/xdp.c +@@ -52,7 +52,7 @@ + #include "scanners.h" + #include "conv.h" + #include "xdp.h" +-#include "bignum_fast.h" ++#include "bignum.h" + #include "filetypes.h" + + static char *dump_xdp(cli_ctx *ctx, const char *start, size_t sz); +diff --git a/m4/reorganization/libs/tomsfastmath.m4 b/m4/reorganization/libs/tomsfastmath.m4 +new file mode 100644 +index 0000000..2a821a1 +--- /dev/null ++++ b/m4/reorganization/libs/tomsfastmath.m4 +@@ -0,0 +1,12 @@ ++dnl Check for system tomsfastmath ++PKG_CHECK_MODULES([TOMSFASTMATH], [tomsfastmath], [have_system_tomsfastmath=yes], [have_system_tomsfastmath=no]) ++ ++AM_CONDITIONAL([SYSTEM_TOMSFASTMATH], [test "x$have_system_tomsfastmath" = "xyes"]) ++ ++if test "x$have_system_tomsfastmath" = "xyes"; then ++ AC_DEFINE([HAVE_SYSTEM_TOMSFASTMATH], [1], [link against system-wide tomsfastmath library]) ++ tomsfastmath_msg="External, $TOMSFASTMATH_CFLAGS $TOMSFASTMATH_LIBS" ++else ++ AC_DEFINE([HAVE_SYSTEM_TOMSFASTMATH], [0], [don't link against system-wide tomsfastmath library]) ++ tomsfastmath_msg="Internal" ++fi diff -Nru clamav-0.103.2+dfsg/debian/patches/Change-paths-in-sample-conf-file-to-match-Debian.patch clamav-0.103.5+dfsg/debian/patches/Change-paths-in-sample-conf-file-to-match-Debian.patch --- clamav-0.103.2+dfsg/debian/patches/Change-paths-in-sample-conf-file-to-match-Debian.patch 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/patches/Change-paths-in-sample-conf-file-to-match-Debian.patch 2022-01-12 20:02:01.000000000 +0000 @@ -0,0 +1,41 @@ +From 481aec8d002c57652acd0ef1fdec517178fc8124 Mon Sep 17 00:00:00 2001 +From: Scott Kitterman +Date: Mon, 10 Mar 2014 19:20:18 -0400 +Subject: Change paths in sample conf file to match Debian + +Patch-Name: Change-paths-in-sample-conf-file-to-match-Debian.patch +--- + etc/clamav-milter.conf.sample | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/etc/clamav-milter.conf.sample b/etc/clamav-milter.conf.sample +index bf46b4f..b1b533b 100644 +--- a/etc/clamav-milter.conf.sample ++++ b/etc/clamav-milter.conf.sample +@@ -64,7 +64,7 @@ Example + # also owned by root to keep other users from tampering with it. + # + # Default: disabled +-#PidFile /var/run/clamav-milter.pid ++#PidFile /var/run/clamav/clamav-milter.pid + + # Optional path to the global temporary directory. + # Default: system specific (usually /tmp or /var/tmp). +@@ -91,7 +91,7 @@ Example + # fashion. + # + # Default: no default +-#ClamdSocket tcp:scanner.mydomain:7357 ++ClamdSocket /var/run/clamav/clamd + + + ## +@@ -214,7 +214,7 @@ Example + # A full path is required. + # + # Default: disabled +-#LogFile /tmp/clamav-milter.log ++#LogFile /var/log/clamav/clamav-milter.log + + # By default the log file is locked for writing - the lock protects against + # running clamav-milter multiple times. diff -Nru clamav-0.103.2+dfsg/debian/patches/clamd_dont_depend_on_clamav_demon_socket.patch clamav-0.103.5+dfsg/debian/patches/clamd_dont_depend_on_clamav_demon_socket.patch --- clamav-0.103.2+dfsg/debian/patches/clamd_dont_depend_on_clamav_demon_socket.patch 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/patches/clamd_dont_depend_on_clamav_demon_socket.patch 2022-01-12 20:02:01.000000000 +0000 @@ -0,0 +1,35 @@ +From bc283c4ce5d40fba273569de5efa5c24877420f5 Mon Sep 17 00:00:00 2001 +From: Sebastian Andrzej Siewior +Date: Thu, 11 Aug 2016 21:54:10 +0200 +Subject: clamd: don't depend on clamav-demon.socket + +Let's try to live without it. +This should avoid the endless loop in #824042. Newer systemd have +rate-limiting on (re)starts. This rate-limiting would stop the socket +service. The only purpose for the socket activation is to get clamd +started after the initial freshclam run on installs so I think we can +live without and manually start the daemon after installation. + +Patch-Name: clamd_dont_depend_on_clamav_demon_socket.patch +Signed-off-by: Sebastian Andrzej Siewior +--- + clamd/clamav-daemon.service.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/clamd/clamav-daemon.service.in b/clamd/clamav-daemon.service.in +index a868f60..bf988d5 100644 +--- a/clamd/clamav-daemon.service.in ++++ b/clamd/clamav-daemon.service.in +@@ -1,7 +1,6 @@ + [Unit] + Description=Clam AntiVirus userspace daemon + Documentation=man:clamd(8) man:clamd.conf(5) https://docs.clamav.net/ +-Requires=clamav-daemon.socket + # Check for database existence + ConditionPathExistsGlob=@DBDIR@/main.{c[vl]d,inc} + ConditionPathExistsGlob=@DBDIR@/daily.{c[vl]d,inc} +@@ -15,4 +14,3 @@ TimeoutStartSec=420 + + [Install] + WantedBy=multi-user.target +-Also=clamav-daemon.socket diff -Nru clamav-0.103.2+dfsg/debian/patches/Fix-ck_assert_msg-call.patch clamav-0.103.5+dfsg/debian/patches/Fix-ck_assert_msg-call.patch --- clamav-0.103.2+dfsg/debian/patches/Fix-ck_assert_msg-call.patch 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/patches/Fix-ck_assert_msg-call.patch 2022-01-12 20:02:01.000000000 +0000 @@ -0,0 +1,25 @@ +From 077fbc57056cf17664c8e3ef04e06ebad2bafbc1 Mon Sep 17 00:00:00 2001 +From: Orion Poplawski +Date: Thu, 17 Sep 2020 22:26:04 -0600 +Subject: unit tests: Fix ck_assert_msg() call + +The first argument to ck_assert_msg() should be a logical condition. + +Patch-Name: Fix-ck_assert_msg-call.patch +--- + unit_tests/check_jsnorm.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/unit_tests/check_jsnorm.c b/unit_tests/check_jsnorm.c +index 114f052..9b2f4eb 100644 +--- a/unit_tests/check_jsnorm.c ++++ b/unit_tests/check_jsnorm.c +@@ -247,7 +247,7 @@ static void tokenizer_test(const char *in, const char *expected, int split) + fd = open(filename, O_RDONLY); + if (fd < 0) { + jstest_teardown(); +- ck_assert_msg("failed to open output file: %s", filename); ++ ck_assert_msg(0, "failed to open output file: %s", filename); + } + + diff_file_mem(fd, expected, len); diff -Nru clamav-0.103.2+dfsg/debian/patches/lp_1926300_multiscan_param_segfault.patch clamav-0.103.5+dfsg/debian/patches/lp_1926300_multiscan_param_segfault.patch --- clamav-0.103.2+dfsg/debian/patches/lp_1926300_multiscan_param_segfault.patch 2021-04-29 11:24:57.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/patches/lp_1926300_multiscan_param_segfault.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,255 +0,0 @@ -From 5adef25d8d0f4e5f3f2f9dc24c59beede72abf9a Mon Sep 17 00:00:00 2001 -From: "Micah Snyder (micasnyd)" -Date: Mon, 22 Mar 2021 16:52:14 -0700 -Subject: [PATCH] clamdscan: Fix --fdpass -m & ExcludePath crash - -If you set an ExcludePath regex in clamd.conf and then perform a -ClamDScan scan with --fdpass --multiscan, it will segfault. -The same issue also affects --fdpass --multiscan scans when using -ExcludePath when scanning a patch that doesn't exist. - -The issue is that the filepath isn't being passed along for the path -exclusion regex match, resulting in a NULL deref. - -This commit also fixes a possible memory leak if by duplicating the path -for the handle_entry() call _after_ the callback() runs, in case ret -isn't CL_SUCCESS and the function exits without every using the entry -structure or free'ing the copied filename. - -The above work temporarily caused a test failure in check_clamd and a -valgrind failure in clamd for the nonexistent file test due to a minor -memory leak. This made it apparent that there were a few other nearby -possible memory leaks. - -This commit fixes the above plus cleans up the error handling in clamd's -the file tree walk functions. ---- - clamd/scanner.c | 17 +++--- - libclamav/others.h | 2 +- - libclamav/others_common.c | 107 +++++++++++++++++++++++++++----------- - unit_tests/check_clamd.c | 2 +- - 4 files changed, 89 insertions(+), 39 deletions(-) - -Index: clamav-0.103.2+dfsg/clamd/scanner.c -=================================================================== ---- clamav-0.103.2+dfsg.orig/clamd/scanner.c -+++ clamav-0.103.2+dfsg/clamd/scanner.c -@@ -146,8 +146,8 @@ cl_error_t scan_callback(STATBUF *sb, ch - - if (NULL != filename) { - if (CL_SUCCESS != cli_realpath((const char *)filename, &real_filename)) { -- conn_reply_errno(scandata->conn, msg, "Failed to determine real path:"); -- logg("^Failed to determine real path for: %s\n", filename); -+ conn_reply_errno(scandata->conn, msg, "File path check failure:"); -+ logg("^File path check failure for: %s\n", filename); - logg("*Quarantine of the file may fail if file path contains symlinks.\n"); - } else { - free(filename); -@@ -180,25 +180,30 @@ cl_error_t scan_callback(STATBUF *sb, ch - else - logg("!Memory allocation failed during cli_ftw()\n"); - scandata->errors++; -+ free(filename); - return CL_EMEM; - case error_stat: -- conn_reply_errno(scandata->conn, msg, "lstat() failed:"); -- logg("^lstat() failed on: %s\n", msg); -+ conn_reply_errno(scandata->conn, msg, "File path check failure:"); -+ logg("^File path check failure on: %s\n", msg); - scandata->errors++; -+ free(filename); - return CL_SUCCESS; - case warning_skipped_dir: -- logg("^Directory recursion limit reached, skipping %s\n", -- msg); -+ logg("^Directory recursion limit reached, skipping %s\n", msg); -+ free(filename); - return CL_SUCCESS; - case warning_skipped_link: - logg("$Skipping symlink: %s\n", msg); -+ free(filename); - return CL_SUCCESS; - case warning_skipped_special: - if (msg == scandata->toplevel_path) - conn_reply(scandata->conn, msg, "Not supported file type", "ERROR"); - logg("*Not supported file type: %s\n", msg); -+ free(filename); - return CL_SUCCESS; - case visit_directory_toplev: -+ free(filename); - return CL_SUCCESS; - case visit_file: - break; -Index: clamav-0.103.2+dfsg/libclamav/others.h -=================================================================== ---- clamav-0.103.2+dfsg.orig/libclamav/others.h -+++ clamav-0.103.2+dfsg/libclamav/others.h -@@ -951,7 +951,7 @@ typedef int (*cli_ftw_pathchk)(const cha - * which one it is. - * If it is a file, it simply calls the callback once, otherwise recurses. - */ --int cli_ftw(char *base, int flags, int maxdepth, cli_ftw_cb callback, struct cli_ftw_cbdata *data, cli_ftw_pathchk pathchk); -+cl_error_t cli_ftw(char *base, int flags, int maxdepth, cli_ftw_cb callback, struct cli_ftw_cbdata *data, cli_ftw_pathchk pathchk); - - const char *cli_strerror(int errnum, char *buf, size_t len); - -Index: clamav-0.103.2+dfsg/libclamav/others_common.c -=================================================================== ---- clamav-0.103.2+dfsg.orig/libclamav/others_common.c -+++ clamav-0.103.2+dfsg/libclamav/others_common.c -@@ -574,28 +574,46 @@ static int get_filetype(const char *fnam - return stated; - } - --static int handle_filetype(const char *fname, int flags, -- STATBUF *statbuf, int *stated, enum filetype *ft, -- cli_ftw_cb callback, struct cli_ftw_cbdata *data) -+static cl_error_t handle_filetype(const char *fname, int flags, -+ STATBUF *statbuf, int *stated, enum filetype *ft, -+ cli_ftw_cb callback, struct cli_ftw_cbdata *data) - { -- int ret; -+ cl_error_t status = CL_EMEM; - - *stated = get_filetype(fname, flags, flags & CLI_FTW_NEED_STAT, statbuf, ft); - - if (*stated == -1) { -- /* we failed a stat() or lstat() */ -- ret = callback(NULL, NULL, fname, error_stat, data); -- if (ret != CL_SUCCESS) -- return ret; -+ /* we failed a stat() or lstat() */ -+ char *fname_copy = cli_strdup(fname); -+ if (NULL == fname_copy) { -+ goto done; -+ } -+ -+ status = callback(NULL, fname_copy, fname, error_stat, data); -+ if (status != CL_SUCCESS) { -+ goto done; -+ } - *ft = ft_unknown; - } else if (*ft == ft_skipped_link || *ft == ft_skipped_special) { - /* skipped filetype */ -- ret = callback(stated ? statbuf : NULL, NULL, fname, -- *ft == ft_skipped_link ? warning_skipped_link : warning_skipped_special, data); -- if (ret != CL_SUCCESS) -- return ret; -+ char *fname_copy = cli_strdup(fname); -+ if (NULL == fname_copy) { -+ goto done; -+ } -+ -+ status = callback(stated ? statbuf : NULL, -+ fname_copy, -+ fname, -+ *ft == ft_skipped_link ? warning_skipped_link : warning_skipped_special, -+ data); -+ if (status != CL_SUCCESS) -+ goto done; - } -- return CL_SUCCESS; -+ -+ status = CL_SUCCESS; -+ -+done: -+ return status; - } - - static int cli_ftw_dir(const char *dirname, int flags, int maxdepth, cli_ftw_cb callback, struct cli_ftw_cbdata *data, cli_ftw_pathchk pathchk); -@@ -608,13 +626,14 @@ static int handle_entry(struct dirent_da - } - } - --int cli_ftw(char *path, int flags, int maxdepth, cli_ftw_cb callback, struct cli_ftw_cbdata *data, cli_ftw_pathchk pathchk) -+cl_error_t cli_ftw(char *path, int flags, int maxdepth, cli_ftw_cb callback, struct cli_ftw_cbdata *data, cli_ftw_pathchk pathchk) - { -+ cl_error_t status = CL_EMEM; - STATBUF statbuf; - enum filetype ft = ft_unknown; - struct dirent_data entry; -- int stated = 0; -- int ret; -+ int stated = 0; -+ char *path_copy = NULL; - - if (((flags & CLI_FTW_TRIM_SLASHES) || pathchk) && path[0] && path[1]) { - char *pathend; -@@ -627,23 +646,49 @@ int cli_ftw(char *path, int flags, int m - while (pathend > path && pathend[-1] == *PATHSEP) --pathend; - *pathend = '\0'; - } -- if (pathchk && pathchk(path, data) == 1) -- return CL_SUCCESS; -- ret = handle_filetype(path, flags, &statbuf, &stated, &ft, callback, data); -- if (ret != CL_SUCCESS) -- return ret; -- if (ft_skipped(ft)) -- return CL_SUCCESS; -- entry.statbuf = stated ? &statbuf : NULL; -- entry.is_dir = ft == ft_directory; -- entry.filename = entry.is_dir ? NULL : strdup(path); -- entry.dirname = entry.is_dir ? path : NULL; -+ -+ if (pathchk && pathchk(path, data) == 1) { -+ status = CL_SUCCESS; -+ goto done; -+ } -+ -+ status = handle_filetype(path, flags, &statbuf, &stated, &ft, callback, data); -+ if (status != CL_SUCCESS) { -+ goto done; -+ } -+ -+ if (ft_skipped(ft)) { -+ status = CL_SUCCESS; -+ goto done; -+ } -+ -+ entry.statbuf = stated ? &statbuf : NULL; -+ entry.is_dir = ft == ft_directory; -+ - if (entry.is_dir) { -- ret = callback(entry.statbuf, NULL, path, visit_directory_toplev, data); -- if (ret != CL_SUCCESS) -- return ret; -+ path_copy = cli_strdup(path); -+ if (NULL == path_copy) { -+ goto done; -+ } -+ -+ status = callback(entry.statbuf, path_copy, path, visit_directory_toplev, data); -+ if (status != CL_SUCCESS) { -+ goto done; -+ } -+ } -+ -+ path_copy = cli_strdup(path); -+ if (NULL == path_copy) { -+ goto done; - } -- return handle_entry(&entry, flags, maxdepth, callback, data, pathchk); -+ -+ entry.filename = entry.is_dir ? NULL : path_copy; -+ entry.dirname = entry.is_dir ? path : NULL; -+ -+ status = handle_entry(&entry, flags, maxdepth, callback, data, pathchk); -+ -+done: -+ return status; - } - - static int cli_ftw_dir(const char *dirname, int flags, int maxdepth, cli_ftw_cb callback, struct cli_ftw_cbdata *data, cli_ftw_pathchk pathchk) -Index: clamav-0.103.2+dfsg/unit_tests/check_clamd.c -=================================================================== ---- clamav-0.103.2+dfsg.orig/unit_tests/check_clamd.c -+++ clamav-0.103.2+dfsg/unit_tests/check_clamd.c -@@ -121,7 +121,7 @@ static void conn_teardown(void) - - #define NONEXISTENT "/nonexistent\vfilename" - --#define NONEXISTENT_REPLY NONEXISTENT ": lstat() failed: No such file or directory. ERROR" -+#define NONEXISTENT_REPLY NONEXISTENT ": File path check failure: No such file or directory. ERROR" - - #define ACCDENIED BUILDDIR "/accdenied" - #define ACCDENIED_REPLY ACCDENIED ": Access denied. ERROR" diff -Nru clamav-0.103.2+dfsg/debian/patches/series clamav-0.103.5+dfsg/debian/patches/series --- clamav-0.103.2+dfsg/debian/patches/series 2021-04-29 11:24:57.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/patches/series 2022-01-12 20:02:01.000000000 +0000 @@ -1,8 +1,7 @@ -0001-Change-paths-in-sample-conf-file-to-match-Debian.patch -0002-add-support-for-system-tomsfastmath.patch -0003-clamd-don-t-depend-on-clamav-demon.socket.patch -0004-Add-support-for-LLVM-3.7.patch -0005-Add-support-for-LLVM-3.8.patch -0006-Add-support-for-LLVM-3.9.patch -0007-unit-tests-Fix-ck_assert_msg-call.patch -lp_1926300_multiscan_param_segfault.patch +Change-paths-in-sample-conf-file-to-match-Debian.patch +add-support-for-system-tomsfastmath.patch +clamd_dont_depend_on_clamav_demon_socket.patch +Add-support-for-LLVM-3.7.patch +Add-support-for-LLVM-3.8.patch +Add-support-for-LLVM-3.9.patch +Fix-ck_assert_msg-call.patch diff -Nru clamav-0.103.2+dfsg/debian/po/cs.po clamav-0.103.5+dfsg/debian/po/cs.po --- clamav-0.103.2+dfsg/debian/po/cs.po 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/po/cs.po 2022-01-17 13:19:19.000000000 +0000 @@ -15,7 +15,7 @@ msgstr "" "Project-Id-Version: clamav\n" "Report-Msgid-Bugs-To: clamav@packages.debian.org\n" -"POT-Creation-Date: 2021-02-21 15:05+0000\n" +"POT-Creation-Date: 2022-01-17 08:27-0500\n" "PO-Revision-Date: 2010-10-13 19:12+0200\n" "Last-Translator: Miroslav Kure \n" "Language-Team: Czech \n" @@ -232,41 +232,19 @@ #. Description #: ../clamav-freshclam.templates:9001 #, fuzzy -#| msgid "Do you want to enable mail scanning?" -msgid "Do you want to enable support for Google Safe Browsing?" -msgstr "Chcete povolit prohledávání pošty?" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:9001 -msgid "" -"When activated for the first time, freshclam will download a new database " -"file (safebrowsing.cvd) which will be automatically loaded by clamd and " -"clamscan during the next reload, provided that the heuristic phishing " -"detection is turned on. This database includes information about websites " -"that may be phishing sites or possible sources of malware. When using this " -"option, it's mandatory to run freshclam at least every 30 minutes. Freshclam " -"uses the ClamAV's mirror infrastructure to distribute the database and its " -"updates but all the contents are provided under Google's terms of use." -msgstr "" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:10001 -#, fuzzy #| msgid "Do you want to load bytecode from the database?" msgid "Do you want to download the bytecode database?" msgstr "Chcete z databáze nahrávat bajtkód?" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "Private mirror for freshclam:" msgstr "" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "" "This option allows you to easily point freshclam to private mirrors. If " "PrivateMirror is set, freshclam does not attempt to use DNS to determine " @@ -280,7 +258,7 @@ #. Type: boolean #. Description -#: ../clamav-freshclam.templates:12001 ../clamav-daemon.templates:22001 +#: ../clamav-freshclam.templates:11001 ../clamav-daemon.templates:22001 #: ../clamav-milter.templates:32001 #, fuzzy #| msgid "Do you want to enable mail scanning?" @@ -1488,6 +1466,11 @@ msgstr "" #, fuzzy +#~| msgid "Do you want to enable mail scanning?" +#~ msgid "Do you want to enable support for Google Safe Browsing?" +#~ msgstr "Chcete povolit prohledávání pošty?" + +#, fuzzy #~| msgid "Do you want to enable archive scanning?" #~ msgid "Do you want to enable on-access scanning?" #~ msgstr "Chcete povolit prohledávání archivů?" diff -Nru clamav-0.103.2+dfsg/debian/po/da.po clamav-0.103.5+dfsg/debian/po/da.po --- clamav-0.103.2+dfsg/debian/po/da.po 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/po/da.po 2022-01-17 13:19:19.000000000 +0000 @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: clamav\n" "Report-Msgid-Bugs-To: clamav@packages.debian.org\n" -"POT-Creation-Date: 2021-02-21 15:05+0000\n" +"POT-Creation-Date: 2022-01-17 08:27-0500\n" "PO-Revision-Date: 2014-07-06 12:42+0000\n" "Last-Translator: Joe Hansen \n" "Language-Team: Danish \n" @@ -229,47 +229,18 @@ #. Type: boolean #. Description #: ../clamav-freshclam.templates:9001 -msgid "Do you want to enable support for Google Safe Browsing?" -msgstr "Ønsker du at aktivere understøttelse for Google Safe Browsing?" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:9001 -msgid "" -"When activated for the first time, freshclam will download a new database " -"file (safebrowsing.cvd) which will be automatically loaded by clamd and " -"clamscan during the next reload, provided that the heuristic phishing " -"detection is turned on. This database includes information about websites " -"that may be phishing sites or possible sources of malware. When using this " -"option, it's mandatory to run freshclam at least every 30 minutes. Freshclam " -"uses the ClamAV's mirror infrastructure to distribute the database and its " -"updates but all the contents are provided under Google's terms of use." -msgstr "" -"Når aktiveret for første gang vil freshclam hente en ny databasefil " -"(safebrowsing.cvd), som vil blive indlæst automatisk af clamd og clamscan " -"under den næste genindlæsning, så længe at den heuristiske phishing-" -"detektering er tændt. Denne database inkluderer information om " -"internetsider, som kan være phishing-sider eller mulige kilder til malware. " -"Når du bruger denne indstilling, er det krævet at du kører freshclam mindst " -"hver 30. minut. Freshclam bruger ClamAV's spejlinfrastruktur til at " -"distribuere databasen og dets opdateringer, men alt indhold leveres under " -"Googles betingelser for brug." - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:10001 msgid "Do you want to download the bytecode database?" msgstr "Vil du hente bytecode-databasen?" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "Private mirror for freshclam:" msgstr "Privat spejl for freshclam:" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "" "This option allows you to easily point freshclam to private mirrors. If " "PrivateMirror is set, freshclam does not attempt to use DNS to determine " @@ -291,7 +262,7 @@ #. Type: boolean #. Description -#: ../clamav-freshclam.templates:12001 ../clamav-daemon.templates:22001 +#: ../clamav-freshclam.templates:11001 ../clamav-daemon.templates:22001 #: ../clamav-milter.templates:32001 msgid "Do you want to enable log rotation?" msgstr "Ønsker du at aktivere logrotation?" @@ -1535,6 +1506,30 @@ "Bemærk: Selvom det sikkert er en god ide at aktivere denne indstilling, så " "er standardværdien deaktiveret på grund af arv (legacy)." +#~ msgid "Do you want to enable support for Google Safe Browsing?" +#~ msgstr "Ønsker du at aktivere understøttelse for Google Safe Browsing?" + +#~ msgid "" +#~ "When activated for the first time, freshclam will download a new database " +#~ "file (safebrowsing.cvd) which will be automatically loaded by clamd and " +#~ "clamscan during the next reload, provided that the heuristic phishing " +#~ "detection is turned on. This database includes information about websites " +#~ "that may be phishing sites or possible sources of malware. When using " +#~ "this option, it's mandatory to run freshclam at least every 30 minutes. " +#~ "Freshclam uses the ClamAV's mirror infrastructure to distribute the " +#~ "database and its updates but all the contents are provided under Google's " +#~ "terms of use." +#~ msgstr "" +#~ "Når aktiveret for første gang vil freshclam hente en ny databasefil " +#~ "(safebrowsing.cvd), som vil blive indlæst automatisk af clamd og clamscan " +#~ "under den næste genindlæsning, så længe at den heuristiske phishing-" +#~ "detektering er tændt. Denne database inkluderer information om " +#~ "internetsider, som kan være phishing-sider eller mulige kilder til " +#~ "malware. Når du bruger denne indstilling, er det krævet at du kører " +#~ "freshclam mindst hver 30. minut. Freshclam bruger ClamAV's " +#~ "spejlinfrastruktur til at distribuere databasen og dets opdateringer, men " +#~ "alt indhold leveres under Googles betingelser for brug." + #~ msgid "Do you want to enable on-access scanning?" #~ msgstr "Vil du aktivere ved adgang-skanning?" diff -Nru clamav-0.103.2+dfsg/debian/po/de.po clamav-0.103.5+dfsg/debian/po/de.po --- clamav-0.103.2+dfsg/debian/po/de.po 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/po/de.po 2022-01-17 13:19:19.000000000 +0000 @@ -14,7 +14,7 @@ msgstr "" "Project-Id-Version: clamav_0.98.4~rc1+dfsg-3_de\n" "Report-Msgid-Bugs-To: clamav@packages.debian.org\n" -"POT-Creation-Date: 2021-02-21 15:05+0000\n" +"POT-Creation-Date: 2022-01-17 08:27-0500\n" "PO-Revision-Date: 2014-06-12 10:23+0100\n" "Last-Translator: Mario Blättermann \n" "Language-Team: Deutsch \n" @@ -241,48 +241,18 @@ #. Type: boolean #. Description #: ../clamav-freshclam.templates:9001 -msgid "Do you want to enable support for Google Safe Browsing?" -msgstr "Soll die Unterstützung für Google Safe Browsing aktiviert werden?" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:9001 -msgid "" -"When activated for the first time, freshclam will download a new database " -"file (safebrowsing.cvd) which will be automatically loaded by clamd and " -"clamscan during the next reload, provided that the heuristic phishing " -"detection is turned on. This database includes information about websites " -"that may be phishing sites or possible sources of malware. When using this " -"option, it's mandatory to run freshclam at least every 30 minutes. Freshclam " -"uses the ClamAV's mirror infrastructure to distribute the database and its " -"updates but all the contents are provided under Google's terms of use." -msgstr "" -"Wenn dies zum ersten Mal aktiviert wird, dann lädt Freshclam eine neue " -"Datenbankdatei (safebrowsing.cvd) herunter, die automatisch von Clamd und " -"Clamscan beim nächsten erneuten Laden berücksichtigt wird, sofern die " -"heuristische Phishing-Erkennung eingeschaltet ist. Diese Datenbank enthält " -"Informationen über Webseiten, die Phishing-Seiten oder mögliche Malware-" -"Quellen sein könnten. Bei Verwendung dieser Option ist es vorgeschrieben, " -"dass Freshclam mindestens alle 30 Minuten ausgeführt wird. Freshclam " -"verwendet die Spiegel-Infrastruktur von ClamAV, um die Datenbank und deren " -"Aktualisierungen zu verteilen, aber alle Inhalte werden unter den " -"Nutzungsbedingungen von Google bereitgestellt." - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:10001 msgid "Do you want to download the bytecode database?" msgstr "Wollen Sie die Bytecode-Datenbank herunterladen?" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "Private mirror for freshclam:" msgstr "Privater Spiegel für Freshclam:" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "" "This option allows you to easily point freshclam to private mirrors. If " "PrivateMirror is set, freshclam does not attempt to use DNS to determine " @@ -306,7 +276,7 @@ #. Type: boolean #. Description -#: ../clamav-freshclam.templates:12001 ../clamav-daemon.templates:22001 +#: ../clamav-freshclam.templates:11001 ../clamav-daemon.templates:22001 #: ../clamav-milter.templates:32001 msgid "Do you want to enable log rotation?" msgstr "Soll das Rotieren der Protokolldateien aktiviert werden?" @@ -1578,6 +1548,31 @@ "einzuschalten, ist der Standardwert aus historischen Gründen auf »Aus« " "geschaltet." +#~ msgid "Do you want to enable support for Google Safe Browsing?" +#~ msgstr "Soll die Unterstützung für Google Safe Browsing aktiviert werden?" + +#~ msgid "" +#~ "When activated for the first time, freshclam will download a new database " +#~ "file (safebrowsing.cvd) which will be automatically loaded by clamd and " +#~ "clamscan during the next reload, provided that the heuristic phishing " +#~ "detection is turned on. This database includes information about websites " +#~ "that may be phishing sites or possible sources of malware. When using " +#~ "this option, it's mandatory to run freshclam at least every 30 minutes. " +#~ "Freshclam uses the ClamAV's mirror infrastructure to distribute the " +#~ "database and its updates but all the contents are provided under Google's " +#~ "terms of use." +#~ msgstr "" +#~ "Wenn dies zum ersten Mal aktiviert wird, dann lädt Freshclam eine neue " +#~ "Datenbankdatei (safebrowsing.cvd) herunter, die automatisch von Clamd und " +#~ "Clamscan beim nächsten erneuten Laden berücksichtigt wird, sofern die " +#~ "heuristische Phishing-Erkennung eingeschaltet ist. Diese Datenbank " +#~ "enthält Informationen über Webseiten, die Phishing-Seiten oder mögliche " +#~ "Malware-Quellen sein könnten. Bei Verwendung dieser Option ist es " +#~ "vorgeschrieben, dass Freshclam mindestens alle 30 Minuten ausgeführt " +#~ "wird. Freshclam verwendet die Spiegel-Infrastruktur von ClamAV, um die " +#~ "Datenbank und deren Aktualisierungen zu verteilen, aber alle Inhalte " +#~ "werden unter den Nutzungsbedingungen von Google bereitgestellt." + #~ msgid "Do you want to enable on-access scanning?" #~ msgstr "Soll die Bei-Zugriff-Überprüfung aktiviert werden?" diff -Nru clamav-0.103.2+dfsg/debian/po/es.po clamav-0.103.5+dfsg/debian/po/es.po --- clamav-0.103.2+dfsg/debian/po/es.po 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/po/es.po 2022-01-17 13:19:19.000000000 +0000 @@ -42,7 +42,7 @@ msgstr "" "Project-Id-Version: clamav 0.96+dfsg-4\n" "Report-Msgid-Bugs-To: clamav@packages.debian.org\n" -"POT-Creation-Date: 2021-02-21 15:05+0000\n" +"POT-Creation-Date: 2022-01-17 08:27-0500\n" "PO-Revision-Date: 2014-12-05 18:25+0100\n" "Last-Translator: Javier Fernández-Sanguino Peña \n" "Language-Team: Debian Spanish \n" @@ -288,48 +288,18 @@ #. Type: boolean #. Description #: ../clamav-freshclam.templates:9001 -msgid "Do you want to enable support for Google Safe Browsing?" -msgstr "¿Desea activar el soporte para la Navegación Segura de Google?" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:9001 -msgid "" -"When activated for the first time, freshclam will download a new database " -"file (safebrowsing.cvd) which will be automatically loaded by clamd and " -"clamscan during the next reload, provided that the heuristic phishing " -"detection is turned on. This database includes information about websites " -"that may be phishing sites or possible sources of malware. When using this " -"option, it's mandatory to run freshclam at least every 30 minutes. Freshclam " -"uses the ClamAV's mirror infrastructure to distribute the database and its " -"updates but all the contents are provided under Google's terms of use." -msgstr "" -"Cuando se activa por primera vez, freshclam descargará un nuevo archivo de " -"base de datos (safebrowsing.cvd) que se cargará automáticamente por clamd y " -"clamscan en el siguiente reinicio. Esto se realizará siempre que la " -"detección heurística de phishing esté activa. Esta base de datos incluye " -"información de sitios web que pueden ser sitios de robo de credenciales " -"(«phishing») o posibles fuentes de programas maliciosos. Es obligatorio " -"ejecutar freshclam al menos cada 30 minutos si se utiliza esta opción. " -"Freshclam utiliza la infraestructura de réplicas de ClamAV para distribuir " -"la base de datos y sus actualizaciones, pero todos los contenidos se ofrecen " -"bajo los términos de uso de Google." - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:10001 msgid "Do you want to download the bytecode database?" msgstr "¿Desea descargar la base de datos de bytecodes?" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "Private mirror for freshclam:" msgstr "Réplica privada para freshclam:" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "" "This option allows you to easily point freshclam to private mirrors. If " "PrivateMirror is set, freshclam does not attempt to use DNS to determine " @@ -353,7 +323,7 @@ #. Type: boolean #. Description -#: ../clamav-freshclam.templates:12001 ../clamav-daemon.templates:22001 +#: ../clamav-freshclam.templates:11001 ../clamav-daemon.templates:22001 #: ../clamav-milter.templates:32001 msgid "Do you want to enable log rotation?" msgstr "¿Desea activar la rotación de logs?" @@ -1620,6 +1590,31 @@ "Nota: Aunque habitualmente sea una buena idea habilitar esta opción, por " "omisión se deshabilita por motivos históricos." +#~ msgid "Do you want to enable support for Google Safe Browsing?" +#~ msgstr "¿Desea activar el soporte para la Navegación Segura de Google?" + +#~ msgid "" +#~ "When activated for the first time, freshclam will download a new database " +#~ "file (safebrowsing.cvd) which will be automatically loaded by clamd and " +#~ "clamscan during the next reload, provided that the heuristic phishing " +#~ "detection is turned on. This database includes information about websites " +#~ "that may be phishing sites or possible sources of malware. When using " +#~ "this option, it's mandatory to run freshclam at least every 30 minutes. " +#~ "Freshclam uses the ClamAV's mirror infrastructure to distribute the " +#~ "database and its updates but all the contents are provided under Google's " +#~ "terms of use." +#~ msgstr "" +#~ "Cuando se activa por primera vez, freshclam descargará un nuevo archivo " +#~ "de base de datos (safebrowsing.cvd) que se cargará automáticamente por " +#~ "clamd y clamscan en el siguiente reinicio. Esto se realizará siempre que " +#~ "la detección heurística de phishing esté activa. Esta base de datos " +#~ "incluye información de sitios web que pueden ser sitios de robo de " +#~ "credenciales («phishing») o posibles fuentes de programas maliciosos. Es " +#~ "obligatorio ejecutar freshclam al menos cada 30 minutos si se utiliza " +#~ "esta opción. Freshclam utiliza la infraestructura de réplicas de ClamAV " +#~ "para distribuir la base de datos y sus actualizaciones, pero todos los " +#~ "contenidos se ofrecen bajo los términos de uso de Google." + #~ msgid "Do you want to enable on-access scanning?" #~ msgstr "¿Desea activar el análisis en el momento del acceso?" diff -Nru clamav-0.103.2+dfsg/debian/po/eu.po clamav-0.103.5+dfsg/debian/po/eu.po --- clamav-0.103.2+dfsg/debian/po/eu.po 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/po/eu.po 2022-01-17 13:19:19.000000000 +0000 @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: clamav-eu\n" "Report-Msgid-Bugs-To: clamav@packages.debian.org\n" -"POT-Creation-Date: 2021-02-21 15:05+0000\n" +"POT-Creation-Date: 2022-01-17 08:27-0500\n" "PO-Revision-Date: 2008-09-24 11:03+0200\n" "Last-Translator: Piarres Beobide \n" "Language-Team: Euskara \n" @@ -228,41 +228,19 @@ #. Description #: ../clamav-freshclam.templates:9001 #, fuzzy -#| msgid "Do you want to enable mail scanning?" -msgid "Do you want to enable support for Google Safe Browsing?" -msgstr "Posta eskaneatzea gaitu nahi al duzu?" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:9001 -msgid "" -"When activated for the first time, freshclam will download a new database " -"file (safebrowsing.cvd) which will be automatically loaded by clamd and " -"clamscan during the next reload, provided that the heuristic phishing " -"detection is turned on. This database includes information about websites " -"that may be phishing sites or possible sources of malware. When using this " -"option, it's mandatory to run freshclam at least every 30 minutes. Freshclam " -"uses the ClamAV's mirror infrastructure to distribute the database and its " -"updates but all the contents are provided under Google's terms of use." -msgstr "" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:10001 -#, fuzzy #| msgid "Do you want to log time information with each message?" msgid "Do you want to download the bytecode database?" msgstr "Mezu bakoitzarekin ordu informazioa erregistratzea nahi duzu?" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "Private mirror for freshclam:" msgstr "" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "" "This option allows you to easily point freshclam to private mirrors. If " "PrivateMirror is set, freshclam does not attempt to use DNS to determine " @@ -276,7 +254,7 @@ #. Type: boolean #. Description -#: ../clamav-freshclam.templates:12001 ../clamav-daemon.templates:22001 +#: ../clamav-freshclam.templates:11001 ../clamav-daemon.templates:22001 #: ../clamav-milter.templates:32001 #, fuzzy #| msgid "Do you want to enable mail scanning?" @@ -1430,6 +1408,11 @@ msgstr "" #, fuzzy +#~| msgid "Do you want to enable mail scanning?" +#~ msgid "Do you want to enable support for Google Safe Browsing?" +#~ msgstr "Posta eskaneatzea gaitu nahi al duzu?" + +#, fuzzy #~| msgid "Do you want to enable archive scanning?" #~ msgid "Do you want to enable on-access scanning?" #~ msgstr "Artxibo eskaneatzea gaitu nahi al duzu?" diff -Nru clamav-0.103.2+dfsg/debian/po/fi.po clamav-0.103.5+dfsg/debian/po/fi.po --- clamav-0.103.2+dfsg/debian/po/fi.po 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/po/fi.po 2022-01-17 13:19:19.000000000 +0000 @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: clamav\n" "Report-Msgid-Bugs-To: clamav@packages.debian.org\n" -"POT-Creation-Date: 2021-02-21 15:05+0000\n" +"POT-Creation-Date: 2022-01-17 08:27-0500\n" "PO-Revision-Date: 2009-06-22 22:47+0300\n" "Last-Translator: Esko Arajärvi \n" "Language-Team: Finnish \n" @@ -224,41 +224,19 @@ #. Description #: ../clamav-freshclam.templates:9001 #, fuzzy -#| msgid "Do you want to enable mail scanning?" -msgid "Do you want to enable support for Google Safe Browsing?" -msgstr "Otetaanko käyttöön sähköpostien tutkinta?" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:9001 -msgid "" -"When activated for the first time, freshclam will download a new database " -"file (safebrowsing.cvd) which will be automatically loaded by clamd and " -"clamscan during the next reload, provided that the heuristic phishing " -"detection is turned on. This database includes information about websites " -"that may be phishing sites or possible sources of malware. When using this " -"option, it's mandatory to run freshclam at least every 30 minutes. Freshclam " -"uses the ClamAV's mirror infrastructure to distribute the database and its " -"updates but all the contents are provided under Google's terms of use." -msgstr "" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:10001 -#, fuzzy #| msgid "Do you want to log time information with each message?" msgid "Do you want to download the bytecode database?" msgstr "Haluatko tallentaa aikatiedon jokaisen viestin yhteydessä?" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "Private mirror for freshclam:" msgstr "" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "" "This option allows you to easily point freshclam to private mirrors. If " "PrivateMirror is set, freshclam does not attempt to use DNS to determine " @@ -272,7 +250,7 @@ #. Type: boolean #. Description -#: ../clamav-freshclam.templates:12001 ../clamav-daemon.templates:22001 +#: ../clamav-freshclam.templates:11001 ../clamav-daemon.templates:22001 #: ../clamav-milter.templates:32001 #, fuzzy #| msgid "Do you want to enable mail scanning?" @@ -1499,6 +1477,11 @@ msgstr "" #, fuzzy +#~| msgid "Do you want to enable mail scanning?" +#~ msgid "Do you want to enable support for Google Safe Browsing?" +#~ msgstr "Otetaanko käyttöön sähköpostien tutkinta?" + +#, fuzzy #~| msgid "Do you want to enable archive scanning?" #~ msgid "Do you want to enable on-access scanning?" #~ msgstr "Otetaanko käyttöön arkistojen tutkinta?" diff -Nru clamav-0.103.2+dfsg/debian/po/fr.po clamav-0.103.5+dfsg/debian/po/fr.po --- clamav-0.103.2+dfsg/debian/po/fr.po 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/po/fr.po 2022-01-17 13:19:19.000000000 +0000 @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: clamav_0.98.4\n" "Report-Msgid-Bugs-To: clamav@packages.debian.org\n" -"POT-Creation-Date: 2021-02-21 15:05+0000\n" +"POT-Creation-Date: 2022-01-17 08:27-0500\n" "PO-Revision-Date: 2014-06-10 14:21+0100\n" "Last-Translator: Julien Patriarca \n" "Language-Team: FRENCH \n" @@ -240,36 +240,6 @@ #. Type: boolean #. Description #: ../clamav-freshclam.templates:9001 -msgid "Do you want to enable support for Google Safe Browsing?" -msgstr "Souhaitez-vous activer le support de « Google Safe Browsing » ?" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:9001 -msgid "" -"When activated for the first time, freshclam will download a new database " -"file (safebrowsing.cvd) which will be automatically loaded by clamd and " -"clamscan during the next reload, provided that the heuristic phishing " -"detection is turned on. This database includes information about websites " -"that may be phishing sites or possible sources of malware. When using this " -"option, it's mandatory to run freshclam at least every 30 minutes. Freshclam " -"uses the ClamAV's mirror infrastructure to distribute the database and its " -"updates but all the contents are provided under Google's terms of use." -msgstr "" -"Lors du premier lancement, freshclam téléchargera un nouveau fichier de base " -"de données (safebrowsing.cvd) qui sera automatiquement chargé par clamd et " -"clamscan lors du prochain rechargement, si la détection d'hameçonnage " -"heuristique est activée. Cette base de données contient des informations à " -"propos de sites internet qui pourraient être des sites d'hameçonnage ou de " -"possibles sources de logiciels malveillants. Lorsque vous utilisez cette " -"option, il est obligatoire de lancer freshclam au moins toutes les " -"30 minutes. Freshclam utilise l'infrastructure de miroirs de ClamAV pour " -"distribuer la base de données et ses mises à jour mais tout le contenu est " -"fourni selon les conditions d'utilisation de Google." - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:10001 msgid "Do you want to download the bytecode database?" msgstr "" "Souhaitez-vous télécharger la base de données du code intermédiaire " @@ -277,13 +247,13 @@ #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "Private mirror for freshclam:" msgstr "Miroir privé pour freshclam :" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "" "This option allows you to easily point freshclam to private mirrors. If " "PrivateMirror is set, freshclam does not attempt to use DNS to determine " @@ -307,7 +277,7 @@ #. Type: boolean #. Description -#: ../clamav-freshclam.templates:12001 ../clamav-daemon.templates:22001 +#: ../clamav-freshclam.templates:11001 ../clamav-daemon.templates:22001 #: ../clamav-milter.templates:32001 msgid "Do you want to enable log rotation?" msgstr "Souhaitez-vous activer la rotation des journaux ?" @@ -1578,6 +1548,32 @@ "Bien que ce soit probablement une bonne idée d'activer cette option, pour " "des raisons historiques, la valeur est désactivée par défaut." +#~ msgid "Do you want to enable support for Google Safe Browsing?" +#~ msgstr "Souhaitez-vous activer le support de « Google Safe Browsing » ?" + +#~ msgid "" +#~ "When activated for the first time, freshclam will download a new database " +#~ "file (safebrowsing.cvd) which will be automatically loaded by clamd and " +#~ "clamscan during the next reload, provided that the heuristic phishing " +#~ "detection is turned on. This database includes information about websites " +#~ "that may be phishing sites or possible sources of malware. When using " +#~ "this option, it's mandatory to run freshclam at least every 30 minutes. " +#~ "Freshclam uses the ClamAV's mirror infrastructure to distribute the " +#~ "database and its updates but all the contents are provided under Google's " +#~ "terms of use." +#~ msgstr "" +#~ "Lors du premier lancement, freshclam téléchargera un nouveau fichier de " +#~ "base de données (safebrowsing.cvd) qui sera automatiquement chargé par " +#~ "clamd et clamscan lors du prochain rechargement, si la détection " +#~ "d'hameçonnage heuristique est activée. Cette base de données contient des " +#~ "informations à propos de sites internet qui pourraient être des sites " +#~ "d'hameçonnage ou de possibles sources de logiciels malveillants. Lorsque " +#~ "vous utilisez cette option, il est obligatoire de lancer freshclam au " +#~ "moins toutes les 30 minutes. Freshclam utilise l'infrastructure de " +#~ "miroirs de ClamAV pour distribuer la base de données et ses mises à jour " +#~ "mais tout le contenu est fourni selon les conditions d'utilisation de " +#~ "Google." + #~ msgid "Do you want to enable on-access scanning?" #~ msgstr "Souhaitez-vous activer la vérification des archives ?" diff -Nru clamav-0.103.2+dfsg/debian/po/gl.po clamav-0.103.5+dfsg/debian/po/gl.po --- clamav-0.103.2+dfsg/debian/po/gl.po 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/po/gl.po 2022-01-17 13:19:19.000000000 +0000 @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: clamav\n" "Report-Msgid-Bugs-To: clamav@packages.debian.org\n" -"POT-Creation-Date: 2021-02-21 15:05+0000\n" +"POT-Creation-Date: 2022-01-17 08:27-0500\n" "PO-Revision-Date: 2009-05-06 21:40+0200\n" "Last-Translator: marce villarino \n" "Language-Team: Galician \n" @@ -231,41 +231,19 @@ #. Description #: ../clamav-freshclam.templates:9001 #, fuzzy -#| msgid "Do you want to enable mail scanning?" -msgid "Do you want to enable support for Google Safe Browsing?" -msgstr "Desexa activar o exame do correo?" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:9001 -msgid "" -"When activated for the first time, freshclam will download a new database " -"file (safebrowsing.cvd) which will be automatically loaded by clamd and " -"clamscan during the next reload, provided that the heuristic phishing " -"detection is turned on. This database includes information about websites " -"that may be phishing sites or possible sources of malware. When using this " -"option, it's mandatory to run freshclam at least every 30 minutes. Freshclam " -"uses the ClamAV's mirror infrastructure to distribute the database and its " -"updates but all the contents are provided under Google's terms of use." -msgstr "" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:10001 -#, fuzzy #| msgid "Do you want to log time information with each message?" msgid "Do you want to download the bytecode database?" msgstr "Desexa rexistrar a hora con cada mensaxe?" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "Private mirror for freshclam:" msgstr "" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "" "This option allows you to easily point freshclam to private mirrors. If " "PrivateMirror is set, freshclam does not attempt to use DNS to determine " @@ -279,7 +257,7 @@ #. Type: boolean #. Description -#: ../clamav-freshclam.templates:12001 ../clamav-daemon.templates:22001 +#: ../clamav-freshclam.templates:11001 ../clamav-daemon.templates:22001 #: ../clamav-milter.templates:32001 #, fuzzy #| msgid "Do you want to enable mail scanning?" @@ -1511,6 +1489,11 @@ msgstr "" #, fuzzy +#~| msgid "Do you want to enable mail scanning?" +#~ msgid "Do you want to enable support for Google Safe Browsing?" +#~ msgstr "Desexa activar o exame do correo?" + +#, fuzzy #~| msgid "Do you want to enable archive scanning?" #~ msgid "Do you want to enable on-access scanning?" #~ msgstr "Desexa activar o exame de arquivos?" diff -Nru clamav-0.103.2+dfsg/debian/po/it.po clamav-0.103.5+dfsg/debian/po/it.po --- clamav-0.103.2+dfsg/debian/po/it.po 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/po/it.po 2022-01-17 13:19:19.000000000 +0000 @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: clamav 0.96.1+dfsg-3 \n" "Report-Msgid-Bugs-To: clamav@packages.debian.org\n" -"POT-Creation-Date: 2021-02-21 15:05+0000\n" +"POT-Creation-Date: 2022-01-17 08:27-0500\n" "PO-Revision-Date: 2011-02-19 15:31+0100\n" "Last-Translator: Luca Monducci \n" "Language-Team: Italian \n" @@ -231,41 +231,19 @@ #. Description #: ../clamav-freshclam.templates:9001 #, fuzzy -#| msgid "Do you want to enable mail scanning?" -msgid "Do you want to enable support for Google Safe Browsing?" -msgstr "Attivare l'analisi delle email?" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:9001 -msgid "" -"When activated for the first time, freshclam will download a new database " -"file (safebrowsing.cvd) which will be automatically loaded by clamd and " -"clamscan during the next reload, provided that the heuristic phishing " -"detection is turned on. This database includes information about websites " -"that may be phishing sites or possible sources of malware. When using this " -"option, it's mandatory to run freshclam at least every 30 minutes. Freshclam " -"uses the ClamAV's mirror infrastructure to distribute the database and its " -"updates but all the contents are provided under Google's terms of use." -msgstr "" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:10001 -#, fuzzy #| msgid "Do you want to load bytecode from the database?" msgid "Do you want to download the bytecode database?" msgstr "Caricare il bytecode dal database?" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "Private mirror for freshclam:" msgstr "" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "" "This option allows you to easily point freshclam to private mirrors. If " "PrivateMirror is set, freshclam does not attempt to use DNS to determine " @@ -279,7 +257,7 @@ #. Type: boolean #. Description -#: ../clamav-freshclam.templates:12001 ../clamav-daemon.templates:22001 +#: ../clamav-freshclam.templates:11001 ../clamav-daemon.templates:22001 #: ../clamav-milter.templates:32001 #, fuzzy #| msgid "Do you want to enable mail scanning?" @@ -1500,6 +1478,11 @@ msgstr "" #, fuzzy +#~| msgid "Do you want to enable mail scanning?" +#~ msgid "Do you want to enable support for Google Safe Browsing?" +#~ msgstr "Attivare l'analisi delle email?" + +#, fuzzy #~| msgid "Do you want to enable archive scanning?" #~ msgid "Do you want to enable on-access scanning?" #~ msgstr "Attivare la scansione degli archivi?" diff -Nru clamav-0.103.2+dfsg/debian/po/ja.po clamav-0.103.5+dfsg/debian/po/ja.po --- clamav-0.103.2+dfsg/debian/po/ja.po 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/po/ja.po 2022-01-17 13:19:19.000000000 +0000 @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: clamav\n" "Report-Msgid-Bugs-To: clamav@packages.debian.org\n" -"POT-Creation-Date: 2021-02-21 15:05+0000\n" +"POT-Creation-Date: 2022-01-17 08:27-0500\n" "PO-Revision-Date: 2014-05-11 06:12+0900\n" "Last-Translator: Kenshi Muto \n" "Language-Team: Japanese \n" @@ -222,46 +222,18 @@ #. Type: boolean #. Description #: ../clamav-freshclam.templates:9001 -msgid "Do you want to enable support for Google Safe Browsing?" -msgstr "Google Safe Browsing を有効にしますか?" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:9001 -msgid "" -"When activated for the first time, freshclam will download a new database " -"file (safebrowsing.cvd) which will be automatically loaded by clamd and " -"clamscan during the next reload, provided that the heuristic phishing " -"detection is turned on. This database includes information about websites " -"that may be phishing sites or possible sources of malware. When using this " -"option, it's mandatory to run freshclam at least every 30 minutes. Freshclam " -"uses the ClamAV's mirror infrastructure to distribute the database and its " -"updates but all the contents are provided under Google's terms of use." -msgstr "" -"初回の有効化時に freshclam は新しいデータベースファイル (safebrowsing.cvd) を" -"ダウンロードします。このファイルは偽装の発見的検知 (heuristic phishing " -"detection) を有効にしていれば clamd や clamscan が次回リロード時に自動的に読" -"み込みます。このデータベースには、偽装サイトやマルウェアのソースの可能性があ" -"るウェブサイトについての情報が収録されています。このオプションを使う場合は " -"freshclam を最低でも30分に一度は実行することが必須となります。freshclam は " -"ClamAV のミラー基盤を使ってデータベースや更新を配布しますが、その内容は全て" -"が Google の定める利用条件の下で提供されています。" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:10001 msgid "Do you want to download the bytecode database?" msgstr "バイトコードデータベースをダウンロードしますか?" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "Private mirror for freshclam:" msgstr "freshclam 用プライベートミラー:" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "" "This option allows you to easily point freshclam to private mirrors. If " "PrivateMirror is set, freshclam does not attempt to use DNS to determine " @@ -283,7 +255,7 @@ #. Type: boolean #. Description -#: ../clamav-freshclam.templates:12001 ../clamav-daemon.templates:22001 +#: ../clamav-freshclam.templates:11001 ../clamav-daemon.templates:22001 #: ../clamav-milter.templates:32001 msgid "Do you want to enable log rotation?" msgstr "ログファイル切り替えを有効にしますか?" @@ -1506,6 +1478,29 @@ "注意: このオプションを有効にするのは恐らく良い選択ではありますが、歴史的理由" "によりデフォルト値は現在 off となっています。" +#~ msgid "Do you want to enable support for Google Safe Browsing?" +#~ msgstr "Google Safe Browsing を有効にしますか?" + +#~ msgid "" +#~ "When activated for the first time, freshclam will download a new database " +#~ "file (safebrowsing.cvd) which will be automatically loaded by clamd and " +#~ "clamscan during the next reload, provided that the heuristic phishing " +#~ "detection is turned on. This database includes information about websites " +#~ "that may be phishing sites or possible sources of malware. When using " +#~ "this option, it's mandatory to run freshclam at least every 30 minutes. " +#~ "Freshclam uses the ClamAV's mirror infrastructure to distribute the " +#~ "database and its updates but all the contents are provided under Google's " +#~ "terms of use." +#~ msgstr "" +#~ "初回の有効化時に freshclam は新しいデータベースファイル (safebrowsing." +#~ "cvd) をダウンロードします。このファイルは偽装の発見的検知 (heuristic " +#~ "phishing detection) を有効にしていれば clamd や clamscan が次回リロード時" +#~ "に自動的に読み込みます。このデータベースには、偽装サイトやマルウェアのソー" +#~ "スの可能性があるウェブサイトについての情報が収録されています。このオプショ" +#~ "ンを使う場合は freshclam を最低でも30分に一度は実行することが必須となりま" +#~ "す。freshclam は ClamAV のミラー基盤を使ってデータベースや更新を配布します" +#~ "が、その内容は全てが Google の定める利用条件の下で提供されています。" + #~ msgid "Do you want to enable on-access scanning?" #~ msgstr "アクセス時の走査を有効にしますか?" diff -Nru clamav-0.103.2+dfsg/debian/po/nl.po clamav-0.103.5+dfsg/debian/po/nl.po --- clamav-0.103.2+dfsg/debian/po/nl.po 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/po/nl.po 2022-01-17 13:19:19.000000000 +0000 @@ -9,7 +9,7 @@ msgstr "" "Project-Id-Version: clamav 0.97.3+dfsg-2\n" "Report-Msgid-Bugs-To: clamav@packages.debian.org\n" -"POT-Creation-Date: 2021-02-21 15:05+0000\n" +"POT-Creation-Date: 2022-01-17 08:27-0500\n" "PO-Revision-Date: 2014-09-23 18:02+0200\n" "Last-Translator: Frans Spiesschaert \n" "Language-Team: Debian Dutch l10n Team \n" @@ -237,48 +237,18 @@ #. Type: boolean #. Description #: ../clamav-freshclam.templates:9001 -msgid "Do you want to enable support for Google Safe Browsing?" -msgstr "Wilt u ondersteuning voor 'Google Veilig Surfen' aanzetten?" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:9001 -msgid "" -"When activated for the first time, freshclam will download a new database " -"file (safebrowsing.cvd) which will be automatically loaded by clamd and " -"clamscan during the next reload, provided that the heuristic phishing " -"detection is turned on. This database includes information about websites " -"that may be phishing sites or possible sources of malware. When using this " -"option, it's mandatory to run freshclam at least every 30 minutes. Freshclam " -"uses the ClamAV's mirror infrastructure to distribute the database and its " -"updates but all the contents are provided under Google's terms of use." -msgstr "" -"De eerste maal dat freshclam geactiveerd wordt, zal het een nieuw " -"databasebestand downloaden (safebrowsing.cvd). Als \"heuristisch opsporen " -"van phishing\" actief is, zullen clamd en clamscan deze database automatisch " -"gaan gebruiken. Het is een database die informatie bevat over websites die " -"misschien wel gebruik maken van phishing of mogelijke verspreiders van " -"malware zijn. Indien u van deze optie gebruik maakt, is het verplicht om " -"minstens elke 30 minuten het commando freshclam uit te laten voeren. " -"Freshclam maakt gebruik van de spiegelserverinfrastructuur van ClamAV om de " -"database en aanpassingen eraan aan te bieden, maar alle inhoud wordt " -"aangeboden onder de gebruikscondities die Google stelt." - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:10001 msgid "Do you want to download the bytecode database?" msgstr "Wilt u de bytecodedatabase laden?" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "Private mirror for freshclam:" msgstr "Privéspiegelserver voor freshclam:" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "" "This option allows you to easily point freshclam to private mirrors. If " "PrivateMirror is set, freshclam does not attempt to use DNS to determine " @@ -302,7 +272,7 @@ #. Type: boolean #. Description -#: ../clamav-freshclam.templates:12001 ../clamav-daemon.templates:22001 +#: ../clamav-freshclam.templates:11001 ../clamav-daemon.templates:22001 #: ../clamav-milter.templates:32001 msgid "Do you want to enable log rotation?" msgstr "Wilt u logrotatie activeren?" @@ -1581,6 +1551,31 @@ "Merk op: hoewel het wellicht een goede zaak is om deze optie aan te zetten, " "staat ze standaard toch uit om historische redenen." +#~ msgid "Do you want to enable support for Google Safe Browsing?" +#~ msgstr "Wilt u ondersteuning voor 'Google Veilig Surfen' aanzetten?" + +#~ msgid "" +#~ "When activated for the first time, freshclam will download a new database " +#~ "file (safebrowsing.cvd) which will be automatically loaded by clamd and " +#~ "clamscan during the next reload, provided that the heuristic phishing " +#~ "detection is turned on. This database includes information about websites " +#~ "that may be phishing sites or possible sources of malware. When using " +#~ "this option, it's mandatory to run freshclam at least every 30 minutes. " +#~ "Freshclam uses the ClamAV's mirror infrastructure to distribute the " +#~ "database and its updates but all the contents are provided under Google's " +#~ "terms of use." +#~ msgstr "" +#~ "De eerste maal dat freshclam geactiveerd wordt, zal het een nieuw " +#~ "databasebestand downloaden (safebrowsing.cvd). Als \"heuristisch opsporen " +#~ "van phishing\" actief is, zullen clamd en clamscan deze database " +#~ "automatisch gaan gebruiken. Het is een database die informatie bevat over " +#~ "websites die misschien wel gebruik maken van phishing of mogelijke " +#~ "verspreiders van malware zijn. Indien u van deze optie gebruik maakt, is " +#~ "het verplicht om minstens elke 30 minuten het commando freshclam uit te " +#~ "laten voeren. Freshclam maakt gebruik van de spiegelserverinfrastructuur " +#~ "van ClamAV om de database en aanpassingen eraan aan te bieden, maar alle " +#~ "inhoud wordt aangeboden onder de gebruikscondities die Google stelt." + #~ msgid "Do you want to enable on-access scanning?" #~ msgstr "Wilt u de functie scannen-bij-openen aanzetten?" diff -Nru clamav-0.103.2+dfsg/debian/po/pl.po clamav-0.103.5+dfsg/debian/po/pl.po --- clamav-0.103.2+dfsg/debian/po/pl.po 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/po/pl.po 2022-01-17 13:19:19.000000000 +0000 @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: clamav@packages.debian.org\n" -"POT-Creation-Date: 2021-02-21 15:05+0000\n" +"POT-Creation-Date: 2022-01-17 08:27-0500\n" "PO-Revision-Date: 2012-02-10 16:25+0100\n" "Last-Translator: Michał Kułach \n" "Language-Team: Polish \n" @@ -230,41 +230,19 @@ #. Description #: ../clamav-freshclam.templates:9001 #, fuzzy -#| msgid "Do you want to enable mail scanning?" -msgid "Do you want to enable support for Google Safe Browsing?" -msgstr "Włączyć skanowanie poczty elektronicznej?" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:9001 -msgid "" -"When activated for the first time, freshclam will download a new database " -"file (safebrowsing.cvd) which will be automatically loaded by clamd and " -"clamscan during the next reload, provided that the heuristic phishing " -"detection is turned on. This database includes information about websites " -"that may be phishing sites or possible sources of malware. When using this " -"option, it's mandatory to run freshclam at least every 30 minutes. Freshclam " -"uses the ClamAV's mirror infrastructure to distribute the database and its " -"updates but all the contents are provided under Google's terms of use." -msgstr "" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:10001 -#, fuzzy #| msgid "Do you want to load bytecode from the database?" msgid "Do you want to download the bytecode database?" msgstr "Ładować kod bajtowy z bazy danych?" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "Private mirror for freshclam:" msgstr "" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "" "This option allows you to easily point freshclam to private mirrors. If " "PrivateMirror is set, freshclam does not attempt to use DNS to determine " @@ -278,7 +256,7 @@ #. Type: boolean #. Description -#: ../clamav-freshclam.templates:12001 ../clamav-daemon.templates:22001 +#: ../clamav-freshclam.templates:11001 ../clamav-daemon.templates:22001 #: ../clamav-milter.templates:32001 #, fuzzy #| msgid "Do you want to enable mail scanning?" @@ -1505,6 +1483,11 @@ msgstr "" #, fuzzy +#~| msgid "Do you want to enable mail scanning?" +#~ msgid "Do you want to enable support for Google Safe Browsing?" +#~ msgstr "Włączyć skanowanie poczty elektronicznej?" + +#, fuzzy #~| msgid "Do you want to enable archive scanning?" #~ msgid "Do you want to enable on-access scanning?" #~ msgstr "Włączyć skanowanie archiwów?" diff -Nru clamav-0.103.2+dfsg/debian/po/pt_BR.po clamav-0.103.5+dfsg/debian/po/pt_BR.po --- clamav-0.103.2+dfsg/debian/po/pt_BR.po 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/po/pt_BR.po 2022-01-17 13:19:19.000000000 +0000 @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: clamav\n" "Report-Msgid-Bugs-To: clamav@packages.debian.org\n" -"POT-Creation-Date: 2021-02-21 15:05+0000\n" +"POT-Creation-Date: 2022-01-17 08:27-0500\n" "PO-Revision-Date: 2016-02-23 07:22-0300\n" "Last-Translator: Adriano Rafael Gomes \n" "Language-Team: Brazilian Portuguese \n" "Language-Team: Portuguese \n" @@ -229,48 +229,18 @@ #. Type: boolean #. Description #: ../clamav-freshclam.templates:9001 -msgid "Do you want to enable support for Google Safe Browsing?" -msgstr "Deseja activar o suporte para Google Safe Browsing?" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:9001 -msgid "" -"When activated for the first time, freshclam will download a new database " -"file (safebrowsing.cvd) which will be automatically loaded by clamd and " -"clamscan during the next reload, provided that the heuristic phishing " -"detection is turned on. This database includes information about websites " -"that may be phishing sites or possible sources of malware. When using this " -"option, it's mandatory to run freshclam at least every 30 minutes. Freshclam " -"uses the ClamAV's mirror infrastructure to distribute the database and its " -"updates but all the contents are provided under Google's terms of use." -msgstr "" -"Quando activado pela primeira vez, o freshclam irá descarregar um novo " -"ficheiro de base de dados (safebrowsing.cvd) o qual será carregado " -"automaticamente pelo clamd e pelo clamscan durante o próximo arranque, " -"contando que a detecção de phishing heurística esteja ligada. Esta base de " -"dados inclui informação acerca de websites que podem ser sites de phishing " -"ou possíveis fontes de malware. Quando se usa esta opção, é obrigatório " -"correr o freshclam a cada 30 minutos no mínimo. O freshclam usa a " -"infraestrutura de mirror do ClamAV para distribuir a base de dados e as suas " -"actualizações mas todos os conteúdos são disponibilizados sob os termos de " -"utilização da Google." - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:10001 msgid "Do you want to download the bytecode database?" msgstr "Deseja descarregar a base de dados de bytecode?" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "Private mirror for freshclam:" msgstr "Mirror privado para o freshclam:" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "" "This option allows you to easily point freshclam to private mirrors. If " "PrivateMirror is set, freshclam does not attempt to use DNS to determine " @@ -293,7 +263,7 @@ #. Type: boolean #. Description -#: ../clamav-freshclam.templates:12001 ../clamav-daemon.templates:22001 +#: ../clamav-freshclam.templates:11001 ../clamav-daemon.templates:22001 #: ../clamav-milter.templates:32001 msgid "Do you want to enable log rotation?" msgstr "Deseja activar a rotação dos relatórios?" @@ -1542,6 +1512,31 @@ "predefinido está presentemente definido para a desactivar por razões de " "compatibilidades antigas (legacy)." +#~ msgid "Do you want to enable support for Google Safe Browsing?" +#~ msgstr "Deseja activar o suporte para Google Safe Browsing?" + +#~ msgid "" +#~ "When activated for the first time, freshclam will download a new database " +#~ "file (safebrowsing.cvd) which will be automatically loaded by clamd and " +#~ "clamscan during the next reload, provided that the heuristic phishing " +#~ "detection is turned on. This database includes information about websites " +#~ "that may be phishing sites or possible sources of malware. When using " +#~ "this option, it's mandatory to run freshclam at least every 30 minutes. " +#~ "Freshclam uses the ClamAV's mirror infrastructure to distribute the " +#~ "database and its updates but all the contents are provided under Google's " +#~ "terms of use." +#~ msgstr "" +#~ "Quando activado pela primeira vez, o freshclam irá descarregar um novo " +#~ "ficheiro de base de dados (safebrowsing.cvd) o qual será carregado " +#~ "automaticamente pelo clamd e pelo clamscan durante o próximo arranque, " +#~ "contando que a detecção de phishing heurística esteja ligada. Esta base " +#~ "de dados inclui informação acerca de websites que podem ser sites de " +#~ "phishing ou possíveis fontes de malware. Quando se usa esta opção, é " +#~ "obrigatório correr o freshclam a cada 30 minutos no mínimo. O freshclam " +#~ "usa a infraestrutura de mirror do ClamAV para distribuir a base de dados " +#~ "e as suas actualizações mas todos os conteúdos são disponibilizados sob " +#~ "os termos de utilização da Google." + #~ msgid "Do you want to enable on-access scanning?" #~ msgstr "Deseja activar a inspecção no momento de acesso (on-access)?" diff -Nru clamav-0.103.2+dfsg/debian/po/ru.po clamav-0.103.5+dfsg/debian/po/ru.po --- clamav-0.103.2+dfsg/debian/po/ru.po 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/po/ru.po 2022-01-17 13:19:19.000000000 +0000 @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: clamav 0.98.4~rc1+dfsg-3\n" "Report-Msgid-Bugs-To: clamav@packages.debian.org\n" -"POT-Creation-Date: 2021-02-21 15:05+0000\n" +"POT-Creation-Date: 2022-01-17 08:27-0500\n" "PO-Revision-Date: 2014-06-07 09:00+0400\n" "Last-Translator: Yuri Kozlov \n" "Language-Team: Russian \n" @@ -231,48 +231,18 @@ #. Type: boolean #. Description #: ../clamav-freshclam.templates:9001 -msgid "Do you want to enable support for Google Safe Browsing?" -msgstr "Включить поддержку Google Safe Browsing?" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:9001 -msgid "" -"When activated for the first time, freshclam will download a new database " -"file (safebrowsing.cvd) which will be automatically loaded by clamd and " -"clamscan during the next reload, provided that the heuristic phishing " -"detection is turned on. This database includes information about websites " -"that may be phishing sites or possible sources of malware. When using this " -"option, it's mandatory to run freshclam at least every 30 minutes. Freshclam " -"uses the ClamAV's mirror infrastructure to distribute the database and its " -"updates but all the contents are provided under Google's terms of use." -msgstr "" -"При первом запуске freshclam скачает новый файл базы данных (safebrowsing." -"cvd), который автоматически загрузится clamd и clamscan при следующий " -"перезагрузке, и будет включено эвристическое обнаружение сетевого " -"мошенничества (heuristic phishing detection). Данная база данных содержит " -"информацию о веб-сайтах, которые могут быть созданы мошенниками или являются " -"источниками вредоносных программ (malware). При включении данного механизма " -"нужно обязательно запускать freshclam не реже чем 1 раз в 30 минут. " -"Freshclam использует инфраструктуру монитора ClamAV для распространения базы " -"данных и её обновления, но всё содержимое предоставляется на условиях " -"использования Google." - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:10001 msgid "Do you want to download the bytecode database?" msgstr "Скачивать базу данных байт-кода?" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "Private mirror for freshclam:" msgstr "Частное зеркало freshclam:" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "" "This option allows you to easily point freshclam to private mirrors. If " "PrivateMirror is set, freshclam does not attempt to use DNS to determine " @@ -294,7 +264,7 @@ #. Type: boolean #. Description -#: ../clamav-freshclam.templates:12001 ../clamav-daemon.templates:22001 +#: ../clamav-freshclam.templates:11001 ../clamav-daemon.templates:22001 #: ../clamav-milter.templates:32001 msgid "Do you want to enable log rotation?" msgstr "Включить циклическую перестановку журнала (log rotation)?" @@ -1529,6 +1499,31 @@ "Замечание: хотя, вероятно, правильно ответить утвердительно, по умолчанию " "действие выключено в целях обратной совместимости." +#~ msgid "Do you want to enable support for Google Safe Browsing?" +#~ msgstr "Включить поддержку Google Safe Browsing?" + +#~ msgid "" +#~ "When activated for the first time, freshclam will download a new database " +#~ "file (safebrowsing.cvd) which will be automatically loaded by clamd and " +#~ "clamscan during the next reload, provided that the heuristic phishing " +#~ "detection is turned on. This database includes information about websites " +#~ "that may be phishing sites or possible sources of malware. When using " +#~ "this option, it's mandatory to run freshclam at least every 30 minutes. " +#~ "Freshclam uses the ClamAV's mirror infrastructure to distribute the " +#~ "database and its updates but all the contents are provided under Google's " +#~ "terms of use." +#~ msgstr "" +#~ "При первом запуске freshclam скачает новый файл базы данных (safebrowsing." +#~ "cvd), который автоматически загрузится clamd и clamscan при следующий " +#~ "перезагрузке, и будет включено эвристическое обнаружение сетевого " +#~ "мошенничества (heuristic phishing detection). Данная база данных содержит " +#~ "информацию о веб-сайтах, которые могут быть созданы мошенниками или " +#~ "являются источниками вредоносных программ (malware). При включении " +#~ "данного механизма нужно обязательно запускать freshclam не реже чем 1 раз " +#~ "в 30 минут. Freshclam использует инфраструктуру монитора ClamAV для " +#~ "распространения базы данных и её обновления, но всё содержимое " +#~ "предоставляется на условиях использования Google." + #~ msgid "Do you want to enable on-access scanning?" #~ msgstr "Включить сканирование при доступе?" diff -Nru clamav-0.103.2+dfsg/debian/po/sv.po clamav-0.103.5+dfsg/debian/po/sv.po --- clamav-0.103.2+dfsg/debian/po/sv.po 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/po/sv.po 2022-01-17 13:19:19.000000000 +0000 @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: clamav_0.93~dfsg-1_sv\n" "Report-Msgid-Bugs-To: clamav@packages.debian.org\n" -"POT-Creation-Date: 2021-02-21 15:05+0000\n" +"POT-Creation-Date: 2022-01-17 08:27-0500\n" "PO-Revision-Date: 2011-06-29 22:37+0100\n" "Last-Translator: Martin Bagge / brother \n" "Language-Team: Swedish \n" @@ -229,41 +229,19 @@ #. Description #: ../clamav-freshclam.templates:9001 #, fuzzy -#| msgid "Do you want to enable mail scanning?" -msgid "Do you want to enable support for Google Safe Browsing?" -msgstr "Vill du aktivera skanning av e-post?" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:9001 -msgid "" -"When activated for the first time, freshclam will download a new database " -"file (safebrowsing.cvd) which will be automatically loaded by clamd and " -"clamscan during the next reload, provided that the heuristic phishing " -"detection is turned on. This database includes information about websites " -"that may be phishing sites or possible sources of malware. When using this " -"option, it's mandatory to run freshclam at least every 30 minutes. Freshclam " -"uses the ClamAV's mirror infrastructure to distribute the database and its " -"updates but all the contents are provided under Google's terms of use." -msgstr "" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:10001 -#, fuzzy #| msgid "Do you want to load bytecode from the database?" msgid "Do you want to download the bytecode database?" msgstr "Ska bytekod laddas från databasen?" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "Private mirror for freshclam:" msgstr "" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "" "This option allows you to easily point freshclam to private mirrors. If " "PrivateMirror is set, freshclam does not attempt to use DNS to determine " @@ -277,7 +255,7 @@ #. Type: boolean #. Description -#: ../clamav-freshclam.templates:12001 ../clamav-daemon.templates:22001 +#: ../clamav-freshclam.templates:11001 ../clamav-daemon.templates:22001 #: ../clamav-milter.templates:32001 #, fuzzy #| msgid "Do you want to enable mail scanning?" @@ -1486,6 +1464,11 @@ msgstr "" #, fuzzy +#~| msgid "Do you want to enable mail scanning?" +#~ msgid "Do you want to enable support for Google Safe Browsing?" +#~ msgstr "Vill du aktivera skanning av e-post?" + +#, fuzzy #~| msgid "Do you want to enable archive scanning?" #~ msgid "Do you want to enable on-access scanning?" #~ msgstr "Vill du aktivera skanning av arkiv?" diff -Nru clamav-0.103.2+dfsg/debian/po/templates.pot clamav-0.103.5+dfsg/debian/po/templates.pot --- clamav-0.103.2+dfsg/debian/po/templates.pot 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/po/templates.pot 2022-01-17 13:19:19.000000000 +0000 @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: clamav\n" "Report-Msgid-Bugs-To: clamav@packages.debian.org\n" -"POT-Creation-Date: 2021-02-21 15:05+0000\n" +"POT-Creation-Date: 2022-01-17 08:27-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -191,38 +191,18 @@ #. Type: boolean #. Description #: ../clamav-freshclam.templates:9001 -msgid "Do you want to enable support for Google Safe Browsing?" -msgstr "" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:9001 -msgid "" -"When activated for the first time, freshclam will download a new database " -"file (safebrowsing.cvd) which will be automatically loaded by clamd and " -"clamscan during the next reload, provided that the heuristic phishing " -"detection is turned on. This database includes information about websites " -"that may be phishing sites or possible sources of malware. When using this " -"option, it's mandatory to run freshclam at least every 30 minutes. Freshclam " -"uses the ClamAV's mirror infrastructure to distribute the database and its " -"updates but all the contents are provided under Google's terms of use." -msgstr "" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:10001 msgid "Do you want to download the bytecode database?" msgstr "" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "Private mirror for freshclam:" msgstr "" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "" "This option allows you to easily point freshclam to private mirrors. If " "PrivateMirror is set, freshclam does not attempt to use DNS to determine " @@ -236,7 +216,7 @@ #. Type: boolean #. Description -#: ../clamav-freshclam.templates:12001 ../clamav-daemon.templates:22001 +#: ../clamav-freshclam.templates:11001 ../clamav-daemon.templates:22001 #: ../clamav-milter.templates:32001 msgid "Do you want to enable log rotation?" msgstr "" diff -Nru clamav-0.103.2+dfsg/debian/po/vi.po clamav-0.103.5+dfsg/debian/po/vi.po --- clamav-0.103.2+dfsg/debian/po/vi.po 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/po/vi.po 2022-01-17 13:19:19.000000000 +0000 @@ -6,7 +6,7 @@ msgstr "" "Project-Id-Version: clamav 0.96.3+dfsg-2\n" "Report-Msgid-Bugs-To: clamav@packages.debian.org\n" -"POT-Creation-Date: 2021-02-21 15:05+0000\n" +"POT-Creation-Date: 2022-01-17 08:27-0500\n" "PO-Revision-Date: 2010-10-27 16:18+0930\n" "Last-Translator: Clytie Siddall \n" "Language-Team: Vietnamese \n" @@ -228,41 +228,19 @@ #. Description #: ../clamav-freshclam.templates:9001 #, fuzzy -#| msgid "Do you want to enable mail scanning?" -msgid "Do you want to enable support for Google Safe Browsing?" -msgstr "Muốn hiệu lực quét thư?" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:9001 -msgid "" -"When activated for the first time, freshclam will download a new database " -"file (safebrowsing.cvd) which will be automatically loaded by clamd and " -"clamscan during the next reload, provided that the heuristic phishing " -"detection is turned on. This database includes information about websites " -"that may be phishing sites or possible sources of malware. When using this " -"option, it's mandatory to run freshclam at least every 30 minutes. Freshclam " -"uses the ClamAV's mirror infrastructure to distribute the database and its " -"updates but all the contents are provided under Google's terms of use." -msgstr "" - -#. Type: boolean -#. Description -#: ../clamav-freshclam.templates:10001 -#, fuzzy #| msgid "Do you want to load bytecode from the database?" msgid "Do you want to download the bytecode database?" msgstr "Muốn nạp mã byte từ cơ sở dữ liệu ?" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "Private mirror for freshclam:" msgstr "" #. Type: string #. Description -#: ../clamav-freshclam.templates:11001 +#: ../clamav-freshclam.templates:10001 msgid "" "This option allows you to easily point freshclam to private mirrors. If " "PrivateMirror is set, freshclam does not attempt to use DNS to determine " @@ -276,7 +254,7 @@ #. Type: boolean #. Description -#: ../clamav-freshclam.templates:12001 ../clamav-daemon.templates:22001 +#: ../clamav-freshclam.templates:11001 ../clamav-daemon.templates:22001 #: ../clamav-milter.templates:32001 #, fuzzy #| msgid "Do you want to enable mail scanning?" @@ -1507,6 +1485,11 @@ msgstr "" #, fuzzy +#~| msgid "Do you want to enable mail scanning?" +#~ msgid "Do you want to enable support for Google Safe Browsing?" +#~ msgstr "Muốn hiệu lực quét thư?" + +#, fuzzy #~| msgid "Do you want to enable archive scanning?" #~ msgid "Do you want to enable on-access scanning?" #~ msgstr "Muốn hiệu lực quét kho nén ?" diff -Nru clamav-0.103.2+dfsg/debian/rules clamav-0.103.5+dfsg/debian/rules --- clamav-0.103.2+dfsg/debian/rules 2021-04-12 18:59:21.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/rules 2022-01-12 20:16:45.000000000 +0000 @@ -88,7 +88,7 @@ fi;\ done; \ # Check for library features which may have been upgraded. - if ! grep -q "CL_FLEVEL 123" libclamav/others.h ; then \ + if ! grep -q "CL_FLEVEL 126" libclamav/others.h ; then \ echo "cl_retflevel needs boosting in symbol file"; \ touch debian/exit; \ fi; @@ -145,6 +145,11 @@ endif override_dh_install: +# clamonacc is only built on linux architectures +ifneq (linux, $(DEB_HOST_ARCH_OS)) + grep -v 'clamonacc' debian/clamav-daemon.install > debian/clamav-daemon.install.$(DEB_HOST_ARCH_OS) + grep -v 'clamonacc' debian/clamav-daemon.manpages > debian/clamav-daemon.manpages.$(DEB_HOST_ARCH_OS) +endif dh_install dh_apparmor -pclamav-freshclam --profile-name=usr.bin.freshclam dh_apparmor -pclamav-daemon --profile-name=usr.sbin.clamd @@ -168,3 +173,7 @@ # Don't compress the example configuration files and the documentation PDFs. override_dh_compress: dh_compress -Xexamples + +override_dh_clean: + rm -f debian/clamav-daemon.install.$(DEB_HOST_ARCH_OS) debian/clamav-daemon.manpages.$(DEB_HOST_ARCH_OS) + dh_clean diff -Nru clamav-0.103.2+dfsg/debian/watch clamav-0.103.5+dfsg/debian/watch --- clamav-0.103.2+dfsg/debian/watch 2021-04-12 18:51:19.000000000 +0000 +++ clamav-0.103.5+dfsg/debian/watch 2022-01-12 20:02:01.000000000 +0000 @@ -1,7 +1,7 @@ version=3 opts="uversionmangle=s/(\d+)-*(beta|rc)/$1~$2/,dversionmangle=s/\+dfsg//,pgpsigurlmangle=s/$/.sig/" \ -https://www.clamav.net/download.html /downloads/.*/clamav-(.*).tar.gz +https://www.clamav.net/previous_stable_releases /downloads/.*/clamav-(0\.103\..*).tar.gz +#https://www.clamav.net/download.html /downloads/.*/clamav-(0.103.*).tar.gz # repository snapshots: fallback location with larger tarballs and without signatures #opts="uversionmangle=s/(\d+)-*(beta|rc)/$1~$2/,dversionmangle=s/\+dfsg//" \ #https://github.com/vrtadmin/clamav-devel/tags /vrtadmin/clamav-devel/archive/clamav-(.*)\.tar\.gz - diff -Nru clamav-0.103.2+dfsg/docs/html/404.html clamav-0.103.5+dfsg/docs/html/404.html --- clamav-0.103.2+dfsg/docs/html/404.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/404.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Document not found (404)

+

This URL is invalid, sorry. Please use the navigation bar or search to continue.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/ace.js clamav-0.103.5+dfsg/docs/html/ace.js --- clamav-0.103.2+dfsg/docs/html/ace.js 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/ace.js 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,43 @@ +/* +Copyright (c) 2010, Ajax.org B.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Ajax.org B.V. nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +(function(){function o(n){var i=e;n&&(e[n]||(e[n]={}),i=e[n]);if(!i.define||!i.define.packaged)t.original=i.define,i.define=t,i.define.packaged=!0;if(!i.require||!i.require.packaged)r.original=i.require,i.require=r,i.require.packaged=!0}var ACE_NAMESPACE = "ace",e=function(){return this}();!e&&typeof window!="undefined"&&(e=window);if(!ACE_NAMESPACE&&typeof requirejs!="undefined")return;var t=function(e,n,r){if(typeof e!="string"){t.original?t.original.apply(this,arguments):(console.error("dropping module because define wasn't a string."),console.trace());return}arguments.length==2&&(r=n),t.modules[e]||(t.payloads[e]=r,t.modules[e]=null)};t.modules={},t.payloads={};var n=function(e,t,n){if(typeof t=="string"){var i=s(e,t);if(i!=undefined)return n&&n(),i}else if(Object.prototype.toString.call(t)==="[object Array]"){var o=[];for(var u=0,a=t.length;u1&&u(t,"")>-1&&(a=RegExp(this.source,r.replace.call(o(this),"g","")),r.replace.call(e.slice(t.index),a,function(){for(var e=1;et.index&&this.lastIndex--}return t},s||(RegExp.prototype.test=function(e){var t=r.exec.call(this,e);return t&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--,!!t})}),ace.define("ace/lib/es5-shim",["require","exports","module"],function(e,t,n){function r(){}function w(e){try{return Object.defineProperty(e,"sentinel",{}),"sentinel"in e}catch(t){}}function H(e){return e=+e,e!==e?e=0:e!==0&&e!==1/0&&e!==-1/0&&(e=(e>0||-1)*Math.floor(Math.abs(e))),e}function B(e){var t=typeof e;return e===null||t==="undefined"||t==="boolean"||t==="number"||t==="string"}function j(e){var t,n,r;if(B(e))return e;n=e.valueOf;if(typeof n=="function"){t=n.call(e);if(B(t))return t}r=e.toString;if(typeof r=="function"){t=r.call(e);if(B(t))return t}throw new TypeError}Function.prototype.bind||(Function.prototype.bind=function(t){var n=this;if(typeof n!="function")throw new TypeError("Function.prototype.bind called on incompatible "+n);var i=u.call(arguments,1),s=function(){if(this instanceof s){var e=n.apply(this,i.concat(u.call(arguments)));return Object(e)===e?e:this}return n.apply(t,i.concat(u.call(arguments)))};return n.prototype&&(r.prototype=n.prototype,s.prototype=new r,r.prototype=null),s});var i=Function.prototype.call,s=Array.prototype,o=Object.prototype,u=s.slice,a=i.bind(o.toString),f=i.bind(o.hasOwnProperty),l,c,h,p,d;if(d=f(o,"__defineGetter__"))l=i.bind(o.__defineGetter__),c=i.bind(o.__defineSetter__),h=i.bind(o.__lookupGetter__),p=i.bind(o.__lookupSetter__);if([1,2].splice(0).length!=2)if(!function(){function e(e){var t=new Array(e+2);return t[0]=t[1]=0,t}var t=[],n;t.splice.apply(t,e(20)),t.splice.apply(t,e(26)),n=t.length,t.splice(5,0,"XXX"),n+1==t.length;if(n+1==t.length)return!0}())Array.prototype.splice=function(e,t){var n=this.length;e>0?e>n&&(e=n):e==void 0?e=0:e<0&&(e=Math.max(n+e,0)),e+ta)for(h=l;h--;)this[f+h]=this[a+h];if(s&&e===c)this.length=c,this.push.apply(this,i);else{this.length=c+s;for(h=0;h>>0;if(a(t)!="[object Function]")throw new TypeError;while(++s>>0,s=Array(i),o=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var u=0;u>>0,s=[],o,u=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var f=0;f>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduce of empty array with no initial value");var s=0,o;if(arguments.length>=2)o=arguments[1];else do{if(s in r){o=r[s++];break}if(++s>=i)throw new TypeError("reduce of empty array with no initial value")}while(!0);for(;s>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduceRight of empty array with no initial value");var s,o=i-1;if(arguments.length>=2)s=arguments[1];else do{if(o in r){s=r[o--];break}if(--o<0)throw new TypeError("reduceRight of empty array with no initial value")}while(!0);do o in this&&(s=t.call(void 0,s,r[o],o,n));while(o--);return s});if(!Array.prototype.indexOf||[0,1].indexOf(1,2)!=-1)Array.prototype.indexOf=function(t){var n=g&&a(this)=="[object String]"?this.split(""):F(this),r=n.length>>>0;if(!r)return-1;var i=0;arguments.length>1&&(i=H(arguments[1])),i=i>=0?i:Math.max(0,r+i);for(;i>>0;if(!r)return-1;var i=r-1;arguments.length>1&&(i=Math.min(i,H(arguments[1]))),i=i>=0?i:r-Math.abs(i);for(;i>=0;i--)if(i in n&&t===n[i])return i;return-1};Object.getPrototypeOf||(Object.getPrototypeOf=function(t){return t.__proto__||(t.constructor?t.constructor.prototype:o)});if(!Object.getOwnPropertyDescriptor){var y="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(t,n){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(y+t);if(!f(t,n))return;var r,i,s;r={enumerable:!0,configurable:!0};if(d){var u=t.__proto__;t.__proto__=o;var i=h(t,n),s=p(t,n);t.__proto__=u;if(i||s)return i&&(r.get=i),s&&(r.set=s),r}return r.value=t[n],r}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(t){return Object.keys(t)});if(!Object.create){var b;Object.prototype.__proto__===null?b=function(){return{__proto__:null}}:b=function(){var e={};for(var t in e)e[t]=null;return e.constructor=e.hasOwnProperty=e.propertyIsEnumerable=e.isPrototypeOf=e.toLocaleString=e.toString=e.valueOf=e.__proto__=null,e},Object.create=function(t,n){var r;if(t===null)r=b();else{if(typeof t!="object")throw new TypeError("typeof prototype["+typeof t+"] != 'object'");var i=function(){};i.prototype=t,r=new i,r.__proto__=t}return n!==void 0&&Object.defineProperties(r,n),r}}if(Object.defineProperty){var E=w({}),S=typeof document=="undefined"||w(document.createElement("div"));if(!E||!S)var x=Object.defineProperty}if(!Object.defineProperty||x){var T="Property description must be an object: ",N="Object.defineProperty called on non-object: ",C="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(t,n,r){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(N+t);if(typeof r!="object"&&typeof r!="function"||r===null)throw new TypeError(T+r);if(x)try{return x.call(Object,t,n,r)}catch(i){}if(f(r,"value"))if(d&&(h(t,n)||p(t,n))){var s=t.__proto__;t.__proto__=o,delete t[n],t[n]=r.value,t.__proto__=s}else t[n]=r.value;else{if(!d)throw new TypeError(C);f(r,"get")&&l(t,n,r.get),f(r,"set")&&c(t,n,r.set)}return t}}Object.defineProperties||(Object.defineProperties=function(t,n){for(var r in n)f(n,r)&&Object.defineProperty(t,r,n[r]);return t}),Object.seal||(Object.seal=function(t){return t}),Object.freeze||(Object.freeze=function(t){return t});try{Object.freeze(function(){})}catch(k){Object.freeze=function(t){return function(n){return typeof n=="function"?n:t(n)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(t){return t}),Object.isSealed||(Object.isSealed=function(t){return!1}),Object.isFrozen||(Object.isFrozen=function(t){return!1}),Object.isExtensible||(Object.isExtensible=function(t){if(Object(t)===t)throw new TypeError;var n="";while(f(t,n))n+="?";t[n]=!0;var r=f(t,n);return delete t[n],r});if(!Object.keys){var L=!0,A=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],O=A.length;for(var M in{toString:null})L=!1;Object.keys=function I(e){if(typeof e!="object"&&typeof e!="function"||e===null)throw new TypeError("Object.keys called on a non-object");var I=[];for(var t in e)f(e,t)&&I.push(t);if(L)for(var n=0,r=O;n=0?parseFloat((i.match(/(?:MSIE |Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]):parseFloat((i.match(/(?:Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]),t.isOldIE=t.isIE&&t.isIE<9,t.isGecko=t.isMozilla=i.match(/ Gecko\/\d+/),t.isOpera=window.opera&&Object.prototype.toString.call(window.opera)=="[object Opera]",t.isWebKit=parseFloat(i.split("WebKit/")[1])||undefined,t.isChrome=parseFloat(i.split(" Chrome/")[1])||undefined,t.isEdge=parseFloat(i.split(" Edge/")[1])||undefined,t.isAIR=i.indexOf("AdobeAIR")>=0,t.isIPad=i.indexOf("iPad")>=0,t.isAndroid=i.indexOf("Android")>=0,t.isChromeOS=i.indexOf(" CrOS ")>=0,t.isIOS=/iPad|iPhone|iPod/.test(i)&&!window.MSStream,t.isIOS&&(t.isMac=!0),t.isMobile=t.isIPad||t.isAndroid}),ace.define("ace/lib/dom",["require","exports","module","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("./useragent"),i="http://www.w3.org/1999/xhtml";t.buildDom=function o(e,t,n){if(typeof e=="string"&&e){var r=document.createTextNode(e);return t&&t.appendChild(r),r}if(!Array.isArray(e))return e;if(typeof e[0]!="string"||!e[0]){var i=[];for(var s=0;s=1.5:!0;if(typeof document!="undefined"){var s=document.createElement("div");t.HI_DPI&&s.style.transform!==undefined&&(t.HAS_CSS_TRANSFORMS=!0),!r.isEdge&&typeof s.style.animationName!="undefined"&&(t.HAS_CSS_ANIMATION=!0),s=null}t.HAS_CSS_TRANSFORMS?t.translate=function(e,t,n){e.style.transform="translate("+Math.round(t)+"px, "+Math.round(n)+"px)"}:t.translate=function(e,t,n){e.style.top=Math.round(n)+"px",e.style.left=Math.round(t)+"px"}}),ace.define("ace/lib/oop",["require","exports","module"],function(e,t,n){"use strict";t.inherits=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})},t.mixin=function(e,t){for(var n in t)e[n]=t[n];return e},t.implement=function(e,n){t.mixin(e,n)}}),ace.define("ace/lib/keys",["require","exports","module","ace/lib/oop"],function(e,t,n){"use strict";var r=e("./oop"),i=function(){var e={MODIFIER_KEYS:{16:"Shift",17:"Ctrl",18:"Alt",224:"Meta"},KEY_MODS:{ctrl:1,alt:2,option:2,shift:4,"super":8,meta:8,command:8,cmd:8},FUNCTION_KEYS:{8:"Backspace",9:"Tab",13:"Return",19:"Pause",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"Print",45:"Insert",46:"Delete",96:"Numpad0",97:"Numpad1",98:"Numpad2",99:"Numpad3",100:"Numpad4",101:"Numpad5",102:"Numpad6",103:"Numpad7",104:"Numpad8",105:"Numpad9","-13":"NumpadEnter",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"Numlock",145:"Scrolllock"},PRINTABLE_KEYS:{32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",61:"=",65:"a",66:"b",67:"c",68:"d",69:"e",70:"f",71:"g",72:"h",73:"i",74:"j",75:"k",76:"l",77:"m",78:"n",79:"o",80:"p",81:"q",82:"r",83:"s",84:"t",85:"u",86:"v",87:"w",88:"x",89:"y",90:"z",107:"+",109:"-",110:".",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'",111:"/",106:"*"}},t,n;for(n in e.FUNCTION_KEYS)t=e.FUNCTION_KEYS[n].toLowerCase(),e[t]=parseInt(n,10);for(n in e.PRINTABLE_KEYS)t=e.PRINTABLE_KEYS[n].toLowerCase(),e[t]=parseInt(n,10);return r.mixin(e,e.MODIFIER_KEYS),r.mixin(e,e.PRINTABLE_KEYS),r.mixin(e,e.FUNCTION_KEYS),e.enter=e["return"],e.escape=e.esc,e.del=e["delete"],e[173]="-",function(){var t=["cmd","ctrl","alt","shift"];for(var n=Math.pow(2,t.length);n--;)e.KEY_MODS[n]=t.filter(function(t){return n&e.KEY_MODS[t]}).join("-")+"-"}(),e.KEY_MODS[0]="",e.KEY_MODS[-1]="input-",e}();r.mixin(t,i),t.keyCodeToString=function(e){var t=i[e];return typeof t!="string"&&(t=String.fromCharCode(e)),t.toLowerCase()}}),ace.define("ace/lib/event",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function a(e,t,n){var a=u(t);if(!i.isMac&&s){t.getModifierState&&(t.getModifierState("OS")||t.getModifierState("Win"))&&(a|=8);if(s.altGr){if((3&a)==3)return;s.altGr=0}if(n===18||n===17){var f="location"in t?t.location:t.keyLocation;if(n===17&&f===1)s[n]==1&&(o=t.timeStamp);else if(n===18&&a===3&&f===2){var l=t.timeStamp-o;l<50&&(s.altGr=!0)}}}n in r.MODIFIER_KEYS&&(n=-1),a&8&&n>=91&&n<=93&&(n=-1);if(!a&&n===13){var f="location"in t?t.location:t.keyLocation;if(f===3){e(t,a,-n);if(t.defaultPrevented)return}}if(i.isChromeOS&&a&8){e(t,a,n);if(t.defaultPrevented)return;a&=-9}return!!a||n in r.FUNCTION_KEYS||n in r.PRINTABLE_KEYS?e(t,a,n):!1}function f(){s=Object.create(null)}var r=e("./keys"),i=e("./useragent"),s=null,o=0;t.addListener=function(e,t,n){if(e.addEventListener)return e.addEventListener(t,n,!1);if(e.attachEvent){var r=function(){n.call(e,window.event)};n._wrapper=r,e.attachEvent("on"+t,r)}},t.removeListener=function(e,t,n){if(e.removeEventListener)return e.removeEventListener(t,n,!1);e.detachEvent&&e.detachEvent("on"+t,n._wrapper||n)},t.stopEvent=function(e){return t.stopPropagation(e),t.preventDefault(e),!1},t.stopPropagation=function(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0},t.preventDefault=function(e){e.preventDefault?e.preventDefault():e.returnValue=!1},t.getButton=function(e){return e.type=="dblclick"?0:e.type=="contextmenu"||i.isMac&&e.ctrlKey&&!e.altKey&&!e.shiftKey?2:e.preventDefault?e.button:{1:0,2:2,4:1}[e.button]},t.capture=function(e,n,r){function i(e){n&&n(e),r&&r(e),t.removeListener(document,"mousemove",n,!0),t.removeListener(document,"mouseup",i,!0),t.removeListener(document,"dragstart",i,!0)}return t.addListener(document,"mousemove",n,!0),t.addListener(document,"mouseup",i,!0),t.addListener(document,"dragstart",i,!0),i},t.addTouchMoveListener=function(e,n){var r,i;t.addListener(e,"touchstart",function(e){var t=e.touches,n=t[0];r=n.clientX,i=n.clientY}),t.addListener(e,"touchmove",function(e){var t=e.touches;if(t.length>1)return;var s=t[0];e.wheelX=r-s.clientX,e.wheelY=i-s.clientY,r=s.clientX,i=s.clientY,n(e)})},t.addMouseWheelListener=function(e,n){"onmousewheel"in e?t.addListener(e,"mousewheel",function(e){var t=8;e.wheelDeltaX!==undefined?(e.wheelX=-e.wheelDeltaX/t,e.wheelY=-e.wheelDeltaY/t):(e.wheelX=0,e.wheelY=-e.wheelDelta/t),n(e)}):"onwheel"in e?t.addListener(e,"wheel",function(e){var t=.35;switch(e.deltaMode){case e.DOM_DELTA_PIXEL:e.wheelX=e.deltaX*t||0,e.wheelY=e.deltaY*t||0;break;case e.DOM_DELTA_LINE:case e.DOM_DELTA_PAGE:e.wheelX=(e.deltaX||0)*5,e.wheelY=(e.deltaY||0)*5}n(e)}):t.addListener(e,"DOMMouseScroll",function(e){e.axis&&e.axis==e.HORIZONTAL_AXIS?(e.wheelX=(e.detail||0)*5,e.wheelY=0):(e.wheelX=0,e.wheelY=(e.detail||0)*5),n(e)})},t.addMultiMouseDownListener=function(e,n,r,s){function c(e){t.getButton(e)!==0?o=0:e.detail>1?(o++,o>4&&(o=1)):o=1;if(i.isIE){var c=Math.abs(e.clientX-u)>5||Math.abs(e.clientY-a)>5;if(!f||c)o=1;f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),o==1&&(u=e.clientX,a=e.clientY)}e._clicks=o,r[s]("mousedown",e);if(o>4)o=0;else if(o>1)return r[s](l[o],e)}function h(e){o=2,f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),r[s]("mousedown",e),r[s](l[o],e)}var o=0,u,a,f,l={2:"dblclick",3:"tripleclick",4:"quadclick"};Array.isArray(e)||(e=[e]),e.forEach(function(e){t.addListener(e,"mousedown",c),i.isOldIE&&t.addListener(e,"dblclick",h)})};var u=!i.isMac||!i.isOpera||"KeyboardEvent"in window?function(e){return 0|(e.ctrlKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.metaKey?8:0)}:function(e){return 0|(e.metaKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.ctrlKey?8:0)};t.getModifierString=function(e){return r.KEY_MODS[u(e)]},t.addCommandKeyListener=function(e,n){var r=t.addListener;if(i.isOldGecko||i.isOpera&&!("KeyboardEvent"in window)){var o=null;r(e,"keydown",function(e){o=e.keyCode}),r(e,"keypress",function(e){return a(n,e,o)})}else{var u=null;r(e,"keydown",function(e){s[e.keyCode]=(s[e.keyCode]||0)+1;var t=a(n,e,e.keyCode);return u=e.defaultPrevented,t}),r(e,"keypress",function(e){u&&(e.ctrlKey||e.altKey||e.shiftKey||e.metaKey)&&(t.stopEvent(e),u=null)}),r(e,"keyup",function(e){s[e.keyCode]=null}),s||(f(),r(window,"focus",f))}};if(typeof window=="object"&&window.postMessage&&!i.isOldIE){var l=1;t.nextTick=function(e,n){n=n||window;var r="zero-timeout-message-"+l++,i=function(s){s.data==r&&(t.stopPropagation(s),t.removeListener(n,"message",i),e())};t.addListener(n,"message",i),n.postMessage(r,"*")}}t.$idleBlocked=!1,t.onIdle=function(e,n){return setTimeout(function r(){t.$idleBlocked?setTimeout(r,100):e()},n)},t.$idleBlockId=null,t.blockIdle=function(e){t.$idleBlockId&&clearTimeout(t.$idleBlockId),t.$idleBlocked=!0,t.$idleBlockId=setTimeout(function(){t.$idleBlocked=!1},e||100)},t.nextFrame=typeof window=="object"&&(window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame),t.nextFrame?t.nextFrame=t.nextFrame.bind(window):t.nextFrame=function(e){setTimeout(e,17)}}),ace.define("ace/range",["require","exports","module"],function(e,t,n){"use strict";var r=function(e,t){return e.row-t.row||e.column-t.column},i=function(e,t,n,r){this.start={row:e,column:t},this.end={row:n,column:r}};(function(){this.isEqual=function(e){return this.start.row===e.start.row&&this.end.row===e.end.row&&this.start.column===e.start.column&&this.end.column===e.end.column},this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(e,t){return this.compare(e,t)==0},this.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},this.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},this.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},this.isStart=function(e,t){return this.start.row==e&&this.start.column==t},this.setStart=function(e,t){typeof e=="object"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){typeof e=="object"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},this.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},this.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},this.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?tthis.end.column?1:0:ethis.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},this.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},this.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.clipRows=function(e,t){if(this.end.row>t)var n={row:t+1,column:0};else if(this.end.rowt)var r={row:t+1,column:0};else if(this.start.row0){t&1&&(n+=e);if(t>>=1)e+=e}return n};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;n63,l=400,c=e("../lib/keys"),h=c.KEY_MODS,p=i.isIOS,d=p?/\s/:/\n/,v=function(e,t){function W(){x=!0,n.blur(),n.focus(),x=!1}function V(e){e.keyCode==27&&n.value.lengthC&&T[s]=="\n")o=c.end;else if(rC&&T.slice(0,s).split("\n").length>2)o=c.down;else if(s>C&&T[s-1]==" ")o=c.right,u=h.option;else if(s>C||s==C&&C!=N&&r==s)o=c.right;r!==s&&(u|=h.shift),o&&(t.onCommandKey(null,u,o),N=r,C=s,A(""))};document.addEventListener("selectionchange",s),t.on("destroy",function(){document.removeEventListener("selectionchange",s)})}var n=s.createElement("textarea");n.className="ace_text-input",n.setAttribute("wrap","off"),n.setAttribute("autocorrect","off"),n.setAttribute("autocapitalize","off"),n.setAttribute("spellcheck",!1),n.style.opacity="0",e.insertBefore(n,e.firstChild);var v=!1,m=!1,g=!1,y=!1,b="",w=!0,E=!1;i.isMobile||(n.style.fontSize="1px");var S=!1,x=!1,T="",N=0,C=0;try{var k=document.activeElement===n}catch(L){}r.addListener(n,"blur",function(e){if(x)return;t.onBlur(e),k=!1}),r.addListener(n,"focus",function(e){if(x)return;k=!0;if(i.isEdge)try{if(!document.hasFocus())return}catch(e){}t.onFocus(e),i.isEdge?setTimeout(A):A()}),this.$focusScroll=!1,this.focus=function(){if(b||f||this.$focusScroll=="browser")return n.focus({preventScroll:!0});var e=n.style.top;n.style.position="fixed",n.style.top="0px";try{var t=n.getBoundingClientRect().top!=0}catch(r){return}var i=[];if(t){var s=n.parentElement;while(s&&s.nodeType==1)i.push(s),s.setAttribute("ace_nocontext",!0),!s.parentElement&&s.getRootNode?s=s.getRootNode().host:s=s.parentElement}n.focus({preventScroll:!0}),t&&i.forEach(function(e){e.removeAttribute("ace_nocontext")}),setTimeout(function(){n.style.position="",n.style.top=="0px"&&(n.style.top=e)},0)},this.blur=function(){n.blur()},this.isFocused=function(){return k},t.on("beforeEndOperation",function(){if(t.curOp&&t.curOp.command.name=="insertstring")return;g&&(T=n.value="",z()),A()});var A=p?function(e){if(!k||v&&!e||y)return;e||(e="");var r="\n ab"+e+"cde fg\n";r!=n.value&&(n.value=T=r);var i=4,s=4+(e.length||(t.selection.isEmpty()?0:1));(N!=i||C!=s)&&n.setSelectionRange(i,s),N=i,C=s}:function(){if(g||y)return;if(!k&&!D)return;g=!0;var e=t.selection,r=e.getRange(),i=e.cursor.row,s=r.start.column,o=r.end.column,u=t.session.getLine(i);if(r.start.row!=i){var a=t.session.getLine(i-1);s=r.start.rowi+1?f.length:o,o+=u.length+1,u=u+"\n"+f}u.length>l&&(s=T.length&&e.value===T&&T&&e.selectionEnd!==C},M=function(e){if(g)return;v?v=!1:O(n)&&(t.selectAll(),A())},_=null;this.setInputHandler=function(e){_=e},this.getInputHandler=function(){return _};var D=!1,P=function(e,r){D&&(D=!1);if(m)return A(),e&&t.onPaste(e),m=!1,"";var i=n.selectionStart,s=n.selectionEnd,o=N,u=T.length-C,a=e,f=e.length-i,l=e.length-s,c=0;while(o>0&&T[c]==e[c])c++,o--;a=a.slice(c),c=1;while(u>0&&T.length-c>N-1&&T[T.length-c]==e[e.length-c])c++,u--;return f-=c-1,l-=c-1,a=a.slice(0,a.length-c+1),!r&&f==a.length&&!o&&!u&&!l?"":(y=!0,a&&!o&&!u&&!f&&!l||S?t.onTextInput(a):t.onTextInput(a,{extendLeft:o,extendRight:u,restoreStart:f,restoreEnd:l}),y=!1,T=e,N=i,C=s,a)},H=function(e){if(g)return U();var t=n.value,r=P(t,!0);(t.length>l+100||d.test(r))&&A()},B=function(e,t,n){var r=e.clipboardData||window.clipboardData;if(!r||u)return;var i=a||n?"Text":"text/plain";try{return t?r.setData(i,t)!==!1:r.getData(i)}catch(e){if(!n)return B(e,t,!0)}},j=function(e,i){var s=t.getCopyText();if(!s)return r.preventDefault(e);B(e,s)?(p&&(A(s),v=s,setTimeout(function(){v=!1},10)),i?t.onCut():t.onCopy(),r.preventDefault(e)):(v=!0,n.value=s,n.select(),setTimeout(function(){v=!1,A(),i?t.onCut():t.onCopy()}))},F=function(e){j(e,!0)},I=function(e){j(e,!1)},q=function(e){var s=B(e);typeof s=="string"?(s&&t.onPaste(s,e),i.isIE&&setTimeout(A),r.preventDefault(e)):(n.value="",m=!0)};r.addCommandKeyListener(n,t.onCommandKey.bind(t)),r.addListener(n,"select",M),r.addListener(n,"input",H),r.addListener(n,"cut",F),r.addListener(n,"copy",I),r.addListener(n,"paste",q),(!("oncut"in n)||!("oncopy"in n)||!("onpaste"in n))&&r.addListener(e,"keydown",function(e){if(i.isMac&&!e.metaKey||!e.ctrlKey)return;switch(e.keyCode){case 67:I(e);break;case 86:q(e);break;case 88:F(e)}});var R=function(e){if(g||!t.onCompositionStart||t.$readOnly)return;g={};if(S)return;setTimeout(U,0),t.on("mousedown",W);var r=t.getSelectionRange();r.end.row=r.start.row,r.end.column=r.start.column,g.markerRange=r,g.selectionStart=N,t.onCompositionStart(g),g.useTextareaForIME?(n.value="",T="",N=0,C=0):(n.msGetInputContext&&(g.context=n.msGetInputContext()),n.getInputContext&&(g.context=n.getInputContext()))},U=function(){if(!g||!t.onCompositionUpdate||t.$readOnly)return;if(S)return W();if(g.useTextareaForIME)t.onCompositionUpdate(n.value);else{var e=n.value;P(e),g.markerRange&&(g.context&&(g.markerRange.start.column=g.selectionStart=g.context.compositionStartOffset),g.markerRange.end.column=g.markerRange.start.column+C-g.selectionStart)}},z=function(e){if(!t.onCompositionEnd||t.$readOnly)return;g=!1,t.onCompositionEnd(),t.off("mousedown",W),e&&H()},X=o.delayedCall(U,50).schedule.bind(null,null);r.addListener(n,"compositionstart",R),r.addListener(n,"compositionupdate",U),r.addListener(n,"keyup",V),r.addListener(n,"keydown",X),r.addListener(n,"compositionend",z),this.getElement=function(){return n},this.setCommandMode=function(e){S=e,n.readOnly=!1},this.setReadOnly=function(e){S||(n.readOnly=e)},this.setCopyWithEmptySelection=function(e){E=e},this.onContextMenu=function(e){D=!0,A(),t._emit("nativecontextmenu",{target:t,domEvent:e}),this.moveToMouse(e,!0)},this.moveToMouse=function(e,o){b||(b=n.style.cssText),n.style.cssText=(o?"z-index:100000;":"")+(i.isIE?"opacity:0.1;":"")+"text-indent: -"+(N+C)*t.renderer.characterWidth*.5+"px;";var u=t.container.getBoundingClientRect(),a=s.computedStyle(t.container),f=u.top+(parseInt(a.borderTopWidth)||0),l=u.left+(parseInt(u.borderLeftWidth)||0),c=u.bottom-f-n.clientHeight-2,h=function(e){n.style.left=e.clientX-l-2+"px",n.style.top=Math.min(e.clientY-f-2,c)+"px"};h(e);if(e.type!="mousedown")return;t.renderer.$keepTextAreaAtCursor&&(t.renderer.$keepTextAreaAtCursor=null),clearTimeout($),i.isWin&&r.capture(t.container,h,J)},this.onContextMenuClose=J;var $,K=function(e){t.textInput.onContextMenu(e),J()};r.addListener(n,"mouseup",K),r.addListener(n,"mousedown",function(e){e.preventDefault(),J()}),r.addListener(t.renderer.scroller,"contextmenu",K),r.addListener(n,"contextmenu",K),p&&Q(e,t,n)};t.TextInput=v}),ace.define("ace/mouse/default_handlers",["require","exports","module","ace/lib/useragent"],function(e,t,n){"use strict";function o(e){e.$clickSelection=null;var t=e.editor;t.setDefaultHandler("mousedown",this.onMouseDown.bind(e)),t.setDefaultHandler("dblclick",this.onDoubleClick.bind(e)),t.setDefaultHandler("tripleclick",this.onTripleClick.bind(e)),t.setDefaultHandler("quadclick",this.onQuadClick.bind(e)),t.setDefaultHandler("mousewheel",this.onMouseWheel.bind(e)),t.setDefaultHandler("touchmove",this.onTouchMove.bind(e));var n=["select","startSelect","selectEnd","selectAllEnd","selectByWordsEnd","selectByLinesEnd","dragWait","dragWaitEnd","focusWait"];n.forEach(function(t){e[t]=this[t]},this),e.selectByLines=this.extendSelectionBy.bind(e,"getLineRange"),e.selectByWords=this.extendSelectionBy.bind(e,"getWordRange")}function u(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}function a(e,t){if(e.start.row==e.end.row)var n=2*t.column-e.start.column-e.end.column;else if(e.start.row==e.end.row-1&&!e.start.column&&!e.end.column)var n=t.column-4;else var n=2*t.row-e.start.row-e.end.row;return n<0?{cursor:e.start,anchor:e.end}:{cursor:e.end,anchor:e.start}}var r=e("../lib/useragent"),i=0,s=550;(function(){this.onMouseDown=function(e){var t=e.inSelection(),n=e.getDocumentPosition();this.mousedownEvent=e;var i=this.editor,s=e.getButton();if(s!==0){var o=i.getSelectionRange(),u=o.isEmpty();(u||s==1)&&i.selection.moveToPosition(n),s==2&&(i.textInput.onContextMenu(e.domEvent),r.isMozilla||e.preventDefault());return}this.mousedownEvent.time=Date.now();if(t&&!i.isFocused()){i.focus();if(this.$focusTimeout&&!this.$clickSelection&&!i.inMultiSelectMode){this.setState("focusWait"),this.captureMouse(e);return}}return this.captureMouse(e),this.startSelect(n,e.domEvent._clicks>1),e.preventDefault()},this.startSelect=function(e,t){e=e||this.editor.renderer.screenToTextCoordinates(this.x,this.y);var n=this.editor;if(!this.mousedownEvent)return;this.mousedownEvent.getShiftKey()?n.selection.selectToPosition(e):t||n.selection.moveToPosition(e),t||this.select(),n.renderer.scroller.setCapture&&n.renderer.scroller.setCapture(),n.setStyle("ace_selecting"),this.setState("select")},this.select=function(){var e,t=this.editor,n=t.renderer.screenToTextCoordinates(this.x,this.y);if(this.$clickSelection){var r=this.$clickSelection.comparePoint(n);if(r==-1)e=this.$clickSelection.end;else if(r==1)e=this.$clickSelection.start;else{var i=a(this.$clickSelection,n);n=i.cursor,e=i.anchor}t.selection.setSelectionAnchor(e.row,e.column)}t.selection.selectToPosition(n),t.renderer.scrollCursorIntoView()},this.extendSelectionBy=function(e){var t,n=this.editor,r=n.renderer.screenToTextCoordinates(this.x,this.y),i=n.selection[e](r.row,r.column);if(this.$clickSelection){var s=this.$clickSelection.comparePoint(i.start),o=this.$clickSelection.comparePoint(i.end);if(s==-1&&o<=0){t=this.$clickSelection.end;if(i.end.row!=r.row||i.end.column!=r.column)r=i.start}else if(o==1&&s>=0){t=this.$clickSelection.start;if(i.start.row!=r.row||i.start.column!=r.column)r=i.end}else if(s==-1&&o==1)r=i.end,t=i.start;else{var u=a(this.$clickSelection,r);r=u.cursor,t=u.anchor}n.selection.setSelectionAnchor(t.row,t.column)}n.selection.selectToPosition(r),n.renderer.scrollCursorIntoView()},this.selectEnd=this.selectAllEnd=this.selectByWordsEnd=this.selectByLinesEnd=function(){this.$clickSelection=null,this.editor.unsetStyle("ace_selecting"),this.editor.renderer.scroller.releaseCapture&&this.editor.renderer.scroller.releaseCapture()},this.focusWait=function(){var e=u(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y),t=Date.now();(e>i||t-this.mousedownEvent.time>this.$focusTimeout)&&this.startSelect(this.mousedownEvent.getDocumentPosition())},this.onDoubleClick=function(e){var t=e.getDocumentPosition(),n=this.editor,r=n.session,i=r.getBracketRange(t);i?(i.isEmpty()&&(i.start.column--,i.end.column++),this.setState("select")):(i=n.selection.getWordRange(t.row,t.column),this.setState("selectByWords")),this.$clickSelection=i,this.select()},this.onTripleClick=function(e){var t=e.getDocumentPosition(),n=this.editor;this.setState("selectByLines");var r=n.getSelectionRange();r.isMultiLine()&&r.contains(t.row,t.column)?(this.$clickSelection=n.selection.getLineRange(r.start.row),this.$clickSelection.end=n.selection.getLineRange(r.end.row).end):this.$clickSelection=n.selection.getLineRange(t.row),this.select()},this.onQuadClick=function(e){var t=this.editor;t.selectAll(),this.$clickSelection=t.getSelectionRange(),this.setState("selectAll")},this.onMouseWheel=function(e){if(e.getAccelKey())return;e.getShiftKey()&&e.wheelY&&!e.wheelX&&(e.wheelX=e.wheelY,e.wheelY=0);var t=this.editor;this.$lastScroll||(this.$lastScroll={t:0,vx:0,vy:0,allowed:0});var n=this.$lastScroll,r=e.domEvent.timeStamp,i=r-n.t,o=i?e.wheelX/i:n.vx,u=i?e.wheelY/i:n.vy;i=1&&t.renderer.isScrollableBy(e.wheelX*e.speed,0)&&(f=!0),a<=1&&t.renderer.isScrollableBy(0,e.wheelY*e.speed)&&(f=!0);if(f)n.allowed=r;else if(r-n.allowedt.session.documentToScreenRow(l.row,l.column))return c()}if(f==s)return;f=s.text.join("
"),i.setHtml(f),i.show(),t._signal("showGutterTooltip",i),t.on("mousewheel",c);if(e.$tooltipFollowsMouse)h(u);else{var p=u.domEvent.target,d=p.getBoundingClientRect(),v=i.getElement().style;v.left=d.right+"px",v.top=d.bottom+"px"}}function c(){o&&(o=clearTimeout(o)),f&&(i.hide(),f=null,t._signal("hideGutterTooltip",i),t.removeEventListener("mousewheel",c))}function h(e){i.setPosition(e.x,e.y)}var t=e.editor,n=t.renderer.$gutterLayer,i=new a(t.container);e.editor.setDefaultHandler("guttermousedown",function(r){if(!t.isFocused()||r.getButton()!=0)return;var i=n.getRegion(r);if(i=="foldWidgets")return;var s=r.getDocumentPosition().row,o=t.session.selection;if(r.getShiftKey())o.selectTo(s,0);else{if(r.domEvent.detail==2)return t.selectAll(),r.preventDefault();e.$clickSelection=t.selection.getLineRange(s)}return e.setState("selectByLines"),e.captureMouse(r),r.preventDefault()});var o,u,f;e.editor.setDefaultHandler("guttermousemove",function(t){var n=t.domEvent.target||t.domEvent.srcElement;if(r.hasCssClass(n,"ace_fold-widget"))return c();f&&e.$tooltipFollowsMouse&&h(t),u=t;if(o)return;o=setTimeout(function(){o=null,u&&!e.isMousePressed?l():c()},50)}),s.addListener(t.renderer.$gutter,"mouseout",function(e){u=null;if(!f||o)return;o=setTimeout(function(){o=null,c()},50)}),t.on("changeSession",c)}function a(e){o.call(this,e)}var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/event"),o=e("../tooltip").Tooltip;i.inherits(a,o),function(){this.setPosition=function(e,t){var n=window.innerWidth||document.documentElement.clientWidth,r=window.innerHeight||document.documentElement.clientHeight,i=this.getWidth(),s=this.getHeight();e+=15,t+=15,e+i>n&&(e-=e+i-n),t+s>r&&(t-=20+s),o.prototype.setPosition.call(this,e,t)}}.call(a.prototype),t.GutterHandler=u}),ace.define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=t.MouseEvent=function(e,t){this.domEvent=e,this.editor=t,this.x=this.clientX=e.clientX,this.y=this.clientY=e.clientY,this.$pos=null,this.$inSelection=null,this.propagationStopped=!1,this.defaultPrevented=!1};(function(){this.stopPropagation=function(){r.stopPropagation(this.domEvent),this.propagationStopped=!0},this.preventDefault=function(){r.preventDefault(this.domEvent),this.defaultPrevented=!0},this.stop=function(){this.stopPropagation(),this.preventDefault()},this.getDocumentPosition=function(){return this.$pos?this.$pos:(this.$pos=this.editor.renderer.screenToTextCoordinates(this.clientX,this.clientY),this.$pos)},this.inSelection=function(){if(this.$inSelection!==null)return this.$inSelection;var e=this.editor,t=e.getSelectionRange();if(t.isEmpty())this.$inSelection=!1;else{var n=this.getDocumentPosition();this.$inSelection=t.contains(n.row,n.column)}return this.$inSelection},this.getButton=function(){return r.getButton(this.domEvent)},this.getShiftKey=function(){return this.domEvent.shiftKey},this.getAccelKey=i.isMac?function(){return this.domEvent.metaKey}:function(){return this.domEvent.ctrlKey}}).call(s.prototype)}),ace.define("ace/mouse/dragdrop_handler",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function f(e){function T(e,n){var r=Date.now(),i=!n||e.row!=n.row,s=!n||e.column!=n.column;if(!S||i||s)t.moveCursorToPosition(e),S=r,x={x:p,y:d};else{var o=l(x.x,x.y,p,d);o>a?S=null:r-S>=u&&(t.renderer.scrollCursorIntoView(),S=null)}}function N(e,n){var r=Date.now(),i=t.renderer.layerConfig.lineHeight,s=t.renderer.layerConfig.characterWidth,u=t.renderer.scroller.getBoundingClientRect(),a={x:{left:p-u.left,right:u.right-p},y:{top:d-u.top,bottom:u.bottom-d}},f=Math.min(a.x.left,a.x.right),l=Math.min(a.y.top,a.y.bottom),c={row:e.row,column:e.column};f/s<=2&&(c.column+=a.x.left=o&&t.renderer.scrollCursorIntoView(c):E=r:E=null}function C(){var e=g;g=t.renderer.screenToTextCoordinates(p,d),T(g,e),N(g,e)}function k(){m=t.selection.toOrientedRange(),h=t.session.addMarker(m,"ace_selection",t.getSelectionStyle()),t.clearSelection(),t.isFocused()&&t.renderer.$cursorLayer.setBlinking(!1),clearInterval(v),C(),v=setInterval(C,20),y=0,i.addListener(document,"mousemove",O)}function L(){clearInterval(v),t.session.removeMarker(h),h=null,t.selection.fromOrientedRange(m),t.isFocused()&&!w&&t.renderer.$cursorLayer.setBlinking(!t.getReadOnly()),m=null,g=null,y=0,E=null,S=null,i.removeListener(document,"mousemove",O)}function O(){A==null&&(A=setTimeout(function(){A!=null&&h&&L()},20))}function M(e){var t=e.types;return!t||Array.prototype.some.call(t,function(e){return e=="text/plain"||e=="Text"})}function _(e){var t=["copy","copymove","all","uninitialized"],n=["move","copymove","linkmove","all","uninitialized"],r=s.isMac?e.altKey:e.ctrlKey,i="uninitialized";try{i=e.dataTransfer.effectAllowed.toLowerCase()}catch(e){}var o="none";return r&&t.indexOf(i)>=0?o="copy":n.indexOf(i)>=0?o="move":t.indexOf(i)>=0&&(o="copy"),o}var t=e.editor,n=r.createElement("img");n.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",s.isOpera&&(n.style.cssText="width:1px;height:1px;position:fixed;top:0;left:0;z-index:2147483647;opacity:0;");var f=["dragWait","dragWaitEnd","startDrag","dragReadyEnd","onMouseDrag"];f.forEach(function(t){e[t]=this[t]},this),t.addEventListener("mousedown",this.onMouseDown.bind(e));var c=t.container,h,p,d,v,m,g,y=0,b,w,E,S,x;this.onDragStart=function(e){if(this.cancelDrag||!c.draggable){var r=this;return setTimeout(function(){r.startSelect(),r.captureMouse(e)},0),e.preventDefault()}m=t.getSelectionRange();var i=e.dataTransfer;i.effectAllowed=t.getReadOnly()?"copy":"copyMove",s.isOpera&&(t.container.appendChild(n),n.scrollTop=0),i.setDragImage&&i.setDragImage(n,0,0),s.isOpera&&t.container.removeChild(n),i.clearData(),i.setData("Text",t.session.getTextRange()),w=!0,this.setState("drag")},this.onDragEnd=function(e){c.draggable=!1,w=!1,this.setState(null);if(!t.getReadOnly()){var n=e.dataTransfer.dropEffect;!b&&n=="move"&&t.session.remove(t.getSelectionRange()),t.renderer.$cursorLayer.setBlinking(!0)}this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle("")},this.onDragEnter=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||k(),y++,e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragOver=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||(k(),y++),A!==null&&(A=null),e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragLeave=function(e){y--;if(y<=0&&h)return L(),b=null,i.preventDefault(e)},this.onDrop=function(e){if(!g)return;var n=e.dataTransfer;if(w)switch(b){case"move":m.contains(g.row,g.column)?m={start:g,end:g}:m=t.moveText(m,g);break;case"copy":m=t.moveText(m,g,!0)}else{var r=n.getData("Text");m={start:g,end:t.session.insert(g,r)},t.focus(),b=null}return L(),i.preventDefault(e)},i.addListener(c,"dragstart",this.onDragStart.bind(e)),i.addListener(c,"dragend",this.onDragEnd.bind(e)),i.addListener(c,"dragenter",this.onDragEnter.bind(e)),i.addListener(c,"dragover",this.onDragOver.bind(e)),i.addListener(c,"dragleave",this.onDragLeave.bind(e)),i.addListener(c,"drop",this.onDrop.bind(e));var A=null}function l(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}var r=e("../lib/dom"),i=e("../lib/event"),s=e("../lib/useragent"),o=200,u=200,a=5;(function(){this.dragWait=function(){var e=Date.now()-this.mousedownEvent.time;e>this.editor.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(){var e=this.editor.container;e.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()),this.selectEnd()},this.dragReadyEnd=function(e){this.editor.renderer.$cursorLayer.setBlinking(!this.editor.getReadOnly()),this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle(""),this.dragWaitEnd()},this.startDrag=function(){this.cancelDrag=!1;var e=this.editor,t=e.container;t.draggable=!0,e.renderer.$cursorLayer.setBlinking(!1),e.setStyle("ace_dragging");var n=s.isWin?"default":"move";e.renderer.setCursorStyle(n),this.setState("dragReady")},this.onMouseDrag=function(e){var t=this.editor.container;if(s.isIE&&this.state=="dragReady"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>3&&t.dragDrop()}if(this.state==="dragWait"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>0&&(t.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()))}},this.onMouseDown=function(e){if(!this.$dragEnabled)return;this.mousedownEvent=e;var t=this.editor,n=e.inSelection(),r=e.getButton(),i=e.domEvent.detail||1;if(i===1&&r===0&&n){if(e.editor.inMultiSelectMode&&(e.getAccelKey()||e.getShiftKey()))return;this.mousedownEvent.time=Date.now();var o=e.domEvent.target||e.domEvent.srcElement;"unselectable"in o&&(o.unselectable="on");if(t.getDragDelay()){if(s.isWebKit){this.cancelDrag=!0;var u=t.container;u.draggable=!0}this.setState("dragWait")}else this.startDrag();this.captureMouse(e,this.onMouseDrag.bind(this)),e.defaultPrevented=!0}}}).call(f.prototype),t.DragdropHandler=f}),ace.define("ace/lib/net",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("./dom");t.get=function(e,t){var n=new XMLHttpRequest;n.open("GET",e,!0),n.onreadystatechange=function(){n.readyState===4&&t(n.responseText)},n.send(null)},t.loadScript=function(e,t){var n=r.getDocumentHead(),i=document.createElement("script");i.src=e,n.appendChild(i),i.onload=i.onreadystatechange=function(e,n){if(n||!i.readyState||i.readyState=="loaded"||i.readyState=="complete")i=i.onload=i.onreadystatechange=null,n||t()}},t.qualifyURL=function(e){var t=document.createElement("a");return t.href=e,t.href}}),ace.define("ace/lib/event_emitter",["require","exports","module"],function(e,t,n){"use strict";var r={},i=function(){this.propagationStopped=!0},s=function(){this.defaultPrevented=!0};r._emit=r._dispatchEvent=function(e,t){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var n=this._eventRegistry[e]||[],r=this._defaultHandlers[e];if(!n.length&&!r)return;if(typeof t!="object"||!t)t={};t.type||(t.type=e),t.stopPropagation||(t.stopPropagation=i),t.preventDefault||(t.preventDefault=s),n=n.slice();for(var o=0;o1&&(i=n[n.length-2]);var o=a[t+"Path"];return o==null?o=a.basePath:r=="/"&&(t=r=""),o&&o.slice(-1)!="/"&&(o+="/"),o+t+r+i+this.get("suffix")},t.setModuleUrl=function(e,t){return a.$moduleUrls[e]=t},t.$loading={},t.loadModule=function(n,r){var i,o;Array.isArray(n)&&(o=n[0],n=n[1]);try{i=e(n)}catch(u){}if(i&&!t.$loading[n])return r&&r(i);t.$loading[n]||(t.$loading[n]=[]),t.$loading[n].push(r);if(t.$loading[n].length>1)return;var a=function(){e([n],function(e){t._emit("load.module",{name:n,module:e});var r=t.$loading[n];t.$loading[n]=null,r.forEach(function(t){t&&t(e)})})};if(!t.get("packaged"))return a();s.loadScript(t.moduleUrl(n,o),a),f()};var f=function(){!a.basePath&&!a.workerPath&&!a.modePath&&!a.themePath&&!Object.keys(a.$moduleUrls).length&&(console.error("Unable to infer path to ace from script src,","use ace.config.set('basePath', 'path') to enable dynamic loading of modes and themes","or with webpack use ace/webpack-resolver"),f=function(){})};t.init=l}),ace.define("ace/mouse/mouse_handler",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/mouse/default_handlers","ace/mouse/default_gutter_handler","ace/mouse/mouse_event","ace/mouse/dragdrop_handler","ace/config"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=e("./default_handlers").DefaultHandlers,o=e("./default_gutter_handler").GutterHandler,u=e("./mouse_event").MouseEvent,a=e("./dragdrop_handler").DragdropHandler,f=e("../config"),l=function(e){var t=this;this.editor=e,new s(this),new o(this),new a(this);var n=function(t){var n=!document.hasFocus||!document.hasFocus()||!e.isFocused()&&document.activeElement==(e.textInput&&e.textInput.getElement());n&&window.focus(),e.focus()},u=e.renderer.getMouseEventTarget();r.addListener(u,"click",this.onMouseEvent.bind(this,"click")),r.addListener(u,"mousemove",this.onMouseMove.bind(this,"mousemove")),r.addMultiMouseDownListener([u,e.renderer.scrollBarV&&e.renderer.scrollBarV.inner,e.renderer.scrollBarH&&e.renderer.scrollBarH.inner,e.textInput&&e.textInput.getElement()].filter(Boolean),[400,300,250],this,"onMouseEvent"),r.addMouseWheelListener(e.container,this.onMouseWheel.bind(this,"mousewheel")),r.addTouchMoveListener(e.container,this.onTouchMove.bind(this,"touchmove"));var f=e.renderer.$gutter;r.addListener(f,"mousedown",this.onMouseEvent.bind(this,"guttermousedown")),r.addListener(f,"click",this.onMouseEvent.bind(this,"gutterclick")),r.addListener(f,"dblclick",this.onMouseEvent.bind(this,"gutterdblclick")),r.addListener(f,"mousemove",this.onMouseEvent.bind(this,"guttermousemove")),r.addListener(u,"mousedown",n),r.addListener(f,"mousedown",n),i.isIE&&e.renderer.scrollBarV&&(r.addListener(e.renderer.scrollBarV.element,"mousedown",n),r.addListener(e.renderer.scrollBarH.element,"mousedown",n)),e.on("mousemove",function(n){if(t.state||t.$dragDelay||!t.$dragEnabled)return;var r=e.renderer.screenToTextCoordinates(n.x,n.y),i=e.session.selection.getRange(),s=e.renderer;!i.isEmpty()&&i.insideStart(r.row,r.column)?s.setCursorStyle("default"):s.setCursorStyle("")})};(function(){this.onMouseEvent=function(e,t){this.editor._emit(e,new u(t,this.editor))},this.onMouseMove=function(e,t){var n=this.editor._eventRegistry&&this.editor._eventRegistry.mousemove;if(!n||!n.length)return;this.editor._emit(e,new u(t,this.editor))},this.onMouseWheel=function(e,t){var n=new u(t,this.editor);n.speed=this.$scrollSpeed*2,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.onTouchMove=function(e,t){var n=new u(t,this.editor);n.speed=1,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.setState=function(e){this.state=e},this.captureMouse=function(e,t){this.x=e.x,this.y=e.y,this.isMousePressed=!0;var n=this.editor,s=this.editor.renderer;s.$keepTextAreaAtCursor&&(s.$keepTextAreaAtCursor=null);var o=this,a=function(e){if(!e)return;if(i.isWebKit&&!e.which&&o.releaseMouse)return o.releaseMouse();o.x=e.clientX,o.y=e.clientY,t&&t(e),o.mouseEvent=new u(e,o.editor),o.$mouseMoved=!0},f=function(e){n.off("beforeEndOperation",c),clearInterval(h),l(),o[o.state+"End"]&&o[o.state+"End"](e),o.state="",s.$keepTextAreaAtCursor==null&&(s.$keepTextAreaAtCursor=!0,s.$moveTextAreaToCursor()),o.isMousePressed=!1,o.$onCaptureMouseMove=o.releaseMouse=null,e&&o.onMouseEvent("mouseup",e),n.endOperation()},l=function(){o[o.state]&&o[o.state](),o.$mouseMoved=!1};if(i.isOldIE&&e.domEvent.type=="dblclick")return setTimeout(function(){f(e)});var c=function(e){if(!o.releaseMouse)return;n.curOp.command.name&&n.curOp.selectionChanged&&(o[o.state+"End"]&&o[o.state+"End"](),o.state="",o.releaseMouse())};n.on("beforeEndOperation",c),n.startOperation({command:{name:"mouse"}}),o.$onCaptureMouseMove=a,o.releaseMouse=r.capture(this.editor.container,a,f);var h=setInterval(l,20)},this.releaseMouse=null,this.cancelContextMenu=function(){var e=function(t){if(t&&t.domEvent&&t.domEvent.type!="contextmenu")return;this.editor.off("nativecontextmenu",e),t&&t.domEvent&&r.stopEvent(t.domEvent)}.bind(this);setTimeout(e,10),this.editor.on("nativecontextmenu",e)}}).call(l.prototype),f.defineOptions(l.prototype,"mouseHandler",{scrollSpeed:{initialValue:2},dragDelay:{initialValue:i.isMac?150:0},dragEnabled:{initialValue:!0},focusTimeout:{initialValue:0},tooltipFollowsMouse:{initialValue:!0}}),t.MouseHandler=l}),ace.define("ace/mouse/fold_handler",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";function i(e){e.on("click",function(t){var n=t.getDocumentPosition(),i=e.session,s=i.getFoldAt(n.row,n.column,1);s&&(t.getAccelKey()?i.removeFold(s):i.expandFold(s),t.stop());var o=t.domEvent&&t.domEvent.target;o&&r.hasCssClass(o,"ace_inline_button")&&r.hasCssClass(o,"ace_toggle_wrap")&&(i.setOption("wrap",!0),e.renderer.scrollCursorIntoView())}),e.on("gutterclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session;i.foldWidgets&&i.foldWidgets[r]&&e.session.onFoldWidgetClick(r,t),e.isFocused()||e.focus(),t.stop()}}),e.on("gutterdblclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session,s=i.getParentFoldRangeData(r,!0),o=s.range||s.firstRange;if(o){r=o.start.row;var u=i.getFoldAt(r,i.getLine(r).length,1);u?i.removeFold(u):(i.addFold("...",o),e.renderer.scrollCursorIntoView({row:o.start.row,column:0}))}t.stop()}})}var r=e("../lib/dom");t.FoldHandler=i}),ace.define("ace/keyboard/keybinding",["require","exports","module","ace/lib/keys","ace/lib/event"],function(e,t,n){"use strict";var r=e("../lib/keys"),i=e("../lib/event"),s=function(e){this.$editor=e,this.$data={editor:e},this.$handlers=[],this.setDefaultHandler(e.commands)};(function(){this.setDefaultHandler=function(e){this.removeKeyboardHandler(this.$defaultHandler),this.$defaultHandler=e,this.addKeyboardHandler(e,0)},this.setKeyboardHandler=function(e){var t=this.$handlers;if(t[t.length-1]==e)return;while(t[t.length-1]&&t[t.length-1]!=this.$defaultHandler)this.removeKeyboardHandler(t[t.length-1]);this.addKeyboardHandler(e,1)},this.addKeyboardHandler=function(e,t){if(!e)return;typeof e=="function"&&!e.handleKeyboard&&(e.handleKeyboard=e);var n=this.$handlers.indexOf(e);n!=-1&&this.$handlers.splice(n,1),t==undefined?this.$handlers.push(e):this.$handlers.splice(t,0,e),n==-1&&e.attach&&e.attach(this.$editor)},this.removeKeyboardHandler=function(e){var t=this.$handlers.indexOf(e);return t==-1?!1:(this.$handlers.splice(t,1),e.detach&&e.detach(this.$editor),!0)},this.getKeyboardHandler=function(){return this.$handlers[this.$handlers.length-1]},this.getStatusText=function(){var e=this.$data,t=e.editor;return this.$handlers.map(function(n){return n.getStatusText&&n.getStatusText(t,e)||""}).filter(Boolean).join(" ")},this.$callKeyboardHandlers=function(e,t,n,r){var s,o=!1,u=this.$editor.commands;for(var a=this.$handlers.length;a--;){s=this.$handlers[a].handleKeyboard(this.$data,e,t,n,r);if(!s||!s.command)continue;s.command=="null"?o=!0:o=u.exec(s.command,this.$editor,s.args,r),o&&r&&e!=-1&&s.passEvent!=1&&s.command.passEvent!=1&&i.stopEvent(r);if(o)break}return!o&&e==-1&&(s={command:"insertstring"},o=u.exec("insertstring",this.$editor,t)),o&&this.$editor._signal&&this.$editor._signal("keyboardActivity",s),o},this.onCommandKey=function(e,t,n){var i=r.keyCodeToString(n);this.$callKeyboardHandlers(t,i,n,e)},this.onTextInput=function(e){this.$callKeyboardHandlers(-1,e)}}).call(s.prototype),t.KeyBinding=s}),ace.define("ace/lib/bidiutil",["require","exports","module"],function(e,t,n){"use strict";function F(e,t,n,r){var i=s?d:p,c=null,h=null,v=null,m=0,g=null,y=null,b=-1,w=null,E=null,T=[];if(!r)for(w=0,r=[];w0)if(g==16){for(w=b;w-1){for(w=b;w=0;C--){if(r[C]!=N)break;t[C]=s}}}function I(e,t,n){if(o=e){u=i+1;while(u=e)u++;for(a=i,l=u-1;a=t.length||(o=n[r-1])!=b&&o!=w||(c=t[r+1])!=b&&c!=w)return E;return u&&(c=w),c==o?c:E;case k:o=r>0?n[r-1]:S;if(o==b&&r+10&&n[r-1]==b)return b;if(u)return E;p=r+1,h=t.length;while(p=1425&&d<=2303||d==64286;o=t[p];if(v&&(o==y||o==T))return y}if(r<1||(o=t[r-1])==S)return E;return n[r-1];case S:return u=!1,f=!0,s;case x:return l=!0,E;case O:case M:case D:case P:case _:u=!1;case H:return E}}function R(e){var t=e.charCodeAt(0),n=t>>8;return n==0?t>191?g:B[t]:n==5?/[\u0591-\u05f4]/.test(e)?y:g:n==6?/[\u0610-\u061a\u064b-\u065f\u06d6-\u06e4\u06e7-\u06ed]/.test(e)?A:/[\u0660-\u0669\u066b-\u066c]/.test(e)?w:t==1642?L:/[\u06f0-\u06f9]/.test(e)?b:T:n==32&&t<=8287?j[t&255]:n==254?t>=65136?T:E:E}function U(e){return e>="\u064b"&&e<="\u0655"}var r=["\u0621","\u0641"],i=["\u063a","\u064a"],s=0,o=0,u=!1,a=!1,f=!1,l=!1,c=!1,h=!1,p=[[0,3,0,1,0,0,0],[0,3,0,1,2,2,0],[0,3,0,17,2,0,1],[0,3,5,5,4,1,0],[0,3,21,21,4,0,1],[0,3,5,5,4,2,0]],d=[[2,0,1,1,0,1,0],[2,0,1,1,0,2,0],[2,0,2,1,3,2,0],[2,0,2,33,3,1,1]],v=0,m=1,g=0,y=1,b=2,w=3,E=4,S=5,x=6,T=7,N=8,C=9,k=10,L=11,A=12,O=13,M=14,_=15,D=16,P=17,H=18,B=[H,H,H,H,H,H,H,H,H,x,S,x,N,S,H,H,H,H,H,H,H,H,H,H,H,H,H,H,S,S,S,x,N,E,E,L,L,L,E,E,E,E,E,k,C,k,C,C,b,b,b,b,b,b,b,b,b,b,C,E,E,E,E,E,E,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,E,E,E,E,E,E,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,E,E,E,E,H,H,H,H,H,H,S,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,C,E,L,L,L,L,E,E,E,E,g,E,E,H,E,E,L,L,b,b,E,g,E,E,E,b,g,E,E,E,E,E],j=[N,N,N,N,N,N,N,N,N,N,N,H,H,H,g,y,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,N,S,O,M,_,D,P,C,L,L,L,L,L,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,C,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,N];t.L=g,t.R=y,t.EN=b,t.ON_R=3,t.AN=4,t.R_H=5,t.B=6,t.RLE=7,t.DOT="\u00b7",t.doBidiReorder=function(e,n,r){if(e.length<2)return{};var i=e.split(""),o=new Array(i.length),u=new Array(i.length),a=[];s=r?m:v,F(i,a,i.length,n);for(var f=0;fT&&n[f]0&&i[f-1]==="\u0644"&&/\u0622|\u0623|\u0625|\u0627/.test(i[f])&&(a[f-1]=a[f]=t.R_H,f++);i[i.length-1]===t.DOT&&(a[i.length-1]=t.B),i[0]==="\u202b"&&(a[0]=t.RLE);for(var f=0;f=0&&(e=this.session.$docRowCache[n])}return e},this.getSplitIndex=function(){var e=0,t=this.session.$screenRowCache;if(t.length){var n,r=this.session.$getRowCacheIndex(t,this.currentRow);while(this.currentRow-e>0){n=this.session.$getRowCacheIndex(t,this.currentRow-e-1);if(n!==r)break;r=n,e++}}else e=this.currentRow;return e},this.updateRowLine=function(e,t){e===undefined&&(e=this.getDocumentRow());var n=e===this.session.getLength()-1,s=n?this.EOF:this.EOL;this.wrapIndent=0,this.line=this.session.getLine(e),this.isRtlDir=this.$isRtl||this.line.charAt(0)===this.RLE;if(this.session.$useWrapMode){var o=this.session.$wrapData[e];o&&(t===undefined&&(t=this.getSplitIndex()),t>0&&o.length?(this.wrapIndent=o.indent,this.wrapOffset=this.wrapIndent*this.charWidths[r.L],this.line=tt?this.session.getOverwrite()?e:e-1:t,i=r.getVisualFromLogicalIdx(n,this.bidiMap),s=this.bidiMap.bidiLevels,o=0;!this.session.getOverwrite()&&e<=t&&s[i]%2!==0&&i++;for(var u=0;ut&&s[i]%2===0&&(o+=this.charWidths[s[i]]),this.wrapIndent&&(o+=this.isRtlDir?-1*this.wrapOffset:this.wrapOffset),this.isRtlDir&&(o+=this.rtlLineOffset),o},this.getSelections=function(e,t){var n=this.bidiMap,r=n.bidiLevels,i,s=[],o=0,u=Math.min(e,t)-this.wrapIndent,a=Math.max(e,t)-this.wrapIndent,f=!1,l=!1,c=0;this.wrapIndent&&(o+=this.isRtlDir?-1*this.wrapOffset:this.wrapOffset);for(var h,p=0;p=u&&hn+s/2){n+=s;if(r===i.length-1){s=0;break}s=this.charWidths[i[++r]]}return r>0&&i[r-1]%2!==0&&i[r]%2===0?(e0&&i[r-1]%2===0&&i[r]%2!==0?t=1+(e>n?this.bidiMap.logicalFromVisual[r]:this.bidiMap.logicalFromVisual[r-1]):this.isRtlDir&&r===i.length-1&&s===0&&i[r-1]%2===0||!this.isRtlDir&&r===0&&i[r]%2!==0?t=1+this.bidiMap.logicalFromVisual[r]:(r>0&&i[r-1]%2!==0&&s!==0&&r--,t=this.bidiMap.logicalFromVisual[r]),t===0&&this.isRtlDir&&t++,t+this.wrapIndent}}).call(o.prototype),t.BidiHandler=o}),ace.define("ace/selection",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter","ace/range"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/lang"),s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=function(e){this.session=e,this.doc=e.getDocument(),this.clearSelection(),this.cursor=this.lead=this.doc.createAnchor(0,0),this.anchor=this.doc.createAnchor(0,0),this.$silent=!1;var t=this;this.cursor.on("change",function(e){t.$cursorChanged=!0,t.$silent||t._emit("changeCursor"),!t.$isEmpty&&!t.$silent&&t._emit("changeSelection"),!t.$keepDesiredColumnOnChange&&e.old.column!=e.value.column&&(t.$desiredColumn=null)}),this.anchor.on("change",function(){t.$anchorChanged=!0,!t.$isEmpty&&!t.$silent&&t._emit("changeSelection")})};(function(){r.implement(this,s),this.isEmpty=function(){return this.$isEmpty||this.anchor.row==this.lead.row&&this.anchor.column==this.lead.column},this.isMultiLine=function(){return!this.$isEmpty&&this.anchor.row!=this.cursor.row},this.getCursor=function(){return this.lead.getPosition()},this.setSelectionAnchor=function(e,t){this.$isEmpty=!1,this.anchor.setPosition(e,t)},this.getAnchor=this.getSelectionAnchor=function(){return this.$isEmpty?this.getSelectionLead():this.anchor.getPosition()},this.getSelectionLead=function(){return this.lead.getPosition()},this.isBackwards=function(){var e=this.anchor,t=this.lead;return e.row>t.row||e.row==t.row&&e.column>t.column},this.getRange=function(){var e=this.anchor,t=this.lead;return this.$isEmpty?o.fromPoints(t,t):this.isBackwards()?o.fromPoints(t,e):o.fromPoints(e,t)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},this.selectAll=function(){this.$setSelection(0,0,Number.MAX_VALUE,Number.MAX_VALUE)},this.setRange=this.setSelectionRange=function(e,t){var n=t?e.end:e.start,r=t?e.start:e.end;this.$setSelection(n.row,n.column,r.row,r.column)},this.$setSelection=function(e,t,n,r){var i=this.$isEmpty,s=this.inMultiSelectMode;this.$silent=!0,this.$cursorChanged=this.$anchorChanged=!1,this.anchor.setPosition(e,t),this.cursor.setPosition(n,r),this.$isEmpty=!o.comparePoints(this.anchor,this.cursor),this.$silent=!1,this.$cursorChanged&&this._emit("changeCursor"),(this.$cursorChanged||this.$anchorChanged||i!=this.$isEmpty||s)&&this._emit("changeSelection")},this.$moveSelection=function(e){var t=this.lead;this.$isEmpty&&this.setSelectionAnchor(t.row,t.column),e.call(this)},this.selectTo=function(e,t){this.$moveSelection(function(){this.moveCursorTo(e,t)})},this.selectToPosition=function(e){this.$moveSelection(function(){this.moveCursorToPosition(e)})},this.moveTo=function(e,t){this.clearSelection(),this.moveCursorTo(e,t)},this.moveToPosition=function(e){this.clearSelection(),this.moveCursorToPosition(e)},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.getWordRange=function(e,t){if(typeof t=="undefined"){var n=e||this.lead;e=n.row,t=n.column}return this.session.getWordRange(e,t)},this.selectWord=function(){this.setSelectionRange(this.getWordRange())},this.selectAWord=function(){var e=this.getCursor(),t=this.session.getAWordRange(e.row,e.column);this.setSelectionRange(t)},this.getLineRange=function(e,t){var n=typeof e=="number"?e:this.lead.row,r,i=this.session.getFoldLine(n);return i?(n=i.start.row,r=i.end.row):r=n,t===!0?new o(n,0,r,this.session.getLine(r).length):new o(n,0,r+1,0)},this.selectLine=function(){this.setSelectionRange(this.getLineRange())},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.wouldMoveIntoSoftTab=function(e,t,n){var r=e.column,i=e.column+t;return n<0&&(r=e.column-t,i=e.column),this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(r,i).split(" ").length-1==t},this.moveCursorLeft=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,-1))this.moveCursorTo(t.start.row,t.start.column);else if(e.column===0)e.row>0&&this.moveCursorTo(e.row-1,this.doc.getLine(e.row-1).length);else{var n=this.session.getTabSize();this.wouldMoveIntoSoftTab(e,n,-1)&&!this.session.getNavigateWithinSoftTabs()?this.moveCursorBy(0,-n):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,1))this.moveCursorTo(t.end.row,t.end.column);else if(this.lead.column==this.doc.getLine(this.lead.row).length)this.lead.row0&&(t.column=r)}}this.moveCursorTo(t.row,t.column)},this.moveCursorFileEnd=function(){var e=this.doc.getLength()-1,t=this.doc.getLine(e).length;this.moveCursorTo(e,t)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorLongWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t);this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var i=this.session.getFoldAt(e,t,1);if(i){this.moveCursorTo(i.end.row,i.end.column);return}this.session.nonTokenRe.exec(r)&&(t+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0,r=n.substring(t));if(t>=n.length){this.moveCursorTo(e,n.length),this.moveCursorRight(),e0&&this.moveCursorWordLeft();return}this.session.tokenRe.exec(s)&&(t-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0),this.moveCursorTo(e,t)},this.$shortWordEndIndex=function(e){var t=0,n,r=/\s/,i=this.session.tokenRe;i.lastIndex=0;if(this.session.tokenRe.exec(e))t=this.session.tokenRe.lastIndex;else{while((n=e[t])&&r.test(n))t++;if(t<1){i.lastIndex=0;while((n=e[t])&&!i.test(n)){i.lastIndex=0,t++;if(r.test(n)){if(t>2){t--;break}while((n=e[t])&&r.test(n))t++;if(t>2)break}}}}return i.lastIndex=0,t},this.moveCursorShortWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i=this.session.getFoldAt(e,t,1);if(i)return this.moveCursorTo(i.end.row,i.end.column);if(t==n.length){var s=this.doc.getLength();do e++,r=this.doc.getLine(e);while(e0&&/^\s*$/.test(r));t=r.length,/\s+$/.test(r)||(r="")}var s=i.stringReverse(r),o=this.$shortWordEndIndex(s);return this.moveCursorTo(e,t-o)},this.moveCursorWordRight=function(){this.session.$selectLongWords?this.moveCursorLongWordRight():this.moveCursorShortWordRight()},this.moveCursorWordLeft=function(){this.session.$selectLongWords?this.moveCursorLongWordLeft():this.moveCursorShortWordLeft()},this.moveCursorBy=function(e,t){var n=this.session.documentToScreenPosition(this.lead.row,this.lead.column),r;t===0&&(e!==0&&(this.session.$bidiHandler.isBidiRow(n.row,this.lead.row)?(r=this.session.$bidiHandler.getPosLeft(n.column),n.column=Math.round(r/this.session.$bidiHandler.charWidths[0])):r=n.column*this.session.$bidiHandler.charWidths[0]),this.$desiredColumn?n.column=this.$desiredColumn:this.$desiredColumn=n.column);var i=this.session.screenToDocumentPosition(n.row+e,n.column,r);e!==0&&t===0&&i.row===this.lead.row&&i.column===this.lead.column&&this.session.lineWidgets&&this.session.lineWidgets[i.row]&&(i.row>0||e>0)&&i.row++,this.moveCursorTo(i.row,i.column+t,t===0)},this.moveCursorToPosition=function(e){this.moveCursorTo(e.row,e.column)},this.moveCursorTo=function(e,t,n){var r=this.session.getFoldAt(e,t,1);r&&(e=r.start.row,t=r.start.column),this.$keepDesiredColumnOnChange=!0;var i=this.session.getLine(e);/[\uDC00-\uDFFF]/.test(i.charAt(t))&&i.charAt(t-1)&&(this.lead.row==e&&this.lead.column==t+1?t-=1:t+=1),this.lead.setPosition(e,t),this.$keepDesiredColumnOnChange=!1,n||(this.$desiredColumn=null)},this.moveCursorToScreen=function(e,t,n){var r=this.session.screenToDocumentPosition(e,t);this.moveCursorTo(r.row,r.column,n)},this.detach=function(){this.lead.detach(),this.anchor.detach(),this.session=this.doc=null},this.fromOrientedRange=function(e){this.setSelectionRange(e,e.cursor==e.start),this.$desiredColumn=e.desiredColumn||this.$desiredColumn},this.toOrientedRange=function(e){var t=this.getRange();return e?(e.start.column=t.start.column,e.start.row=t.start.row,e.end.column=t.end.column,e.end.row=t.end.row):e=t,e.cursor=this.isBackwards()?e.start:e.end,e.desiredColumn=this.$desiredColumn,e},this.getRangeOfMovements=function(e){var t=this.getCursor();try{e(this);var n=this.getCursor();return o.fromPoints(t,n)}catch(r){return o.fromPoints(t,t)}finally{this.moveCursorToPosition(t)}},this.toJSON=function(){if(this.rangeCount)var e=this.ranges.map(function(e){var t=e.clone();return t.isBackwards=e.cursor==e.start,t});else{var e=this.getRange();e.isBackwards=this.isBackwards()}return e},this.fromJSON=function(e){if(e.start==undefined){if(this.rangeList&&e.length>1){this.toSingleRange(e[0]);for(var t=e.length;t--;){var n=o.fromPoints(e[t].start,e[t].end);e[t].isBackwards&&(n.cursor=n.start),this.addRange(n,!0)}return}e=e[0]}this.rangeList&&this.toSingleRange(e),this.setSelectionRange(e,e.isBackwards)},this.isEqual=function(e){if((e.length||this.rangeCount)&&e.length!=this.rangeCount)return!1;if(!e.length||!this.ranges)return this.getRange().isEqual(e);for(var t=this.ranges.length;t--;)if(!this.ranges[t].isEqual(e[t]))return!1;return!0}}).call(u.prototype),t.Selection=u}),ace.define("ace/tokenizer",["require","exports","module","ace/config"],function(e,t,n){"use strict";var r=e("./config"),i=2e3,s=function(e){this.states=e,this.regExps={},this.matchMappings={};for(var t in this.states){var n=this.states[t],r=[],i=0,s=this.matchMappings[t]={defaultToken:"text"},o="g",u=[];for(var a=0;a1?f.onMatch=this.$applyToken:f.onMatch=f.token),c>1&&(/\\\d/.test(f.regex)?l=f.regex.replace(/\\([0-9]+)/g,function(e,t){return"\\"+(parseInt(t,10)+i+1)}):(c=1,l=this.removeCapturingGroups(f.regex)),!f.splitRegex&&typeof f.token!="string"&&u.push(f)),s[i]=a,i+=c,r.push(l),f.onMatch||(f.onMatch=null)}r.length||(s[0]=0,r.push("$")),u.forEach(function(e){e.splitRegex=this.createSplitterRegexp(e.regex,o)},this),this.regExps[t]=new RegExp("("+r.join(")|(")+")|($)",o)}};(function(){this.$setMaxTokenCount=function(e){i=e|0},this.$applyToken=function(e){var t=this.splitRegex.exec(e).slice(1),n=this.token.apply(this,t);if(typeof n=="string")return[{type:n,value:e}];var r=[];for(var i=0,s=n.length;il){var g=e.substring(l,m-v.length);h.type==p?h.value+=g:(h.type&&f.push(h),h={type:p,value:g})}for(var y=0;yi){c>2*e.length&&this.reportError("infinite loop with in ace tokenizer",{startState:t,line:e});while(l1&&n[0]!==r&&n.unshift("#tmp",r),{tokens:f,state:n.length?n:r}},this.reportError=r.reportError}).call(s.prototype),t.Tokenizer=s}),ace.define("ace/mode/text_highlight_rules",["require","exports","module","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../lib/lang"),i=function(){this.$rules={start:[{token:"empty_line",regex:"^$"},{defaultToken:"text"}]}};(function(){this.addRules=function(e,t){if(!t){for(var n in e)this.$rules[n]=e[n];return}for(var n in e){var r=e[n];for(var i=0;i=this.$rowTokens.length){this.$row+=1,e||(e=this.$session.getLength());if(this.$row>=e)return this.$row=e-1,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},this.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},this.getCurrentTokenRow=function(){return this.$row},this.getCurrentTokenColumn=function(){var e=this.$rowTokens,t=this.$tokenIndex,n=e[t].start;if(n!==undefined)return n;n=0;while(t>0)t-=1,n+=e[t].value.length;return n},this.getCurrentTokenPosition=function(){return{row:this.$row,column:this.getCurrentTokenColumn()}},this.getCurrentTokenRange=function(){var e=this.$rowTokens[this.$tokenIndex],t=this.getCurrentTokenColumn();return new r(this.$row,t,this.$row,t+e.value.length)}}).call(i.prototype),t.TokenIterator=i}),ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","punctuation.operator"],a=["text","paren.rparen","punctuation.operator","comment"],f,l={},c={'"':'"',"'":"'"},h=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},p=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},d=function(e){this.add("braces","insertion",function(t,n,r,i,s){var u=r.getCursorPosition(),a=i.doc.getLine(u.row);if(s=="{"){h(r);var l=r.getSelectionRange(),c=i.doc.getTextRange(l);if(c!==""&&c!=="{"&&r.getWrapBehavioursEnabled())return p(l,c,"{","}");if(d.isSaneInsertion(r,i))return/[\]\}\)]/.test(a[u.column])||r.inMultiSelectMode||e&&e.braces?(d.recordAutoInsert(r,i,"}"),{text:"{}",selection:[1,1]}):(d.recordMaybeInsert(r,i,"{"),{text:"{",selection:[1,1]})}else if(s=="}"){h(r);var v=a.substring(u.column,u.column+1);if(v=="}"){var m=i.$findOpeningBracket("}",{column:u.column+1,row:u.row});if(m!==null&&d.isAutoInsertedClosing(u,a,s))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(s=="\n"||s=="\r\n"){h(r);var g="";d.isMaybeInsertedClosing(u,a)&&(g=o.stringRepeat("}",f.maybeInsertedBrackets),d.clearMaybeInsertedClosing());var v=a.substring(u.column,u.column+1);if(v==="}"){var y=i.findMatchingBracket({row:u.row,column:u.column+1},"}");if(!y)return null;var b=this.$getIndent(i.getLine(y.row))}else{if(!g){d.clearMaybeInsertedClosing();return}var b=this.$getIndent(a)}var w=b+i.getTabString();return{text:"\n"+w+"\n"+b+g,selection:[1,w.length,1,w.length]}}d.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){h(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return p(s,o,"(",")");if(d.isSaneInsertion(n,r))return d.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){h(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&d.isAutoInsertedClosing(u,a,i))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){h(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return p(s,o,"[","]");if(d.isSaneInsertion(n,r))return d.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){h(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&d.isAutoInsertedClosing(u,a,i))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){var s=r.$mode.$quotes||c;if(i.length==1&&s[i]){if(this.lineCommentStart&&this.lineCommentStart.indexOf(i)!=-1)return;h(n);var o=i,u=n.getSelectionRange(),a=r.doc.getTextRange(u);if(a!==""&&(a.length!=1||!s[a])&&n.getWrapBehavioursEnabled())return p(u,a,o,o);if(!a){var f=n.getCursorPosition(),l=r.doc.getLine(f.row),d=l.substring(f.column-1,f.column),v=l.substring(f.column,f.column+1),m=r.getTokenAt(f.row,f.column),g=r.getTokenAt(f.row,f.column+1);if(d=="\\"&&m&&/escape/.test(m.type))return null;var y=m&&/string|escape/.test(m.type),b=!g||/string|escape/.test(g.type),w;if(v==o)w=y!==b,w&&/string\.end/.test(g.type)&&(w=!1);else{if(y&&!b)return null;if(y&&b)return null;var E=r.$mode.tokenRe;E.lastIndex=0;var S=E.test(d);E.lastIndex=0;var x=E.test(d);if(S||x)return null;if(v&&!/[\s;,.})\]\\]/.test(v))return null;w=!0}return{text:w?o+o:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.$mode.$quotes||c,o=r.doc.getTextRange(i);if(!i.isMultiLine()&&s.hasOwnProperty(o)){h(n);var u=r.doc.getLine(i.start.row),a=u.substring(i.start.column+1,i.start.column+2);if(a==o)return i.end.column++,i}})};d.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},d.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},d.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},d.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},d.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},d.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},d.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},d.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(d,i),t.CstyleBehaviour=d}),ace.define("ace/unicode",["require","exports","module"],function(e,t,n){"use strict";var r=[48,9,8,25,5,0,2,25,48,0,11,0,5,0,6,22,2,30,2,457,5,11,15,4,8,0,2,0,18,116,2,1,3,3,9,0,2,2,2,0,2,19,2,82,2,138,2,4,3,155,12,37,3,0,8,38,10,44,2,0,2,1,2,1,2,0,9,26,6,2,30,10,7,61,2,9,5,101,2,7,3,9,2,18,3,0,17,58,3,100,15,53,5,0,6,45,211,57,3,18,2,5,3,11,3,9,2,1,7,6,2,2,2,7,3,1,3,21,2,6,2,0,4,3,3,8,3,1,3,3,9,0,5,1,2,4,3,11,16,2,2,5,5,1,3,21,2,6,2,1,2,1,2,1,3,0,2,4,5,1,3,2,4,0,8,3,2,0,8,15,12,2,2,8,2,2,2,21,2,6,2,1,2,4,3,9,2,2,2,2,3,0,16,3,3,9,18,2,2,7,3,1,3,21,2,6,2,1,2,4,3,8,3,1,3,2,9,1,5,1,2,4,3,9,2,0,17,1,2,5,4,2,2,3,4,1,2,0,2,1,4,1,4,2,4,11,5,4,4,2,2,3,3,0,7,0,15,9,18,2,2,7,2,2,2,22,2,9,2,4,4,7,2,2,2,3,8,1,2,1,7,3,3,9,19,1,2,7,2,2,2,22,2,9,2,4,3,8,2,2,2,3,8,1,8,0,2,3,3,9,19,1,2,7,2,2,2,22,2,15,4,7,2,2,2,3,10,0,9,3,3,9,11,5,3,1,2,17,4,23,2,8,2,0,3,6,4,0,5,5,2,0,2,7,19,1,14,57,6,14,2,9,40,1,2,0,3,1,2,0,3,0,7,3,2,6,2,2,2,0,2,0,3,1,2,12,2,2,3,4,2,0,2,5,3,9,3,1,35,0,24,1,7,9,12,0,2,0,2,0,5,9,2,35,5,19,2,5,5,7,2,35,10,0,58,73,7,77,3,37,11,42,2,0,4,328,2,3,3,6,2,0,2,3,3,40,2,3,3,32,2,3,3,6,2,0,2,3,3,14,2,56,2,3,3,66,5,0,33,15,17,84,13,619,3,16,2,25,6,74,22,12,2,6,12,20,12,19,13,12,2,2,2,1,13,51,3,29,4,0,5,1,3,9,34,2,3,9,7,87,9,42,6,69,11,28,4,11,5,11,11,39,3,4,12,43,5,25,7,10,38,27,5,62,2,28,3,10,7,9,14,0,89,75,5,9,18,8,13,42,4,11,71,55,9,9,4,48,83,2,2,30,14,230,23,280,3,5,3,37,3,5,3,7,2,0,2,0,2,0,2,30,3,52,2,6,2,0,4,2,2,6,4,3,3,5,5,12,6,2,2,6,67,1,20,0,29,0,14,0,17,4,60,12,5,0,4,11,18,0,5,0,3,9,2,0,4,4,7,0,2,0,2,0,2,3,2,10,3,3,6,4,5,0,53,1,2684,46,2,46,2,132,7,6,15,37,11,53,10,0,17,22,10,6,2,6,2,6,2,6,2,6,2,6,2,6,2,6,2,31,48,0,470,1,36,5,2,4,6,1,5,85,3,1,3,2,2,89,2,3,6,40,4,93,18,23,57,15,513,6581,75,20939,53,1164,68,45,3,268,4,27,21,31,3,13,13,1,2,24,9,69,11,1,38,8,3,102,3,1,111,44,25,51,13,68,12,9,7,23,4,0,5,45,3,35,13,28,4,64,15,10,39,54,10,13,3,9,7,22,4,1,5,66,25,2,227,42,2,1,3,9,7,11171,13,22,5,48,8453,301,3,61,3,105,39,6,13,4,6,11,2,12,2,4,2,0,2,1,2,1,2,107,34,362,19,63,3,53,41,11,5,15,17,6,13,1,25,2,33,4,2,134,20,9,8,25,5,0,2,25,12,88,4,5,3,5,3,5,3,2],i=0,s=[];for(var o=0;o2?r%f!=f-1:r%f==0}}var E=Infinity;w(function(e,t){var n=e.search(/\S/);n!==-1?(ne.length&&(E=e.length)}),u==Infinity&&(u=E,s=!1,o=!1),l&&u%f!=0&&(u=Math.floor(u/f)*f),w(o?m:v)},this.toggleBlockComment=function(e,t,n,r){var i=this.blockComment;if(!i)return;!i.start&&i[0]&&(i=i[0]);var s=new f(t,r.row,r.column),o=s.getCurrentToken(),u=t.selection,a=t.selection.toOrientedRange(),c,h;if(o&&/comment/.test(o.type)){var p,d;while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.start);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;p=new l(m,g,m,g+i.start.length);break}o=s.stepBackward()}var s=new f(t,r.row,r.column),o=s.getCurrentToken();while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.end);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;d=new l(m,g,m,g+i.end.length);break}o=s.stepForward()}d&&t.remove(d),p&&(t.remove(p),c=p.start.row,h=-i.start.length)}else h=i.start.length,c=n.start.row,t.insert(n.end,i.end),t.insert(n.start,i.start);a.start.row==c&&(a.start.column+=h),a.end.row==c&&(a.end.column+=h),t.selection.fromOrientedRange(a)},this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.autoOutdent=function(e,t,n){},this.$getIndent=function(e){return e.match(/^\s*/)[0]},this.createWorker=function(e){return null},this.createModeDelegates=function(e){this.$embeds=[],this.$modes={};for(var t in e)if(e[t]){var n=e[t],i=n.prototype.$id,s=r.$modes[i];s||(r.$modes[i]=s=new n),r.$modes[t]||(r.$modes[t]=s),this.$embeds.push(t),this.$modes[t]=s}var o=["toggleBlockComment","toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction","getCompletions"];for(var t=0;t=0&&t.row=0&&t.column<=e[t.row].length}function s(e,t){t.action!="insert"&&t.action!="remove"&&r(t,"delta.action must be 'insert' or 'remove'"),t.lines instanceof Array||r(t,"delta.lines must be an Array"),(!t.start||!t.end)&&r(t,"delta.start/end must be an present");var n=t.start;i(e,t.start)||r(t,"delta.start must be contained in document");var s=t.end;t.action=="remove"&&!i(e,s)&&r(t,"delta.end must contained in document for 'remove' actions");var o=s.row-n.row,u=s.column-(o==0?n.column:0);(o!=t.lines.length-1||t.lines[o].length!=u)&&r(t,"delta.range must match delta lines")}t.applyDelta=function(e,t,n){var r=t.start.row,i=t.start.column,s=e[r]||"";switch(t.action){case"insert":var o=t.lines;if(o.length===1)e[r]=s.substring(0,i)+t.lines[0]+s.substring(i);else{var u=[r,1].concat(t.lines);e.splice.apply(e,u),e[r]=s.substring(0,i)+e[r],e[r+t.lines.length-1]+=s.substring(i)}break;case"remove":var a=t.end.column,f=t.end.row;r===f?e[r]=s.substring(0,i)+s.substring(a):e.splice(r,f-r+1,s.substring(0,i)+e[f].substring(a))}}}),ace.define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=t.Anchor=function(e,t,n){this.$onChange=this.onChange.bind(this),this.attach(e),typeof n=="undefined"?this.setPosition(t.row,t.column):this.setPosition(t,n)};(function(){function e(e,t,n){var r=n?e.column<=t.column:e.columnthis.row)return;var n=t(e,{row:this.row,column:this.column},this.$insertRight);this.setPosition(n.row,n.column,!0)},this.setPosition=function(e,t,n){var r;n?r={row:e,column:t}:r=this.$clipPositionToDocument(e,t);if(this.row==r.row&&this.column==r.column)return;var i={row:this.row,column:this.column};this.row=r.row,this.column=r.column,this._signal("change",{old:i,value:r})},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.attach=function(e){this.document=e||this.document,this.document.on("change",this.$onChange)},this.$clipPositionToDocument=function(e,t){var n={};return e>=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n}}).call(s.prototype)}),ace.define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./apply_delta").applyDelta,s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=e("./anchor").Anchor,a=function(e){this.$lines=[""],e.length===0?this.$lines=[""]:Array.isArray(e)?this.insertMergedLines({row:0,column:0},e):this.insert({row:0,column:0},e)};(function(){r.implement(this,s),this.setValue=function(e){var t=this.getLength()-1;this.remove(new o(0,0,t,this.getLine(t).length)),this.insert({row:0,column:0},e)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(e,t){return new u(this,e,t)},"aaa".split(/a/).length===0?this.$split=function(e){return e.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(e){return e.split(/\r\n|\r|\n/)},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);this.$autoNewLine=t?t[1]:"\n",this._signal("changeNewLineMode")},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";default:return this.$autoNewLine||"\n"}},this.$autoNewLine="",this.$newLineMode="auto",this.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e,this._signal("changeNewLineMode")},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return e=="\r\n"||e=="\r"||e=="\n"},this.getLine=function(e){return this.$lines[e]||""},this.getLines=function(e,t){return this.$lines.slice(e,t+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(e){return this.getLinesForRange(e).join(this.getNewLineCharacter())},this.getLinesForRange=function(e){var t;if(e.start.row===e.end.row)t=[this.getLine(e.start.row).substring(e.start.column,e.end.column)];else{t=this.getLines(e.start.row,e.end.row),t[0]=(t[0]||"").substring(e.start.column);var n=t.length-1;e.end.row-e.start.row==n&&(t[n]=t[n].substring(0,e.end.column))}return t},this.insertLines=function(e,t){return console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."),this.insertFullLines(e,t)},this.removeLines=function(e,t){return console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."),this.removeFullLines(e,t)},this.insertNewLine=function(e){return console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."),this.insertMergedLines(e,["",""])},this.insert=function(e,t){return this.getLength()<=1&&this.$detectNewLine(t),this.insertMergedLines(e,this.$split(t))},this.insertInLine=function(e,t){var n=this.clippedPos(e.row,e.column),r=this.pos(e.row,e.column+t.length);return this.applyDelta({start:n,end:r,action:"insert",lines:[t]},!0),this.clonePos(r)},this.clippedPos=function(e,t){var n=this.getLength();e===undefined?e=n:e<0?e=0:e>=n&&(e=n-1,t=undefined);var r=this.getLine(e);return t==undefined&&(t=r.length),t=Math.min(Math.max(t,0),r.length),{row:e,column:t}},this.clonePos=function(e){return{row:e.row,column:e.column}},this.pos=function(e,t){return{row:e,column:t}},this.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):(e.row=Math.max(0,e.row),e.column=Math.min(Math.max(e.column,0),this.getLine(e.row).length)),e},this.insertFullLines=function(e,t){e=Math.min(Math.max(e,0),this.getLength());var n=0;e0,r=t=0&&this.applyDelta({start:this.pos(e,this.getLine(e).length),end:this.pos(e+1,0),action:"remove",lines:["",""]})},this.replace=function(e,t){e instanceof o||(e=o.fromPoints(e.start,e.end));if(t.length===0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);var n;return t?n=this.insert(e.start,t):n=e.start,n},this.applyDeltas=function(e){for(var t=0;t=0;t--)this.revertDelta(e[t])},this.applyDelta=function(e,t){var n=e.action=="insert";if(n?e.lines.length<=1&&!e.lines[0]:!o.comparePoints(e.start,e.end))return;n&&e.lines.length>2e4?this.$splitAndapplyLargeDelta(e,2e4):(i(this.$lines,e,t),this._signal("change",e))},this.$splitAndapplyLargeDelta=function(e,t){var n=e.lines,r=n.length-t+1,i=e.start.row,s=e.start.column;for(var o=0,u=0;o20){n.running=setTimeout(n.$worker,20);break}}n.currentLine=t,r==-1&&(r=t),s<=r&&n.fireUpdateEvent(s,r)}};(function(){r.implement(this,i),this.setTokenizer=function(e){this.tokenizer=e,this.lines=[],this.states=[],this.start(0)},this.setDocument=function(e){this.doc=e,this.lines=[],this.states=[],this.stop()},this.fireUpdateEvent=function(e,t){var n={first:e,last:t};this._signal("update",{data:n})},this.start=function(e){this.currentLine=Math.min(e||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.states.splice(this.currentLine,this.states.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.scheduleStart=function(){this.running||(this.running=setTimeout(this.$worker,700))},this.$updateOnChange=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.lines[t]=null;else if(e.action=="remove")this.lines.splice(t,n+1,null),this.states.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.lines.splice.apply(this.lines,r),this.states.splice.apply(this.states,r)}this.currentLine=Math.min(t,this.currentLine,this.doc.getLength()),this.stop()},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(e){return this.lines[e]||this.$tokenizeRow(e)},this.getState=function(e){return this.currentLine==e&&this.$tokenizeRow(e),this.states[e]||"start"},this.$tokenizeRow=function(e){var t=this.doc.getLine(e),n=this.states[e-1],r=this.tokenizer.getLineTokens(t,n,e);return this.states[e]+""!=r.state+""?(this.states[e]=r.state,this.lines[e+1]=null,this.currentLine>e+1&&(this.currentLine=e+1)):this.currentLine==e&&(this.currentLine=e+1),this.lines[e]=r.tokens}}).call(s.prototype),t.BackgroundTokenizer=s}),ace.define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(e,t,n){this.setRegexp(e),this.clazz=t,this.type=n||"text"};(function(){this.MAX_RANGES=500,this.setRegexp=function(e){if(this.regExp+""==e+"")return;this.regExp=e,this.cache=[]},this.update=function(e,t,n,i){if(!this.regExp)return;var o=i.firstRow,u=i.lastRow;for(var a=o;a<=u;a++){var f=this.cache[a];f==null&&(f=r.getMatchOffsets(n.getLine(a),this.regExp),f.length>this.MAX_RANGES&&(f=f.slice(0,this.MAX_RANGES)),f=f.map(function(e){return new s(a,e.offset,a,e.offset+e.length)}),this.cache[a]=f.length?f:"");for(var l=f.length;l--;)t.drawSingleLineMarker(e,f[l].toScreenRange(n),this.clazz,i)}}}).call(o.prototype),t.SearchHighlight=o}),ace.define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(e,t,n){"use strict";function i(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.folds=[t];var n=t[t.length-1];this.range=new r(t[0].start.row,t[0].start.column,n.end.row,n.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(e){e.setFoldLine(this)},this)}var r=e("../range").Range;(function(){this.shiftRow=function(e){this.start.row+=e,this.end.row+=e,this.folds.forEach(function(t){t.start.row+=e,t.end.row+=e})},this.addFold=function(e){if(e.sameRow){if(e.start.rowthis.endRow)throw new Error("Can't add a fold to this FoldLine as it has no connection");this.folds.push(e),this.folds.sort(function(e,t){return-e.range.compareEnd(t.start.row,t.start.column)}),this.range.compareEnd(e.start.row,e.start.column)>0?(this.end.row=e.end.row,this.end.column=e.end.column):this.range.compareStart(e.end.row,e.end.column)<0&&(this.start.row=e.start.row,this.start.column=e.start.column)}else if(e.start.row==this.end.row)this.folds.push(e),this.end.row=e.end.row,this.end.column=e.end.column;else{if(e.end.row!=this.start.row)throw new Error("Trying to add fold to FoldRow that doesn't have a matching row");this.folds.unshift(e),this.start.row=e.start.row,this.start.column=e.start.column}e.foldLine=this},this.containsRow=function(e){return e>=this.start.row&&e<=this.end.row},this.walk=function(e,t,n){var r=0,i=this.folds,s,o,u,a=!0;t==null&&(t=this.end.row,n=this.end.column);for(var f=0;f0)continue;var a=i(e,o.start);return u===0?t&&a!==0?-s-2:s:a>0||a===0&&!t?s:-s-1}return-s-1},this.add=function(e){var t=!e.isEmpty(),n=this.pointIndex(e.start,t);n<0&&(n=-n-1);var r=this.pointIndex(e.end,t,n);return r<0?r=-r-1:r++,this.ranges.splice(n,r-n,e)},this.addList=function(e){var t=[];for(var n=e.length;n--;)t.push.apply(t,this.add(e[n]));return t},this.substractPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges.splice(t,1)},this.merge=function(){var e=[],t=this.ranges;t=t.sort(function(e,t){return i(e.start,t.start)});var n=t[0],r;for(var s=1;s=0},this.containsPoint=function(e){return this.pointIndex(e)>=0},this.rangeAtPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges[t]},this.clipRows=function(e,t){var n=this.ranges;if(n[0].start.row>t||n[n.length-1].start.row=r)break}if(e.action=="insert"){var f=i-r,l=-t.column+n.column;for(;or)break;a.start.row==r&&a.start.column>=t.column&&(a.start.column!=t.column||!this.$insertRight)&&(a.start.column+=l,a.start.row+=f);if(a.end.row==r&&a.end.column>=t.column){if(a.end.column==t.column&&this.$insertRight)continue;a.end.column==t.column&&l>0&&oa.start.column&&a.end.column==s[o+1].start.column&&(a.end.column-=l),a.end.column+=l,a.end.row+=f}}}else{var f=r-i,l=t.column-n.column;for(;oi)break;if(a.end.rowt.column)a.end.column=t.column,a.end.row=t.row}else a.end.column+=l,a.end.row+=f;else a.end.row>i&&(a.end.row+=f);if(a.start.rowt.column)a.start.column=t.column,a.start.row=t.row}else a.start.column+=l,a.start.row+=f;else a.start.row>i&&(a.start.row+=f)}}if(f!=0&&o=e)return i;if(i.end.row>e)return null}return null},this.getNextFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r=e)return i}return null},this.getFoldedRowCount=function(e,t){var n=this.$foldData,r=t-e+1;for(var i=0;i=t){u=e?r-=t-u:r=0);break}o>=e&&(u>=e?r-=o-u:r-=o-e+1)}return r},this.$addFoldLine=function(e){return this.$foldData.push(e),this.$foldData.sort(function(e,t){return e.start.row-t.start.row}),e},this.addFold=function(e,t){var n=this.$foldData,r=!1,o;e instanceof s?o=e:(o=new s(t,e),o.collapseChildren=t.collapseChildren),this.$clipRangeToDocument(o.range);var u=o.start.row,a=o.start.column,f=o.end.row,l=o.end.column;if(u0&&(this.removeFolds(p),p.forEach(function(e){o.addSubFold(e)}));for(var d=0;d0&&this.foldAll(e.start.row+1,e.end.row,e.collapseChildren-1),e.subFolds=[]},this.expandFolds=function(e){e.forEach(function(e){this.expandFold(e)},this)},this.unfold=function(e,t){var n,i;e==null?(n=new r(0,0,this.getLength(),0),t=!0):typeof e=="number"?n=new r(e,0,e,this.getLine(e).length):"row"in e?n=r.fromPoints(e,e):n=e,i=this.getFoldsInRangeList(n);if(t)this.removeFolds(i);else{var s=i;while(s.length)this.expandFolds(s),s=this.getFoldsInRangeList(n)}if(i.length)return i},this.isRowFolded=function(e,t){return!!this.getFoldLine(e,t)},this.getRowFoldEnd=function(e,t){var n=this.getFoldLine(e,t);return n?n.end.row:e},this.getRowFoldStart=function(e,t){var n=this.getFoldLine(e,t);return n?n.start.row:e},this.getFoldDisplayLine=function(e,t,n,r,i){r==null&&(r=e.start.row),i==null&&(i=0),t==null&&(t=e.end.row),n==null&&(n=this.getLine(t).length);var s=this.doc,o="";return e.walk(function(e,t,n,u){if(tl)break}while(s&&a.test(s.type));s=i.stepBackward()}else s=i.getCurrentToken();return f.end.row=i.getCurrentTokenRow(),f.end.column=i.getCurrentTokenColumn()+s.value.length-2,f}},this.foldAll=function(e,t,n){n==undefined&&(n=1e5);var r=this.foldWidgets;if(!r)return;t=t||this.getLength(),e=e||0;for(var i=e;i=e){i=s.end.row;try{var o=this.addFold("...",s);o&&(o.collapseChildren=n)}catch(u){}}}},this.$foldStyles={manual:1,markbegin:1,markbeginend:1},this.$foldStyle="markbegin",this.setFoldStyle=function(e){if(!this.$foldStyles[e])throw new Error("invalid fold style: "+e+"["+Object.keys(this.$foldStyles).join(", ")+"]");if(this.$foldStyle==e)return;this.$foldStyle=e,e=="manual"&&this.unfold();var t=this.$foldMode;this.$setFolding(null),this.$setFolding(t)},this.$setFolding=function(e){if(this.$foldMode==e)return;this.$foldMode=e,this.off("change",this.$updateFoldWidgets),this.off("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets),this._signal("changeAnnotation");if(!e||this.$foldStyle=="manual"){this.foldWidgets=null;return}this.foldWidgets=[],this.getFoldWidget=e.getFoldWidget.bind(e,this,this.$foldStyle),this.getFoldWidgetRange=e.getFoldWidgetRange.bind(e,this,this.$foldStyle),this.$updateFoldWidgets=this.updateFoldWidgets.bind(this),this.$tokenizerUpdateFoldWidgets=this.tokenizerUpdateFoldWidgets.bind(this),this.on("change",this.$updateFoldWidgets),this.on("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets)},this.getParentFoldRangeData=function(e,t){var n=this.foldWidgets;if(!n||t&&n[e])return{};var r=e-1,i;while(r>=0){var s=n[r];s==null&&(s=n[r]=this.getFoldWidget(r));if(s=="start"){var o=this.getFoldWidgetRange(r);i||(i=o);if(o&&o.end.row>=e)break}r--}return{range:r!==-1&&o,firstRange:i}},this.onFoldWidgetClick=function(e,t){t=t.domEvent;var n={children:t.shiftKey,all:t.ctrlKey||t.metaKey,siblings:t.altKey},r=this.$toggleFoldWidget(e,n);if(!r){var i=t.target||t.srcElement;i&&/ace_fold-widget/.test(i.className)&&(i.className+=" ace_invalid")}},this.$toggleFoldWidget=function(e,t){if(!this.getFoldWidget)return;var n=this.getFoldWidget(e),r=this.getLine(e),i=n==="end"?-1:1,s=this.getFoldAt(e,i===-1?0:r.length,i);if(s)return t.children||t.all?this.removeFold(s):this.expandFold(s),s;var o=this.getFoldWidgetRange(e,!0);if(o&&!o.isMultiLine()){s=this.getFoldAt(o.start.row,o.start.column,1);if(s&&o.isEqual(s.range))return this.removeFold(s),s}if(t.siblings){var u=this.getParentFoldRangeData(e);if(u.range)var a=u.range.start.row+1,f=u.range.end.row;this.foldAll(a,f,t.all?1e4:0)}else t.children?(f=o?o.end.row:this.getLength(),this.foldAll(e+1,f,t.all?1e4:0)):o&&(t.all&&(o.collapseChildren=1e4),this.addFold("...",o));return o},this.toggleFoldWidget=function(e){var t=this.selection.getCursor().row;t=this.getRowFoldStart(t);var n=this.$toggleFoldWidget(t,{});if(n)return;var r=this.getParentFoldRangeData(t,!0);n=r.range||r.firstRange;if(n){t=n.start.row;var i=this.getFoldAt(t,this.getLine(t).length,1);i?this.removeFold(i):this.addFold("...",n)}},this.updateFoldWidgets=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.foldWidgets[t]=null;else if(e.action=="remove")this.foldWidgets.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.foldWidgets.splice.apply(this.foldWidgets,r)}},this.tokenizerUpdateFoldWidgets=function(e){var t=e.data;t.first!=t.last&&this.foldWidgets.length>t.first&&this.foldWidgets.splice(t.first,this.foldWidgets.length)}}var r=e("../range").Range,i=e("./fold_line").FoldLine,s=e("./fold").Fold,o=e("../token_iterator").TokenIterator;t.Folding=u}),ace.define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator","ace/range"],function(e,t,n){"use strict";function s(){this.findMatchingBracket=function(e,t){if(e.column==0)return null;var n=t||this.getLine(e.row).charAt(e.column-1);if(n=="")return null;var r=n.match(/([\(\[\{])|([\)\]\}])/);return r?r[1]?this.$findClosingBracket(r[1],e):this.$findOpeningBracket(r[2],e):null},this.getBracketRange=function(e){var t=this.getLine(e.row),n=!0,r,s=t.charAt(e.column-1),o=s&&s.match(/([\(\[\{])|([\)\]\}])/);o||(s=t.charAt(e.column),e={row:e.row,column:e.column+1},o=s&&s.match(/([\(\[\{])|([\)\]\}])/),n=!1);if(!o)return null;if(o[1]){var u=this.$findClosingBracket(o[1],e);if(!u)return null;r=i.fromPoints(e,u),n||(r.end.column++,r.start.column--),r.cursor=r.end}else{var u=this.$findOpeningBracket(o[2],e);if(!u)return null;r=i.fromPoints(u,e),n||(r.start.column++,r.end.column--),r.cursor=r.start}return r},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{","<":">",">":"<"},this.$findOpeningBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("rparen",".paren").replace(/\b(?:end)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn()-2,f=u.value;for(;;){while(a>=0){var l=f.charAt(a);if(l==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else l==e&&(s+=1);a-=1}do u=o.stepBackward();while(u&&!n.test(u.type));if(u==null)break;f=u.value,a=f.length-1}return null},this.$findClosingBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("lparen",".paren").replace(/\b(?:start|begin)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn();for(;;){var f=u.value,l=f.length;while(a=4352&&e<=4447||e>=4515&&e<=4519||e>=4602&&e<=4607||e>=9001&&e<=9002||e>=11904&&e<=11929||e>=11931&&e<=12019||e>=12032&&e<=12245||e>=12272&&e<=12283||e>=12288&&e<=12350||e>=12353&&e<=12438||e>=12441&&e<=12543||e>=12549&&e<=12589||e>=12593&&e<=12686||e>=12688&&e<=12730||e>=12736&&e<=12771||e>=12784&&e<=12830||e>=12832&&e<=12871||e>=12880&&e<=13054||e>=13056&&e<=19903||e>=19968&&e<=42124||e>=42128&&e<=42182||e>=43360&&e<=43388||e>=44032&&e<=55203||e>=55216&&e<=55238||e>=55243&&e<=55291||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65106||e>=65108&&e<=65126||e>=65128&&e<=65131||e>=65281&&e<=65376||e>=65504&&e<=65510}r.implement(this,u),this.setDocument=function(e){this.doc&&this.doc.removeListener("change",this.$onChange),this.doc=e,e.on("change",this.$onChange),this.bgTokenizer&&this.bgTokenizer.setDocument(this.getDocument()),this.resetCaches()},this.getDocument=function(){return this.doc},this.$resetRowCache=function(e){if(!e){this.$docRowCache=[],this.$screenRowCache=[];return}var t=this.$docRowCache.length,n=this.$getRowCacheIndex(this.$docRowCache,e)+1;t>n&&(this.$docRowCache.splice(n,t),this.$screenRowCache.splice(n,t))},this.$getRowCacheIndex=function(e,t){var n=0,r=e.length-1;while(n<=r){var i=n+r>>1,s=e[i];if(t>s)n=i+1;else{if(!(t=t)break}return r=n[s],r?(r.index=s,r.start=i-r.value.length,r):null},this.setUndoManager=function(e){this.$undoManager=e,this.$informUndoManager&&this.$informUndoManager.cancel();if(e){var t=this;e.addSession(this),this.$syncInformUndoManager=function(){t.$informUndoManager.cancel(),t.mergeUndoDeltas=!1},this.$informUndoManager=i.delayedCall(this.$syncInformUndoManager)}else this.$syncInformUndoManager=function(){}},this.markUndoGroup=function(){this.$syncInformUndoManager&&this.$syncInformUndoManager()},this.$defaultUndoManager={undo:function(){},redo:function(){},reset:function(){},add:function(){},addSelection:function(){},startNewGroup:function(){},addSession:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?i.stringRepeat(" ",this.getTabSize()):" "},this.setUseSoftTabs=function(e){this.setOption("useSoftTabs",e)},this.getUseSoftTabs=function(){return this.$useSoftTabs&&!this.$mode.$indentWithTabs},this.setTabSize=function(e){this.setOption("tabSize",e)},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(e){return this.$useSoftTabs&&e.column%this.$tabSize===0},this.setNavigateWithinSoftTabs=function(e){this.setOption("navigateWithinSoftTabs",e)},this.getNavigateWithinSoftTabs=function(){return this.$navigateWithinSoftTabs},this.$overwrite=!1,this.setOverwrite=function(e){this.setOption("overwrite",e)},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.addGutterDecoration=function(e,t){this.$decorations[e]||(this.$decorations[e]=""),this.$decorations[e]+=" "+t,this._signal("changeBreakpoint",{})},this.removeGutterDecoration=function(e,t){this.$decorations[e]=(this.$decorations[e]||"").replace(" "+t,""),this._signal("changeBreakpoint",{})},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(e){this.$breakpoints=[];for(var t=0;t0&&(r=!!n.charAt(t-1).match(this.tokenRe)),r||(r=!!n.charAt(t).match(this.tokenRe));if(r)var i=this.tokenRe;else if(/^\s+$/.test(n.slice(t-1,t+1)))var i=/\s/;else var i=this.nonTokenRe;var s=t;if(s>0){do s--;while(s>=0&&n.charAt(s).match(i));s++}var o=t;while(oe&&(e=t.screenWidth)}),this.lineWidgetWidth=e},this.$computeWidth=function(e){if(this.$modified||e){this.$modified=!1;if(this.$useWrapMode)return this.screenWidth=this.$wrapLimit;var t=this.doc.getAllLines(),n=this.$rowLengthCache,r=0,i=0,s=this.$foldData[i],o=s?s.start.row:Infinity,u=t.length;for(var a=0;ao){a=s.end.row+1;if(a>=u)break;s=this.$foldData[i++],o=s?s.start.row:Infinity}n[a]==null&&(n[a]=this.$getStringScreenWidth(t[a])[0]),n[a]>r&&(r=n[a])}this.screenWidth=r}},this.getLine=function(e){return this.doc.getLine(e)},this.getLines=function(e,t){return this.doc.getLines(e,t)},this.getLength=function(){return this.doc.getLength()},this.getTextRange=function(e){return this.doc.getTextRange(e||this.selection.getRange())},this.insert=function(e,t){return this.doc.insert(e,t)},this.remove=function(e){return this.doc.remove(e)},this.removeFullLines=function(e,t){return this.doc.removeFullLines(e,t)},this.undoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;for(var n=e.length-1;n!=-1;n--){var r=e[n];r.action=="insert"||r.action=="remove"?this.doc.revertDelta(r):r.folds&&this.addFolds(r.folds)}!t&&this.$undoSelect&&(e.selectionBefore?this.selection.fromJSON(e.selectionBefore):this.selection.setRange(this.$getUndoSelection(e,!0))),this.$fromUndo=!1},this.redoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;for(var n=0;ne.end.column&&(s.start.column+=u),s.end.row==e.end.row&&s.end.column>e.end.column&&(s.end.column+=u)),o&&s.start.row>=e.end.row&&(s.start.row+=o,s.end.row+=o)}s.end=this.insert(s.start,r);if(i.length){var a=e.start,f=s.start,o=f.row-a.row,u=f.column-a.column;this.addFolds(i.map(function(e){return e=e.clone(),e.start.row==a.row&&(e.start.column+=u),e.end.row==a.row&&(e.end.column+=u),e.start.row+=o,e.end.row+=o,e}))}return s},this.indentRows=function(e,t,n){n=n.replace(/\t/g,this.getTabString());for(var r=e;r<=t;r++)this.doc.insertInLine({row:r,column:0},n)},this.outdentRows=function(e){var t=e.collapseRows(),n=new l(0,0,0,0),r=this.getTabSize();for(var i=t.start.row;i<=t.end.row;++i){var s=this.getLine(i);n.start.row=i,n.end.row=i;for(var o=0;o0){var r=this.getRowFoldEnd(t+n);if(r>this.doc.getLength()-1)return 0;var i=r-t}else{e=this.$clipRowToDocument(e),t=this.$clipRowToDocument(t);var i=t-e+1}var s=new l(e,0,t,Number.MAX_VALUE),o=this.getFoldsInRange(s).map(function(e){return e=e.clone(),e.start.row+=i,e.end.row+=i,e}),u=n==0?this.doc.getLines(e,t):this.doc.removeFullLines(e,t);return this.doc.insertFullLines(e+i,u),o.length&&this.addFolds(o),i},this.moveLinesUp=function(e,t){return this.$moveLines(e,t,-1)},this.moveLinesDown=function(e,t){return this.$moveLines(e,t,1)},this.duplicateLines=function(e,t){return this.$moveLines(e,t,0)},this.$clipRowToDocument=function(e){return Math.max(0,Math.min(e,this.doc.getLength()-1))},this.$clipColumnToRow=function(e,t){return t<0?0:Math.min(this.doc.getLine(e).length,t)},this.$clipPositionToDocument=function(e,t){t=Math.max(0,t);if(e<0)e=0,t=0;else{var n=this.doc.getLength();e>=n?(e=n-1,t=this.doc.getLine(n-1).length):t=Math.min(this.doc.getLine(e).length,t)}return{row:e,column:t}},this.$clipRangeToDocument=function(e){e.start.row<0?(e.start.row=0,e.start.column=0):e.start.column=this.$clipColumnToRow(e.start.row,e.start.column);var t=this.doc.getLength()-1;return e.end.row>t?(e.end.row=t,e.end.column=this.doc.getLine(t).length):e.end.column=this.$clipColumnToRow(e.end.row,e.end.column),e},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(e){if(e!=this.$useWrapMode){this.$useWrapMode=e,this.$modified=!0,this.$resetRowCache(0);if(e){var t=this.getLength();this.$wrapData=Array(t),this.$updateWrapData(0,t-1)}this._signal("changeWrapMode")}},this.getUseWrapMode=function(){return this.$useWrapMode},this.setWrapLimitRange=function(e,t){if(this.$wrapLimitRange.min!==e||this.$wrapLimitRange.max!==t)this.$wrapLimitRange={min:e,max:t},this.$modified=!0,this.$bidiHandler.markAsDirty(),this.$useWrapMode&&this._signal("changeWrapMode")},this.adjustWrapLimit=function(e,t){var n=this.$wrapLimitRange;n.max<0&&(n={min:t,max:t});var r=this.$constrainWrapLimit(e,n.min,n.max);return r!=this.$wrapLimit&&r>1?(this.$wrapLimit=r,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._signal("changeWrapLimit")),!0):!1},this.$constrainWrapLimit=function(e,t,n){return t&&(e=Math.max(t,e)),n&&(e=Math.min(n,e)),e},this.getWrapLimit=function(){return this.$wrapLimit},this.setWrapLimit=function(e){this.setWrapLimitRange(e,e)},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateInternalDataOnChange=function(e){var t=this.$useWrapMode,n=e.action,r=e.start,i=e.end,s=r.row,o=i.row,u=o-s,a=null;this.$updating=!0;if(u!=0)if(n==="remove"){this[t?"$wrapData":"$rowLengthCache"].splice(s,u);var f=this.$foldData;a=this.getFoldsInRange(e),this.removeFolds(a);var l=this.getFoldLine(i.row),c=0;if(l){l.addRemoveChars(i.row,i.column,r.column-i.column),l.shiftRow(-u);var h=this.getFoldLine(s);h&&h!==l&&(h.merge(l),l=h),c=f.indexOf(l)+1}for(c;c=i.row&&l.shiftRow(-u)}o=s}else{var p=Array(u);p.unshift(s,0);var d=t?this.$wrapData:this.$rowLengthCache;d.splice.apply(d,p);var f=this.$foldData,l=this.getFoldLine(s),c=0;if(l){var v=l.range.compareInside(r.row,r.column);v==0?(l=l.split(r.row,r.column),l&&(l.shiftRow(u),l.addRemoveChars(o,0,i.column-r.column))):v==-1&&(l.addRemoveChars(s,0,i.column-r.column),l.shiftRow(u)),c=f.indexOf(l)+1}for(c;c=s&&l.shiftRow(u)}}else{u=Math.abs(e.start.column-e.end.column),n==="remove"&&(a=this.getFoldsInRange(e),this.removeFolds(a),u=-u);var l=this.getFoldLine(s);l&&l.addRemoveChars(s,r.column,u)}return t&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),this.$updating=!1,t?this.$updateWrapData(s,o):this.$updateRowLengthCache(s,o),a},this.$updateRowLengthCache=function(e,t,n){this.$rowLengthCache[e]=null,this.$rowLengthCache[t]=null},this.$updateWrapData=function(e,t){var r=this.doc.getAllLines(),i=this.getTabSize(),o=this.$wrapData,u=this.$wrapLimit,a,f,l=e;t=Math.min(t,r.length-1);while(l<=t)f=this.getFoldLine(l,f),f?(a=[],f.walk(function(e,t,i,o){var u;if(e!=null){u=this.$getDisplayTokens(e,a.length),u[0]=n;for(var f=1;fr-b){var w=f+r-b;if(e[w-1]>=c&&e[w]>=c){y(w);continue}if(e[w]==n||e[w]==s){for(w;w!=f-1;w--)if(e[w]==n)break;if(w>f){y(w);continue}w=f+r;for(w;w>2)),f-1);while(w>E&&e[w]E&&e[w]E&&e[w]==a)w--}else while(w>E&&e[w]E){y(++w);continue}w=f+r,e[w]==t&&w--,y(w-b)}return o},this.$getDisplayTokens=function(n,r){var i=[],s;r=r||0;for(var o=0;o39&&u<48||u>57&&u<64?i.push(a):u>=4352&&m(u)?i.push(e,t):i.push(e)}return i},this.$getStringScreenWidth=function(e,t,n){if(t==0)return[0,0];t==null&&(t=Infinity),n=n||0;var r,i;for(i=0;i=4352&&m(r)?n+=2:n+=1;if(n>t)break}return[n,i]},this.lineWidgets=null,this.getRowLength=function(e){if(this.lineWidgets)var t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0;else t=0;return!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.getRowLineCount=function(e){return!this.$useWrapMode||!this.$wrapData[e]?1:this.$wrapData[e].length+1},this.getRowWrapIndent=function(e){if(this.$useWrapMode){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE),n=this.$wrapData[t.row];return n.length&&n[0]=0)var u=f[l],i=this.$docRowCache[l],h=e>f[c-1];else var h=!c;var p=this.getLength()-1,d=this.getNextFoldLine(i),v=d?d.start.row:Infinity;while(u<=e){a=this.getRowLength(i);if(u+a>e||i>=p)break;u+=a,i++,i>v&&(i=d.end.row+1,d=this.getNextFoldLine(i,d),v=d?d.start.row:Infinity),h&&(this.$docRowCache.push(i),this.$screenRowCache.push(u))}if(d&&d.start.row<=i)r=this.getFoldDisplayLine(d),i=d.start.row;else{if(u+a<=e||i>p)return{row:p,column:this.getLine(p).length};r=this.getLine(i),d=null}var m=0,g=Math.floor(e-u);if(this.$useWrapMode){var y=this.$wrapData[i];y&&(o=y[g],g>0&&y.length&&(m=y.indent,s=y[g-1]||y[y.length-1],r=r.substring(s)))}return n!==undefined&&this.$bidiHandler.isBidiRow(u+g,i,g)&&(t=this.$bidiHandler.offsetToCol(n)),s+=this.$getStringScreenWidth(r,t-m)[1],this.$useWrapMode&&s>=o&&(s=o-1),d?d.idxToPosition(s):{row:i,column:s}},this.documentToScreenPosition=function(e,t){if(typeof t=="undefined")var n=this.$clipPositionToDocument(e.row,e.column);else n=this.$clipPositionToDocument(e,t);e=n.row,t=n.column;var r=0,i=null,s=null;s=this.getFoldAt(e,t,1),s&&(e=s.start.row,t=s.start.column);var o,u=0,a=this.$docRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var u=a[f],r=this.$screenRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getNextFoldLine(u),p=h?h.start.row:Infinity;while(u=p){o=h.end.row+1;if(o>e)break;h=this.getNextFoldLine(o,h),p=h?h.start.row:Infinity}else o=u+1;r+=this.getRowLength(u),u=o,c&&(this.$docRowCache.push(u),this.$screenRowCache.push(r))}var d="";h&&u>=p?(d=this.getFoldDisplayLine(h,e,t),i=h.start.row):(d=this.getLine(e).substring(0,t),i=e);var v=0;if(this.$useWrapMode){var m=this.$wrapData[i];if(m){var g=0;while(d.length>=m[g])r++,g++;d=d.substring(m[g-1]||0,d.length),v=g>0?m.indent:0}}return{row:r,column:v+this.$getStringScreenWidth(d)[0]}},this.documentToScreenColumn=function(e,t){return this.documentToScreenPosition(e,t).column},this.documentToScreenRow=function(e,t){return this.documentToScreenPosition(e,t).row},this.getScreenLength=function(){var e=0,t=null;if(!this.$useWrapMode){e=this.getLength();var n=this.$foldData;for(var r=0;ro&&(s=t.end.row+1,t=this.$foldData[r++],o=t?t.start.row:Infinity)}}return this.lineWidgets&&(e+=this.$getWidgetScreenLength()),e},this.$setFontMetrics=function(e){if(!this.$enableVarChar)return;this.$getStringScreenWidth=function(t,n,r){if(n===0)return[0,0];n||(n=Infinity),r=r||0;var i,s;for(s=0;sn)break}return[r,s]}},this.destroy=function(){this.bgTokenizer&&(this.bgTokenizer.setDocument(null),this.bgTokenizer=null),this.$stopWorker()},this.isFullWidth=m}.call(d.prototype),e("./edit_session/folding").Folding.call(d.prototype),e("./edit_session/bracket_match").BracketMatch.call(d.prototype),o.defineOptions(d.prototype,"session",{wrap:{set:function(e){!e||e=="off"?e=!1:e=="free"?e=!0:e=="printMargin"?e=-1:typeof e=="string"&&(e=parseInt(e,10)||!1);if(this.$wrap==e)return;this.$wrap=e;if(!e)this.setUseWrapMode(!1);else{var t=typeof e=="number"?e:null;this.setWrapLimitRange(t,t),this.setUseWrapMode(!0)}},get:function(){return this.getUseWrapMode()?this.$wrap==-1?"printMargin":this.getWrapLimitRange().min?this.$wrap:"free":"off"},handlesSet:!0},wrapMethod:{set:function(e){e=e=="auto"?this.$mode.type!="text":e!="text",e!=this.$wrapAsCode&&(this.$wrapAsCode=e,this.$useWrapMode&&(this.$useWrapMode=!1,this.setUseWrapMode(!0)))},initialValue:"auto"},indentedSoftWrap:{set:function(){this.$useWrapMode&&(this.$useWrapMode=!1,this.setUseWrapMode(!0))},initialValue:!0},firstLineNumber:{set:function(){this._signal("changeBreakpoint")},initialValue:1},useWorker:{set:function(e){this.$useWorker=e,this.$stopWorker(),e&&this.$startWorker()},initialValue:!0},useSoftTabs:{initialValue:!0},tabSize:{set:function(e){e=parseInt(e);if(isNaN(e)||this.$tabSize===e)return;this.$modified=!0,this.$rowLengthCache=[],this.$tabSize=e,this._signal("changeTabSize")},initialValue:4,handlesSet:!0},navigateWithinSoftTabs:{initialValue:!1},foldStyle:{set:function(e){this.setFoldStyle(e)},handlesSet:!0},overwrite:{set:function(e){this._signal("changeOverwrite")},initialValue:!1},newLineMode:{set:function(e){this.doc.setNewLineMode(e)},get:function(){return this.doc.getNewLineMode()},handlesSet:!0},mode:{set:function(e){this.setMode(e)},get:function(){return this.$modeId},handlesSet:!0}}),t.EditSession=d}),ace.define("ace/search",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";function u(e,t){function n(e){return/\w/.test(e)||t.regExp?"\\b":""}return n(e[0])+e+n(e[e.length-1])}var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(){this.$options={}};(function(){this.set=function(e){return i.mixin(this.$options,e),this},this.getOptions=function(){return r.copyObject(this.$options)},this.setOptions=function(e){this.$options=e},this.find=function(e){var t=this.$options,n=this.$matchIterator(e,t);if(!n)return!1;var r=null;return n.forEach(function(e,n,i,o){return r=new s(e,n,i,o),n==o&&t.start&&t.start.start&&t.skipCurrent!=0&&r.isEqual(t.start)?(r=null,!1):!0}),r},this.findAll=function(e){var t=this.$options;if(!t.needle)return[];this.$assembleRegExp(t);var n=t.range,i=n?e.getLines(n.start.row,n.end.row):e.doc.getAllLines(),o=[],u=t.re;if(t.$isMultiLine){var a=u.length,f=i.length-a,l;e:for(var c=u.offset||0;c<=f;c++){for(var h=0;hv)continue;o.push(l=new s(c,v,c+a-1,m)),a>2&&(c=c+a-2)}}else for(var g=0;gE&&o[h].end.row==n.end.row)h--;o=o.slice(g,h+1);for(g=0,h=o.length;g=u;n--)if(c(n,Number.MAX_VALUE,e))return;if(t.wrap==0)return;for(n=a,u=o.row;n>=u;n--)if(c(n,Number.MAX_VALUE,e))return};else var f=function(e){var n=o.row;if(c(n,o.column,e))return;for(n+=1;n<=a;n++)if(c(n,0,e))return;if(t.wrap==0)return;for(n=u,a=o.row;n<=a;n++)if(c(n,0,e))return};if(t.$isMultiLine)var l=n.length,c=function(t,i,s){var o=r?t-l+1:t;if(o<0)return;var u=e.getLine(o),a=u.search(n[0]);if(!r&&ai)return;if(s(o,a,o+l-1,c))return!0};else if(r)var c=function(t,r,i){var s=e.getLine(t),o=[],u,a=0;n.lastIndex=0;while(u=n.exec(s)){var f=u[0].length;a=u.index;if(!f){if(a>=s.length)break;n.lastIndex=a+=1}if(u.index+f>r)break;o.push(u.index,f)}for(var l=o.length-1;l>=0;l-=2){var c=o[l-1],f=o[l];if(i(t,c,t,c+f))return!0}};else var c=function(t,r,i){var s=e.getLine(t),o,u;n.lastIndex=r;while(u=n.exec(s)){var a=u[0].length;o=u.index;if(i(t,o,t,o+a))return!0;if(!a){n.lastIndex=o+=1;if(o>=s.length)return!1}}};return{forEach:f}}}).call(o.prototype),t.Search=o}),ace.define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function o(e,t){this.platform=t||(i.isMac?"mac":"win"),this.commands={},this.commandKeyBinding={},this.addCommands(e),this.$singleCommand=!0}function u(e,t){o.call(this,e,t),this.$singleCommand=!1}var r=e("../lib/keys"),i=e("../lib/useragent"),s=r.KEY_MODS;u.prototype=o.prototype,function(){function e(e){return typeof e=="object"&&e.bindKey&&e.bindKey.position||(e.isDefault?-100:0)}this.addCommand=function(e){this.commands[e.name]&&this.removeCommand(e),this.commands[e.name]=e,e.bindKey&&this._buildKeyHash(e)},this.removeCommand=function(e,t){var n=e&&(typeof e=="string"?e:e.name);e=this.commands[n],t||delete this.commands[n];var r=this.commandKeyBinding;for(var i in r){var s=r[i];if(s==e)delete r[i];else if(Array.isArray(s)){var o=s.indexOf(e);o!=-1&&(s.splice(o,1),s.length==1&&(r[i]=s[0]))}}},this.bindKey=function(e,t,n){typeof e=="object"&&e&&(n==undefined&&(n=e.position),e=e[this.platform]);if(!e)return;if(typeof t=="function")return this.addCommand({exec:t,bindKey:e,name:t.name||e});e.split("|").forEach(function(e){var r="";if(e.indexOf(" ")!=-1){var i=e.split(/\s+/);e=i.pop(),i.forEach(function(e){var t=this.parseKeys(e),n=s[t.hashId]+t.key;r+=(r?" ":"")+n,this._addCommandToBinding(r,"chainKeys")},this),r+=" "}var o=this.parseKeys(e),u=s[o.hashId]+o.key;this._addCommandToBinding(r+u,t,n)},this)},this._addCommandToBinding=function(t,n,r){var i=this.commandKeyBinding,s;if(!n)delete i[t];else if(!i[t]||this.$singleCommand)i[t]=n;else{Array.isArray(i[t])?(s=i[t].indexOf(n))!=-1&&i[t].splice(s,1):i[t]=[i[t]],typeof r!="number"&&(r=e(n));var o=i[t];for(s=0;sr)break}o.splice(s,0,n)}},this.addCommands=function(e){e&&Object.keys(e).forEach(function(t){var n=e[t];if(!n)return;if(typeof n=="string")return this.bindKey(n,t);typeof n=="function"&&(n={exec:n});if(typeof n!="object")return;n.name||(n.name=t),this.addCommand(n)},this)},this.removeCommands=function(e){Object.keys(e).forEach(function(t){this.removeCommand(e[t])},this)},this.bindKeys=function(e){Object.keys(e).forEach(function(t){this.bindKey(t,e[t])},this)},this._buildKeyHash=function(e){this.bindKey(e.bindKey,e)},this.parseKeys=function(e){var t=e.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(e){return e}),n=t.pop(),i=r[n];if(r.FUNCTION_KEYS[i])n=r.FUNCTION_KEYS[i].toLowerCase();else{if(!t.length)return{key:n,hashId:-1};if(t.length==1&&t[0]=="shift")return{key:n.toUpperCase(),hashId:-1}}var s=0;for(var o=t.length;o--;){var u=r.KEY_MODS[t[o]];if(u==null)return typeof console!="undefined"&&console.error("invalid modifier "+t[o]+" in "+e),!1;s|=u}return{key:n,hashId:s}},this.findKeyCommand=function(t,n){var r=s[t]+n;return this.commandKeyBinding[r]},this.handleKeyboard=function(e,t,n,r){if(r<0)return;var i=s[t]+n,o=this.commandKeyBinding[i];e.$keyChain&&(e.$keyChain+=" "+i,o=this.commandKeyBinding[e.$keyChain]||o);if(o)if(o=="chainKeys"||o[o.length-1]=="chainKeys")return e.$keyChain=e.$keyChain||i,{command:"null"};if(e.$keyChain)if(!!t&&t!=4||n.length!=1){if(t==-1||r>0)e.$keyChain=""}else e.$keyChain=e.$keyChain.slice(0,-i.length-1);return{command:o}},this.getStatusText=function(e,t){return t.$keyChain||""}}.call(o.prototype),t.HashHandler=o,t.MultiHashHandler=u}),ace.define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../keyboard/hash_handler").MultiHashHandler,s=e("../lib/event_emitter").EventEmitter,o=function(e,t){i.call(this,t,e),this.byName=this.commands,this.setDefaultHandler("exec",function(e){return e.command.exec(e.editor,e.args||{})})};r.inherits(o,i),function(){r.implement(this,s),this.exec=function(e,t,n){if(Array.isArray(e)){for(var r=e.length;r--;)if(this.exec(e[r],t,n))return!0;return!1}typeof e=="string"&&(e=this.commands[e]);if(!e)return!1;if(t&&t.$readOnly&&!e.readOnly)return!1;if(this.$checkCommandState!=0&&e.isAvailable&&!e.isAvailable(t))return!1;var i={editor:t,command:e,args:n};return i.returnValue=this._emit("exec",i),this._signal("afterExec",i),i.returnValue===!1?!1:!0},this.toggleRecording=function(e){if(this.$inReplay)return;return e&&e._emit("changeStatus"),this.recording?(this.macro.pop(),this.removeEventListener("exec",this.$addCommandToMacro),this.macro.length||(this.macro=this.oldMacro),this.recording=!1):(this.$addCommandToMacro||(this.$addCommandToMacro=function(e){this.macro.push([e.command,e.args])}.bind(this)),this.oldMacro=this.macro,this.macro=[],this.on("exec",this.$addCommandToMacro),this.recording=!0)},this.replay=function(e){if(this.$inReplay||!this.macro)return;if(this.recording)return this.toggleRecording(e);try{this.$inReplay=!0,this.macro.forEach(function(t){typeof t=="string"?this.exec(t,e):this.exec(t[0],e,t[1])},this)}finally{this.$inReplay=!1}},this.trimMacro=function(e){return e.map(function(e){return typeof e[0]!="string"&&(e[0]=e[0].name),e[1]||(e=e[0]),e})}}.call(o.prototype),t.CommandManager=o}),ace.define("ace/commands/default_commands",["require","exports","module","ace/lib/lang","ace/config","ace/range"],function(e,t,n){"use strict";function o(e,t){return{win:e,mac:t}}var r=e("../lib/lang"),i=e("../config"),s=e("../range").Range;t.commands=[{name:"showSettingsMenu",bindKey:o("Ctrl-,","Command-,"),exec:function(e){i.loadModule("ace/ext/settings_menu",function(t){t.init(e),e.showSettingsMenu()})},readOnly:!0},{name:"goToNextError",bindKey:o("Alt-E","F4"),exec:function(e){i.loadModule("./ext/error_marker",function(t){t.showErrorMarker(e,1)})},scrollIntoView:"animate",readOnly:!0},{name:"goToPreviousError",bindKey:o("Alt-Shift-E","Shift-F4"),exec:function(e){i.loadModule("./ext/error_marker",function(t){t.showErrorMarker(e,-1)})},scrollIntoView:"animate",readOnly:!0},{name:"selectall",description:"Select all",bindKey:o("Ctrl-A","Command-A"),exec:function(e){e.selectAll()},readOnly:!0},{name:"centerselection",description:"Center selection",bindKey:o(null,"Ctrl-L"),exec:function(e){e.centerSelection()},readOnly:!0},{name:"gotoline",description:"Go to line...",bindKey:o("Ctrl-L","Command-L"),exec:function(e,t){typeof t=="number"&&!isNaN(t)&&e.gotoLine(t),e.prompt({$type:"gotoLine"})},readOnly:!0},{name:"fold",bindKey:o("Alt-L|Ctrl-F1","Command-Alt-L|Command-F1"),exec:function(e){e.session.toggleFold(!1)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"unfold",bindKey:o("Alt-Shift-L|Ctrl-Shift-F1","Command-Alt-Shift-L|Command-Shift-F1"),exec:function(e){e.session.toggleFold(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleFoldWidget",bindKey:o("F2","F2"),exec:function(e){e.session.toggleFoldWidget()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleParentFoldWidget",bindKey:o("Alt-F2","Alt-F2"),exec:function(e){e.session.toggleFoldWidget(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"foldall",description:"Fold all",bindKey:o(null,"Ctrl-Command-Option-0"),exec:function(e){e.session.foldAll()},scrollIntoView:"center",readOnly:!0},{name:"foldOther",description:"Fold other",bindKey:o("Alt-0","Command-Option-0"),exec:function(e){e.session.foldAll(),e.session.unfold(e.selection.getAllRanges())},scrollIntoView:"center",readOnly:!0},{name:"unfoldall",description:"Unfold all",bindKey:o("Alt-Shift-0","Command-Option-Shift-0"),exec:function(e){e.session.unfold()},scrollIntoView:"center",readOnly:!0},{name:"findnext",description:"Find next",bindKey:o("Ctrl-K","Command-G"),exec:function(e){e.findNext()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"findprevious",description:"Find previous",bindKey:o("Ctrl-Shift-K","Command-Shift-G"),exec:function(e){e.findPrevious()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"selectOrFindNext",description:"Select or find next",bindKey:o("Alt-K","Ctrl-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findNext()},readOnly:!0},{name:"selectOrFindPrevious",description:"Select or find previous",bindKey:o("Alt-Shift-K","Ctrl-Shift-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findPrevious()},readOnly:!0},{name:"find",description:"Find",bindKey:o("Ctrl-F","Command-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e)})},readOnly:!0},{name:"overwrite",description:"Overwrite",bindKey:"Insert",exec:function(e){e.toggleOverwrite()},readOnly:!0},{name:"selecttostart",description:"Select to start",bindKey:o("Ctrl-Shift-Home","Command-Shift-Home|Command-Shift-Up"),exec:function(e){e.getSelection().selectFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotostart",description:"Go to start",bindKey:o("Ctrl-Home","Command-Home|Command-Up"),exec:function(e){e.navigateFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectup",description:"Select up",bindKey:o("Shift-Up","Shift-Up|Ctrl-Shift-P"),exec:function(e){e.getSelection().selectUp()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golineup",description:"Go line up",bindKey:o("Up","Up|Ctrl-P"),exec:function(e,t){e.navigateUp(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttoend",description:"Select to end",bindKey:o("Ctrl-Shift-End","Command-Shift-End|Command-Shift-Down"),exec:function(e){e.getSelection().selectFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotoend",description:"Go to end",bindKey:o("Ctrl-End","Command-End|Command-Down"),exec:function(e){e.navigateFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectdown",description:"Select down",bindKey:o("Shift-Down","Shift-Down|Ctrl-Shift-N"),exec:function(e){e.getSelection().selectDown()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golinedown",description:"Go line down",bindKey:o("Down","Down|Ctrl-N"),exec:function(e,t){e.navigateDown(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordleft",description:"Select word left",bindKey:o("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(e){e.getSelection().selectWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordleft",description:"Go to word left",bindKey:o("Ctrl-Left","Option-Left"),exec:function(e){e.navigateWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolinestart",description:"Select to line start",bindKey:o("Alt-Shift-Left","Command-Shift-Left|Ctrl-Shift-A"),exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolinestart",description:"Go to line start",bindKey:o("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(e){e.navigateLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectleft",description:"Select left",bindKey:o("Shift-Left","Shift-Left|Ctrl-Shift-B"),exec:function(e){e.getSelection().selectLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoleft",description:"Go to left",bindKey:o("Left","Left|Ctrl-B"),exec:function(e,t){e.navigateLeft(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordright",description:"Select word right",bindKey:o("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(e){e.getSelection().selectWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordright",description:"Go to word right",bindKey:o("Ctrl-Right","Option-Right"),exec:function(e){e.navigateWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolineend",description:"Select to line end",bindKey:o("Alt-Shift-Right","Command-Shift-Right|Shift-End|Ctrl-Shift-E"),exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolineend",description:"Go to line end",bindKey:o("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(e){e.navigateLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectright",description:"Select right",bindKey:o("Shift-Right","Shift-Right"),exec:function(e){e.getSelection().selectRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoright",description:"Go to right",bindKey:o("Right","Right|Ctrl-F"),exec:function(e,t){e.navigateRight(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectpagedown",description:"Select page down",bindKey:"Shift-PageDown",exec:function(e){e.selectPageDown()},readOnly:!0},{name:"pagedown",description:"Page down",bindKey:o(null,"Option-PageDown"),exec:function(e){e.scrollPageDown()},readOnly:!0},{name:"gotopagedown",description:"Go to page down",bindKey:o("PageDown","PageDown|Ctrl-V"),exec:function(e){e.gotoPageDown()},readOnly:!0},{name:"selectpageup",description:"Select page up",bindKey:"Shift-PageUp",exec:function(e){e.selectPageUp()},readOnly:!0},{name:"pageup",description:"Page up",bindKey:o(null,"Option-PageUp"),exec:function(e){e.scrollPageUp()},readOnly:!0},{name:"gotopageup",description:"Go to page up",bindKey:"PageUp",exec:function(e){e.gotoPageUp()},readOnly:!0},{name:"scrollup",description:"Scroll up",bindKey:o("Ctrl-Up",null),exec:function(e){e.renderer.scrollBy(0,-2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"scrolldown",description:"Scroll down",bindKey:o("Ctrl-Down",null),exec:function(e){e.renderer.scrollBy(0,2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"selectlinestart",description:"Select line start",bindKey:"Shift-Home",exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectlineend",description:"Select line end",bindKey:"Shift-End",exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"togglerecording",description:"Toggle recording",bindKey:o("Ctrl-Alt-E","Command-Option-E"),exec:function(e){e.commands.toggleRecording(e)},readOnly:!0},{name:"replaymacro",description:"Replay macro",bindKey:o("Ctrl-Shift-E","Command-Shift-E"),exec:function(e){e.commands.replay(e)},readOnly:!0},{name:"jumptomatching",description:"Jump to matching",bindKey:o("Ctrl-P","Ctrl-P"),exec:function(e){e.jumpToMatching()},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"selecttomatching",description:"Select to matching",bindKey:o("Ctrl-Shift-P","Ctrl-Shift-P"),exec:function(e){e.jumpToMatching(!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"expandToMatching",description:"Expand to matching",bindKey:o("Ctrl-Shift-M","Ctrl-Shift-M"),exec:function(e){e.jumpToMatching(!0,!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"passKeysToBrowser",description:"Pass keys to browser",bindKey:o(null,null),exec:function(){},passEvent:!0,readOnly:!0},{name:"copy",description:"Copy",exec:function(e){},readOnly:!0},{name:"cut",description:"Cut",exec:function(e){var t=e.$copyWithEmptySelection&&e.selection.isEmpty(),n=t?e.selection.getLineRange():e.selection.getRange();e._emit("cut",n),n.isEmpty()||e.session.remove(n),e.clearSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"paste",description:"Paste",exec:function(e,t){e.$handlePaste(t)},scrollIntoView:"cursor"},{name:"removeline",description:"Remove line",bindKey:o("Ctrl-D","Command-D"),exec:function(e){e.removeLines()},scrollIntoView:"cursor",multiSelectAction:"forEachLine"},{name:"duplicateSelection",description:"Duplicate selection",bindKey:o("Ctrl-Shift-D","Command-Shift-D"),exec:function(e){e.duplicateSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"sortlines",description:"Sort lines",bindKey:o("Ctrl-Alt-S","Command-Alt-S"),exec:function(e){e.sortLines()},scrollIntoView:"selection",multiSelectAction:"forEachLine"},{name:"togglecomment",description:"Toggle comment",bindKey:o("Ctrl-/","Command-/"),exec:function(e){e.toggleCommentLines()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"toggleBlockComment",description:"Toggle block comment",bindKey:o("Ctrl-Shift-/","Command-Shift-/"),exec:function(e){e.toggleBlockComment()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"modifyNumberUp",description:"Modify number up",bindKey:o("Ctrl-Shift-Up","Alt-Shift-Up"),exec:function(e){e.modifyNumber(1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"modifyNumberDown",description:"Modify number down",bindKey:o("Ctrl-Shift-Down","Alt-Shift-Down"),exec:function(e){e.modifyNumber(-1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"replace",description:"Replace",bindKey:o("Ctrl-H","Command-Option-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!0)})}},{name:"undo",description:"Undo",bindKey:o("Ctrl-Z","Command-Z"),exec:function(e){e.undo()}},{name:"redo",description:"Redo",bindKey:o("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(e){e.redo()}},{name:"copylinesup",description:"Copy lines up",bindKey:o("Alt-Shift-Up","Command-Option-Up"),exec:function(e){e.copyLinesUp()},scrollIntoView:"cursor"},{name:"movelinesup",description:"Move lines up",bindKey:o("Alt-Up","Option-Up"),exec:function(e){e.moveLinesUp()},scrollIntoView:"cursor"},{name:"copylinesdown",description:"Copy lines down",bindKey:o("Alt-Shift-Down","Command-Option-Down"),exec:function(e){e.copyLinesDown()},scrollIntoView:"cursor"},{name:"movelinesdown",description:"Move lines down",bindKey:o("Alt-Down","Option-Down"),exec:function(e){e.moveLinesDown()},scrollIntoView:"cursor"},{name:"del",description:"Delete",bindKey:o("Delete","Delete|Ctrl-D|Shift-Delete"),exec:function(e){e.remove("right")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"backspace",description:"Backspace",bindKey:o("Shift-Backspace|Backspace","Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H"),exec:function(e){e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"cut_or_delete",description:"Cut or delete",bindKey:o("Shift-Delete",null),exec:function(e){if(!e.selection.isEmpty())return!1;e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestart",description:"Remove to line start",bindKey:o("Alt-Backspace","Command-Backspace"),exec:function(e){e.removeToLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineend",description:"Remove to line end",bindKey:o("Alt-Delete","Ctrl-K|Command-Delete"),exec:function(e){e.removeToLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestarthard",description:"Remove to line start hard",bindKey:o("Ctrl-Shift-Backspace",null),exec:function(e){var t=e.selection.getRange();t.start.column=0,e.session.remove(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineendhard",description:"Remove to line end hard",bindKey:o("Ctrl-Shift-Delete",null),exec:function(e){var t=e.selection.getRange();t.end.column=Number.MAX_VALUE,e.session.remove(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordleft",description:"Remove word left",bindKey:o("Ctrl-Backspace","Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(e){e.removeWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordright",description:"Remove word right",bindKey:o("Ctrl-Delete","Alt-Delete"),exec:function(e){e.removeWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"outdent",description:"Outdent",bindKey:o("Shift-Tab","Shift-Tab"),exec:function(e){e.blockOutdent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"indent",description:"Indent",bindKey:o("Tab","Tab"),exec:function(e){e.indent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"blockoutdent",description:"Block outdent",bindKey:o("Ctrl-[","Ctrl-["),exec:function(e){e.blockOutdent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"blockindent",description:"Block indent",bindKey:o("Ctrl-]","Ctrl-]"),exec:function(e){e.blockIndent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"insertstring",description:"Insert string",exec:function(e,t){e.insert(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"inserttext",description:"Insert text",exec:function(e,t){e.insert(r.stringRepeat(t.text||"",t.times||1))},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"splitline",description:"Split line",bindKey:o(null,"Ctrl-O"),exec:function(e){e.splitLine()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"transposeletters",description:"Transpose letters",bindKey:o("Alt-Shift-X","Ctrl-T"),exec:function(e){e.transposeLetters()},multiSelectAction:function(e){e.transposeSelections(1)},scrollIntoView:"cursor"},{name:"touppercase",description:"To uppercase",bindKey:o("Ctrl-U","Ctrl-U"),exec:function(e){e.toUpperCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"tolowercase",description:"To lowercase",bindKey:o("Ctrl-Shift-U","Ctrl-Shift-U"),exec:function(e){e.toLowerCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"expandtoline",description:"Expand to line",bindKey:o("Ctrl-Shift-L","Command-Shift-L"),exec:function(e){var t=e.selection.getRange();t.start.column=t.end.column=0,t.end.row++,e.selection.setRange(t,!1)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"joinlines",description:"Join lines",bindKey:o(null,null),exec:function(e){var t=e.selection.isBackwards(),n=t?e.selection.getSelectionLead():e.selection.getSelectionAnchor(),i=t?e.selection.getSelectionAnchor():e.selection.getSelectionLead(),o=e.session.doc.getLine(n.row).length,u=e.session.doc.getTextRange(e.selection.getRange()),a=u.replace(/\n\s*/," ").length,f=e.session.doc.getLine(n.row);for(var l=n.row+1;l<=i.row+1;l++){var c=r.stringTrimLeft(r.stringTrimRight(e.session.doc.getLine(l)));c.length!==0&&(c=" "+c),f+=c}i.row+10?(e.selection.moveCursorTo(n.row,n.column),e.selection.selectTo(n.row,n.column+a)):(o=e.session.doc.getLine(n.row).length>o?o+1:o,e.selection.moveCursorTo(n.row,o))},multiSelectAction:"forEach",readOnly:!0},{name:"invertSelection",description:"Invert selection",bindKey:o(null,null),exec:function(e){var t=e.session.doc.getLength()-1,n=e.session.doc.getLine(t).length,r=e.selection.rangeList.ranges,i=[];r.length<1&&(r=[e.selection.getRange()]);for(var o=0;o=i.lastRow||r.end.row<=i.firstRow)&&this.renderer.scrollSelectionIntoView(this.selection.anchor,this.selection.lead);break;default:}n=="animate"&&this.renderer.animateScrolling(this.curOp.scrollTop)}var s=this.selection.toJSON();this.curOp.selectionAfter=s,this.$lastSel=this.selection.toJSON(),this.session.getUndoManager().addSelection(s),this.prevOp=this.curOp,this.curOp=null}},this.$mergeableCommands=["backspace","del","insertstring"],this.$historyTracker=function(e){if(!this.$mergeUndoDeltas)return;var t=this.prevOp,n=this.$mergeableCommands,r=t.command&&e.command.name==t.command.name;if(e.command.name=="insertstring"){var i=e.args;this.mergeNextCommand===undefined&&(this.mergeNextCommand=!0),r=r&&this.mergeNextCommand&&(!/\s/.test(i)||/\s/.test(t.args)),this.mergeNextCommand=!0}else r=r&&n.indexOf(e.command.name)!==-1;this.$mergeUndoDeltas!="always"&&Date.now()-this.sequenceStartTime>2e3&&(r=!1),r?this.session.mergeUndoDeltas=!0:n.indexOf(e.command.name)!==-1&&(this.sequenceStartTime=Date.now())},this.setKeyboardHandler=function(e,t){if(e&&typeof e=="string"&&e!="ace"){this.$keybindingId=e;var n=this;g.loadModule(["keybinding",e],function(r){n.$keybindingId==e&&n.keyBinding.setKeyboardHandler(r&&r.handler),t&&t()})}else this.$keybindingId=null,this.keyBinding.setKeyboardHandler(e),t&&t()},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(e){if(this.session==e)return;this.curOp&&this.endOperation(),this.curOp={};var t=this.session;if(t){this.session.off("change",this.$onDocumentChange),this.session.off("changeMode",this.$onChangeMode),this.session.off("tokenizerUpdate",this.$onTokenizerUpdate),this.session.off("changeTabSize",this.$onChangeTabSize),this.session.off("changeWrapLimit",this.$onChangeWrapLimit),this.session.off("changeWrapMode",this.$onChangeWrapMode),this.session.off("changeFold",this.$onChangeFold),this.session.off("changeFrontMarker",this.$onChangeFrontMarker),this.session.off("changeBackMarker",this.$onChangeBackMarker),this.session.off("changeBreakpoint",this.$onChangeBreakpoint),this.session.off("changeAnnotation",this.$onChangeAnnotation),this.session.off("changeOverwrite",this.$onCursorChange),this.session.off("changeScrollTop",this.$onScrollTopChange),this.session.off("changeScrollLeft",this.$onScrollLeftChange);var n=this.session.getSelection();n.off("changeCursor",this.$onCursorChange),n.off("changeSelection",this.$onSelectionChange)}this.session=e,e?(this.$onDocumentChange=this.onDocumentChange.bind(this),e.on("change",this.$onDocumentChange),this.renderer.setSession(e),this.$onChangeMode=this.onChangeMode.bind(this),e.on("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),e.on("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.onChangeTabSize.bind(this.renderer),e.on("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),e.on("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),e.on("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),e.on("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.on("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.on("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.on("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.on("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.on("changeOverwrite",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.on("changeScrollTop",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.on("changeScrollLeft",this.$onScrollLeftChange),this.selection=e.getSelection(),this.selection.on("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.on("changeSelection",this.$onSelectionChange),this.onChangeMode(),this.onCursorChange(),this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull()):(this.selection=null,this.renderer.setSession(e)),this._signal("changeSession",{session:e,oldSession:t}),this.curOp=null,t&&t._signal("changeEditor",{oldEditor:this}),e&&e._signal("changeEditor",{editor:this}),e&&e.bgTokenizer&&e.bgTokenizer.scheduleStart()},this.getSession=function(){return this.session},this.setValue=function(e,t){return this.session.doc.setValue(e),t?t==1?this.navigateFileEnd():t==-1&&this.navigateFileStart():this.selectAll(),e},this.getValue=function(){return this.session.getValue()},this.getSelection=function(){return this.selection},this.resize=function(e){this.renderer.onResize(e)},this.setTheme=function(e,t){this.renderer.setTheme(e,t)},this.getTheme=function(){return this.renderer.getTheme()},this.setStyle=function(e){this.renderer.setStyle(e)},this.unsetStyle=function(e){this.renderer.unsetStyle(e)},this.getFontSize=function(){return this.getOption("fontSize")||i.computedStyle(this.container).fontSize},this.setFontSize=function(e){this.setOption("fontSize",e)},this.$highlightBrackets=function(){this.session.$bracketHighlight&&(this.session.removeMarker(this.session.$bracketHighlight),this.session.$bracketHighlight=null);if(this.$highlightPending)return;var e=this;this.$highlightPending=!0,setTimeout(function(){e.$highlightPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=t.findMatchingBracket(e.getCursorPosition());if(n)var r=new p(n.row,n.column,n.row,n.column+1);else if(t.$mode.getMatching)var r=t.$mode.getMatching(e.session);r&&(t.$bracketHighlight=t.addMarker(r,"ace_bracket","text"))},50)},this.$highlightTags=function(){if(this.$highlightTagPending)return;var e=this;this.$highlightTagPending=!0,setTimeout(function(){e.$highlightTagPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=e.getCursorPosition(),r=new y(e.session,n.row,n.column),i=r.getCurrentToken();if(!i||!/\b(?:tag-open|tag-name)/.test(i.type)){t.removeMarker(t.$tagHighlight),t.$tagHighlight=null;return}if(i.type.indexOf("tag-open")!=-1){i=r.stepForward();if(!i)return}var s=i.value,o=0,u=r.stepBackward();if(u.value=="<"){do u=i,i=r.stepForward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="=0)}else{do i=u,u=r.stepBackward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="1)&&(t=!1)}if(e.$highlightLineMarker&&!t)e.removeMarker(e.$highlightLineMarker.id),e.$highlightLineMarker=null;else if(!e.$highlightLineMarker&&t){var n=new p(t.row,t.column,t.row,Infinity);n.id=e.addMarker(n,"ace_active-line","screenLine"),e.$highlightLineMarker=n}else t&&(e.$highlightLineMarker.start.row=t.row,e.$highlightLineMarker.end.row=t.row,e.$highlightLineMarker.start.column=t.column,e._signal("changeBackMarker"))},this.onSelectionChange=function(e){var t=this.session;t.$selectionMarker&&t.removeMarker(t.$selectionMarker),t.$selectionMarker=null;if(!this.selection.isEmpty()){var n=this.selection.getRange(),r=this.getSelectionStyle();t.$selectionMarker=t.addMarker(n,"ace_selection",r)}else this.$updateHighlightActiveLine();var i=this.$highlightSelectedWord&&this.$getSelectionHighLightRegexp();this.session.highlight(i),this._signal("changeSelection")},this.$getSelectionHighLightRegexp=function(){var e=this.session,t=this.getSelectionRange();if(t.isEmpty()||t.isMultiLine())return;var n=t.start.column,r=t.end.column,i=e.getLine(t.start.row),s=i.substring(n,r);if(s.length>5e3||!/[\w\d]/.test(s))return;var o=this.$search.$assembleRegExp({wholeWord:!0,caseSensitive:!0,needle:s}),u=i.substring(n-1,r+1);if(!o.test(u))return;return o},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.updateBreakpoints()},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(e){this.renderer.updateText(),this._emit("changeMode",e)},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},this.getSelectedText=function(){return this.session.getTextRange(this.getSelectionRange())},this.getCopyText=function(){var e=this.getSelectedText(),t=this.session.doc.getNewLineCharacter(),n=!1;if(!e&&this.$copyWithEmptySelection){n=!0;var r=this.selection.getAllRanges();for(var i=0;is.length||i.length<2||!i[1])return this.commands.exec("insertstring",this,t);for(var o=s.length;o--;){var u=s[o];u.isEmpty()||r.remove(u),r.insert(u.start,i[o])}}},this.execCommand=function(e,t){return this.commands.exec(e,this,t)},this.insert=function(e,t){var n=this.session,r=n.getMode(),i=this.getCursorPosition();if(this.getBehavioursEnabled()&&!t){var s=r.transformAction(n.getState(i.row),"insertion",this,n,e);s&&(e!==s.text&&(this.inVirtualSelectionMode||(this.session.mergeUndoDeltas=!1,this.mergeNextCommand=!1)),e=s.text)}e==" "&&(e=this.session.getTabString());if(!this.selection.isEmpty()){var o=this.getSelectionRange();i=this.session.remove(o),this.clearSelection()}else if(this.session.getOverwrite()&&e.indexOf("\n")==-1){var o=new p.fromPoints(i,i);o.end.column+=e.length,this.session.remove(o)}if(e=="\n"||e=="\r\n"){var u=n.getLine(i.row);if(i.column>u.search(/\S|$/)){var a=u.substr(i.column).search(/\S|$/);n.doc.removeInLine(i.row,i.column,i.column+a)}}this.clearSelection();var f=i.column,l=n.getState(i.row),u=n.getLine(i.row),c=r.checkOutdent(l,u,e),h=n.insert(i,e);s&&s.selection&&(s.selection.length==2?this.selection.setSelectionRange(new p(i.row,f+s.selection[0],i.row,f+s.selection[1])):this.selection.setSelectionRange(new p(i.row+s.selection[0],s.selection[1],i.row+s.selection[2],s.selection[3])));if(n.getDocument().isNewLine(e)){var d=r.getNextLineIndent(l,u.slice(0,i.column),n.getTabString());n.insert({row:i.row+1,column:0},d)}c&&r.autoOutdent(l,n,i.row)},this.onTextInput=function(e,t){if(!t)return this.keyBinding.onTextInput(e);this.startOperation({command:{name:"insertstring"}});var n=this.applyComposition.bind(this,e,t);this.selection.rangeCount?this.forEachSelection(n):n(),this.endOperation()},this.applyComposition=function(e,t){if(t.extendLeft||t.extendRight){var n=this.selection.getRange();n.start.column-=t.extendLeft,n.end.column+=t.extendRight,this.selection.setRange(n),!e&&!n.isEmpty()&&this.remove()}(e||!this.selection.isEmpty())&&this.insert(e,!0);if(t.restoreStart||t.restoreEnd){var n=this.selection.getRange();n.start.column-=t.restoreStart,n.end.column-=t.restoreEnd,this.selection.setRange(n)}},this.onCommandKey=function(e,t,n){this.keyBinding.onCommandKey(e,t,n)},this.setOverwrite=function(e){this.session.setOverwrite(e)},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(e){this.setOption("scrollSpeed",e)},this.getScrollSpeed=function(){return this.getOption("scrollSpeed")},this.setDragDelay=function(e){this.setOption("dragDelay",e)},this.getDragDelay=function(){return this.getOption("dragDelay")},this.setSelectionStyle=function(e){this.setOption("selectionStyle",e)},this.getSelectionStyle=function(){return this.getOption("selectionStyle")},this.setHighlightActiveLine=function(e){this.setOption("highlightActiveLine",e)},this.getHighlightActiveLine=function(){return this.getOption("highlightActiveLine")},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.setHighlightSelectedWord=function(e){this.setOption("highlightSelectedWord",e)},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setAnimatedScroll=function(e){this.renderer.setAnimatedScroll(e)},this.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},this.setShowInvisibles=function(e){this.renderer.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setDisplayIndentGuides=function(e){this.renderer.setDisplayIndentGuides(e)},this.getDisplayIndentGuides=function(){return this.renderer.getDisplayIndentGuides()},this.setShowPrintMargin=function(e){this.renderer.setShowPrintMargin(e)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(e){this.renderer.setPrintMarginColumn(e)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.setReadOnly=function(e){this.setOption("readOnly",e)},this.getReadOnly=function(){return this.getOption("readOnly")},this.setBehavioursEnabled=function(e){this.setOption("behavioursEnabled",e)},this.getBehavioursEnabled=function(){return this.getOption("behavioursEnabled")},this.setWrapBehavioursEnabled=function(e){this.setOption("wrapBehavioursEnabled",e)},this.getWrapBehavioursEnabled=function(){return this.getOption("wrapBehavioursEnabled")},this.setShowFoldWidgets=function(e){this.setOption("showFoldWidgets",e)},this.getShowFoldWidgets=function(){return this.getOption("showFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.remove=function(e){this.selection.isEmpty()&&(e=="left"?this.selection.selectLeft():this.selection.selectRight());var t=this.getSelectionRange();if(this.getBehavioursEnabled()){var n=this.session,r=n.getState(t.start.row),i=n.getMode().transformAction(r,"deletion",this,n,t);if(t.end.column===0){var s=n.getTextRange(t);if(s[s.length-1]=="\n"){var o=n.getLine(t.end.row);/^\s+$/.test(o)&&(t.end.column=o.length)}}i&&(t=i)}this.session.remove(t),this.clearSelection()},this.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.selection.isEmpty()&&this.selection.selectLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var e=this.getSelectionRange();e.start.column==e.end.column&&e.start.row==e.end.row&&(e.end.column=0,e.end.row++),this.session.remove(e),this.clearSelection()},this.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var e=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(e)},this.transposeLetters=function(){if(!this.selection.isEmpty())return;var e=this.getCursorPosition(),t=e.column;if(t===0)return;var n=this.session.getLine(e.row),r,i;tt.toLowerCase()?1:0});var i=new p(0,0,0,0);for(var r=e.first;r<=e.last;r++){var s=t.getLine(r);i.start.row=r,i.end.row=r,i.end.column=s.length,t.replace(i,n[r-e.first])}},this.toggleCommentLines=function(){var e=this.session.getState(this.getCursorPosition().row),t=this.$getSelectedRows();this.session.getMode().toggleCommentLines(e,this.session,t.first,t.last)},this.toggleBlockComment=function(){var e=this.getCursorPosition(),t=this.session.getState(e.row),n=this.getSelectionRange();this.session.getMode().toggleBlockComment(t,this.session,n,e)},this.getNumberAt=function(e,t){var n=/[\-]?[0-9]+(?:\.[0-9]+)?/g;n.lastIndex=0;var r=this.session.getLine(e);while(n.lastIndex=t){var s={value:i[0],start:i.index,end:i.index+i[0].length};return s}}return null},this.modifyNumber=function(e){var t=this.selection.getCursor().row,n=this.selection.getCursor().column,r=new p(t,n-1,t,n),i=this.session.getTextRange(r);if(!isNaN(parseFloat(i))&&isFinite(i)){var s=this.getNumberAt(t,n);if(s){var o=s.value.indexOf(".")>=0?s.start+s.value.indexOf(".")+1:s.end,u=s.start+s.value.length-o,a=parseFloat(s.value);a*=Math.pow(10,u),o!==s.end&&n=u&&o<=a&&(n=t,f.selection.clearSelection(),f.moveCursorTo(e,u+r),f.selection.selectTo(e,a+r)),u=a});var l=this.$toggleWordPairs,c;for(var h=0;hp+1)break;p=d.last}l--,u=this.session.$moveLines(h,p,t?0:e),t&&e==-1&&(c=l+1);while(c<=l)o[c].moveBy(u,0),c++;t||(u=0),a+=u}i.fromOrientedRange(i.ranges[0]),i.rangeList.attach(this.session),this.inVirtualSelectionMode=!1}},this.$getSelectedRows=function(e){return e=(e||this.getSelectionRange()).collapseRows(),{first:this.session.getRowFoldStart(e.start.row),last:this.session.getRowFoldEnd(e.end.row)}},this.onCompositionStart=function(e){this.renderer.showComposition(e)},this.onCompositionUpdate=function(e){this.renderer.setCompositionText(e)},this.onCompositionEnd=function(){this.renderer.hideComposition()},this.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},this.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},this.isRowVisible=function(e){return e>=this.getFirstVisibleRow()&&e<=this.getLastVisibleRow()},this.isRowFullyVisible=function(e){return e>=this.renderer.getFirstFullyVisibleRow()&&e<=this.renderer.getLastFullyVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$moveByPage=function(e,t){var n=this.renderer,r=this.renderer.layerConfig,i=e*Math.floor(r.height/r.lineHeight);t===!0?this.selection.$moveSelection(function(){this.moveCursorBy(i,0)}):t===!1&&(this.selection.moveCursorBy(i,0),this.selection.clearSelection());var s=n.scrollTop;n.scrollBy(0,i*r.lineHeight),t!=null&&n.scrollCursorIntoView(null,.5),n.animateScrolling(s)},this.selectPageDown=function(){this.$moveByPage(1,!0)},this.selectPageUp=function(){this.$moveByPage(-1,!0)},this.gotoPageDown=function(){this.$moveByPage(1,!1)},this.gotoPageUp=function(){this.$moveByPage(-1,!1)},this.scrollPageDown=function(){this.$moveByPage(1)},this.scrollPageUp=function(){this.$moveByPage(-1)},this.scrollToRow=function(e){this.renderer.scrollToRow(e)},this.scrollToLine=function(e,t,n,r){this.renderer.scrollToLine(e,t,n,r)},this.centerSelection=function(){var e=this.getSelectionRange(),t={row:Math.floor(e.start.row+(e.end.row-e.start.row)/2),column:Math.floor(e.start.column+(e.end.column-e.start.column)/2)};this.renderer.alignCursor(t,.5)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.selection.selectAll()},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(e,t){this.selection.moveCursorTo(e,t)},this.moveCursorToPosition=function(e){this.selection.moveCursorToPosition(e)},this.jumpToMatching=function(e,t){var n=this.getCursorPosition(),r=new y(this.session,n.row,n.column),i=r.getCurrentToken(),s=i||r.stepForward();if(!s)return;var o,u=!1,a={},f=n.column-s.start,l,c={")":"(","(":"(","]":"[","[":"[","{":"{","}":"{"};do{if(s.value.match(/[{}()\[\]]/g))for(;f=0;--s)this.$tryReplace(n[s],e)&&r++;return this.selection.setSelectionRange(i),r},this.$tryReplace=function(e,t){var n=this.session.getTextRange(e);return t=this.$search.replace(n,t),t!==null?(e.end=this.session.replace(e,t),e):null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(e,t,n){t||(t={}),typeof e=="string"||e instanceof RegExp?t.needle=e:typeof e=="object"&&r.mixin(t,e);var i=this.selection.getRange();t.needle==null&&(e=this.session.getTextRange(i)||this.$search.$options.needle,e||(i=this.session.getWordRange(i.start.row,i.start.column),e=this.session.getTextRange(i)),this.$search.set({needle:e})),this.$search.set(t),t.start||this.$search.set({start:i});var s=this.$search.find(this.session);if(t.preventScroll)return s;if(s)return this.revealRange(s,n),s;t.backwards?i.start=i.end:i.end=i.start,this.selection.setRange(i)},this.findNext=function(e,t){this.find({skipCurrent:!0,backwards:!1},e,t)},this.findPrevious=function(e,t){this.find(e,{skipCurrent:!0,backwards:!0},t)},this.revealRange=function(e,t){this.session.unfold(e),this.selection.setSelectionRange(e);var n=this.renderer.scrollTop;this.renderer.scrollSelectionIntoView(e.start,e.end,.5),t!==!1&&this.renderer.animateScrolling(n)},this.undo=function(){this.session.getUndoManager().undo(this.session),this.renderer.scrollCursorIntoView(null,.5)},this.redo=function(){this.session.getUndoManager().redo(this.session),this.renderer.scrollCursorIntoView(null,.5)},this.destroy=function(){this.renderer.destroy(),this._signal("destroy",this),this.session&&this.session.destroy()},this.setAutoScrollEditorIntoView=function(e){if(!e)return;var t,n=this,r=!1;this.$scrollAnchor||(this.$scrollAnchor=document.createElement("div"));var i=this.$scrollAnchor;i.style.cssText="position:absolute",this.container.insertBefore(i,this.container.firstChild);var s=this.on("changeSelection",function(){r=!0}),o=this.renderer.on("beforeRender",function(){r&&(t=n.renderer.container.getBoundingClientRect())}),u=this.renderer.on("afterRender",function(){if(r&&t&&(n.isFocused()||n.searchBox&&n.searchBox.isFocused())){var e=n.renderer,s=e.$cursorLayer.$pixelPos,o=e.layerConfig,u=s.top-o.offset;s.top>=0&&u+t.top<0?r=!0:s.topwindow.innerHeight?r=!1:r=null,r!=null&&(i.style.top=u+"px",i.style.left=s.left+"px",i.style.height=o.lineHeight+"px",i.scrollIntoView(r)),r=t=null}});this.setAutoScrollEditorIntoView=function(e){if(e)return;delete this.setAutoScrollEditorIntoView,this.off("changeSelection",s),this.renderer.off("afterRender",u),this.renderer.off("beforeRender",o)}},this.$resetCursorStyle=function(){var e=this.$cursorStyle||"ace",t=this.renderer.$cursorLayer;if(!t)return;t.setSmoothBlinking(/smooth/.test(e)),t.isBlinking=!this.$readOnly&&e!="wide",i.setCssClass(t.element,"ace_slim-cursors",/slim/.test(e))},this.prompt=function(e,t,n){var r=this;g.loadModule("./ext/prompt",function(i){i.prompt(r,e,t,n)})}}.call(w.prototype),g.defineOptions(w.prototype,"editor",{selectionStyle:{set:function(e){this.onSelectionChange(),this._signal("changeSelectionStyle",{data:e})},initialValue:"line"},highlightActiveLine:{set:function(){this.$updateHighlightActiveLine()},initialValue:!0},highlightSelectedWord:{set:function(e){this.$onSelectionChange()},initialValue:!0},readOnly:{set:function(e){this.textInput.setReadOnly(e),this.$resetCursorStyle()},initialValue:!1},copyWithEmptySelection:{set:function(e){this.textInput.setCopyWithEmptySelection(e)},initialValue:!1},cursorStyle:{set:function(e){this.$resetCursorStyle()},values:["ace","slim","smooth","wide"],initialValue:"ace"},mergeUndoDeltas:{values:[!1,!0,"always"],initialValue:!0},behavioursEnabled:{initialValue:!0},wrapBehavioursEnabled:{initialValue:!0},autoScrollEditorIntoView:{set:function(e){this.setAutoScrollEditorIntoView(e)}},keyboardHandler:{set:function(e){this.setKeyboardHandler(e)},get:function(){return this.$keybindingId},handlesSet:!0},value:{set:function(e){this.session.setValue(e)},get:function(){return this.getValue()},handlesSet:!0,hidden:!0},session:{set:function(e){this.setSession(e)},get:function(){return this.session},handlesSet:!0,hidden:!0},showLineNumbers:{set:function(e){this.renderer.$gutterLayer.setShowLineNumbers(e),this.renderer.$loop.schedule(this.renderer.CHANGE_GUTTER),e&&this.$relativeLineNumbers?E.attach(this):E.detach(this)},initialValue:!0},relativeLineNumbers:{set:function(e){this.$showLineNumbers&&e?E.attach(this):E.detach(this)}},hScrollBarAlwaysVisible:"renderer",vScrollBarAlwaysVisible:"renderer",highlightGutterLine:"renderer",animatedScroll:"renderer",showInvisibles:"renderer",showPrintMargin:"renderer",printMarginColumn:"renderer",printMargin:"renderer",fadeFoldWidgets:"renderer",showFoldWidgets:"renderer",displayIndentGuides:"renderer",showGutter:"renderer",fontSize:"renderer",fontFamily:"renderer",maxLines:"renderer",minLines:"renderer",scrollPastEnd:"renderer",fixedWidthGutter:"renderer",theme:"renderer",hasCssTransforms:"renderer",maxPixelHeight:"renderer",useTextareaForIME:"renderer",scrollSpeed:"$mouseHandler",dragDelay:"$mouseHandler",dragEnabled:"$mouseHandler",focusTimeout:"$mouseHandler",tooltipFollowsMouse:"$mouseHandler",firstLineNumber:"session",overwrite:"session",newLineMode:"session",useWorker:"session",useSoftTabs:"session",navigateWithinSoftTabs:"session",tabSize:"session",wrap:"session",indentedSoftWrap:"session",foldStyle:"session",mode:"session"});var E={getText:function(e,t){return(Math.abs(e.selection.lead.row-t)||t+1+(t<9?"\u00b7":""))+""},getWidth:function(e,t,n){return Math.max(t.toString().length,(n.lastRow+1).toString().length,2)*n.characterWidth},update:function(e,t){t.renderer.$loop.schedule(t.renderer.CHANGE_GUTTER)},attach:function(e){e.renderer.$gutterLayer.$renderer=this,e.on("changeSelection",this.update),this.update(null,e)},detach:function(e){e.renderer.$gutterLayer.$renderer==this&&(e.renderer.$gutterLayer.$renderer=null),e.off("changeSelection",this.update),this.update(null,e)}};t.Editor=w}),ace.define("ace/undomanager",["require","exports","module","ace/range"],function(e,t,n){"use strict";function i(e,t){for(var n=t;n--;){var r=e[n];if(r&&!r[0].ignore){while(n0){a.row+=i,a.column+=a.row==r.row?s:0;continue}!t&&l<=0&&(a.row=n.row,a.column=n.column,l===0&&(a.bias=1))}}function f(e){return{row:e.row,column:e.column}}function l(e){return{start:f(e.start),end:f(e.end),action:e.action,lines:e.lines.slice()}}function c(e){e=e||this;if(Array.isArray(e))return e.map(c).join("\n");var t="";e.action?(t=e.action=="insert"?"+":"-",t+="["+e.lines+"]"):e.value&&(Array.isArray(e.value)?t=e.value.map(h).join("\n"):t=h(e.value)),e.start&&(t+=h(e));if(e.id||e.rev)t+=" ("+(e.id||e.rev)+")";return t}function h(e){return e.start.row+":"+e.start.column+"=>"+e.end.row+":"+e.end.column}function p(e,t){var n=e.action=="insert",r=t.action=="insert";if(n&&r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.start,e.start)<=0))return null;m(e,t,1)}else if(n&&!r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.end,e.start)<=0))return null;m(e,t,-1)}else if(!n&&r)if(o(t.start,e.start)>=0)m(t,e,1);else{if(!(o(t.start,e.start)<=0))return null;m(e,t,1)}else if(!n&&!r)if(o(t.start,e.start)>=0)m(t,e,1);else{if(!(o(t.end,e.start)<=0))return null;m(e,t,-1)}return[t,e]}function d(e,t){for(var n=e.length;n--;)for(var r=0;r=0?m(e,t,-1):o(e.start,t.start)<=0?m(t,e,1):(m(e,s.fromPoints(t.start,e.start),-1),m(t,e,1));else if(!n&&r)o(t.start,e.end)>=0?m(t,e,-1):o(t.start,e.start)<=0?m(e,t,1):(m(t,s.fromPoints(e.start,t.start),-1),m(e,t,1));else if(!n&&!r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.end,e.start)<=0)){var i,u;return o(e.start,t.start)<0&&(i=e,e=y(e,t.start)),o(e.end,t.end)>0&&(u=y(e,t.end)),g(t.end,e.start,e.end,-1),u&&!i&&(e.lines=u.lines,e.start=u.start,e.end=u.end,u=e),[t,i,u].filter(Boolean)}m(e,t,-1)}return[t,e]}function m(e,t,n){g(e.start,t.start,t.end,n),g(e.end,t.start,t.end,n)}function g(e,t,n,r){e.row==(r==1?t:n).row&&(e.column+=r*(n.column-t.column)),e.row+=r*(n.row-t.row)}function y(e,t){var n=e.lines,r=e.end;e.end=f(t);var i=e.end.row-e.start.row,s=n.splice(i,n.length),o=i?t.column:t.column-e.start.column;n.push(s[0].substring(0,o)),s[0]=s[0].substr(o);var u={start:f(t),end:r,lines:s,action:e.action};return u}function b(e,t){t=l(t);for(var n=e.length;n--;){var r=e[n];for(var i=0;i0},this.canRedo=function(){return this.$redoStack.length>0},this.bookmark=function(e){e==undefined&&(e=this.$rev),this.mark=e},this.isAtBookmark=function(){return this.$rev===this.mark},this.toJSON=function(){},this.fromJSON=function(){},this.hasUndo=this.canUndo,this.hasRedo=this.canRedo,this.isClean=this.isAtBookmark,this.markClean=this.bookmark,this.$prettyPrint=function(e){return e?c(e):c(this.$undoStack)+"\n---\n"+c(this.$redoStack)}}).call(r.prototype);var s=e("./range").Range,o=s.comparePoints,u=s.comparePoints;t.UndoManager=r}),ace.define("ace/layer/lines",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=function(e,t){this.element=e,this.canvasHeight=t||5e5,this.element.style.height=this.canvasHeight*2+"px",this.cells=[],this.cellCache=[],this.$offsetCoefficient=0};(function(){this.moveContainer=function(e){r.translate(this.element,0,-(e.firstRowScreen*e.lineHeight%this.canvasHeight)-e.offset*this.$offsetCoefficient)},this.pageChanged=function(e,t){return Math.floor(e.firstRowScreen*e.lineHeight/this.canvasHeight)!==Math.floor(t.firstRowScreen*t.lineHeight/this.canvasHeight)},this.computeLineTop=function(e,t,n){var r=t.firstRowScreen*t.lineHeight,i=Math.floor(r/this.canvasHeight),s=n.documentToScreenRow(e,0)*t.lineHeight;return s-i*this.canvasHeight},this.computeLineHeight=function(e,t,n){return t.lineHeight*n.getRowLength(e)},this.getLength=function(){return this.cells.length},this.get=function(e){return this.cells[e]},this.shift=function(){this.$cacheCell(this.cells.shift())},this.pop=function(){this.$cacheCell(this.cells.pop())},this.push=function(e){if(Array.isArray(e)){this.cells.push.apply(this.cells,e);var t=r.createFragment(this.element);for(var n=0;ns&&(a=i.end.row+1,i=t.getNextFoldLine(a,i),s=i?i.start.row:Infinity);if(a>r){while(this.$lines.getLength()>u+1)this.$lines.pop();break}o=this.$lines.get(++u),o?o.row=a:(o=this.$lines.createCell(a,e,this.session,f),this.$lines.push(o)),this.$renderCell(o,e,i,a),a++}this._signal("afterRender"),this.$updateGutterWidth(e)},this.$updateGutterWidth=function(e){var t=this.session,n=t.gutterRenderer||this.$renderer,r=t.$firstLineNumber,i=this.$lines.last()?this.$lines.last().text:"";if(this.$fixedWidth||t.$useWrapMode)i=t.getLength()+r-1;var s=n?n.getWidth(t,i,e):i.toString().length*e.characterWidth,o=this.$padding||this.$computePadding();s+=o.left+o.right,s!==this.gutterWidth&&!isNaN(s)&&(this.gutterWidth=s,this.element.parentNode.style.width=this.element.style.width=Math.ceil(this.gutterWidth)+"px",this._signal("changeGutterWidth",s))},this.$updateCursorRow=function(){if(!this.$highlightGutterLine)return;var e=this.session.selection.getCursor();if(this.$cursorRow===e.row)return;this.$cursorRow=e.row},this.updateLineHighlight=function(){if(!this.$highlightGutterLine)return;var e=this.session.selection.cursor.row;this.$cursorRow=e;if(this.$cursorCell&&this.$cursorCell.row==e)return;this.$cursorCell&&(this.$cursorCell.element.className=this.$cursorCell.element.className.replace("ace_gutter-active-line ",""));var t=this.$lines.cells;this.$cursorCell=null;for(var n=0;n=this.$cursorRow){if(r.row>this.$cursorRow){var i=this.session.getFoldLine(this.$cursorRow);if(!(n>0&&i&&i.start.row==t[n-1].row))break;r=t[n-1]}r.element.className="ace_gutter-active-line "+r.element.className,this.$cursorCell=r;break}}},this.scrollLines=function(e){var t=this.config;this.config=e,this.$updateCursorRow();if(this.$lines.pageChanged(t,e))return this.update(e);this.$lines.moveContainer(e);var n=Math.min(e.lastRow+e.gutterOffset,this.session.getLength()-1),r=this.oldLastRow;this.oldLastRow=n;if(!t||r0;i--)this.$lines.shift();if(r>n)for(var i=this.session.getFoldedRowCount(n+1,r);i>0;i--)this.$lines.pop();e.firstRowr&&this.$lines.push(this.$renderLines(e,r+1,n)),this.updateLineHighlight(),this._signal("afterRender"),this.$updateGutterWidth(e)},this.$renderLines=function(e,t,n){var r=[],i=t,s=this.session.getNextFoldLine(i),o=s?s.start.row:Infinity;for(;;){i>o&&(i=s.end.row+1,s=this.session.getNextFoldLine(i,s),o=s?s.start.row:Infinity);if(i>n)break;var u=this.$lines.createCell(i,e,this.session,f);this.$renderCell(u,e,s,i),r.push(u),i++}return r},this.$renderCell=function(e,t,n,i){var s=e.element,o=this.session,u=s.childNodes[0],a=s.childNodes[1],f=o.$firstLineNumber,l=o.$breakpoints,c=o.$decorations,h=o.gutterRenderer||this.$renderer,p=this.$showFoldWidgets&&o.foldWidgets,d=n?n.start.row:Number.MAX_VALUE,v="ace_gutter-cell ";this.$highlightGutterLine&&(i==this.$cursorRow||n&&i=d&&this.$cursorRow<=n.end.row)&&(v+="ace_gutter-active-line ",this.$cursorCell!=e&&(this.$cursorCell&&(this.$cursorCell.element.className=this.$cursorCell.element.className.replace("ace_gutter-active-line ","")),this.$cursorCell=e)),l[i]&&(v+=l[i]),c[i]&&(v+=c[i]),this.$annotations[i]&&(v+=this.$annotations[i].className),s.className!=v&&(s.className=v);if(p){var m=p[i];m==null&&(m=p[i]=o.getFoldWidget(i))}if(m){var v="ace_fold-widget ace_"+m;m=="start"&&i==d&&in.right-t.right)return"foldWidgets"}}).call(a.prototype),t.Gutter=a}),ace.define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../range").Range,i=e("../lib/dom"),s=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_marker-layer",e.appendChild(this.element)};(function(){function e(e,t,n,r){return(e?1:0)|(t?2:0)|(n?4:0)|(r?8:0)}this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setMarkers=function(e){this.markers=e},this.elt=function(e,t){var n=this.i!=-1&&this.element.childNodes[this.i];n?this.i++:(n=document.createElement("div"),this.element.appendChild(n),this.i=-1),n.style.cssText=t,n.className=e},this.update=function(e){if(!e)return;this.config=e,this.i=0;var t;for(var n in this.markers){var r=this.markers[n];if(!r.range){r.update(t,this,this.session,e);continue}var i=r.range.clipRows(e.firstRow,e.lastRow);if(i.isEmpty())continue;i=i.toScreenRange(this.session);if(r.renderer){var s=this.$getTop(i.start.row,e),o=this.$padding+i.start.column*e.characterWidth;r.renderer(t,i,o,s,e)}else r.type=="fullLine"?this.drawFullLineMarker(t,i,r.clazz,e):r.type=="screenLine"?this.drawScreenLineMarker(t,i,r.clazz,e):i.isMultiLine()?r.type=="text"?this.drawTextMarker(t,i,r.clazz,e):this.drawMultiLineMarker(t,i,r.clazz,e):this.drawSingleLineMarker(t,i,r.clazz+" ace_start"+" ace_br15",e)}if(this.i!=-1)while(this.ip,l==f),s,l==f?0:1,o)},this.drawMultiLineMarker=function(e,t,n,r,i){var s=this.$padding,o=r.lineHeight,u=this.$getTop(t.start.row,r),a=s+t.start.column*r.characterWidth;i=i||"";if(this.session.$bidiHandler.isBidiRow(t.start.row)){var f=t.clone();f.end.row=f.start.row,f.end.column=this.session.getLine(f.start.row).length,this.drawBidiSingleLineMarker(e,f,n+" ace_br1 ace_start",r,null,i)}else this.elt(n+" ace_br1 ace_start","height:"+o+"px;"+"right:0;"+"top:"+u+"px;left:"+a+"px;"+(i||""));if(this.session.$bidiHandler.isBidiRow(t.end.row)){var f=t.clone();f.start.row=f.end.row,f.start.column=0,this.drawBidiSingleLineMarker(e,f,n+" ace_br12",r,null,i)}else{u=this.$getTop(t.end.row,r);var l=t.end.column*r.characterWidth;this.elt(n+" ace_br12","height:"+o+"px;"+"width:"+l+"px;"+"top:"+u+"px;"+"left:"+s+"px;"+(i||""))}o=(t.end.row-t.start.row-1)*r.lineHeight;if(o<=0)return;u=this.$getTop(t.start.row+1,r);var c=(t.start.column?1:0)|(t.end.column?0:8);this.elt(n+(c?" ace_br"+c:""),"height:"+o+"px;"+"right:0;"+"top:"+u+"px;"+"left:"+s+"px;"+(i||""))},this.drawSingleLineMarker=function(e,t,n,r,i,s){if(this.session.$bidiHandler.isBidiRow(t.start.row))return this.drawBidiSingleLineMarker(e,t,n,r,i,s);var o=r.lineHeight,u=(t.end.column+(i||0)-t.start.column)*r.characterWidth,a=this.$getTop(t.start.row,r),f=this.$padding+t.start.column*r.characterWidth;this.elt(n,"height:"+o+"px;"+"width:"+u+"px;"+"top:"+a+"px;"+"left:"+f+"px;"+(s||""))},this.drawBidiSingleLineMarker=function(e,t,n,r,i,s){var o=r.lineHeight,u=this.$getTop(t.start.row,r),a=this.$padding,f=this.session.$bidiHandler.getSelections(t.start.column,t.end.column);f.forEach(function(e){this.elt(n,"height:"+o+"px;"+"width:"+e.width+(i||0)+"px;"+"top:"+u+"px;"+"left:"+(a+e.left)+"px;"+(s||""))},this)},this.drawFullLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;t.start.row!=t.end.row&&(o+=this.$getTop(t.end.row,r)-s),this.elt(n,"height:"+o+"px;"+"top:"+s+"px;"+"left:0;right:0;"+(i||""))},this.drawScreenLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;this.elt(n,"height:"+o+"px;"+"top:"+s+"px;"+"left:0;right:0;"+(i||""))}}).call(s.prototype),t.Marker=s}),ace.define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/layer/lines","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("./lines").Lines,u=e("../lib/event_emitter").EventEmitter,a=function(e){this.dom=i,this.element=this.dom.createElement("div"),this.element.className="ace_layer ace_text-layer",e.appendChild(this.element),this.$updateEolChar=this.$updateEolChar.bind(this),this.$lines=new o(this.element)};(function(){r.implement(this,u),this.EOF_CHAR="\u00b6",this.EOL_CHAR_LF="\u00ac",this.EOL_CHAR_CRLF="\u00a4",this.EOL_CHAR=this.EOL_CHAR_LF,this.TAB_CHAR="\u2014",this.SPACE_CHAR="\u00b7",this.$padding=0,this.MAX_LINE_LENGTH=1e4,this.$updateEolChar=function(){var e=this.session.doc,t=e.getNewLineCharacter()=="\n"&&e.getNewLineMode()!="windows",n=t?this.EOL_CHAR_LF:this.EOL_CHAR_CRLF;if(this.EOL_CHAR!=n)return this.EOL_CHAR=n,!0},this.setPadding=function(e){this.$padding=e,this.element.style.margin="0 "+e+"px"},this.getLineHeight=function(){return this.$fontMetrics.$characterSize.height||0},this.getCharacterWidth=function(){return this.$fontMetrics.$characterSize.width||0},this.$setFontMetrics=function(e){this.$fontMetrics=e,this.$fontMetrics.on("changeCharacterSize",function(e){this._signal("changeCharacterSize",e)}.bind(this)),this.$pollSizeChanges()},this.checkForSizeChanges=function(){this.$fontMetrics.checkForSizeChanges()},this.$pollSizeChanges=function(){return this.$pollSizeChangesTimer=this.$fontMetrics.$pollSizeChanges()},this.setSession=function(e){this.session=e,e&&this.$computeTabString()},this.showInvisibles=!1,this.setShowInvisibles=function(e){return this.showInvisibles==e?!1:(this.showInvisibles=e,this.$computeTabString(),!0)},this.displayIndentGuides=!0,this.setDisplayIndentGuides=function(e){return this.displayIndentGuides==e?!1:(this.displayIndentGuides=e,this.$computeTabString(),!0)},this.$tabStrings=[],this.onChangeTabSize=this.$computeTabString=function(){var e=this.session.getTabSize();this.tabSize=e;var t=this.$tabStrings=[0];for(var n=1;nl&&(u=a.end.row+1,a=this.session.getNextFoldLine(u,a),l=a?a.start.row:Infinity);if(u>i)break;var c=s[o++];if(c){this.dom.removeChildren(c),this.$renderLine(c,u,u==l?a:!1);var h=e.lineHeight*this.session.getRowLength(u)+"px";c.style.height!=h&&(f=!0,c.style.height=h)}u++}if(f)while(o0;i--)this.$lines.shift();if(t.lastRow>e.lastRow)for(var i=this.session.getFoldedRowCount(e.lastRow+1,t.lastRow);i>0;i--)this.$lines.pop();e.firstRowt.lastRow&&this.$lines.push(this.$renderLinesFragment(e,t.lastRow+1,e.lastRow))},this.$renderLinesFragment=function(e,t,n){var r=[],s=t,o=this.session.getNextFoldLine(s),u=o?o.start.row:Infinity;for(;;){s>u&&(s=o.end.row+1,o=this.session.getNextFoldLine(s,o),u=o?o.start.row:Infinity);if(s>n)break;var a=this.$lines.createCell(s,e,this.session),f=a.element;this.dom.removeChildren(f),i.setStyle(f.style,"height",this.$lines.computeLineHeight(s,e,this.session)+"px"),i.setStyle(f.style,"top",this.$lines.computeLineTop(s,e,this.session)+"px"),this.$renderLine(f,s,s==u?o:!1),this.$useLineGroups()?f.className="ace_line_group":f.className="ace_line",r.push(a),s++}return r},this.update=function(e){this.$lines.moveContainer(e),this.config=e;var t=e.firstRow,n=e.lastRow,r=this.$lines;while(r.getLength())r.pop();r.push(this.$renderLinesFragment(e,t,n))},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(e,t,n,r){var i=this,o=/(\t)|( +)|([\x00-\x1f\x80-\xa0\xad\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\uFEFF\uFFF9-\uFFFC]+)|(\u3000)|([\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3001-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]|[\uD800-\uDBFF][\uDC00-\uDFFF])/g,u=this.dom.createFragment(this.element),a,f=0;while(a=o.exec(r)){var l=a[1],c=a[2],h=a[3],p=a[4],d=a[5];if(!i.showInvisibles&&c)continue;var v=f!=a.index?r.slice(f,a.index):"";f=a.index+a[0].length,v&&u.appendChild(this.dom.createTextNode(v,this.element));if(l){var m=i.session.getScreenTabSize(t+a.index);u.appendChild(i.$tabStrings[m].cloneNode(!0)),t+=m-1}else if(c)if(i.showInvisibles){var g=this.dom.createElement("span");g.className="ace_invisible ace_invisible_space",g.textContent=s.stringRepeat(i.SPACE_CHAR,c.length),u.appendChild(g)}else u.appendChild(this.com.createTextNode(c,this.element));else if(h){var g=this.dom.createElement("span");g.className="ace_invisible ace_invisible_space ace_invalid",g.textContent=s.stringRepeat(i.SPACE_CHAR,h.length),u.appendChild(g)}else if(p){var y=i.showInvisibles?i.SPACE_CHAR:"";t+=1;var g=this.dom.createElement("span");g.style.width=i.config.characterWidth*2+"px",g.className=i.showInvisibles?"ace_cjk ace_invisible ace_invisible_space":"ace_cjk",g.textContent=i.showInvisibles?i.SPACE_CHAR:"",u.appendChild(g)}else if(d){t+=1;var g=this.dom.createElement("span");g.style.width=i.config.characterWidth*2+"px",g.className="ace_cjk",g.textContent=d,u.appendChild(g)}}u.appendChild(this.dom.createTextNode(f?r.slice(f):r,this.element));if(!this.$textToken[n.type]){var b="ace_"+n.type.replace(/\./g," ace_"),g=this.dom.createElement("span");n.type=="fold"&&(g.style.width=n.value.length*this.config.characterWidth+"px"),g.className=b,g.appendChild(u),e.appendChild(g)}else e.appendChild(u);return t+r.length},this.renderIndentGuide=function(e,t,n){var r=t.search(this.$indentGuideRe);if(r<=0||r>=n)return t;if(t[0]==" "){r-=r%this.tabSize;var i=r/this.tabSize;for(var s=0;s=o)u=this.$renderToken(a,u,l,c.substring(0,o-r)),c=c.substring(o-r),r=o,a=this.$createLineElement(),e.appendChild(a),a.appendChild(this.dom.createTextNode(s.stringRepeat("\u00a0",n.indent),this.element)),i++,u=0,o=n[i]||Number.MAX_VALUE;c.length!=0&&(r+=c.length,u=this.$renderToken(a,u,l,c))}}},this.$renderSimpleLine=function(e,t){var n=0,r=t[0],i=r.value;this.displayIndentGuides&&(i=this.renderIndentGuide(e,i)),i&&(n=this.$renderToken(e,n,r,i));for(var s=1;sthis.MAX_LINE_LENGTH)return this.$renderOverflowMessage(e,n,r,i);n=this.$renderToken(e,n,r,i)}},this.$renderOverflowMessage=function(e,t,n,r){this.$renderToken(e,t,n,r.slice(0,this.MAX_LINE_LENGTH-t));var i=this.dom.createElement("span");i.className="ace_inline_button ace_keyword ace_toggle_wrap",i.style.position="absolute",i.style.right="0",i.textContent="",e.appendChild(i)},this.$renderLine=function(e,t,n){!n&&n!=0&&(n=this.session.getFoldLine(t));if(n)var r=this.$getFoldLineTokens(t,n);else var r=this.session.getTokens(t);var i=e;if(r.length){var s=this.session.getRowSplitData(t);if(s&&s.length){this.$renderWrappedLine(e,r,s);var i=e.lastChild}else{var i=e;this.$useLineGroups()&&(i=this.$createLineElement(),e.appendChild(i)),this.$renderSimpleLine(i,r)}}else this.$useLineGroups()&&(i=this.$createLineElement(),e.appendChild(i));if(this.showInvisibles&&i){n&&(t=n.end.row);var o=this.dom.createElement("span");o.className="ace_invisible ace_invisible_eol",o.textContent=t==this.session.getLength()-1?this.EOF_CHAR:this.EOL_CHAR,i.appendChild(o)}},this.$getFoldLineTokens=function(e,t){function i(e,t,n){var i=0,s=0;while(s+e[i].value.lengthn-t&&(o=o.substring(0,n-t)),r.push({type:e[i].type,value:o}),s=t+o.length,i+=1}while(sn?r.push({type:e[i].type,value:o.substring(0,n-s)}):r.push(e[i]),s+=o.length,i+=1}}var n=this.session,r=[],s=n.getTokens(e);return t.walk(function(e,t,o,u,a){e!=null?r.push({type:"fold",value:e}):(a&&(s=n.getTokens(t)),s.length&&i(s,u,o))},t.end.row,this.session.getLine(t.end.row).length),r},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){}}).call(a.prototype),t.Text=a}),ace.define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_cursor-layer",e.appendChild(this.element),this.isVisible=!1,this.isBlinking=!0,this.blinkInterval=1e3,this.smoothBlinking=!1,this.cursors=[],this.cursor=this.addCursor(),r.addCssClass(this.element,"ace_hidden-cursors"),this.$updateCursors=this.$updateOpacity.bind(this)};(function(){this.$updateOpacity=function(e){var t=this.cursors;for(var n=t.length;n--;)r.setStyle(t[n].style,"opacity",e?"":"0")},this.$startCssAnimation=function(){var e=this.cursors;for(var t=e.length;t--;)e[t].style.animationDuration=this.blinkInterval+"ms";setTimeout(function(){r.addCssClass(this.element,"ace_animate-blinking")}.bind(this))},this.$stopCssAnimation=function(){r.removeCssClass(this.element,"ace_animate-blinking")},this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setBlinking=function(e){e!=this.isBlinking&&(this.isBlinking=e,this.restartTimer())},this.setBlinkInterval=function(e){e!=this.blinkInterval&&(this.blinkInterval=e,this.restartTimer())},this.setSmoothBlinking=function(e){e!=this.smoothBlinking&&(this.smoothBlinking=e,r.setCssClass(this.element,"ace_smooth-blinking",e),this.$updateCursors(!0),this.restartTimer())},this.addCursor=function(){var e=r.createElement("div");return e.className="ace_cursor",this.element.appendChild(e),this.cursors.push(e),e},this.removeCursor=function(){if(this.cursors.length>1){var e=this.cursors.pop();return e.parentNode.removeChild(e),e}},this.hideCursor=function(){this.isVisible=!1,r.addCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.showCursor=function(){this.isVisible=!0,r.removeCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.restartTimer=function(){var e=this.$updateCursors;clearInterval(this.intervalId),clearTimeout(this.timeoutId),this.$stopCssAnimation(),this.smoothBlinking&&r.removeCssClass(this.element,"ace_smooth-blinking"),e(!0);if(!this.isBlinking||!this.blinkInterval||!this.isVisible){this.$stopCssAnimation();return}this.smoothBlinking&&setTimeout(function(){r.addCssClass(this.element,"ace_smooth-blinking")}.bind(this));if(r.HAS_CSS_ANIMATION)this.$startCssAnimation();else{var t=function(){this.timeoutId=setTimeout(function(){e(!1)},.6*this.blinkInterval)}.bind(this);this.intervalId=setInterval(function(){e(!0),t()},this.blinkInterval),t()}},this.getPixelPosition=function(e,t){if(!this.config||!this.session)return{left:0,top:0};e||(e=this.session.selection.getCursor());var n=this.session.documentToScreenPosition(e),r=this.$padding+(this.session.$bidiHandler.isBidiRow(n.row,e.row)?this.session.$bidiHandler.getPosLeft(n.column):n.column*this.config.characterWidth),i=(n.row-(t?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:r,top:i}},this.isCursorInView=function(e,t){return e.top>=0&&e.tope.height+e.offset||o.top<0)&&n>1)continue;var u=this.cursors[i++]||this.addCursor(),a=u.style;this.drawCursor?this.drawCursor(u,o,e,t[n],this.session):this.isCursorInView(o,e)?(r.setStyle(a,"display","block"),r.translate(u,o.left,o.top),r.setStyle(a,"width",Math.round(e.characterWidth)+"px"),r.setStyle(a,"height",e.lineHeight+"px")):r.setStyle(a,"display","none")}while(this.cursors.length>i)this.removeCursor();var f=this.session.getOverwrite();this.$setOverwrite(f),this.$pixelPos=o,this.restartTimer()},this.drawCursor=null,this.$setOverwrite=function(e){e!=this.overwrite&&(this.overwrite=e,e?r.addCssClass(this.element,"ace_overwrite-cursors"):r.removeCssClass(this.element,"ace_overwrite-cursors"))},this.destroy=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId)}}).call(i.prototype),t.Cursor=i}),ace.define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/event"),o=e("./lib/event_emitter").EventEmitter,u=32768,a=function(e){this.element=i.createElement("div"),this.element.className="ace_scrollbar ace_scrollbar"+this.classSuffix,this.inner=i.createElement("div"),this.inner.className="ace_scrollbar-inner",this.element.appendChild(this.inner),e.appendChild(this.element),this.setVisible(!1),this.skipEvent=!1,s.addListener(this.element,"scroll",this.onScroll.bind(this)),s.addListener(this.element,"mousedown",s.preventDefault)};(function(){r.implement(this,o),this.setVisible=function(e){this.element.style.display=e?"":"none",this.isVisible=e,this.coeff=1}}).call(a.prototype);var f=function(e,t){a.call(this,e),this.scrollTop=0,this.scrollHeight=0,t.$scrollbarWidth=this.width=i.scrollbarWidth(e.ownerDocument),this.inner.style.width=this.element.style.width=(this.width||15)+5+"px",this.$minWidth=0};r.inherits(f,a),function(){this.classSuffix="-v",this.onScroll=function(){if(!this.skipEvent){this.scrollTop=this.element.scrollTop;if(this.coeff!=1){var e=this.element.clientHeight/this.scrollHeight;this.scrollTop=this.scrollTop*(1-e)/(this.coeff-e)}this._emit("scroll",{data:this.scrollTop})}this.skipEvent=!1},this.getWidth=function(){return Math.max(this.isVisible?this.width:0,this.$minWidth||0)},this.setHeight=function(e){this.element.style.height=e+"px"},this.setInnerHeight=this.setScrollHeight=function(e){this.scrollHeight=e,e>u?(this.coeff=u/e,e=u):this.coeff!=1&&(this.coeff=1),this.inner.style.height=e+"px"},this.setScrollTop=function(e){this.scrollTop!=e&&(this.skipEvent=!0,this.scrollTop=e,this.element.scrollTop=e*this.coeff)}}.call(f.prototype);var l=function(e,t){a.call(this,e),this.scrollLeft=0,this.height=t.$scrollbarWidth,this.inner.style.height=this.element.style.height=(this.height||15)+5+"px"};r.inherits(l,a),function(){this.classSuffix="-h",this.onScroll=function(){this.skipEvent||(this.scrollLeft=this.element.scrollLeft,this._emit("scroll",{data:this.scrollLeft})),this.skipEvent=!1},this.getHeight=function(){return this.isVisible?this.height:0},this.setWidth=function(e){this.element.style.width=e+"px"},this.setInnerWidth=function(e){this.inner.style.width=e+"px"},this.setScrollWidth=function(e){this.inner.style.width=e+"px"},this.setScrollLeft=function(e){this.scrollLeft!=e&&(this.skipEvent=!0,this.scrollLeft=this.element.scrollLeft=e)}}.call(l.prototype),t.ScrollBar=f,t.ScrollBarV=f,t.ScrollBarH=l,t.VScrollBar=f,t.HScrollBar=l}),ace.define("ace/renderloop",["require","exports","module","ace/lib/event"],function(e,t,n){"use strict";var r=e("./lib/event"),i=function(e,t){this.onRender=e,this.pending=!1,this.changes=0,this.$recursionLimit=2,this.window=t||window;var n=this;this._flush=function(e){n.pending=!1;var t=n.changes;t&&(r.blockIdle(100),n.changes=0,n.onRender(t));if(n.changes){if(n.$recursionLimit--<0)return;n.schedule()}else n.$recursionLimit=2}};(function(){this.schedule=function(e){this.changes=this.changes|e,this.changes&&!this.pending&&(r.nextFrame(this._flush),this.pending=!0)},this.clear=function(e){var t=this.changes;return this.changes=0,t}}).call(i.prototype),t.RenderLoop=i}),ace.define("ace/layer/font_metrics",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/event","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/event"),u=e("../lib/useragent"),a=e("../lib/event_emitter").EventEmitter,f=256,l=typeof ResizeObserver=="function",c=200,h=t.FontMetrics=function(e){this.el=i.createElement("div"),this.$setMeasureNodeStyles(this.el.style,!0),this.$main=i.createElement("div"),this.$setMeasureNodeStyles(this.$main.style),this.$measureNode=i.createElement("div"),this.$setMeasureNodeStyles(this.$measureNode.style),this.el.appendChild(this.$main),this.el.appendChild(this.$measureNode),e.appendChild(this.el),this.$measureNode.innerHTML=s.stringRepeat("X",f),this.$characterSize={width:0,height:0},l?this.$addObserver():this.checkForSizeChanges()};(function(){r.implement(this,a),this.$characterSize={width:0,height:0},this.$setMeasureNodeStyles=function(e,t){e.width=e.height="auto",e.left=e.top="0px",e.visibility="hidden",e.position="absolute",e.whiteSpace="pre",u.isIE<8?e["font-family"]="inherit":e.font="inherit",e.overflow=t?"hidden":"visible"},this.checkForSizeChanges=function(e){e===undefined&&(e=this.$measureSizes());if(e&&(this.$characterSize.width!==e.width||this.$characterSize.height!==e.height)){this.$measureNode.style.fontWeight="bold";var t=this.$measureSizes();this.$measureNode.style.fontWeight="",this.$characterSize=e,this.charSizes=Object.create(null),this.allowBoldFonts=t&&t.width===e.width&&t.height===e.height,this._emit("changeCharacterSize",{data:e})}},this.$addObserver=function(){var e=this;this.$observer=new window.ResizeObserver(function(t){var n=t[0].contentRect;e.checkForSizeChanges({height:n.height,width:n.width/f})}),this.$observer.observe(this.$measureNode)},this.$pollSizeChanges=function(){if(this.$pollSizeChangesTimer||this.$observer)return this.$pollSizeChangesTimer;var e=this;return this.$pollSizeChangesTimer=o.onIdle(function t(){e.checkForSizeChanges(),o.onIdle(t,500)},500)},this.setPolling=function(e){e?this.$pollSizeChanges():this.$pollSizeChangesTimer&&(clearInterval(this.$pollSizeChangesTimer),this.$pollSizeChangesTimer=0)},this.$measureSizes=function(e){var t={height:(e||this.$measureNode).clientHeight,width:(e||this.$measureNode).clientWidth/f};return t.width===0||t.height===0?null:t},this.$measureCharWidth=function(e){this.$main.innerHTML=s.stringRepeat(e,f);var t=this.$main.getBoundingClientRect();return t.width/f},this.getCharacterWidth=function(e){var t=this.charSizes[e];return t===undefined&&(t=this.charSizes[e]=this.$measureCharWidth(e)/this.$characterSize.width),t},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$observer&&this.$observer.disconnect(),this.el&&this.el.parentNode&&this.el.parentNode.removeChild(this.el)},this.$getZoom=function e(t){return t?(window.getComputedStyle(t).zoom||1)*e(t.parentElement):1},this.$initTransformMeasureNodes=function(){var e=function(e,t){return["div",{style:"position: absolute;top:"+e+"px;left:"+t+"px;"}]};this.els=i.buildDom([e(0,0),e(c,0),e(0,c),e(c,c)],this.el)},this.transformCoordinates=function(e,t){function r(e,t,n){var r=e[1]*t[0]-e[0]*t[1];return[(-t[1]*n[0]+t[0]*n[1])/r,(+e[1]*n[0]-e[0]*n[1])/r]}function i(e,t){return[e[0]-t[0],e[1]-t[1]]}function s(e,t){return[e[0]+t[0],e[1]+t[1]]}function o(e,t){return[e*t[0],e*t[1]]}function u(e){var t=e.getBoundingClientRect();return[t.left,t.top]}if(e){var n=this.$getZoom(this.el);e=o(1/n,e)}this.els||this.$initTransformMeasureNodes();var a=u(this.els[0]),f=u(this.els[1]),l=u(this.els[2]),h=u(this.els[3]),p=r(i(h,f),i(h,l),i(s(f,l),s(h,a))),d=o(1+p[0],i(f,a)),v=o(1+p[1],i(l,a));if(t){var m=t,g=p[0]*m[0]/c+p[1]*m[1]/c+1,y=s(o(m[0],d),o(m[1],v));return s(o(1/g/c,y),a)}var b=i(e,a),w=r(i(d,o(p[0],b)),i(v,o(p[1],b)),b);return o(c,w)}}).call(h.prototype)}),ace.define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/config","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/scrollbar","ace/renderloop","ace/layer/font_metrics","ace/lib/event_emitter","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./config"),o=e("./layer/gutter").Gutter,u=e("./layer/marker").Marker,a=e("./layer/text").Text,f=e("./layer/cursor").Cursor,l=e("./scrollbar").HScrollBar,c=e("./scrollbar").VScrollBar,h=e("./renderloop").RenderLoop,p=e("./layer/font_metrics").FontMetrics,d=e("./lib/event_emitter").EventEmitter,v='.ace_br1 {border-top-left-radius : 3px;}.ace_br2 {border-top-right-radius : 3px;}.ace_br3 {border-top-left-radius : 3px; border-top-right-radius: 3px;}.ace_br4 {border-bottom-right-radius: 3px;}.ace_br5 {border-top-left-radius : 3px; border-bottom-right-radius: 3px;}.ace_br6 {border-top-right-radius : 3px; border-bottom-right-radius: 3px;}.ace_br7 {border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px;}.ace_br8 {border-bottom-left-radius : 3px;}.ace_br9 {border-top-left-radius : 3px; border-bottom-left-radius: 3px;}.ace_br10{border-top-right-radius : 3px; border-bottom-left-radius: 3px;}.ace_br11{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br12{border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br13{border-top-left-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br14{border-top-right-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br15{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_editor {position: relative;overflow: hidden;font: 12px/normal \'Monaco\', \'Menlo\', \'Ubuntu Mono\', \'Consolas\', \'source-code-pro\', monospace;direction: ltr;text-align: left;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}.ace_scroller {position: absolute;overflow: hidden;top: 0;bottom: 0;background-color: inherit;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;cursor: text;}.ace_content {position: absolute;box-sizing: border-box;min-width: 100%;contain: style size layout;}.ace_dragging .ace_scroller:before{position: absolute;top: 0;left: 0;right: 0;bottom: 0;content: \'\';background: rgba(250, 250, 250, 0.01);z-index: 1000;}.ace_dragging.ace_dark .ace_scroller:before{background: rgba(0, 0, 0, 0.01);}.ace_selecting, .ace_selecting * {cursor: text !important;}.ace_gutter {position: absolute;overflow : hidden;width: auto;top: 0;bottom: 0;left: 0;cursor: default;z-index: 4;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;contain: style size layout;}.ace_gutter-active-line {position: absolute;left: 0;right: 0;}.ace_scroller.ace_scroll-left {box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset;}.ace_gutter-cell {position: absolute;top: 0;left: 0;right: 0;padding-left: 19px;padding-right: 6px;background-repeat: no-repeat;}.ace_gutter-cell.ace_error {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: 2px center;}.ace_gutter-cell.ace_warning {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg==");background-position: 2px center;}.ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=");background-position: 2px center;}.ace_dark .ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC");}.ace_scrollbar {contain: strict;position: absolute;right: 0;bottom: 0;z-index: 6;}.ace_scrollbar-inner {position: absolute;cursor: text;left: 0;top: 0;}.ace_scrollbar-v{overflow-x: hidden;overflow-y: scroll;top: 0;}.ace_scrollbar-h {overflow-x: scroll;overflow-y: hidden;left: 0;}.ace_print-margin {position: absolute;height: 100%;}.ace_text-input {position: absolute;z-index: 0;width: 0.5em;height: 1em;opacity: 0;background: transparent;-moz-appearance: none;appearance: none;border: none;resize: none;outline: none;overflow: hidden;font: inherit;padding: 0 1px;margin: 0 -1px;contain: strict;-ms-user-select: text;-moz-user-select: text;-webkit-user-select: text;user-select: text;white-space: pre!important;}.ace_text-input.ace_composition {background: transparent;color: inherit;z-index: 1000;opacity: 1;}.ace_composition_placeholder { color: transparent }.ace_composition_marker { border-bottom: 1px solid;position: absolute;border-radius: 0;margin-top: 1px;}[ace_nocontext=true] {transform: none!important;filter: none!important;perspective: none!important;clip-path: none!important;mask : none!important;contain: none!important;perspective: none!important;mix-blend-mode: initial!important;z-index: auto;}.ace_layer {z-index: 1;position: absolute;overflow: hidden;word-wrap: normal;white-space: pre;height: 100%;width: 100%;box-sizing: border-box;pointer-events: none;}.ace_gutter-layer {position: relative;width: auto;text-align: right;pointer-events: auto;height: 1000000px;contain: style size layout;}.ace_text-layer {font: inherit !important;position: absolute;height: 1000000px;width: 1000000px;contain: style size layout;}.ace_text-layer > .ace_line, .ace_text-layer > .ace_line_group {contain: style size layout;position: absolute;top: 0;left: 0;right: 0;}.ace_hidpi .ace_text-layer,.ace_hidpi .ace_gutter-layer,.ace_hidpi .ace_content,.ace_hidpi .ace_gutter {contain: strict;will-change: transform;}.ace_hidpi .ace_text-layer > .ace_line, .ace_hidpi .ace_text-layer > .ace_line_group {contain: strict;}.ace_cjk {display: inline-block;text-align: center;}.ace_cursor-layer {z-index: 4;}.ace_cursor {z-index: 4;position: absolute;box-sizing: border-box;border-left: 2px solid;transform: translatez(0);}.ace_multiselect .ace_cursor {border-left-width: 1px;}.ace_slim-cursors .ace_cursor {border-left-width: 1px;}.ace_overwrite-cursors .ace_cursor {border-left-width: 0;border-bottom: 1px solid;}.ace_hidden-cursors .ace_cursor {opacity: 0.2;}.ace_smooth-blinking .ace_cursor {transition: opacity 0.18s;}.ace_animate-blinking .ace_cursor {animation-duration: 1000ms;animation-timing-function: step-end;animation-name: blink-ace-animate;animation-iteration-count: infinite;}.ace_animate-blinking.ace_smooth-blinking .ace_cursor {animation-duration: 1000ms;animation-timing-function: ease-in-out;animation-name: blink-ace-animate-smooth;}@keyframes blink-ace-animate {from, to { opacity: 1; }60% { opacity: 0; }}@keyframes blink-ace-animate-smooth {from, to { opacity: 1; }45% { opacity: 1; }60% { opacity: 0; }85% { opacity: 0; }}.ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {position: absolute;z-index: 3;}.ace_marker-layer .ace_selection {position: absolute;z-index: 5;}.ace_marker-layer .ace_bracket {position: absolute;z-index: 6;}.ace_marker-layer .ace_active-line {position: absolute;z-index: 2;}.ace_marker-layer .ace_selected-word {position: absolute;z-index: 4;box-sizing: border-box;}.ace_line .ace_fold {box-sizing: border-box;display: inline-block;height: 11px;margin-top: -2px;vertical-align: middle;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=");background-repeat: no-repeat, repeat-x;background-position: center center, top left;color: transparent;border: 1px solid black;border-radius: 2px;cursor: pointer;pointer-events: auto;}.ace_dark .ace_fold {}.ace_fold:hover{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC");}.ace_tooltip {background-color: #FFF;background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1));border: 1px solid gray;border-radius: 1px;box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);color: black;max-width: 100%;padding: 3px 4px;position: fixed;z-index: 999999;box-sizing: border-box;cursor: default;white-space: pre;word-wrap: break-word;line-height: normal;font-style: normal;font-weight: normal;letter-spacing: normal;pointer-events: none;}.ace_folding-enabled > .ace_gutter-cell {padding-right: 13px;}.ace_fold-widget {box-sizing: border-box;margin: 0 -12px 0 1px;display: none;width: 11px;vertical-align: top;background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: center;border-radius: 3px;border: 1px solid transparent;cursor: pointer;}.ace_folding-enabled .ace_fold-widget {display: inline-block; }.ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==");}.ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==");}.ace_fold-widget:hover {border: 1px solid rgba(0, 0, 0, 0.3);background-color: rgba(255, 255, 255, 0.2);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);}.ace_fold-widget:active {border: 1px solid rgba(0, 0, 0, 0.4);background-color: rgba(0, 0, 0, 0.05);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);}.ace_dark .ace_fold-widget {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC");}.ace_dark .ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget:hover {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);background-color: rgba(255, 255, 255, 0.1);}.ace_dark .ace_fold-widget:active {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);}.ace_inline_button {border: 1px solid lightgray;display: inline-block;margin: -1px 8px;padding: 0 5px;pointer-events: auto;cursor: pointer;}.ace_inline_button:hover {border-color: gray;background: rgba(200,200,200,0.2);display: inline-block;pointer-events: auto;}.ace_fold-widget.ace_invalid {background-color: #FFB4B4;border-color: #DE5555;}.ace_fade-fold-widgets .ace_fold-widget {transition: opacity 0.4s ease 0.05s;opacity: 0;}.ace_fade-fold-widgets:hover .ace_fold-widget {transition: opacity 0.05s ease 0.05s;opacity:1;}.ace_underline {text-decoration: underline;}.ace_bold {font-weight: bold;}.ace_nobold .ace_bold {font-weight: normal;}.ace_italic {font-style: italic;}.ace_error-marker {background-color: rgba(255, 0, 0,0.2);position: absolute;z-index: 9;}.ace_highlight-marker {background-color: rgba(255, 255, 0,0.2);position: absolute;z-index: 8;}',m=e("./lib/useragent"),g=m.isIE;i.importCssString(v,"ace_editor.css");var y=function(e,t){var n=this;this.container=e||i.createElement("div"),i.addCssClass(this.container,"ace_editor"),i.HI_DPI&&i.addCssClass(this.container,"ace_hidpi"),this.setTheme(t),this.$gutter=i.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.$gutter.setAttribute("aria-hidden",!0),this.scroller=i.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=i.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new o(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onGutterResize.bind(this)),this.$markerBack=new u(this.content);var r=this.$textLayer=new a(this.content);this.canvas=r.element,this.$markerFront=new u(this.content),this.$cursorLayer=new f(this.content),this.$horizScroll=!1,this.$vScroll=!1,this.scrollBar=this.scrollBarV=new c(this.container,this),this.scrollBarH=new l(this.container,this),this.scrollBarV.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollTop(e.data-n.scrollMargin.top)}),this.scrollBarH.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollLeft(e.data-n.scrollMargin.left)}),this.scrollTop=0,this.scrollLeft=0,this.cursorPos={row:0,column:0},this.$fontMetrics=new p(this.container),this.$textLayer.$setFontMetrics(this.$fontMetrics),this.$textLayer.addEventListener("changeCharacterSize",function(e){n.updateCharacterSize(),n.onResize(!0,n.gutterWidth,n.$size.width,n.$size.height),n._signal("changeCharacterSize",e)}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0,$dirty:!0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:0,characterWidth:0,minHeight:1,maxHeight:1,offset:0,height:1,gutterOffset:1},this.scrollMargin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.margin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.$keepTextAreaAtCursor=!m.isIOS,this.$loop=new h(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.updateCharacterSize(),this.setPadding(4),s.resetOptions(this),s._emit("renderer",this)};(function(){this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,this.CHANGE_H_SCROLL=1024,r.implement(this,d),this.updateCharacterSize=function(){this.$textLayer.allowBoldFonts!=this.$allowBoldFonts&&(this.$allowBoldFonts=this.$textLayer.allowBoldFonts,this.setStyle("ace_nobold",!this.$allowBoldFonts)),this.layerConfig.characterWidth=this.characterWidth=this.$textLayer.getCharacterWidth(),this.layerConfig.lineHeight=this.lineHeight=this.$textLayer.getLineHeight(),this.$updatePrintMargin()},this.setSession=function(e){this.session&&this.session.doc.off("changeNewLineMode",this.onChangeNewLineMode),this.session=e,e&&this.scrollMargin.top&&e.getScrollTop()<=0&&e.setScrollTop(-this.scrollMargin.top),this.$cursorLayer.setSession(e),this.$markerBack.setSession(e),this.$markerFront.setSession(e),this.$gutterLayer.setSession(e),this.$textLayer.setSession(e);if(!e)return;this.$loop.schedule(this.CHANGE_FULL),this.session.$setFontMetrics(this.$fontMetrics),this.scrollBarH.scrollLeft=this.scrollBarV.scrollTop=null,this.onChangeNewLineMode=this.onChangeNewLineMode.bind(this),this.onChangeNewLineMode(),this.session.doc.on("changeNewLineMode",this.onChangeNewLineMode)},this.updateLines=function(e,t,n){t===undefined&&(t=Infinity),this.$changedLines?(this.$changedLines.firstRow>e&&(this.$changedLines.firstRow=e),this.$changedLines.lastRowthis.layerConfig.lastRow)return;this.$loop.schedule(this.CHANGE_LINES)},this.onChangeNewLineMode=function(){this.$loop.schedule(this.CHANGE_TEXT),this.$textLayer.$updateEolChar(),this.session.$bidiHandler.setEolChar(this.$textLayer.EOL_CHAR)},this.onChangeTabSize=function(){this.$loop.schedule(this.CHANGE_TEXT|this.CHANGE_MARKER),this.$textLayer.onChangeTabSize()},this.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},this.updateFull=function(e){e?this.$renderChanges(this.CHANGE_FULL,!0):this.$loop.schedule(this.CHANGE_FULL)},this.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},this.$changes=0,this.$updateSizeAsync=function(){this.$loop.pending?this.$size.$dirty=!0:this.onResize()},this.onResize=function(e,t,n,r){if(this.resizing>2)return;this.resizing>0?this.resizing++:this.resizing=e?1:0;var i=this.container;r||(r=i.clientHeight||i.scrollHeight),n||(n=i.clientWidth||i.scrollWidth);var s=this.$updateCachedSize(e,t,n,r);if(!this.$size.scrollerHeight||!n&&!r)return this.resizing=0;e&&(this.$gutterLayer.$padding=null),e?this.$renderChanges(s|this.$changes,!0):this.$loop.schedule(s|this.$changes),this.resizing&&(this.resizing=0),this.scrollBarV.scrollLeft=this.scrollBarV.scrollTop=null},this.$updateCachedSize=function(e,t,n,r){r-=this.$extraHeight||0;var s=0,o=this.$size,u={width:o.width,height:o.height,scrollerHeight:o.scrollerHeight,scrollerWidth:o.scrollerWidth};r&&(e||o.height!=r)&&(o.height=r,s|=this.CHANGE_SIZE,o.scrollerHeight=o.height,this.$horizScroll&&(o.scrollerHeight-=this.scrollBarH.getHeight()),this.scrollBarV.element.style.bottom=this.scrollBarH.getHeight()+"px",s|=this.CHANGE_SCROLL);if(n&&(e||o.width!=n)){s|=this.CHANGE_SIZE,o.width=n,t==null&&(t=this.$showGutter?this.$gutter.offsetWidth:0),this.gutterWidth=t,i.setStyle(this.scrollBarH.element.style,"left",t+"px"),i.setStyle(this.scroller.style,"left",t+this.margin.left+"px"),o.scrollerWidth=Math.max(0,n-t-this.scrollBarV.getWidth()-this.margin.h),i.setStyle(this.$gutter.style,"left",this.margin.left+"px");var a=this.scrollBarV.getWidth()+"px";i.setStyle(this.scrollBarH.element.style,"right",a),i.setStyle(this.scroller.style,"right",a),i.setStyle(this.scroller.style,"bottom",this.scrollBarH.getHeight());if(this.session&&this.session.getUseWrapMode()&&this.adjustWrapLimit()||e)s|=this.CHANGE_FULL}return o.$dirty=!n||!r,s&&this._signal("resize",u),s},this.onGutterResize=function(e){var t=this.$showGutter?e:0;t!=this.gutterWidth&&(this.$changes|=this.$updateCachedSize(!0,t,this.$size.width,this.$size.height)),this.session.getUseWrapMode()&&this.adjustWrapLimit()?this.$loop.schedule(this.CHANGE_FULL):this.$size.$dirty?this.$loop.schedule(this.CHANGE_FULL):this.$computeLayerConfig()},this.adjustWrapLimit=function(){var e=this.$size.scrollerWidth-this.$padding*2,t=Math.floor(e/this.characterWidth);return this.session.adjustWrapLimit(t,this.$showPrintMargin&&this.$printMarginColumn)},this.setAnimatedScroll=function(e){this.setOption("animatedScroll",e)},this.getAnimatedScroll=function(){return this.$animatedScroll},this.setShowInvisibles=function(e){this.setOption("showInvisibles",e),this.session.$bidiHandler.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.getOption("showInvisibles")},this.getDisplayIndentGuides=function(){return this.getOption("displayIndentGuides")},this.setDisplayIndentGuides=function(e){this.setOption("displayIndentGuides",e)},this.setShowPrintMargin=function(e){this.setOption("showPrintMargin",e)},this.getShowPrintMargin=function(){return this.getOption("showPrintMargin")},this.setPrintMarginColumn=function(e){this.setOption("printMarginColumn",e)},this.getPrintMarginColumn=function(){return this.getOption("printMarginColumn")},this.getShowGutter=function(){return this.getOption("showGutter")},this.setShowGutter=function(e){return this.setOption("showGutter",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.$updatePrintMargin=function(){if(!this.$showPrintMargin&&!this.$printMarginEl)return;if(!this.$printMarginEl){var e=i.createElement("div");e.className="ace_layer ace_print-margin-layer",this.$printMarginEl=i.createElement("div"),this.$printMarginEl.className="ace_print-margin",e.appendChild(this.$printMarginEl),this.content.insertBefore(e,this.content.firstChild)}var t=this.$printMarginEl.style;t.left=Math.round(this.characterWidth*this.$printMarginColumn+this.$padding)+"px",t.visibility=this.$showPrintMargin?"visible":"hidden",this.session&&this.session.$wrap==-1&&this.adjustWrapLimit()},this.getContainerElement=function(){return this.container},this.getMouseEventTarget=function(){return this.scroller},this.getTextAreaContainer=function(){return this.container},this.$moveTextAreaToCursor=function(){var e=this.textarea.style;if(!this.$keepTextAreaAtCursor){i.translate(this.textarea,-100,0);return}var t=this.$cursorLayer.$pixelPos;if(!t)return;var n=this.$composition;n&&n.markerRange&&(t=this.$cursorLayer.getPixelPosition(n.markerRange.start,!0));var r=this.layerConfig,s=t.top,o=t.left;s-=r.offset;var u=n&&n.useTextareaForIME?this.lineHeight:g?0:1;if(s<0||s>r.height-u){i.translate(this.textarea,0,0);return}var a=1;if(!n)s+=this.lineHeight;else if(n.useTextareaForIME){var f=this.textarea.value;a=this.characterWidth*this.session.$getStringScreenWidth(f)[0],u+=2}else s+=this.lineHeight+2;o-=this.scrollLeft,o>this.$size.scrollerWidth-a&&(o=this.$size.scrollerWidth-a),o+=this.gutterWidth+this.margin.left,i.setStyle(e,"height",u+"px"),i.setStyle(e,"width",a+"px"),i.translate(this.textarea,Math.min(o,this.$size.scrollerWidth-a),Math.min(s,this.$size.height-u))},this.getFirstVisibleRow=function(){return this.layerConfig.firstRow},this.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(this.layerConfig.offset===0?0:1)},this.getLastFullyVisibleRow=function(){var e=this.layerConfig,t=e.lastRow,n=this.session.documentToScreenRow(t,0)*e.lineHeight;return n-this.session.getScrollTop()>e.height-e.lineHeight?t-1:t},this.getLastVisibleRow=function(){return this.layerConfig.lastRow},this.$padding=null,this.setPadding=function(e){this.$padding=e,this.$textLayer.setPadding(e),this.$cursorLayer.setPadding(e),this.$markerFront.setPadding(e),this.$markerBack.setPadding(e),this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},this.setScrollMargin=function(e,t,n,r){var i=this.scrollMargin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,i.top&&this.scrollTop<=0&&this.session&&this.session.setScrollTop(-i.top),this.updateFull()},this.setMargin=function(e,t,n,r){var i=this.margin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,this.$updateCachedSize(!0,this.gutterWidth,this.$size.width,this.$size.height),this.updateFull()},this.getHScrollBarAlwaysVisible=function(){return this.$hScrollBarAlwaysVisible},this.setHScrollBarAlwaysVisible=function(e){this.setOption("hScrollBarAlwaysVisible",e)},this.getVScrollBarAlwaysVisible=function(){return this.$vScrollBarAlwaysVisible},this.setVScrollBarAlwaysVisible=function(e){this.setOption("vScrollBarAlwaysVisible",e)},this.$updateScrollBarV=function(){var e=this.layerConfig.maxHeight,t=this.$size.scrollerHeight;!this.$maxLines&&this.$scrollPastEnd&&(e-=(t-this.lineHeight)*this.$scrollPastEnd,this.scrollTop>e-t&&(e=this.scrollTop+t,this.scrollBarV.scrollTop=null)),this.scrollBarV.setScrollHeight(e+this.scrollMargin.v),this.scrollBarV.setScrollTop(this.scrollTop+this.scrollMargin.top)},this.$updateScrollBarH=function(){this.scrollBarH.setScrollWidth(this.layerConfig.width+2*this.$padding+this.scrollMargin.h),this.scrollBarH.setScrollLeft(this.scrollLeft+this.scrollMargin.left)},this.$frozen=!1,this.freeze=function(){this.$frozen=!0},this.unfreeze=function(){this.$frozen=!1},this.$renderChanges=function(e,t){this.$changes&&(e|=this.$changes,this.$changes=0);if(!this.session||!this.container.offsetWidth||this.$frozen||!e&&!t){this.$changes|=e;return}if(this.$size.$dirty)return this.$changes|=e,this.onResize(!0);this.lineHeight||this.$textLayer.checkForSizeChanges(),this._signal("beforeRender"),this.session&&this.session.$bidiHandler&&this.session.$bidiHandler.updateCharacterWidths(this.$fontMetrics);var n=this.layerConfig;if(e&this.CHANGE_FULL||e&this.CHANGE_SIZE||e&this.CHANGE_TEXT||e&this.CHANGE_LINES||e&this.CHANGE_SCROLL||e&this.CHANGE_H_SCROLL){e|=this.$computeLayerConfig()|this.$loop.clear();if(n.firstRow!=this.layerConfig.firstRow&&n.firstRowScreen==this.layerConfig.firstRowScreen){var r=this.scrollTop+(n.firstRow-this.layerConfig.firstRow)*this.lineHeight;r>0&&(this.scrollTop=r,e|=this.CHANGE_SCROLL,e|=this.$computeLayerConfig()|this.$loop.clear())}n=this.layerConfig,this.$updateScrollBarV(),e&this.CHANGE_H_SCROLL&&this.$updateScrollBarH(),i.translate(this.content,-this.scrollLeft,-n.offset);var s=n.width+2*this.$padding+"px",o=n.minHeight+"px";i.setStyle(this.content.style,"width",s),i.setStyle(this.content.style,"height",o)}e&this.CHANGE_H_SCROLL&&(i.translate(this.content,-this.scrollLeft,-n.offset),this.scroller.className=this.scrollLeft<=0?"ace_scroller":"ace_scroller ace_scroll-left");if(e&this.CHANGE_FULL){this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this._signal("afterRender");return}if(e&this.CHANGE_SCROLL){e&this.CHANGE_TEXT||e&this.CHANGE_LINES?this.$textLayer.update(n):this.$textLayer.scrollLines(n),this.$showGutter&&(e&this.CHANGE_GUTTER||e&this.CHANGE_LINES?this.$gutterLayer.update(n):this.$gutterLayer.scrollLines(n)),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this._signal("afterRender");return}e&this.CHANGE_TEXT?(this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n)):e&this.CHANGE_LINES?(this.$updateLines()||e&this.CHANGE_GUTTER&&this.$showGutter)&&this.$gutterLayer.update(n):e&this.CHANGE_TEXT||e&this.CHANGE_GUTTER?this.$showGutter&&this.$gutterLayer.update(n):e&this.CHANGE_CURSOR&&this.$highlightGutterLine&&this.$gutterLayer.updateLineHighlight(n),e&this.CHANGE_CURSOR&&(this.$cursorLayer.update(n),this.$moveTextAreaToCursor()),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(n),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(n),this._signal("afterRender")},this.$autosize=function(){var e=this.session.getScreenLength()*this.lineHeight,t=this.$maxLines*this.lineHeight,n=Math.min(t,Math.max((this.$minLines||1)*this.lineHeight,e))+this.scrollMargin.v+(this.$extraHeight||0);this.$horizScroll&&(n+=this.scrollBarH.getHeight()),this.$maxPixelHeight&&n>this.$maxPixelHeight&&(n=this.$maxPixelHeight);var r=n<=2*this.lineHeight,i=!r&&e>t;if(n!=this.desiredHeight||this.$size.height!=this.desiredHeight||i!=this.$vScroll){i!=this.$vScroll&&(this.$vScroll=i,this.scrollBarV.setVisible(i));var s=this.container.clientWidth;this.container.style.height=n+"px",this.$updateCachedSize(!0,this.$gutterWidth,s,n),this.desiredHeight=n,this._signal("autosize")}},this.$computeLayerConfig=function(){var e=this.session,t=this.$size,n=t.height<=2*this.lineHeight,r=this.session.getScreenLength(),i=r*this.lineHeight,s=this.$getLongestLine(),o=!n&&(this.$hScrollBarAlwaysVisible||t.scrollerWidth-s-2*this.$padding<0),u=this.$horizScroll!==o;u&&(this.$horizScroll=o,this.scrollBarH.setVisible(o));var a=this.$vScroll;this.$maxLines&&this.lineHeight>1&&this.$autosize();var f=t.scrollerHeight+this.lineHeight,l=!this.$maxLines&&this.$scrollPastEnd?(t.scrollerHeight-this.lineHeight)*this.$scrollPastEnd:0;i+=l;var c=this.scrollMargin;this.session.setScrollTop(Math.max(-c.top,Math.min(this.scrollTop,i-t.scrollerHeight+c.bottom))),this.session.setScrollLeft(Math.max(-c.left,Math.min(this.scrollLeft,s+2*this.$padding-t.scrollerWidth+c.right)));var h=!n&&(this.$vScrollBarAlwaysVisible||t.scrollerHeight-i+l<0||this.scrollTop>c.top),p=a!==h;p&&(this.$vScroll=h,this.scrollBarV.setVisible(h));var d=this.scrollTop%this.lineHeight,v=Math.ceil(f/this.lineHeight)-1,m=Math.max(0,Math.round((this.scrollTop-d)/this.lineHeight)),g=m+v,y,b,w=this.lineHeight;m=e.screenToDocumentRow(m,0);var E=e.getFoldLine(m);E&&(m=E.start.row),y=e.documentToScreenRow(m,0),b=e.getRowLength(m)*w,g=Math.min(e.screenToDocumentRow(g,0),e.getLength()-1),f=t.scrollerHeight+e.getRowLength(g)*w+b,d=this.scrollTop-y*w;var S=0;if(this.layerConfig.width!=s||u)S=this.CHANGE_H_SCROLL;if(u||p)S|=this.$updateCachedSize(!0,this.gutterWidth,t.width,t.height),this._signal("scrollbarVisibilityChanged"),p&&(s=this.$getLongestLine());return this.layerConfig={width:s,padding:this.$padding,firstRow:m,firstRowScreen:y,lastRow:g,lineHeight:w,characterWidth:this.characterWidth,minHeight:f,maxHeight:i,offset:d,gutterOffset:w?Math.max(0,Math.ceil((d+t.height-t.scrollerHeight)/w)):0,height:this.$size.scrollerHeight},this.session.$bidiHandler&&this.session.$bidiHandler.setContentWidth(s-this.$padding),S},this.$updateLines=function(){if(!this.$changedLines)return;var e=this.$changedLines.firstRow,t=this.$changedLines.lastRow;this.$changedLines=null;var n=this.layerConfig;if(e>n.lastRow+1)return;if(tthis.$textLayer.MAX_LINE_LENGTH&&(e=this.$textLayer.MAX_LINE_LENGTH+30),Math.max(this.$size.scrollerWidth-2*this.$padding,Math.round(e*this.characterWidth))},this.updateFrontMarkers=function(){this.$markerFront.setMarkers(this.session.getMarkers(!0)),this.$loop.schedule(this.CHANGE_MARKER_FRONT)},this.updateBackMarkers=function(){this.$markerBack.setMarkers(this.session.getMarkers()),this.$loop.schedule(this.CHANGE_MARKER_BACK)},this.addGutterDecoration=function(e,t){this.$gutterLayer.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){this.$gutterLayer.removeGutterDecoration(e,t)},this.updateBreakpoints=function(e){this.$loop.schedule(this.CHANGE_GUTTER)},this.setAnnotations=function(e){this.$gutterLayer.setAnnotations(e),this.$loop.schedule(this.CHANGE_GUTTER)},this.updateCursor=function(){this.$loop.schedule(this.CHANGE_CURSOR)},this.hideCursor=function(){this.$cursorLayer.hideCursor()},this.showCursor=function(){this.$cursorLayer.showCursor()},this.scrollSelectionIntoView=function(e,t,n){this.scrollCursorIntoView(e,n),this.scrollCursorIntoView(t,n)},this.scrollCursorIntoView=function(e,t,n){if(this.$size.scrollerHeight===0)return;var r=this.$cursorLayer.getPixelPosition(e),i=r.left,s=r.top,o=n&&n.top||0,u=n&&n.bottom||0,a=this.$scrollAnimation?this.session.getScrollTop():this.scrollTop;a+o>s?(t&&a+o>s+this.lineHeight&&(s-=t*this.$size.scrollerHeight),s===0&&(s=-this.scrollMargin.top),this.session.setScrollTop(s)):a+this.$size.scrollerHeight-ui?(i=1-this.scrollMargin.top)return!0;if(t>0&&this.session.getScrollTop()+this.$size.scrollerHeight-this.layerConfig.maxHeight<-1+this.scrollMargin.bottom)return!0;if(e<0&&this.session.getScrollLeft()>=1-this.scrollMargin.left)return!0;if(e>0&&this.session.getScrollLeft()+this.$size.scrollerWidth-this.layerConfig.width<-1+this.scrollMargin.right)return!0},this.pixelToScreenCoordinates=function(e,t){var n;if(this.$hasCssTransforms){n={top:0,left:0};var r=this.$fontMetrics.transformCoordinates([e,t]);e=r[1]-this.gutterWidth-this.margin.left,t=r[0]}else n=this.scroller.getBoundingClientRect();var i=e+this.scrollLeft-n.left-this.$padding,s=i/this.characterWidth,o=Math.floor((t+this.scrollTop-n.top)/this.lineHeight),u=this.$blockCursor?Math.floor(s):Math.round(s);return{row:o,column:u,side:s-u>0?1:-1,offsetX:i}},this.screenToTextCoordinates=function(e,t){var n;if(this.$hasCssTransforms){n={top:0,left:0};var r=this.$fontMetrics.transformCoordinates([e,t]);e=r[1]-this.gutterWidth-this.margin.left,t=r[0]}else n=this.scroller.getBoundingClientRect();var i=e+this.scrollLeft-n.left-this.$padding,s=i/this.characterWidth,o=this.$blockCursor?Math.floor(s):Math.round(s),u=Math.floor((t+this.scrollTop-n.top)/this.lineHeight);return this.session.screenToDocumentPosition(u,Math.max(o,0),i)},this.textToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=this.session.documentToScreenPosition(e,t),i=this.$padding+(this.session.$bidiHandler.isBidiRow(r.row,e)?this.session.$bidiHandler.getPosLeft(r.column):Math.round(r.column*this.characterWidth)),s=r.row*this.lineHeight;return{pageX:n.left+i-this.scrollLeft,pageY:n.top+s-this.scrollTop}},this.visualizeFocus=function(){i.addCssClass(this.container,"ace_focus")},this.visualizeBlur=function(){i.removeCssClass(this.container,"ace_focus")},this.showComposition=function(e){this.$composition=e,e.cssText||(e.cssText=this.textarea.style.cssText,e.keepTextAreaAtCursor=this.$keepTextAreaAtCursor),e.useTextareaForIME=this.$useTextareaForIME,this.$useTextareaForIME?(this.$keepTextAreaAtCursor=!0,i.addCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText="",this.$moveTextAreaToCursor(),this.$cursorLayer.element.style.display="none"):e.markerId=this.session.addMarker(e.markerRange,"ace_composition_marker","text")},this.setCompositionText=function(e){var t=this.session.selection.cursor;this.addToken(e,"composition_placeholder",t.row,t.column),this.$moveTextAreaToCursor()},this.hideComposition=function(){if(!this.$composition)return;this.$composition.markerId&&this.session.removeMarker(this.$composition.markerId),i.removeCssClass(this.textarea,"ace_composition"),this.$keepTextAreaAtCursor=this.$composition.keepTextAreaAtCursor,this.textarea.style.cssText=this.$composition.cssText,this.$composition=null,this.$cursorLayer.element.style.display=""},this.addToken=function(e,t,n,r){var i=this.session;i.bgTokenizer.lines[n]=null;var s={type:t,value:e},o=i.getTokens(n);if(r==null)o.push(s);else{var u=0;for(var a=0;a50&&e.length>this.$doc.getLength()>>1?this.call("setValue",[this.$doc.getValue()]):this.emit("change",{data:e})}}).call(f.prototype);var l=function(e,t,n){var r=null,i=!1,u=Object.create(s),a=[],l=new f({messageBuffer:a,terminate:function(){},postMessage:function(e){a.push(e);if(!r)return;i?setTimeout(c):c()}});l.setEmitSync=function(e){i=e};var c=function(){var e=a.shift();e.command?r[e.command].apply(r,e.args):e.event&&u._signal(e.event,e.data)};return u.postMessage=function(e){l.onMessage({data:e})},u.callback=function(e,t){this.postMessage({type:"call",id:t,data:e})},u.emit=function(e,t){this.postMessage({type:"event",name:e,data:t})},o.loadModule(["worker",t],function(e){r=new e[n](u);while(a.length)c()}),l};t.UIWorkerClient=l,t.WorkerClient=f,t.createWorker=a}),ace.define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(e,t,n){"use strict";var r=e("./range").Range,i=e("./lib/event_emitter").EventEmitter,s=e("./lib/oop"),o=function(e,t,n,r,i,s){var o=this;this.length=t,this.session=e,this.doc=e.getDocument(),this.mainClass=i,this.othersClass=s,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate),this.$others=r,this.$onCursorChange=function(){setTimeout(function(){o.onCursorChange()})},this.$pos=n;var u=e.getUndoManager().$undoStack||e.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=u.length,this.setup(),e.selection.on("changeCursor",this.$onCursorChange)};(function(){s.implement(this,i),this.setup=function(){var e=this,t=this.doc,n=this.session;this.selectionBefore=n.selection.toJSON(),n.selection.inMultiSelectMode&&n.selection.toSingleRange(),this.pos=t.createAnchor(this.$pos.row,this.$pos.column);var i=this.pos;i.$insertRight=!0,i.detach(),i.markerId=n.addMarker(new r(i.row,i.column,i.row,i.column+this.length),this.mainClass,null,!1),this.others=[],this.$others.forEach(function(n){var r=t.createAnchor(n.row,n.column);r.$insertRight=!0,r.detach(),e.others.push(r)}),n.setUndoSelect(!1)},this.showOtherMarkers=function(){if(this.othersActive)return;var e=this.session,t=this;this.othersActive=!0,this.others.forEach(function(n){n.markerId=e.addMarker(new r(n.row,n.column,n.row,n.column+t.length),t.othersClass,null,!1)})},this.hideOtherMarkers=function(){if(!this.othersActive)return;this.othersActive=!1;for(var e=0;e=this.pos.column&&t.start.column<=this.pos.column+this.length+1,s=t.start.column-this.pos.column;this.updateAnchors(e),i&&(this.length+=n);if(i&&!this.session.$fromUndo)if(e.action==="insert")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.insertMergedLines(a,e.lines)}else if(e.action==="remove")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.remove(new r(a.row,a.column,a.row,a.column-n))}this.$updating=!1,this.updateMarkers()},this.updateAnchors=function(e){this.pos.onChange(e);for(var t=this.others.length;t--;)this.others[t].onChange(e);this.updateMarkers()},this.updateMarkers=function(){if(this.$updating)return;var e=this,t=this.session,n=function(n,i){t.removeMarker(n.markerId),n.markerId=t.addMarker(new r(n.row,n.column,n.row,n.column+e.length),i,null,!1)};n(this.pos,this.mainClass);for(var i=this.others.length;i--;)n(this.others[i],this.othersClass)},this.onCursorChange=function(e){if(this.$updating||!this.session)return;var t=this.session.selection.getCursor();t.row===this.pos.row&&t.column>=this.pos.column&&t.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",e)):(this.hideOtherMarkers(),this._emit("cursorLeave",e))},this.detach=function(){this.session.removeMarker(this.pos&&this.pos.markerId),this.hideOtherMarkers(),this.doc.removeEventListener("change",this.$onUpdate),this.session.selection.removeEventListener("changeCursor",this.$onCursorChange),this.session.setUndoSelect(!0),this.session=null},this.cancel=function(){if(this.$undoStackDepth===-1)return;var e=this.session.getUndoManager(),t=(e.$undoStack||e.$undostack).length-this.$undoStackDepth;for(var n=0;n1&&!this.inMultiSelectMode&&(this._signal("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session)),t||this.fromOrientedRange(e)},this.toSingleRange=function(e){e=e||this.ranges[0];var t=this.rangeList.removeAll();t.length&&this.$onRemoveRange(t),e&&this.fromOrientedRange(e)},this.substractPoint=function(e){var t=this.rangeList.substractPoint(e);if(t)return this.$onRemoveRange(t),t[0]},this.mergeOverlappingRanges=function(){var e=this.rangeList.merge();e.length&&this.$onRemoveRange(e)},this.$onAddRange=function(e){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(e),this._signal("addRange",{range:e})},this.$onRemoveRange=function(e){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var t=this.rangeList.ranges.pop();e.push(t),this.rangeCount=0}for(var n=e.length;n--;){var r=this.ranges.indexOf(e[n]);this.ranges.splice(r,1)}this._signal("removeRange",{ranges:e}),this.rangeCount===0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._signal("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),t=t||this.ranges[0],t&&!t.isEqual(this.getRange())&&this.fromOrientedRange(t)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new r,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeCount?this.rangeList.ranges.concat():[this.getRange()]},this.splitIntoLines=function(){if(this.rangeCount>1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var n=this.getRange(),r=this.isBackwards(),s=n.start.row,o=n.end.row;if(s==o){if(r)var u=n.end,a=n.start;else var u=n.start,a=n.end;this.addRange(i.fromPoints(a,a)),this.addRange(i.fromPoints(u,u));return}var f=[],l=this.getLineRange(s,!0);l.start.column=n.start.column,f.push(l);for(var c=s+1;c1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var r=this.session.documentToScreenPosition(this.cursor),s=this.session.documentToScreenPosition(this.anchor),o=this.rectangularRangeBlock(r,s);o.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(e,t,n){var r=[],s=e.column0)g--;if(g>0){var y=0;while(r[y].isEmpty())y++}for(var b=g;b>=y;b--)r[b].isEmpty()&&r.splice(b,1)}return r}}.call(s.prototype);var d=e("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(e){e.cursor||(e.cursor=e.end);var t=this.getSelectionStyle();return e.marker=this.session.addMarker(e,"ace_selection",t),this.session.$selectionMarkers.push(e),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,e},this.removeSelectionMarker=function(e){if(!e.marker)return;this.session.removeMarker(e.marker);var t=this.session.$selectionMarkers.indexOf(e);t!=-1&&this.session.$selectionMarkers.splice(t,1),this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.removeSelectionMarkers=function(e){var t=this.session.$selectionMarkers;for(var n=e.length;n--;){var r=e[n];if(!r.marker)continue;this.session.removeMarker(r.marker);var i=t.indexOf(r);i!=-1&&t.splice(i,1)}this.session.selectionMarkerCount=t.length},this.$onAddRange=function(e){this.addSelectionMarker(e.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(e){this.removeSelectionMarkers(e.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(e){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle("ace_multiselect"),this.keyBinding.addKeyboardHandler(f.keyboardHandler),this.commands.setDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(e){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle("ace_multiselect"),this.keyBinding.removeKeyboardHandler(f.keyboardHandler),this.commands.removeDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers(),this._emit("changeSelection")},this.$onMultiSelectExec=function(e){var t=e.command,n=e.editor;if(!n.multiSelect)return;if(!t.multiSelectAction){var r=t.exec(n,e.args||{});n.multiSelect.addRange(n.multiSelect.toOrientedRange()),n.multiSelect.mergeOverlappingRanges()}else t.multiSelectAction=="forEach"?r=n.forEachSelection(t,e.args):t.multiSelectAction=="forEachLine"?r=n.forEachSelection(t,e.args,!0):t.multiSelectAction=="single"?(n.exitMultiSelectMode(),r=t.exec(n,e.args||{})):r=t.multiSelectAction(n,e.args||{});return r},this.forEachSelection=function(e,t,n){if(this.inVirtualSelectionMode)return;var r=n&&n.keepOrder,i=n==1||n&&n.$byLines,o=this.session,u=this.selection,a=u.rangeList,f=(r?u:a).ranges,l;if(!f.length)return e.exec?e.exec(this,t||{}):e(this,t||{});var c=u._eventRegistry;u._eventRegistry={};var h=new s(o);this.inVirtualSelectionMode=!0;for(var p=f.length;p--;){if(i)while(p>0&&f[p].start.row==f[p-1].end.row)p--;h.fromOrientedRange(f[p]),h.index=p,this.selection=o.selection=h;var d=e.exec?e.exec(this,t||{}):e(this,t||{});!l&&d!==undefined&&(l=d),h.toOrientedRange(f[p])}h.detach(),this.selection=o.selection=u,this.inVirtualSelectionMode=!1,u._eventRegistry=c,u.mergeOverlappingRanges(),u.ranges[0]&&u.fromOrientedRange(u.ranges[0]);var v=this.renderer.$scrollAnimation;return this.onCursorChange(),this.onSelectionChange(),v&&v.from==v.to&&this.renderer.animateScrolling(v.from),l},this.exitMultiSelectMode=function(){if(!this.inMultiSelectMode||this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getSelectedText=function(){var e="";if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){var t=this.multiSelect.rangeList.ranges,n=[];for(var r=0;r0);u<0&&(u=0),f>=c&&(f=c-1)}var p=this.session.removeFullLines(u,f);p=this.$reAlignText(p,l),this.session.insert({row:u,column:0},p.join("\n")+"\n"),l||(o.start.column=0,o.end.column=p[p.length-1].length),this.selection.setRange(o)}else{s.forEach(function(e){t.substractPoint(e.cursor)});var d=0,v=Infinity,m=n.map(function(t){var n=t.cursor,r=e.getLine(n.row),i=r.substr(n.column).search(/\S/g);return i==-1&&(i=0),n.column>d&&(d=n.column),io?e.insert(r,a.stringRepeat(" ",s-o)):e.remove(new i(r.row,r.column,r.row,r.column-s+o)),t.start.column=t.end.column=d,t.start.row=t.end.row=r.row,t.cursor=t.end}),t.fromOrientedRange(n[0]),this.renderer.updateCursor(),this.renderer.updateBackMarkers()}},this.$reAlignText=function(e,t){function u(e){return a.stringRepeat(" ",e)}function f(e){return e[2]?u(i)+e[2]+u(s-e[2].length+o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function l(e){return e[2]?u(i+s-e[2].length)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function c(e){return e[2]?u(i)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}var n=!0,r=!0,i,s,o;return e.map(function(e){var t=e.match(/(\s*)(.*?)(\s*)([=:].*)/);return t?i==null?(i=t[1].length,s=t[2].length,o=t[3].length,t):(i+s+o!=t[1].length+t[2].length+t[3].length&&(r=!1),i!=t[1].length&&(n=!1),i>t[1].length&&(i=t[1].length),st[3].length&&(o=t[3].length),t):[e]}).map(t?f:n?r?l:f:c)}}).call(d.prototype),t.onSessionChange=function(e){var t=e.session;t&&!t.multiSelect&&(t.$selectionMarkers=[],t.selection.$initRangeList(),t.multiSelect=t.selection),this.multiSelect=t&&t.multiSelect;var n=e.oldSession;n&&(n.multiSelect.off("addRange",this.$onAddRange),n.multiSelect.off("removeRange",this.$onRemoveRange),n.multiSelect.off("multiSelect",this.$onMultiSelect),n.multiSelect.off("singleSelect",this.$onSingleSelect),n.multiSelect.lead.off("change",this.$checkMultiselectChange),n.multiSelect.anchor.off("change",this.$checkMultiselectChange)),t&&(t.multiSelect.on("addRange",this.$onAddRange),t.multiSelect.on("removeRange",this.$onRemoveRange),t.multiSelect.on("multiSelect",this.$onMultiSelect),t.multiSelect.on("singleSelect",this.$onSingleSelect),t.multiSelect.lead.on("change",this.$checkMultiselectChange),t.multiSelect.anchor.on("change",this.$checkMultiselectChange)),t&&this.inMultiSelectMode!=t.selection.inMultiSelectMode&&(t.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},t.MultiSelect=m,e("./config").defineOptions(d.prototype,"editor",{enableMultiselect:{set:function(e){m(this),e?(this.on("changeSession",this.$multiselectOnSessionChange),this.on("mousedown",o)):(this.off("changeSession",this.$multiselectOnSessionChange),this.off("mousedown",o))},value:!0},enableBlockSelect:{set:function(e){this.$blockSelectEnabled=e},value:!0}})}),ace.define("ace/mode/folding/fold_mode",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../../range").Range,i=t.FoldMode=function(){};(function(){this.foldingStartMarker=null,this.foldingStopMarker=null,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);return this.foldingStartMarker.test(r)?"start":t=="markbeginend"&&this.foldingStopMarker&&this.foldingStopMarker.test(r)?"end":""},this.getFoldWidgetRange=function(e,t,n){return null},this.indentationBlock=function(e,t,n){var i=/\S/,s=e.getLine(t),o=s.search(i);if(o==-1)return;var u=n||s.length,a=e.getLength(),f=t,l=t;while(++tf){var h=e.getLine(l).length;return new r(f,u,l,h)}},this.openingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i+1},u=e.$findClosingBracket(t,o,s);if(!u)return;var a=e.foldWidgets[u.row];return a==null&&(a=e.getFoldWidget(u.row)),a=="start"&&u.row>o.row&&(u.row--,u.column=e.getLine(u.row).length),r.fromPoints(o,u)},this.closingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i},u=e.$findOpeningBracket(t,o);if(!u)return;return u.column++,o.column--,r.fromPoints(u,o)}}).call(i.prototype)}),ace.define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";t.isDark=!1,t.cssClass="ace-tm",t.cssText='.ace-tm .ace_gutter {background: #f0f0f0;color: #333;}.ace-tm .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-tm .ace_fold {background-color: #6B72E6;}.ace-tm {background-color: #FFFFFF;color: black;}.ace-tm .ace_cursor {color: black;}.ace-tm .ace_invisible {color: rgb(191, 191, 191);}.ace-tm .ace_storage,.ace-tm .ace_keyword {color: blue;}.ace-tm .ace_constant {color: rgb(197, 6, 11);}.ace-tm .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-tm .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-tm .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-tm .ace_invalid {background-color: rgba(255, 0, 0, 0.1);color: red;}.ace-tm .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-tm .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-tm .ace_support.ace_type,.ace-tm .ace_support.ace_class {color: rgb(109, 121, 222);}.ace-tm .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-tm .ace_string {color: rgb(3, 106, 7);}.ace-tm .ace_comment {color: rgb(76, 136, 107);}.ace-tm .ace_comment.ace_doc {color: rgb(0, 102, 255);}.ace-tm .ace_comment.ace_doc.ace_tag {color: rgb(128, 159, 191);}.ace-tm .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-tm .ace_variable {color: rgb(49, 132, 149);}.ace-tm .ace_xml-pe {color: rgb(104, 104, 91);}.ace-tm .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-tm .ace_heading {color: rgb(12, 7, 255);}.ace-tm .ace_list {color:rgb(185, 6, 144);}.ace-tm .ace_meta.ace_tag {color:rgb(0, 22, 142);}.ace-tm .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-tm .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-tm.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;}.ace-tm .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-tm .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-tm .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-tm .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-tm .ace_gutter-active-line {background-color : #dcdcdc;}.ace-tm .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-tm .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}',t.$id="ace/theme/textmate";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}),ace.define("ace/line_widgets",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e){this.session=e,this.session.widgetManager=this,this.session.getRowLength=this.getRowLength,this.session.$getWidgetScreenLength=this.$getWidgetScreenLength,this.updateOnChange=this.updateOnChange.bind(this),this.renderWidgets=this.renderWidgets.bind(this),this.measureWidgets=this.measureWidgets.bind(this),this.session._changedWidgets=[],this.$onChangeEditor=this.$onChangeEditor.bind(this),this.session.on("change",this.updateOnChange),this.session.on("changeFold",this.updateOnFold),this.session.on("changeEditor",this.$onChangeEditor)}var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./range").Range;(function(){this.getRowLength=function(e){var t;return this.lineWidgets?t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0:t=0,!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.$getWidgetScreenLength=function(){var e=0;return this.lineWidgets.forEach(function(t){t&&t.rowCount&&!t.hidden&&(e+=t.rowCount)}),e},this.$onChangeEditor=function(e){this.attach(e.editor)},this.attach=function(e){e&&e.widgetManager&&e.widgetManager!=this&&e.widgetManager.detach();if(this.editor==e)return;this.detach(),this.editor=e,e&&(e.widgetManager=this,e.renderer.on("beforeRender",this.measureWidgets),e.renderer.on("afterRender",this.renderWidgets))},this.detach=function(e){var t=this.editor;if(!t)return;this.editor=null,t.widgetManager=null,t.renderer.off("beforeRender",this.measureWidgets),t.renderer.off("afterRender",this.renderWidgets);var n=this.session.lineWidgets;n&&n.forEach(function(e){e&&e.el&&e.el.parentNode&&(e._inDocument=!1,e.el.parentNode.removeChild(e.el))})},this.updateOnFold=function(e,t){var n=t.lineWidgets;if(!n||!e.action)return;var r=e.data,i=r.start.row,s=r.end.row,o=e.action=="add";for(var u=i+1;u0&&!r[i])i--;this.firstRow=n.firstRow,this.lastRow=n.lastRow,t.$cursorLayer.config=n;for(var o=i;o<=s;o++){var u=r[o];if(!u||!u.el)continue;if(u.hidden){u.el.style.top=-100-(u.pixelHeight||0)+"px";continue}u._inDocument||(u._inDocument=!0,t.container.appendChild(u.el));var a=t.$cursorLayer.getPixelPosition({row:o,column:0},!0).top;u.coverLine||(a+=n.lineHeight*this.session.getRowLineCount(u.row)),u.el.style.top=a-n.offset+"px";var f=u.coverGutter?0:t.gutterWidth;u.fixedWidth||(f-=t.scrollLeft),u.el.style.left=f+"px",u.fullWidth&&u.screenWidth&&(u.el.style.minWidth=n.width+2*n.padding+"px"),u.fixedWidth?u.el.style.right=t.scrollBar.getWidth()+"px":u.el.style.right=""}}}).call(o.prototype),t.LineWidgets=o}),ace.define("ace/ext/error_marker",["require","exports","module","ace/line_widgets","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e,t,n){var r=0,i=e.length-1;while(r<=i){var s=r+i>>1,o=n(t,e[s]);if(o>0)r=s+1;else{if(!(o<0))return s;i=s-1}}return-(r+1)}function u(e,t,n){var r=e.getAnnotations().sort(s.comparePoints);if(!r.length)return;var i=o(r,{row:t,column:-1},s.comparePoints);i<0&&(i=-i-1),i>=r.length?i=n>0?0:r.length-1:i===0&&n<0&&(i=r.length-1);var u=r[i];if(!u||!n)return;if(u.row===t){do u=r[i+=n];while(u&&u.row===t);if(!u)return r.slice()}var a=[];t=u.row;do a[n<0?"unshift":"push"](u),u=r[i+=n];while(u&&u.row==t);return a.length&&a}var r=e("../line_widgets").LineWidgets,i=e("../lib/dom"),s=e("../range").Range;t.showErrorMarker=function(e,t){var n=e.session;n.widgetManager||(n.widgetManager=new r(n),n.widgetManager.attach(e));var s=e.getCursorPosition(),o=s.row,a=n.widgetManager.getWidgetsAtRow(o).filter(function(e){return e.type=="errorMarker"})[0];a?a.destroy():o-=t;var f=u(n,o,t),l;if(f){var c=f[0];s.column=(c.pos&&typeof c.column!="number"?c.pos.sc:c.column)||0,s.row=c.row,l=e.renderer.$gutterLayer.$annotations[s.row]}else{if(a)return;l={text:["Looks good!"],className:"ace_ok"}}e.session.unfold(s.row),e.selection.moveToPosition(s);var h={row:s.row,fixedWidth:!0,coverGutter:!0,el:i.createElement("div"),type:"errorMarker"},p=h.el.appendChild(i.createElement("div")),d=h.el.appendChild(i.createElement("div"));d.className="error_widget_arrow "+l.className;var v=e.renderer.$cursorLayer.getPixelPosition(s).left;d.style.left=v+e.renderer.gutterWidth-5+"px",h.el.className="error_widget_wrapper",p.className="error_widget "+l.className,p.innerHTML=l.text.join("
"),p.appendChild(i.createElement("div"));var m=function(e,t,n){if(t===0&&(n==="esc"||n==="return"))return h.destroy(),{command:"null"}};h.destroy=function(){if(e.$mouseHandler.isMousePressed)return;e.keyBinding.removeKeyboardHandler(m),n.widgetManager.removeLineWidget(h),e.off("changeSelection",h.destroy),e.off("changeSession",h.destroy),e.off("mouseup",h.destroy),e.off("change",h.destroy)},e.keyBinding.addKeyboardHandler(m),e.on("changeSelection",h.destroy),e.on("changeSession",h.destroy),e.on("mouseup",h.destroy),e.on("change",h.destroy),e.session.widgetManager.addLineWidget(h),h.el.onmousedown=e.focus.bind(e),e.renderer.scrollCursorIntoView(null,.5,{bottom:h.el.offsetHeight})},i.importCssString(" .error_widget_wrapper { background: inherit; color: inherit; border:none } .error_widget { border-top: solid 2px; border-bottom: solid 2px; margin: 5px 0; padding: 10px 40px; white-space: pre-wrap; } .error_widget.ace_error, .error_widget_arrow.ace_error{ border-color: #ff5a5a } .error_widget.ace_warning, .error_widget_arrow.ace_warning{ border-color: #F1D817 } .error_widget.ace_info, .error_widget_arrow.ace_info{ border-color: #5a5a5a } .error_widget.ace_ok, .error_widget_arrow.ace_ok{ border-color: #5aaa5a } .error_widget_arrow { position: absolute; border: solid 5px; border-top-color: transparent!important; border-right-color: transparent!important; border-left-color: transparent!important; top: -5px; }","")}),ace.define("ace/ace",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/dom","ace/lib/event","ace/range","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/worker/worker_client","ace/keyboard/hash_handler","ace/placeholder","ace/multi_select","ace/mode/folding/fold_mode","ace/theme/textmate","ace/ext/error_marker","ace/config"],function(e,t,n){"use strict";e("./lib/fixoldbrowsers");var r=e("./lib/dom"),i=e("./lib/event"),s=e("./range").Range,o=e("./editor").Editor,u=e("./edit_session").EditSession,a=e("./undomanager").UndoManager,f=e("./virtual_renderer").VirtualRenderer;e("./worker/worker_client"),e("./keyboard/hash_handler"),e("./placeholder"),e("./multi_select"),e("./mode/folding/fold_mode"),e("./theme/textmate"),e("./ext/error_marker"),t.config=e("./config"),t.require=e,typeof define=="function"&&(t.define=define),t.edit=function(e,n){if(typeof e=="string"){var s=e;e=document.getElementById(s);if(!e)throw new Error("ace.edit can't find div #"+s)}if(e&&e.env&&e.env.editor instanceof o)return e.env.editor;var u="";if(e&&/input|textarea/i.test(e.tagName)){var a=e;u=a.value,e=r.createElement("pre"),a.parentNode.replaceChild(e,a)}else e&&(u=e.textContent,e.innerHTML="");var l=t.createEditSession(u),c=new o(new f(e),l,n),h={document:l,editor:c,onResize:c.resize.bind(c,null)};return a&&(h.textarea=a),i.addListener(window,"resize",h.onResize),c.on("destroy",function(){i.removeListener(window,"resize",h.onResize),h.editor.container.env=null}),c.container.env=c.env=h,c},t.createEditSession=function(e,t){var n=new u(e,t);return n.setUndoManager(new a),n},t.Range=s,t.Editor=o,t.EditSession=u,t.UndoManager=a,t.VirtualRenderer=f,t.version="1.4.4"}); (function() { + ace.require(["ace/ace"], function(a) { + if (a) { + a.config.init(true); + a.define = ace.define; + } + if (!window.ace) + window.ace = a; + for (var key in a) if (a.hasOwnProperty(key)) + window.ace[key] = a[key]; + window.ace["default"] = window.ace; + if (typeof module == "object" && typeof exports == "object" && module) { + module.exports = window.ace; + } + }); + })(); diff -Nru clamav-0.103.2+dfsg/docs/html/appendix/Appendix.html clamav-0.103.5+dfsg/docs/html/appendix/Appendix.html --- clamav-0.103.2+dfsg/docs/html/appendix/Appendix.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/appendix/Appendix.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,207 @@ + + + + + + Appendix - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/appendix/Authenticode.html clamav-0.103.5+dfsg/docs/html/appendix/Authenticode.html --- clamav-0.103.2+dfsg/docs/html/appendix/Authenticode.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/appendix/Authenticode.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,594 @@ + + + + + + Microsoft Authenticode Signature Verification - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Microsoft Authenticode Signature Verification

+

About Microsoft Authenticode

+

Authenticode is Microsoft's system for using digital signatures to ensure that programs to be run/installed on Windows systems come from a verified source and has not been modified by anyone else. At a high level, it works by having software developers:

+
    +
  1. Obtain a code-signing certificate from a certificate authority trusted by the Windows OS.
  2. +
  3. Compute digital signatures for executables and related software installation files using that certificate.
  4. +
  5. Include the signatures as part of the software execution/installation process so that Windows can use them in the verification process.
  6. +
+

In addition, Authenticode signatures can be countersigned by a time-stamping service that allows signature verification to succeed even if the code-signing certificate expires or gets revoked.

+

For more information, check-out the following resources:

+ +

Authenticode and ClamAV

+

ClamAV supports parsing the Authenticode section and performing signature verification on a given executable to determine whether it should be trusted (based on rules loaded in from ClamAV .crb files). An overview of this process, including information on the .crb file format and on how to add new trusted certificate entries, is explained in the Authenticode Certificate Chain Verification ClamAV blog post.

+

There are a few things not covered in the blog post that are worth mentioning:

+
    +
  • +

    As of ClamAV 0.102, leaf certificates (the ones actually issued to the entity signing the binary) may be used for certificate verification in addition to certificates that issued the leaf certificate (and certificates higher up in the chain) can be used.

    +
  • +
  • +

    As of ClamAV 0.102, .crb rules may also be used to block malicious executables where in previous versions these block list entries just override .crb rules that would otherwise trust a given sample.

    +
  • +
  • +

    SigTool offers the --print-certs flag, which can be used to show information about embedded Authenticode signatures without having to first match on a signature (which is currently a requirement for clamscan)

    +
  • +
  • +

    External Authenticode signatures contained in .cat files can be loaded in to ClamAV by passing a -d flag and indicating the path to the .cat file from which to load signatures. Note, however, that at least one certificate in the .cat file's certificate chain must be trusted (in other words, it must have a backing .crb trusted certificate rule.)

    +
  • +
+

Helpful Info for Working with Authenticode Signatures

+

Below is some useful information collected when improving ClamAV support for Authenticode signatures.

+

Format Specifications

+

The Windows Authenticode 2008 specification document can be found at the link below. Note, however, that it is not 100% accurate. For instance, the documented steps for computing the Authenticode hash are not correct in the case where you have sections that overlap with the PE header or with one another.

+ +

Verifying the Signature

+

On Linux, osslsigncode can be used to verify a signature:

+
    $ osslsigncode verify /path/to/signed/file
+    Current PE checksum   : 00092934
+    Calculated PE checksum: 00092934
+
+    Message digest algorithm  : SHA256
+    Current message digest    : 56924EB391B1B04572B1841ED5D5C10927CE7D6E9553A69F994B9BA855A73933
+    Calculated message digest : 56924EB391B1B04572B1841ED5D5C10927CE7D6E9553A69F994B9BA855A73933
+
+    Signature verification: ok
+
+    Number of signers: 1
+        Signer #0:
+            Subject: /C=US/ST=California/L=Mountain View/O=Google Inc/CN=Google Inc
+            Issuer : /C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 SHA256 Code Signing CA
+
+    Number of certificates: 2
+        Cert #0:
+            Subject: /C=US/ST=California/L=Mountain View/O=Google Inc/CN=Google Inc
+            Issuer : /C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 SHA256 Code Signing CA
+        Cert #1:
+            Subject: /C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 SHA256 Code Signing CA
+            Issuer : /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
+
+    Succeeded
+
+

On Windows,

+

AnalyzePESig is a great tool for displaying signature information. In addition, +signtool can be used.

+

NOTE that the machine on which these commands is run should have Internet connectivity so that revocation lists can be consulted. Otherwise, Windows may default to assuming that none of the certificates are revoked.

+

There is also the verify-sigs python script that performs verification, but this script is no longer maintained.

+

Extracting the Signature

+

On Linux, the osslsigncode command can be used to extract the contents of the PE security section:

+

osslsigncode extract-signature -in /path/to/exe -out /path/to/extracted

+
+

Note: This will also extract the 8-byte WIN_CERTIFICATE structure data. +To skip this data, use:

+
dd if=/path/to/extracted of=/path/to/extracted.p7b bs=1 skip=8`
+
+
+

Inspecting the Signature

+

On Linux, openssl has some useful functions for printing the certificate information and parsing the PKCS7 ASN1:

+
    $ openssl pkcs7 -inform der -print_certs -in extracted.p7b -noout -text
+    Certificate:
+        Data:
+            Version: 3 (0x2)
+            Serial Number:
+                2a:9c:21:ac:aa:a6:3a:3c:58:a7:b9:32:2b:ee:94:8d
+        Signature Algorithm: sha256WithRSAEncryption
+            Issuer: C=US, O=Symantec Corporation, OU=Symantec Trust Network, CN=Symantec Class 3 SHA256 Code Signing CA
+            Validity
+                Not Before: Dec 16 00:00:00 2015 GMT
+                Not After : Dec 16 23:59:59 2018 GMT
+            Subject: C=US, ST=California, L=Mountain View, O=Google Inc, CN=Google Inc
+            Subject Public Key Info:
+                Public Key Algorithm: rsaEncryption
+                    Public-Key: (2048 bit)
+                    Modulus:
+                        00:c4:0d:82:c4:41:29:28:e5:fd:0c:3f:a5:c7:0e:
+                        66:bd:a5:c4:8b:b3:8a:ac:84:03:9f:84:2e:38:df:
+                        06:b1:4e:fd:33:60:58:38:36:dd:22:cf:df:f1:50:
+                        1f:47:f1:55:05:c1:81:01:e7:28:3e:ff:5f:89:12:
+                        09:ea:df:aa:17:49:2c:71:ab:48:d1:9d:2e:f4:51:
+                        e0:03:e0:f7:16:6c:7b:0c:22:75:6d:7e:1f:49:c4:
+                        43:28:88:41:dc:6c:ed:13:2a:03:99:eb:62:14:f9:
+                        35:26:6e:12:2c:03:e2:f7:81:b9:1a:05:67:06:7c:
+                        a6:1a:5b:ed:20:15:e5:2d:83:de:8e:36:fa:1e:08:
+                        41:1c:1a:48:9f:b6:f1:c3:2f:02:13:4b:a7:ca:ba:
+                        ef:1c:58:6f:8e:d3:0f:14:a4:0b:2b:5d:ba:f4:5a:
+                        a3:0d:64:34:a5:8a:d7:8f:4d:22:66:4d:a4:ae:e1:
+                        f9:cd:c6:58:e6:c6:11:77:32:df:ba:df:39:48:8a:
+                        d1:27:d7:33:77:a8:c9:e4:5e:ed:fa:12:cf:f3:fd:
+                        fa:ee:ab:80:86:13:34:eb:5a:7e:6f:6c:1b:ee:d8:
+                        4b:b2:cc:77:98:87:ac:ca:f5:bb:64:6f:49:1e:5b:
+                        91:63:50:1f:63:2d:83:27:73:07:9f:2b:16:f4:7b:
+                        71:29
+                    Exponent: 65537 (0x10001)
+            X509v3 extensions:
+                X509v3 Basic Constraints:
+                    CA:FALSE
+                X509v3 Key Usage: critical
+                    Digital Signature
+                X509v3 Extended Key Usage:
+                    Code Signing
+                X509v3 Certificate Policies:
+                    Policy: 2.16.840.1.113733.1.7.23.3
+                    CPS: https://d.symcb.com/cps
+                    User Notice:
+                        Explicit Text: https://d.symcb.com/rpa
+
+                X509v3 Authority Key Identifier:
+                    keyid:96:3B:53:F0:79:33:97:AF:7D:83:EF:2E:2B:CC:CA:B7:86:1E:72:66
+
+                X509v3 CRL Distribution Points:
+
+                    Full Name:
+                    URI:http://sv.symcb.com/sv.crl
+
+                Authority Information Access:
+                    OCSP - URI:http://sv.symcd.com
+                    CA Issuers - URI:http://sv.symcb.com/sv.crt
+
+                Netscape Cert Type:
+                    Object Signing
+                1.3.6.1.4.1.311.2.1.27:
+                    0.......
+        Signature Algorithm: sha256WithRSAEncryption
+            23:e7:93:93:af:db:a8:4d:af:af:54:e8:d8:26:95:80:cd:23:
+            91:70:ed:0b:5b:b1:e9:d8:dd:1e:40:37:78:97:18:ed:9f:e5:
+            84:67:85:06:50:b5:f1:ab:e6:83:5a:17:7b:51:be:7f:18:c6:
+            47:5e:2b:aa:f4:a0:1f:35:3e:05:9f:43:40:f7:9f:d1:f4:e1:
+            a7:02:f3:8e:c9:71:fe:18:37:48:42:d7:e4:36:73:10:92:d4:
+            d8:d9:1c:c4:26:58:18:67:b6:24:22:69:63:02:f7:49:51:6b:
+            75:f6:b4:7d:56:ff:2c:f4:88:f7:67:6f:08:86:f3:8b:0b:30:
+            02:7f:6d:92:d9:4e:bd:99:f7:7b:74:86:0c:cb:b9:ad:2c:bf:
+            44:79:a8:00:82:9c:62:f4:aa:11:df:d2:bf:f0:e1:92:28:11:
+            90:bb:5e:33:88:86:96:4d:dd:0b:af:c3:67:a1:95:2d:44:32:
+            c6:fa:f7:b8:80:c1:4e:38:be:1f:b6:84:f7:f1:21:31:67:49:
+            a8:9f:8a:75:07:df:3b:3a:c3:ea:72:cd:40:7f:a7:da:7c:c9:
+            2e:7c:a9:0c:f1:5d:5c:82:42:62:b9:49:94:8f:70:e6:a5:c0:
+            5f:17:fb:40:36:c1:3a:89:63:03:1c:3f:66:a0:3d:8f:a1:4c:
+            4e:5c:ac:bf
+    ...
+
+
    $ openssl asn1parse -inform der -i -in extracted.p7b
+        0:d=0  hl=4 l=6984 cons: SEQUENCE
+        4:d=1  hl=2 l=   9 prim:  OBJECT            :pkcs7-signedData
+    15:d=1  hl=4 l=6969 cons:  cont [ 0 ]
+    19:d=2  hl=4 l=6965 cons:   SEQUENCE
+    23:d=3  hl=2 l=   1 prim:    INTEGER           :01
+    26:d=3  hl=2 l=  15 cons:    SET
+    28:d=4  hl=2 l=  13 cons:     SEQUENCE
+    30:d=5  hl=2 l=   9 prim:      OBJECT            :sha256
+    41:d=5  hl=2 l=   0 prim:      NULL
+    43:d=3  hl=2 l=  92 cons:    SEQUENCE
+    45:d=4  hl=2 l=  10 prim:     OBJECT            :1.3.6.1.4.1.311.2.1.4
+    57:d=4  hl=2 l=  78 cons:     cont [ 0 ]
+    59:d=5  hl=2 l=  76 cons:      SEQUENCE
+    61:d=6  hl=2 l=  23 cons:       SEQUENCE
+    63:d=7  hl=2 l=  10 prim:        OBJECT            :1.3.6.1.4.1.311.2.1.15
+    75:d=7  hl=2 l=   9 cons:        SEQUENCE
+    77:d=8  hl=2 l=   1 prim:         BIT STRING
+    80:d=8  hl=2 l=   4 cons:         cont [ 0 ]
+    82:d=9  hl=2 l=   2 cons:          cont [ 2 ]
+    84:d=10 hl=2 l=   0 prim:           cont [ 0 ]
+    86:d=6  hl=2 l=  49 cons:       SEQUENCE
+    88:d=7  hl=2 l=  13 cons:        SEQUENCE
+    90:d=8  hl=2 l=   9 prim:         OBJECT            :sha256
+    101:d=8  hl=2 l=   0 prim:         NULL
+    103:d=7  hl=2 l=  32 prim:        OCTET STRING      [HEX DUMP]:56924EB391B1B04572B1841ED5D5C10927CE7D6E9553A69F994B9BA855A73933
+    ...
+
+

On Windows, the certutil executable has a great ASN parser:

+
    C:\>certutil -asn extracted.p7b
+    0000: 30 82 1b 48                               ; SEQUENCE (1b48 Bytes)
+    0004: |  06 09                                  ; OBJECT_ID (9 Bytes)
+    0006: |  |  2a 86 48 86 f7 0d 01 07  02
+        |  |     ; 1.2.840.113549.1.7.2 PKCS 7 Signed
+    000f: |  a0 82 1b 39                            ; OPTIONAL[0] (1b39 Bytes)
+    0013: |     30 82 1b 35                         ; SEQUENCE (1b35 Bytes)
+    0017: |        02 01                            ; INTEGER (1 Bytes)
+    0019: |        |  01
+    001a: |        31 0f                            ; SET (f Bytes)
+    001c: |        |  30 0d                         ; SEQUENCE (d Bytes)
+    001e: |        |     06 09                      ; OBJECT_ID (9 Bytes)
+    0020: |        |     |  60 86 48 01 65 03 04 02  01
+        |        |     |     ; 2.16.840.1.101.3.4.2.1 sha256 (sha256NoSign)
+    0029: |        |     05 00                      ; NULL (0 Bytes)
+    002b: |        30 5c                            ; SEQUENCE (5c Bytes)
+    002d: |        |  06 0a                         ; OBJECT_ID (a Bytes)
+    002f: |        |  |  2b 06 01 04 01 82 37 02  01 04
+        |        |  |     ; 1.3.6.1.4.1.311.2.1.4 SPC_INDIRECT_DATA_OBJID
+    0039: |        |  a0 4e                         ; OPTIONAL[0] (4e Bytes)
+    003b: |        |     30 4c                      ; SEQUENCE (4c Bytes)
+    003d: |        |        30 17                   ; SEQUENCE (17 Bytes)
+    003f: |        |        |  06 0a                ; OBJECT_ID (a Bytes)
+    0041: |        |        |  |  2b 06 01 04 01 82 37 02  01 0f
+        |        |        |  |     ; 1.3.6.1.4.1.311.2.1.15 SPC_PE_IMAGE_DATA_OBJID
+    004b: |        |        |  30 09                ; SEQUENCE (9 Bytes)
+    ...
+
+

There is also a website that offers ASN1 parser and allows you to interactively +hide/view parts of the structure:

+ +

Creating Signed Executables

+

For Linux, Didier Stevens has a great post about how to create signed binaries using self-signed certificates:

+ +

On Windows, a program called signtool ships with the Windows SDK and can be used. See the following for tutorials/examples:

+ +

Samples with Interesting Authenticode Signatures

+

Below are some PE files with interesting Authenticode signatures. These are probably only interesting to other researchers who are looking at Authenticode in-depth. All samples are available via VirusTotal.

+
    +
  • +

    SHA256-based code-signing signature without a countersignature

    +
      +
    • 8886d96e9ed475e4686ffba3d242e97836de8a56b75cc915e21bb324cc89de03
    • +
    +
  • +
  • +

    SHA256-based code-signing sig and SHA1-based timestamping countersig

    +
      +
    • 20367d0e3a5ad12154095d424b8d9818c33e7d6087525e6a3304ef6c22a53665
    • +
    +
  • +
  • +

    SHA384-based cert used in the code-signing chain

    +
      +
    • 2249611fef630d666f667ac9dc7b665d3b9382956e41f10704e40bd900decbb8
    • +
    +
  • +
  • +

    Uses SHA512 to compute the Authenticode hash

    +
      +
    • eeb5469a214d5aac1dcd7e8415f93eca14edc38f47d1e360d3d97d432695207a
    • +
    +
  • +
  • +

    Signed by an MS root cert that doesn't have a KU or EKU specified

    +
      +
    • 69b61b2c00323cea3686315617d0f452e205dae10c47e02cbe1ea96fea38f582
    • +
    +
  • +
  • +

    Has a v1 x509 cert and uses MD2-based hashing

    +
      +
    • 1cb16f94cebdcad7dd05c8537375a6ff6379fcdb08528fc83889f26efaa84e2a
    • +
    +
  • +
  • +

    Countersignature with version 0 instead of version 1

    +
      +
    • 145fbbf59b1071493686bf41f4eb364627d8be3c9dc8fb927bbe853e593864ec
    • +
    +
  • +
  • +

    Countersignature with contentType == timestampToken instead of pkcs7-data

    +
      +
    • 8a364e0881fd7201cd6f0a0ff747451c9b93182d5699afb28ad8466f7f726660
    • +
    +
  • +
  • +

    Countersignature with AlgoIdentifier sha1WithRSAEncryption instead of SHA1

    +
      +
    • 2aa6b18d509090c60c3e4ecdd8aeb16e5f149807e3404c86892112710eab576d
    • +
    +
  • +
  • +

    Countersignature uses v1 x509 certs

    +
      +
    • 934860a4ac2446240e4c7053ddc27ff4c2463d4ad433cc28c9fcc2ea4690fb86
    • +
    +
  • +
  • +

    Certificate chain has old certificates without a version

    +
      +
    • 374a31b20fbafdcd31d52ae11a0dcad58baba556c8942a3cdfae0bb96ae117a1
    • +
    +
  • +
  • +

    Has extra data after the PKCS7, a violation of MS13-098

    +
      +
    • 0ee196bb23f0eafe4f61d30bf6c676fd7365cb12ae66a6bde278851e91901ac1
    • +
    +
  • +
  • +

    Authenticode signature covers data not in a section

    +
      +
    • 0123c163ac981e639565caff72ee3af2df7174613ee12003ac89124be461c6e6
    • +
    +
  • +
  • +

    Authenticode signature with a section that overlaps the PE header (UPX-packed)

    +
      +
    • 0059fb3f225c5784789622eeccb97197d591972851b63d59f5bd107ddfdb7a21
    • +
    +
  • +
  • +

    Authenticode signature with overlapping sections

    +
      +
    • 014b66cf2cef39620e9a985d237971b8cf272043e9ac372d5dcef44db754a1d2
    • +
    +
  • +
  • +

    Uses certs with no NULL after AlgorithmIdentifier OID

    +
      +
    • 66b797b3b4f99488f53c2b676610dfe9868984c779536891a8d8f73ee214bc4b
    • +
    +
  • +
  • +

    Uses an ASN1 indefinite length object

    +
      +
    • 8ca912e397a9e1b0cc54c216144ff550da9d43610392208193c0781b1aa5d695
    • +
    +
  • +
  • +

    Unexpected contentType for embedded mode signature (copied from a .cat?)

    +
      +
    • 6ed9b5f6d32f94b3d06456b176c8536be123e1047763cc0a31c6e8fd6a0242b1
    • +
    +
  • +
  • +

    Security directory appears to overlap with the PE header

    +
      +
    • ff482f69f2183b5fd3c1b45d9006156524b8f8a5f518e33d6e92ea079787e64d
    • +
    +
  • +
  • +

    x509 cert with a public key using exponent 3

    +
      +
    • 012760e582e541c6dd34a2cbd5d053f402eebcb8b60ed4a88fecb5589bd17bb9
    • +
    +
  • +
  • +

    x509 UTCDate is missing the seconds field

    +
      +
    • 05de45fd6a406dc147a4c8040a54eee947cd6eba02f28c0279ffd1a229e17130
    • +
    +
  • +
  • +

    x509 cert with a negative serial number

    +
      +
    • 6218d50eb5c898acd3482daaea8f615b4f1f87ef0d06220cc1d7f700bc35888b
    • +
    +
  • +
+

Additional References

+
    +
  • +

    What are x509 certificates? (Provides an overview of the ASN1 structure of x509 certificates)

    +
  • +
  • +

    Signed Malware (Research papers on signed malware with interactive tables of malicious code signing certs)

    +
  • +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/appendix/CvdPrivateMirror.html clamav-0.103.5+dfsg/docs/html/appendix/CvdPrivateMirror.html --- clamav-0.103.2+dfsg/docs/html/appendix/CvdPrivateMirror.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/appendix/CvdPrivateMirror.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,291 @@ + + + + + + Hosting a Private Database Mirror - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Private Local Mirrors

+

There are some situations in which it may be desirable to set up a private mirror for distributing ClamAV databases.

+

If you run ClamAV on many clients on your network, each new installation will download a copy of the database files. This is a waste of bandwidth and resources for your network and for our mirrors network.

+

Sometimes the servers which perform the scan are not directly connected to Internet and can only download updates from a server in the same network segment.

+

For people who face these problems, we recommend using one of the following solutions:

+ +

Use cvdupdate to serve whole databases and database patch files from a private mirror

+

You may use a tool named cvdupdate on a private mirror to maintain the latest CVD databases and CDIFF patch files.

+

This solution will allow you to host a mirror that functions in the same way as the official database CDN, serving CVD and CDIFF files. This means that your downstream freshclam clients will be able to update using the CDIFF patch files, which should save some bandwidth between your private mirror and your clients.

+

These instructions use a tool named cvdupdate. cvdupdate requires:

+
    +
  • Python 3.6 or newer.
  • +
  • An internet connection with DNS enabled.
  • +
  • It should work fine on Linux/Unix and on Windows.
  • +
+

IMPORTANT: Please do NOT use cvdupdate if you don't need to host a private database mirror. freshclam is far more efficient, even for a small cluster of installs, because it will update with CDIFF patches after the initial database downloads. cvdupdate, on the otherhand, will download both the new daily CDIFF and the daily CVD every day.

+

You can easily install cvdupdate using Python 3's Pip package manager:

+
pip3 install cvdupdate
+
+

(optional) Once installed, you may wish to configure where the databases are stored:

+
cvd config set --dbdir <your www path>
+
+

Now run this as often as you need, or at least once a day to download/update the databases:

+
cvd update
+
+

You may wish to set up a cron job to check for updates.

+

If you didn't set a custom database path, the databases will be stored in ~/.cvdupdate/database

+
+

Tip: You can use --help with any cvd command to learn more. For ore detailed instructions, or to report issues, please visit: https://github.com/Cisco-Talos/cvdupdate](https://github.com/Cisco-Talos/cvdupdate)

+
+

Once you have the database files, host them with your favorite webserver, or use the cvd serve test-webserver (not intended for production).

+

Next, you'll need to configure the freshclam clients so they'll update from your private mirror.

+

For freshclam.conf on your downstream freshclam clients, set:

+
# Replace `mirror.mylan` and `8000` with your domain and port number.
+DatabaseMirror http://mirror.mylan:8000
+
+

You may wish to set up a proxy to enable HTTPS. If you do, you can specify https instead of http:

+
DatabaseMirror https://mirror.mylan
+
+

You could also host the files in a subdirectory. E.g.:

+
DatabaseMirror http://mirror.mylan:8000/clamav
+
+

When you run freshclam on your client machines, they will still use a DNS query to clamav.net to find out if there should be an update before attempting to update from your private server. If your freshclam clients attempt to update before your private mirror updates, that's okay. The freshclam clients will tolerate being 1 version behind what was advertised on clamav.net.

+
+

Tip: If the freshclam clients will not have access to the internet to perform that DNS lookup, you may wish to set DNSDatabaseInfo no in your freshclam.conf file. freshclam may complain that the DNS lookup to "no" failed, which is fine. It will fall-back to checking the database version using an HTTP Range-request to your server.

+
+
+

CAUTION: If your freshclam clients cannot use DNS to check if there is an update, be certain that your private mirror's webserver supports HTTP Range-requests, or else it may serve the ENTIRE database CVD file when a freshclam client means to check if a newer version exists, and not just a small portion containing the database version.

+

The Python simple.http server does NOT support HTTP Range requests.

+
+

Use freshclam to serve only whole database files from a private mirror

+

You may use freshclam on a private mirror to maintain the latest CVD or CLD databases.

+

The freshclam program running on your private mirror will update using the CDIFF patch files. When you update a CVD database with ClamAV's CDIFF patching process, it produces a CLD "local" database. With this solution for hosting a private mirror, you will serve those CVD or CLD databases to downstream freshclam clients. Unlike when using cvdupdate, this option will not allow you to serve CDIFF patch files.

+
+

Tip: This method may be best if your public IP address is shared with other clients. At present we rate-limit CVD downloads by IP address. So if your public IP address is used by others, cvdupdate may be rate-limited when it attempts to download daily.cvd. But freshclam should never be rate limited for attempting to download the lateset CDIFF patch file.

+
+

This solution is simple to implement. But because you will not be serving CDIFF patch files, it is only effective if your clients are all on the same local network or if bandwidth between your private mirror and your clients is not an issue for you.

+

To get started, configure a local webserver on one of your machines (say mirror.mylan). Set up freshclam on that server so it downloads the database files from http://database.clamav.net and stores them in your webserver’s DocumentRoot directory.

+

For freshclam.conf on your private mirror, set:

+
# The private mirror will update from database.clamav.net.
+DatabaseMirror database.clamav.net
+
+# Customize the DatabaseDirectory so that FreshClam will update the DocumentRoot.
+DatabaseDirectory /your/server/www
+
+# Enable CLD compression to save bandwidth between your mirror and your clients.
+CompressLocalDatabase yes
+
+

Set up freshclam to run as a service or in a cron job so that your private mirror always serves the latest databases.

+

Next, you'll need to configure the freshclam clients so they'll update from your private mirror.

+

For freshclam.conf on your downstream freshclam clients, set:

+
# PrivateMirror is used instead of DatabaseMirror so that FreshClam will:
+# 1. Accept CVD or CLD files, not just CVD files.
+# 2. Use an HTTP Range-request to check if there is an update, rather than DNS.
+PrivateMirror http://mirror.mylan:8000
+
+# ScriptedUpdates is needed because you won't be serving CDIFF files.
+ScriptedUpdates no
+
+

When you run freshclam on your client machines, they should check for updates from your private server over HTTP by downloading just the database header*. If there is a new version, the client will download the whole CVD or CLD file from your private server to update.

+
+

*Important: Make sure your HTTP server will accept and handle HTTP Range requests. If yours does not, then each time a client checks for an update it will download the whole database!

+

The Python simple.http server does NOT support HTTP Range requests.

+
+

Use an HTTP proxy

+

This solution is really easy to implement and is bandwidth efficient.

+

Install a proxy server that supports caching files (e.g. squid) and then tell your freshclam clients to use it. This can be done by setting the HTTPProxyServer parameter in freshclam.conf (see man 5 freshclam.conf for the details).

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/appendix/FileTypes.html clamav-0.103.5+dfsg/docs/html/appendix/FileTypes.html --- clamav-0.103.2+dfsg/docs/html/appendix/FileTypes.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/appendix/FileTypes.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,329 @@ + + + + + + ClamAV File Types and Target Types - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

ClamAV File Types

+

ClamAV has two file typing systems for filtering signature matches: Target Types and File Types.

+

Target Types

+

A Target Type is an integer that indicates which kind of file the signature will match against. Target Type notation was first created for the purposes writing efficient signatures. A signature with a target type of 0 will be run against every file type, and thus is not ideal. However, the Target Type notation is limited and it may be unavoidable.

+

Although the newer CL_TYPE string name notation has replaced the Target Type for some signature formats, many signature formats require a target type number.

+

This is the current list of available Target Types:

+ + + + + + + + + + + + + + +
Target TypeDescription
0any file
1Portable Executable, both 32- and 64-bit
2OLE2 containers, including specific macros. Primarily used by MS Office and MSI installation files
3HTML (normalized)
4Mail file
5Graphics
6ELF
7ASCII text file (normalized)
8Unused
9Mach-O files
10PDF files
11Flash files
12Java class files
+
+

Important: HTML, ASCII, Javascript are all normalized:

+
    +
  • ASCII - All lowercase.
  • +
  • HTML - Whitespace transformed to spaces, tags/tag attributes normalized, all lowercase.
  • +
  • Javascript - All strings are normalized (hex encoding is decoded), numbers are parsed and normalized, local variables/function names are normalized to n001 format, argument to eval() is parsed as JS again, unescape() is handled, some simple JS packers are handled, output is whitespace normalized.
  • +
+
+

File Types

+

ClamAV maintains it's own file typing format and assigns these types using either:

+
    +
  • +

    Evaluation of a unique sequence of bytes at the start of a file (File Type Magic).

    +
  • +
  • +

    File type indicators when parsing container files.

    +
      +
    • For example: +CL_TYPE_SCRIPT may be assigned to data contained in a PDF when the PDF indicates that a stream of bytes is "Javascript"
    • +
    +
  • +
  • +

    File type determination based on the names or characteristics contained within the file.

    +
      +
    • For example: +CL_TYPE_OOXML_WORD may be assigned to a Zip file containing files with specific names.
    • +
    +
  • +
+

ClamAV File Types are prefixed with CL_TYPE_. The following is an exhaustive list of all current file types.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CL_TYPEDescription
CL_TYPE_7Z7-Zip Archive
CL_TYPE_7ZSFXSelf-Extracting 7-Zip Archive
CL_TYPE_APMDisk Image - Apple Partition Map
CL_TYPE_ARJARJ Archive
CL_TYPE_ARJSFXSelf-Extracting ARJ Archive
CL_TYPE_AUTOITAutoIt Automation Executable
CL_TYPE_BINARY_DATAbinary data
CL_TYPE_BINHEXBinHex Macintosh 7-bit ASCII email attachment encoding
CL_TYPE_BZBZip Compressed File
CL_TYPE_CABSFXSelf-Extracting Microsoft CAB Archive
CL_TYPE_CPIO_CRCCPIO Archive (CRC)
CL_TYPE_CPIO_NEWCCPIO Archive (NEWC)
CL_TYPE_CPIO_ODCCPIO Archive (ODC)
CL_TYPE_CPIO_OLDCPIO Archive (OLD, Little Endian or Big Endian)
CL_TYPE_CRYPTFFFiles encrypted by CryptFF malware
CL_TYPE_DMGApple DMG Archive
CL_TYPE_EGGESTSoft EGG Archive, new in 0.102
CL_TYPE_ELFELF Executable (Linux/Unix program or library)
CL_TYPE_GIFGIF Graphics File, new in 0.103
CL_TYPE_GPTDisk Image - GUID Partition Table
CL_TYPE_GRAPHICSOther graphics files; BMP, JPEG2000
CL_TYPE_GZGZip Compressed File
CL_TYPE_HTML_UTF16Wide-Character / UTF16 encoded HTML
CL_TYPE_HTMLHTML data
CL_TYPE_HWP3Hangul Word Processor (3.X)
CL_TYPE_HWPOLE2Hangul Word Processor embedded OLE2
CL_TYPE_INTERNALInternal properties
CL_TYPE_ISHIELD_MSIWindows Install Shield MSI installer
CL_TYPE_ISO9660ISO 9660 file system for optical disc media
CL_TYPE_JAVAJava Class File
CL_TYPE_JPEGJPEG Graphics File, new in 0.103.1
CL_TYPE_LNKMicrosoft Windows Shortcut File
CL_TYPE_MACHO_UNIBINUniversal Binary/Java Bytecode
CL_TYPE_MACHOApple/NeXTSTEP Mach-O Executable file format
CL_TYPE_MAILEmail file
CL_TYPE_MBRDisk Image - Master Boot Record
CL_TYPE_MHTMLMHTML Saved Web Page
CL_TYPE_MSCABMicrosoft CAB Archive
CL_TYPE_MSCHMMicrosoft CHM help archive
CL_TYPE_MSEXEMicrosoft EXE / DLL Executable file
CL_TYPE_MSOLE2Microsoft OLE2 Container file
CL_TYPE_MSSZDDMicrosoft Compressed EXE
CL_TYPE_NULSFTNullSoft Scripted Installer program
CL_TYPE_OLD_TARTAR archive (old)
CL_TYPE_OOXML_HWPHangul Office Open Word Processor (5.X)
CL_TYPE_OOXML_PPTMicrosoft Office Open XML PowerPoint
CL_TYPE_OOXML_WORDMicrosoft Office Open Word 2007+
CL_TYPE_OOXML_XLMicrosoft Office Open Excel 2007+
CL_TYPE_PART_HFSPLUSApple HFS+ partition
CL_TYPE_PDFAdobe PDF document
CL_TYPE_PNGPNG Graphics File, new in 0.103
CL_TYPE_POSIX_TARTAR archive
CL_TYPE_PSPostscript
CL_TYPE_RARRAR Archive
CL_TYPE_RARSFXSelf-Extracting RAR Archive
CL_TYPE_RIFFResource Interchange File Format container formatted file
CL_TYPE_RTFRich Text Format document
CL_TYPE_SCRENCFiles encrypted by ScrEnc malware
CL_TYPE_SCRIPTGeneric type for scripts (Javascript, Python, etc)
CL_TYPE_SISSymbian OS Software Installation Script Archive
CL_TYPE_SWFAdobe Flash File (LZMA, Zlib, or uncompressed)
CL_TYPE_TEXT_ASCIIASCII text
CL_TYPE_TEXT_UTF16BEUTF-16BE text
CL_TYPE_TEXT_UTF16LEUTF-16LE text
CL_TYPE_TEXT_UTF8UTF-8 text
CL_TYPE_TIFFTIFF Graphics File (Little or Big Endian), new in 0.103.1
CL_TYPE_TNEFMicrosoft Outlook & Exchange email attachment format
CL_TYPE_UUENCODEDUUEncoded (Unix-to-Unix) binary file (Unix email attachment)
CL_TYPE_XARXAR Archive
CL_TYPE_XDPAdobe XDP - Embedded PDF
CL_TYPE_XML_HWPHangul Word Processor XML (HWPML) Document
CL_TYPE_XML_WORDMicrosoft Word 2003 XML Document
CL_TYPE_XML_XLMicrosoft Excel 2003 XML Document
CL_TYPE_XZXZ Archive
CL_TYPE_ZIPZip Archive
CL_TYPE_ZIPSFXSelf-Extracting Zip Archive
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/appendix/FunctionalityLevels.html clamav-0.103.5+dfsg/docs/html/appendix/FunctionalityLevels.html --- clamav-0.103.2+dfsg/docs/html/appendix/FunctionalityLevels.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/appendix/FunctionalityLevels.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,261 @@ + + + + + + ClamAV Versions and Functionality Levels - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Versions & Functionality Levels (FLEVELs)

+

The Functionality Level (or FLEVEL) is an integer that signatures may use to define which versions of ClamAV the signature features support. It is up to the signature writers to select the correct FLEVEL or range of FLEVELs when writing a signature so that it does not cause failures in older versions of ClamAV.

+

Setting appropriate FLEVELs in signatures is particularly crucial when using features added in the last 3-4 major release versions.

+

ClamAV Version to FLEVEL chart

+
+

Note: This markdown table is generated from a spreadsheet using this tool.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Release DateReleaseFLEVELFunctionalityLevel (bytecode enum)clamav lib.sofreshclam lib.soAPI/ABI changes, major features, other notes
n/a1.0.0160FUNC_LEVEL_1_0n/an/an/an/a
n/a0.105.0150FUNC_LEVEL_0105n/an/an/an/a
Nov-20210.104.1141FUNC_LEVEL_0104_110:00:019.1.02:02:002.0.2Critical bug fixes
Sep-20210.104.0140FUNC_LEVEL_010410:00:019.1.02:02:002.0.2CMake stabilized (autotools removed); Added db load/compile/free callbacks to clamav.h API.
Nov-20210.103.4125FUNC_LEVEL_0103_49:05:009.0.52:01:002.0.1Critical bug fixes
Jun-20210.103.3124FUNC_LEVEL_0103_39:05:009.0.52:01:002.0.1Critical bug fixes
Apr-20210.103.2123FUNC_LEVEL_0103_29:05:009.0.52:01:002.0.1Security fixes
Feb-20210.103.1122FUNC_LEVEL_0103_19:05:009.0.52:01:002.0.1Fix PNG parser; Loosen GIF format validation (trailer byte); Fix scan issue on Windows RAM disks; Fix clamonacc FD-passing; Enable JPEG & TIFF validation; Add CL_TYPE_JPEG, CL_TYPE_TIFF; Adds ALERT_BROKEN_IMAGES scan heuristics option
Sep-20200.103.0120FUNC_LEVEL_01039:05:009.0.52:01:002.0.1Add CL_TYPE_PNG, CL_TYPE_GIF; Add XLM macro detection; Add bzip2 & LZMA decompression functions to bytecode API
Jul-20200.102.4115FUNC_LEVEL_0102_49:04:009.0.42:00:002.0.0Security fixes
May-20200.102.3114FUNC_LEVEL_0102_39:04:009.0.42:00:002.0.0Security fixes
Feb-20200.102.2113FUNC_LEVEL_0102_29:04:009.0.42:00:002.0.0Security fixes
Nov-20190.102.1112FUNC_LEVEL_0102_19:04:009.0.42:00:002.0.0Security fixes; Significant load time improvement for LDBs with common pattern prefixes
Oct-20190.102.0110FUNC_LEVEL_01029:04:009.0.42:00:002.0.0BytecodeKind: BC_ELF_UNPACKER, BC_MACHO_UNPACKER; Add CL_TYPE_EGG/CL_TYPE_EGG_SFX; Added scan time limit
Nov-20190.101.5106FUNC_LEVEL_0101_59:04:009.0.4Security fixes; Significant load time improvement for LDBs with common pattern prefixes
Aug-20190.101.4105FUNC_LEVEL_0101_49:04:009.0.4Security fixes; Added scan time limit, bzip vuln fix
Aug-20190.101.3102FUNC_LEVEL_0101_39:03:009.0.3Security fixes; Flevel not incremented (whoops)
Mar-20190.101.2102FUNC_LEVEL_0101_29:02:009.0.2Security fixes; Flevel not incremented (whoops)
Jan-20190.101.1102FUNC_LEVEL_0101_19:01:009.0.1Fix to clamav.h header; Adds clamav-types.h
Dec-20180.101.0100FUNC_LEVEL_01019:00:009.0.0Non-backwards compatible API/ABI change: Added filename to scanfile & scandesc, and scan options became a struct; RAR5 Support; Byte-Compare Subsigs; Add CL_TYPE_LNK
Mar-20190.100.394FUNC_LEVEL_0100_38:02:017.1.2Security fixes
Sep-20180.100.293FUNC_LEVEL_0100_28:01:017.1.1Security fixes; Some lenience changes to FreshClam
Jun-20180.100.192FUNC_LEVEL_0100_18:01:017.1.1Security fixes; Add support for HTTPS in ClamSubmit
Mar-20180.100.090FUNC_LEVEL_01008:01:017.1.1Feature release 2 years in dev't; Many improvements; Notably Container/Intermediates changes; Changes to wildcard signatures
Mar-20180.99.485FUNC_LEVEL_099_48:01:017.1.1Security fixes; Other important bug fixes
Jan-20180.99.384FUNC_LEVEL_099_38:01:017.1.1Security fixes; Minor bug fixes
May-20160.99.282FUNC_LEVEL_099_28:01:017.1.1Various bug fixes
Mar-20160.99.182FUNC_LEVEL_099_18:01:017.1.1Security fixes; HWP support
Dec-20150.99.081FUNC_LEVEL_0998:01:017.1.1Add Yara and PCRE support; Add 'other' targets type (14) for non-listed target types; Improved on-access scanning.
Apr-20150.98.780FUNC_LEVEL_098_77:26:016.1.26Security fixes; MSXML & PDF fixes
Jan-20160.98.679FUNC_LEVEL_098_67:25:016.1.25Security fixes; Other bug fixes
Nov-20140.98.579FUNC_LEVEL_098_57:22:016.1.22Added internal target type (13); File properties JSON output.
Jun-20140.98.477FUNC_LEVEL_098_47:23:016.1.23
May-20140.98.377FUNC_LEVEL_098_37:22:016.1.22
May-20140.98.277FUNC_LEVEL_098_27:22:016.1.22Add engine_options bit field (and DisableCache option); Add stats callbacks and callback context
Jan-20140.98.176FUNC_LEVEL_098_17:20:016.1.20Added XZ support and ForceToDisk scan option; Added Libxml2 dependency + XAR, DMG, HFS+/HFSX support; Added FTM type 4 (for in-buffer partition magic, analogous to type 0 for files)
Sep-20130.98.074FUNC_LEVEL_0987:18:016.1.18Add Target-Types 10 - 13 (PDF, FLASH, JAVA, and INTERNAL); Introduced all-scan options; SWF and Java targets (11 & 12); Introduced with "SE" offset modifier; Introduced with ISO9660 scanning support; Add wild card bracket notation{} for body-based signatures; Disable SWF parser
Apr-20130.97.869FUNC_LEVEL_097_87:17:016.1.17Security fixes
Mar-20130.97.768FUNC_LEVEL_097_77:16:016.1.16Security fixes
Sep-20120.97.667FUNC_LEVEL_097_67:15:016.1.15Fixed error-handling issues
Jun-20120.97.565FUNC_LEVEL_097_57:14:016.1.14First Sourcefire ClamAV release
Mar-20120.97.464FUNC_LEVEL_097_47:13:016.1.13Support comment lines in ALL DB files
Oct-20110.97.363FUNC_LEVEL_097_37:12:016.1.12
Jul-20110.97.262FUNC_LEVEL_097_27:11:016.1.11
Jun-20110.97.161FUNC_LEVEL_097_17:10:016.1.10
Feb-20110.97.060FUNC_LEVEL_0977:09:016.1.9
Nov-20100.96.558FUNC_LEVEL_096_57:07:016.1.7
Oct-20100.96.456FUNC_LEVEL_096_47:06:016.1.6Minimal FLEVEL allowed for all current bytecode signatures (quadratic load-time before this point)
Sep-20100.96.355FUNC_LEVEL_096_37:05:016.1.5
Aug-20100.96.254FUNC_LEVEL_096_27:04:016.1.4
May-20100.96.153FUNC_LEVEL_096_17:03:016.1.3
Mar-20100.9651FUNC_LEVEL_0967:02:016.1.2Add bytecode & CDB signatures, Ignores should use IGN2 (or take name field only from IGN)
Oct-20090.95.3446:05:006.0.5
Jun-20090.95.2436:04:006.0.4
Apr-20090.95.1426:03:006.0.3
Mar-20090.95416:02:006.0.2Ignores should use IGN format (including line number)
+
+

For more information on ClamAV file type support, see the File Types Reference.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/appendix/Terminology.html clamav-0.103.5+dfsg/docs/html/appendix/Terminology.html --- clamav-0.103.2+dfsg/docs/html/appendix/Terminology.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/appendix/Terminology.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,233 @@ + + + + + + Terminology - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Terminology

+

General Terminology

+ + + + + + + + + + + + + +
TermDescription
adwareAdware is software that injects extra advertisements into other applications like your browser, or extra advertisements bundled by a third party into software that is ordinarily free of advertisements. Adware is often considered to be a type of Potentially Unwanted Application (PUA) when the the advertisements aren't used to fund development of the application but are injected by a third party.
bytecodeBytecode is a partially compiled executable that is platform agnostic but must be further compiled or otherwise interpreted in order to be executed. For ClamAV, "bytecode signature" refer to ClamAV plugin with a .cbc file extension. Some bytecode signatures detect specific types of malware, as the name suggests. Other bytecode signatures extend file parser support within ClamAV, enabling faster deployment of new ClamAV features.
CLDA CLD is the uncompressed ClamAV signature database archive. CLD files are created by FreshClam when a CVD or CLD database archive is updated with a CDIFF patch file.
CVDA CVD is an compressed ClamAV signature database archive that is signed and distributed by Cisco-Talos. "CVD" refers to the .cvd file extension, although when updated using a CDIFF database patch file, the extension is changed to .cld.
CDIFFA CDIFF is a patch file for a CVD or CLD database archive. CDIFFs allow for frequent updates to the ClamAV database set without requiring a large download each time.
endpointAn endpoint is a computer that a human interacts with, such as a laptop, desktop, or mobile device.
endpoint security suiteAn endpoint security suite is software that bundles a variety of services to protect a computer system. In addition to scheduled malware scanning, this may include features like a firewall, on-access scanning, network traffic monitoring, process behavioral monitoring, and other real time protection features.
false negativeA false negative is when a scan fails to alert on a file. You can report false negatives on clamav.net.
false positiveA false positive is when a scan incorrectly alerts on a file. You can report false positive detections on clamav.net.
malwareMalware is a general term for software intended to cause harm, disrupt, or gain unauthorized access to a computer system. Trojans, worms, miners, rootkits, viruses, keyloggers, and ransomware are all examples of different types of malware.
on-accessOn-access in the context of malware detection refers to a technology to scan files when they are created, opened, moved, or otherwise accessed. On-access scanning is one form of "real time protection". The scan may block access to the file and prevent access if an alert occurs, or it may simply scan as the file is being accessed to alert or take some action when the scan is complete.
PUAA Potentially Unwanted Application (PUA) or Potentially Unwanted Program (PUP) is a program that probably isn't malware, but little benefit to the user and is considered to be undesirable by most people. This may include software like crypto currency mining software, adware, and other software that may be legitimate but may also be used to take advantage of unsuspecting users. See our FAQ page about PUA signatures for more information.
+

ClamAV Components

+ + + + + + + + + + + + + + + +
ComponentDescription
clamav-configThis is a script for checking how ClamAV was compiled. clamav-config is not present on Windows installations.
clamav-milterClamAV-Milter is a daemon that performs email filter scanning through clamd.
clambc-compilerThe ClamAV Bytecode Compiler (ClamBC-Compiler or ClamBCC) is a compiler for building bytecode executable signature plugins. The Bytecode Compiler installed separately. See the ClamAV Bytecode Compiler project on Github for more details.
clambcClamBC is another signature manipulation tool specifically for bytecode signatures.
clamconfClamConf is a tool to check or generate ClamAV configuration files and collect additional information that may be needed to help remotely debug issues.
clamdClamD is a multi threaded daemon that listens for scan requests on a network socket or unix socket. Client applications for clamd include clamdtop, clamdscan, clamav-milter, and clamonacc. The socket interface is documented and there are also a variety of third-party clients for clamd. See the Community Projects page for more details.
clamdscanClamDScan is a command line program to scan files and directories through clamd.
clamdtopClamDTop is a command line GUI to monitor ClamD.
clamonaccClamOnAcc is a daemon that receives on-access events from the kernel for real-time protection scanning through ClamD. ClamOnAcc is only available for Linux at this time.
clamscanClamScan is a command line program to scan files and directories that does not require the clamd daemon.
freshclamFreshClam is the signature database (cvd) update tool.
libclamavThe ClamAV library enables you to build the ClamAV engine into your programs. The C programming API can be found in clamav.h.
libfreshclamThe FreshClam library enables you to build the signature update update features into your programs. The C programming API can be found in libfreshclam.h.
sigtoolSigTool is a signature database (cvd) manipulation tool for malware analysts and signature writers.
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/ayu-highlight.css clamav-0.103.5+dfsg/docs/html/ayu-highlight.css --- clamav-0.103.2+dfsg/docs/html/ayu-highlight.css 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/ayu-highlight.css 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,79 @@ +/* +Based off of the Ayu theme +Original by Dempfi (https://github.com/dempfi/ayu) +*/ + +.hljs { + display: block; + overflow-x: auto; + background: #191f26; + color: #e6e1cf; + padding: 0.5em; +} + +.hljs-comment, +.hljs-quote { + color: #5c6773; + font-style: italic; +} + +.hljs-variable, +.hljs-template-variable, +.hljs-attribute, +.hljs-attr, +.hljs-regexp, +.hljs-link, +.hljs-selector-id, +.hljs-selector-class { + color: #ff7733; +} + +.hljs-number, +.hljs-meta, +.hljs-builtin-name, +.hljs-literal, +.hljs-type, +.hljs-params { + color: #ffee99; +} + +.hljs-string, +.hljs-bullet { + color: #b8cc52; +} + +.hljs-title, +.hljs-built_in, +.hljs-section { + color: #ffb454; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-symbol { + color: #ff7733; +} + +.hljs-name { + color: #36a3d9; +} + +.hljs-tag { + color: #00568d; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +.hljs-addition { + color: #91b362; +} + +.hljs-deletion { + color: #d96c75; +} diff -Nru clamav-0.103.2+dfsg/docs/html/book.js clamav-0.103.5+dfsg/docs/html/book.js --- clamav-0.103.2+dfsg/docs/html/book.js 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/book.js 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,655 @@ +"use strict"; + +// Fix back button cache problem +window.onunload = function () { }; + +// Global variable, shared between modules +function playground_text(playground) { + let code_block = playground.querySelector("code"); + + if (window.ace && code_block.classList.contains("editable")) { + let editor = window.ace.edit(code_block); + return editor.getValue(); + } else { + return code_block.textContent; + } +} + +(function codeSnippets() { + function fetch_with_timeout(url, options, timeout = 6000) { + return Promise.race([ + fetch(url, options), + new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout)) + ]); + } + + var playgrounds = Array.from(document.querySelectorAll(".playground")); + if (playgrounds.length > 0) { + fetch_with_timeout("https://play.rust-lang.org/meta/crates", { + headers: { + 'Content-Type': "application/json", + }, + method: 'POST', + mode: 'cors', + }) + .then(response => response.json()) + .then(response => { + // get list of crates available in the rust playground + let playground_crates = response.crates.map(item => item["id"]); + playgrounds.forEach(block => handle_crate_list_update(block, playground_crates)); + }); + } + + function handle_crate_list_update(playground_block, playground_crates) { + // update the play buttons after receiving the response + update_play_button(playground_block, playground_crates); + + // and install on change listener to dynamically update ACE editors + if (window.ace) { + let code_block = playground_block.querySelector("code"); + if (code_block.classList.contains("editable")) { + let editor = window.ace.edit(code_block); + editor.addEventListener("change", function (e) { + update_play_button(playground_block, playground_crates); + }); + // add Ctrl-Enter command to execute rust code + editor.commands.addCommand({ + name: "run", + bindKey: { + win: "Ctrl-Enter", + mac: "Ctrl-Enter" + }, + exec: _editor => run_rust_code(playground_block) + }); + } + } + } + + // updates the visibility of play button based on `no_run` class and + // used crates vs ones available on http://play.rust-lang.org + function update_play_button(pre_block, playground_crates) { + var play_button = pre_block.querySelector(".play-button"); + + // skip if code is `no_run` + if (pre_block.querySelector('code').classList.contains("no_run")) { + play_button.classList.add("hidden"); + return; + } + + // get list of `extern crate`'s from snippet + var txt = playground_text(pre_block); + var re = /extern\s+crate\s+([a-zA-Z_0-9]+)\s*;/g; + var snippet_crates = []; + var item; + while (item = re.exec(txt)) { + snippet_crates.push(item[1]); + } + + // check if all used crates are available on play.rust-lang.org + var all_available = snippet_crates.every(function (elem) { + return playground_crates.indexOf(elem) > -1; + }); + + if (all_available) { + play_button.classList.remove("hidden"); + } else { + play_button.classList.add("hidden"); + } + } + + function run_rust_code(code_block) { + var result_block = code_block.querySelector(".result"); + if (!result_block) { + result_block = document.createElement('code'); + result_block.className = 'result hljs language-bash'; + + code_block.append(result_block); + } + + let text = playground_text(code_block); + let classes = code_block.querySelector('code').classList; + let has_2018 = classes.contains("edition2018"); + let edition = has_2018 ? "2018" : "2015"; + + var params = { + version: "stable", + optimize: "0", + code: text, + edition: edition + }; + + if (text.indexOf("#![feature") !== -1) { + params.version = "nightly"; + } + + result_block.innerText = "Running..."; + + fetch_with_timeout("https://play.rust-lang.org/evaluate.json", { + headers: { + 'Content-Type': "application/json", + }, + method: 'POST', + mode: 'cors', + body: JSON.stringify(params) + }) + .then(response => response.json()) + .then(response => result_block.innerText = response.result) + .catch(error => result_block.innerText = "Playground Communication: " + error.message); + } + + // Syntax highlighting Configuration + hljs.configure({ + tabReplace: ' ', // 4 spaces + languages: [], // Languages used for auto-detection + }); + + let code_nodes = Array + .from(document.querySelectorAll('code')) + // Don't highlight `inline code` blocks in headers. + .filter(function (node) {return !node.parentElement.classList.contains("header"); }); + + if (window.ace) { + // language-rust class needs to be removed for editable + // blocks or highlightjs will capture events + Array + .from(document.querySelectorAll('code.editable')) + .forEach(function (block) { block.classList.remove('language-rust'); }); + + Array + .from(document.querySelectorAll('code:not(.editable)')) + .forEach(function (block) { hljs.highlightBlock(block); }); + } else { + code_nodes.forEach(function (block) { hljs.highlightBlock(block); }); + } + + // Adding the hljs class gives code blocks the color css + // even if highlighting doesn't apply + code_nodes.forEach(function (block) { block.classList.add('hljs'); }); + + Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) { + + var lines = Array.from(block.querySelectorAll('.boring')); + // If no lines were hidden, return + if (!lines.length) { return; } + block.classList.add("hide-boring"); + + var buttons = document.createElement('div'); + buttons.className = 'buttons'; + buttons.innerHTML = ""; + + // add expand button + var pre_block = block.parentNode; + pre_block.insertBefore(buttons, pre_block.firstChild); + + pre_block.querySelector('.buttons').addEventListener('click', function (e) { + if (e.target.classList.contains('fa-eye')) { + e.target.classList.remove('fa-eye'); + e.target.classList.add('fa-eye-slash'); + e.target.title = 'Hide lines'; + e.target.setAttribute('aria-label', e.target.title); + + block.classList.remove('hide-boring'); + } else if (e.target.classList.contains('fa-eye-slash')) { + e.target.classList.remove('fa-eye-slash'); + e.target.classList.add('fa-eye'); + e.target.title = 'Show hidden lines'; + e.target.setAttribute('aria-label', e.target.title); + + block.classList.add('hide-boring'); + } + }); + }); + + if (window.playground_copyable) { + Array.from(document.querySelectorAll('pre code')).forEach(function (block) { + var pre_block = block.parentNode; + if (!pre_block.classList.contains('playground')) { + var buttons = pre_block.querySelector(".buttons"); + if (!buttons) { + buttons = document.createElement('div'); + buttons.className = 'buttons'; + pre_block.insertBefore(buttons, pre_block.firstChild); + } + + var clipButton = document.createElement('button'); + clipButton.className = 'fa fa-copy clip-button'; + clipButton.title = 'Copy to clipboard'; + clipButton.setAttribute('aria-label', clipButton.title); + clipButton.innerHTML = ''; + + buttons.insertBefore(clipButton, buttons.firstChild); + } + }); + } + + // Process playground code blocks + Array.from(document.querySelectorAll(".playground")).forEach(function (pre_block) { + // Add play button + var buttons = pre_block.querySelector(".buttons"); + if (!buttons) { + buttons = document.createElement('div'); + buttons.className = 'buttons'; + pre_block.insertBefore(buttons, pre_block.firstChild); + } + + var runCodeButton = document.createElement('button'); + runCodeButton.className = 'fa fa-play play-button'; + runCodeButton.hidden = true; + runCodeButton.title = 'Run this code'; + runCodeButton.setAttribute('aria-label', runCodeButton.title); + + buttons.insertBefore(runCodeButton, buttons.firstChild); + runCodeButton.addEventListener('click', function (e) { + run_rust_code(pre_block); + }); + + if (window.playground_copyable) { + var copyCodeClipboardButton = document.createElement('button'); + copyCodeClipboardButton.className = 'fa fa-copy clip-button'; + copyCodeClipboardButton.innerHTML = ''; + copyCodeClipboardButton.title = 'Copy to clipboard'; + copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title); + + buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild); + } + + let code_block = pre_block.querySelector("code"); + if (window.ace && code_block.classList.contains("editable")) { + var undoChangesButton = document.createElement('button'); + undoChangesButton.className = 'fa fa-history reset-button'; + undoChangesButton.title = 'Undo changes'; + undoChangesButton.setAttribute('aria-label', undoChangesButton.title); + + buttons.insertBefore(undoChangesButton, buttons.firstChild); + + undoChangesButton.addEventListener('click', function () { + let editor = window.ace.edit(code_block); + editor.setValue(editor.originalCode); + editor.clearSelection(); + }); + } + }); +})(); + +(function themes() { + var html = document.querySelector('html'); + var themeToggleButton = document.getElementById('theme-toggle'); + var themePopup = document.getElementById('theme-list'); + var themeColorMetaTag = document.querySelector('meta[name="theme-color"]'); + var stylesheets = { + ayuHighlight: document.querySelector("[href$='ayu-highlight.css']"), + tomorrowNight: document.querySelector("[href$='tomorrow-night.css']"), + highlight: document.querySelector("[href$='highlight.css']"), + }; + + function showThemes() { + themePopup.style.display = 'block'; + themeToggleButton.setAttribute('aria-expanded', true); + themePopup.querySelector("button#" + get_theme()).focus(); + } + + function hideThemes() { + themePopup.style.display = 'none'; + themeToggleButton.setAttribute('aria-expanded', false); + themeToggleButton.focus(); + } + + function get_theme() { + var theme; + try { theme = localStorage.getItem('mdbook-theme'); } catch (e) { } + if (theme === null || theme === undefined) { + return default_theme; + } else { + return theme; + } + } + + function set_theme(theme, store = true) { + let ace_theme; + + if (theme == 'clamav') { + stylesheets.ayuHighlight.disabled = true; + stylesheets.tomorrowNight.disabled = false; + stylesheets.highlight.disabled = true; + + ace_theme = "ace/theme/tomorrow_night"; + } else { + stylesheets.ayuHighlight.disabled = true; + stylesheets.tomorrowNight.disabled = true; + stylesheets.highlight.disabled = false; + ace_theme = "ace/theme/dawn"; + } + + setTimeout(function () { + themeColorMetaTag.content = getComputedStyle(document.body).backgroundColor; + }, 1); + + if (window.ace && window.editors) { + window.editors.forEach(function (editor) { + editor.setTheme(ace_theme); + }); + } + + var previousTheme = get_theme(); + + if (store) { + try { localStorage.setItem('mdbook-theme', theme); } catch (e) { } + } + + html.classList.remove(previousTheme); + html.classList.add(theme); + } + + // Set theme + var theme = get_theme(); + + set_theme(theme, false); + + themeToggleButton.addEventListener('click', function () { + if (themePopup.style.display === 'block') { + hideThemes(); + } else { + showThemes(); + } + }); + + themePopup.addEventListener('click', function (e) { + var theme = e.target.id || e.target.parentElement.id; + set_theme(theme); + }); + + themePopup.addEventListener('focusout', function(e) { + // e.relatedTarget is null in Safari and Firefox on macOS (see workaround below) + if (!!e.relatedTarget && !themeToggleButton.contains(e.relatedTarget) && !themePopup.contains(e.relatedTarget)) { + hideThemes(); + } + }); + + // Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628 + document.addEventListener('click', function(e) { + if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) { + hideThemes(); + } + }); + + document.addEventListener('keydown', function (e) { + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } + if (!themePopup.contains(e.target)) { return; } + + switch (e.key) { + case 'Escape': + e.preventDefault(); + hideThemes(); + break; + case 'ArrowUp': + e.preventDefault(); + var li = document.activeElement.parentElement; + if (li && li.previousElementSibling) { + li.previousElementSibling.querySelector('button').focus(); + } + break; + case 'ArrowDown': + e.preventDefault(); + var li = document.activeElement.parentElement; + if (li && li.nextElementSibling) { + li.nextElementSibling.querySelector('button').focus(); + } + break; + case 'Home': + e.preventDefault(); + themePopup.querySelector('li:first-child button').focus(); + break; + case 'End': + e.preventDefault(); + themePopup.querySelector('li:last-child button').focus(); + break; + } + }); +})(); + +(function sidebar() { + var html = document.querySelector("html"); + var sidebar = document.getElementById("sidebar"); + var sidebarLinks = document.querySelectorAll('#sidebar a'); + var sidebarToggleButton = document.getElementById("sidebar-toggle"); + var sidebarResizeHandle = document.getElementById("sidebar-resize-handle"); + var firstContact = null; + + function showSidebar() { + html.classList.remove('sidebar-hidden') + html.classList.add('sidebar-visible'); + Array.from(sidebarLinks).forEach(function (link) { + link.setAttribute('tabIndex', 0); + }); + sidebarToggleButton.setAttribute('aria-expanded', true); + sidebar.setAttribute('aria-hidden', false); + try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { } + } + + + var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle'); + + function toggleSection(ev) { + ev.currentTarget.parentElement.classList.toggle('expanded'); + } + + Array.from(sidebarAnchorToggles).forEach(function (el) { + el.addEventListener('click', toggleSection); + }); + + function hideSidebar() { + html.classList.remove('sidebar-visible') + html.classList.add('sidebar-hidden'); + Array.from(sidebarLinks).forEach(function (link) { + link.setAttribute('tabIndex', -1); + }); + sidebarToggleButton.setAttribute('aria-expanded', false); + sidebar.setAttribute('aria-hidden', true); + try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { } + } + + // Toggle sidebar + sidebarToggleButton.addEventListener('click', function sidebarToggle() { + if (html.classList.contains("sidebar-hidden")) { + var current_width = parseInt( + document.documentElement.style.getPropertyValue('--sidebar-width'), 10); + if (current_width < 150) { + document.documentElement.style.setProperty('--sidebar-width', '150px'); + } + showSidebar(); + } else if (html.classList.contains("sidebar-visible")) { + hideSidebar(); + } else { + if (getComputedStyle(sidebar)['transform'] === 'none') { + hideSidebar(); + } else { + showSidebar(); + } + } + }); + + sidebarResizeHandle.addEventListener('mousedown', initResize, false); + + function initResize(e) { + window.addEventListener('mousemove', resize, false); + window.addEventListener('mouseup', stopResize, false); + html.classList.add('sidebar-resizing'); + } + function resize(e) { + var pos = (e.clientX - sidebar.offsetLeft); + if (pos < 20) { + hideSidebar(); + } else { + if (html.classList.contains("sidebar-hidden")) { + showSidebar(); + } + pos = Math.min(pos, window.innerWidth - 100); + document.documentElement.style.setProperty('--sidebar-width', pos + 'px'); + } + } + //on mouseup remove windows functions mousemove & mouseup + function stopResize(e) { + html.classList.remove('sidebar-resizing'); + window.removeEventListener('mousemove', resize, false); + window.removeEventListener('mouseup', stopResize, false); + } + + document.addEventListener('touchstart', function (e) { + firstContact = { + x: e.touches[0].clientX, + time: Date.now() + }; + }, { passive: true }); + + document.addEventListener('touchmove', function (e) { + if (!firstContact) + return; + + var curX = e.touches[0].clientX; + var xDiff = curX - firstContact.x, + tDiff = Date.now() - firstContact.time; + + if (tDiff < 250 && Math.abs(xDiff) >= 150) { + if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300)) + showSidebar(); + else if (xDiff < 0 && curX < 300) + hideSidebar(); + + firstContact = null; + } + }, { passive: true }); + + // Scroll sidebar to current active section + var activeSection = document.getElementById("sidebar").querySelector(".active"); + if (activeSection) { + // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView + activeSection.scrollIntoView({ block: 'center' }); + } +})(); + +(function chapterNavigation() { + document.addEventListener('keydown', function (e) { + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } + if (window.search && window.search.hasFocus()) { return; } + + switch (e.key) { + case 'ArrowRight': + e.preventDefault(); + var nextButton = document.querySelector('.nav-chapters.next'); + if (nextButton) { + window.location.href = nextButton.href; + } + break; + case 'ArrowLeft': + e.preventDefault(); + var previousButton = document.querySelector('.nav-chapters.previous'); + if (previousButton) { + window.location.href = previousButton.href; + } + break; + } + }); +})(); + +(function clipboard() { + var clipButtons = document.querySelectorAll('.clip-button'); + + function hideTooltip(elem) { + elem.firstChild.innerText = ""; + elem.className = 'fa fa-copy clip-button'; + } + + function showTooltip(elem, msg) { + elem.firstChild.innerText = msg; + elem.className = 'fa fa-copy tooltipped'; + } + + var clipboardSnippets = new ClipboardJS('.clip-button', { + text: function (trigger) { + hideTooltip(trigger); + let playground = trigger.closest("pre"); + return playground_text(playground); + } + }); + + Array.from(clipButtons).forEach(function (clipButton) { + clipButton.addEventListener('mouseout', function (e) { + hideTooltip(e.currentTarget); + }); + }); + + clipboardSnippets.on('success', function (e) { + e.clearSelection(); + showTooltip(e.trigger, "Copied!"); + }); + + clipboardSnippets.on('error', function (e) { + showTooltip(e.trigger, "Clipboard error!"); + }); +})(); + +(function scrollToTop () { + var menuTitle = document.querySelector('.menu-title'); + + menuTitle.addEventListener('click', function () { + document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' }); + }); +})(); + +(function controllMenu() { + var menu = document.getElementById('menu-bar'); + + (function controllPosition() { + var scrollTop = document.scrollingElement.scrollTop; + var prevScrollTop = scrollTop; + var minMenuY = -menu.clientHeight - 50; + // When the script loads, the page can be at any scroll (e.g. if you reforesh it). + menu.style.top = scrollTop + 'px'; + // Same as parseInt(menu.style.top.slice(0, -2), but faster + var topCache = menu.style.top.slice(0, -2); + menu.classList.remove('sticky'); + var stickyCache = false; // Same as menu.classList.contains('sticky'), but faster + document.addEventListener('scroll', function () { + scrollTop = Math.max(document.scrollingElement.scrollTop, 0); + // `null` means that it doesn't need to be updated + var nextSticky = null; + var nextTop = null; + var scrollDown = scrollTop > prevScrollTop; + var menuPosAbsoluteY = topCache - scrollTop; + if (scrollDown) { + nextSticky = false; + if (menuPosAbsoluteY > 0) { + nextTop = prevScrollTop; + } + } else { + if (menuPosAbsoluteY > 0) { + nextSticky = true; + } else if (menuPosAbsoluteY < minMenuY) { + nextTop = prevScrollTop + minMenuY; + } + } + if (nextSticky === true && stickyCache === false) { + menu.classList.add('sticky'); + stickyCache = true; + } else if (nextSticky === false && stickyCache === true) { + menu.classList.remove('sticky'); + stickyCache = false; + } + if (nextTop !== null) { + menu.style.top = nextTop + 'px'; + topCache = nextTop; + } + prevScrollTop = scrollTop; + }, { passive: true }); + })(); + (function controllBorder() { + menu.classList.remove('bordered'); + document.addEventListener('scroll', function () { + if (menu.offsetTop === 0) { + menu.classList.remove('bordered'); + } else { + menu.classList.add('bordered'); + } + }, { passive: true }); + })(); +})(); diff -Nru clamav-0.103.2+dfsg/docs/html/clipboard.min.js clamav-0.103.5+dfsg/docs/html/clipboard.min.js --- clamav-0.103.2+dfsg/docs/html/clipboard.min.js 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/clipboard.min.js 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.4 + * https://zenorocha.github.io/clipboard.js + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return function(n){var o={};function r(t){if(o[t])return o[t].exports;var e=o[t]={i:t,l:!1,exports:{}};return n[t].call(e.exports,e,e.exports,r),e.l=!0,e.exports}return r.m=n,r.c=o,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=0)}([function(t,e,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function o(t,e){for(var n=0;n .hljs { + color: var(--links); +} + +/* Menu Bar */ + +#menu-bar, +#menu-bar-hover-placeholder { + z-index: 101; + margin: auto calc(0px - var(--page-padding)); +} +#menu-bar { + position: relative; + display: flex; + flex-wrap: wrap; + background-color: var(--bg); + border-bottom-color: var(--bg); + border-bottom-width: 1px; + border-bottom-style: solid; +} +#menu-bar.sticky, +.js #menu-bar-hover-placeholder:hover + #menu-bar, +.js #menu-bar:hover, +.js.sidebar-visible #menu-bar { + position: -webkit-sticky; + position: sticky; + top: 0 !important; +} +#menu-bar-hover-placeholder { + position: sticky; + position: -webkit-sticky; + top: 0; + height: var(--menu-bar-height); +} +#menu-bar.bordered { + border-bottom-color: var(--table-border-color); +} +#menu-bar i, #menu-bar .icon-button { + position: relative; + padding: 0 8px; + z-index: 10; + line-height: var(--menu-bar-height); + cursor: pointer; + transition: color 0.5s; +} +@media only screen and (max-width: 420px) { + #menu-bar i, #menu-bar .icon-button { + padding: 0 5px; + } +} + +.icon-button { + border: none; + background: none; + padding: 0; + color: inherit; +} +.icon-button i { + margin: 0; +} + +.right-buttons { + margin: 0 15px; +} +.right-buttons a { + text-decoration: none; +} + +.left-buttons { + display: flex; + margin: 0 5px; +} +.no-js .left-buttons { + display: none; +} + +.menu-title { + display: inline-block; + font-weight: 200; + font-size: 2.4rem; + line-height: var(--menu-bar-height); + text-align: center; + margin: 0; + flex: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.js .menu-title { + cursor: pointer; +} + +.menu-bar, +.menu-bar:visited, +.nav-chapters, +.nav-chapters:visited, +.mobile-nav-chapters, +.mobile-nav-chapters:visited, +.menu-bar .icon-button, +.menu-bar a i { + color: var(--icons); +} + +.menu-bar i:hover, +.menu-bar .icon-button:hover, +.nav-chapters:hover, +.mobile-nav-chapters i:hover { + color: var(--icons-hover); +} + +/* Nav Icons */ + +.nav-chapters { + font-size: 2.5em; + text-align: center; + text-decoration: none; + + position: fixed; + top: 0; + bottom: 0; + margin: 0; + max-width: 150px; + min-width: 90px; + + display: flex; + justify-content: center; + align-content: center; + flex-direction: column; + + transition: color 0.5s, background-color 0.5s; +} + +.nav-chapters:hover { + text-decoration: none; + background-color: var(--theme-hover); + transition: background-color 0.15s, color 0.15s; +} + +.nav-wrapper { + margin-top: 50px; + display: none; +} + +.mobile-nav-chapters { + font-size: 2.5em; + text-align: center; + text-decoration: none; + width: 90px; + border-radius: 5px; + background-color: var(--sidebar-bg); +} + +.previous { + float: left; +} + +.next { + float: right; + right: var(--page-padding); +} + +@media only screen and (max-width: 1080px) { + .nav-wide-wrapper { display: none; } + .nav-wrapper { display: block; } +} + +@media only screen and (max-width: 1380px) { + .sidebar-visible .nav-wide-wrapper { display: none; } + .sidebar-visible .nav-wrapper { display: block; } +} + +/* Inline code */ + +:not(pre) > .hljs { + display: inline; + padding: 0.1em 0.3em; + border-radius: 3px; +} + +:not(pre):not(a) > .hljs { + color: var(--inline-code-color); + overflow-x: initial; +} + +a:hover > .hljs { + text-decoration: underline; +} + +pre { + position: relative; +} +pre > .buttons { + position: absolute; + z-index: 100; + right: 5px; + top: 5px; + + color: var(--sidebar-fg); + cursor: pointer; +} +pre > .buttons :hover { + color: var(--sidebar-active); +} +pre > .buttons i { + margin-left: 8px; +} +pre > .buttons button { + color: inherit; + background: transparent; + border: none; + cursor: inherit; +} +pre > .result { + margin-top: 10px; +} + +/* Search */ + +#searchresults a { + text-decoration: none; +} + +mark { + border-radius: 2px; + padding: 0 3px 1px 3px; + margin: 0 -3px -1px -3px; + background-color: var(--search-mark-bg); + transition: background-color 300ms linear; + cursor: pointer; +} + +mark.fade-out { + background-color: rgba(0,0,0,0) !important; + cursor: auto; +} + +.searchbar-outer { + margin-left: auto; + margin-right: auto; + max-width: var(--content-max-width); +} + +#searchbar { + width: 100%; + margin: 5px auto 0px auto; + padding: 10px 16px; + transition: box-shadow 300ms ease-in-out; + border: 1px solid var(--searchbar-border-color); + border-radius: 3px; + background-color: var(--searchbar-bg); + color: var(--searchbar-fg); +} +#searchbar:focus, +#searchbar.active { + box-shadow: 0 0 3px var(--searchbar-shadow-color); +} + +.searchresults-header { + font-weight: bold; + font-size: 1em; + padding: 18px 0 0 5px; + color: var(--searchresults-header-fg); +} + +.searchresults-outer { + margin-left: auto; + margin-right: auto; + max-width: var(--content-max-width); + border-bottom: 1px dashed var(--searchresults-border-color); +} + +ul#searchresults { + list-style: none; + padding-left: 20px; +} +ul#searchresults li { + margin: 10px 0px; + padding: 2px; + border-radius: 2px; +} +ul#searchresults li.focus { + background-color: var(--searchresults-li-bg); +} +ul#searchresults span.teaser { + display: block; + clear: both; + margin: 5px 0 0 20px; + font-size: 0.8em; +} +ul#searchresults span.teaser em { + font-weight: bold; + font-style: normal; +} + +/* Sidebar */ + +.sidebar { + position: fixed; + left: 0; + top: 0; + bottom: 0; + width: var(--sidebar-width); + font-size: 0.875em; + box-sizing: border-box; + -webkit-overflow-scrolling: touch; + overscroll-behavior-y: contain; + background-color: var(--sidebar-bg); + color: var(--sidebar-fg); +} +.sidebar-resizing { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} +.js:not(.sidebar-resizing) .sidebar { + transition: transform 0.3s; /* Animation: slide away */ +} +.sidebar code { + line-height: 2em; +} +.sidebar .sidebar-scrollbox { + overflow-y: auto; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + padding: 10px 10px; +} +.sidebar .sidebar-resize-handle { + position: absolute; + cursor: col-resize; + width: 0; + right: 0; + top: 0; + bottom: 0; +} +.js .sidebar .sidebar-resize-handle { + cursor: col-resize; + width: 5px; +} +.sidebar-hidden .sidebar { + transform: translateX(calc(0px - var(--sidebar-width))); +} +.sidebar::-webkit-scrollbar { + background: var(--sidebar-bg); +} +.sidebar::-webkit-scrollbar-thumb { + background: var(--scrollbar); +} + +.sidebar-visible .page-wrapper { + transform: translateX(var(--sidebar-width)); +} +@media only screen and (min-width: 620px) { + .sidebar-visible .page-wrapper { + transform: none; + margin-left: var(--sidebar-width); + } +} + +.chapter { + list-style: none outside none; + padding-left: 0; + line-height: 2.2em; +} + +.chapter ol { + width: 100%; +} + +.chapter li { + display: flex; + color: var(--sidebar-non-existant); +} +.chapter li a { + display: block; + padding: 0; + text-decoration: none; + color: var(--sidebar-fg); +} + +.chapter li a:hover { + color: var(--sidebar-active); +} + +.chapter li a.active { + color: var(--sidebar-active); +} + +.chapter li > a.toggle { + cursor: pointer; + display: block; + margin-left: auto; + padding: 0 10px; + user-select: none; + opacity: 0.68; +} + +.chapter li > a.toggle div { + transition: transform 0.5s; +} + +/* collapse the section */ +.chapter li:not(.expanded) + li > ol { + display: none; +} + +.chapter li.chapter-item { + line-height: 1.5em; + margin-top: 0.6em; +} + +.chapter li.expanded > a.toggle div { + transform: rotate(90deg); +} + +.spacer { + width: 100%; + height: 3px; + margin: 5px 0px; +} +.chapter .spacer { + background-color: var(--sidebar-spacer); +} + +@media (-moz-touch-enabled: 1), (pointer: coarse) { + .chapter li a { padding: 5px 0; } + .spacer { margin: 10px 0; } +} + +.section { + list-style: none outside none; + padding-left: 20px; + line-height: 1.9em; +} + +/* Theme Menu Popup */ + +.theme-popup { + position: absolute; + left: 10px; + top: var(--menu-bar-height); + z-index: 1000; + border-radius: 4px; + font-size: 0.7em; + color: var(--fg); + background: var(--theme-popup-bg); + border: 1px solid var(--theme-popup-border); + margin: 0; + padding: 0; + list-style: none; + display: none; +} +.theme-popup .default { + color: var(--icons); +} +.theme-popup .theme { + width: 100%; + border: 0; + margin: 0; + padding: 2px 10px; + line-height: 25px; + white-space: nowrap; + text-align: left; + cursor: pointer; + color: inherit; + background: inherit; + font-size: inherit; +} +.theme-popup .theme:hover { + background-color: var(--theme-hover); +} +.theme-popup .theme:hover:first-child, +.theme-popup .theme:hover:last-child { + border-top-left-radius: inherit; + border-top-right-radius: inherit; +} diff -Nru clamav-0.103.2+dfsg/docs/html/css/general.css clamav-0.103.5+dfsg/docs/html/css/general.css --- clamav-0.103.2+dfsg/docs/html/css/general.css 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/css/general.css 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,182 @@ +/* Base styles and content styles */ + +@import 'variables.css'; + +:root { + /* Browser default font-size is 16px, this way 1 rem = 10px */ + font-size: 62.5%; +} + +html { + font-family: "Open Sans", sans-serif; + color: var(--fg); + background-color: var(--bg); + text-size-adjust: none; + -webkit-text-size-adjust: none; +} + +body { + margin: 0; + font-size: 1.6rem; + overflow-x: hidden; +} + +code { + font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace !important; + font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */ +} + +/* Don't change font size in headers. */ +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + font-size: unset; +} + +.left { float: left; } +.right { float: right; } +.boring { opacity: 0.6; } +.hide-boring .boring { display: none; } +.hidden { display: none !important; } + +h2, h3 { margin-top: 2.5em; } +h4, h5 { margin-top: 2em; } + +.header + .header h3, +.header + .header h4, +.header + .header h5 { + margin-top: 1em; +} + +h1:target::before, +h2:target::before, +h3:target::before, +h4:target::before, +h5:target::before, +h6:target::before { + display: inline-block; + content: "»"; + margin-left: -30px; + width: 30px; +} + +/* This is broken on Safari as of version 14, but is fixed + in Safari Technology Preview 117 which I think will be Safari 14.2. + https://bugs.webkit.org/show_bug.cgi?id=218076 +*/ +:target { + scroll-margin-top: calc(var(--menu-bar-height) + 0.5em); +} + +.page { + outline: 0; + padding: 0 var(--page-padding); + margin-top: calc(0px - var(--menu-bar-height)); /* Compensate for the #menu-bar-hover-placeholder */ +} +.page-wrapper { + box-sizing: border-box; +} +.js:not(.sidebar-resizing) .page-wrapper { + transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */ +} + +.content { + overflow-y: auto; + padding: 0 15px; + padding-bottom: 50px; +} +.content main { + margin-left: auto; + margin-right: auto; + max-width: var(--content-max-width); +} +.content p { line-height: 1.45em; } +.content ol { line-height: 1.45em; } +.content ul { line-height: 1.45em; } +.content a { text-decoration: none; } +.content a:hover { text-decoration: underline; } +.content img, .content video { max-width: 100%; } +.content .header:link, +.content .header:visited { + color: var(--fg); +} +.content .header:link, +.content .header:visited:hover { + text-decoration: none; +} + +table { + margin: 0 auto; + border-collapse: collapse; +} +table td { + padding: 3px 20px; + border: 1px var(--table-border-color) solid; +} +table thead { + background: var(--table-header-bg); +} +table thead td { + font-weight: 700; + border: none; +} +table thead th { + padding: 3px 20px; +} +table thead tr { + border: 1px var(--table-header-bg) solid; +} +/* Alternate background colors for rows */ +table tbody tr:nth-child(2n) { + background: var(--table-alternate-bg); +} + + +blockquote { + margin: 20px 0; + padding: 0 20px; + color: var(--fg); + background-color: var(--quote-bg); + border-top: .1em solid var(--quote-border); + border-bottom: .1em solid var(--quote-border); +} + + +:not(.footnote-definition) + .footnote-definition, +.footnote-definition + :not(.footnote-definition) { + margin-top: 2em; +} +.footnote-definition { + font-size: 0.9em; + margin: 0.5em 0; +} +.footnote-definition p { + display: inline; +} + +.tooltiptext { + position: absolute; + visibility: hidden; + color: #fff; + background-color: #333; + transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */ + left: -8px; /* Half of the width of the icon */ + top: -35px; + font-size: 0.8em; + text-align: center; + border-radius: 6px; + padding: 5px 8px; + margin: 5px; + z-index: 1000; +} +.tooltipped .tooltiptext { + visibility: visible; +} + +.chapter li.part-title { + color: var(--sidebar-fg); + margin: 5px 0px; + font-weight: bold; +} + +.result-no-output { + font-style: italic; +} diff -Nru clamav-0.103.2+dfsg/docs/html/css/print.css clamav-0.103.5+dfsg/docs/html/css/print.css --- clamav-0.103.2+dfsg/docs/html/css/print.css 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/css/print.css 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,54 @@ + +#sidebar, +#menu-bar, +.nav-chapters, +.mobile-nav-chapters { + display: none; +} + +#page-wrapper.page-wrapper { + transform: none; + margin-left: 0px; + overflow-y: initial; +} + +#content { + max-width: none; + margin: 0; + padding: 0; +} + +.page { + overflow-y: initial; +} + +code { + background-color: #666666; + border-radius: 5px; + + /* Force background to be printed in Chrome */ + -webkit-print-color-adjust: exact; +} + +pre > .buttons { + z-index: 2; +} + +a, a:visited, a:active, a:hover { + color: #4183c4; + text-decoration: none; +} + +h1, h2, h3, h4, h5, h6 { + page-break-inside: avoid; + page-break-after: avoid; +} + +pre, code { + page-break-inside: avoid; + white-space: pre-wrap; +} + +.fa { + display: none !important; +} diff -Nru clamav-0.103.2+dfsg/docs/html/css/variables.css clamav-0.103.5+dfsg/docs/html/css/variables.css --- clamav-0.103.2+dfsg/docs/html/css/variables.css 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/css/variables.css 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,133 @@ + +/* Globals */ + +:root { + --sidebar-width: 300px; + --page-padding: 15px; + --content-max-width: 750px; + --menu-bar-height: 50px; +} + +/* Themes */ + +.clamav { + --bg: #222222; + --fg: #ffffff; + + --sidebar-bg: #1c1c1c; + --sidebar-fg: #c5c5c5; + --sidebar-non-existant: #5c6773; + --sidebar-active: #ff4646; + --sidebar-spacer: #2d334f; + + --scrollbar: var(--sidebar-fg); + + --icons: #fff; + --icons-hover: #b7b9cc; + + --links: #0096cf; + + --inline-code-color: #e6e1cf; + + --theme-popup-bg: #14191f; + --theme-popup-border: #5c6773; + --theme-hover: #1c1c1c; + + --quote-bg: #292c2f; + --quote-border: hsl(226, 15%, 22%); + + --table-border-color: #35393d; + --table-header-bg: hsl(195, 7%, 21%); + --table-alternate-bg: #292c2f; + + --searchbar-border-color: #848484; + --searchbar-bg: #424242; + --searchbar-fg: #fff; + --searchbar-shadow-color: #d4c89f; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #252932; + --search-mark-bg: #ff4646; +} + +.clamav_light { + --bg: #fafafa; + --fg: #262625; + + --sidebar-bg: #1c1c1c; + --sidebar-fg: #c5c5c5; + --sidebar-non-existant: #505254; + --sidebar-active: #ff4646; + --sidebar-spacer: #45373a; + + --scrollbar: var(--sidebar-fg); + + --icons: #737480; + --icons-hover: #262625; + + --links: #2b79a2; + + --inline-code-color: #6e6b5e; + + --theme-popup-bg: #e1e1db; + --theme-popup-border: #b38f6b; + --theme-hover: #c5c5c5; + + --quote-bg: hsl(60, 5%, 75%); + --quote-border: hsl(60, 5%, 70%); + + --table-border-color: hsl(60, 9%, 82%); + --table-header-bg: #b3a497; + --table-alternate-bg: hsl(60, 9%, 84%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #fafafa; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #dec2a2; + --search-mark-bg: #ff4646; +} + +@media (prefers-color-scheme: dark) { + .clamav_light.no-js { + --bg: #222222; + --fg: #ffffff; + + --sidebar-bg: #1c1c1c; + --sidebar-fg: #c5c5c5; + --sidebar-non-existant: #5c6773; + --sidebar-active: #ff4646; + --sidebar-spacer: #2d334f; + + --scrollbar: var(--sidebar-fg); + + --icons: #fff; + --icons-hover: #b7b9cc; + + --links: #0096cf; + + --inline-code-color: #e6e1cf; + + --theme-popup-bg: #14191f; + --theme-popup-border: #5c6773; + --theme-hover: #1c1c1c; + + --quote-bg: #292c2f; + --quote-border: hsl(226, 15%, 22%); + + --table-border-color: #35393d; + --table-header-bg: hsl(195, 7%, 21%); + --table-alternate-bg: #292c2f; + + --searchbar-border-color: #848484; + --searchbar-bg: #424242; + --searchbar-fg: #fff; + --searchbar-shadow-color: #d4c89f; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #252932; + --search-mark-bg: #ff4646; + } +} diff -Nru clamav-0.103.2+dfsg/docs/html/editor.js clamav-0.103.5+dfsg/docs/html/editor.js --- clamav-0.103.2+dfsg/docs/html/editor.js 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/editor.js 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,29 @@ +"use strict"; +window.editors = []; +(function(editors) { + if (typeof(ace) === 'undefined' || !ace) { + return; + } + + Array.from(document.querySelectorAll('.editable')).forEach(function(editable) { + let display_line_numbers = window.playground_line_numbers || false; + + let editor = ace.edit(editable); + editor.setOptions({ + highlightActiveLine: false, + showPrintMargin: false, + showLineNumbers: display_line_numbers, + showGutter: display_line_numbers, + maxLines: Infinity, + fontSize: "0.875em" // please adjust the font size of the code in general.css + }); + + editor.$blockScrolling = Infinity; + + editor.getSession().setMode("ace/mode/rust"); + + editor.originalCode = editor.getValue(); + + editors.push(editor); + }); +})(window.editors); diff -Nru clamav-0.103.2+dfsg/docs/html/elasticlunr.min.js clamav-0.103.5+dfsg/docs/html/elasticlunr.min.js --- clamav-0.103.2+dfsg/docs/html/elasticlunr.min.js 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/elasticlunr.min.js 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,10 @@ +/** + * elasticlunr - http://weixsong.github.io + * Lightweight full-text search engine in Javascript for browser search and offline search. - 0.9.5 + * + * Copyright (C) 2017 Oliver Nightingale + * Copyright (C) 2017 Wei Song + * MIT Licensed + * @license + */ +!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();o + + + + + Signature Database (CVD) - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

ClamAV Virus Database FAQ

+

The following FAQ should help you understand how ClamAV CVD signature databases work and any issues you may experience working with them.

+

If you're unable to find an answer to your question in the FAQ, you can seek help in our clamav-users mailing list, on our Discord server, or by submitting an issue on GitHub. The mailing list archives and existing Github issues (open or closed) may also have an answer to your question.

+

Please consider contributing answered questions back to this FAQ, and improving the quality of these answers, by submitting pull requests to our documentation source repository.

+

How do I keep my virus database up to date?

+

ClamAV comes with FreshClam, a tool which periodically checks for new database releases and keeps your database up to date. It is encouraged that you update to at least version 0.103.2, which respects our bandwidth limitations.

+

How often is the virus database updated?

+

The virus database is usually updated once or twice per day. Sign up for our VirusDB mailing list to see our response times to new threats. The virusdb team tries to keep up with the latest threats in the wild. You can contribute to make the virusdb updating process more efficient by submitting samples of viruses via our "Contact" page on ClamAV.net.

+

The last CVD update crashed my ClamAV installation. Why?

+

Before publishing a CVD update, we verify that it can be correctly loaded by the last two stable release series of ClamAV.

+

The last CVD update detects a lot of false positives on my system. Why?

+

Before publishing a CVD update, we test it for false positives using the latest stable release of ClamAV. If you want to avoid problems with false positives, you must run the latest stable version of ClamAV. Please stay tuned to our EOL policy for what versions are actively supported.

+

I tried to submit a sample through the web interface, but it said the sample is already recognized by ClamAV. My ClamScan tells me it's not. I have already updated my database and ClamAV engine, what's wrong with my setup?

+

Please run ClamScan with the --alert-broken option. Also check that FreshClam and ClamScan are using the same path for storing/reading the database.

+

I found an infected file in my HD/USB/mailbox, but ClamAV doesn't recognize it yet. Can you help me?

+

Our virus database is kept up to date with the help of the community. Whenever you find a new virus which is not detected by ClamAV you should complete this form. The virusdb team will review your submission and update the database if necessary. Before submitting a new sample: - check that the value of DatabaseDirectory, in both clamd.conf and freshclam.conf, is the same - and update your database by running FreshClam to ensure you've scanned it with the latest virus database.

+

I'm running ClamAV on a lot of clients on my local network. Can I serve the cvd files from a local server so that each client doesn't have to download them from your servers?

+

Sure, you can find more details on our Private Local Mirror page.

+
    +
  1. +

    If you want to take advantage of incremental updates, install a proxy server and then configure your FreshClam clients to use it (watch for the HTTPProxyServer parameter in freshclam.conf).

    +
  2. +
  3. +

    The second possible solution is to:

    +
  4. +
+
    +
  • +

    Configure a local webserver on one of your machines (say machine1.mylan)

    +
  • +
  • +

    Let FreshClam download the *.cvd files from http://database.clamav.net to the webserver's DocumentRoot.

    +
  • +
  • +

    Finally, change freshclam.conf on your clients so that it includes:

    +
    DatabaseMirror machine1.mylan
    +
    +ScriptedUpdates off
    +
    +

    First the database will be downloaded to the local webserver and then the other clients on the network will update their copy of the database from it.

    +

    Important: For this to work, you have to add ScriptedUpdates off on all of your machines!

    +
  • +
+

I can't wait for you to update the database! I need to use the new signature NOW!

+

No problem, save your own signatures in a text file with the appropriate extension (see our signature writing documentation for more information). Put the signature files in the same directory where the .cvd files are located. This is typically /usr/local/share/clamav or /var/lib/clamav. ClamAV will load it after the official .cvd files. You do not need to sign your custom database files.

+

Can I download the virus database manually?

+

This practice is discouraged, please use either FreshClam or CVDUpdate to update your definitions. Please check out our FreshClam FAQ and our Private Mirror Documentation for further information and links to CVDUpdate.

+

I am getting error codes such as 403, 429, etc when FreshClam (or other update system) attempts to download updates

+

Are you attempting to download safebrowsing.cvd and getting a 403? If so, take a look at this blog post, otherwise check out our Freshclam FAQ under the section on "Error Codes".

+
+

For other questions regarding issues with FreshClam, see our FreshClam Troubleshooting FAQ.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/faq/faq-eol.html clamav-0.103.5+dfsg/docs/html/faq/faq-eol.html --- clamav-0.103.2+dfsg/docs/html/faq/faq-eol.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/faq/faq-eol.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,308 @@ + + + + + + ClamAV EOL Policy - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

End of Life (EOL) Policy

+

This document describes the End of Life (EOL) policy for Long Term Support (LTS) feature releases and for regular (non-LTS) feature releases.

+

Skip to our Version Support Matrix to quickly check if your version is still supported.

+
+

Disclaimer: If this policy has to change due to a compatibility problem that prohibits the use of new detection technology, or impacts the stability of ClamAV infrastructure, we will announce the end of life for those versions four months before they become unsupported.

+
+

Long Term Support (LTS) Feature Releases

+

ClamAV 0.103 is the first Long Term Support (LTS) feature release.

+

LTS feature releases will be supported for at least three (3) years from the initial publication date of that LTS feature version. In other words, support for the LTS release "X.Y" starts when version "X.Y.0" is published and ends three years after.

+

Each LTS feature release will be supported with critical patch versions and access to download signatures for the duration of the three year support period.

+

A new LTS feature release will be identified approximately every two (2) years.

+

Users must stay up-to-date with the latest patch versions for continued support. As of November 3, that means version 0.103.4.

+

Regular (non-LTS) Feature Releases

+

Non-LTS feature releases will be supported with critical patch versions for at least four (4) months from the initial publication date of the next feature release or until the next-next feature release is published.

+

Non-LTS feature releases will be allowed access to download signatures until at least four (4) months after the next-next feature release is published.

+

Definitions

+
    +
  • +

    "feature release" -- A version starting with MAJOR.MINOR.0 to include all PATCH versions.

    +

    For example: 0.103.0 and 0.103.1 are both "patch versions" within the same "feature release".

    +
  • +
  • +

    "patch version" -- A specific MAJOR.MINOR.PATCH version.

    +

    For example: 0.103.1 is a "patch version" in the 0.103 "feature release".

    +
  • +
  • +

    "end of life" (EOL) -- The date after which the ClamAV Team will no longer support a feature version in any way.

    +

    After this point, all patch versions of that release may be blocked from downloading official signature content.

    +
  • +
  • +

    "long term support (LTS) release" -- A feature release that will get critical patch versions for an extended period.

    +

    The latest patch version will continue to get access to download signature databases for the duration of the support period. See above for policy details.

    +
  • +
  • +

    "regular (non-LTS) release" -- A feature release that will only be supported until a little after the next feature release.

    +

    The latest patch version will continue to get access to download signature databases a little longer than that, but users are encouraged to upgrade and may be vulnerable if a security patch is only published for the next feature release and they fail to upgrade. See above for policy details.

    +
  • +
  • +

    "support" -- The ClamAV project defines "support" in several ways:

    +
      +
    1. +

      Critical patch support:

      +

      The ClamAV Team provides critical patch versions for supported feature releases(**).

      +

      The Application Binary Interface (ABI) shall not change between patch versions within a given feature release. That is, the SONAMEs for the ClamAV libraries will remain the same and changes in a patch version will not break compatibility with older library versions.

      +

      Users are responsible for updating to the latest patch version for continued support.

      +
    2. +
    3. +

      Signature Database (CVD) Access:

      +

      Supported releases are allowed free access to download the latest signature databases.

      +

      Users must keep up-to-date with the latest patch version to maintain access to the official signature database content.

      +

      We reserve the right to block older/problematic patch versions 4 months after the release of a newer patch version.

      +
    4. +
    5. +

      New-Signature Load/Functional Testing:

      +

      The ClamAV Team will load-test new sigantures for functional correctness on all versions that are allowed access to download the official signature databases.

      +
    6. +
    7. +

      New-Signature False Positive Testing:

      +

      The ClamAV Team will perform false positive testing for new signature content, but only using the latest patch version of the latest feature release. False positive testing requires scanning large data sets. It is more time consuming than functional testing.

      +
    8. +
    +

    Cisco does not offer paid technical support or paid extended long term support for ClamAV.

    +
  • +
+

Version Support Matrix

+
+

Note: This markdown table is generated from a spreadsheet using this tool.

+
+ + + + + + + +
Feature releaseFirst PublishedLatest patch versionExpected End of Life (EOL)Signature load testing untilSignature FP testing untilDB downloads allowed untilPatch versions continue until
0.104Sep-3 20210.104.10.106 + 4 months0.106 + 4 months0.106 + 4 months0.105 + 4 months, or 0.106
0.103 LTSSep-14 20200.103.4Sep-14 2023Sep-14 20230.104 publishedSep-14 2023Sep-14 2023
0.102Oct-2 20190.102.4Jan-3 2022 (0.104 + 4 mo.)Jan-3 2022Jan-3 2022
0.101Dec-3 20180.101.5Jan-3 2022Jan-3 2022Jan-3 2022
0.100Apr-9 20180.100.3Oct-29 2021Oct-29 2021Oct-29 2021
0.99Dec-1 20150.99.4Mar-1 2021
+

Currently, every version from ClamAV 0.100 and down, including all patch versions, are unsupported, and are actively blocked from downloading new updates.

+

Additional Detail About Critical Patch Support

+

Like all bugs, security patches are first prepared for our upcoming feature release. Only security patches and other critical fixes or critical improvements are backported to previous feature releases.

+

The ClamAV Team is small and can't afford to publish patch versions for every release. To keep up momentum crafting new features and other improvements, our policy is to backport no further than 1 or 2 of the latest feature releases plus the Long Term Support (LTS) feature releases.

+

Critical Patches After a Breaking Change

+

On rare occasion we have made breaking changes to the way that users or other software interact with ClamAV or libclamav. The 0.101.0 release was one such example where we made a breaking change to the libclamav programming API. When this happens, we will provide extra time for users to upgrade to a newer feature release before we discontinue support for the older feature release.

+

The amount of extra time before we discontinue support will vary depending on the severity of the breaking change and the level of difficulty to upgrade.

+

Examples

+

Security Patches Less than four (4) Months After a Feature Release

+

If the non-disclosure / release date for a security patch falls within four (4) months since the previous feature release was published, we will craft a security patch version for the future feature release, the current feature release, the previous feature release, and any LTS feature releases.

+
+

Example: Let's say ClamAV 0.105.0 was just released and development begins on 0.106. But shortly after the release or as we're preparing for the release, it is discovered that 0.105.0 contains one or more security issues.

+

We understand with such a recent release, not everyone has had time to verify that they can indeed upgrade without some other supporting changes to their system or product.

+

In this situation, we would prepare a security patch version for:

+
    +
  • the 0.105 feature release (eg. 0.105.1),
  • +
  • for the 0.104 feature release (eg. 0.104.3),
  • +
  • and for the 0.103 LTS feature release.
  • +
+

Once the critical patch versions have been published, the same or equivalent fixes will be merged into the main branch for inclusion into the next feature release (0.106.0).

+
+

Security Patches More than four (4) Months After a Feature Release Has Been Available

+

If the non-disclosure / release date for a security patch falls after four (4) months since the previous feature release was published, we will only craft a security patch version for the future feature release, the current feature release, and any LTS feature releases.

+
+

Example: If 0.105.0 was released in January and a security issue was found in March, but the non-disclosure agreement allowed for the bug to be patched after the standard 90 days, then a security patch release will likely be prepared for release in early June.

+

This would exceed our 4-month policy, so we would publish the fix:

+
    +
  • in 0.105 (eg. 0.105.1),
  • +
  • and in 0.103 LTS, ...
  • +
+

... but would not publish a patch version for the 0.104 feature release.

+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/faq/faq-freshclam.html clamav-0.103.5+dfsg/docs/html/faq/faq-freshclam.html --- clamav-0.103.2+dfsg/docs/html/faq/faq-freshclam.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/faq/faq-freshclam.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,275 @@ + + + + + + FreshClam (Signature Updater) - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

FreshClam FAQ

+

The following FAQ should help you understand why freshclam may have failed to fetch the latest updates.

+

If you're unable to find an answer to your question in the FAQ, you can seek help in our clamav-users mailing list, on our Discord server, or by submitting an issue on GitHub. The mailing list archives and existing Github issues (open or closed) may also have an answer to your question.

+

Please consider contributing answered questions back to this FAQ, and improving the quality of these answers, by submitting pull requests to our documentation source repository.

+

Failed to get information about user "clamav"

+

If you've installed ClamAV and are running Freshclam as root or with sudo but don't have a clamav user account for Freshclam to run as, you may encounter this error:

+
ERROR: Failed to get information about user "clamav".
+Create the "clamav" user account for freshclam to use, or set the DatabaseOwner config option in freshclam.conf to a different user.
+For more information, see https://docs.clamav.net/manual/Installing/Installing-from-source-Unix.html
+ERROR: Initialization error!
+
+

You can resolve this issue by following these steps to create a clamav service account.

+

Can't create freshclam.dat in /usr/local/share/clamav

+

If the database directory exists but is not owned by the user account that Freshclam is being run as, you may encounter this error:

+
ERROR: Can't create freshclam.dat in /usr/local/share/clamav
+Hint: The database directory must be writable for UID 1000 or GID 1000
+ERROR: Failed to save freshclam.dat!
+WARNING: Failed to create a new freshclam.dat!
+ERROR: initialize: libfreshclam init failed.
+ERROR: Initialization error!
+
+

To resolve this issue, change ownership of the directory to the appropriate user account.

+

For example if you're running Freshclam under your user account "bob", something like this may resolve the issue:

+
sudo chown -R bob /usr/local/share/clamav
+
+

If running Freshclam as root (or with sudo), then Freshclam will try to automatically switch to run as the clamav user, or whichever user is specified as the DatabaseOwner in freshclam.conf. Run this to resolve the issue:

+
sudo chown -R clamav /usr/local/share/clamav
+
+

Problem with the SSL CA cert

+

On Linux/Unix systems, Freshclam uses openssl to validate certificates for TLS connections. It relies on finding the openssl CA bundle in the default path. You may encounter the following error if you're missing the ca-certificates package, or on some distributions where the path to the CA bundle has been customized:

+
WARNING: Download failed (77) WARNING:  Message: Problem with the SSL CA cert (path? access rights?
+WARNING: Can't download daily.cvd from https://database.clamav.net/daily.cvd
+
+

First you may try installing the ca-certificates package. If that is already installed, or the issue persists, then you may need to set the CURL_CA_BUNDLE environment variable to direct Freshclam to the path of the CA bundle on your system.

+

For example, on openSUSE you may need to set CURL_CA_BUNDLE=/var/lib/ca-certificates/ca-bundle.pem. You can test this by running:

+
CURL_CA_BUNDLE=/var/lib/ca-certificates/ca-bundle.pem freshclam
+
+

If this resolves the issue, you may wish to export the CURL_CA_BUNDLE variable in your .bashrc file or the equivalent for your shell.

+
+

Tip: The CURL_CA_BUNDLE variable is also used by ClamSubmit.

+
+

Invalid DNS reply. Falling back to HTTP mode or ERROR: Can't query current.cvd.clamav.net

+

There is a problem with your DNS server. Please check the entries in /etc/resolv.conf and verify that you can resolve the TXT record manually:

+

$ host -t txt current.cvd.clamav.net

+

If you can't, it means your network is broken. You'll be still able to download the updates, but you'll waste a lot of bandwidth checking for updates. Please note that some not RFC compliant DNS servers (namely the one shipped with the Alcatel (now Thomson) SpeedTouch 510 modem) can't resolve TXT record. If that's the case, please recompile ClamAV with the flag --enable-dns-fix.

+

ERROR: Connection with ??? failed

+

Either your dns servers are not working or you are blocking port 53/tcp. You should manually check that you can resolve hostnames with:

+

$ host database.clamav.net

+

If it doesn't work, check your dns settings in /etc/resolv.conf. If it works, check that you can receive dns answers longer than 512 bytes, e.g. check that your firewall is not blocking packets which originate from port 53/tcp. An easy way to find it out is:

+

$ dig @ns1.clamav.net db.us.big.clamav.net

+

WARNING: Incremental update failed, trying to download daily.cvd

+

For some reason, incremental update failed. FreshClam can recover from this situation by downloading the whole daily.cvd.

+

Database update process failed: Downloaded database had lower version than advertised

+

For some reason, the content delivery network is not serving the latest updates yet. If you experience this problem, please report the issue on GitHub Issues.

+

Update failed. Your network may be down or the ClamAV database content delivery network is experiencing an outage

+

It's not your lucky day. Your network may be down or the ClamAV database content delivery network is experiencing an outage. Please wait a few minutes and try again. Remember to pass the -v option to freshclam.

+

Update failed. Updating too frequently with an outdated version

+

Starting from ClamAV 0.9x, whenever your ClamAV engine becomes outdated and the difference between the functionality level required by the CVD and the functionality level supported by your ClamAV engine is more than 3, freshclam will refuse to check for updates more often than 6 times per day.

+

The reason for this is that bandwidth can be expensive. It is not helpful to generate extra traffic on our content delivery network if you cannot take advantage of all the signatures anyway. If you really care about catching as much malware as possible and you want to check for updates more often than 6 times per day, then you should also not run such an old version of ClamAV.

+

Your ClamAV installation is OUTDATED

+

This message does NOT indicate that you are unable to download the latest CVD update! You'll get this message whenever a new version of ClamAV is released. In order to detect all the latest viruses, it's not enough to keep your database up to date. You also need to run the latest version of the scanner.

+

WARNING: Current functionality level = 1, required = 2

+

The functionality level of the database determines which scanner engine version is required to use all of its signatures. If you don't upgrade immediately you will still be able to download the latest CVD updates but the engine won't be able to use ALL of them.

+

Ignoring mirror <IP> (has connected too many times with an outdated version)

+

If you are experiencing this problem, please do the following: Stop the freshclam daemon if it's running, delete both mirrors.dat and daily.cvd, then restart the freshclam daemon. FreshClam will then download a new daily.cvd and will be up-to-date.

+

HTTP Error Codes

+

If you are receiving a 403, 503, or 1020 error codes when downloading from Cloudflare, then you are either explicitly blocked, using an EOL'ed version of ClamAV or you are downloading incorrectly.

+

If FreshClam is failing and you're not sure why, you may run freshclam -v for "Verbose Mode" to see the HTTP request & response details (ClamAV 0.102+).

+

After checking that you are using a current version of ClamAV, please discontinue whatever method of download you are using and immediately move to using either FreshClam or cvdupdate. These are the two supported methods for downloading AV updates from ClamAV. All other methods may be rate limited, or blocked at our discretion. Use of Wget, Curl, or other command line tools that are scripted are explicitly denied.

+

If you are receiving a 429, that means you are rate limited. You're downloading too fast or too much. Please use Freshclam or cvdupdate. If you are using a shared hosting provider, like Amazon AWS, Google Cloud Computing, Oracle, Azure, etc, you will most likely be rate limited, however cvdupdate should handle this gracefully. If you continue to receive these, we recommend you try from a different external IP address.

+

If you are receiving a 403 specifically on the safebrowsing.cvd file, please read this blog post immediately!

+

Are you running a version of FreshClam/ClamAV lower than 0.103.2? If so, you should immediately upgrade to at least 0.103.2.

+

If you have checked all of the above and you are still seeing errors, please open a ticket using the below link.

+ +

Please report freshclam update failures or issues on GitHub Issues.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/faq/faq.html clamav-0.103.5+dfsg/docs/html/faq/faq.html --- clamav-0.103.2+dfsg/docs/html/faq/faq.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/faq/faq.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,224 @@ + + + + + + Frequently Asked Questions - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Clam AntiVirus Frequently Asked Questions

+

If you're unable to find an answer to your question in the FAQ, you can seek help in our clamav-users mailing list, on our Discord server, or by submitting an issue on GitHub. The mailing list archives and existing Github issues (open or closed) may also have an answer to your question.

+

Please consider contributing answered questions back to this FAQ, and improving the quality of these answers, by submitting pull requests to our documentation source repository.

+

Table Of Contents

+
    +
  1. Selecting the Right Version of ClamAV for You
  2. +
  3. FreshClam (Signature Updater)
  4. +
  5. Signature Database (CVD)
  6. +
  7. Misc
  8. +
  9. Mailing Lists
  10. +
  11. Safe Browsing
  12. +
  13. Troubleshooting
  14. +
  15. Interpreting Scan Alerts
  16. +
  17. Upgrading
  18. +
  19. Win32
  20. +
  21. ClamAV EOL Policy
  22. +
  23. PUA (Potentially Unwanted Application)
  24. +
  25. Ignore
  26. +
  27. Uninstall
  28. +
+

ClamAV FAQ © 2021 Cisco Systems, Inc.

+

This document is distributed under the terms of the GNU General Public License v2.

+

Clam AntiVirus is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

+

ClamAV and Clam AntiVirus are trademarks of Cisco Systems, Inc.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/faq/faq-ignore.html clamav-0.103.5+dfsg/docs/html/faq/faq-ignore.html --- clamav-0.103.2+dfsg/docs/html/faq/faq-ignore.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/faq/faq-ignore.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,209 @@ + + + + + + Ignore - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

How do I ignore a ClamAV signature?

+

Creating an ignore file

+

Change Directory into the location where your ClamAV databases are stored. Create a file called ignore_list.ign2, for example, like this:

+

touch ignore_list.ign2

+

Ignore individual signatures

+

Place the signatures you'd like to ignore, each on it's own line, within the file ignore_list.ign2.

+

For example:

+

Signature.Ignore-1

+

Signature.Ignore-2

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/faq/faq-misc.html clamav-0.103.5+dfsg/docs/html/faq/faq-misc.html --- clamav-0.103.2+dfsg/docs/html/faq/faq-misc.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/faq/faq-misc.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,248 @@ + + + + + + Misc - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Miscellaneous FAQ

+

If you're unable to find an answer to your question in our FAQ, you can seek help in our clamav-users mailing list, on our Discord server, or by submitting an issue on GitHub. The mailing list archives and existing Github issues (open or closed) may also have an answer to your question.

+

Please consider contributing answered questions back to this FAQ, and improving the quality of these answers, by submitting pull requests to our documentation source repository.

+

I see you have Bugzilla AND GitHub Issues. Which one should I use?

+

Please use GitHub Issues. We're slowly phasing out Bugzilla. But please read the Security Policy and do not disclose anything in a ticket that should be kept private or that may represent a vulnerability.

+

I reported a bug on Bugzilla, but no one can see it. What do I do?

+

Bugs reported to ClamAV's Bugzilla ticket tracker are private by default. Our policy is to make tickets public if they are not security-related. If you believe your bug report isn't security-related, please ping the ticket requesting that the ticket be made public. Chances are high that we simply forgot to remove the tag when the report came in.

+

Where can I find the bug ticket for a security bug fix?

+

Our policy is to make security-related bugs public either: +A. fourteen (14) days after a security patch version has been published with a fix for the bug, or +B. after the non-disclosure agreement with a vulnerability reporter has expired and the bug details are otherwise already public.

+

This policy serves to reduce the risk that a malicious party would find enough details in the ticket to craft their own exploit for the bug before users have had an opportunity to upgrade to a patched version.

+

Where can I find a test file to prove that a security bug doesn't affect me or has been fixed in my version?

+

We do not share test files for security bugs. Sharing these files puts users at risk for at least two reasons:

+
    +
  1. Sharing a test file outside of our organization increases the risk of it falling into the wrong hands and being used to craft an exploit.
  2. +
  3. Test files generated by a fuzzer are designed to demonstrate a bug in a specific test environment and do the bare minimum necessary to trigger the bug. Scanning a test file outside of that environment is unlikely to demonstrate the issue EVEN THOUGH THE ISSUE STILL EXISTS. A clever adversary may very well be able to craft a bigger and better exploit for that issue that does affect your unpatched system. Testing with the original fuzzer-generated file is most likely to give you a false sense of security.
  4. +
+

Can phishing be considered one kind of spam? ClamAV should not detect it as some kind of malware.

+

Starting from release 0.90, ClamAV allows you to choose whether to detect phish as some kind of malware or not. This should put an end to the endless threads on our mailing lists. So long, and thanks for all the phish.

+

Why is my legitimate HTML newsletter/email detected by ClamAV as Phishing.Heuristics.Email.SpoofedDomain?

+

If it contains links in the form of href="http://yourdomain.example.tld"> otherdomain.tld, where otherdomain.tld (ProtectedDomain) doesn't belong to you and is listed in ClamAV database (like amazon.com, ebay.com, ...) then ClamAV detects it as a phishing attempt.

+

My legitimate emails from yourdomain.tld are detected as Phishing.Heuristics.Email.SpoofedDomain

+

Please submit a sample, marking it as a false positive, phishing. If it's really a false positive, we will add an allow list entry for it.

+

Can I convert the new database format to the old one?

+

Yes, install a recent version of sigtool and run:

+

sigtool --unpack-current daily.cvd; sigtool --unpack-current main.cvd

+

How do I read inside the CVD files?

+

See previous FAQ.

+

I'm using ClamAV in a production environment and a brand new virus is not being recognized by ClamAV. How long do I have to wait before ClamAV can start filtering the virus?

+

No time at all! Find a signature for that virus and modify your virus database accordingly (see our signature writing documentation for details). +Remember to submit the sample to the virusdb team.

+

Why is ClamAV calling the XXX virus with another name?

+

This usually happens when we add a signature before other AV vendors. No well-known name is available at that moment so we have to invent one. Renaming the virus after a few days would just confuse people more, so we usually keep on using our name for that virus. The only exception is when a new name is established soon after the signature addition.

+

I get many false positives of Oversized.zip

+

Whenever a file exceeds ArchiveMaxCompressionRatio (see clamd.conf man page), it's considered a logic bomb and marked as Oversized.zip. Try increasing your ArchiveMaxCompressionRatio setting.

+

What is PUA? I get a lot of false positives named PUA.

+

With the release of ClamAV 0.91.2 we introduce the option to scan for Potentially Unwanted Applications.

+

The PUA database contains detection for applications that are not malicious by itself but can be used in a malicious or unwanted context. As an example: A tool to retrieve passwords from a system can be useful as long as the person who uses it, is authorized to do so. However, the same tool can be used to steal passwords from a system. To make use of the PUA database you can use the --detect-pua switch for clamscan or enable it in the config file for ClamD (add: DetectPUA yes).

+

At this point we DO NOT recommend using it in production environments, because the detection may be too aggressive and lead to false positives. In one of the next releases we will provide additional features for fine-tuning allowing better adjustments to different setups. NOTE: A detection as PUA does NOT tell if an application is good or bad. All it says is, that a file MAY BE unwanted or MAYBE could compromise your system security and it MAY BE a good idea to check it twice.

+

Can ClamAV disinfect files?

+

No, it can't. We will add support for disinfecting OLE2 files in one of the next stable releases. There are no plans for disinfecting other types of files. There are many reasons for it: cleaning viruses from files is virtually pointless these days. It is very seldom that there is anything useful left after cleaning, and even if there is, would you trust it?

+

When using clamscan, is there a way to know which message within an mbox is infected?

+

There are two solutions: Run clamscan --debug, look for Deal with email number xxx Alternatively you can convert the mbox to Maildir format, run clamscan on it and then convert it back to mbox format. There are many tools available which can convert to and from Maildir format: formail, mbox2maildir and maildir2mbox

+

What platforms does it support?

+

Clam AntiVirus works with Linux®, Solaris, FreeBSD, OpenBSD, NetBSD, Mac OS X, Cygwin B20 on multiple architectures such as Intel, Alpha, Sparc, Cobalt MIPS boxes, PowerPC, RISC 6000.

+

Where can I find more information about ClamAV?

+

Please read the complete documentation in pdf/ps format. You will find it inside the package or in the documentation section of this website. You can also try searching the mailing list archives. If you can't find the answer, you can ask for support on the clamav-users mailing-list, but please before doing it, search the archives! Also, make sure that you don't send HTML messages and that you don't top post: these violate the netiquette and lessen your chances of being answered.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/faq/faq-ml.html clamav-0.103.5+dfsg/docs/html/faq/faq-ml.html --- clamav-0.103.2+dfsg/docs/html/faq/faq-ml.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/faq/faq-ml.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,221 @@ + + + + + + Mailing Lists - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Mailing Lists FAQ

+

If you're unable to find an answer to your question in our FAQ, you can seek help in our clamav-users mailing list, on our Discord server, or by submitting an issue on GitHub. The mailing list archives and existing Github issues (open or closed) may also have an answer to your question.

+

Please consider contributing answered questions back to this FAQ, and improving the quality of these answers, by submitting pull requests to our documentation source repository.

+

Where can I ask questions about using ClamAV?

+

Subscribe to our clamav-users mailing-list.

+

I want to take part to the development of ClamAV. Where can I get more info?

+

Subscribe to the clamav-devel mailing-list.

+

The mailing-lists generate too many messages per day. I can't handle them. What shall I do?

+

There are two possible solutions:

+
    +
  • Go to the mailing-list mailman interface, click on Unsubscribe or edit options, and turn digest mode on.
  • +
  • Access the mailing-lists using a "news reader".
  • +
+

I sent a message to one of ClamAV's mailing-lists, but the mail was rejected/held for approval. Why?

+

Only subscribers are allowed to post to the mailing-list. This is done to avoid spammers.

+

I read the mailing-list from the Gmane news gateway. Can I post to the mailing-list?

+

See previous FAQ.

+

I've been unsubscribed from one of the mailing-lists. What happened?

+

There are two possible reasons: If your account generates too many bounces you'll be automatically unsubscribed. Please subscribe again with a more reliable account. If we receive even one out of office notification from your vacation program, your address will be unsubscribed and banned from our mailing-lists forever. Sorry for that, there are just too many stupid people out there.

+

How do I disable mail delivery from the mailing-list I'm subscribed to?

+

Suppose you are subscribed to clamav-users. Go to clamav-users and enter your mail address at the bottom of the page. Click on Unsubscribe or edit options. At the next page enter your password and press Log in. Under Your clamav-users Subscription Options choose Disabled opposite Mail delivery and press Submit My Changes at the bottom of the page.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/faq/faq-pua.html clamav-0.103.5+dfsg/docs/html/faq/faq-pua.html --- clamav-0.103.2+dfsg/docs/html/faq/faq-pua.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/faq/faq-pua.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,357 @@ + + + + + + PUA (Potentially Unwanted Application) - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Potentially Unwanted Applications (PUA)

+

ClamAV supports the detection of Potentially Unwanted Applications (PUA).

+

PUA Config Options

+

You can customize PUA detection for ClamD with these clamd.conf options:

+
  DetectPUA yes        # Detect Possibly Unwanted Applications
+  ExcludePUA CAT       # Skip PUA sigs of category CAT
+  IncludePUA CAT       # Load PUA sigs of category CAT
+
+

You can customize PUA detection for ClamScan with these command-line options:

+
  --detect-pua         # Detect Possibly Unwanted Applications
+  --exclude-pua=CAT    # Skip PUA sigs of category CAT
+  --include-pua=CAT    # Load PUA sigs of category CAT
+
+

The category name is a string match with the 2nd token in a PUA.* signature name.

+
  PUA.category.subcategory.description-version
+
+

Some examples:

+
    +
  • +

    PUA.Win.Packer.BorlandDelphi-5 : The category name is Win.

    +
  • +
  • +

    PUA.Cert.Revoked.PEAuthenticode-5750538-0 : The category name is Cert.

    +
  • +
+

There is presently no support for including or excluding by subcategory.

+

Current PUA Categories

+

PUA categories are the product of signature naming conventions. These vary over time as new signatures are added.

+
+

Disclaimer: PUA signatures are not as carefully curated as malware signatures because they are not as commonly used. You should expect more false positives when using PUA signatures. Further, inclusion or exclusion of specific categories may not be very intuitive or predictable. Specifically, excluding the Win category will not exclude all Windows application PUA signatures. There are undoubtedly more Windows PUA signatures in the Packed, Tool, Spy, NetTool, etc categories that target Windows applications. Similarly, excluding the Packed category will not guarantee that you exclude signatures like PUA.Win.Packer.Whatever-0123. In short, the inclusion and exclusion of PUA signatures will likely be frustrating. Improvements to PUA include/exclude options to support subcategories as well as SigTool features to enumerate current PUA categories and subcategories would be a good candidate for a community contribution project.

+
+
+

Disclaimer 2: The Virus/Ransomeware/Trojan/etc malware categories or subcategories for PUA signatures were mistakenly selected by automated tools. Those tools have since been fixed and no new signatures should appear with these names. The existing malware-name categories for these PUA signatures are expected to be removed/renamed as time permits.

+
+

The following is a snapshot of the PUA signature name categories and subcategories from daily.cvd & main.cvd (Jan 29, 2020):

+
PUA.Andr.Adware
+PUA.Andr.Downloader
+PUA.Andr.Dropper
+PUA.Andr.Tool
+PUA.Andr.Trojan
+PUA.Andr.Virus
+PUA.Cert.Revoked
+PUA.Doc.Dropper
+PUA.Doc.Packed
+PUA.Doc.Tool
+PUA.Doc.Trojan
+PUA.Email.Phishing
+PUA.Email.Trojan
+PUA.Embedded.File
+PUA.Html.Exploit
+PUA.Html.Tool
+PUA.Html.Trojan
+PUA.Java.Exploit
+PUA.Java.Packer
+PUA.Js.Exploit
+PUA.Osx.File
+PUA.Osx.Trojan
+PUA.Packed.Tool
+PUA.Pdf.Exploit
+PUA.Pdf.Trojan
+PUA.Php.Trojan
+PUA.Rtf.Exploit
+PUA.Spy.Tool
+PUA.Swf.Spyware
+PUA.Tool.Countermeasure
+PUA.Tool.Tool
+PUA.Unix.Adware
+PUA.Unix.Coinminer
+PUA.Unix.Downloader
+PUA.Unix.File
+PUA.Unix.Malware
+PUA.Unix.Tool
+PUA.Unix.Trojan
+PUA.Unix.Virus
+PUA.Win.Adware
+PUA.Win.Coinminer
+PUA.Win.Downloader
+PUA.Win.Dropper
+PUA.Win.Exploit
+PUA.Win.File
+PUA.Win.Ircbot
+PUA.Win.Joke
+PUA.Win.Keylogger
+PUA.Win.Malware
+PUA.Win.Packed
+PUA.Win.Packer
+PUA.Win.Proxy
+PUA.Win.Ransomware
+PUA.Win.Spyware
+PUA.Win.Tool
+PUA.Win.Trojan
+PUA.Win.Virus
+
+

PUA Category Descriptions

+

The following category descriptions should give you some idea of how the PUA signature naming conventions are used. Please note this list is not exhaustive. As noted above, PUA signatures are not as carefully curated and there will be exceptions:

+
    +
  • +

    Andr

    +

    Potentially unwanted applications for Android mobile devices.

    +
  • +
  • +

    Java

    +

    Potentially unwanted applications written for the Java runtime.

    +
  • +
  • +

    NetTool

    +

    Applications that can be used to sniff, filter, manipulate or scan network traffic or networks. While a network scanner - for example - can be a extremely helpful tool for admins, you may not want to see an average user playing around with it. Same goes for tools like netcat and the like.

    +
  • +
  • +

    P2P

    +

    Peer to Peer clients can be used to generate a lot of unwanted traffic and sometimes it happens that copyrights are violated by downloading copyright protected content (Music, Movies) - therefore we consider them possibly unwanted as well.

    +
  • +
  • +

    Packed

    +

    This is a detection for files that use some kind of runtime packer. A runtime packer can be used to reduce the size of executable files without the need for an external unpacker. While this can't be considered malicious in general, runtime packers are widely used with malicious files since they can prevent a already known malware from detection by an anti-virus product.

    +
  • +
  • +

    PwTool

    +

    Password tools are all applications that can be used to recover or decrypt passwords for various applications - like mail clients or system passwords. Such tools can be quite helpful if a password is lost, however, it can also be used to spy out passwords.

    +
  • +
  • +

    IRC

    +

    IRC Clients can be a productivity killer and depending on the client - a powerful platform for malicious scripts (take mIRC for example).

    +
  • +
  • +

    Osx

    +

    Potentially unwanted applications for macOS systems.

    +
  • +
  • +

    RAT

    +

    Remote Access Trojans are used to remotely access systems, but can be used also by system admins, for example VNC or RAdmin.

    +
  • +
  • +

    Server

    +

    Server based badware like DistributedNet.

    +
  • +
  • +

    Script

    +

    Known "problem" scripts written in JavaScript, ActiveX or similar.

    +
  • +
  • +

    Spy

    +

    Keyloggers, spying tools.

    +
  • +
  • +

    Tool

    +

    General system tools, like process killers/finders.

    +
  • +
  • +

    Unix

    +

    Potentially unwanted applications for Unix systems.

    +
  • +
  • +

    Win

    +

    Potentially unwanted applications for Windows systems.

    +
  • +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/faq/faq-safebrowsing.html clamav-0.103.5+dfsg/docs/html/faq/faq-safebrowsing.html --- clamav-0.103.2+dfsg/docs/html/faq/faq-safebrowsing.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/faq/faq-safebrowsing.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,222 @@ + + + + + + Safe Browsing - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Safebrowsing

+

About

+

Google Safe Browsing may be used to get advanced protection against emails with links to suspicious websites.

+

Current Status

+

The Safe Browsing feature has now been spun off into a related project.

+

ClamAV previously provided a "safebrowsing" signature database derived from Google's Safe Browsing API using our own account and API key. Due to changes to the terms of service, we can no longer provide the signature content for you. Users that desire Safe Browsing coverage for non-commercial purposes can still generate their own safebrowsing signature database using the clamav-safebrowsing tools with a Google Safe Browsing API key.

+

Briefly, clamav-safebrowsing tools are needed to:

+
    +
  1. +

    Download the data from Google to a local MySQL database using Google's API [*];

    +
  2. +
  3. +

    Produce a local copy of the safebrowsing database file in a form suitable for use by the ClamAV tools;

    +
  4. +
+

You will have to decide how to distribute this database file to the systems which need it; and to notify any clamd daemons of the change so that they load the new signatures.

+

[*] For efficiency, the API permits downloading differences, in much the same way that ClamAV itself uses .cdiff files. The clamav-safebrowsing tool uses a MySQL database to store the Safe Browsing data so it can apply these differences the next time you use it.

+

For more information, please visit the ClamAV Safebrowsing project on Github.

+

History

+

ClamAV 0.95 introduced an optional "safebrowsing" signature database to provide advanced protection against emails with links to suspicious websites. This was generated using Google's Safe Browsing API.

+

The "safebrowsing" signature database which was distributed in the same way as the other ClamAV database files via the ClamAV mirror network. Downloading the database was disabled by default, and the feature was to be enabled only with extreme caution. In order to enable this feature it was necessary to add the option SafeBrowsing Yes to freshclam.conf.

+

As of Nov. 11, 2019, we stopped updating the "safebrowsing" signature database because Google announced changes to their Safe Browsing API terms of service. Google now requires commercial users to use the Google Web Risk API, a for-profit feature, instead of the Safe Browsing API. Though ClamAV itself is free and open-source, we cannot continue to provide Google Safe Browsing data to the general public.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/faq/faq-scan-alerts.html clamav-0.103.5+dfsg/docs/html/faq/faq-scan-alerts.html --- clamav-0.103.2+dfsg/docs/html/faq/faq-scan-alerts.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/faq/faq-scan-alerts.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,307 @@ + + + + + + Interpreting Scan Alerts - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Interpreting Scan Alerts FAQ

+

If you're unable to find an answer to your question in our FAQ, you can seek help in our clamav-users mailing list, on our Discord server, or by submitting an issue on GitHub. The mailing list archives and existing Github issues (open or closed) may also have an answer to your question.

+

Please consider contributing answered questions back to this FAQ, and improving the quality of these answers, by submitting pull requests to our documentation source repository.

+

ClamAV alerted on a file during a scan. What do I do?

+

ClamAV may have found a malicious or suspicious file. However, you're probably asking yourself if the alert is a False Positive (FP). It may well be, so don't just delete the file out-of-hand.

+

ClamAV alerted on a file in the clamav source code. Am I infected?

+

If you scan the build directory for ClamAV, you may see an alert on a ClamAV test file, like this:

+
clamav-0.104.1/build/unit_tests/input/clamav_hdb_scanfiles/clam.chm: Clamav.Test.File-6 FOUND
+
+

You can savely ignore this alert. The files found under the clamav unit_tests/input in the build directory are supposed to alert, to demonstrate correct file parser functionality.

+

Online Research

+

First, consider the file itself and whether or not the alert makes sense. If you're concerned, start by searching the name of the signature on Google. If FP's are being reported, you may see others complaining about the same thing, or you may be able to get an understanding of what the signature is trying to find.

+

Technical Investigation

+

Second, if you're technically inclined, you may want to try to read the signature details to understand how it works and what, specifically, it's alerting on. Take heed, this investigation might leave you more confused than when you started. ClamAV doesn't post write-ups on how each signature in-part because a good number of our signatures these days are generated automatically and not by a human mind.

+
    +
  1. +

    Start by opening a command prompt in a new empty directory, for example:

    +
    user@laptop:~$  mkdir /tmp/sigdump
    +
    +user@laptop:~$  cd /tmp/sigdump
    +
    +
  2. +
  3. +

    Use the sigtool program to unpack the ClamAV databases into their separate components. SigTool should be installed alongside clamscan, probably in /usr/local/bin/sigtool. The ClamAV databases are traditionally installed in /usr/local/share/clamav although if you installed from a package manager, your paths may vary:

    +
    user@laptop:/tmp/sigdump$  sigtool -u /usr/local/share/clamav/main.cvd
    +
    +user@laptop:/tmp/sigdump$  sigtool -u /usr/local/share/clamav/daily.cvd        # May be: daily.cld
    +
    +user@laptop:/tmp/sigdump$  sigtool -u /usr/local/share/clamav/bytecode.cvd     # May be: bytecode.cld
    +
    +
  4. +
  5. +

    Use ls to verify that you've successfully unpacked the databases:

    +
    user@laptop:/tmp/sigdump$  ls
    +
    +3986187.cbc    3986230.cbc    3986303.cbc    4553522.cbc    6335443.cbc    6399052.cbc    daily.cfg      daily.msb
    +3986188.cbc    3986231.cbc    3986305.cbc    4970075.cbc    6335540.cbc    6404655.cbc    daily.crb      daily.msu
    +3986206.cbc    3986232.cbc    3986306.cbc    5044126.cbc    6335560.cbc    6428210.cbc    daily.fp       daily.ndb
    +3986212.cbc    3986233.cbc    3986310.cbc    5588995.cbc    6335564.cbc    6428556.cbc    daily.ftm      daily.ndu
    +3986214.cbc    3986234.cbc    3986321.cbc    5819336.cbc    6335669.cbc    6441308.cbc    daily.hdb      daily.pdb
    +3986215.cbc    3986235.cbc    3986322.cbc    5999914.cbc    6336023.cbc    6442366.cbc    daily.hdu      daily.sfp
    +3986216.cbc    3986236.cbc    3986327.cbc    5999936.cbc    6336035.cbc    6447941.cbc    daily.hsb      daily.wdb
    +3986217.cbc    3986242.cbc    3986328.cbc    6300337.cbc    6336074.cbc    6453673.cbc    daily.hsu      main.crb
    +3986218.cbc    3986244.cbc    3986334.cbc    6311970.cbc    6336259.cbc    6471051.cbc    daily.idb      main.fp
    +3986219.cbc    3986249.cbc    3986337.cbc    6316126.cbc    6336260.cbc    6497366.cbc    daily.ign      main.hdb
    +3986220.cbc    3986259.cbc    4306126.cbc    6324281.cbc    6336630.cbc    6539706.cbc    daily.ign2     main.hsb
    +3986221.cbc    3986282.cbc    4306157.cbc    6327695.cbc    6336737.cbc    6566834.cbc    daily.info     main.info
    +3986222.cbc    3986283.cbc    4307467.cbc    6329916.cbc    6336739.cbc    6614848.cbc    daily.ldb      main.mdb
    +3986223.cbc    3986289.cbc    4416867.cbc    6329917.cbc    6364361.cbc    COPYING        daily.ldu      main.msb
    +3986224.cbc    3986292.cbc    4510302.cbc    6335400.cbc    6380163.cbc    bytecode.info  daily.mdb      main.ndb
    +3986229.cbc    3986301.cbc    4526683.cbc    6335427.cbc    6395243.cbc    daily.cdb      daily.mdu      main.sfp
    +
    +
  6. +
  7. +

    Use grep to search for the signature in question. For example:

    +
    user@laptop:/tmp/sigdump$  grep -r Win.Downloader.DDECmdExec-6683887-5
    +
    +Win.Downloader.DDECmdExec-6683887-5;Engine:81-255,Target:0;4;0:1f8b;0:255044462d;0:4d5a{-100}50450000;7c27{-255}2721;(0=0&1=0&2=0)&3/(?<!\x20)[=+\-@]\s*?(\w+\s*?\x28)?.{0,50}(certutil|cmd|cmstp|cscript|dnscmd|msiexec|netsh|regsvr32|rpcping|rundll32|schtasks|telnet|tscon|tsdiscon|wmic|wscript).{0,50}\|\s*?\x27[^\x27]{5,255}\x27\s*?\x21/i
    +
    +
  8. +
  9. +

    Reading ClamAV signatures is hard. You can familiarize yourself with the ClamAV signature format by reading the documentation on writing ClamAV Signatures.

    +

    To get a jump start, you can make sigtool print out a more human readable represenation of what the signature is looking for. Pipe the output from grep directly into sigtool by using the --decode-sigs option:

    +
    user@laptop:/tmp/sigdump$  grep Win.Downloader.DDECmdExec-6683887-5 -r . | ../../bin/sigtool --decode-sigs
    +
    +

    The output will look something like this:

    +
        VIRUS NAME: ./daily.ldb:Win.Downloader.DDECmdExec-6683887-5
    +    TDB: Engine:81-255,Target:0
    +    LOGICAL EXPRESSION: 4
    +     * SUBSIG ID 0
    +     +-> OFFSET: 0
    +     +-> SIGMOD: NONE
    +     +-> DECODED SUBSIGNATURE:
    +    �
    +     * SUBSIG ID 1
    +     +-> OFFSET: 0
    +     +-> SIGMOD: NONE
    +     +-> DECODED SUBSIGNATURE:
    +    %PDF-
    +     * SUBSIG ID 2
    +     +-> OFFSET: 0
    +     +-> SIGMOD: NONE
    +     +-> DECODED SUBSIGNATURE:
    +    MZ{WILDCARD_ANY_STRING(LENGTH<=100)}PE
    +     * SUBSIG ID 3
    +     +-> OFFSET: ANY
    +     +-> SIGMOD: NONE
    +     +-> DECODED SUBSIGNATURE:
    +    |'{WILDCARD_ANY_STRING(LENGTH<=255)}'!
    +     * SUBSIG ID 4
    +     +-> OFFSET: ANY
    +     +-> SIGMOD: NONE
    +     +-> DECODED SUBSIGNATURE:
    +         +-> TRIGGER: (0=0&1=0&2=0)&3
    +         +-> REGEX: (?<!\x20)[=+\-@]\s*?(\w+\s*?\x28)?.{0,50}(certutil|cmd|cmstp|cscript|dnscmd|msiexec|netsh|regsvr32|rpcping|rundll32|schtasks|telnet|tscon|`tsdiscon|wmic|wscript).{0,50}\|\s*?\x27[^\x27]{5,255}\x27\s*?\x21
    +         +-> CFLAGS: i
    +
    +
  10. +
  11. +

    Interpret the results. ClamAV signatures can be as simple as a hash-based signature of a known-malicious file, but they can also be a complex logical test. You may not learn enough to make an educated decision. The above example is a pretty complicated one, so I will try to walk you through it.

    +

    You can see that there are 5 subsignatures (numbered 0 - 4). The LOGICICAL EXPRESSION indicates which subsignature(s) matter and why. This could be something like 0 AND 1 to indicate that 2 subsignatures must both trigger in order for the overall signature to alert. In this case, only subsignature 4 is required by the LOGICAL EXPRESSION.

    +

    If you look at SUBSIG ID 4, you'll see that has a has a TRIGGER which acts in much the same way as the above LOGICAL EXPRESSION. If the subsignatures in the logical expression are satisfied, then the regular expression REGEX will be tested. If the regular expression matches, then the SUBSIG ID 4 will trigger and the overall signature will alert.

    +
  12. +
+

Reporting

+

If you believe that the signature alerted on a benign file, please report the False Positive so our analysts can refine or remove the faulty signature. You can report false positives on our website or you can submit the report using the clamsubmit command-line program.

+

If you're concerned that the file may be malicious, and aren't comfortable quarantining and/or deleting the file, feel free to ask in the user mailing lists for advice. Please subscribe to clamav-users and then post a message to all the list members by sending an email to clamav-users -at- lists -dot- clamav -dot- net.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/faq/faq-troubleshoot.html clamav-0.103.5+dfsg/docs/html/faq/faq-troubleshoot.html --- clamav-0.103.2+dfsg/docs/html/faq/faq-troubleshoot.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/faq/faq-troubleshoot.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,239 @@ + + + + + + Troubleshooting - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Troubleshooting FAQ

+

The following questions and answers may help you troubleshoot issues you may encounter when using ClamAV.

+

If you're unable to find an answer to your question in our FAQ, you can seek help in our clamav-users mailing list, on our Discord server, or by submitting an issue on GitHub. The mailing list archives and existing Github issues (open or closed) may also have an answer to your question.

+

Please consider contributing answered questions back to this FAQ, and improving the quality of these answers, by submitting pull requests to our documentation source repository.

+

After ClamAV is installed, then what? How do I update / refresh the virus database?

+

You will need to edit the freshclam.conf.example file located in /usr/local/etc. Once that is done, you will need to run a sudo freshclam to download the signatures. You will need to run the command to update signatures often so that ClamAV has the most up to date signatures.

+

How many times per hour shall I run FreshClam?

+

You can check for database update as often as 4 times per hour provided that you have the following options in freshclam.conf:

+
DNSDatabaseInfo current.cvd.clamav.net
+
+DatabaseMirror database.clamav.net
+
+

I get this error when running FreshClam: Invalid DNS reply. Falling back to HTTP mode or ERROR: Can't query current.cvd.clamav.net . What does it mean?

+

There is a problem with your DNS server. Please check the entries in /etc/resolv.conf and verify that you can resolve the TXT record manually:

+
host -t txt current.cvd.clamav.net
+
+

If you can't, it means your network is broken. You'll be still able to download the updates, but you'll waste a lot of bandwidth checking for updates.

+

What does WARNING: DNS record is older than 3 hours mean?

+

FreshClam attempts to detect potential problems with DNS caches and switches to use HTTPS if something looks suspicious. If this message appears seldomly, you can safely ignore it. If you get the error every time you run FreshClam, check your system clock. If it is set correctly, check your dns settings. If those didn't help, try putting this at the top of your cronjob:

+
host -t txt current.cvd.clamav.net; perl -e 'printf "%d\n", time;'
+
+

The 4th field of the first line should be less than 3 ∗ 3600 behind the output of the second line. If not, you have a caching DNS server somewhere misbehaving.

+

I get this error when running FreshClam: ERROR: Connection with ??? failed . What shall I do?

+

Either your dns servers are not working or you are blocking port 53/tcp. You should manually check that you can resolve host names with:

+
host database.clamav.net
+
+

If it doesn't work, check your dns settings in /etc/resolv.conf. If it works, check that you can receive dns answers longer than 512 bytes, e.g. check that your firewall is not blocking packets which originate from port 53/tcp.

+

An easy way to find it out is:

+
dig @ns1.clamav.net db.us.big.clamav.net
+
+

How do I know if my IP address has been blocked?

+

Run FreshClam in verbose-mode (-v) to view the HTTP requests and responses. If you're seeing an HTTP 403 response, then you may have been blocked. If think you've been blocked, feel free to contact us for help getting un-blocked. For more information about HTTP error codes, see the FreshClam FAQ

+

I can't resolve current.cvd.clamav.net! Is there a problem with your/my DNS servers?

+

current.cvd.clamav.net has got only a TXT record, not a type A record! Try this command:

+
$ host -t txt current.cvd.clamav.net
+
+

Please note that some not RFC compliant DNS servers (namely the one shipped with the Alcatel (now Thomson) SpeedTouch 510 modem) can't resolve TXT record. If that's the case, please recompile ClamAV with the flag --enable-dns-fix if using ./configure or -D ENABLE_FRESHCLAM_DNS_FIX=ON if using CMake.

+
+

For other questions regarding issues with the signature databases, see our Virus Database FAQ.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/faq/faq-uninstall.html clamav-0.103.5+dfsg/docs/html/faq/faq-uninstall.html --- clamav-0.103.2+dfsg/docs/html/faq/faq-uninstall.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/faq/faq-uninstall.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,274 @@ + + + + + + Uninstall - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Uninstalling ClamAV

+

If you installed from source

+

If you installed using Autotools (0.103.0 and older)

+

If you installed from source, the easiest way to uninstall will require that same source and build configuration in order to uninstall.

+

Then run:

+
sudo make uninstall
+
+
+

Tip: If you don't have the old source / build directory from when you installed, you can get the source again and re-configure, as if for a build, so that you can use the build system to uninstall. Just be sure you use the same ./configure options as when you first installed. For example:

+
./configure
+sudo make uninstall
+
+
+

If you installed using CMake (0.104.0 and newer)

+

CMake doesn't provide a simple command to uninstall. However, CMake does build an install_manifest.txt file when you do the install. You can use the manifest to remove the installed files.

+

You will find the manifest in the directory where you compiled ClamAV. If you followed the recommendations in our Installing from Source section, then you will find it at <clamav source directory>/build/install_manifest.txt.

+

Feel free to inspect the file so you're comfortable knowing what you're about to delete.

+

Open a terminal and cd to that <clamav source directory>/build directory. Then run:

+
xargs rm < install_manifest.txt
+
+

This will leave behind the directories, and will leave behind any files added after install including the signature databases and any config files. You will have to delete these extra files yourself.

+
+

Tip: If you don't have the old source / build directory from when you installed, you can get the source again. Unlike with Autotools, you'll have to do more than just re-configure. You'll have to compile and re-install overtop of your existing install in order to get a new copy of the install_manifest.txt file. But once you do that, you should be able to use the command above to uninstall. For example:

+
cmake . &&
+make && sudo make install
+sudo xargs rm < install_manifest.txt
+
+
+

If you installed from packages

+
    +
  • +

    Debian/Ubuntu:

    +

    apt remove clamav

    +
  • +
  • +

    Redhat/Fedora:

    +

    dnf remove clamav*

    +

    or, on older systems:

    +

    yum remove clamav*

    +
  • +
  • +

    Mandriva:

    +

    urpme clamav

    +
  • +
  • +

    Gentoo:

    +

    emerge -C clamav

    +
  • +
  • +

    FreeBSD:

    +

    pkg delete clamav

    +
  • +
  • +

    OpenBSD:

    +

    pkg_delete clamav

    +
  • +
  • +

    NetBSD:

    +

    pkgin remove clamav

    +
  • +
  • +

    Slackware:

    +

    /etc/rc.d/rc.clamav stop; removepkg clamav

    +
  • +
+

Caveats

+

Make sure that you haven’t got old libraries (libclamav.so) lying around your filesystem. You can verify it using:

+
ldd `which freshclam`
+
+

Also make sure there is really only one version of ClamAV installed on your system:

+
whereis freshclam
+
+whereis clamscan
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/faq/faq-upgrade.html clamav-0.103.5+dfsg/docs/html/faq/faq-upgrade.html --- clamav-0.103.2+dfsg/docs/html/faq/faq-upgrade.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/faq/faq-upgrade.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,253 @@ + + + + + + Upgrading - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Upgrading ClamAV

+

ClamAV from Packages

+

If you installed from a package, we suggest you find the approved package from your distro provider and install that. The ClamAV team does not maintain individual packages for every distribution build. +If there are no new packages, you have three options:

+
    +
  • Wait
  • +
  • Build ClamAV Package
  • +
  • Install ClamAV From Source
  • +
+

Install ClamAV From Source

+

If you installed from sources, first uninstall the old version.

+

Then, compile and install the new one.

+

Depending on your installation method, you might want to backup configuration (located in /usr/local/etc by default) and signature database (located in /usr/local/share/clamav by default). Don't forget to restore backups before starting up updated ClamAV.

+

Backup your database signature (located in /usr/local/share/clamav by default) before upgrading to newer ClamAV version. Restore the backed up database signature before running the updated version. This is to avoid getting the /usr/local/share/clamav not locked error message when doing freshclam.

+

Webmin and Yum

+

To obtain a new version:

+
yum list clamav
+
+yum update clamav
+
+

If everything updated properly, run freshclam to update your signature database.

+

What does "WARNING: Current functionality level = 1, required = 2" mean?

+

The functionality level of the database determines which scanner engine version is required to use all of its signatures. If you don't upgrade immediately you will be missing the latest viruses.

+

What does "Your ClamAV installation is OUTDATED" mean?

+

You'll get this message whenever a new version of ClamAV is released. In order to detect all the latest viruses, it's not enough to keep your database up to date. You also need to run the latest version of the scanner. You can download the sources of the latest release from our website. If you are afraid to break something while upgrading, use the pre-compiled packages for your operating system/distribution. Remember: running the latest stable release also improves stability.

+

I upgraded to the latest stable version but I still get the message "Your ClamAV installation is OUTDATED", why?

+

Make sure there is really only one version of ClamAV installed on your system:

+
whereis freshclam
+
+whereis clamscan
+
+

Also make sure that you haven't got old libraries (libclamav.so*) lying around your filesystem. You can verify it using:

+
ldd $(which freshclam)
+
+

What does "Malformed hexstring: This ClamAV version has reached End of Life" mean?

+

Please refer to our End-of-Life (EOL) policy.

+

How do I verify the integrity of ClamAV sources?

+

Using GnuPG you can easily verify the authenticity of your stable release downloads by using the following method: Download the Talos PGP public key from the VRT labs site. Import the key into your local public keyring:

+
gpg --import vrt.gpg
+
+

Download the stable release AND the corresponding .sig file to the same directory. Verify that the stable release download is signed with the Talos PGP public key:

+
gpg --verify clamav-X.XX.tar.gz.sig
+
+

Please note that the resulting output should look like the following:

+
    gpg: Signature made Wed Jan 24 19:31:26 2018 EST
+    gpg:                using RSA key F13F9E16BCA5BFAD
+    gpg: Good signature from "Talos (Talos, Cisco Systems Inc.) [email address]" [unknown]
+
+

For other PGP implementation, please refer to their manual.

+

Where can I get the latest release, beta, or release candidate of ClamAV?

+

Visit the source download page.

+

Is my compiler/hardware/operating system supported by ClamAV?

+

ClamAV supports a wide variety of compilers, hardware and operating systems. Our core compiler is GCC with Linux on 32 and 64 bit Intel platforms, LLVM/Clang on macOS, and MSVC on Windows.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/faq/faq-whichversion.html clamav-0.103.5+dfsg/docs/html/faq/faq-whichversion.html --- clamav-0.103.2+dfsg/docs/html/faq/faq-whichversion.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/faq/faq-whichversion.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,206 @@ + + + + + + Selecting the Right Version of ClamAV for You - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Which Version of ClamAV should I use?

+

Official ClamAV releases are available for download from ClamAV.net.

+

Stable release

+

For critical systems, please install using the latest stable release.

+

Beta and Release candidates Programs

+

If your system is not critical, we occasionally release beta and release candidate (RC) versions of the next feature release. Your participation in our beta and release candidate programs is always appreciated.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/faq/faq-win32.html clamav-0.103.5+dfsg/docs/html/faq/faq-win32.html --- clamav-0.103.2+dfsg/docs/html/faq/faq-win32.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/faq/faq-win32.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,254 @@ + + + + + + Win32 - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

ClamAV on Microsoft Windows FAQ

+

ClamAV offers a versions of ClamAV for Microsoft Windows compatible with both 32bit and 64bit versions of Windows 7 and newer.

+

We provide both two install packages for Windows:

+
    +
  • +

    Portable Install Package

    +
      +
    • Zip file containing all you need to run ClamAV without running an installer.
    • +
    +
  • +
  • +

    Install Program

    +
      +
    • Traditional executable installer that will install ClamAV in the "Program Files" directory.
    • +
    +
  • +
+

In addition to the above ClamAV versions that run on Windows, Cisco offers two more endpoint security products powered in-part by ClamAV. These are:

+
    +
  • +

    Immunet

    +
      +
    • Immunet is a malware and antivirus protection system that utilizes cloud computing to provide enhanced community-based security.
    • +
    • Immunet is free for non-commercial use.
    • +
    • Commercial users should consider Cisco AMP.
    • +
    +
  • +
  • +

    Cisco Advanced Malware Protection (AMP) for Endpoints

    +
      +
    • AMP for Endpoints prevents threats at point of entry, then continuously tracks every file it lets onto your endpoints. AMP can uncover even the most advanced threats, including fileless malware and ransomware.
    • +
    • AMP for Endpoints is subscription-based, managed through a web-based management console, and may be deployed on a variety of platforms to include Windows, macOS, and Linux operating systems.
    • +
    • For details, please visit the AMP for Endpoints website.
    • +
    +
  • +
+

What is the difference between ClamAV, Immunet, and ClamWin?

+

ClamAV is available for Windows with the same scanning and detection capabilities available when using ClamAV on macOS and Linux, with exception to the On-Access Scanning feature (Linux).

+

Immunet is a real-time fully featured desktop AV solution. It contains cloud based detection technologies and the enterprise grade ClamAV detection engine. The product is produced and maintained by Cisco which owns both ClamAV and Immunet.

+

ClamWin is a free application that provides a graphical front-end for ClamAV, similar to ClamTk. ClamWin is maintained by ClamWin Pty Ltd., and has no association with ClamAV, Cisco, or the Immunet product. Additionally, ClamWin does not contain an on-access real-time scanner, and can only be used to manually scan files.

+

Is Immunet free for commercial use?

+

Immunet is not free for commercial use. AMP for Endpoints is our product that replaced the commercial version. If you are interested in deploying this in a commercial environment, we highly suggest checking that out.

+

Will Immunet send any sensitive data from my computer to the cloud?

+

Immunet sends information about the files its scanning back to the cloud. This information is in the form of SHA hashes and file heuristics. Currently, this information is only collected for Windows PE files, or in other terms what most people refer to as executable files. No information is collected for other types of files, like Word, Excel, or PDF. Additionally, in some situations the entire PE file will be uploaded to the Cloud to determine if it is malicious. +For a complete overview please see the privacy policy.

+

Are you going to make use of the Cloud in the *nix version of ClamAV?

+

The simple answer is “yes”. The complex answer is “when”. We are currently working on that roadmap and will keep everyone informed as that information becomes available.

+

Can I use Immunet with my current AV solution?

+

Yes. In fact it is encouraged.

+

Where should I report false positives or undetected malware?

+

Please report Undetected Malware here.

+

Please report False Positives here.

+

Are there 64 bit versions of ClamAV for Windows as well as 32 bit?

+

Yes. You can find both versions at ClamAV/downloads.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/favicon.png and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/favicon.png differ diff -Nru clamav-0.103.2+dfsg/docs/html/FontAwesome/css/font-awesome.css clamav-0.103.5+dfsg/docs/html/FontAwesome/css/font-awesome.css --- clamav-0.103.2+dfsg/docs/html/FontAwesome/css/font-awesome.css 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/FontAwesome/css/font-awesome.css 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/FontAwesome/fonts/FontAwesome.ttf and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/FontAwesome/fonts/FontAwesome.ttf differ Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/FontAwesome/fonts/fontawesome-webfont.eot and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/FontAwesome/fonts/fontawesome-webfont.eot differ diff -Nru clamav-0.103.2+dfsg/docs/html/FontAwesome/fonts/fontawesome-webfont.svg clamav-0.103.5+dfsg/docs/html/FontAwesome/fonts/fontawesome-webfont.svg --- clamav-0.103.2+dfsg/docs/html/FontAwesome/fonts/fontawesome-webfont.svg 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/FontAwesome/fonts/fontawesome-webfont.svg 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/FontAwesome/fonts/fontawesome-webfont.ttf and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/FontAwesome/fonts/fontawesome-webfont.ttf differ Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/FontAwesome/fonts/fontawesome-webfont.woff and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/FontAwesome/fonts/fontawesome-webfont.woff differ Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/FontAwesome/fonts/fontawesome-webfont.woff2 and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/FontAwesome/fonts/fontawesome-webfont.woff2 differ diff -Nru clamav-0.103.2+dfsg/docs/html/fonts/fonts.css clamav-0.103.5+dfsg/docs/html/fonts/fonts.css --- clamav-0.103.2+dfsg/docs/html/fonts/fonts.css 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/fonts/fonts.css 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,100 @@ +/* Open Sans is licensed under the Apache License, Version 2.0. See http://www.apache.org/licenses/LICENSE-2.0 */ +/* Source Code Pro is under the Open Font License. See https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL */ + +/* open-sans-300 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 300; + src: local('Open Sans Light'), local('OpenSans-Light'), + url('open-sans-v17-all-charsets-300.woff2') format('woff2'); +} + +/* open-sans-300italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 300; + src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), + url('open-sans-v17-all-charsets-300italic.woff2') format('woff2'); +} + +/* open-sans-regular - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + src: local('Open Sans Regular'), local('OpenSans-Regular'), + url('open-sans-v17-all-charsets-regular.woff2') format('woff2'); +} + +/* open-sans-italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 400; + src: local('Open Sans Italic'), local('OpenSans-Italic'), + url('open-sans-v17-all-charsets-italic.woff2') format('woff2'); +} + +/* open-sans-600 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 600; + src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), + url('open-sans-v17-all-charsets-600.woff2') format('woff2'); +} + +/* open-sans-600italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 600; + src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), + url('open-sans-v17-all-charsets-600italic.woff2') format('woff2'); +} + +/* open-sans-700 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 700; + src: local('Open Sans Bold'), local('OpenSans-Bold'), + url('open-sans-v17-all-charsets-700.woff2') format('woff2'); +} + +/* open-sans-700italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 700; + src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), + url('open-sans-v17-all-charsets-700italic.woff2') format('woff2'); +} + +/* open-sans-800 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 800; + src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), + url('open-sans-v17-all-charsets-800.woff2') format('woff2'); +} + +/* open-sans-800italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 800; + src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), + url('open-sans-v17-all-charsets-800italic.woff2') format('woff2'); +} + +/* source-code-pro-500 - latin_vietnamese_latin-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Source Code Pro'; + font-style: normal; + font-weight: 500; + src: url('source-code-pro-v11-all-charsets-500.woff2') format('woff2'); +} diff -Nru clamav-0.103.2+dfsg/docs/html/fonts/OPEN-SANS-LICENSE.txt clamav-0.103.5+dfsg/docs/html/fonts/OPEN-SANS-LICENSE.txt --- clamav-0.103.2+dfsg/docs/html/fonts/OPEN-SANS-LICENSE.txt 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/fonts/OPEN-SANS-LICENSE.txt 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/fonts/open-sans-v17-all-charsets-300italic.woff2 and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/fonts/open-sans-v17-all-charsets-300italic.woff2 differ Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/fonts/open-sans-v17-all-charsets-300.woff2 and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/fonts/open-sans-v17-all-charsets-300.woff2 differ Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/fonts/open-sans-v17-all-charsets-600italic.woff2 and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/fonts/open-sans-v17-all-charsets-600italic.woff2 differ Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/fonts/open-sans-v17-all-charsets-600.woff2 and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/fonts/open-sans-v17-all-charsets-600.woff2 differ Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/fonts/open-sans-v17-all-charsets-700italic.woff2 and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/fonts/open-sans-v17-all-charsets-700italic.woff2 differ Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/fonts/open-sans-v17-all-charsets-700.woff2 and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/fonts/open-sans-v17-all-charsets-700.woff2 differ Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/fonts/open-sans-v17-all-charsets-800italic.woff2 and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/fonts/open-sans-v17-all-charsets-800italic.woff2 differ Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/fonts/open-sans-v17-all-charsets-800.woff2 and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/fonts/open-sans-v17-all-charsets-800.woff2 differ Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/fonts/open-sans-v17-all-charsets-italic.woff2 and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/fonts/open-sans-v17-all-charsets-italic.woff2 differ Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/fonts/open-sans-v17-all-charsets-regular.woff2 and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/fonts/open-sans-v17-all-charsets-regular.woff2 differ diff -Nru clamav-0.103.2+dfsg/docs/html/fonts/SOURCE-CODE-PRO-LICENSE.txt clamav-0.103.5+dfsg/docs/html/fonts/SOURCE-CODE-PRO-LICENSE.txt --- clamav-0.103.2+dfsg/docs/html/fonts/SOURCE-CODE-PRO-LICENSE.txt 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/fonts/SOURCE-CODE-PRO-LICENSE.txt 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,93 @@ +Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/fonts/source-code-pro-v11-all-charsets-500.woff2 and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/fonts/source-code-pro-v11-all-charsets-500.woff2 differ diff -Nru clamav-0.103.2+dfsg/docs/html/highlight.css clamav-0.103.5+dfsg/docs/html/highlight.css --- clamav-0.103.2+dfsg/docs/html/highlight.css 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/highlight.css 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,83 @@ +/* + * An increased contrast highlighting scheme loosely based on the + * "Base16 Atelier Dune Light" theme by Bram de Haan + * (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune) + * Original Base16 color scheme by Chris Kempson + * (https://github.com/chriskempson/base16) + */ + +/* Comment */ +.hljs-comment, +.hljs-quote { + color: #575757; +} + +/* Red */ +.hljs-variable, +.hljs-template-variable, +.hljs-attribute, +.hljs-tag, +.hljs-name, +.hljs-regexp, +.hljs-link, +.hljs-name, +.hljs-selector-id, +.hljs-selector-class { + color: #d70025; +} + +/* Orange */ +.hljs-number, +.hljs-meta, +.hljs-built_in, +.hljs-builtin-name, +.hljs-literal, +.hljs-type, +.hljs-params { + color: #b21e00; +} + +/* Green */ +.hljs-string, +.hljs-symbol, +.hljs-bullet { + color: #008200; +} + +/* Blue */ +.hljs-title, +.hljs-section { + color: #0030f2; +} + +/* Purple */ +.hljs-keyword, +.hljs-selector-tag { + color: #9d00ec; +} + +.hljs { + display: block; + overflow-x: auto; + background: #f6f7f6; + color: #000; + padding: 0.5em; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +.hljs-addition { + color: #22863a; + background-color: #f0fff4; +} + +.hljs-deletion { + color: #b31d28; + background-color: #ffeef0; +} diff -Nru clamav-0.103.2+dfsg/docs/html/highlight.js clamav-0.103.5+dfsg/docs/html/highlight.js --- clamav-0.103.2+dfsg/docs/html/highlight.js 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/highlight.js 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,6 @@ +/* + Highlight.js 10.1.1 (93fd0d73) + License: BSD-3-Clause + Copyright (c) 2006-2020, Ivan Sagalaev +*/ +var hljs=function(){"use strict";function e(n){Object.freeze(n);var t="function"==typeof n;return Object.getOwnPropertyNames(n).forEach((function(r){!Object.hasOwnProperty.call(n,r)||null===n[r]||"object"!=typeof n[r]&&"function"!=typeof n[r]||t&&("caller"===r||"callee"===r||"arguments"===r)||Object.isFrozen(n[r])||e(n[r])})),n}class n{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data}ignoreMatch(){this.ignore=!0}}function t(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function r(e,...n){var t={};for(const n in e)t[n]=e[n];return n.forEach((function(e){for(const n in e)t[n]=e[n]})),t}function a(e){return e.nodeName.toLowerCase()}var i=Object.freeze({__proto__:null,escapeHTML:t,inherit:r,nodeStream:function(e){var n=[];return function e(t,r){for(var i=t.firstChild;i;i=i.nextSibling)3===i.nodeType?r+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:r,node:i}),r=e(i,r),a(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:r,node:i}));return r}(e,0),n},mergeStreams:function(e,n,r){var i=0,s="",o=[];function l(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offset"}function u(e){s+=""}function d(e){("start"===e.event?c:u)(e.node)}for(;e.length||n.length;){var g=l();if(s+=t(r.substring(i,g[0].offset)),i=g[0].offset,g===e){o.reverse().forEach(u);do{d(g.splice(0,1)[0]),g=l()}while(g===e&&g.length&&g[0].offset===i);o.reverse().forEach(c)}else"start"===g[0].event?o.push(g[0].node):o.pop(),d(g.splice(0,1)[0])}return s+t(r.substr(i))}});const s="",o=e=>!!e.kind;class l{constructor(e,n){this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){this.buffer+=t(e)}openNode(e){if(!o(e))return;let n=e.kind;e.sublanguage||(n=`${this.classPrefix}${n}`),this.span(n)}closeNode(e){o(e)&&(this.buffer+=s)}value(){return this.buffer}span(e){this.buffer+=``}}class c{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const n={kind:e,children:[]};this.add(n),this.stack.push(n)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n),n.children.forEach(n=>this._walk(e,n)),e.closeNode(n)),e}static _collapse(e){"string"!=typeof e&&e.children&&(e.children.every(e=>"string"==typeof e)?e.children=[e.children.join("")]:e.children.forEach(e=>{c._collapse(e)}))}}class u extends c{constructor(e){super(),this.options=e}addKeyword(e,n){""!==e&&(this.openNode(n),this.addText(e),this.closeNode())}addText(e){""!==e&&this.add(e)}addSublanguage(e,n){const t=e.root;t.kind=n,t.sublanguage=!0,this.add(t)}toHTML(){return new l(this,this.options).value()}finalize(){return!0}}function d(e){return e?"string"==typeof e?e:e.source:null}const g="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",h={begin:"\\\\[\\s\\S]",relevance:0},f={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[h]},p={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[h]},b={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},m=function(e,n,t={}){var a=r({className:"comment",begin:e,end:n,contains:[]},t);return a.contains.push(b),a.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),a},v=m("//","$"),x=m("/\\*","\\*/"),E=m("#","$");var _=Object.freeze({__proto__:null,IDENT_RE:"[a-zA-Z]\\w*",UNDERSCORE_IDENT_RE:"[a-zA-Z_]\\w*",NUMBER_RE:"\\b\\d+(\\.\\d+)?",C_NUMBER_RE:g,BINARY_NUMBER_RE:"\\b(0b[01]+)",RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{const n=/^#![ ]*\//;return e.binary&&(e.begin=function(...e){return e.map(e=>d(e)).join("")}(n,/.*\b/,e.binary,/\b.*/)),r({className:"meta",begin:n,end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)},BACKSLASH_ESCAPE:h,APOS_STRING_MODE:f,QUOTE_STRING_MODE:p,PHRASAL_WORDS_MODE:b,COMMENT:m,C_LINE_COMMENT_MODE:v,C_BLOCK_COMMENT_MODE:x,HASH_COMMENT_MODE:E,NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?",relevance:0},C_NUMBER_MODE:{className:"number",begin:g,relevance:0},BINARY_NUMBER_MODE:{className:"number",begin:"\\b(0b[01]+)",relevance:0},CSS_NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[h,{begin:/\[/,end:/\]/,relevance:0,contains:[h]}]}]},TITLE_MODE:{className:"title",begin:"[a-zA-Z]\\w*",relevance:0},UNDERSCORE_TITLE_MODE:{className:"title",begin:"[a-zA-Z_]\\w*",relevance:0},METHOD_GUARD:{begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:function(e){return Object.assign(e,{"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{n.data._beginMatch!==e[1]&&n.ignoreMatch()}})}}),N="of and for in not or if then".split(" ");function w(e,n){return n?+n:function(e){return N.includes(e.toLowerCase())}(e)?0:1}const R=t,y=r,{nodeStream:k,mergeStreams:O}=i,M=Symbol("nomatch");return function(t){var a=[],i={},s={},o=[],l=!0,c=/(^(<[^>]+>|\t|)+|\n)/gm,g="Could not find the language '{}', did you forget to load/include a language module?";const h={disableAutodetect:!0,name:"Plain text",contains:[]};var f={noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:null,__emitter:u};function p(e){return f.noHighlightRe.test(e)}function b(e,n,t,r){var a={code:n,language:e};S("before:highlight",a);var i=a.result?a.result:m(a.language,a.code,t,r);return i.code=a.code,S("after:highlight",i),i}function m(e,t,a,s){var o=t;function c(e,n){var t=E.case_insensitive?n[0].toLowerCase():n[0];return Object.prototype.hasOwnProperty.call(e.keywords,t)&&e.keywords[t]}function u(){null!=y.subLanguage?function(){if(""!==A){var e=null;if("string"==typeof y.subLanguage){if(!i[y.subLanguage])return void O.addText(A);e=m(y.subLanguage,A,!0,k[y.subLanguage]),k[y.subLanguage]=e.top}else e=v(A,y.subLanguage.length?y.subLanguage:null);y.relevance>0&&(I+=e.relevance),O.addSublanguage(e.emitter,e.language)}}():function(){if(!y.keywords)return void O.addText(A);let e=0;y.keywordPatternRe.lastIndex=0;let n=y.keywordPatternRe.exec(A),t="";for(;n;){t+=A.substring(e,n.index);const r=c(y,n);if(r){const[e,a]=r;O.addText(t),t="",I+=a,O.addKeyword(n[0],e)}else t+=n[0];e=y.keywordPatternRe.lastIndex,n=y.keywordPatternRe.exec(A)}t+=A.substr(e),O.addText(t)}(),A=""}function h(e){return e.className&&O.openNode(e.className),y=Object.create(e,{parent:{value:y}})}function p(e){return 0===y.matcher.regexIndex?(A+=e[0],1):(L=!0,0)}var b={};function x(t,r){var i=r&&r[0];if(A+=t,null==i)return u(),0;if("begin"===b.type&&"end"===r.type&&b.index===r.index&&""===i){if(A+=o.slice(r.index,r.index+1),!l){const n=Error("0 width match regex");throw n.languageName=e,n.badRule=b.rule,n}return 1}if(b=r,"begin"===r.type)return function(e){var t=e[0],r=e.rule;const a=new n(r),i=[r.__beforeBegin,r["on:begin"]];for(const n of i)if(n&&(n(e,a),a.ignore))return p(t);return r&&r.endSameAsBegin&&(r.endRe=RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")),r.skip?A+=t:(r.excludeBegin&&(A+=t),u(),r.returnBegin||r.excludeBegin||(A=t)),h(r),r.returnBegin?0:t.length}(r);if("illegal"===r.type&&!a){const e=Error('Illegal lexeme "'+i+'" for mode "'+(y.className||"")+'"');throw e.mode=y,e}if("end"===r.type){var s=function(e){var t=e[0],r=o.substr(e.index),a=function e(t,r,a){let i=function(e,n){var t=e&&e.exec(n);return t&&0===t.index}(t.endRe,a);if(i){if(t["on:end"]){const e=new n(t);t["on:end"](r,e),e.ignore&&(i=!1)}if(i){for(;t.endsParent&&t.parent;)t=t.parent;return t}}if(t.endsWithParent)return e(t.parent,r,a)}(y,e,r);if(!a)return M;var i=y;i.skip?A+=t:(i.returnEnd||i.excludeEnd||(A+=t),u(),i.excludeEnd&&(A=t));do{y.className&&O.closeNode(),y.skip||y.subLanguage||(I+=y.relevance),y=y.parent}while(y!==a.parent);return a.starts&&(a.endSameAsBegin&&(a.starts.endRe=a.endRe),h(a.starts)),i.returnEnd?0:t.length}(r);if(s!==M)return s}if("illegal"===r.type&&""===i)return 1;if(B>1e5&&B>3*r.index)throw Error("potential infinite loop, way more iterations than matches");return A+=i,i.length}var E=T(e);if(!E)throw console.error(g.replace("{}",e)),Error('Unknown language: "'+e+'"');var _=function(e){function n(n,t){return RegExp(d(n),"m"+(e.case_insensitive?"i":"")+(t?"g":""))}class t{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,n){n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]),this.matchAt+=function(e){return RegExp(e.toString()+"|").exec("").length-1}(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map(e=>e[1]);this.matcherRe=n(function(e,n="|"){for(var t=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,r=0,a="",i=0;i0&&(a+=n),a+="(";o.length>0;){var l=t.exec(o);if(null==l){a+=o;break}a+=o.substring(0,l.index),o=o.substring(l.index+l[0].length),"\\"===l[0][0]&&l[1]?a+="\\"+(+l[1]+s):(a+=l[0],"("===l[0]&&r++)}a+=")"}return a}(e),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const n=this.matcherRe.exec(e);if(!n)return null;const t=n.findIndex((e,n)=>n>0&&void 0!==e),r=this.matchIndexes[t];return n.splice(0,t),Object.assign(n,r)}}class a{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t;return this.rules.slice(e).forEach(([e,t])=>n.addRule(e,t)),n.compile(),this.multiRegexes[e]=n,n}considerAll(){this.regexIndex=0}addRule(e,n){this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex;const t=n.exec(e);return t&&(this.regexIndex+=t.position+1,this.regexIndex===this.count&&(this.regexIndex=0)),t}}function i(e,n){const t=e.input[e.index-1],r=e.input[e.index+e[0].length];"."!==t&&"."!==r||n.ignoreMatch()}if(e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return function t(s,o){const l=s;if(s.compiled)return l;s.compiled=!0,s.__beforeBegin=null,s.keywords=s.keywords||s.beginKeywords;let c=null;if("object"==typeof s.keywords&&(c=s.keywords.$pattern,delete s.keywords.$pattern),s.keywords&&(s.keywords=function(e,n){var t={};return"string"==typeof e?r("keyword",e):Object.keys(e).forEach((function(n){r(n,e[n])})),t;function r(e,r){n&&(r=r.toLowerCase()),r.split(" ").forEach((function(n){var r=n.split("|");t[r[0]]=[e,w(r[0],r[1])]}))}}(s.keywords,e.case_insensitive)),s.lexemes&&c)throw Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ");return l.keywordPatternRe=n(s.lexemes||c||/\w+/,!0),o&&(s.beginKeywords&&(s.begin="\\b("+s.beginKeywords.split(" ").join("|")+")(?=\\b|\\s)",s.__beforeBegin=i),s.begin||(s.begin=/\B|\b/),l.beginRe=n(s.begin),s.endSameAsBegin&&(s.end=s.begin),s.end||s.endsWithParent||(s.end=/\B|\b/),s.end&&(l.endRe=n(s.end)),l.terminator_end=d(s.end)||"",s.endsWithParent&&o.terminator_end&&(l.terminator_end+=(s.end?"|":"")+o.terminator_end)),s.illegal&&(l.illegalRe=n(s.illegal)),void 0===s.relevance&&(s.relevance=1),s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map((function(e){return function(e){return e.variants&&!e.cached_variants&&(e.cached_variants=e.variants.map((function(n){return r(e,{variants:null},n)}))),e.cached_variants?e.cached_variants:function e(n){return!!n&&(n.endsWithParent||e(n.starts))}(e)?r(e,{starts:e.starts?r(e.starts):null}):Object.isFrozen(e)?r(e):e}("self"===e?s:e)}))),s.contains.forEach((function(e){t(e,l)})),s.starts&&t(s.starts,o),l.matcher=function(e){const n=new a;return e.contains.forEach(e=>n.addRule(e.begin,{rule:e,type:"begin"})),e.terminator_end&&n.addRule(e.terminator_end,{type:"end"}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n}(l),l}(e)}(E),N="",y=s||_,k={},O=new f.__emitter(f);!function(){for(var e=[],n=y;n!==E;n=n.parent)n.className&&e.unshift(n.className);e.forEach(e=>O.openNode(e))}();var A="",I=0,S=0,B=0,L=!1;try{for(y.matcher.considerAll();;){B++,L?L=!1:(y.matcher.lastIndex=S,y.matcher.considerAll());const e=y.matcher.exec(o);if(!e)break;const n=x(o.substring(S,e.index),e);S=e.index+n}return x(o.substr(S)),O.closeAllNodes(),O.finalize(),N=O.toHTML(),{relevance:I,value:N,language:e,illegal:!1,emitter:O,top:y}}catch(n){if(n.message&&n.message.includes("Illegal"))return{illegal:!0,illegalBy:{msg:n.message,context:o.slice(S-100,S+100),mode:n.mode},sofar:N,relevance:0,value:R(o),emitter:O};if(l)return{illegal:!1,relevance:0,value:R(o),emitter:O,language:e,top:y,errorRaised:n};throw n}}function v(e,n){n=n||f.languages||Object.keys(i);var t=function(e){const n={relevance:0,emitter:new f.__emitter(f),value:R(e),illegal:!1,top:h};return n.emitter.addText(e),n}(e),r=t;return n.filter(T).filter(I).forEach((function(n){var a=m(n,e,!1);a.language=n,a.relevance>r.relevance&&(r=a),a.relevance>t.relevance&&(r=t,t=a)})),r.language&&(t.second_best=r),t}function x(e){return f.tabReplace||f.useBR?e.replace(c,e=>"\n"===e?f.useBR?"
":e:f.tabReplace?e.replace(/\t/g,f.tabReplace):e):e}function E(e){let n=null;const t=function(e){var n=e.className+" ";n+=e.parentNode?e.parentNode.className:"";const t=f.languageDetectRe.exec(n);if(t){var r=T(t[1]);return r||(console.warn(g.replace("{}",t[1])),console.warn("Falling back to no-highlight mode for this block.",e)),r?t[1]:"no-highlight"}return n.split(/\s+/).find(e=>p(e)||T(e))}(e);if(p(t))return;S("before:highlightBlock",{block:e,language:t}),f.useBR?(n=document.createElement("div")).innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n"):n=e;const r=n.textContent,a=t?b(t,r,!0):v(r),i=k(n);if(i.length){const e=document.createElement("div");e.innerHTML=a.value,a.value=O(i,k(e),r)}a.value=x(a.value),S("after:highlightBlock",{block:e,result:a}),e.innerHTML=a.value,e.className=function(e,n,t){var r=n?s[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),e.includes(r)||a.push(r),a.join(" ").trim()}(e.className,t,a.language),e.result={language:a.language,re:a.relevance,relavance:a.relevance},a.second_best&&(e.second_best={language:a.second_best.language,re:a.second_best.relevance,relavance:a.second_best.relevance})}const N=()=>{if(!N.called){N.called=!0;var e=document.querySelectorAll("pre code");a.forEach.call(e,E)}};function T(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}function A(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach(e=>{s[e]=n})}function I(e){var n=T(e);return n&&!n.disableAutodetect}function S(e,n){var t=e;o.forEach((function(e){e[t]&&e[t](n)}))}Object.assign(t,{highlight:b,highlightAuto:v,fixMarkup:x,highlightBlock:E,configure:function(e){f=y(f,e)},initHighlighting:N,initHighlightingOnLoad:function(){window.addEventListener("DOMContentLoaded",N,!1)},registerLanguage:function(e,n){var r=null;try{r=n(t)}catch(n){if(console.error("Language definition for '{}' could not be registered.".replace("{}",e)),!l)throw n;console.error(n),r=h}r.name||(r.name=e),i[e]=r,r.rawDefinition=n.bind(null,t),r.aliases&&A(r.aliases,{languageName:e})},listLanguages:function(){return Object.keys(i)},getLanguage:T,registerAliases:A,requireLanguage:function(e){var n=T(e);if(n)return n;throw Error("The '{}' language is required, but not loaded.".replace("{}",e))},autoDetection:I,inherit:y,addPlugin:function(e){o.push(e)}}),t.debugMode=function(){l=!1},t.safeMode=function(){l=!0},t.versionString="10.1.1";for(const n in _)"object"==typeof _[n]&&e(_[n]);return Object.assign(t,_),t}({})}();"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);hljs.registerLanguage("php",function(){"use strict";return function(e){var r={begin:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},t={className:"meta",variants:[{begin:/<\?php/,relevance:10},{begin:/<\?[=]?/},{begin:/\?>/}]},a={className:"string",contains:[e.BACKSLASH_ESCAPE,t],variants:[{begin:'b"',end:'"'},{begin:"b'",end:"'"},e.inherit(e.APOS_STRING_MODE,{illegal:null}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null})]},n={variants:[e.BINARY_NUMBER_MODE,e.C_NUMBER_MODE]},i={keyword:"__CLASS__ __DIR__ __FILE__ __FUNCTION__ __LINE__ __METHOD__ __NAMESPACE__ __TRAIT__ die echo exit include include_once print require require_once array abstract and as binary bool boolean break callable case catch class clone const continue declare default do double else elseif empty enddeclare endfor endforeach endif endswitch endwhile eval extends final finally float for foreach from global goto if implements instanceof insteadof int integer interface isset iterable list new object or private protected public real return string switch throw trait try unset use var void while xor yield",literal:"false null true",built_in:"Error|0 AppendIterator ArgumentCountError ArithmeticError ArrayIterator ArrayObject AssertionError BadFunctionCallException BadMethodCallException CachingIterator CallbackFilterIterator CompileError Countable DirectoryIterator DivisionByZeroError DomainException EmptyIterator ErrorException Exception FilesystemIterator FilterIterator GlobIterator InfiniteIterator InvalidArgumentException IteratorIterator LengthException LimitIterator LogicException MultipleIterator NoRewindIterator OutOfBoundsException OutOfRangeException OuterIterator OverflowException ParentIterator ParseError RangeException RecursiveArrayIterator RecursiveCachingIterator RecursiveCallbackFilterIterator RecursiveDirectoryIterator RecursiveFilterIterator RecursiveIterator RecursiveIteratorIterator RecursiveRegexIterator RecursiveTreeIterator RegexIterator RuntimeException SeekableIterator SplDoublyLinkedList SplFileInfo SplFileObject SplFixedArray SplHeap SplMaxHeap SplMinHeap SplObjectStorage SplObserver SplObserver SplPriorityQueue SplQueue SplStack SplSubject SplSubject SplTempFileObject TypeError UnderflowException UnexpectedValueException ArrayAccess Closure Generator Iterator IteratorAggregate Serializable Throwable Traversable WeakReference Directory __PHP_Incomplete_Class parent php_user_filter self static stdClass"};return{aliases:["php","php3","php4","php5","php6","php7"],case_insensitive:!0,keywords:i,contains:[e.HASH_COMMENT_MODE,e.COMMENT("//","$",{contains:[t]}),e.COMMENT("/\\*","\\*/",{contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.COMMENT("__halt_compiler.+?;",!1,{endsWithParent:!0,keywords:"__halt_compiler"}),{className:"string",begin:/<<<['"]?\w+['"]?$/,end:/^\w+;?$/,contains:[e.BACKSLASH_ESCAPE,{className:"subst",variants:[{begin:/\$\w+/},{begin:/\{\$/,end:/\}/}]}]},t,{className:"keyword",begin:/\$this\b/},r,{begin:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{className:"function",beginKeywords:"fn function",end:/[;{]/,excludeEnd:!0,illegal:"[$%\\[]",contains:[e.UNDERSCORE_TITLE_MODE,{className:"params",begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:i,contains:["self",r,e.C_BLOCK_COMMENT_MODE,a,n]}]},{className:"class",beginKeywords:"class interface",end:"{",excludeEnd:!0,illegal:/[:\(\$"]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"namespace",end:";",illegal:/[\.']/,contains:[e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"use",end:";",contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"=>"},a,n]}}}());hljs.registerLanguage("nginx",function(){"use strict";return function(e){var n={className:"variable",variants:[{begin:/\$\d+/},{begin:/\$\{/,end:/}/},{begin:"[\\$\\@]"+e.UNDERSCORE_IDENT_RE}]},a={endsWithParent:!0,keywords:{$pattern:"[a-z/_]+",literal:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},relevance:0,illegal:"=>",contains:[e.HASH_COMMENT_MODE,{className:"string",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:/"/,end:/"/},{begin:/'/,end:/'/}]},{begin:"([a-z]+):/",end:"\\s",endsWithParent:!0,excludeEnd:!0,contains:[n]},{className:"regexp",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:"\\s\\^",end:"\\s|{|;",returnEnd:!0},{begin:"~\\*?\\s+",end:"\\s|{|;",returnEnd:!0},{begin:"\\*(\\.[a-z\\-]+)+"},{begin:"([a-z\\-]+\\.)+\\*"}]},{className:"number",begin:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{className:"number",begin:"\\b\\d+[kKmMgGdshdwy]*\\b",relevance:0},n]};return{name:"Nginx config",aliases:["nginxconf"],contains:[e.HASH_COMMENT_MODE,{begin:e.UNDERSCORE_IDENT_RE+"\\s+{",returnBegin:!0,end:"{",contains:[{className:"section",begin:e.UNDERSCORE_IDENT_RE}],relevance:0},{begin:e.UNDERSCORE_IDENT_RE+"\\s",end:";|{",returnBegin:!0,contains:[{className:"attribute",begin:e.UNDERSCORE_IDENT_RE,starts:a}],relevance:0}],illegal:"[^\\s\\}]"}}}());hljs.registerLanguage("csharp",function(){"use strict";return function(e){var n={keyword:"abstract as base bool break byte case catch char checked const continue decimal default delegate do double enum event explicit extern finally fixed float for foreach goto if implicit in int interface internal is lock long object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this try typeof uint ulong unchecked unsafe ushort using virtual void volatile while add alias ascending async await by descending dynamic equals from get global group into join let nameof on orderby partial remove select set value var when where yield",literal:"null false true"},i=e.inherit(e.TITLE_MODE,{begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},s={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}]},t=e.inherit(s,{illegal:/\n/}),l={className:"subst",begin:"{",end:"}",keywords:n},r=e.inherit(l,{illegal:/\n/}),c={className:"string",begin:/\$"/,end:'"',illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},e.BACKSLASH_ESCAPE,r]},o={className:"string",begin:/\$@"/,end:'"',contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},l]},g=e.inherit(o,{illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},r]});l.contains=[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE],r.contains=[g,c,t,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{illegal:/\n/})];var d={variants:[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},E={begin:"<",end:">",contains:[{beginKeywords:"in out"},i]},_=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",b={begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"],keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0,contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{begin:"\x3c!--|--\x3e"},{begin:""}]}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#",end:"$",keywords:{"meta-keyword":"if else elif endif define undef warning error line region endregion pragma checksum"}},d,a,{beginKeywords:"class interface",end:/[{;=]/,illegal:/[^\s:,]/,contains:[{beginKeywords:"where class"},i,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace",end:/[{;=]/,illegal:/[^\s:]/,contains:[i,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta",begin:"^\\s*\\[",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{className:"meta-string",begin:/"/,end:/"/}]},{beginKeywords:"new return throw await else",relevance:0},{className:"function",begin:"("+_+"\\s+)+"+e.IDENT_RE+"\\s*(\\<.+\\>)?\\s*\\(",returnBegin:!0,end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{begin:e.IDENT_RE+"\\s*(\\<.+\\>)?\\s*\\(",returnBegin:!0,contains:[e.TITLE_MODE,E],relevance:0},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0,contains:[d,a,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},b]}}}());hljs.registerLanguage("perl",function(){"use strict";return function(e){var n={$pattern:/[\w.]+/,keyword:"getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qq fileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmget sub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedir ioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when"},t={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:n},s={begin:"->{",end:"}"},r={variants:[{begin:/\$\d/},{begin:/[\$%@](\^\w\b|#\w+(::\w+)*|{\w+}|\w+(::\w*)*)/},{begin:/[\$%@][^\s\w{]/,relevance:0}]},i=[e.BACKSLASH_ESCAPE,t,r],a=[r,e.HASH_COMMENT_MODE,e.COMMENT("^\\=\\w","\\=cut",{endsWithParent:!0}),s,{className:"string",contains:i,variants:[{begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[",end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*\\<",end:"\\>",relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'",contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE]},{begin:"{\\w+}",contains:[],relevance:0},{begin:"-?\\w+\\s*\\=\\>",contains:[],relevance:0}]},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*",keywords:"split return print reverse grep",relevance:0,contains:[e.HASH_COMMENT_MODE,{className:"regexp",begin:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",relevance:10},{className:"regexp",begin:"(m|qr)?/",end:"/[a-z]*",contains:[e.BACKSLASH_ESCAPE],relevance:0}]},{className:"function",beginKeywords:"sub",end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}]}];return t.contains=a,s.contains=a,{name:"Perl",aliases:["pl","pm"],keywords:n,contains:a}}}());hljs.registerLanguage("swift",function(){"use strict";return function(e){var i={keyword:"#available #colorLiteral #column #else #elseif #endif #file #fileLiteral #function #if #imageLiteral #line #selector #sourceLocation _ __COLUMN__ __FILE__ __FUNCTION__ __LINE__ Any as as! as? associatedtype associativity break case catch class continue convenience default defer deinit didSet do dynamic dynamicType else enum extension fallthrough false fileprivate final for func get guard if import in indirect infix init inout internal is lazy left let mutating nil none nonmutating open operator optional override postfix precedence prefix private protocol Protocol public repeat required rethrows return right self Self set static struct subscript super switch throw throws true try try! try? Type typealias unowned var weak where while willSet",literal:"true false nil",built_in:"abs advance alignof alignofValue anyGenerator assert assertionFailure bridgeFromObjectiveC bridgeFromObjectiveCUnconditional bridgeToObjectiveC bridgeToObjectiveCUnconditional c compactMap contains count countElements countLeadingZeros debugPrint debugPrintln distance dropFirst dropLast dump encodeBitsAsWords enumerate equal fatalError filter find getBridgedObjectiveCType getVaList indices insertionSort isBridgedToObjectiveC isBridgedVerbatimToObjectiveC isUniquelyReferenced isUniquelyReferencedNonObjC join lazy lexicographicalCompare map max maxElement min minElement numericCast overlaps partition posix precondition preconditionFailure print println quickSort readLine reduce reflect reinterpretCast reverse roundUpToAlignment sizeof sizeofValue sort split startsWith stride strideof strideofValue swap toString transcode underestimateCount unsafeAddressOf unsafeBitCast unsafeDowncast unsafeUnwrap unsafeReflect withExtendedLifetime withObjectAtPlusZero withUnsafePointer withUnsafePointerToObject withUnsafeMutablePointer withUnsafeMutablePointers withUnsafePointer withUnsafePointers withVaList zip"},n=e.COMMENT("/\\*","\\*/",{contains:["self"]}),t={className:"subst",begin:/\\\(/,end:"\\)",keywords:i,contains:[]},a={className:"string",contains:[e.BACKSLASH_ESCAPE,t],variants:[{begin:/"""/,end:/"""/},{begin:/"/,end:/"/}]},r={className:"number",begin:"\\b([\\d_]+(\\.[\\deE_]+)?|0x[a-fA-F0-9_]+(\\.[a-fA-F0-9p_]+)?|0b[01_]+|0o[0-7_]+)\\b",relevance:0};return t.contains=[r],{name:"Swift",keywords:i,contains:[a,e.C_LINE_COMMENT_MODE,n,{className:"type",begin:"\\b[A-Z][\\wÀ-ʸ']*[!?]"},{className:"type",begin:"\\b[A-Z][\\wÀ-ʸ']*",relevance:0},r,{className:"function",beginKeywords:"func",end:"{",excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][0-9A-Za-z$_]*/}),{begin://},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:i,contains:["self",r,a,e.C_BLOCK_COMMENT_MODE,{begin:":"}],illegal:/["']/}],illegal:/\[|%/},{className:"class",beginKeywords:"struct protocol class extension enum",keywords:i,end:"\\{",excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/})]},{className:"meta",begin:"(@discardableResult|@warn_unused_result|@exported|@lazy|@noescape|@NSCopying|@NSManaged|@objc|@objcMembers|@convention|@required|@noreturn|@IBAction|@IBDesignable|@IBInspectable|@IBOutlet|@infix|@prefix|@postfix|@autoclosure|@testable|@available|@nonobjc|@NSApplicationMain|@UIApplicationMain|@dynamicMemberLookup|@propertyWrapper)\\b"},{beginKeywords:"import",end:/$/,contains:[e.C_LINE_COMMENT_MODE,n]}]}}}());hljs.registerLanguage("makefile",function(){"use strict";return function(e){var i={className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)",contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin:"",relevance:10,contains:[a,i,t,s,{begin:"\\[",end:"\\]",contains:[{className:"meta",begin:"",contains:[a,s,i,t]}]}]},e.COMMENT("\x3c!--","--\x3e",{relevance:10}),{begin:"<\\!\\[CDATA\\[",end:"\\]\\]>",relevance:10},n,{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",begin:")",end:">",keywords:{name:"style"},contains:[c],starts:{end:"",returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:")",end:">",keywords:{name:"script"},contains:[c],starts:{end:"<\/script>",returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:"",contains:[{className:"name",begin:/[^\/><\s]+/,relevance:0},c]}]}}}());hljs.registerLanguage("bash",function(){"use strict";return function(e){const s={};Object.assign(s,{className:"variable",variants:[{begin:/\$[\w\d#@][\w\d_]*/},{begin:/\$\{/,end:/\}/,contains:[{begin:/:-/,contains:[s]}]}]});const t={className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},n={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,t]};t.contains.push(n);const a={begin:/\$\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,s]},i=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10}),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b-?[a-z\._]+\b/,keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",_:"-ne -eq -lt -gt -f -d -e -s -l -a"},contains:[i,e.SHEBANG(),c,a,e.HASH_COMMENT_MODE,n,{className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},s]}}}());hljs.registerLanguage("c-like",function(){"use strict";return function(e){function t(e){return"(?:"+e+")?"}var n="(decltype\\(auto\\)|"+t("[a-zA-Z_]\\w*::")+"[a-zA-Z_]\\w*"+t("<.*?>")+")",r={className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},a={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",end:"'",illegal:"."},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},i={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},s={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{"meta-keyword":"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(a,{className:"meta-string"}),{className:"meta-string",begin:/<.*?>/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},o={className:"title",begin:t("[a-zA-Z_]\\w*::")+e.IDENT_RE,relevance:0},c=t("[a-zA-Z_]\\w*::")+e.IDENT_RE+"\\s*\\(",l={keyword:"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq",built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary",literal:"true false nullptr NULL"},d=[r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,i,a],_={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:l,contains:d.concat([{begin:/\(/,end:/\)/,keywords:l,contains:d.concat(["self"]),relevance:0}]),relevance:0},u={className:"function",begin:"("+n+"[\\*&\\s]+)+"+c,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:l,illegal:/[^\w\s\*&:<>]/,contains:[{begin:"decltype\\(auto\\)",keywords:l,relevance:0},{begin:c,returnBegin:!0,contains:[o],relevance:0},{className:"params",begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r,{begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:["self",e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r]}]},r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s]};return{aliases:["c","cc","h","c++","h++","hpp","hh","hxx","cxx"],keywords:l,disableAutodetect:!0,illegal:"",keywords:l,contains:["self",r]},{begin:e.IDENT_RE+"::",keywords:l},{className:"class",beginKeywords:"class struct",end:/[{;:]/,contains:[{begin://,contains:["self"]},e.TITLE_MODE]}]),exports:{preprocessor:s,strings:a,keywords:l}}}}());hljs.registerLanguage("coffeescript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);return function(r){var t={keyword:e.concat(["then","unless","until","loop","by","when","and","or","is","isnt","not"]).filter((e=>n=>!e.includes(n))(["var","const","let","function","static"])).join(" "),literal:n.concat(["yes","no","on","off"]).join(" "),built_in:a.concat(["npm","print"]).join(" ")},i="[A-Za-z$_][0-9A-Za-z$_]*",s={className:"subst",begin:/#\{/,end:/}/,keywords:t},o=[r.BINARY_NUMBER_MODE,r.inherit(r.C_NUMBER_MODE,{starts:{end:"(\\s*/)?",relevance:0}}),{className:"string",variants:[{begin:/'''/,end:/'''/,contains:[r.BACKSLASH_ESCAPE]},{begin:/'/,end:/'/,contains:[r.BACKSLASH_ESCAPE]},{begin:/"""/,end:/"""/,contains:[r.BACKSLASH_ESCAPE,s]},{begin:/"/,end:/"/,contains:[r.BACKSLASH_ESCAPE,s]}]},{className:"regexp",variants:[{begin:"///",end:"///",contains:[s,r.HASH_COMMENT_MODE]},{begin:"//[gim]{0,3}(?=\\W)",relevance:0},{begin:/\/(?![ *]).*?(?![\\]).\/[gim]{0,3}(?=\W)/}]},{begin:"@"+i},{subLanguage:"javascript",excludeBegin:!0,excludeEnd:!0,variants:[{begin:"```",end:"```"},{begin:"`",end:"`"}]}];s.contains=o;var c=r.inherit(r.TITLE_MODE,{begin:i}),l={className:"params",begin:"\\([^\\(]",returnBegin:!0,contains:[{begin:/\(/,end:/\)/,keywords:t,contains:["self"].concat(o)}]};return{name:"CoffeeScript",aliases:["coffee","cson","iced"],keywords:t,illegal:/\/\*/,contains:o.concat([r.COMMENT("###","###"),r.HASH_COMMENT_MODE,{className:"function",begin:"^\\s*"+i+"\\s*=\\s*(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[c,l]},{begin:/[:\(,=]\s*/,relevance:0,contains:[{className:"function",begin:"(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[l]}]},{className:"class",beginKeywords:"class",end:"$",illegal:/[:="\[\]]/,contains:[{beginKeywords:"extends",endsWithParent:!0,illegal:/[:="\[\]]/,contains:[c]},c]},{begin:i+":",end:":",returnBegin:!0,returnEnd:!0,relevance:0}])}}}());hljs.registerLanguage("ruby",function(){"use strict";return function(e){var n="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",a={keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",literal:"true false nil"},s={className:"doctag",begin:"@[A-Za-z]+"},i={begin:"#<",end:">"},r=[e.COMMENT("#","$",{contains:[s]}),e.COMMENT("^\\=begin","^\\=end",{contains:[s],relevance:10}),e.COMMENT("^__END__","\\n$")],c={className:"subst",begin:"#\\{",end:"}",keywords:a},t={className:"string",contains:[e.BACKSLASH_ESCAPE,c],variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:"%[qQwWx]?\\(",end:"\\)"},{begin:"%[qQwWx]?\\[",end:"\\]"},{begin:"%[qQwWx]?{",end:"}"},{begin:"%[qQwWx]?<",end:">"},{begin:"%[qQwWx]?/",end:"/"},{begin:"%[qQwWx]?%",end:"%"},{begin:"%[qQwWx]?-",end:"-"},{begin:"%[qQwWx]?\\|",end:"\\|"},{begin:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/},{begin:/<<[-~]?'?(\w+)(?:.|\n)*?\n\s*\1\b/,returnBegin:!0,contains:[{begin:/<<[-~]?'?/},e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,contains:[e.BACKSLASH_ESCAPE,c]})]}]},b={className:"params",begin:"\\(",end:"\\)",endsParent:!0,keywords:a},d=[t,i,{className:"class",beginKeywords:"class module",end:"$|;",illegal:/=/,contains:[e.inherit(e.TITLE_MODE,{begin:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{begin:"<\\s*",contains:[{begin:"("+e.IDENT_RE+"::)?"+e.IDENT_RE}]}].concat(r)},{className:"function",beginKeywords:"def",end:"$|;",contains:[e.inherit(e.TITLE_MODE,{begin:n}),b].concat(r)},{begin:e.IDENT_RE+"::"},{className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"(\\!|\\?)?:",relevance:0},{className:"symbol",begin:":(?!\\s)",contains:[t,{begin:n}],relevance:0},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{className:"params",begin:/\|/,end:/\|/,keywords:a},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[i,{className:"regexp",contains:[e.BACKSLASH_ESCAPE,c],illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:"%r{",end:"}[a-z]*"},{begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}]}].concat(r),relevance:0}].concat(r);c.contains=d,b.contains=d;var g=[{begin:/^\s*=>/,starts:{end:"$",contains:d}},{className:"meta",begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+>|(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>)",starts:{end:"$",contains:d}}];return{name:"Ruby",aliases:["rb","gemspec","podspec","thor","irb"],keywords:a,illegal:/\/\*/,contains:r.concat(g).concat(d)}}}());hljs.registerLanguage("yaml",function(){"use strict";return function(e){var n="true false yes no null",a="[\\w#;/?:@&=+$,.~*\\'()[\\]]+",s={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:"{{",end:"}}"},{begin:"%{",end:"}"}]}]},i=e.inherit(s,{variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={end:",",endsWithParent:!0,excludeEnd:!0,contains:[],keywords:n,relevance:0},t={begin:"{",end:"}",contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[",end:"\\]",contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr",variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---s*$",relevance:10},{className:"string",begin:"[\\|>]([0-9]?[+-])?[ ]*\\n( *)[\\S ]+\\n(\\2[\\S ]+\\n?)*"},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type",begin:"!<"+a+">"},{className:"type",begin:"!"+a},{className:"type",begin:"!!"+a},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"\\-(?=[ ]|$)",relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{className:"number",begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"},{className:"number",begin:e.C_NUMBER_RE+"\\b"},t,g,s],c=[...b];return c.pop(),c.push(i),l.contains=c,{name:"YAML",case_insensitive:!0,aliases:["yml","YAML"],contains:b}}}());hljs.registerLanguage("d",function(){"use strict";return function(e){var a={$pattern:e.UNDERSCORE_IDENT_RE,keyword:"abstract alias align asm assert auto body break byte case cast catch class const continue debug default delete deprecated do else enum export extern final finally for foreach foreach_reverse|10 goto if immutable import in inout int interface invariant is lazy macro mixin module new nothrow out override package pragma private protected public pure ref return scope shared static struct super switch synchronized template this throw try typedef typeid typeof union unittest version void volatile while with __FILE__ __LINE__ __gshared|10 __thread __traits __DATE__ __EOF__ __TIME__ __TIMESTAMP__ __VENDOR__ __VERSION__",built_in:"bool cdouble cent cfloat char creal dchar delegate double dstring float function idouble ifloat ireal long real short string ubyte ucent uint ulong ushort wchar wstring",literal:"false null true"},d="((0|[1-9][\\d_]*)|0[bB][01_]+|0[xX]([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*))",n="\\\\(['\"\\?\\\\abfnrtv]|u[\\dA-Fa-f]{4}|[0-7]{1,3}|x[\\dA-Fa-f]{2}|U[\\dA-Fa-f]{8})|&[a-zA-Z\\d]{2,};",t={className:"number",begin:"\\b"+d+"(L|u|U|Lu|LU|uL|UL)?",relevance:0},_={className:"number",begin:"\\b(((0[xX](([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*)\\.([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*)|\\.?([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*))[pP][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d))|((0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)(\\.\\d*|([eE][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)))|\\d+\\.(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)|\\.(0|[1-9][\\d_]*)([eE][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d))?))([fF]|L|i|[fF]i|Li)?|"+d+"(i|[fF]i|Li))",relevance:0},r={className:"string",begin:"'("+n+"|.)",end:"'",illegal:"."},i={className:"string",begin:'"',contains:[{begin:n,relevance:0}],end:'"[cwd]?'},s=e.COMMENT("\\/\\+","\\+\\/",{contains:["self"],relevance:10});return{name:"D",keywords:a,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s,{className:"string",begin:'x"[\\da-fA-F\\s\\n\\r]*"[cwd]?',relevance:10},i,{className:"string",begin:'[rq]"',end:'"[cwd]?',relevance:5},{className:"string",begin:"`",end:"`[cwd]?"},{className:"string",begin:'q"\\{',end:'\\}"'},_,t,r,{className:"meta",begin:"^#!",end:"$",relevance:5},{className:"meta",begin:"#(line)",end:"$",relevance:5},{className:"keyword",begin:"@[a-zA-Z_][a-zA-Z_\\d]*"}]}}}());hljs.registerLanguage("properties",function(){"use strict";return function(e){var n="[ \\t\\f]*",t="("+n+"[:=]"+n+"|[ \\t\\f]+)",a="([^\\\\:= \\t\\f\\n]|\\\\.)+",s={end:t,relevance:0,starts:{className:"string",end:/$/,relevance:0,contains:[{begin:"\\\\\\n"}]}};return{name:".properties",case_insensitive:!0,illegal:/\S/,contains:[e.COMMENT("^\\s*[!#]","$"),{begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+"+t,returnBegin:!0,contains:[{className:"attr",begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+",endsParent:!0,relevance:0}],starts:s},{begin:a+t,returnBegin:!0,relevance:0,contains:[{className:"meta",begin:a,endsParent:!0,relevance:0}],starts:s},{className:"attr",relevance:0,begin:a+n+"$"}]}}}());hljs.registerLanguage("http",function(){"use strict";return function(e){var n="HTTP/[0-9\\.]+";return{name:"HTTP",aliases:["https"],illegal:"\\S",contains:[{begin:"^"+n,end:"$",contains:[{className:"number",begin:"\\b\\d{3}\\b"}]},{begin:"^[A-Z]+ (.*?) "+n+"$",returnBegin:!0,end:"$",contains:[{className:"string",begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{begin:n},{className:"keyword",begin:"[A-Z]+"}]},{className:"attribute",begin:"^\\w",end:": ",excludeEnd:!0,illegal:"\\n|\\s|=",starts:{end:"$",relevance:0}},{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}]}}}());hljs.registerLanguage("haskell",function(){"use strict";return function(e){var n={variants:[e.COMMENT("--","$"),e.COMMENT("{-","-}",{contains:["self"]})]},i={className:"meta",begin:"{-#",end:"#-}"},a={className:"meta",begin:"^#",end:"$"},s={className:"type",begin:"\\b[A-Z][\\w']*",relevance:0},l={begin:"\\(",end:"\\)",illegal:'"',contains:[i,a,{className:"type",begin:"\\b[A-Z][\\w]*(\\((\\.\\.|,|\\w+)\\))?"},e.inherit(e.TITLE_MODE,{begin:"[_a-z][\\w']*"}),n]};return{name:"Haskell",aliases:["hs"],keywords:"let in if then else case of where do module import hiding qualified type data newtype deriving class instance as default infix infixl infixr foreign export ccall stdcall cplusplus jvm dotnet safe unsafe family forall mdo proc rec",contains:[{beginKeywords:"module",end:"where",keywords:"module where",contains:[l,n],illegal:"\\W\\.|;"},{begin:"\\bimport\\b",end:"$",keywords:"import qualified as hiding",contains:[l,n],illegal:"\\W\\.|;"},{className:"class",begin:"^(\\s*)?(class|instance)\\b",end:"where",keywords:"class family instance where",contains:[s,l,n]},{className:"class",begin:"\\b(data|(new)?type)\\b",end:"$",keywords:"data family type newtype deriving",contains:[i,s,l,{begin:"{",end:"}",contains:l.contains},n]},{beginKeywords:"default",end:"$",contains:[s,l,n]},{beginKeywords:"infix infixl infixr",end:"$",contains:[e.C_NUMBER_MODE,n]},{begin:"\\bforeign\\b",end:"$",keywords:"foreign import export ccall stdcall cplusplus jvm dotnet safe unsafe",contains:[s,e.QUOTE_STRING_MODE,n]},{className:"meta",begin:"#!\\/usr\\/bin\\/env runhaskell",end:"$"},i,a,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,s,e.inherit(e.TITLE_MODE,{begin:"^[_a-z][\\w']*"}),n,{begin:"->|<-"}]}}}());hljs.registerLanguage("handlebars",function(){"use strict";function e(...e){return e.map(e=>(function(e){return e?"string"==typeof e?e:e.source:null})(e)).join("")}return function(n){const a={"builtin-name":"action bindattr collection component concat debugger each each-in get hash if in input link-to loc log lookup mut outlet partial query-params render template textarea unbound unless view with yield"},t=/\[.*?\]/,s=/[^\s!"#%&'()*+,.\/;<=>@\[\\\]^`{|}~]+/,i=e("(",/'.*?'/,"|",/".*?"/,"|",t,"|",s,"|",/\.|\//,")+"),r=e("(",t,"|",s,")(?==)"),l={begin:i,lexemes:/[\w.\/]+/},c=n.inherit(l,{keywords:{literal:"true false undefined null"}}),o={begin:/\(/,end:/\)/},m={className:"attr",begin:r,relevance:0,starts:{begin:/=/,end:/=/,starts:{contains:[n.NUMBER_MODE,n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,c,o]}}},d={contains:[n.NUMBER_MODE,n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,{begin:/as\s+\|/,keywords:{keyword:"as"},end:/\|/,contains:[{begin:/\w+/}]},m,c,o],returnEnd:!0},g=n.inherit(l,{className:"name",keywords:a,starts:n.inherit(d,{end:/\)/})});o.contains=[g];const u=n.inherit(l,{keywords:a,className:"name",starts:n.inherit(d,{end:/}}/})}),b=n.inherit(l,{keywords:a,className:"name"}),h=n.inherit(l,{className:"name",keywords:a,starts:n.inherit(d,{end:/}}/})});return{name:"Handlebars",aliases:["hbs","html.hbs","html.handlebars","htmlbars"],case_insensitive:!0,subLanguage:"xml",contains:[{begin:/\\\{\{/,skip:!0},{begin:/\\\\(?=\{\{)/,skip:!0},n.COMMENT(/\{\{!--/,/--\}\}/),n.COMMENT(/\{\{!/,/\}\}/),{className:"template-tag",begin:/\{\{\{\{(?!\/)/,end:/\}\}\}\}/,contains:[u],starts:{end:/\{\{\{\{\//,returnEnd:!0,subLanguage:"xml"}},{className:"template-tag",begin:/\{\{\{\{\//,end:/\}\}\}\}/,contains:[b]},{className:"template-tag",begin:/\{\{#/,end:/\}\}/,contains:[u]},{className:"template-tag",begin:/\{\{(?=else\}\})/,end:/\}\}/,keywords:"else"},{className:"template-tag",begin:/\{\{\//,end:/\}\}/,contains:[b]},{className:"template-variable",begin:/\{\{\{/,end:/\}\}\}/,contains:[h]},{className:"template-variable",begin:/\{\{/,end:/\}\}/,contains:[h]}]}}}());hljs.registerLanguage("rust",function(){"use strict";return function(e){var n="([ui](8|16|32|64|128|size)|f(32|64))?",t="drop i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize f32 f64 str char bool Box Option Result String Vec Copy Send Sized Sync Drop Fn FnMut FnOnce ToOwned Clone Debug PartialEq PartialOrd Eq Ord AsRef AsMut Into From Default Iterator Extend IntoIterator DoubleEndedIterator ExactSizeIterator SliceConcatExt ToString assert! assert_eq! bitflags! bytes! cfg! col! concat! concat_idents! debug_assert! debug_assert_eq! env! panic! file! format! format_args! include_bin! include_str! line! local_data_key! module_path! option_env! print! println! select! stringify! try! unimplemented! unreachable! vec! write! writeln! macro_rules! assert_ne! debug_assert_ne!";return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",keyword:"abstract as async await become box break const continue crate do dyn else enum extern false final fn for if impl in let loop macro match mod move mut override priv pub ref return self Self static struct super trait true try type typeof unsafe unsized use virtual where while yield",literal:"true false Some None Ok Err",built_in:t},illegal:""}]}}}());hljs.registerLanguage("cpp",function(){"use strict";return function(e){var t=e.getLanguage("c-like").rawDefinition();return t.disableAutodetect=!1,t.name="C++",t.aliases=["cc","c++","h++","hpp","hh","hxx","cxx"],t}}());hljs.registerLanguage("ini",function(){"use strict";function e(e){return e?"string"==typeof e?e:e.source:null}function n(...n){return n.map(n=>e(n)).join("")}return function(a){var s={className:"number",relevance:0,variants:[{begin:/([\+\-]+)?[\d]+_[\d_]+/},{begin:a.NUMBER_RE}]},i=a.COMMENT();i.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];var t={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{begin:/\$\{(.*?)}/}]},r={className:"literal",begin:/\bon|off|true|false|yes|no\b/},l={className:"string",contains:[a.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}]},c={begin:/\[/,end:/\]/,contains:[i,r,t,l,s,"self"],relevance:0},g="("+[/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/].map(n=>e(n)).join("|")+")";return{name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/,contains:[i,{className:"section",begin:/\[+/,end:/\]+/},{begin:n(g,"(\\s*\\.\\s*",g,")*",n("(?=",/\s*=\s*[^#\s]/,")")),className:"attr",starts:{end:/$/,contains:[i,c,r,t,l,s]}}]}}}());hljs.registerLanguage("objectivec",function(){"use strict";return function(e){var n=/[a-zA-Z@][a-zA-Z0-9_]*/,_={$pattern:n,keyword:"@interface @class @protocol @implementation"};return{name:"Objective-C",aliases:["mm","objc","obj-c"],keywords:{$pattern:n,keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required @encode @package @import @defs @compatibility_alias __bridge __bridge_transfer __bridge_retained __bridge_retain __covariant __contravariant __kindof _Nonnull _Nullable _Null_unspecified __FUNCTION__ __PRETTY_FUNCTION__ __attribute__ getter setter retain unsafe_unretained nonnull nullable null_unspecified null_resettable class instancetype NS_DESIGNATED_INITIALIZER NS_UNAVAILABLE NS_REQUIRES_SUPER NS_RETURNS_INNER_POINTER NS_INLINE NS_AVAILABLE NS_DEPRECATED NS_ENUM NS_OPTIONS NS_SWIFT_UNAVAILABLE NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_REFINED_FOR_SWIFT NS_SWIFT_NAME NS_SWIFT_NOTHROW NS_DURING NS_HANDLER NS_ENDHANDLER NS_VALUERETURN NS_VOIDRETURN",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},illegal:"/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class",begin:"("+_.keyword.split(" ").join("|")+")\\b",end:"({|$)",excludeEnd:!0,keywords:_,contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE,relevance:0}]}}}());hljs.registerLanguage("apache",function(){"use strict";return function(e){var n={className:"number",begin:"\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?"};return{name:"Apache config",aliases:["apacheconf"],case_insensitive:!0,contains:[e.HASH_COMMENT_MODE,{className:"section",begin:"",contains:[n,{className:"number",begin:":\\d{1,5}"},e.inherit(e.QUOTE_STRING_MODE,{relevance:0})]},{className:"attribute",begin:/\w+/,relevance:0,keywords:{nomarkup:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{end:/$/,relevance:0,keywords:{literal:"on off all deny allow"},contains:[{className:"meta",begin:"\\s\\[",end:"\\]$"},{className:"variable",begin:"[\\$%]\\{",end:"\\}",contains:["self",{className:"number",begin:"[\\$%]\\d+"}]},n,{className:"number",begin:"\\d+"},e.QUOTE_STRING_MODE]}}],illegal:/\S/}}}());hljs.registerLanguage("java",function(){"use strict";function e(e){return e?"string"==typeof e?e:e.source:null}function n(e){return a("(",e,")?")}function a(...n){return n.map(n=>e(n)).join("")}function s(...n){return"("+n.map(n=>e(n)).join("|")+")"}return function(e){var t="false synchronized int abstract float private char boolean var static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",i={className:"meta",begin:"@[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*",contains:[{begin:/\(/,end:/\)/,contains:["self"]}]},r=e=>a("[",e,"]+([",e,"_]*[",e,"]+)?"),c={className:"number",variants:[{begin:`\\b(0[bB]${r("01")})[lL]?`},{begin:`\\b(0${r("0-7")})[dDfFlL]?`},{begin:a(/\b0[xX]/,s(a(r("a-fA-F0-9"),/\./,r("a-fA-F0-9")),a(r("a-fA-F0-9"),/\.?/),a(/\./,r("a-fA-F0-9"))),/([pP][+-]?(\d+))?/,/[fFdDlL]?/)},{begin:a(/\b/,s(a(/\d*\./,r("\\d")),r("\\d")),/[eE][+-]?[\d]+[dDfF]?/)},{begin:a(/\b/,r(/\d/),n(/\.?/),n(r(/\d/)),/[dDfFlL]?/)}],relevance:0};return{name:"Java",aliases:["jsp"],keywords:t,illegal:/<\/|#/,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/,relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"class",beginKeywords:"class interface",end:/[{;=]/,excludeEnd:!0,keywords:"class interface",illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"new throw return else",relevance:0},{className:"function",begin:"([À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(<[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(\\s*,\\s*[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*)*>)?\\s+)+"+e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:t,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"params",begin:/\(/,end:/\)/,keywords:t,relevance:0,contains:[i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},c,i]}}}());hljs.registerLanguage("x86asm",function(){"use strict";return function(s){return{name:"Intel x86 Assembly",case_insensitive:!0,keywords:{$pattern:"[.%]?"+s.IDENT_RE,keyword:"lock rep repe repz repne repnz xaquire xrelease bnd nobnd aaa aad aam aas adc add and arpl bb0_reset bb1_reset bound bsf bsr bswap bt btc btr bts call cbw cdq cdqe clc cld cli clts cmc cmp cmpsb cmpsd cmpsq cmpsw cmpxchg cmpxchg486 cmpxchg8b cmpxchg16b cpuid cpu_read cpu_write cqo cwd cwde daa das dec div dmint emms enter equ f2xm1 fabs fadd faddp fbld fbstp fchs fclex fcmovb fcmovbe fcmove fcmovnb fcmovnbe fcmovne fcmovnu fcmovu fcom fcomi fcomip fcomp fcompp fcos fdecstp fdisi fdiv fdivp fdivr fdivrp femms feni ffree ffreep fiadd ficom ficomp fidiv fidivr fild fimul fincstp finit fist fistp fisttp fisub fisubr fld fld1 fldcw fldenv fldl2e fldl2t fldlg2 fldln2 fldpi fldz fmul fmulp fnclex fndisi fneni fninit fnop fnsave fnstcw fnstenv fnstsw fpatan fprem fprem1 fptan frndint frstor fsave fscale fsetpm fsin fsincos fsqrt fst fstcw fstenv fstp fstsw fsub fsubp fsubr fsubrp ftst fucom fucomi fucomip fucomp fucompp fxam fxch fxtract fyl2x fyl2xp1 hlt ibts icebp idiv imul in inc incbin insb insd insw int int01 int1 int03 int3 into invd invpcid invlpg invlpga iret iretd iretq iretw jcxz jecxz jrcxz jmp jmpe lahf lar lds lea leave les lfence lfs lgdt lgs lidt lldt lmsw loadall loadall286 lodsb lodsd lodsq lodsw loop loope loopne loopnz loopz lsl lss ltr mfence monitor mov movd movq movsb movsd movsq movsw movsx movsxd movzx mul mwait neg nop not or out outsb outsd outsw packssdw packsswb packuswb paddb paddd paddsb paddsiw paddsw paddusb paddusw paddw pand pandn pause paveb pavgusb pcmpeqb pcmpeqd pcmpeqw pcmpgtb pcmpgtd pcmpgtw pdistib pf2id pfacc pfadd pfcmpeq pfcmpge pfcmpgt pfmax pfmin pfmul pfrcp pfrcpit1 pfrcpit2 pfrsqit1 pfrsqrt pfsub pfsubr pi2fd pmachriw pmaddwd pmagw pmulhriw pmulhrwa pmulhrwc pmulhw pmullw pmvgezb pmvlzb pmvnzb pmvzb pop popa popad popaw popf popfd popfq popfw por prefetch prefetchw pslld psllq psllw psrad psraw psrld psrlq psrlw psubb psubd psubsb psubsiw psubsw psubusb psubusw psubw punpckhbw punpckhdq punpckhwd punpcklbw punpckldq punpcklwd push pusha pushad pushaw pushf pushfd pushfq pushfw pxor rcl rcr rdshr rdmsr rdpmc rdtsc rdtscp ret retf retn rol ror rdm rsdc rsldt rsm rsts sahf sal salc sar sbb scasb scasd scasq scasw sfence sgdt shl shld shr shrd sidt sldt skinit smi smint smintold smsw stc std sti stosb stosd stosq stosw str sub svdc svldt svts swapgs syscall sysenter sysexit sysret test ud0 ud1 ud2b ud2 ud2a umov verr verw fwait wbinvd wrshr wrmsr xadd xbts xchg xlatb xlat xor cmove cmovz cmovne cmovnz cmova cmovnbe cmovae cmovnb cmovb cmovnae cmovbe cmovna cmovg cmovnle cmovge cmovnl cmovl cmovnge cmovle cmovng cmovc cmovnc cmovo cmovno cmovs cmovns cmovp cmovpe cmovnp cmovpo je jz jne jnz ja jnbe jae jnb jb jnae jbe jna jg jnle jge jnl jl jnge jle jng jc jnc jo jno js jns jpo jnp jpe jp sete setz setne setnz seta setnbe setae setnb setnc setb setnae setcset setbe setna setg setnle setge setnl setl setnge setle setng sets setns seto setno setpe setp setpo setnp addps addss andnps andps cmpeqps cmpeqss cmpleps cmpless cmpltps cmpltss cmpneqps cmpneqss cmpnleps cmpnless cmpnltps cmpnltss cmpordps cmpordss cmpunordps cmpunordss cmpps cmpss comiss cvtpi2ps cvtps2pi cvtsi2ss cvtss2si cvttps2pi cvttss2si divps divss ldmxcsr maxps maxss minps minss movaps movhps movlhps movlps movhlps movmskps movntps movss movups mulps mulss orps rcpps rcpss rsqrtps rsqrtss shufps sqrtps sqrtss stmxcsr subps subss ucomiss unpckhps unpcklps xorps fxrstor fxrstor64 fxsave fxsave64 xgetbv xsetbv xsave xsave64 xsaveopt xsaveopt64 xrstor xrstor64 prefetchnta prefetcht0 prefetcht1 prefetcht2 maskmovq movntq pavgb pavgw pextrw pinsrw pmaxsw pmaxub pminsw pminub pmovmskb pmulhuw psadbw pshufw pf2iw pfnacc pfpnacc pi2fw pswapd maskmovdqu clflush movntdq movnti movntpd movdqa movdqu movdq2q movq2dq paddq pmuludq pshufd pshufhw pshuflw pslldq psrldq psubq punpckhqdq punpcklqdq addpd addsd andnpd andpd cmpeqpd cmpeqsd cmplepd cmplesd cmpltpd cmpltsd cmpneqpd cmpneqsd cmpnlepd cmpnlesd cmpnltpd cmpnltsd cmpordpd cmpordsd cmpunordpd cmpunordsd cmppd comisd cvtdq2pd cvtdq2ps cvtpd2dq cvtpd2pi cvtpd2ps cvtpi2pd cvtps2dq cvtps2pd cvtsd2si cvtsd2ss cvtsi2sd cvtss2sd cvttpd2pi cvttpd2dq cvttps2dq cvttsd2si divpd divsd maxpd maxsd minpd minsd movapd movhpd movlpd movmskpd movupd mulpd mulsd orpd shufpd sqrtpd sqrtsd subpd subsd ucomisd unpckhpd unpcklpd xorpd addsubpd addsubps haddpd haddps hsubpd hsubps lddqu movddup movshdup movsldup clgi stgi vmcall vmclear vmfunc vmlaunch vmload vmmcall vmptrld vmptrst vmread vmresume vmrun vmsave vmwrite vmxoff vmxon invept invvpid pabsb pabsw pabsd palignr phaddw phaddd phaddsw phsubw phsubd phsubsw pmaddubsw pmulhrsw pshufb psignb psignw psignd extrq insertq movntsd movntss lzcnt blendpd blendps blendvpd blendvps dppd dpps extractps insertps movntdqa mpsadbw packusdw pblendvb pblendw pcmpeqq pextrb pextrd pextrq phminposuw pinsrb pinsrd pinsrq pmaxsb pmaxsd pmaxud pmaxuw pminsb pminsd pminud pminuw pmovsxbw pmovsxbd pmovsxbq pmovsxwd pmovsxwq pmovsxdq pmovzxbw pmovzxbd pmovzxbq pmovzxwd pmovzxwq pmovzxdq pmuldq pmulld ptest roundpd roundps roundsd roundss crc32 pcmpestri pcmpestrm pcmpistri pcmpistrm pcmpgtq popcnt getsec pfrcpv pfrsqrtv movbe aesenc aesenclast aesdec aesdeclast aesimc aeskeygenassist vaesenc vaesenclast vaesdec vaesdeclast vaesimc vaeskeygenassist vaddpd vaddps vaddsd vaddss vaddsubpd vaddsubps vandpd vandps vandnpd vandnps vblendpd vblendps vblendvpd vblendvps vbroadcastss vbroadcastsd vbroadcastf128 vcmpeq_ospd vcmpeqpd vcmplt_ospd vcmpltpd vcmple_ospd vcmplepd vcmpunord_qpd vcmpunordpd vcmpneq_uqpd vcmpneqpd vcmpnlt_uspd vcmpnltpd vcmpnle_uspd vcmpnlepd vcmpord_qpd vcmpordpd vcmpeq_uqpd vcmpnge_uspd vcmpngepd vcmpngt_uspd vcmpngtpd vcmpfalse_oqpd vcmpfalsepd vcmpneq_oqpd vcmpge_ospd vcmpgepd vcmpgt_ospd vcmpgtpd vcmptrue_uqpd vcmptruepd vcmplt_oqpd vcmple_oqpd vcmpunord_spd vcmpneq_uspd vcmpnlt_uqpd vcmpnle_uqpd vcmpord_spd vcmpeq_uspd vcmpnge_uqpd vcmpngt_uqpd vcmpfalse_ospd vcmpneq_ospd vcmpge_oqpd vcmpgt_oqpd vcmptrue_uspd vcmppd vcmpeq_osps vcmpeqps vcmplt_osps vcmpltps vcmple_osps vcmpleps vcmpunord_qps vcmpunordps vcmpneq_uqps vcmpneqps vcmpnlt_usps vcmpnltps vcmpnle_usps vcmpnleps vcmpord_qps vcmpordps vcmpeq_uqps vcmpnge_usps vcmpngeps vcmpngt_usps vcmpngtps vcmpfalse_oqps vcmpfalseps vcmpneq_oqps vcmpge_osps vcmpgeps vcmpgt_osps vcmpgtps vcmptrue_uqps vcmptrueps vcmplt_oqps vcmple_oqps vcmpunord_sps vcmpneq_usps vcmpnlt_uqps vcmpnle_uqps vcmpord_sps vcmpeq_usps vcmpnge_uqps vcmpngt_uqps vcmpfalse_osps vcmpneq_osps vcmpge_oqps vcmpgt_oqps vcmptrue_usps vcmpps vcmpeq_ossd vcmpeqsd vcmplt_ossd vcmpltsd vcmple_ossd vcmplesd vcmpunord_qsd vcmpunordsd vcmpneq_uqsd vcmpneqsd vcmpnlt_ussd vcmpnltsd vcmpnle_ussd vcmpnlesd vcmpord_qsd vcmpordsd vcmpeq_uqsd vcmpnge_ussd vcmpngesd vcmpngt_ussd vcmpngtsd vcmpfalse_oqsd vcmpfalsesd vcmpneq_oqsd vcmpge_ossd vcmpgesd vcmpgt_ossd vcmpgtsd vcmptrue_uqsd vcmptruesd vcmplt_oqsd vcmple_oqsd vcmpunord_ssd vcmpneq_ussd vcmpnlt_uqsd vcmpnle_uqsd vcmpord_ssd vcmpeq_ussd vcmpnge_uqsd vcmpngt_uqsd vcmpfalse_ossd vcmpneq_ossd vcmpge_oqsd vcmpgt_oqsd vcmptrue_ussd vcmpsd vcmpeq_osss vcmpeqss vcmplt_osss vcmpltss vcmple_osss vcmpless vcmpunord_qss vcmpunordss vcmpneq_uqss vcmpneqss vcmpnlt_usss vcmpnltss vcmpnle_usss vcmpnless vcmpord_qss vcmpordss vcmpeq_uqss vcmpnge_usss vcmpngess vcmpngt_usss vcmpngtss vcmpfalse_oqss vcmpfalsess vcmpneq_oqss vcmpge_osss vcmpgess vcmpgt_osss vcmpgtss vcmptrue_uqss vcmptruess vcmplt_oqss vcmple_oqss vcmpunord_sss vcmpneq_usss vcmpnlt_uqss vcmpnle_uqss vcmpord_sss vcmpeq_usss vcmpnge_uqss vcmpngt_uqss vcmpfalse_osss vcmpneq_osss vcmpge_oqss vcmpgt_oqss vcmptrue_usss vcmpss vcomisd vcomiss vcvtdq2pd vcvtdq2ps vcvtpd2dq vcvtpd2ps vcvtps2dq vcvtps2pd vcvtsd2si vcvtsd2ss vcvtsi2sd vcvtsi2ss vcvtss2sd vcvtss2si vcvttpd2dq vcvttps2dq vcvttsd2si vcvttss2si vdivpd vdivps vdivsd vdivss vdppd vdpps vextractf128 vextractps vhaddpd vhaddps vhsubpd vhsubps vinsertf128 vinsertps vlddqu vldqqu vldmxcsr vmaskmovdqu vmaskmovps vmaskmovpd vmaxpd vmaxps vmaxsd vmaxss vminpd vminps vminsd vminss vmovapd vmovaps vmovd vmovq vmovddup vmovdqa vmovqqa vmovdqu vmovqqu vmovhlps vmovhpd vmovhps vmovlhps vmovlpd vmovlps vmovmskpd vmovmskps vmovntdq vmovntqq vmovntdqa vmovntpd vmovntps vmovsd vmovshdup vmovsldup vmovss vmovupd vmovups vmpsadbw vmulpd vmulps vmulsd vmulss vorpd vorps vpabsb vpabsw vpabsd vpacksswb vpackssdw vpackuswb vpackusdw vpaddb vpaddw vpaddd vpaddq vpaddsb vpaddsw vpaddusb vpaddusw vpalignr vpand vpandn vpavgb vpavgw vpblendvb vpblendw vpcmpestri vpcmpestrm vpcmpistri vpcmpistrm vpcmpeqb vpcmpeqw vpcmpeqd vpcmpeqq vpcmpgtb vpcmpgtw vpcmpgtd vpcmpgtq vpermilpd vpermilps vperm2f128 vpextrb vpextrw vpextrd vpextrq vphaddw vphaddd vphaddsw vphminposuw vphsubw vphsubd vphsubsw vpinsrb vpinsrw vpinsrd vpinsrq vpmaddwd vpmaddubsw vpmaxsb vpmaxsw vpmaxsd vpmaxub vpmaxuw vpmaxud vpminsb vpminsw vpminsd vpminub vpminuw vpminud vpmovmskb vpmovsxbw vpmovsxbd vpmovsxbq vpmovsxwd vpmovsxwq vpmovsxdq vpmovzxbw vpmovzxbd vpmovzxbq vpmovzxwd vpmovzxwq vpmovzxdq vpmulhuw vpmulhrsw vpmulhw vpmullw vpmulld vpmuludq vpmuldq vpor vpsadbw vpshufb vpshufd vpshufhw vpshuflw vpsignb vpsignw vpsignd vpslldq vpsrldq vpsllw vpslld vpsllq vpsraw vpsrad vpsrlw vpsrld vpsrlq vptest vpsubb vpsubw vpsubd vpsubq vpsubsb vpsubsw vpsubusb vpsubusw vpunpckhbw vpunpckhwd vpunpckhdq vpunpckhqdq vpunpcklbw vpunpcklwd vpunpckldq vpunpcklqdq vpxor vrcpps vrcpss vrsqrtps vrsqrtss vroundpd vroundps vroundsd vroundss vshufpd vshufps vsqrtpd vsqrtps vsqrtsd vsqrtss vstmxcsr vsubpd vsubps vsubsd vsubss vtestps vtestpd vucomisd vucomiss vunpckhpd vunpckhps vunpcklpd vunpcklps vxorpd vxorps vzeroall vzeroupper pclmullqlqdq pclmulhqlqdq pclmullqhqdq pclmulhqhqdq pclmulqdq vpclmullqlqdq vpclmulhqlqdq vpclmullqhqdq vpclmulhqhqdq vpclmulqdq vfmadd132ps vfmadd132pd vfmadd312ps vfmadd312pd vfmadd213ps vfmadd213pd vfmadd123ps vfmadd123pd vfmadd231ps vfmadd231pd vfmadd321ps vfmadd321pd vfmaddsub132ps vfmaddsub132pd vfmaddsub312ps vfmaddsub312pd vfmaddsub213ps vfmaddsub213pd vfmaddsub123ps vfmaddsub123pd vfmaddsub231ps vfmaddsub231pd vfmaddsub321ps vfmaddsub321pd vfmsub132ps vfmsub132pd vfmsub312ps vfmsub312pd vfmsub213ps vfmsub213pd vfmsub123ps vfmsub123pd vfmsub231ps vfmsub231pd vfmsub321ps vfmsub321pd vfmsubadd132ps vfmsubadd132pd vfmsubadd312ps vfmsubadd312pd vfmsubadd213ps vfmsubadd213pd vfmsubadd123ps vfmsubadd123pd vfmsubadd231ps vfmsubadd231pd vfmsubadd321ps vfmsubadd321pd vfnmadd132ps vfnmadd132pd vfnmadd312ps vfnmadd312pd vfnmadd213ps vfnmadd213pd vfnmadd123ps vfnmadd123pd vfnmadd231ps vfnmadd231pd vfnmadd321ps vfnmadd321pd vfnmsub132ps vfnmsub132pd vfnmsub312ps vfnmsub312pd vfnmsub213ps vfnmsub213pd vfnmsub123ps vfnmsub123pd vfnmsub231ps vfnmsub231pd vfnmsub321ps vfnmsub321pd vfmadd132ss vfmadd132sd vfmadd312ss vfmadd312sd vfmadd213ss vfmadd213sd vfmadd123ss vfmadd123sd vfmadd231ss vfmadd231sd vfmadd321ss vfmadd321sd vfmsub132ss vfmsub132sd vfmsub312ss vfmsub312sd vfmsub213ss vfmsub213sd vfmsub123ss vfmsub123sd vfmsub231ss vfmsub231sd vfmsub321ss vfmsub321sd vfnmadd132ss vfnmadd132sd vfnmadd312ss vfnmadd312sd vfnmadd213ss vfnmadd213sd vfnmadd123ss vfnmadd123sd vfnmadd231ss vfnmadd231sd vfnmadd321ss vfnmadd321sd vfnmsub132ss vfnmsub132sd vfnmsub312ss vfnmsub312sd vfnmsub213ss vfnmsub213sd vfnmsub123ss vfnmsub123sd vfnmsub231ss vfnmsub231sd vfnmsub321ss vfnmsub321sd rdfsbase rdgsbase rdrand wrfsbase wrgsbase vcvtph2ps vcvtps2ph adcx adox rdseed clac stac xstore xcryptecb xcryptcbc xcryptctr xcryptcfb xcryptofb montmul xsha1 xsha256 llwpcb slwpcb lwpval lwpins vfmaddpd vfmaddps vfmaddsd vfmaddss vfmaddsubpd vfmaddsubps vfmsubaddpd vfmsubaddps vfmsubpd vfmsubps vfmsubsd vfmsubss vfnmaddpd vfnmaddps vfnmaddsd vfnmaddss vfnmsubpd vfnmsubps vfnmsubsd vfnmsubss vfrczpd vfrczps vfrczsd vfrczss vpcmov vpcomb vpcomd vpcomq vpcomub vpcomud vpcomuq vpcomuw vpcomw vphaddbd vphaddbq vphaddbw vphadddq vphaddubd vphaddubq vphaddubw vphaddudq vphadduwd vphadduwq vphaddwd vphaddwq vphsubbw vphsubdq vphsubwd vpmacsdd vpmacsdqh vpmacsdql vpmacssdd vpmacssdqh vpmacssdql vpmacsswd vpmacssww vpmacswd vpmacsww vpmadcsswd vpmadcswd vpperm vprotb vprotd vprotq vprotw vpshab vpshad vpshaq vpshaw vpshlb vpshld vpshlq vpshlw vbroadcasti128 vpblendd vpbroadcastb vpbroadcastw vpbroadcastd vpbroadcastq vpermd vpermpd vpermps vpermq vperm2i128 vextracti128 vinserti128 vpmaskmovd vpmaskmovq vpsllvd vpsllvq vpsravd vpsrlvd vpsrlvq vgatherdpd vgatherqpd vgatherdps vgatherqps vpgatherdd vpgatherqd vpgatherdq vpgatherqq xabort xbegin xend xtest andn bextr blci blcic blsi blsic blcfill blsfill blcmsk blsmsk blsr blcs bzhi mulx pdep pext rorx sarx shlx shrx tzcnt tzmsk t1mskc valignd valignq vblendmpd vblendmps vbroadcastf32x4 vbroadcastf64x4 vbroadcasti32x4 vbroadcasti64x4 vcompresspd vcompressps vcvtpd2udq vcvtps2udq vcvtsd2usi vcvtss2usi vcvttpd2udq vcvttps2udq vcvttsd2usi vcvttss2usi vcvtudq2pd vcvtudq2ps vcvtusi2sd vcvtusi2ss vexpandpd vexpandps vextractf32x4 vextractf64x4 vextracti32x4 vextracti64x4 vfixupimmpd vfixupimmps vfixupimmsd vfixupimmss vgetexppd vgetexpps vgetexpsd vgetexpss vgetmantpd vgetmantps vgetmantsd vgetmantss vinsertf32x4 vinsertf64x4 vinserti32x4 vinserti64x4 vmovdqa32 vmovdqa64 vmovdqu32 vmovdqu64 vpabsq vpandd vpandnd vpandnq vpandq vpblendmd vpblendmq vpcmpltd vpcmpled vpcmpneqd vpcmpnltd vpcmpnled vpcmpd vpcmpltq vpcmpleq vpcmpneqq vpcmpnltq vpcmpnleq vpcmpq vpcmpequd vpcmpltud vpcmpleud vpcmpnequd vpcmpnltud vpcmpnleud vpcmpud vpcmpequq vpcmpltuq vpcmpleuq vpcmpnequq vpcmpnltuq vpcmpnleuq vpcmpuq vpcompressd vpcompressq vpermi2d vpermi2pd vpermi2ps vpermi2q vpermt2d vpermt2pd vpermt2ps vpermt2q vpexpandd vpexpandq vpmaxsq vpmaxuq vpminsq vpminuq vpmovdb vpmovdw vpmovqb vpmovqd vpmovqw vpmovsdb vpmovsdw vpmovsqb vpmovsqd vpmovsqw vpmovusdb vpmovusdw vpmovusqb vpmovusqd vpmovusqw vpord vporq vprold vprolq vprolvd vprolvq vprord vprorq vprorvd vprorvq vpscatterdd vpscatterdq vpscatterqd vpscatterqq vpsraq vpsravq vpternlogd vpternlogq vptestmd vptestmq vptestnmd vptestnmq vpxord vpxorq vrcp14pd vrcp14ps vrcp14sd vrcp14ss vrndscalepd vrndscaleps vrndscalesd vrndscaless vrsqrt14pd vrsqrt14ps vrsqrt14sd vrsqrt14ss vscalefpd vscalefps vscalefsd vscalefss vscatterdpd vscatterdps vscatterqpd vscatterqps vshuff32x4 vshuff64x2 vshufi32x4 vshufi64x2 kandnw kandw kmovw knotw kortestw korw kshiftlw kshiftrw kunpckbw kxnorw kxorw vpbroadcastmb2q vpbroadcastmw2d vpconflictd vpconflictq vplzcntd vplzcntq vexp2pd vexp2ps vrcp28pd vrcp28ps vrcp28sd vrcp28ss vrsqrt28pd vrsqrt28ps vrsqrt28sd vrsqrt28ss vgatherpf0dpd vgatherpf0dps vgatherpf0qpd vgatherpf0qps vgatherpf1dpd vgatherpf1dps vgatherpf1qpd vgatherpf1qps vscatterpf0dpd vscatterpf0dps vscatterpf0qpd vscatterpf0qps vscatterpf1dpd vscatterpf1dps vscatterpf1qpd vscatterpf1qps prefetchwt1 bndmk bndcl bndcu bndcn bndmov bndldx bndstx sha1rnds4 sha1nexte sha1msg1 sha1msg2 sha256rnds2 sha256msg1 sha256msg2 hint_nop0 hint_nop1 hint_nop2 hint_nop3 hint_nop4 hint_nop5 hint_nop6 hint_nop7 hint_nop8 hint_nop9 hint_nop10 hint_nop11 hint_nop12 hint_nop13 hint_nop14 hint_nop15 hint_nop16 hint_nop17 hint_nop18 hint_nop19 hint_nop20 hint_nop21 hint_nop22 hint_nop23 hint_nop24 hint_nop25 hint_nop26 hint_nop27 hint_nop28 hint_nop29 hint_nop30 hint_nop31 hint_nop32 hint_nop33 hint_nop34 hint_nop35 hint_nop36 hint_nop37 hint_nop38 hint_nop39 hint_nop40 hint_nop41 hint_nop42 hint_nop43 hint_nop44 hint_nop45 hint_nop46 hint_nop47 hint_nop48 hint_nop49 hint_nop50 hint_nop51 hint_nop52 hint_nop53 hint_nop54 hint_nop55 hint_nop56 hint_nop57 hint_nop58 hint_nop59 hint_nop60 hint_nop61 hint_nop62 hint_nop63",built_in:"ip eip rip al ah bl bh cl ch dl dh sil dil bpl spl r8b r9b r10b r11b r12b r13b r14b r15b ax bx cx dx si di bp sp r8w r9w r10w r11w r12w r13w r14w r15w eax ebx ecx edx esi edi ebp esp eip r8d r9d r10d r11d r12d r13d r14d r15d rax rbx rcx rdx rsi rdi rbp rsp r8 r9 r10 r11 r12 r13 r14 r15 cs ds es fs gs ss st st0 st1 st2 st3 st4 st5 st6 st7 mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7 xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7 xmm8 xmm9 xmm10 xmm11 xmm12 xmm13 xmm14 xmm15 xmm16 xmm17 xmm18 xmm19 xmm20 xmm21 xmm22 xmm23 xmm24 xmm25 xmm26 xmm27 xmm28 xmm29 xmm30 xmm31 ymm0 ymm1 ymm2 ymm3 ymm4 ymm5 ymm6 ymm7 ymm8 ymm9 ymm10 ymm11 ymm12 ymm13 ymm14 ymm15 ymm16 ymm17 ymm18 ymm19 ymm20 ymm21 ymm22 ymm23 ymm24 ymm25 ymm26 ymm27 ymm28 ymm29 ymm30 ymm31 zmm0 zmm1 zmm2 zmm3 zmm4 zmm5 zmm6 zmm7 zmm8 zmm9 zmm10 zmm11 zmm12 zmm13 zmm14 zmm15 zmm16 zmm17 zmm18 zmm19 zmm20 zmm21 zmm22 zmm23 zmm24 zmm25 zmm26 zmm27 zmm28 zmm29 zmm30 zmm31 k0 k1 k2 k3 k4 k5 k6 k7 bnd0 bnd1 bnd2 bnd3 cr0 cr1 cr2 cr3 cr4 cr8 dr0 dr1 dr2 dr3 dr8 tr3 tr4 tr5 tr6 tr7 r0 r1 r2 r3 r4 r5 r6 r7 r0b r1b r2b r3b r4b r5b r6b r7b r0w r1w r2w r3w r4w r5w r6w r7w r0d r1d r2d r3d r4d r5d r6d r7d r0h r1h r2h r3h r0l r1l r2l r3l r4l r5l r6l r7l r8l r9l r10l r11l r12l r13l r14l r15l db dw dd dq dt ddq do dy dz resb resw resd resq rest resdq reso resy resz incbin equ times byte word dword qword nosplit rel abs seg wrt strict near far a32 ptr",meta:"%define %xdefine %+ %undef %defstr %deftok %assign %strcat %strlen %substr %rotate %elif %else %endif %if %ifmacro %ifctx %ifidn %ifidni %ifid %ifnum %ifstr %iftoken %ifempty %ifenv %error %warning %fatal %rep %endrep %include %push %pop %repl %pathsearch %depend %use %arg %stacksize %local %line %comment %endcomment .nolist __FILE__ __LINE__ __SECT__ __BITS__ __OUTPUT_FORMAT__ __DATE__ __TIME__ __DATE_NUM__ __TIME_NUM__ __UTC_DATE__ __UTC_TIME__ __UTC_DATE_NUM__ __UTC_TIME_NUM__ __PASS__ struc endstruc istruc at iend align alignb sectalign daz nodaz up down zero default option assume public bits use16 use32 use64 default section segment absolute extern global common cpu float __utf16__ __utf16le__ __utf16be__ __utf32__ __utf32le__ __utf32be__ __float8__ __float16__ __float32__ __float64__ __float80m__ __float80e__ __float128l__ __float128h__ __Infinity__ __QNaN__ __SNaN__ Inf NaN QNaN SNaN float8 float16 float32 float64 float80m float80e float128l float128h __FLOAT_DAZ__ __FLOAT_ROUND__ __FLOAT__"},contains:[s.COMMENT(";","$",{relevance:0}),{className:"number",variants:[{begin:"\\b(?:([0-9][0-9_]*)?\\.[0-9_]*(?:[eE][+-]?[0-9_]+)?|(0[Xx])?[0-9][0-9_]*\\.?[0-9_]*(?:[pP](?:[+-]?[0-9_]+)?)?)\\b",relevance:0},{begin:"\\$[0-9][0-9A-Fa-f]*",relevance:0},{begin:"\\b(?:[0-9A-Fa-f][0-9A-Fa-f_]*[Hh]|[0-9][0-9_]*[DdTt]?|[0-7][0-7_]*[QqOo]|[0-1][0-1_]*[BbYy])\\b"},{begin:"\\b(?:0[Xx][0-9A-Fa-f_]+|0[DdTt][0-9_]+|0[QqOo][0-7_]+|0[BbYy][0-1_]+)\\b"}]},s.QUOTE_STRING_MODE,{className:"string",variants:[{begin:"'",end:"[^\\\\]'"},{begin:"`",end:"[^\\\\]`"}],relevance:0},{className:"symbol",variants:[{begin:"^\\s*[A-Za-z._?][A-Za-z0-9_$#@~.?]*(:|\\s+label)"},{begin:"^\\s*%%[A-Za-z0-9_$#@~.?]*:"}],relevance:0},{className:"subst",begin:"%[0-9]+",relevance:0},{className:"subst",begin:"%!S+",relevance:0},{className:"meta",begin:/^\s*\.[\w_-]+/}]}}}());hljs.registerLanguage("kotlin",function(){"use strict";return function(e){var n={keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual trait volatile transient native default",built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing",literal:"true false null"},a={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@"},i={className:"subst",begin:"\\${",end:"}",contains:[e.C_NUMBER_MODE]},s={className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},t={className:"string",variants:[{begin:'"""',end:'"""(?=[^"])',contains:[s,i]},{begin:"'",end:"'",illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/,contains:[e.BACKSLASH_ESCAPE,s,i]}]};i.contains.push(t);var r={className:"meta",begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?"},l={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/,end:/\)/,contains:[e.inherit(t,{className:"meta-string"})]}]},c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),o={variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/,contains:[]}]},d=o;return d.variants[1].contains=[o],o.variants[1].contains=[d],{name:"Kotlin",aliases:["kt"],keywords:n,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,c,{className:"keyword",begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol",begin:/@\w+/}]}},a,r,l,{className:"function",beginKeywords:"fun",end:"[(]|$",returnBegin:!0,excludeEnd:!0,keywords:n,illegal:/fun\s+(<.*>)?[^\s\(]+(\s+[^\s\(]+)\s*=/,relevance:5,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://,keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/,endsWithParent:!0,contains:[o,e.C_LINE_COMMENT_MODE,c],relevance:0},e.C_LINE_COMMENT_MODE,c,r,l,t,e.C_NUMBER_MODE]},c]},{className:"class",beginKeywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0,illegal:"extends implements",contains:[{beginKeywords:"public protected internal private constructor"},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,]|$/,excludeBegin:!0,returnEnd:!0},r,l]},t,{className:"meta",begin:"^#!/usr/bin/env",end:"$",illegal:"\n"},{className:"number",begin:"\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\d]+[\\d_]+[\\d]+|[\\d]+)(\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))?|\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))([eE][-+]?\\d+)?)[lLfF]?",relevance:0}]}}}());hljs.registerLanguage("armasm",function(){"use strict";return function(s){const e={variants:[s.COMMENT("^[ \\t]*(?=#)","$",{relevance:0,excludeBegin:!0}),s.COMMENT("[;@]","$",{relevance:0}),s.C_LINE_COMMENT_MODE,s.C_BLOCK_COMMENT_MODE]};return{name:"ARM Assembly",case_insensitive:!0,aliases:["arm"],keywords:{$pattern:"\\.?"+s.IDENT_RE,meta:".2byte .4byte .align .ascii .asciz .balign .byte .code .data .else .end .endif .endm .endr .equ .err .exitm .extern .global .hword .if .ifdef .ifndef .include .irp .long .macro .rept .req .section .set .skip .space .text .word .arm .thumb .code16 .code32 .force_thumb .thumb_func .ltorg ALIAS ALIGN ARM AREA ASSERT ATTR CN CODE CODE16 CODE32 COMMON CP DATA DCB DCD DCDU DCDO DCFD DCFDU DCI DCQ DCQU DCW DCWU DN ELIF ELSE END ENDFUNC ENDIF ENDP ENTRY EQU EXPORT EXPORTAS EXTERN FIELD FILL FUNCTION GBLA GBLL GBLS GET GLOBAL IF IMPORT INCBIN INCLUDE INFO KEEP LCLA LCLL LCLS LTORG MACRO MAP MEND MEXIT NOFP OPT PRESERVE8 PROC QN READONLY RELOC REQUIRE REQUIRE8 RLIST FN ROUT SETA SETL SETS SN SPACE SUBT THUMB THUMBX TTL WHILE WEND ",built_in:"r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 pc lr sp ip sl sb fp a1 a2 a3 a4 v1 v2 v3 v4 v5 v6 v7 v8 f0 f1 f2 f3 f4 f5 f6 f7 p0 p1 p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 p12 p13 p14 p15 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 q0 q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14 q15 cpsr_c cpsr_x cpsr_s cpsr_f cpsr_cx cpsr_cxs cpsr_xs cpsr_xsf cpsr_sf cpsr_cxsf spsr_c spsr_x spsr_s spsr_f spsr_cx spsr_cxs spsr_xs spsr_xsf spsr_sf spsr_cxsf s0 s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 s12 s13 s14 s15 s16 s17 s18 s19 s20 s21 s22 s23 s24 s25 s26 s27 s28 s29 s30 s31 d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 d13 d14 d15 d16 d17 d18 d19 d20 d21 d22 d23 d24 d25 d26 d27 d28 d29 d30 d31 {PC} {VAR} {TRUE} {FALSE} {OPT} {CONFIG} {ENDIAN} {CODESIZE} {CPU} {FPU} {ARCHITECTURE} {PCSTOREOFFSET} {ARMASM_VERSION} {INTER} {ROPI} {RWPI} {SWST} {NOSWST} . @"},contains:[{className:"keyword",begin:"\\b(adc|(qd?|sh?|u[qh]?)?add(8|16)?|usada?8|(q|sh?|u[qh]?)?(as|sa)x|and|adrl?|sbc|rs[bc]|asr|b[lx]?|blx|bxj|cbn?z|tb[bh]|bic|bfc|bfi|[su]bfx|bkpt|cdp2?|clz|clrex|cmp|cmn|cpsi[ed]|cps|setend|dbg|dmb|dsb|eor|isb|it[te]{0,3}|lsl|lsr|ror|rrx|ldm(([id][ab])|f[ds])?|ldr((s|ex)?[bhd])?|movt?|mvn|mra|mar|mul|[us]mull|smul[bwt][bt]|smu[as]d|smmul|smmla|mla|umlaal|smlal?([wbt][bt]|d)|mls|smlsl?[ds]|smc|svc|sev|mia([bt]{2}|ph)?|mrr?c2?|mcrr2?|mrs|msr|orr|orn|pkh(tb|bt)|rbit|rev(16|sh)?|sel|[su]sat(16)?|nop|pop|push|rfe([id][ab])?|stm([id][ab])?|str(ex)?[bhd]?|(qd?)?sub|(sh?|q|u[qh]?)?sub(8|16)|[su]xt(a?h|a?b(16)?)|srs([id][ab])?|swpb?|swi|smi|tst|teq|wfe|wfi|yield)(eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al|hs|lo)?[sptrx]?(?=\\s)"},e,s.QUOTE_STRING_MODE,{className:"string",begin:"'",end:"[^\\\\]'",relevance:0},{className:"title",begin:"\\|",end:"\\|",illegal:"\\n",relevance:0},{className:"number",variants:[{begin:"[#$=]?0x[0-9a-f]+"},{begin:"[#$=]?0b[01]+"},{begin:"[#$=]\\d+"},{begin:"\\b\\d+"}],relevance:0},{className:"symbol",variants:[{begin:"^[ \\t]*[a-z_\\.\\$][a-z0-9_\\.\\$]+:"},{begin:"^[a-z_\\.\\$][a-z0-9_\\.\\$]+"},{begin:"[=#]\\w+"}],relevance:0}]}}}());hljs.registerLanguage("go",function(){"use strict";return function(e){var n={keyword:"break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune",literal:"true false iota nil",built_in:"append cap close complex copy imag len make new panic print println real recover delete"};return{name:"Go",aliases:["golang"],keywords:n,illegal:">>|\.\.\.) /},i={className:"subst",begin:/\{/,end:/\}/,keywords:n,illegal:/#/},s={begin:/\{\{/,relevance:0},r={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:/(u|b)?r?'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(u|b)?r?"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(fr|rf|f)'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(fr|rf|f)"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(u|r|ur)'/,end:/'/,relevance:10},{begin:/(u|r|ur)"/,end:/"/,relevance:10},{begin:/(b|br)'/,end:/'/},{begin:/(b|br)"/,end:/"/},{begin:/(fr|rf|f)'/,end:/'/,contains:[e.BACKSLASH_ESCAPE,s,i]},{begin:/(fr|rf|f)"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,i]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},l={className:"number",relevance:0,variants:[{begin:e.BINARY_NUMBER_RE+"[lLjJ]?"},{begin:"\\b(0o[0-7]+)[lLjJ]?"},{begin:e.C_NUMBER_RE+"[lLjJ]?"}]},t={className:"params",variants:[{begin:/\(\s*\)/,skip:!0,className:null},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:["self",a,l,r,e.HASH_COMMENT_MODE]}]};return i.contains=[r,l,a],{name:"Python",aliases:["py","gyp","ipython"],keywords:n,illegal:/(<\/|->|\?)|=>/,contains:[a,l,{beginKeywords:"if",relevance:0},r,e.HASH_COMMENT_MODE,{variants:[{className:"function",beginKeywords:"def"},{className:"class",beginKeywords:"class"}],end:/:/,illegal:/[${=;\n,]/,contains:[e.UNDERSCORE_TITLE_MODE,t,{begin:/->/,endsWithParent:!0,keywords:"None"}]},{className:"meta",begin:/^[\t ]*@/,end:/$/},{begin:/\b(print|exec)\(/}]}}}());hljs.registerLanguage("shell",function(){"use strict";return function(s){return{name:"Shell Session",aliases:["console"],contains:[{className:"meta",begin:"^\\s{0,3}[/\\w\\d\\[\\]()@-]*[>%$#]",starts:{end:"$",subLanguage:"bash"}}]}}}());hljs.registerLanguage("scala",function(){"use strict";return function(e){var n={className:"subst",variants:[{begin:"\\$[A-Za-z0-9_]+"},{begin:"\\${",end:"}"}]},a={className:"string",variants:[{begin:'"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:'"""',end:'"""',relevance:10},{begin:'[a-z]+"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE,n]},{className:"string",begin:'[a-z]+"""',end:'"""',contains:[n],relevance:10}]},s={className:"type",begin:"\\b[A-Z][A-Za-z0-9_]*",relevance:0},t={className:"title",begin:/[^0-9\n\t "'(),.`{}\[\]:;][^\n\t "'(),.`{}\[\]:;]+|[^0-9\n\t "'(),.`{}\[\]:;=]/,relevance:0},i={className:"class",beginKeywords:"class object trait type",end:/[:={\[\n;]/,excludeEnd:!0,contains:[{beginKeywords:"extends with",relevance:10},{begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[s]},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[s]},t]},l={className:"function",beginKeywords:"def",end:/[:={\[(\n;]/,excludeEnd:!0,contains:[t]};return{name:"Scala",keywords:{literal:"true false null",keyword:"type yield lazy override def with val var sealed abstract private trait object if forSome for while throw finally protected extends import final return else break new catch super class case package default try this match continue throws implicit"},contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,{className:"symbol",begin:"'\\w[\\w\\d_]*(?!')"},s,l,i,e.C_NUMBER_MODE,{className:"meta",begin:"@[A-Za-z]+"}]}}}());hljs.registerLanguage("julia",function(){"use strict";return function(e){var r="[A-Za-z_\\u00A1-\\uFFFF][A-Za-z_0-9\\u00A1-\\uFFFF]*",t={$pattern:r,keyword:"in isa where baremodule begin break catch ccall const continue do else elseif end export false finally for function global if import importall let local macro module quote return true try using while type immutable abstract bitstype typealias ",literal:"true false ARGS C_NULL DevNull ENDIAN_BOM ENV I Inf Inf16 Inf32 Inf64 InsertionSort JULIA_HOME LOAD_PATH MergeSort NaN NaN16 NaN32 NaN64 PROGRAM_FILE QuickSort RoundDown RoundFromZero RoundNearest RoundNearestTiesAway RoundNearestTiesUp RoundToZero RoundUp STDERR STDIN STDOUT VERSION catalan e|0 eu|0 eulergamma golden im nothing pi γ π φ ",built_in:"ANY AbstractArray AbstractChannel AbstractFloat AbstractMatrix AbstractRNG AbstractSerializer AbstractSet AbstractSparseArray AbstractSparseMatrix AbstractSparseVector AbstractString AbstractUnitRange AbstractVecOrMat AbstractVector Any ArgumentError Array AssertionError Associative Base64DecodePipe Base64EncodePipe Bidiagonal BigFloat BigInt BitArray BitMatrix BitVector Bool BoundsError BufferStream CachingPool CapturedException CartesianIndex CartesianRange Cchar Cdouble Cfloat Channel Char Cint Cintmax_t Clong Clonglong ClusterManager Cmd CodeInfo Colon Complex Complex128 Complex32 Complex64 CompositeException Condition ConjArray ConjMatrix ConjVector Cptrdiff_t Cshort Csize_t Cssize_t Cstring Cuchar Cuint Cuintmax_t Culong Culonglong Cushort Cwchar_t Cwstring DataType Date DateFormat DateTime DenseArray DenseMatrix DenseVecOrMat DenseVector Diagonal Dict DimensionMismatch Dims DirectIndexString Display DivideError DomainError EOFError EachLine Enum Enumerate ErrorException Exception ExponentialBackOff Expr Factorization FileMonitor Float16 Float32 Float64 Function Future GlobalRef GotoNode HTML Hermitian IO IOBuffer IOContext IOStream IPAddr IPv4 IPv6 IndexCartesian IndexLinear IndexStyle InexactError InitError Int Int128 Int16 Int32 Int64 Int8 IntSet Integer InterruptException InvalidStateException Irrational KeyError LabelNode LinSpace LineNumberNode LoadError LowerTriangular MIME Matrix MersenneTwister Method MethodError MethodTable Module NTuple NewvarNode NullException Nullable Number ObjectIdDict OrdinalRange OutOfMemoryError OverflowError Pair ParseError PartialQuickSort PermutedDimsArray Pipe PollingFileWatcher ProcessExitedException Ptr QuoteNode RandomDevice Range RangeIndex Rational RawFD ReadOnlyMemoryError Real ReentrantLock Ref Regex RegexMatch RemoteChannel RemoteException RevString RoundingMode RowVector SSAValue SegmentationFault SerializationState Set SharedArray SharedMatrix SharedVector Signed SimpleVector Slot SlotNumber SparseMatrixCSC SparseVector StackFrame StackOverflowError StackTrace StepRange StepRangeLen StridedArray StridedMatrix StridedVecOrMat StridedVector String SubArray SubString SymTridiagonal Symbol Symmetric SystemError TCPSocket Task Text TextDisplay Timer Tridiagonal Tuple Type TypeError TypeMapEntry TypeMapLevel TypeName TypeVar TypedSlot UDPSocket UInt UInt128 UInt16 UInt32 UInt64 UInt8 UndefRefError UndefVarError UnicodeError UniformScaling Union UnionAll UnitRange Unsigned UpperTriangular Val Vararg VecElement VecOrMat Vector VersionNumber Void WeakKeyDict WeakRef WorkerConfig WorkerPool "},a={keywords:t,illegal:/<\//},n={className:"subst",begin:/\$\(/,end:/\)/,keywords:t},o={className:"variable",begin:"\\$"+r},i={className:"string",contains:[e.BACKSLASH_ESCAPE,n,o],variants:[{begin:/\w*"""/,end:/"""\w*/,relevance:10},{begin:/\w*"/,end:/"\w*/}]},l={className:"string",contains:[e.BACKSLASH_ESCAPE,n,o],begin:"`",end:"`"},s={className:"meta",begin:"@"+r};return a.name="Julia",a.contains=[{className:"number",begin:/(\b0x[\d_]*(\.[\d_]*)?|0x\.\d[\d_]*)p[-+]?\d+|\b0[box][a-fA-F0-9][a-fA-F0-9_]*|(\b\d[\d_]*(\.[\d_]*)?|\.\d[\d_]*)([eEfF][-+]?\d+)?/,relevance:0},{className:"string",begin:/'(.|\\[xXuU][a-zA-Z0-9]+)'/},i,l,s,{className:"comment",variants:[{begin:"#=",end:"=#",relevance:10},{begin:"#",end:"$"}]},e.HASH_COMMENT_MODE,{className:"keyword",begin:"\\b(((abstract|primitive)\\s+)type|(mutable\\s+)?struct)\\b"},{begin:/<:/}],n.contains=a.contains,a}}());hljs.registerLanguage("php-template",function(){"use strict";return function(n){return{name:"PHP template",subLanguage:"xml",contains:[{begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*",end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0},n.inherit(n.APOS_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0}),n.inherit(n.QUOTE_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0})]}]}}}());hljs.registerLanguage("scss",function(){"use strict";return function(e){var t={className:"variable",begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b"},i={className:"number",begin:"#[0-9A-Fa-f]+"};return e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{name:"SCSS",case_insensitive:!0,illegal:"[=/|']",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"selector-id",begin:"\\#[A-Za-z0-9_-]+",relevance:0},{className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0},{className:"selector-attr",begin:"\\[",end:"\\]",illegal:"$"},{className:"selector-tag",begin:"\\b(a|abbr|acronym|address|area|article|aside|audio|b|base|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|frame|frameset|(h[1-6])|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|mark|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|samp|script|section|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|tt|ul|var|video)\\b",relevance:0},{className:"selector-pseudo",begin:":(visited|valid|root|right|required|read-write|read-only|out-range|optional|only-of-type|only-child|nth-of-type|nth-last-of-type|nth-last-child|nth-child|not|link|left|last-of-type|last-child|lang|invalid|indeterminate|in-range|hover|focus|first-of-type|first-line|first-letter|first-child|first|enabled|empty|disabled|default|checked|before|after|active)"},{className:"selector-pseudo",begin:"::(after|before|choices|first-letter|first-line|repeat-index|repeat-item|selection|value)"},t,{className:"attribute",begin:"\\b(src|z-index|word-wrap|word-spacing|word-break|width|widows|white-space|visibility|vertical-align|unicode-bidi|transition-timing-function|transition-property|transition-duration|transition-delay|transition|transform-style|transform-origin|transform|top|text-underline-position|text-transform|text-shadow|text-rendering|text-overflow|text-indent|text-decoration-style|text-decoration-line|text-decoration-color|text-decoration|text-align-last|text-align|tab-size|table-layout|right|resize|quotes|position|pointer-events|perspective-origin|perspective|page-break-inside|page-break-before|page-break-after|padding-top|padding-right|padding-left|padding-bottom|padding|overflow-y|overflow-x|overflow-wrap|overflow|outline-width|outline-style|outline-offset|outline-color|outline|orphans|order|opacity|object-position|object-fit|normal|none|nav-up|nav-right|nav-left|nav-index|nav-down|min-width|min-height|max-width|max-height|mask|marks|margin-top|margin-right|margin-left|margin-bottom|margin|list-style-type|list-style-position|list-style-image|list-style|line-height|letter-spacing|left|justify-content|initial|inherit|ime-mode|image-orientation|image-resolution|image-rendering|icon|hyphens|height|font-weight|font-variant-ligatures|font-variant|font-style|font-stretch|font-size-adjust|font-size|font-language-override|font-kerning|font-feature-settings|font-family|font|float|flex-wrap|flex-shrink|flex-grow|flex-flow|flex-direction|flex-basis|flex|filter|empty-cells|display|direction|cursor|counter-reset|counter-increment|content|column-width|column-span|column-rule-width|column-rule-style|column-rule-color|column-rule|column-gap|column-fill|column-count|columns|color|clip-path|clip|clear|caption-side|break-inside|break-before|break-after|box-sizing|box-shadow|box-decoration-break|bottom|border-width|border-top-width|border-top-style|border-top-right-radius|border-top-left-radius|border-top-color|border-top|border-style|border-spacing|border-right-width|border-right-style|border-right-color|border-right|border-radius|border-left-width|border-left-style|border-left-color|border-left|border-image-width|border-image-source|border-image-slice|border-image-repeat|border-image-outset|border-image|border-color|border-collapse|border-bottom-width|border-bottom-style|border-bottom-right-radius|border-bottom-left-radius|border-bottom-color|border-bottom|border|background-size|background-repeat|background-position|background-origin|background-image|background-color|background-clip|background-attachment|background-blend-mode|background|backface-visibility|auto|animation-timing-function|animation-play-state|animation-name|animation-iteration-count|animation-fill-mode|animation-duration|animation-direction|animation-delay|animation|align-self|align-items|align-content)\\b",illegal:"[^\\s]"},{begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b"},{begin:":",end:";",contains:[t,i,e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{className:"meta",begin:"!important"}]},{begin:"@(page|font-face)",lexemes:"@[a-z-]+",keywords:"@page @font-face"},{begin:"@",end:"[{;]",returnBegin:!0,keywords:"and or not only",contains:[{begin:"@[a-z-]+",className:"keyword"},t,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,i,e.CSS_NUMBER_MODE]}]}}}());hljs.registerLanguage("r",function(){"use strict";return function(e){var n="([a-zA-Z]|\\.[a-zA-Z.])[a-zA-Z0-9._]*";return{name:"R",contains:[e.HASH_COMMENT_MODE,{begin:n,keywords:{$pattern:n,keyword:"function if in break next repeat else for return switch while try tryCatch stop warning require library attach detach source setMethod setGeneric setGroupGeneric setClass ...",literal:"NULL NA TRUE FALSE T F Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10"},relevance:0},{className:"number",begin:"0[xX][0-9a-fA-F]+[Li]?\\b",relevance:0},{className:"number",begin:"\\d+(?:[eE][+\\-]?\\d*)?L\\b",relevance:0},{className:"number",begin:"\\d+\\.(?!\\d)(?:i\\b)?",relevance:0},{className:"number",begin:"\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d*)?i?\\b",relevance:0},{className:"number",begin:"\\.\\d+(?:[eE][+\\-]?\\d*)?i?\\b",relevance:0},{begin:"`",end:"`",relevance:0},{className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:'"',end:'"'},{begin:"'",end:"'"}]}]}}}());hljs.registerLanguage("sql",function(){"use strict";return function(e){var t=e.COMMENT("--","$");return{name:"SQL",case_insensitive:!0,illegal:/[<>{}*]/,contains:[{beginKeywords:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup revoke comment values with",end:/;/,endsWithParent:!0,keywords:{$pattern:/[\w\.]+/,keyword:"as abort abs absolute acc acce accep accept access accessed accessible account acos action activate add addtime admin administer advanced advise aes_decrypt aes_encrypt after agent aggregate ali alia alias all allocate allow alter always analyze ancillary and anti any anydata anydataset anyschema anytype apply archive archived archivelog are as asc ascii asin assembly assertion associate asynchronous at atan atn2 attr attri attrib attribu attribut attribute attributes audit authenticated authentication authid authors auto autoallocate autodblink autoextend automatic availability avg backup badfile basicfile before begin beginning benchmark between bfile bfile_base big bigfile bin binary_double binary_float binlog bit_and bit_count bit_length bit_or bit_xor bitmap blob_base block blocksize body both bound bucket buffer_cache buffer_pool build bulk by byte byteordermark bytes cache caching call calling cancel capacity cascade cascaded case cast catalog category ceil ceiling chain change changed char_base char_length character_length characters characterset charindex charset charsetform charsetid check checksum checksum_agg child choose chr chunk class cleanup clear client clob clob_base clone close cluster_id cluster_probability cluster_set clustering coalesce coercibility col collate collation collect colu colum column column_value columns columns_updated comment commit compact compatibility compiled complete composite_limit compound compress compute concat concat_ws concurrent confirm conn connec connect connect_by_iscycle connect_by_isleaf connect_by_root connect_time connection consider consistent constant constraint constraints constructor container content contents context contributors controlfile conv convert convert_tz corr corr_k corr_s corresponding corruption cos cost count count_big counted covar_pop covar_samp cpu_per_call cpu_per_session crc32 create creation critical cross cube cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime customdatum cycle data database databases datafile datafiles datalength date_add date_cache date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts day day_to_second dayname dayofmonth dayofweek dayofyear days db_role_change dbtimezone ddl deallocate declare decode decompose decrement decrypt deduplicate def defa defau defaul default defaults deferred defi defin define degrees delayed delegate delete delete_all delimited demand dense_rank depth dequeue des_decrypt des_encrypt des_key_file desc descr descri describ describe descriptor deterministic diagnostics difference dimension direct_load directory disable disable_all disallow disassociate discardfile disconnect diskgroup distinct distinctrow distribute distributed div do document domain dotnet double downgrade drop dumpfile duplicate duration each edition editionable editions element ellipsis else elsif elt empty enable enable_all enclosed encode encoding encrypt end end-exec endian enforced engine engines enqueue enterprise entityescaping eomonth error errors escaped evalname evaluate event eventdata events except exception exceptions exchange exclude excluding execu execut execute exempt exists exit exp expire explain explode export export_set extended extent external external_1 external_2 externally extract failed failed_login_attempts failover failure far fast feature_set feature_value fetch field fields file file_name_convert filesystem_like_logging final finish first first_value fixed flash_cache flashback floor flush following follows for forall force foreign form forma format found found_rows freelist freelists freepools fresh from from_base64 from_days ftp full function general generated get get_format get_lock getdate getutcdate global global_name globally go goto grant grants greatest group group_concat group_id grouping grouping_id groups gtid_subtract guarantee guard handler hash hashkeys having hea head headi headin heading heap help hex hierarchy high high_priority hosts hour hours http id ident_current ident_incr ident_seed identified identity idle_time if ifnull ignore iif ilike ilm immediate import in include including increment index indexes indexing indextype indicator indices inet6_aton inet6_ntoa inet_aton inet_ntoa infile initial initialized initially initrans inmemory inner innodb input insert install instance instantiable instr interface interleaved intersect into invalidate invisible is is_free_lock is_ipv4 is_ipv4_compat is_not is_not_null is_used_lock isdate isnull isolation iterate java join json json_exists keep keep_duplicates key keys kill language large last last_day last_insert_id last_value lateral lax lcase lead leading least leaves left len lenght length less level levels library like like2 like4 likec limit lines link list listagg little ln load load_file lob lobs local localtime localtimestamp locate locator lock locked log log10 log2 logfile logfiles logging logical logical_reads_per_call logoff logon logs long loop low low_priority lower lpad lrtrim ltrim main make_set makedate maketime managed management manual map mapping mask master master_pos_wait match matched materialized max maxextents maximize maxinstances maxlen maxlogfiles maxloghistory maxlogmembers maxsize maxtrans md5 measures median medium member memcompress memory merge microsecond mid migration min minextents minimum mining minus minute minutes minvalue missing mod mode model modification modify module monitoring month months mount move movement multiset mutex name name_const names nan national native natural nav nchar nclob nested never new newline next nextval no no_write_to_binlog noarchivelog noaudit nobadfile nocheck nocompress nocopy nocycle nodelay nodiscardfile noentityescaping noguarantee nokeep nologfile nomapping nomaxvalue nominimize nominvalue nomonitoring none noneditionable nonschema noorder nopr nopro noprom nopromp noprompt norely noresetlogs noreverse normal norowdependencies noschemacheck noswitch not nothing notice notnull notrim novalidate now nowait nth_value nullif nulls num numb numbe nvarchar nvarchar2 object ocicoll ocidate ocidatetime ociduration ociinterval ociloblocator ocinumber ociref ocirefcursor ocirowid ocistring ocitype oct octet_length of off offline offset oid oidindex old on online only opaque open operations operator optimal optimize option optionally or oracle oracle_date oradata ord ordaudio orddicom orddoc order ordimage ordinality ordvideo organization orlany orlvary out outer outfile outline output over overflow overriding package pad parallel parallel_enable parameters parent parse partial partition partitions pascal passing password password_grace_time password_lock_time password_reuse_max password_reuse_time password_verify_function patch path patindex pctincrease pctthreshold pctused pctversion percent percent_rank percentile_cont percentile_disc performance period period_add period_diff permanent physical pi pipe pipelined pivot pluggable plugin policy position post_transaction pow power pragma prebuilt precedes preceding precision prediction prediction_cost prediction_details prediction_probability prediction_set prepare present preserve prior priority private private_sga privileges procedural procedure procedure_analyze processlist profiles project prompt protection public publishingservername purge quarter query quick quiesce quota quotename radians raise rand range rank raw read reads readsize rebuild record records recover recovery recursive recycle redo reduced ref reference referenced references referencing refresh regexp_like register regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy reject rekey relational relative relaylog release release_lock relies_on relocate rely rem remainder rename repair repeat replace replicate replication required reset resetlogs resize resource respect restore restricted result result_cache resumable resume retention return returning returns reuse reverse revoke right rlike role roles rollback rolling rollup round row row_count rowdependencies rowid rownum rows rtrim rules safe salt sample save savepoint sb1 sb2 sb4 scan schema schemacheck scn scope scroll sdo_georaster sdo_topo_geometry search sec_to_time second seconds section securefile security seed segment select self semi sequence sequential serializable server servererror session session_user sessions_per_user set sets settings sha sha1 sha2 share shared shared_pool short show shrink shutdown si_averagecolor si_colorhistogram si_featurelist si_positionalcolor si_stillimage si_texture siblings sid sign sin size size_t sizes skip slave sleep smalldatetimefromparts smallfile snapshot some soname sort soundex source space sparse spfile split sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_small_result sql_variant_property sqlcode sqldata sqlerror sqlname sqlstate sqrt square standalone standby start starting startup statement static statistics stats_binomial_test stats_crosstab stats_ks_test stats_mode stats_mw_test stats_one_way_anova stats_t_test_ stats_t_test_indep stats_t_test_one stats_t_test_paired stats_wsr_test status std stddev stddev_pop stddev_samp stdev stop storage store stored str str_to_date straight_join strcmp strict string struct stuff style subdate subpartition subpartitions substitutable substr substring subtime subtring_index subtype success sum suspend switch switchoffset switchover sync synchronous synonym sys sys_xmlagg sysasm sysaux sysdate sysdatetimeoffset sysdba sysoper system system_user sysutcdatetime table tables tablespace tablesample tan tdo template temporary terminated tertiary_weights test than then thread through tier ties time time_format time_zone timediff timefromparts timeout timestamp timestampadd timestampdiff timezone_abbr timezone_minute timezone_region to to_base64 to_date to_days to_seconds todatetimeoffset trace tracking transaction transactional translate translation treat trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse type ub1 ub2 ub4 ucase unarchived unbounded uncompress under undo unhex unicode uniform uninstall union unique unix_timestamp unknown unlimited unlock unnest unpivot unrecoverable unsafe unsigned until untrusted unusable unused update updated upgrade upped upper upsert url urowid usable usage use use_stored_outlines user user_data user_resources users using utc_date utc_timestamp uuid uuid_short validate validate_password_strength validation valist value values var var_samp varcharc vari varia variab variabl variable variables variance varp varraw varrawc varray verify version versions view virtual visible void wait wallet warning warnings week weekday weekofyear wellformed when whene whenev wheneve whenever where while whitespace window with within without work wrapped xdb xml xmlagg xmlattributes xmlcast xmlcolattval xmlelement xmlexists xmlforest xmlindex xmlnamespaces xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltype xor year year_to_month years yearweek",literal:"true false null unknown",built_in:"array bigint binary bit blob bool boolean char character date dec decimal float int int8 integer interval number numeric real record serial serial8 smallint text time timestamp tinyint varchar varchar2 varying void"},contains:[{className:"string",begin:"'",end:"'",contains:[{begin:"''"}]},{className:"string",begin:'"',end:'"',contains:[{begin:'""'}]},{className:"string",begin:"`",end:"`"},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]},e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]}}}());hljs.registerLanguage("c",function(){"use strict";return function(e){var n=e.getLanguage("c-like").rawDefinition();return n.name="C",n.aliases=["c","h"],n}}());hljs.registerLanguage("json",function(){"use strict";return function(n){var e={literal:"true false null"},i=[n.C_LINE_COMMENT_MODE,n.C_BLOCK_COMMENT_MODE],t=[n.QUOTE_STRING_MODE,n.C_NUMBER_MODE],a={end:",",endsWithParent:!0,excludeEnd:!0,contains:t,keywords:e},l={begin:"{",end:"}",contains:[{className:"attr",begin:/"/,end:/"/,contains:[n.BACKSLASH_ESCAPE],illegal:"\\n"},n.inherit(a,{begin:/:/})].concat(i),illegal:"\\S"},s={begin:"\\[",end:"\\]",contains:[n.inherit(a)],illegal:"\\S"};return t.push(l,s),i.forEach((function(n){t.push(n)})),{name:"JSON",contains:t,keywords:e,illegal:"\\S"}}}());hljs.registerLanguage("python-repl",function(){"use strict";return function(n){return{aliases:["pycon"],contains:[{className:"meta",starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}}}());hljs.registerLanguage("markdown",function(){"use strict";return function(n){const e={begin:"<",end:">",subLanguage:"xml",relevance:0},a={begin:"\\[.+?\\][\\(\\[].*?[\\)\\]]",returnBegin:!0,contains:[{className:"string",begin:"\\[",end:"\\]",excludeBegin:!0,returnEnd:!0,relevance:0},{className:"link",begin:"\\]\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0},{className:"symbol",begin:"\\]\\[",end:"\\]",excludeBegin:!0,excludeEnd:!0}],relevance:10},i={className:"strong",contains:[],variants:[{begin:/_{2}/,end:/_{2}/},{begin:/\*{2}/,end:/\*{2}/}]},s={className:"emphasis",contains:[],variants:[{begin:/\*(?!\*)/,end:/\*/},{begin:/_(?!_)/,end:/_/,relevance:0}]};i.contains.push(s),s.contains.push(i);var c=[e,a];return i.contains=i.contains.concat(c),s.contains=s.contains.concat(c),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:c=c.concat(i,s)},{begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",contains:c}]}]},e,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:c,end:"$"},{className:"code",variants:[{begin:"(`{3,})(.|\\n)*?\\1`*[ ]*"},{begin:"(~{3,})(.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}}());hljs.registerLanguage("javascript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);function s(e){return r("(?=",e,")")}function r(...e){return e.map(e=>(function(e){return e?"string"==typeof e?e:e.source:null})(e)).join("")}return function(t){var i="[A-Za-z$_][0-9A-Za-z$_]*",c={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/},o={$pattern:"[A-Za-z$_][0-9A-Za-z$_]*",keyword:e.join(" "),literal:n.join(" "),built_in:a.join(" ")},l={className:"number",variants:[{begin:"\\b(0[bB][01]+)n?"},{begin:"\\b(0[oO][0-7]+)n?"},{begin:t.C_NUMBER_RE+"n?"}],relevance:0},E={className:"subst",begin:"\\$\\{",end:"\\}",keywords:o,contains:[]},d={begin:"html`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"xml"}},g={begin:"css`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"css"}},u={className:"string",begin:"`",end:"`",contains:[t.BACKSLASH_ESCAPE,E]};E.contains=[t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,d,g,u,l,t.REGEXP_MODE];var b=E.contains.concat([{begin:/\(/,end:/\)/,contains:["self"].concat(E.contains,[t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE])},t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE]),_={className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:b};return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:o,contains:[t.SHEBANG({binary:"node",relevance:5}),{className:"meta",relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,d,g,u,t.C_LINE_COMMENT_MODE,t.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+",contains:[{className:"type",begin:"\\{",end:"\\}",relevance:0},{className:"variable",begin:i+"(?=\\s*(-)|$)",endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]}),t.C_BLOCK_COMMENT_MODE,l,{begin:r(/[{,\n]\s*/,s(r(/(((\/\/.*)|(\/\*(.|\n)*\*\/))\s*)*/,i+"\\s*:"))),relevance:0,contains:[{className:"attr",begin:i+s("\\s*:"),relevance:0}]},{begin:"("+t.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",contains:[t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE,t.REGEXP_MODE,{className:"function",begin:"(\\([^(]*(\\([^(]*(\\([^(]*\\))?\\))?\\)|"+t.UNDERSCORE_IDENT_RE+")\\s*=>",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:t.UNDERSCORE_IDENT_RE},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:o,contains:b}]}]},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{variants:[{begin:"<>",end:""},{begin:c.begin,end:c.end}],subLanguage:"xml",contains:[{begin:c.begin,end:c.end,skip:!0,contains:["self"]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/\{/,excludeEnd:!0,contains:[t.inherit(t.TITLE_MODE,{begin:i}),_],illegal:/\[|%/},{begin:/\$[(.]/},t.METHOD_GUARD,{className:"class",beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends"},t.UNDERSCORE_TITLE_MODE]},{beginKeywords:"constructor",end:/\{/,excludeEnd:!0},{begin:"(get|set)\\s+(?="+i+"\\()",end:/{/,keywords:"get set",contains:[t.inherit(t.TITLE_MODE,{begin:i}),{begin:/\(\)/},_]}],illegal:/#(?!!)/}}}());hljs.registerLanguage("typescript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);return function(r){var t={$pattern:"[A-Za-z$_][0-9A-Za-z$_]*",keyword:e.concat(["type","namespace","typedef","interface","public","private","protected","implements","declare","abstract","readonly"]).join(" "),literal:n.join(" "),built_in:a.concat(["any","void","number","boolean","string","object","never","enum"]).join(" ")},s={className:"meta",begin:"@[A-Za-z$_][0-9A-Za-z$_]*"},i={className:"number",variants:[{begin:"\\b(0[bB][01]+)n?"},{begin:"\\b(0[oO][0-7]+)n?"},{begin:r.C_NUMBER_RE+"n?"}],relevance:0},o={className:"subst",begin:"\\$\\{",end:"\\}",keywords:t,contains:[]},c={begin:"html`",end:"",starts:{end:"`",returnEnd:!1,contains:[r.BACKSLASH_ESCAPE,o],subLanguage:"xml"}},l={begin:"css`",end:"",starts:{end:"`",returnEnd:!1,contains:[r.BACKSLASH_ESCAPE,o],subLanguage:"css"}},E={className:"string",begin:"`",end:"`",contains:[r.BACKSLASH_ESCAPE,o]};o.contains=[r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,c,l,E,i,r.REGEXP_MODE];var d={begin:"\\(",end:/\)/,keywords:t,contains:["self",r.QUOTE_STRING_MODE,r.APOS_STRING_MODE,r.NUMBER_MODE]},u={className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:t,contains:[r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,s,d]};return{name:"TypeScript",aliases:["ts"],keywords:t,contains:[r.SHEBANG(),{className:"meta",begin:/^\s*['"]use strict['"]/},r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,c,l,E,r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,i,{begin:"("+r.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",contains:[r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,r.REGEXP_MODE,{className:"function",begin:"(\\([^(]*(\\([^(]*(\\([^(]*\\))?\\))?\\)|"+r.UNDERSCORE_IDENT_RE+")\\s*=>",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:r.UNDERSCORE_IDENT_RE},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:t,contains:d.contains}]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/[\{;]/,excludeEnd:!0,keywords:t,contains:["self",r.inherit(r.TITLE_MODE,{begin:"[A-Za-z$_][0-9A-Za-z$_]*"}),u],illegal:/%/,relevance:0},{beginKeywords:"constructor",end:/[\{;]/,excludeEnd:!0,contains:["self",u]},{begin:/module\./,keywords:{built_in:"module"},relevance:0},{beginKeywords:"module",end:/\{/,excludeEnd:!0},{beginKeywords:"interface",end:/\{/,excludeEnd:!0,keywords:"interface extends"},{begin:/\$[(.]/},{begin:"\\."+r.IDENT_RE,relevance:0},s,d]}}}());hljs.registerLanguage("plaintext",function(){"use strict";return function(t){return{name:"Plain text",aliases:["text","txt"],disableAutodetect:!0}}}());hljs.registerLanguage("less",function(){"use strict";return function(e){var n="([\\w-]+|@{[\\w-]+})",a=[],s=[],t=function(e){return{className:"string",begin:"~?"+e+".*?"+e}},r=function(e,n,a){return{className:e,begin:n,relevance:a}},i={begin:"\\(",end:"\\)",contains:s,relevance:0};s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,t("'"),t('"'),e.CSS_NUMBER_MODE,{begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]",excludeEnd:!0}},r("number","#[0-9A-Fa-f]+\\b"),i,r("variable","@@?[\\w-]+",10),r("variable","@{[\\w-]+}"),r("built_in","~?`[^`]*?`"),{className:"attribute",begin:"[\\w-]+\\s*:",end:":",returnBegin:!0,excludeEnd:!0},{className:"meta",begin:"!important"});var c=s.concat({begin:"{",end:"}",contains:a}),l={beginKeywords:"when",endsWithParent:!0,contains:[{beginKeywords:"and not"}].concat(s)},o={begin:n+"\\s*:",returnBegin:!0,end:"[;}]",relevance:0,contains:[{className:"attribute",begin:n,end:":",excludeEnd:!0,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s}}]},g={className:"keyword",begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b",starts:{end:"[;{}]",returnEnd:!0,contains:s,relevance:0}},d={className:"variable",variants:[{begin:"@[\\w-]+\\s*:",relevance:15},{begin:"@[\\w-]+"}],starts:{end:"[;}]",returnEnd:!0,contains:c}},b={variants:[{begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:n,end:"{"}],returnBegin:!0,returnEnd:!0,illegal:"[<='$\"]",relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,l,r("keyword","all\\b"),r("variable","@{[\\w-]+}"),r("selector-tag",n+"%?",0),r("selector-id","#"+n),r("selector-class","\\."+n,0),r("selector-tag","&",0),{className:"selector-attr",begin:"\\[",end:"\\]"},{className:"selector-pseudo",begin:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{begin:"\\(",end:"\\)",contains:c},{begin:"!important"}]};return a.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,g,d,o,b),{name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:a}}}());hljs.registerLanguage("lua",function(){"use strict";return function(e){var t={begin:"\\[=*\\[",end:"\\]=*\\]",contains:["self"]},a=[e.COMMENT("--(?!\\[=*\\[)","$"),e.COMMENT("--\\[=*\\[","\\]=*\\]",{contains:[t],relevance:10})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE,literal:"true false nil",keyword:"and break do else elseif end for goto if in local not or repeat return then until while",built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove"},contains:a.concat([{className:"function",beginKeywords:"function",end:"\\)",contains:[e.inherit(e.TITLE_MODE,{begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params",begin:"\\(",endsWithParent:!0,contains:a}].concat(a)},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string",begin:"\\[=*\\[",end:"\\]=*\\]",contains:[t],relevance:5}])}}}()); Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/images/change-fork-name.png and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/images/change-fork-name.png differ Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/images/cisco.png and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/images/cisco.png differ Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/images/clone-your-fork.png and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/images/clone-your-fork.png differ Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/images/create-a-fork.png and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/images/create-a-fork.png differ Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/images/demon.png and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/images/demon.png differ diff -Nru clamav-0.103.2+dfsg/docs/html/images/flamegraph.svg clamav-0.103.5+dfsg/docs/html/images/flamegraph.svg --- clamav-0.103.2+dfsg/docs/html/images/flamegraph.svg 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/images/flamegraph.svg 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,952 @@ + + + + + + + + + + + + + + +Flame Graph + +Reset Zoom +Search +ic + + + +cli_magic_scan_desc (2 samples, 6.25%) +cli_magi.. + + +cli_scan_fmap (1 samples, 3.12%) +cli.. + + +__vfs_read (1 samples, 3.12%) +__v.. + + +scanraw (22 samples, 68.75%) +scanraw + + +__GI___libc_write (2 samples, 6.25%) +__GI___l.. + + +lzxd_decompress (1 samples, 3.12%) +lzx.. + + +cl_scandesc_callback (27 samples, 84.38%) +cl_scandesc_callback + + +scanfile (27 samples, 84.38%) +scanfile + + +cli_magic_scan_desc_type (1 samples, 3.12%) +cli.. + + +cli_magic_scan (1 samples, 3.12%) +cli.. + + +cli_scan_fmap (1 samples, 3.12%) +cli.. + + +cli_magic_scan (2 samples, 6.25%) +cli_magi.. + + +matcher_run (1 samples, 3.12%) +mat.. + + +all (32 samples, 100%) + + + +fmap_readpage (1 samples, 3.12%) +fma.. + + +cli_magic_scan_desc (1 samples, 3.12%) +cli.. + + +cli_ac_scanbuff (1 samples, 3.12%) +cli.. + + +entry_SYSCALL_64_after_hwframe (1 samples, 3.12%) +ent.. + + +matcher_run (1 samples, 3.12%) +mat.. + + +cli_magic_scan_desc (2 samples, 6.25%) +cli_magi.. + + +clamscan (32 samples, 100.00%) +clamscan + + +is_dump_and_scan (2 samples, 6.25%) +is_dump_.. + + +cli_magic_scan_desc_type (3 samples, 9.38%) +cli_magic_sca.. + + +cli_bm_scanbuff (1 samples, 3.12%) +cli.. + + +generic_perform_write (2 samples, 6.25%) +generic_.. + + +scanraw (1 samples, 3.12%) +sca.. + + +ksys_pread64 (1 samples, 3.12%) +ksy.. + + +fmap_need_off_once (1 samples, 3.12%) +fma.. + + +__libc_write (1 samples, 3.12%) +__l.. + + +cli_scan_fmap (1 samples, 3.12%) +cli.. + + +scan_common (26 samples, 81.25%) +scan_common + + +[libz.so.1.2.11] (1 samples, 3.12%) +[li.. + + +cli_scanishield (3 samples, 9.38%) +cli_scanishield + + +do_syscall_64 (1 samples, 3.12%) +do_.. + + +cli_magic_scan_desc (2 samples, 6.25%) +cli_magi.. + + +scanraw (1 samples, 3.12%) +sca.. + + +lzxd_decompress (1 samples, 3.12%) +lzx.. + + +cli_scanmscab (2 samples, 6.25%) +cli_scan.. + + +cli_magic_scan_desc_type (2 samples, 6.25%) +cli_magi.. + + +cli_magic_scan_desc_type (2 samples, 6.25%) +cli_magi.. + + +cli_magic_scan (26 samples, 81.25%) +cli_magic_scan + + +matcher_run (1 samples, 3.12%) +mat.. + + +ext4_mark_inode_dirty (1 samples, 3.12%) +ext.. + + +matcher_run (10 samples, 31.25%) +matcher_run + + +fmap (1 samples, 3.12%) +fmap + + +scanraw (7 samples, 21.88%) +scanraw + + +cli_scanscript (1 samples, 3.12%) +cli.. + + +__generic_file_write_iter (2 samples, 6.25%) +__generi.. + + +cli_magic_scan (1 samples, 3.12%) +cli.. + + +ext4_da_write_end (1 samples, 3.12%) +ext.. + + +generic_file_read_iter (1 samples, 3.12%) +gen.. + + +block_write_end (1 samples, 3.12%) +blo.. + + +cli_bm_scanbuff (1 samples, 3.12%) +cli.. + + +vfs_read (1 samples, 3.12%) +vfs.. + + +cli_magic_scan_desc_type (2 samples, 6.25%) +cli_magi.. + + +cli_magic_scan (3 samples, 9.38%) +cli_magic_scan + + +cli_ac_scanbuff (2 samples, 6.25%) +cli_ac_s.. + + +ext4_da_write_end (1 samples, 3.12%) +ext.. + + +do_syscall_64 (2 samples, 6.25%) +do_sysca.. + + +cli_scanmscab (1 samples, 3.12%) +cli.. + + +cli_bm_scanbuff (1 samples, 3.12%) +cli.. + + +scandirs (27 samples, 84.38%) +scandirs + + +vfs_write (1 samples, 3.12%) +vfs.. + + +ext4_mark_iloc_dirty (1 samples, 3.12%) +ext.. + + +cli_scanembpe (8 samples, 25.00%) +cli_scanembpe + + +vfs_write (2 samples, 6.25%) +vfs_write + + +ext4_inode_csum.isra.65 (1 samples, 3.12%) +ext.. + + +ext4_file_write_iter (2 samples, 6.25%) +ext4_fil.. + + +is_parse_hdr (1 samples, 3.12%) +is_.. + + +cl_fmap_open_handle (1 samples, 3.12%) +cl_.. + + +cli_bm_scanbuff (1 samples, 3.12%) +cli.. + + +cli_scan_fmap (1 samples, 3.12%) +cli.. + + +cli_scanishield_msi (2 samples, 6.25%) +cli_scan.. + + +is_extract_cab (1 samples, 3.12%) +is_.. + + +filter_search_ext (2 samples, 6.25%) +filter_s.. + + +cli_scan_buff (1 samples, 3.12%) +cli.. + + +__vfs_write (1 samples, 3.12%) +__v.. + + +iov_iter_copy_from_user_atomic (1 samples, 3.12%) +iov.. + + +[libz.so.1.2.11] (1 samples, 3.12%) +[li.. + + +scanraw (3 samples, 9.38%) +scanraw + + +handle_need (1 samples, 3.12%) +han.. + + +ksys_write (2 samples, 6.25%) +ksys_write + + +main (27 samples, 84.38%) +main + + +cli_magic_scan_desc_type (1 samples, 3.12%) +cli.. + + +lzxd_decompress (2 samples, 6.25%) +lzxd_dec.. + + +cabd_extract (1 samples, 3.12%) +cab.. + + +handler_otf (2 samples, 6.25%) +handler_.. + + +cli_scan_fmap (3 samples, 9.38%) +cli_scan_fmap + + +entry_SYSCALL_64_after_hwframe (2 samples, 6.25%) +entry_SY.. + + +cli_ole2_extract (2 samples, 6.25%) +cli_ole2.. + + +cli_scanpe (4 samples, 12.50%) +cli_scanpe + + +copyout (1 samples, 3.12%) +cop.. + + +ext4_inode_csum_set (1 samples, 3.12%) +ext.. + + +__mark_inode_dirty (1 samples, 3.12%) +__m.. + + +fmap_check_empty (1 samples, 3.12%) +fma.. + + +scanraw (1 samples, 3.12%) +sca.. + + +ole2_walk_property_tree (2 samples, 6.25%) +ole2_wal.. + + +cli_magic_scan (2 samples, 6.25%) +cli_magi.. + + +ext4_file_write_iter (1 samples, 3.12%) +ext.. + + +fmap_get_MD5 (1 samples, 3.12%) +fma.. + + +mark_buffer_dirty (1 samples, 3.12%) +mar.. + + +filter_search_ext (1 samples, 3.12%) +fil.. + + +generic_write_end (1 samples, 3.12%) +gen.. + + +ksys_write (1 samples, 3.12%) +ksy.. + + +cli_magic_scan_desc (3 samples, 9.38%) +cli_magic_sca.. + + +generic_perform_write (1 samples, 3.12%) +gen.. + + +__libc_start_main (27 samples, 84.38%) +__libc_start_main + + +cli_scanole2 (2 samples, 6.25%) +cli_scan.. + + +scanmanager (27 samples, 84.38%) +scanmanager + + +cabd_extract (2 samples, 6.25%) +cabd_ext.. + + +cli_magic_scan_desc (7 samples, 21.88%) +cli_magic_scan_desc + + +cli_magic_scan (2 samples, 6.25%) +cli_magi.. + + +filter_search_ext (3 samples, 9.38%) +filter_search.. + + +ext4_dirty_inode (1 samples, 3.12%) +ext.. + + +cli_bm_scanbuff (1 samples, 3.12%) +cli.. + + +cli_magic_scan_file (3 samples, 9.38%) +cli_magic_sca.. + + +cli_ac_scanbuff (1 samples, 3.12%) +cli.. + + +cli_magic_scan (7 samples, 21.88%) +cli_magic_scan + + +cli_scanmscab (4 samples, 12.50%) +cli_scanmscab + + +do_syscall_64 (1 samples, 3.12%) +do_.. + + +cli_scan_fmap (10 samples, 31.25%) +cli_scan_fmap + + +matcher_run (1 samples, 3.12%) +mat.. + + +matcher_run (3 samples, 9.38%) +matcher_run + + +cli_magic_scan_desc_type (7 samples, 21.88%) +cli_magic_scan_desc_type + + +matcher_run (1 samples, 3.12%) +mat.. + + +cabd_extract (1 samples, 3.12%) +cab.. + + +cli_bm_scanbuff (5 samples, 15.62%) +cli_bm_scanbuff + + +entry_SYSCALL_64_after_hwframe (1 samples, 3.12%) +ent.. + + +copy_page_to_iter (1 samples, 3.12%) +cop.. + + +[libcrypto.so.1.1] (1 samples, 3.12%) +[li.. + + +__generic_file_write_iter (1 samples, 3.12%) +__g.. + + +__block_commit_write.isra.39 (1 samples, 3.12%) +__b.. + + +__vfs_write (2 samples, 6.25%) +__vfs_wr.. + + +matcher_run (3 samples, 9.38%) +matcher_run + + +__libc_pread64 (1 samples, 3.12%) +__l.. + + +cli_magic_scan_desc (1 samples, 3.12%) +cli.. + + +ole2_walk_property_tree (2 samples, 6.25%) +ole2_wal.. + + +page_mapping (1 samples, 3.12%) +pag.. + + +[unknown] (4 samples, 12.50%) +[unknown] + + +__generic_write_end (1 samples, 3.12%) +__g.. + + +cli_bm_scanbuff (1 samples, 3.12%) +cli.. + + +scanraw (1 samples, 3.12%) +sca.. + + +copy_user_enhanced_fast_string (1 samples, 3.12%) +cop.. + + +cli_scan_fmap (3 samples, 9.38%) +cli_scan_fmap + + + Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/images/fork-is-behind.png and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/images/fork-is-behind.png differ Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/images/logo.png and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/images/logo.png differ Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/images/new-git-workflow.png and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/images/new-git-workflow.png differ Binary files /tmp/tmp83p5kwql/ZfX_nyC70m/clamav-0.103.2+dfsg/docs/html/images/old-git-workflow.png and /tmp/tmp83p5kwql/h6p08hcdRT/clamav-0.103.5+dfsg/docs/html/images/old-git-workflow.png differ diff -Nru clamav-0.103.2+dfsg/docs/html/index.html clamav-0.103.5+dfsg/docs/html/index.html --- clamav-0.103.2+dfsg/docs/html/index.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/index.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,410 @@ + + + + + + Introduction - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

ClamAV

+

+ + Maeve, the ClamAV mascot + +

+

ClamAV is an open source (GPLv2) anti-virus toolkit, designed especially for e-mail scanning on mail gateways. It provides a number of utilities including a flexible and scalable multi-threaded daemon, a command line scanner and advanced tool for automatic database updates. The core of the package is an anti-virus engine available in a form of shared library.

+
+

Tip: ClamAV is not a traditional anti-virus or endpoint security suite. For a fully featured modern endpoint security suite, check out Cisco Secure Endpoint. See "related products", below, for more details.

+
+

ClamAV is brought to you by Cisco Systems, Inc.

+

Community Projects

+

ClamAV has a diverse ecosystem of community projects, products, and other tools that either depend on ClamAV to provide malware detection capabilities or supplement ClamAV with new features such as improved support for 3rd party signature databases, graphical user interfaces (GUI), and more.

+

Features

+
    +
  • ClamAV is designed to scan files quickly.
  • +
  • Real time protection (Linux only). The ClamOnAcc client for the ClamD scanning daemon provides on-access scanning on modern versions of Linux. This includes an optional capability to block file access until a file has been scanned (on-access prevention).
  • +
  • ClamAV detects millions of viruses, worms, trojans, and other malware, including Microsoft Office macro viruses, mobile malware, and other threats.
  • +
  • ClamAV's bytecode signature runtime, powered by either LLVM or our custom bytecode interpreter, allows the ClamAV signature writers to create and distribute very complex detection routines and remotely enhance the scanner’s functionality.
  • +
  • Signed signature databases ensure that ClamAV will only execute trusted signature definitions.
  • +
  • ClamAV scans within archives and compressed files but also protects against archive bombs. Built-in archive extraction capabilities include: +
      +
    • Zip (including SFX, excluding some newer or more complex extensions)
    • +
    • RAR (including SFX, most versions)
    • +
    • 7Zip
    • +
    • ARJ (including SFX)
    • +
    • Tar
    • +
    • CPIO
    • +
    • Gzip
    • +
    • Bzip2
    • +
    • DMG
    • +
    • IMG
    • +
    • ISO 9660
    • +
    • PKG
    • +
    • HFS+ partition
    • +
    • HFSX partition
    • +
    • APM disk image
    • +
    • GPT disk image
    • +
    • MBR disk image
    • +
    • XAR
    • +
    • XZ
    • +
    • Microsoft OLE2 (Office documments)
    • +
    • Microsoft OOXML (Office documments)
    • +
    • Microsoft Cabinet Files (including SFX)
    • +
    • Microsoft CHM (Compiled HTML)
    • +
    • Microsoft SZDD compression format
    • +
    • HWP (Hangul Word Processor documents)
    • +
    • BinHex
    • +
    • SIS (SymbianOS packages)
    • +
    • AutoIt
    • +
    • InstallShield
    • +
    • ESTsoft EGG
    • +
    +
  • +
  • Supports Windows executable file parsing, also known as Portable Executables (PE) both 32/64-bit, including PE files that are compressed or obfuscated with: +
      +
    • AsPack
    • +
    • UPX
    • +
    • FSG
    • +
    • Petite
    • +
    • PeSpin
    • +
    • NsPack
    • +
    • wwpack32
    • +
    • MEW
    • +
    • Upack
    • +
    • Y0da Cryptor
    • +
    +
  • +
  • Supports ELF and Mach-O files (both 32 and 64-bit)
  • +
  • Supports almost all mail file formats
  • +
  • Support for other special files/formats includes: +
      +
    • HTML
    • +
    • RTF
    • +
    • PDF
    • +
    • Files encrypted with CryptFF and ScrEnc
    • +
    • uuencode
    • +
    • TNEF (winmail.dat)
    • +
    +
  • +
  • Advanced database updater with support for scripted updates, digital signatures and DNS based database version queries
  • +
+
+

Disclaimer: Many of the above file formats continue to evolve. Executable packing and obfuscation tools in particular are constantly changing. We cannot guarantee that we can unpack or extract every version or variant of the listed formats.

+
+

License

+

ClamAV is licensed under the GNU General Public License, Version 2.

+

Supported platforms

+

Clam AntiVirus is highly cross-platform. The development team cannot test every OS, so we have chosen to test ClamAV using the two most recent Long Term Support (LTS) versions of each of the most popular desktop operating systems. Our regularly tested operating systems include:

+
    +
  • GNU/Linux +
      +
    • Alpine +
        +
      • 3.11 (64bit)
      • +
      +
    • +
    • Ubuntu +
        +
      • 18.04 (64bit, 32bit)
      • +
      • 20.04 (64bit)
      • +
      +
    • +
    • Debian +
        +
      • 9 (64bit, 32bit)
      • +
      • 10 (64bit, 32bit)
      • +
      +
    • +
    • CentOS +
        +
      • 7 (64bit, 32bit)
      • +
      • 8 (64bit)
      • +
      +
    • +
    • Fedora +
        +
      • 30 (64bit)
      • +
      • 31 (64bit)
      • +
      +
    • +
    • openSUSE +
        +
      • Leap (64bit)
      • +
      +
    • +
    +
  • +
  • UNIX +
      +
    • FreeBSD +
        +
      • 11 (64bit)
      • +
      • 12 (64bit)
      • +
      +
    • +
    • macOS +
        +
      • 10.13 High Sierra (x86_64)
      • +
      • 10.15 Catalina (x86_64)
      • +
      • 11.5 Big Sur (x86_64, arm64)
      • +
      +
    • +
    +
  • +
  • Windows +
      +
    • 7 (64bit, 32bit)
    • +
    • 10 (64bit, 32bit)
    • +
    +
  • +
+ +

The following minimum recommended system requirements are for using ClamScan or ClamD applications with the standard ClamAV signature database provided by Cisco.

+

Minimum recommended RAM for ClamAV:

+
    +
  • FreeBSD and Linux server edition: 2 GiB+
  • +
  • Linux non-server edition: 2 GiB+
  • +
  • Windows 7 & 10 32-bit: 2 GiB+
  • +
  • Windows 7 & 10 64-bit: 3 GiB+
  • +
  • macOS: 3 GiB+
  • +
+

Minimum recommended CPU for ClamAV:

+
    +
  • 1 CPU at 2.0 Ghz+
  • +
+

Minimum available hard disk space required:

+

For the ClamAV application we recommend having 5 GB of free space available. This recommendation is in addition to the recommended disk space for each OS.

+
+

Note: The tests to determine these minimum requirements were performed on systems that were not running other applications. If other applications are being run on the system, additional resources will be required in addition to our recommended minimums.

+
+

Mailing Lists and Chat

+

If you have a trouble installing or using ClamAV try asking on our mailing lists. There are four lists available:

+
    +
  • clamav-announce (at) lists.clamav.net +
      +
    • info about new versions, moderated.
    • +
    • Subscribers are not allowed to post to this mailing list.
    • +
    +
  • +
  • clamav-users (at) lists.clamav.net +
      +
    • user questions
    • +
    +
  • +
  • clamav-devel (at) lists.clamav.net +
      +
    • technical discussions
    • +
    +
  • +
  • clamav-virusdb (at) lists.clamav.net +
      +
    • database update announcements, moderated
    • +
    +
  • +
  • clamav-binary (at) lists.clamav.net +
      +
    • discussion and announcements for package maintainers
    • +
    +
  • +
+

You can subscribe and search the mailing list archives here.

+

You can also join the community on our ClamAV Discord chat server.

+

Submitting New or Otherwise Undetected Malware

+

If you've got a virus which is not detected by the current version of ClamAV using the latest signature databases, please submit the sample for review at our website:

+

https://www.clamav.net/reports/malware

+

Likewise, if you have a benign file that is flagging as a virus and you wish to report a False Positive, please submit the sample for review at our website:

+

https://www.clamav.net/reports/fp

+ +

Cisco Secure Endpoint (formerly AMP for Endpoints) is Cisco's cloud-based security suite for commercial and enterprise customers. Secure Endpoint is available for Windows, Linux, and macOS and provides superior malware detection capabilities, behavioral monitoring, dynamic file analysis, endpoint isolation, analytics, and threat hunting. Secure Endpoint sports a modern administrative web interface (dashboard).

+

Immunet is a cloud-based antivirus application for Windows that is free for non-commercial use. Immunet offers great malware detection efficacy but, as a completely free product, Immunet's does not have same features or the quality user experience that Secure Endpoint offers. There is an Immunet user forum but Cisco offers no official user support.

+

+ + Cisco Systems, Inc + +

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/Introduction.html clamav-0.103.5+dfsg/docs/html/Introduction.html --- clamav-0.103.2+dfsg/docs/html/Introduction.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/Introduction.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,410 @@ + + + + + + Introduction - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

ClamAV

+

+ + Maeve, the ClamAV mascot + +

+

ClamAV is an open source (GPLv2) anti-virus toolkit, designed especially for e-mail scanning on mail gateways. It provides a number of utilities including a flexible and scalable multi-threaded daemon, a command line scanner and advanced tool for automatic database updates. The core of the package is an anti-virus engine available in a form of shared library.

+
+

Tip: ClamAV is not a traditional anti-virus or endpoint security suite. For a fully featured modern endpoint security suite, check out Cisco Secure Endpoint. See "related products", below, for more details.

+
+

ClamAV is brought to you by Cisco Systems, Inc.

+

Community Projects

+

ClamAV has a diverse ecosystem of community projects, products, and other tools that either depend on ClamAV to provide malware detection capabilities or supplement ClamAV with new features such as improved support for 3rd party signature databases, graphical user interfaces (GUI), and more.

+

Features

+
    +
  • ClamAV is designed to scan files quickly.
  • +
  • Real time protection (Linux only). The ClamOnAcc client for the ClamD scanning daemon provides on-access scanning on modern versions of Linux. This includes an optional capability to block file access until a file has been scanned (on-access prevention).
  • +
  • ClamAV detects millions of viruses, worms, trojans, and other malware, including Microsoft Office macro viruses, mobile malware, and other threats.
  • +
  • ClamAV's bytecode signature runtime, powered by either LLVM or our custom bytecode interpreter, allows the ClamAV signature writers to create and distribute very complex detection routines and remotely enhance the scanner’s functionality.
  • +
  • Signed signature databases ensure that ClamAV will only execute trusted signature definitions.
  • +
  • ClamAV scans within archives and compressed files but also protects against archive bombs. Built-in archive extraction capabilities include: +
      +
    • Zip (including SFX, excluding some newer or more complex extensions)
    • +
    • RAR (including SFX, most versions)
    • +
    • 7Zip
    • +
    • ARJ (including SFX)
    • +
    • Tar
    • +
    • CPIO
    • +
    • Gzip
    • +
    • Bzip2
    • +
    • DMG
    • +
    • IMG
    • +
    • ISO 9660
    • +
    • PKG
    • +
    • HFS+ partition
    • +
    • HFSX partition
    • +
    • APM disk image
    • +
    • GPT disk image
    • +
    • MBR disk image
    • +
    • XAR
    • +
    • XZ
    • +
    • Microsoft OLE2 (Office documments)
    • +
    • Microsoft OOXML (Office documments)
    • +
    • Microsoft Cabinet Files (including SFX)
    • +
    • Microsoft CHM (Compiled HTML)
    • +
    • Microsoft SZDD compression format
    • +
    • HWP (Hangul Word Processor documents)
    • +
    • BinHex
    • +
    • SIS (SymbianOS packages)
    • +
    • AutoIt
    • +
    • InstallShield
    • +
    • ESTsoft EGG
    • +
    +
  • +
  • Supports Windows executable file parsing, also known as Portable Executables (PE) both 32/64-bit, including PE files that are compressed or obfuscated with: +
      +
    • AsPack
    • +
    • UPX
    • +
    • FSG
    • +
    • Petite
    • +
    • PeSpin
    • +
    • NsPack
    • +
    • wwpack32
    • +
    • MEW
    • +
    • Upack
    • +
    • Y0da Cryptor
    • +
    +
  • +
  • Supports ELF and Mach-O files (both 32 and 64-bit)
  • +
  • Supports almost all mail file formats
  • +
  • Support for other special files/formats includes: +
      +
    • HTML
    • +
    • RTF
    • +
    • PDF
    • +
    • Files encrypted with CryptFF and ScrEnc
    • +
    • uuencode
    • +
    • TNEF (winmail.dat)
    • +
    +
  • +
  • Advanced database updater with support for scripted updates, digital signatures and DNS based database version queries
  • +
+
+

Disclaimer: Many of the above file formats continue to evolve. Executable packing and obfuscation tools in particular are constantly changing. We cannot guarantee that we can unpack or extract every version or variant of the listed formats.

+
+

License

+

ClamAV is licensed under the GNU General Public License, Version 2.

+

Supported platforms

+

Clam AntiVirus is highly cross-platform. The development team cannot test every OS, so we have chosen to test ClamAV using the two most recent Long Term Support (LTS) versions of each of the most popular desktop operating systems. Our regularly tested operating systems include:

+
    +
  • GNU/Linux +
      +
    • Alpine +
        +
      • 3.11 (64bit)
      • +
      +
    • +
    • Ubuntu +
        +
      • 18.04 (64bit, 32bit)
      • +
      • 20.04 (64bit)
      • +
      +
    • +
    • Debian +
        +
      • 9 (64bit, 32bit)
      • +
      • 10 (64bit, 32bit)
      • +
      +
    • +
    • CentOS +
        +
      • 7 (64bit, 32bit)
      • +
      • 8 (64bit)
      • +
      +
    • +
    • Fedora +
        +
      • 30 (64bit)
      • +
      • 31 (64bit)
      • +
      +
    • +
    • openSUSE +
        +
      • Leap (64bit)
      • +
      +
    • +
    +
  • +
  • UNIX +
      +
    • FreeBSD +
        +
      • 11 (64bit)
      • +
      • 12 (64bit)
      • +
      +
    • +
    • macOS +
        +
      • 10.13 High Sierra (x86_64)
      • +
      • 10.15 Catalina (x86_64)
      • +
      • 11.5 Big Sur (x86_64, arm64)
      • +
      +
    • +
    +
  • +
  • Windows +
      +
    • 7 (64bit, 32bit)
    • +
    • 10 (64bit, 32bit)
    • +
    +
  • +
+ +

The following minimum recommended system requirements are for using ClamScan or ClamD applications with the standard ClamAV signature database provided by Cisco.

+

Minimum recommended RAM for ClamAV:

+
    +
  • FreeBSD and Linux server edition: 2 GiB+
  • +
  • Linux non-server edition: 2 GiB+
  • +
  • Windows 7 & 10 32-bit: 2 GiB+
  • +
  • Windows 7 & 10 64-bit: 3 GiB+
  • +
  • macOS: 3 GiB+
  • +
+

Minimum recommended CPU for ClamAV:

+
    +
  • 1 CPU at 2.0 Ghz+
  • +
+

Minimum available hard disk space required:

+

For the ClamAV application we recommend having 5 GB of free space available. This recommendation is in addition to the recommended disk space for each OS.

+
+

Note: The tests to determine these minimum requirements were performed on systems that were not running other applications. If other applications are being run on the system, additional resources will be required in addition to our recommended minimums.

+
+

Mailing Lists and Chat

+

If you have a trouble installing or using ClamAV try asking on our mailing lists. There are four lists available:

+
    +
  • clamav-announce (at) lists.clamav.net +
      +
    • info about new versions, moderated.
    • +
    • Subscribers are not allowed to post to this mailing list.
    • +
    +
  • +
  • clamav-users (at) lists.clamav.net +
      +
    • user questions
    • +
    +
  • +
  • clamav-devel (at) lists.clamav.net +
      +
    • technical discussions
    • +
    +
  • +
  • clamav-virusdb (at) lists.clamav.net +
      +
    • database update announcements, moderated
    • +
    +
  • +
  • clamav-binary (at) lists.clamav.net +
      +
    • discussion and announcements for package maintainers
    • +
    +
  • +
+

You can subscribe and search the mailing list archives here.

+

You can also join the community on our ClamAV Discord chat server.

+

Submitting New or Otherwise Undetected Malware

+

If you've got a virus which is not detected by the current version of ClamAV using the latest signature databases, please submit the sample for review at our website:

+

https://www.clamav.net/reports/malware

+

Likewise, if you have a benign file that is flagging as a virus and you wish to report a False Positive, please submit the sample for review at our website:

+

https://www.clamav.net/reports/fp

+ +

Cisco Secure Endpoint (formerly AMP for Endpoints) is Cisco's cloud-based security suite for commercial and enterprise customers. Secure Endpoint is available for Windows, Linux, and macOS and provides superior malware detection capabilities, behavioral monitoring, dynamic file analysis, endpoint isolation, analytics, and threat hunting. Secure Endpoint sports a modern administrative web interface (dashboard).

+

Immunet is a cloud-based antivirus application for Windows that is free for non-commercial use. Immunet offers great malware detection efficacy but, as a completely free product, Immunet's does not have same features or the quality user experience that Secure Endpoint offers. There is an Immunet user forum but Cisco offers no official user support.

+

+ + Cisco Systems, Inc + +

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Development/build-installer-packages.html clamav-0.103.5+dfsg/docs/html/manual/Development/build-installer-packages.html --- clamav-0.103.2+dfsg/docs/html/manual/Development/build-installer-packages.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Development/build-installer-packages.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,441 @@ + + + + + + Building the Installer Packages - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Building the Installer Packages

+

ClamAV's installer packages are compiled in a Jenkins CI environment in the Cisco-Talos development network. For each supported OS / packaging system / architecture, we have a computer (or VM) that maintains a copy of ClamAV's external library dependencies. These libraries are recompiled using Mussels any time there is a change to the recipes in our "clamav" Mussels cookbook.

+

Instructions follow for how you can build the installer packages in much the same way that we do.

+

Linux

+
+

Tip: Using an older version of Linux is best. ClamAV's only dependency will be on glibc, which is forwards compatible. That is to say that if you build the installer on an older version of Linux, it should install and run on a new version of Linux. The opposite is not true.

+
+

First, install Mussels:

+
python3 -m pip install --user mussels
+
+

Mussels also requires Git, so if you don't have that installed, install it now.

+

Now to use Mussels, run:

+
# This requires Git, and will clone the the "clamav" and "scrapbook" cookbooks.
+msl up
+
+# This is to enable running the scripts in the clamav cookbook to build the clamav dependencies.
+msl cookbook trust -y clamav
+
+# This is just to get you in a small directory so Mussels don't spend forever searching your harddrive for build recipes.
+mkdir tmp && cd tmp
+
+# First try a dry-run. If you're missing any tools, it will tell you.
+# If you have everything, it will give you a list of the order it plans to build everything.
+msl build -t host-static clamav_deps --dry-run
+
+# Then do the build.
+msl build -t host-static clamav_deps
+
+
+

Tip: On some systems you may encounter this error:

+
RuntimeError: Click will abort further execution because Python was configured to use ASCII as encoding for the environment. Consult https://click.palletsprojects.com/unicode-support/ for mitigation steps.
+
+This system lists some UTF-8 supporting locales that you can pick from. The following suitable locales were discovered: en_AG.utf8, en_AU.utf8, en_BW.utf8, en_CA.utf8, en_DK.utf8, en_GB.utf8, en_HK.utf8, en_IE.utf8, en_IN.utf8, en_NG.utf8, en_NZ.utf8, en_PH.utf8, en_SG.utf8, en_US.utf8, en_ZA.utf8, en_ZM.utf8, en_ZW.utf8
+
+

To resolve this, pick a local and set it, like this:

+
export LC_ALL=en_US.utf8
+
+

After you've set LC_ALL to your desired locale, re-run the above msl commands. You should see it run Git to update the cookbooks without error.

+
+

So assuming you've now build the clamav dependencies, you should be ready to build ClamAV.

+

In a Git clone of the clamav source (or the extracted source tarball from a release), create a build subdirectory and open a terminal in that build directory.

+

Run the following...

+

Configure (generate the build system):

+
cmake .. \
+    -D CMAKE_FIND_PACKAGE_PREFER_CONFIG=TRUE                                          \
+    -D CMAKE_PREFIX_PATH="$HOME/.mussels/install/host-static"                         \
+    -D CMAKE_INSTALL_PREFIX="/usr/local"                                              \
+    -D CPACK_PACKAGING_INSTALL_PREFIX="/usr/local"                                    \
+    -D CPACK_DEBIAN_PACKAGE_RELEASE=1                                                 \
+    -D CPACK_RPM_PACKAGE_RELEASE=1                                                    \
+    -D CMAKE_MODULE_PATH="$HOME/.mussels/install/host-static/lib/cmake"               \
+    -D CMAKE_BUILD_TYPE=RelWithDebInfo                                                \
+    -D ENABLE_EXAMPLES=OFF                                                            \
+    -D ENABLE_MILTER=OFF                                                              \
+    -D JSONC_INCLUDE_DIR="$HOME/.mussels/install/host-static/include/json-c"          \
+    -D JSONC_LIBRARY="$HOME/.mussels/install/host-static/lib/libjson-c.a"             \
+    -D ENABLE_JSON_SHARED=OFF                                                         \
+    -D BZIP2_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"                 \
+    -D BZIP2_LIBRARY_RELEASE="$HOME/.mussels/install/host-static/lib/libbz2_static.a" \
+    -D OPENSSL_ROOT_DIR="$HOME/.mussels/install/host-static"                          \
+    -D OPENSSL_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"               \
+    -D OPENSSL_CRYPTO_LIBRARY="$HOME/.mussels/install/host-static/lib/libcrypto.a"    \
+    -D OPENSSL_SSL_LIBRARY="$HOME/.mussels/install/host-static/lib/libssl.a"          \
+    -D LIBXML2_INCLUDE_DIR="$HOME/.mussels/install/host-static/include/libxml2"       \
+    -D LIBXML2_LIBRARY="$HOME/.mussels/install/host-static/lib/libxml2.a"             \
+    -D PCRE2_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"                 \
+    -D PCRE2_LIBRARY="$HOME/.mussels/install/host-static/lib/libpcre2-8.a"            \
+    -D NCURSES_INCLUDE_DIR="$HOME/.mussels/install/host-static/include/ncurses"       \
+    -D CURSES_LIBRARY="$HOME/.mussels/install/host-static/lib/libncurses.a"           \
+    -D ZLIB_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"                  \
+    -D ZLIB_LIBRARY="$HOME/.mussels/install/host-static/lib/libz.a"                   \
+    -D LIBCHECK_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"              \
+    -D LIBCHECK_LIBRARY="$HOME/.mussels/install/host-static/lib/libcheck.a"
+
+
+

Tip: Note the use of CPACK_DEBIAN_PACKAGE_RELEASE and CPACK_RPM_PACKAGE_RELEASE. Feel free to only use the one you need for whichever platform you're targeting. You should increase the release version number if re-releasing a new package of the same ClamAV version.

+
+

Build:

+
make -j12
+
+

It's a good idea to run the public test suite at this point:

+
ctest -V
+
+

To make the RPM package for RPM-based distributions, you'll need the rpmbuild tool, which you can install with yum install rpm-build. Then run:

+
cpack -G RPM
+
+

To make the DEB package for Debian-based distributions, run:

+
cpack -G DEB
+
+

macOS

+
+

Note: The macOS instructions depend on Xcode, which is required to build the arm64 + x86_64 "universal" binaries. You may need to install it from the macOS app store. Be sure to run it once to accept the EULA before you proceed.

+
+

First, install Mussels:

+
python3 -m pip install --user mussels
+
+

Mussels also requires Git, so if you don't have that installed, install it now.

+

Now to use Mussels, run:

+
# This requires Git, and will clone the the "clamav" and "scrapbook" cookbooks.
+msl up
+
+# This is to enable running the scripts in the clamav cookbook to build the clamav dependencies.
+msl cookbook trust -y clamav
+
+# This is just to get you in a small directory so Mussels don't spend forever searching your harddrive for build recipes.
+mkdir tmp && cd tmp
+
+# First try a dry-run. If you're missing any tools, it will tell you.
+# If you have everything, it will give you a list of the order it plans to build everything.
+msl build -t host-static clamav_deps --dry-run
+
+# Then do the build.
+msl build -t host-static clamav_deps
+
+

So assuming you've now build the clamav dependencies, you should be ready to build ClamAV.

+

In a Git clone of the clamav source (or the extracted source tarball from a release), create a build subdirectory and open a terminal in that build directory.

+

Run the following...

+

Configure (generate the build system):

+
cmake .. \
+    -G Xcode                                                                            \
+    -D CLAMAV_SIGN_FILE=ON                                                              \
+    -D CMAKE_OSX_ARCHITECTURES="arm64;x86_64"                                           \
+    -D CMAKE_FIND_PACKAGE_PREFER_CONFIG=TRUE                                            \
+    -D CMAKE_PREFIX_PATH="$HOME/.mussels/install/host-static"                           \
+    -D CMAKE_INSTALL_PREFIX="/usr/local/clamav"                                         \
+    -D CPACK_PACKAGING_INSTALL_PREFIX="/usr/local"                                      \
+    -D CMAKE_MODULE_PATH="$HOME/.mussels/install/host-static/lib/cmake"                 \
+    -D ENABLE_EXAMPLES=OFF                                                              \
+    -D JSONC_INCLUDE_DIR="$HOME/.mussels/install/host-static/include/json-c"            \
+    -D JSONC_LIBRARY="$HOME/.mussels/install/host-static/lib/libjson-c.a"               \
+    -D ENABLE_JSON_SHARED=OFF                                                           \
+    -D BZIP2_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"                   \
+    -D BZIP2_LIBRARY_RELEASE="$HOME/.mussels/install/host-static/lib/libbz2_static.a"   \
+    -D OPENSSL_ROOT_DIR="$HOME/.mussels/install/host-static"                            \
+    -D OPENSSL_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"                 \
+    -D OPENSSL_CRYPTO_LIBRARY="$HOME/.mussels/install/host-static/lib/libcrypto.a"      \
+    -D OPENSSL_SSL_LIBRARY="$HOME/.mussels/install/host-static/lib/libssl.a"            \
+    -D LIBXML2_INCLUDE_DIR="$HOME/.mussels/install/host-static/include/libxml2"         \
+    -D LIBXML2_LIBRARY="$HOME/.mussels/install/host-static/lib/libxml2.a"               \
+    -D PCRE2_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"                   \
+    -D PCRE2_LIBRARY="$HOME/.mussels/install/host-static/lib/libpcre2-8.a"              \
+    -D CURSES_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"                  \
+    -D CURSES_LIBRARY="$HOME/.mussels/install/host-static/lib/libncurses.a"             \
+    -D ZLIB_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"                    \
+    -D ZLIB_LIBRARY="$HOME/.mussels/install/host-static/lib/libz.a"                     \
+    -D LIBCHECK_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"                \
+    -D LIBCHECK_LIBRARY="$HOME/.mussels/install/host-static/lib/libcheck.a"
+
+

Build:

+
cmake --build . --config RelWithDebInfo
+
+

It's a good idea to run the public test suite at this point:

+
ctest -C RelWithDebInfo -V
+
+

Now, to make the installer, just run:

+
cpack -C RelWithDebInfo
+
+

This will generate the PKG installer package.

+

Windows

+

For tips on installing development tools for Windows, see the development build instructions.

+

First, install Mussels:

+
python3 -m pip install --user mussels
+
+

Mussels also requires Git, so if you don't have that installed, install it now.

+
+

Tip: You may receive a warning that installed scripts are not in your PATH environment variable. I strongly recommend adding the Scripts directory described to your PATH. After which, you may run Mussels using msl on the command line instead of typing python3 -m mussels, which is exceedingly tedious ;-).

+
+

Now to use Mussels, run:

+
# This requires Git, and will clone the the "clamav" and "scrapbook" cookbooks.
+msl up
+
+# This is to enable running the scripts in the clamav cookbook to build the clamav dependencies.
+msl cookbook trust -y clamav
+
+# This is just to get you in a small directory so Mussels don't spend forever searching your harddrive for build recipes.
+mkdir tmp && cd tmp
+
+# First try a dry-run. If you're missing any tools, it will tell you.
+# If you have everything, it will give you a list of the order it plans to build everything.
+msl build clamav_deps --dry-run
+
+# Then do the build.
+msl build clamav_deps
+
+# By default, this build will be for x64*
+# If you want to build for x86, run:
+msl build -t x86 clamav_deps
+
+
+

*For Windows, our recipes only provide two build targets: x64 and x86. We don't as of yet have an x64-static variant for the recipes. You'll probably want x64, which is the default.

+
+

So assuming you've now build the clamav dependencies, you should be ready to build ClamAV.

+

In a Git clone of the clamav source (or the extracted source tarball from a release), create a build subdirectory and open a Powershell terminal in that build directory.

+

Run the following, replacing "2019" and "Community" with different versions or editions as needed to match your Visual Studio installation...

+

Configure (generate the build system):

+
pushd "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools"
+cmd /c "VsDevCmd.bat -arch=amd64 & set" |
+foreach {
+  if ($_ -match "=") {
+    $v = $_.split("="); set-item -force -path "ENV:\$($v[0])"  -value "$($v[1])"
+  }
+}
+popd
+Write-Host "`nVisual Studio Command Prompt variables set." -ForegroundColor Yellow
+
+cmake ..  -G Ninja -D CMAKE_BUILD_TYPE="RelWithDebInfo" `
+    -D ENABLE_EXAMPLES=OFF                                                   `
+    -D ENABLE_JSON_SHARED=OFF                                                `
+    -D JSONC_INCLUDE_DIR="$home\.mussels\install\x64\include\json-c"         `
+    -D JSONC_LIBRARY="$home\.mussels\install\x64\lib\json-c.lib"             `
+    -D BZIP2_INCLUDE_DIR="$home\.mussels\install\x64\include"                `
+    -D BZIP2_LIBRARY_RELEASE="$home\.mussels\install\x64\lib\libbz2.lib"     `
+    -D CURL_INCLUDE_DIR="$home\.mussels\install\x64\include"                 `
+    -D CURL_LIBRARY="$home\.mussels\install\x64\lib\libcurl_imp.lib"         `
+    -D OPENSSL_ROOT_DIR="$home\.mussels\install\x64"                         `
+    -D OPENSSL_INCLUDE_DIR="$home\.mussels\install\x64\include"              `
+    -D OPENSSL_CRYPTO_LIBRARY="$home\.mussels\install\x64\lib\libcrypto.lib" `
+    -D ZLIB_LIBRARY="$home\.mussels\install\x64\lib\libssl.lib"              `
+    -D LIBXML2_INCLUDE_DIR="$home\.mussels\install\x64\include\libxml2"      `
+    -D LIBXML2_LIBRARY="$home\.mussels\install\x64\lib\libxml2.lib"          `
+    -D PCRE2_INCLUDE_DIR="$home\.mussels\install\x64\include"                `
+    -D PCRE2_LIBRARY="$home\.mussels\install\x64\lib\pcre2-8.lib"            `
+    -D CURSES_INCLUDE_DIR="$home\.mussels\install\x64\include"               `
+    -D CURSES_LIBRARY="$home\.mussels\install\x64\lib\pdcurses.lib"          `
+    -D PThreadW32_INCLUDE_DIR="$home\.mussels\install\x64\include"           `
+    -D PThreadW32_LIBRARY="$home\.mussels\install\x64\lib\pthreadVC2.lib"    `
+    -D ZLIB_INCLUDE_DIR="$home\.mussels\install\x64\include"                 `
+    -D ZLIB_LIBRARY="$home\.mussels\install\x64\lib\zlibstatic.lib"          `
+    -D LIBCHECK_INCLUDE_DIR="$home\.mussels\install\x64\include"             `
+    -D LIBCHECK_LIBRARY="$home\.mussels\install\x64\lib\checkDynamic.lib"
+
+

Build:

+
ninja
+
+

It's a good idea to run the public test suite at this point:

+
ctest -C RelWithDebInfo -V
+
+

Now, to make the installer, just run:

+
cpack -C RelWithDebInfo
+
+

This will generate both the ZIP and the MSI installer packages.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Development/clamav-git-work-flow.html clamav-0.103.5+dfsg/docs/html/manual/Development/clamav-git-work-flow.html --- clamav-0.103.2+dfsg/docs/html/manual/Development/clamav-git-work-flow.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Development/clamav-git-work-flow.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,215 @@ + + + + + + ClamAV Git Work Flow - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

ClamAV Git Work Flow

+

ClamAV's Git work flow isn't very complicated, but it is more structured than most. It looks like this. Note that in the diagrams below, merged branches are regular merges and will add all of the commits from the source branch to the destination branch. The diagram doesn't show all the merged commits, for simplicity:

+

Git Work Flow

+

main:

+

the development branch. testing is done in pull-requests (PR's), so this branch should be stable, though we make no guarantees.

+

rel/0.104, rel/1.0:

+

Feature release branches. These always contain the latest stable patch versions for each feature release. When development on a feature release (E.g. dev/0.104) or a patch release (E.g. dev/0.104.1) is complete, they are merged here and tagged.

+

dev/0.104.1:

+

A development branch used to test hotfixes prior to a patch release

+

sec/dev/0.104.1:

+

A private development branch used to test security-related hotfixes prior to a patch release. This branch will be rebased like any feature branch as needed up until the release at which point it is merged into the dev/0.104.1 branch and the dev/0.104.1 branch is merged into the rel/0.104 branch and tagged as "clamav-0.104.1".

+

feature/blah:

+

A long-running branch for adding a major feature. It may be rebased several times with the default branch before > it is ready to merge.

+

CLAM-####-description, issue-####-description, bb####-description:

+

A branch for working a JIRA task, GitHub issue, or Bugzilla Bug. These are typically only found in a personal > fork and appear as pull requests from the fork to the upstream clamav repository.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Development/code-coverage.html clamav-0.103.5+dfsg/docs/html/manual/Development/code-coverage.html --- clamav-0.103.2+dfsg/docs/html/manual/Development/code-coverage.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Development/code-coverage.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,218 @@ + + + + + + Computing Code Coverage - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Computing Code Coverage

+

Code coverage when using CMake (v0.104 and newer)

+
+

TO-DO: Help us figure out how to compute code coverage and document the process here.

+
+

Code coverage when using Autotools (v0.103 and older)

+

gcov/lcov can be used to produce a code coverage report indicating which lines of code were executed on a single run or by multiple runs of clamscan. NOTE: for these metrics to be collected, ClamAV needs to have been configured with the --enable-coverage option.

+

First, run the following to zero out all of the performance metrics:

+
lcov -z --directory . --output-file coverage.lcov.data
+
+

Next, run ClamAV through whatever test cases you have. Then, run lcov again to collect the coverage data as follows:

+
lcov -c --directory . --output-file coverage.lcov.data
+
+

Finally, run the genhtml tool that ships with lcov to produce the code coverage report:

+
genhtml coverage.lcov.data --output-directory report
+
+

The report directory will have an index.html page which can be loaded into any web browser.

+

For more information, visit the lcov webpage

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Development/Contribute.html clamav-0.103.5+dfsg/docs/html/manual/Development/Contribute.html --- clamav-0.103.2+dfsg/docs/html/manual/Development/Contribute.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Development/Contribute.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,486 @@ + + + + + + Contribute - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Project Ideas

+

For ClamAV library & application projects, submit pull-requests to: https://github.com/Cisco-Talos/clamav

+

For ClamAV documentation projects, submit pull-requests to: https://github.com/Cisco-Talos/clamav-faq/pulls

+
+

Tip: If you find that any of the bugs or projects have already been completed, you can help out simply by updating the list in a pull-request to update this document.

+
+ +

Bugs

+

There's only so much our core dev team can schedule into each release. Many bugs probably won't be fixed without your help! Feel free to troll our open Bugzilla tickets and our open GitHub Issues if you're looking for project ideas!

+

Larger Projects

+

The following are a list of project ideas for someone looking to work on a larger project. +Any projects labeled "Risky" or "Exploratory" are thought to be more likely to fail, or to have significant drawbacks that will result in the new feature being ultimately rejected.

+

Please don't take it personally if the ClamAV team decide not to merge your implementation due to perceived complexity, stability, or other such concerns.

+

Contributors are expected to implement ample documentation for any new code or feature. Directions on how to test the contribution as well as unit and/or system tests will significantly help with PR review and will improve the likelihood that your contribution will be accepted.

+

Unstable or incomplete work is not likely to be accepted. The core development team has a long backlog of tasks and a curated roadmap for the next 6-12 months and will not have time to complete an unfinished project for you.

+

Contributors submitting a sizeable new feature will be asked to sign a Contributors License Agreement (CLA) before the contribution can be accepted.

+

CMake: -D MAINTAINER_MODE=ON

+

The purpose of "maintainer" build-mode is to update source generated by tools like Flex, Bison, and GPerf which are not readily accessible on every platform.

+

In this case, the project is to add GNU gperf support to the our CMake build system's Maintainer-Mode (-D MAINTAINER_MODE=ON). To complete this task, you'll need to detect GPerf when using Maintainer-Mode, and it should be required. When the build runs, it should regenerate and overwrite the libclamav/jsparse/generated files in the source directory using gperf with jsparse-keywords.gperf.

+

The contributor should add the new option to CMakeOptions.cmake and document the feature in INSTALL.cmake.md as well as in the clamav-faq repo's development.md developer documentation, after the feature has merged.

+

Category: Low-hanging fruit, Development

+

What you will learn from this project:

+
    +
  • CMake C/C++ build system skills
  • +
+

Required skills:

+
    +
  • Linux/Unix familiarity. Familiarity with compiling C/C++ projects.
  • +
+

Project Size: Small

+

CMake: -D CODE_COVERAGE=ON

+

Add a -D CODE_COVERAGE=ON option to the CMake build system which will build ClamAV with code coverage features enabled.

+

An ideal solution would support code coverage in when using GCC, Clang, and MSVC.

+

See development.md in the clamav-faq repo for additional insight on how gcov, lcov, and genhtml can be used today with the Autotools build system.

+

The contributor should add the new option to CMakeOptions.cmake and document the feature in INSTALL.cmake.md as well as in the clamav-faq repo's development.md developer documentation, after the feature has merged.

+

Category: Low-hanging fruit, Development

+

What you will learn from this project:

+
    +
  • CMake C/C++ build system skills
  • +
  • Familiarity with C/C++ code coverage
  • +
+

Required skills:

+
    +
  • Linux/Unix familiarity. Familiarity with compiling C/C++ projects.
  • +
+

Project Size: Small

+

Develop New Detection Capabilities for PE/ELF/MachO Executables

+

ClamAV parses the PE/ELF/MachO headers on executables that it scans, but doesn't make all of the data that it extracts available for use by NDB/LDB signatures. Some features that would be great to have include:

+
    +
  • The ability to distinguish between regular executables and DLLs/SOs/DYLIBs (add new keywords?)
  • +
  • Subsignature modifiers that can limit subsigs to only being evaluated against sections with memory permission flags (Read/Write/Execute). This would allow signatures to be evaluated more efficiently and also would decrease the chance of signature false positives.
  • +
  • Parsing digital signatures in signed MachO exes and evaluating against the certificate trust / block .crb rules
  • +
  • Other features that might be helpful?
  • +
+

As PE, ELF, and MachO parsing features already exist in C, C is the mostly likely language of choice. However any major new self contained code would ideally be written in Rust.

+

Category: Core Development

+

What you will learn from this project:

+
    +
  • The PE, ELF, and MachO file formats
  • +
  • How ClamAV parses executable headers, performs signature matching, and the capabilities are provided
  • +
  • How to write ClamAV signatures to match on executable files
  • +
+

Required skills:

+
    +
  • Strong C development experience
  • +
  • Rust development experince (as needed)
  • +
+

Project Size: Large

+

Develop Memory Scanning Capabilities for Unix

+

Today, ClamAV works by scanning files on disk for malware. It'd be great if ClamAV could also be used to scan process memory on a system its running on in order to detect malware that isn't present on disk.

+

The ClamAV team is already looking into integrating such a feature from clamav-win32, a project by Gianluigi Tiesi who has graciously agreed to allow us to include this memory scanning feature and others in the upstream clamav project.

+

This project would be to develop a similar capability for use on Linux and/or macOS and/or BSD Unix scanning clients.

+

As this is a relatively large new feature, an ideal solution would be written in Rust.

+

Category: Fun/Peripheral

+

What you will learn from this project:

+
    +
  • The techniques and OS APIs related to inspecting the memory of running processes
  • +
  • The security mechanisms in place to limit arbitrary access to process memory
  • +
+

Required skills:

+
    +
  • Strong Rust development experience.
  • +
  • Linux/Unix operating systems experience.
  • +
+

Project Size: Large

+

WebAssembly Runtime

+

Background: ClamAV has for a long time had runtime support for running portable plugins we call "bytecode signatures". ClamAV has a custom bytecode compiler to compile these plugins from a C-like language and uses LLVM or a homegrown "bytecode interpreter" to run the plugins. This solution is strikingly similar to a newer portable plugin technology: WebAssembly!

+

The goal of project would be to create a proof-of-concept WebAssembly (wasm) runtime in ClamAV so that "wasm signatures" could be written in Rust and executed in a wasm sandbox. As with our current bytecode signature technology, the wasm signatures would run at specific hooks in the ClamAV scanning process. They would need access to the file map (buffer) being scanned, and would be given a limited API to call into ClamAV functions.

+

For a proof-of-concept, executing a local wasm plugin that has access to the file being scanned (without copying the data) would be fine. A production solution would need to convert the wasm plugin to an ascii-text encoding so it can be distributed much the same way the current bytecode signature .cbc plugins are distributed. As with the bytecode signatures, clamscan and clamd must not load the plugins unless they've been digitally signed or the --bytecode-unsigned/BytecodeUnsigned options are set, which would disable this safety precaution.

+
+

Important Notes: The ClamAV bytecode compiler project is currently undergoing a major re-write. Once complete, the new bytecode compiler will effectively be a Python script that invokes clang with a collection of custom compiler passes that effectively compile C code into ClamAV-bytecode plugins. This project would have you extend that project to instead use rustc to compile Rust ClamAV-WASM plugins.

+
+

Category: Core Development, Fun

+

What you will learn from this project:

+
    +
  • Compilers
  • +
  • LLVM, WebAssembly JIT
  • +
  • Executable plugin sandboxing
  • +
  • Rust
  • +
+

Required skills:

+
    +
  • C/C++ development experience.
  • +
  • Rust development experience.
  • +
+

Project Size: Large

+

Add Unpacking Support for New Packers

+

ClamAV includes support for unpacking executables generated by several software packers so that malware can't use them to easily evade detection. The list of packers currently supported can be found in the Introduction of the ClamAV Manual. There are many packers out there, though, so there is always a need to write unpacking code for ones that are frequently used by malware authors. Some that are currently needed include:

+
    +
  • UPX for ELF
  • +
  • MPRESS (although we do have some bytecode signatures for MPRESS - those might be sufficient)
  • +
  • If anyone is interested in this, we can analyze thousands of samples and identify more candidates for this list
  • +
+

Improvements to existing executable (PE/ELF/MachO) parsing code would likely be in C, but any new standalone modules would ideally be written in Rust.

+

Category: Fun/Peripheral

+

What you will learn from this project:

+
    +
  • How packers function, the steps involved in run-time loading and fixing memory maps, and a general approach to unpacking
  • +
  • You'll gain experience reverse-engineering real-world malware
  • +
+

Required skills:

+
    +
  • C development experience.
  • +
  • Rust development experience.
  • +
+

Project Size: Large

+

Add Support for Matching on .NET Internals

+

YARA extracts certain properties of .NET executables and makes them available for signatures to use for detection: https://yara.readthedocs.io/en/v3.6.0/modules/dotnet.html

+

Can ClamAV do something similar? For instance, extract the GUIDs and allow matching on those the way we do entries in the PE VersionInfo section?

+
+

Tip: An ideal solution for this and any new file parsing feature should be written in Rust and called by our existing C code.

+
+

Category: Fun/Peripheral

+

What you will learn from this project:

+
    +
  • How .NET executables are structured, and how they work internally
  • +
  • How to write .NET applications (for testing)
  • +
  • You'll also test your code against real-world malware, and perform reverse-engineering of samples as needed (if they break your code).
  • +
+

Required skills:

+
    +
  • C development experience.
  • +
  • Rust development experience.
  • +
  • Any prior experience in the areas listed above is a plus.
  • +
+

Project Size: Large

+

Extract Macros from OXML docs

+

ClamAV and SigTool currently support parsing OLE Office files to decompress and extract macros for scanning. The newer version OOXML Office files do not have this support, resulting in detection possible for macros in these documents. The ability to both extract and scan macros would enable better coverage. This might mean creating a new target type to prevent creating two signatures one for OLE macros and another for OOXML macros.

+
+

Tip: An ideal solution for this and any new file parsing feature should be written in Rust and called by our existing C code.

+
+

Category:

+

What you will learn from this project:

+
    +
  • ClamAV and SigTool internals
  • +
  • Office document macro compression (RLE compression)
  • +
  • Macro storage in OOXML files
  • +
+

Required skills:

+
    +
  • C development experience.
  • +
  • Rust development experience.
  • +
  • Any prior experience in the areas listed above is a plus.
  • +
+

Project Size: Medium

+

Dynamically add new file types simply by adding file type magic (.ftm) signatures

+

Known file types are currently baked into each ClamAV versions along with file type magic signatures. See filetypes_int.h, filetypes.h, and filetypes.c. The hardcoded signature definitions for these hardcoded types are generally overridden by daily.ftm, a component of daily.cvd used to tweak file type identification definitions after release.

+

This project would be to re-architect how file types are stored in libclamav so new file types can be dynamically added when daily.ftm (or some other .ftm file) is loaded. Supplemental .ftm files should supplement the existing file type definitions, allowing an extra.ftm file to be tested alongside daily.cvd.

+

This new capability when combined with the ability to register bytecode signatures as new file type scanners will dramatically increase the ability to extend ClamAV functionality between major version updates. Even when combined with logical signatures that target specific file types (using the proposed new Type: keyword instead of Target:, see below project idea), will allow creative analysts to write more compact and efficient logical signatures.

+

Category: Fun, Core Development

+

What you will learn from this project:

+
    +
  • Software architecture experience.
  • +
+

Required skills:

+
    +
  • C development experience.
  • +
+

Project Size: Medium

+

Register scanners for each file type, Write bytecode "signature" scanners.

+

Bytecode signatures are the portable executable plugin format for ClamAV. If ClamAV file types each had one or more* linked list of file type handlers ("scanners"), then a bytecode API could be added to register a bytecode signature as a new scanner for a file type.

+

This project should be completed after the project to dynamically add new file types with new file type magic signatures (above). This new scanning architecture would be really powerful way to add features to the product without requiring a major version update. When combined with the project to run WebAssembly signatures written in Rust (project idea above) -- this plugin-based scanner feature would have the potential to become the fastest and safest way to add new capabilities to ClamAV.

+

Example use case:

+

One example use case of this feature would be to alert on the malicious use of crypto miner wallet IDs.

+

Cryptomining malware has become increasingly prevalent with the rise in cryptocurrency prices, and we have thousands of wallet identifiers known to be associated with malicious cryptomining campaigns. We don't have a robust way of using these IDs for detection, though, because we only want to raise an alert if the ID appears to be used in a malicious way (Ex: hardcoded into a mining application or as part of a coin miner configuration file) and not in legitimate ways (Ex: blog posts about campaigns or wallet block lists used by the mining pools).

+

The two use-cases that we want to alert on are miner config files and executables with the embedded wallet identifier. We could have two .ftm rules (one for each case) that indicate a CL_TYPE_MINER or something like that, and then scanning execution for CL_TYPE_MINER can go to the bytecode sig to perform any other checks that may be necessary.

+

*Additional Considerations: ClamAV has several locations in the scanning process for invoking file type scanners:

+
    +
  1. After initial file type identification, and before the "raw scan". In cli_magic_scan().
  2. +
  3. Once for each embedded file types found when using scanraw() to also match on embedded type recognition signatures*. In scanraw(). +
      +
    • *Embedded type recognition signature matching is a feature used to identify self-extracting archives and some harder to identify file formats, like XML-based office document formats, DMG files, master boot records (MBR), etc. It isn't used for some archive and disk image formats that we'll unpack later anyways because they cause excessive type false positives and duplicate file scanning. A common example without this safety measure was duplicate file extraction and scanning of zip file entries found in a tarball.
    • +
    +
  4. +
  5. After scanning all of the found embedded types (above). At the end of scanraw(). These could probably be moved to (4) if it is deemed safe to remove the 1st "safety measure" call to scanraw() in cli_magic_scan() (i.e we'd only call scanraw() once, ever).
  6. +
  7. Again, after the call to scanraw() at the bottom of cli_magic_scan(), for types that have bytecode hooks that won't execute unless a logical signature matches, requiring scanraw() to perform matching first.
  8. +
+

Considering that there are 3 or 4 placement options for scanners, it may be required to have 3 (or 4) different lists to add to when registering a new scanner to indicate when to run the scanner in the scanning process. An enum argument for the function would indicate which list to add it to. If inserting the new scanner for a given type from the front of the list, and only invoking the next scanner if the first one returns CL_EPARSE or CL_EFORMAT, then a scanner registration could be used to override an existing/built-in one or supplement it, whichever is desired.

+

This project would would require coming up with a common file-type-scanner API for all scanners (including bytecode scanners), and would enable moving all file-type-scanners out of scanners.c and into a new file for each in a scanners subdirectory. A separate parsers subdirectory should be added at this time and each file type parser would be moved there. The distinction between a "scanner" and a "parser" is this. A scanner uses a parser to extract bits to be scanned. A parser may simply be something like an archive extraction library. In some cases, particularly in internally developed code, the distinction may be less clear and so the entire thing may be better placed under the scanners directory as the entry-point will doubtless need to use the common file-type-scanner API.

+

This project will also require creating lots of regression tests for file type identification to ensure that the new architecture doesn't accidentally misclassify or fail to scan certain files.

+

The majority of the work won't actually change ClamAV's behavior, which may seem frustrating, but the end goal is super cool. Code cleanup and organization along the way will also make a meaningful difference. This project could be split into pieces:

+
    +
  1. Establish a common file type scanner function API and reorganize the scanners and parsers as described above.
  2. +
  3. Convert the API into a callback function pointer definition and create a registration API. Add a set of scanner callback lists to each file type. The built-in scanners should be initialized either at compile time or at least when libclamav is initialized, depending on the chosen design.
  4. +
+

Category: Very Fun, Core Development

+

What you will learn from this project:

+
    +
  • Software architecture experience
  • +
  • How to write ClamAV signatures (bytecode and LDB sigs)
  • +
  • You'll test your code against real-world malware, and can do reverse engineering if you'd like to expand the initial coinminer classification logic.
  • +
+

Required skills:

+
    +
  • Strong C development experience.
  • +
  • Any prior experience in the areas listed below is a plus.
  • +
+

Project Size: Very Large

+

Limit logical signature alerts based on file type

+

ClamAV signatures have a "Target Type" which is an integer type which can be used in signatures to limit signature matches to specific file types. ClamAV also categorizes signature patterns into two different Aho-Corasick pattern-matching trie's by Target Type. Target Type 1 (Windows executables (EXE/DLL/SYS/etc.) go in one trie, and everything else goes in the other trie. Unfortunately, not every file type has an associated target type. In addition, while it's conceivable to be able to add new text-based file types dynamically (see the above project idea about file type magic signatures), it is less feasible to dynamically add new numerical target types.

+

For some advanced reading, see:

+
    +
  • <appendix/FileTypes.md>
  • +
  • <manual/Signatures/LogicalSignatures.md>
  • +
+

This project is to add a new "Type:" keyword to the TargetDescriptionBlock for Logical Signature (.ldb) to limit logical signature alerts to specific file types, much like you currently can do with Target Types ("Target:"), Container File Types ("Container:"), and Container Intermediate Types ("Intermediates:"). While this isn't expected to improve scan times, it should reduce overall signature size as analysts will no longer need to duplicate the file-type-magic signature in order to limit alerting on a signature match by file type.

+

To illustrate, this is the file type magic signature for a Microsoft Shortcut File, aka CL_TYPE_LNK:

+
0:0:4C0000000114020000000000C000000000000046:Microsoft Windows Shortcut File:CL_TYPE_ANY:CL_TYPE_LNK:100
+
+

Though we can classify a file as CL_TYPE_LNK and even unpack the file with custom scanner using that type, there is presently no way to write a signature for CL_TYPE_LNK files without duplicating the 0:4C0000000114020000000000C000000000000046 bit.

+

At present a signature to alert on a "malicious" shortcut containing 0xdeadbeef might look like this:

+
SignatureName;Target:0;(0&1);0:4C0000000114020000000000C000000000000046;deadbeef
+
+

After this change, the signature could instead read:

+
SignatureName;Target:0,Type:CL_TYPE_LNK;(0);deadbeef
+
+

Category: Low-hanging Fruit, Core Development

+

What you will learn from this project:

+
    +
  • Knowledge of ClamAV's signature databases, and logical signature evaluation.
  • +
+

Required skills:

+
    +
  • C development experience.
  • +
+

Project Size: Small

+

libclamav Callback Function to Request Additional File

+

Add a callback function to give libclamav file parsers the ability to request additional file data from the scanning application -- I.e. clamscan and clamd (and by extension clamdscan & clamonacc).

+

This feature would enable support for split-archive scans, if all components of the split archive are present and available to the scanning application. To make this work for clamdscan+clamd, or clamonacc+clamd, the request would also have to be relayed by clamd over the socket API to the scanning client, and the client would have to respond with additional data, filepath, or file descriptor for clamd to provide via the callback to file parser.

+
+

Disclaimer: It's entirely likely that this idea is bogus and wouldn't work over the clamd+clamdscan socket API. This task would require a fair amount exploratory coding.

+
+

When a file is scanned, the scanner (eg cli_scanrar) may call a callback function provided by clamscan or clamd to request scan access to other files by name, with the expectation that it would receive an fmap in response. Specifically, when the first file in a split archive is scanned, the parser could request fmaps for subsequent files to provide to the archive extraction library. Direct scanning of files other than the first file in a split archive will skip, because they are split and are not the first file.

+

Category: Risky/Exploratory, Core Development

+

What you will learn from this project:

+
    +
  • ClamAV and SigTool internals
  • +
  • Socket programming
  • +
+

Required skills:

+
    +
  • C and C++ development experience.
  • +
+

Project Size: Large

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Development/development-builds.html clamav-0.103.5+dfsg/docs/html/manual/Development/development-builds.html --- clamav-0.103.2+dfsg/docs/html/manual/Development/development-builds.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Development/development-builds.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,594 @@ + + + + + + Building for Development - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Building for Development

+

The following are instructions for building ClamAV using CMake or Autotools with recommendations specific to ClamAV software development.

+ +

Build dependencies

+

Instructions to install the build dependencies for each major operating system and distribution can be found in our install from source instructions. See:

+ +

For development, you may also need to install the following...

+

For Linux/UNIX

+
    +
  • +

    git

    +
  • +
  • +

    autoconf, automake, libtool, m4 (For 0.103 and older, only):

    +

    These four packages make up "Autotools" and are required to run the ./autogen.sh script that generates the ./configure tool for configuring an Autotools build. You will need these packakges if building from a git clone, but the 0.103 release tarballs have this stuff pre-generated for normal users.

    +
  • +
  • +

    valgrind:

    +

    Valgrind is used to enhance our unit tests and feature tests. It will be automatically when running ctest for CMake builds, or when running make check VG=1 for Autotools builds.

    +
  • +
  • +

    pytest (For 0.104 and newer):

    +

    Pytest is used in our test suite for CMake builds to get better test output when analyzing test failures. Having pytest

    +
  • +
  • +

    bison and flex:

    +

    Bison and Flex re-generating the Yara rule parser, used by our "maintainer mode" in the CMake build system (v0.104+), and used by default in the Autotools build system (v0.103-).

    +
  • +
  • +

    gperf:

    +

    GPerf is another tool used by our "maintainer mode". It is used to generate code for our javascript normalizer. *Enabling GPerf in the "maintainer mode" for the CMake build system (v0.104+) is still a to-do.

    +
  • +
  • +

    ninja or ninja-build (For 0.104 and newer):

    +

    Ninja is a build system that CMake can use in place of Makefiles (UNIX) or Visual Studio (Windows). It's faster than both, particularly as compared with Visual Studio. If you're compiling ClamAV frequently, you may find it very helpful. The only real downside is that it's highly multithreaded so errors and warnings tend to get mixed up and it can be hard to inspect when you're having build failures.

    +
  • +
+

For Windows

+
    +
  • +

    Git for Windows.

    +
  • +
  • +

    Windows Terminal:

    +

    Windows Terminal is Microsoft's new open source terminal program. It's quite nice, and miles better than using a traditional CMD prompt or Powershell.

    +
  • +
  • +

    Visual Studio:

    +

    2019 Community Edition is fine. It may also possible to build using Clang + Ninja. Investigate at your peril.

    +
  • +
  • +

    Visual Studio Code:

    +

    I recommend a modern text editor like VSCode instead of working with Visual Studio directly. Visual Studio is powerful, but can be slow and unwieldy. Those coming from a Unix/Linux background may find that the Vim plugin is particularly nice.

    +
  • +
  • +

    Chocolatey:

    +

    Chocolatey is an application package manager for Windows that makes it easy to install stuff on Windows. After you install Chocolatey, you can use it to install CMake, WiX, Flex, Bison, Perl, and Nasm very simply, like this:

    +
    choco install cmake
    +
    +
  • +
  • +

    Wix Toolset:

    +

    If you want to build the installer, you'll also need WiX Toolset. If not, you can skip it. You can install it with Chocolatey:

    +
    choco install wixtoolset
    +
    +
  • +
  • +

    bison and flex:

    +

    As mentioned in the Linux/Unix section, Bison and Flex may be needed if working with the yara parser module. You can install it with Chocolatey:

    +
    choco install winflexbison
    +
    +
  • +
  • +

    ActivePerl and Netwide Assembler (NASM):

    +

    Perl and NASM are required to compile openssl from source if using Mussels to build your dependencies. If using vcpkg, you can skip them. You can install these tools using Chocolatey:

    +
    choco install activeperl nasm
    +
    +
  • +
+

We support two options for sourcing and building ClamAV library dependencies:

+
    +
  1. vcpkg
  2. +
  3. Mussels
  4. +
+

For basic builds, vcpkg should do just fine*, and is easier to get started.

+

If you want to customize how the dependencies are built, use Mussels. The ClamAV project uses Mussels to build the installers available on our website. The recipes to build the ClamAV dependencies, the definitions for finding and using the required development tools are hosted in the ClamAV Mussels Cookbook.

+
+

* There is a known issue with the unit tests when building with vcpkg in Debug mode. When you run the libclamav unit tests (check_clamav), the program will crash and a popup will claim there was heap corruption. If you use Task Manager to kill the check_clamav.exe process, the rest of the tests pass just fine. This issue does not occur when using Mussels to supply the library dependencies. Commenting out the following lines in readdb.c resolves the heap corruption crash when running check_clamav, but of course introduces a memory leak:

+
    if (engine->stats_data)
+        free(engine->stats_data);
+
+

If anyone has time to figure out the real cause of the vcpkg Debug-build crash +in check_clamav, it would be greatly appreciated.

+
+

Download the Source

+

If you don't already have the source, use Git to clone it:

+
git clone https://github.com/Cisco-Talos/clamav.git
+cd clamav
+
+

If you intend to make changes and submit a pull request, fork the Cisco-Talos/clamav repo first and then clone your fork of the repository instead:

+
git clone https://github.com/<yourusername>/clamav.git clamav-fork
+cd clamav-fork
+
+

Building ClamAV with CMake (v0.104 and newer)

+

ClamAV versions 0.103+ provide CMake build tooling. In 0.103, this is for experimental and development purposes only. Autotools should be used for production builds. In 0.104+, CMake is the only build system. Autotools and Visual Studio build systems have been removed.

+

To get started, first review the official build isntructions. Then return hrefer for some tips on building for development. For a complete reference of ClamAV build configuration options, see the INSTALL.md file located in the clamav repository.

+

The following instructions assume you have installed CMake, Ninja, and either GCC, Clang, or Visual Studio 2015 or newer.

+

Linux/Unix

+

Linux/Unix builds are often much simpler because the system's package manager can provide all of the dependencies for your. But if you want, you can build them yourself, using a tool like Mussels. The instructions listed in the Windows section are nearly identical. With Mussels on Linux, Unix, and macOS you can even use the host-static target to build all of the dependencies as static libraries to make ClamAV more portable and easier to package. Feel free to reach out on Discord if you want to learn more.

+

Configure (generate the build system):

+
cmake .. -G Ninja                   \
+    -D CMAKE_BUILD_TYPE="Debug"     \
+    -D OPTIMIZE=OFF                 \
+    -D CMAKE_INSTALL_PREFIX=install \
+    -D ENABLE_MILTER=ON             \
+    -D ENABLE_EXAMPLES=ON           \
+    -D ENABLE_STATIC_LIB=ON         \
+    -D ENABLE_SYSTEMD=OFF
+
+

Build:

+
ninja
+
+

Install (to the CMAKE_INSTALL_PREFIX directory):

+
ninja install
+
+

Windows

+

vcpkg

+

vcpkg can be used to build the ClamAV library dependencies automatically. See the vcpkg README for installation instructions.

+

Once installed, set the variable $VCPKG_PATH to the location where you installed vcpkg:

+
$VCPKG_PATH="..." # Path to your vcpkg installation
+
+

By default, CMake and vcpkg build for 32-bit. If you want to build for 64-bit, set the VCPKG_DEFAULT_TRIPLET environment variable:

+
$env:VCPKG_DEFAULT_TRIPLET="x64-windows"
+
+

Now run the following to build ClamAV's library dependencies:

+
& "$VCPKG_PATH\vcpkg" install 'curl[openssl]' 'json-c' 'libxml2' 'pcre2' 'pthreads' 'zlib' 'pdcurses' 'bzip2' 'check'
+
+

Finally, you can use the following to build ClamAV using Ninja for super fast builds. Replace "2019" and "Community" with different versions or editions as needed to match your Visual Studio installation.

+

Configure (generate the build system):

+
pushd "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools"
+cmd /c "VsDevCmd.bat -arch=amd64 & set" |
+foreach {
+  if ($_ -match "=") {
+    $v = $_.split("="); set-item -force -path "ENV:\$($v[0])"  -value "$($v[1])"
+  }
+}
+popd
+Write-Host "`nVisual Studio 2019 Command Prompt variables set." -ForegroundColor Yellow
+
+cmake .. -G Ninja                                                          `
+    -D CMAKE_BUILD_TYPE="Debug"                                             `
+    -D CMAKE_TOOLCHAIN_FILE="$VCPKG_PATH\scripts\buildsystems\vcpkg.cmake"  `
+    -D CMAKE_INSTALL_PREFIX="install"
+
+

Build:

+
ninja
+
+

Install (to the CMAKE_INSTALL_PREFIX directory):

+
ninja install
+
+
+

Tip: I like to place the "Configure" script in a configure-vcpkg.ps1 script file in my home directory. This way I can simply run ~\configure-vcpkg.ps1 followed by ninja to do a build.

+
+

Mussels

+

Here are some tips for using Mussels to build the clamav dependencies and then for building ClamAV with those dependencies.

+

Install Mussels

+
python3 -m pip install --user mussels
+
+

After the install, pip may suggest you add a "Scripts" directory to your PATH environment variable. I strongly suggest that you do this, so you can run msl for mussels commands instead of having to type python3 -m mussels for every command.

+

Now to use Mussels, run:

+
# This requires Git, and will clone the the "clamav" and "scrapbook" cookbooks.
+msl up
+
+# This is to enable running the scripts in the clamav cookbook to build the clamav dependencies.
+msl cookbook trust -y clamav
+
+# This is just to get you in a small directory so Mussels don't spend forever searching your harddrive for build recipes.
+mkdir tmp && cd tmp
+
+# First try a dry-run. If you're missing any tools, it will tell you.
+# If you have everything, it will give you a list of the order it plans to build everything.
+msl build clamav_deps --dry-run
+
+# Then do the build.
+msl build clamav_deps
+
+# You could also have it build clamav for you too, but that's not so useful for development work.
+msl build clamav
+
+

Mussels isn't great for doing repeated builds, because it tends to retry every build in the dependency chain, and that will take some time. Mussels also builds from a downloaded source tarball, and not from a local Git repository. If you're interested in working on improvement Mussels for development purposes and smoothing over some of these sharp edges, we'd love the help.

+

So assuming you've now build the clamav dependencies, you should be ready to build ClamAV. Replace "2019" and "Community" with different versions or editions as needed to match your Visual Studio installation

+

Configure (generate the build system):

+
pushd "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools"
+cmd /c "VsDevCmd.bat -arch=amd64 & set" |
+foreach {
+  if ($_ -match "=") {
+    $v = $_.split("="); set-item -force -path "ENV:\$($v[0])"  -value "$($v[1])"
+  }
+}
+popd
+Write-Host "`nVisual Studio 2019 Command Prompt variables set." -ForegroundColor Yellow
+
+cmake ..  -G Ninja -D CMAKE_BUILD_TYPE="Debug" `
+     -D ENABLE_EXAMPLES=OFF                                                 `
+     -D JSONC_INCLUDE_DIR="$home\.mussels\install\x64\include\json-c"         `
+     -D JSONC_LIBRARY="$home\.mussels\install\x64\lib\json-c.lib"             `
+     -D ENABLE_JSON_SHARED=OFF                                              `
+     -D BZIP2_INCLUDE_DIR="$home\.mussels\install\x64\include"                `
+     -D BZIP2_LIBRARY_RELEASE="$home\.mussels\install\x64\lib\libbz2.lib"     `
+     -D CURL_INCLUDE_DIR="$home\.mussels\install\x64\include"                 `
+     -D CURL_LIBRARY="$home\.mussels\install\x64\lib\libcurl_imp.lib"         `
+     -D OPENSSL_ROOT_DIR="$home\.mussels\install\x64"                         `
+     -D OPENSSL_INCLUDE_DIR="$home\.mussels\install\x64\include"              `
+     -D OPENSSL_CRYPTO_LIBRARY="$home\.mussels\install\x64\lib\libcrypto.lib" `
+     -D ZLIB_LIBRARY="$home\.mussels\install\x64\lib\libssl.lib"              `
+     -D LIBXML2_INCLUDE_DIR="$home\.mussels\install\x64\include\libxml2"      `
+     -D LIBXML2_LIBRARY="$home\.mussels\install\x64\lib\libxml2.lib"          `
+     -D PCRE2_INCLUDE_DIR="$home\.mussels\install\x64\include"                `
+     -D PCRE2_LIBRARY="$home\.mussels\install\x64\lib\pcre2-8.lib"            `
+     -D CURSES_INCLUDE_DIR="$home\.mussels\install\x64\include"               `
+     -D CURSES_LIBRARY="$home\.mussels\install\x64\lib\pdcurses.lib"          `
+     -D PThreadW32_INCLUDE_DIR="$home\.mussels\install\x64\include"           `
+     -D PThreadW32_LIBRARY="$home\.mussels\install\x64\lib\pthreadVC2.lib"    `
+     -D ZLIB_INCLUDE_DIR="$home\.mussels\install\x64\include"                 `
+     -D ZLIB_LIBRARY="$home\.mussels\install\x64\lib\zlibstatic.lib"          `
+     -D LIBCHECK_INCLUDE_DIR="$home\.mussels\install\x64\include"             `
+     -D LIBCHECK_LIBRARY="$home\.mussels\install\x64\lib\checkDynamic.lib"    `
+     -D CMAKE_INSTALL_PREFIX="install"
+
+

Build:

+
ninja
+
+

Install (to the CMAKE_INSTALL_PREFIX directory):

+
ninja install
+
+
+

Tip: I like to place the "Configure" script in a configure-mussels.ps1 script file in my home directory. This way I can simply run ~\configure-mussels.ps1 followed by ninja to do a build.

+
+

Testing with CTest

+

ClamAV version 0.104+ will include unit tests, integration tests, & feature tests performed via CMake's ctest toolset. All tests are executed within through ctest but within a Python test framework build around Python's unittest module. See clamav/unit_tests/testcase.py. Python 3.5+ is required.

+
+

Note: Valgrind tests are performed on Linux if Valgrind is installed.

+
+

Unit Tests

+

The libclamav unit tests use the libcheck framework. There are presently no unit tests for libfreshclam. See clamav/unit_tests/check_clamav.c for the libclamav unit tests.

+

Integration Tests

+

ClamAV is presently light on integration tests for libclamav, though you may think of the application feature as integration tests, because the apps integrate libclamav. Tests for additional features not easily exercised via the existing applications could be added by creating new example applications in clamav/examples and exercising those programs in new CTest tests. See clamav/unit_tests/CMakeLists.txt and clamav/examples/CMakeLists.txt for details.

+

Feature Tests

+

ClamAV primarily has feature tests for ClamD and ClamScan, though basic version tests do exist for FreshClam and SigTool as well. See clamav/unit_tests/CMakeLists.txt and clamav/unit_tests/clamscan_test.py for an example.

+

Building ClamAV with Autotools (v0.103 and older)

+

Running autogen.sh

+

ClamAV versions 0.103+ will require you to run autogen.sh before running configure when building from a git clone. The files generated by Autotools, such as configure, are no longer stored in the Git repo. When you run autogen.sh it will generate those files for you.

+
./autogen.sh
+
+

To run autogen.sh, you will need some extra tools:

+
    +
  • autoconf
  • +
  • automake
  • +
  • libtool
  • +
  • m4
  • +
  • pkg-config
  • +
+

The packages to install are...

+

Redhat / Centos / Fedora

+
dnf/yum install -y \
+  autoconf autoreconf automake libtool libtool-ltdl-devel m4 pkg-config
+
+

Debian / Ubuntu

+
apt-get install -y \
+  autoconf autoconf-archive automake libtool libltdl-dev m4 pkg-config
+
+

Running configure

+

To ensure that build artifacts don't clutter the source code directory, create a subdirectory named build.

+
mkdir build
+cd build
+
+

For a basic build, just run ../configure. If you've installed the dependencies with your platforms respective package manager, it should detect the dependencies automatically. macOS users will need to use this option to properly detect openssl --with-openssl=/usr/local/opt/openssl@1.1.

+

Run ../configure --help to see a full list of options. The following suggestions will help you get started:

+
    +
  • +

    Modify the CFLAGS, CXXFLAGS, OBJCFLAGS variables as follows (assuming you're build with gcc):

    +
      +
    • +

      Include gdb debugging information (-ggdb). This will make it easier to debug with gdb.

      +
    • +
    • +

      Disable optimizations (-O0). This will ensure the line numbers you see in gdb match up with what is actually being executed.

      +
    • +
    +

    Example:

    +
    CFLAGS="-ggdb -O0" CXXFLAGS="-ggdb -O0" OBJCFLAGS="-ggdb -O0" ../configure
    +
    +

    NOTE: Setting OBJCFLAGS is needed because currently, clamsubmit gets built with the Objective-C compiler. See this Stack Overflow post for a discussion of why this occurs.

    +
  • +
  • +

    Run configure with the following options:

    +
      +
    • +

      --prefix=`pwd`/../installed: This will cause make install to install into the specified directory (a directory named installed in the root of the ClamAV source code directory).

      +
    • +
    • +

      --enable-debug: This will define CL_DEBUG, which mostly just enables additional print statements that are useful for debugging.

      +
    • +
    • +

      --enable-check: Enables the unit tests, which can be run with make check.

      +
    • +
    • +

      --enable-coverage: If using gcc, sets -fprofile-arcs -ftest-coverage so that code coverage metrics will get generated when the program is run. Note that the code inserted to store program flow data may show up in any generated flame graphs or profiling output, so if you don't care about code coverage, omit this.

      +
    • +
    • +

      --enable-libjson: Enables libjson, which enables the --gen-json option. The json output contains additional metadata that might be helpful when debugging.

      +
    • +
    • +

      --with-systemdsystemunitdir=no: Don't try to register clamd as a systemd service (on systems that use systemd). You likely don't want this development build of clamd to register as a service, and this eliminates the need to run make install with sudo.

      +
    • +
    • +

      You might want to include the following flags also so that the optional functionality is enabled: --enable-experimental --enable-clamdtop --enable-milter --enable-xml --enable-pcre. Note that this may require you to install additional development libraries.

      +
    • +
    • +

      --enable-llvm --with-system-llvm=no: When LLVM is enabled, LLVM provides the capability to just-in-time compile ClamAV bytecode signatures. Without LLVM, ClamAV uses a built-in bytecode interpreter to execute bytecode signatures. With LLVM, options, "system LLVM" and "internal LLVM". The bytecode interpreter is somewhat slower than using LLVM, though the results are the same. At present only LLVM versions up to LLVM 3.6.2 are supported by ClamAV, and LLVM 3.6.2 is old enough that newer distributions no longer provide it. Therefore, we recommend using the --enable-llvm --with-system-llvm=no configure option to use the "internal LLVM". It is worth noting that the internal LLVM can take a while to build, and that the JIT compilation process for loading bytecode signatures also takes a while when starting clamd or clamdscan. For compile speed and clamscan load speed, you may wish to instead ouse --disable-llvm.

      +
    • +
    +
  • +
+

Altogether, the following configure command can be used:

+
CFLAGS="-ggdb -O0" CXXFLAGS="-ggdb -O0" OBJCFLAGS="-ggdb -O0" ../configure --prefix=`pwd`/../installed --enable-debug --enable-check --enable-coverage --enable-libjson --with-systemdsystemunitdir=no --enable-experimental --enable-clamdtop --enable-xml --enable-pcre --enable-llvm --with-system-llvm=no
+
+

NOTE: It is possible to build libclamav as a static library and have it statically linked into clamscan/clamd (to do this, run ../configure with --enable-static --disable-shared). This is useful for using tools like gprof that do not support profiling code in shared objects. However, there are two drawbacks to doing this:

+
    +
  • +

    clamscan/clamd will not be able to extract files from RAR archives. Based on the software license of the unrar library that ClamAV uses, the library can only be dynamically loaded. ClamAV will attempt to dlopen the unrar library shared object and will continue on without RAR extraction support if the library can't be found (or if it doesn't get built, which is what happens if you indicate that shared libraries should not be built).

    +
  • +
  • +

    If you make changes to libclamav, you'll need to make clean, make, and make install again to have clamscan/clamd rebuilt using the new libclamav.a. The makefiles don't seem to know to rebuild clamscan/clamd when libclamav.a changes (TODO, fix this).

    +
  • +
+

Running make

+

Run the following to finishing building. -j2 in the code below is used to indicate that the build process should use 2 cores. Increase this if your machine is more powerful.

+
make -j2
+make install
+
+

The ClamAV executables will get installed in ../installed/bin/, so to invoke clamscan do:

+
cd ..
+./installed/bin/clamscan
+
+

Testing with make check

+

You can run make check to run the unit tests and feature tests.

+

Unlike with the CTest tool used for CMake builds, you must use make check VG=1 if you wish to run extra tests using Valgrind (must be installed).

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Development/fuzzing-sanitizers.html clamav-0.103.5+dfsg/docs/html/manual/Development/fuzzing-sanitizers.html --- clamav-0.103.2+dfsg/docs/html/manual/Development/fuzzing-sanitizers.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Development/fuzzing-sanitizers.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,234 @@ + + + + + + Fuzzing Sanitizers - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Building and Testing ClamAV with Fuzzing Sanitizers

+

Build & Reproduce Fuzz Reports using OSS-Fuzz Tools

+

Check out the google/oss-fuzz repository and the clamav repo side by side. .

+

Then inside the oss-fuzz directory, run:

+
python3 infra/helper.py build_image clamav
+python3 infra/helper.py build_fuzzers --sanitizer address clamav ../clamav
+python3 infra/helper.py reproduce clamav clamav_scanmap_fuzzer /path/to/poc_file
+
+
+

Tip: You can substitute address argument in the second command that selects the Address Sanitizer (ASan) with memory or undefined to use the Memory Sanitizer (MSan) or the Undefined Behavior Sanitizer (UBSan).

+
+

See https://google.github.io/oss-fuzz/advanced-topics/reproducing/ for more details.

+

Build & Test Fuzz Targets Sanitizers in CMake (v0.104 and newer)

+

Inside your clamav git clone, run:

+
mkdir build-fuzz && cd build-fuzz
+
+export CC=`which clang` && \
+    export CXX=`which clang++` && \
+    export SANITIZER=address && \
+    cmake .. -G Ninja \
+        -D OPTIMIZE=OFF \
+        -D CMAKE_BUILD_TYPE="Debug" \
+        -D ENABLE_FUZZ=ON \
+    && \
+    ninja
+
+

This will build the fuzz target executables in the build-fuzz/fuzz/ directory. You can run them just like a regular program, passing the file as input.

+

E.g.:

+
./fuzz/clamav_scanfile_fuzzer /path/to/poc_file
+
+

ClamAV with Address Sanitizer (ASAN) in Autotools (v0.103 and older)

+

Building ClamAV with ASAN support can be extremely useful in detecting memory corruption and memory leaks. To build with ASAN, use a ..\configure line like the following:

+
CFLAGS="-ggdb -O0 -fsanitize=address -fno-omit-frame-pointer" LDFLAGS="-fsanitize=address" CXXFLAGS="-ggdb -O0 -fsanitize=address -fno-omit-frame-pointer" OBJCFLAGS="-ggdb -O0 -fsanitize=address -fno-omit-frame-pointer" ../configure --prefix=`pwd`/../installed --enable-debug --enable-libjson --with-systemdsystemunitdir=no --enable-experimental --enable-clamdtop --enable-libjson --enable-xml --enable-pcre --disable-llvm
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Development/github-pr-basics.html clamav-0.103.5+dfsg/docs/html/manual/Development/github-pr-basics.html --- clamav-0.103.2+dfsg/docs/html/manual/Development/github-pr-basics.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Development/github-pr-basics.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,228 @@ + + + + + + Pull Request Basics - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

GitHub Pull Request Basics

+

Like most projects on GitHub, we use a pull request (PR) work flow for contributions to the clamav Git repository. This is true both for contributions from the community as well as for those from the core development team here at Cisco.

+

Historically, our team PRs have been reviewed and merged on an internal upstream Git repository, though we are now migrating to do all work on GitHub directly, with a private backup fork of the project used exclusively for review of security-related fixes and in preparation of each security patch version release.

+

The general pattern for working with Pull Requests on GitHub is this:

+
    +
  1. +

    You create a fork of the project repository on GitHub.

    +
  2. +
  3. +

    You clone (download) a copy of your fork on your computer.

    +
  4. +
  5. +

    In your local fork, you create a new branch for the feature or bug fix.

    +
  6. +
  7. +

    For each change, you create a Git commit. Your commits may include incomplete work as you're going, so you can save your work each day, in the end you should edit or squash those commits down so that each of your commits represent a single completed task and that the build is never broken for any given commit.

    +
  8. +
  9. +

    You push your commits to your remote fork on GitHub as needed, both to back up your work and to prepare for submitting a pull request for team review. If you've pushed commits and then edited or squashed those commits, you may have a conflict and may need to "force push" in order to update your remote fork.

    +
  10. +
  11. +

    When ready, submit a pull request, either by using the link provided by Git when you do a push, or by using the GitHub website. Use the pull request description to include instructions for how to test your changes!

    +
  12. +
  13. +

    There are also some automated tests that get run when you submit a pull request, but you may need to re-run them if something goes wrong with the test infrastructure. Review the test results and work with the other developers to fix any issues with your pull request so it can be approved and merged.

    +
  14. +
+

If you're new to all of this, have a look at GitHub's flow guide for their general recommendations on a basic PR work flow. Then continue in the next section to learn about ClamAV's work flow.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Development/libclamav.html clamav-0.103.5+dfsg/docs/html/manual/Development/libclamav.html --- clamav-0.103.2+dfsg/docs/html/manual/Development/libclamav.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Development/libclamav.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,551 @@ + + + + + + libclamav - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Libclamav

+

Libclamav provides an easy and effective way to add a virus protection into your software. The library is thread-safe and transparently recognizes and scans within archives, mail files, MS Office document files, executables and other special formats.

+

License

+

Libclamav is licensed under the GNU GPL v2 license. This means you are not allowed to link commercial, closed-source software against it. All software using libclamav must be GPL compliant.

+

Supported formats and features

+

Executables

+

The library has a built-in support for 32- and 64-bit Portable Executable, ELF and Mach-O files. Additionally, it can handle PE files compressed or obfuscated with the following tools:

+
    +
  • Aspack (2.12)
  • +
  • UPX +
      +
    • PE (Windows) built-in
    • +
    • ELF, Mach-O enabled via bytecode signatures
    • +
    +
  • +
  • FSG (1.3, 1.31, 1.33, 2.0)
  • +
  • Petite (2.x)
  • +
  • PeSpin (1.1)
  • +
  • NsPack
  • +
  • wwpack32 (1.20)
  • +
  • MEW
  • +
  • Upack
  • +
  • Y0da Cryptor (1.3)
  • +
+

Mail files

+

Libclamav can handle almost every mail file format including TNEF (winmail.dat) attachments.

+

Archives and compressed files

+

The following archive and compression formats are supported by internal handlers:

+
    +
  • Zip (+ SFX)
  • +
  • RAR (+ SFX)
  • +
  • 7Zip
  • +
  • Tar
  • +
  • CPIO
  • +
  • Gzip
  • +
  • Bzip2
  • +
  • DMG
  • +
  • IMG
  • +
  • ISO 9660
  • +
  • PKG
  • +
  • HFS+ partition
  • +
  • HFSX partition
  • +
  • APM disk image
  • +
  • GPT disk image
  • +
  • MBR disk image
  • +
  • XAR
  • +
  • XZ
  • +
  • MS OLE2
  • +
  • MS Cabinet Files (+ SFX)
  • +
  • MS CHM (Compiled HTML)
  • +
  • MS SZDD compression format
  • +
  • BinHex
  • +
  • SIS (SymbianOS packages)
  • +
  • AutoIt
  • +
  • NSIS
  • +
  • InstallShield
  • +
+

Documents

+

The most popular file formats are supported:

+
    +
  • MS Office and MacOffice files
  • +
  • RTF
  • +
  • PDF
  • +
  • HTML
  • +
+

In the case of Office, RTF and PDF files, libclamav will only extract the embedded objects and will not decode the text data itself. The text decoding and normalization is only performed for HTML files.

+

Data Loss Prevention

+

Libclamav includes a DLP module which can detect the following credit card issuers: AMEX, VISA, MasterCard, Discover, Diner’s Club, and JCB and U.S. social security numbers inside text files.

+

Future versions of Libclamav may include additional features to detect other credit cards and other forms of PII (Personally Identifiable Information) which may be transmitted without the benefit of being encrypted.

+

Others

+

Libclamav can handle various obfuscators, encoders, files vulnerable to security risks such as:

+
    +
  • JPEG (exploit detection)
  • +
  • RIFF (exploit detection)
  • +
  • uuencode
  • +
  • ScrEnc obfuscation
  • +
  • CryptFF
  • +
+

API

+

Header file

+

Every program using libclamav must include the header file clamav.h:

+
    #include "clamav.h"
+
+

Initialization

+

Before using libclamav, you should call cl_init() to initialize it. CL_INIT_DEFAULT is a macro that can be passed to cl_init() representing the default initialization settings. When it’s done, you’re ready to create a new scan engine by calling cl_engine_new(). To free resources allocated by the engine use cl_engine_free(). Function prototypes:

+
    int cl_init(unsigned int options);
+    struct cl_engine *cl_engine_new(void);
+    int cl_engine_free(struct cl_engine *engine);
+
+

cl_init() and cl_engine_free() return CL_SUCCESS on success or another code on error. cl_engine_new() return a pointer or NULL if there’s not enough memory to allocate a new engine structure.

+

Database loading

+

The following set of functions provides an interface for loading the virus database:

+
    const char *cl_retdbdir(void);
+
+    int cl_load(const char *path, struct cl_engine *engine,
+            unsigned int *signo, unsigned int options);
+
+

cl_retdbdir() returns the default (hardcoded) path to the directory with ClamAV databases. cl_load() loads a single database file or all databases from a given directory (when path points to a directory). The second argument is used for passing in the pointer to the engine that should be previously allocated with cl_engine_new(). A number of loaded signatures will be added to signo. The last argument can pass the following flags:

+
    +
  • CL_DB_STDOPT +This is an alias for a recommended set of scan options.
  • +
  • CL_DB_PHISHING +Load phishing signatures.
  • +
  • CL_DB_PHISHING_URLS +Initialize the phishing detection module and load .wdb and .pdb +files.
  • +
  • CL_DB_PUA +Load signatures for Potentially Unwanted Applications.
  • +
  • CL_DB_OFFICIAL_ONLY +Only load official signatures from digitally signed databases.
  • +
  • CL_DB_BYTECODE +Load bytecode.
  • +
+

cl_load() returns CL_SUCCESS on success and another code on failure.

+
        ...
+        struct cl_engine *engine;
+        unsigned int sigs = 0;
+        int ret;
+
+    if((ret = cl_init(CL_INIT_DEFAULT)) != CL_SUCCESS) {
+        printf("cl_init() error: %s\n", cl_strerror(ret));
+        return 1;
+    }
+
+    if(!(engine = cl_engine_new())) {
+        printf("Can't create new engine\n");
+        return 1;
+    }
+
+    ret = cl_load(cl_retdbdir(), engine, &sigs, CL_DB_STDOPT);
+
+

Database verification

+

The cl_load() API will verify that the database is signed and is correct, although it will also return CL_SUCCESS for non-database files that of course cannot be loaded.

+

You can, however, use the cl_cvdverify() API to verify a database directly:

+
/**
+ * @brief Verify a CVD file by loading and unloading it.
+ *
+ * @param file          Filepath of CVD file.
+ * @return cl_error_t   CL_SUCCESS if success, else a CL_E* error code.
+ */
+extern cl_error_t cl_cvdverify(const char *file);
+
+

As the comment block explains, this will load-test the database. Be advised that for some larger databases, this may use a fair bit system RAM.

+

Error handling

+

Use cl_strerror() to convert error codes into human readable messages. The function returns a statically allocated string:

+
    if(ret != CL_SUCCESS) {
+        printf("cl_load() error: %s\n", cl_strerror(ret));
+        cl_engine_free(engine);
+        return 1;
+    }
+
+

Engine structure

+

When all required databases are loaded you should prepare the detection engine by calling cl_engine_compile(). In case of failure you should still free the memory allocated to the engine with cl_engine_free():

+
    int cl_engine_compile(struct cl_engine *engine);
+
+

In our example:

+
    if((ret = cl_engine_compile(engine)) != CL_SUCCESS) {
+        printf("cl_engine_compile() error: %s\n", cl_strerror(ret));
+        cl_engine_free(engine);
+        return 1;
+    }
+
+

Limits

+

When you create a new engine with cl_engine_new(), it will have all internal settings set to default values as recommended by the ClamAV authors. It’s possible to check and modify the values (numerical and strings) using the following set of functions:

+
    int cl_engine_set_num(struct cl_engine *engine,
+    enum cl_engine_field field, long long num);
+
+    long long cl_engine_get_num(const struct cl_engine *engine,
+    enum cl_engine_field field, int *err);
+
+    int cl_engine_set_str(struct cl_engine *engine,
+    enum cl_engine_field field, const char *str);
+
+    const char *cl_engine_get_str(const struct cl_engine *engine,
+    enum cl_engine_field field, int *err);
+
+

Please don’t modify the default values unless you know what you’re doing. Refer to the ClamAV sources (clamscan, clamd) for examples.

+

Database checks

+

It’s very important to keep the internal instance of the database up to date. You can watch database changes with the cl_stat..() family of functions.

+
    int cl_statinidir(const char *dirname, struct cl_stat *dbstat);
+    int cl_statchkdir(const struct cl_stat *dbstat);
+    int cl_statfree(struct cl_stat *dbstat);
+
+

Initialization:

+
        ...
+        struct cl_stat dbstat;
+
+    memset(&dbstat, 0, sizeof(struct cl_stat));
+    cl_statinidir(dbdir, &dbstat);
+
+

To check for a change you just need to call cl_statchkdir and check its return value (0 - no change, 1 - some change occurred). Remember to reset the cl_stat structure after reloading the database.

+
    if(cl_statchkdir(&dbstat) == 1) {
+        reload_database...;
+        cl_statfree(&dbstat);
+        cl_statinidir(cl_retdbdir(), &dbstat);
+    }
+
+

Libclamav includes and additional call to check the number of signatures that can be loaded from a given directory:

+
    int cl_countsigs(const char *path, unsigned int countoptions,
+        unsigned int *sigs);
+
+

The first argument points to the database directory, the second one specifies what signatures should be counted: CL_COUNTSIGS_OFFICIAL (official signatures), CL_COUNTSIGS_UNOFFICIAL (third party signatures), CL_COUNTSIGS_ALL (all signatures). The last argument points to the counter to which the number of detected signatures will be added (therefore the counter should be initially set to 0). The call returns CL_SUCCESS or an error code.

+

Data scan functions

+

It’s possible to scan a file or descriptor using:

+
    int cl_scanfile(
+        const char *filename,
+        const char **virname,
+        unsigned long int *scanned,
+        const struct cl_engine *engine,
+        struct cl_scan_options *options);
+
+    int cl_scandesc(
+        int desc,
+        const char *filename,
+        const char **virname,
+        unsigned long int *scanned,
+        const struct cl_engine *engine,
+        struct cl_scan_options *options);
+
+

Both functions will store a virus name under the pointer virname, the virus name is part of the engine structure and must not be released directly. If the third argument (scanned) is not NULL, the functions will increase its value with the size of scanned data (in CL_COUNT_PRECISION units). The last argument (options) requires a pointer to a data structure that specifies the scan options. The data structure should be memset() Each variable in the structure is a bit-flag field. The structure definition is:

+
    struct cl_scan_options {
+        uint32_t general;
+        uint32_t parse;
+        uint32_t alert;
+        uint32_t heuristic_alert;
+        uint32_t mail;
+        uint32_t dev;
+    };
+
+

Supported flags for each of the fields are as follows:

+

general - General scanning options.

+
    +
  • CL_SCAN_GENERAL_ALLMATCHES +Scan in all-match mode
  • +
  • CL_SCAN_GENERAL_COLLECT_METADATA +Collect metadata (--gen-json)
  • +
  • CL_SCAN_GENERAL_HEURISTICS +Option to enable heuristic alerts. Required for any of the heuristic alerting options to work.
  • +
+

parse - Options to enable/disable specific parsing capabilities. Generally you will want to enable all parsers. The easiest way to do this is to set the parse flags to ~0.

+
    +
  • CL_SCAN_PARSE_ARCHIVE +This flag enables transparent scanning of various archive formats.
  • +
  • CL_SCAN_PARSE_ELF +Enable support for ELF files.
  • +
  • CL_SCAN_PARSE_PDF +Enables scanning within PDF files.
  • +
  • CL_SCAN_PARSE_SWF +Enables scanning within SWF files, notably compressed SWF.
  • +
  • CL_SCAN_PARSE_HWP +Enables scanning of Hangul Word Processor (HWP) files.
  • +
  • CL_SCAN_PARSE_XMLDOCS +Enables scanning of XML-formatted documents (e.g. Word, Excel, PowerPoint, HWP).
  • +
  • CL_SCAN_PARSE_MAIL +Enable support for mail files.
  • +
  • CL_SCAN_PARSE_OLE2 +Enables support for OLE2 containers (used by MS Office and .msi files).
  • +
  • CL_SCAN_PARSE_HTML +This flag enables HTML normalization (including ScrEnc decryption).
  • +
  • CL_SCAN_PARSE_PE +This flag enables deep scanning of Portable Executable files and allows libclamav to unpack executables compressed with run-time unpackers.
  • +
+

heuristic - Options to enable specific heuristic alerts

+
    +
  • CL_SCAN_GENERAL_HEURISTIC_PRECEDENCE +Allow heuristic match to take precedence. When enabled, if a heuristic scan (such as phishingScan) detects a possible virus/phish it will stop scan immediately. Recommended, saves CPU scan-time. When disabled, virus/phish detected by heuristic scans will be reported only at the end of a scan. If an archive contains both a heuristically detected virus/phishing, and a real malware, the real malware will be reported.
  • +
  • CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE +With this flag the library will mark encrypted archives as viruses (encrypted .zip, .7zip, .rar).
  • +
  • CL_SCAN_HEURISTIC_ENCRYPTED_DOC +With this flag the library will mark encrypted documents as viruses (encrypted .pdf).
  • +
  • CL_SCAN_HEURISTIC_BROKEN +libclamav will try to detect broken executables and mark them as Broken.Executable.
  • +
  • CL_SCAN_HEURISTIC_EXCEEDS_MAX +Alert when the scan of any file exceeds maximums such as max-filesize, max-scansize, max-recursion level.
  • +
  • CL_SCAN_HEURISTIC_PHISHING_SSL_MISMATCH +Heuristic for phishing module: alert on SSL mismatches in URLs.
  • +
  • CL_SCAN_HEURISTIC_PHISHING_CLOAK +Heuristic for phishing module: alert on cloaked URLs.
  • +
  • CL_SCAN_HEURISTIC_MACROS +OLE2 containers, which contain VBA macros will be marked infected (Heuristics.OLE2.ContainsMacros).
  • +
  • CL_SCAN_HEURISTIC_PARTITION_INTXN +alert if partition table size doesn't make sense
  • +
  • CL_SCAN_HEURISTIC_STRUCTURED +Enable the data loss prevention (DLP) module which scans for credit card and SSN numbers. i.e. alert when detecting personal information
  • +
  • CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL +Search for [and alert when detecting] SSNs formatted as xx-yy-zzzz.
  • +
  • CL_SCAN_HEURISTIC_STRUCTURED_SSN_STRIPPED +Search for [and alert when detecting] SSNs formatted as xxyyzzzz.
  • +
+

mail - Options to enable specific mail parsing features

+
    +
  • CL_SCAN_MAIL_PARTIAL_MESSAGE +Scan RFC1341 messages split over many emails. You will need to periodically clean up $TemporaryDirectory/clamav-partial directory.
  • +
+

dev - Options designed for use by ClamAV developers

+
    +
  • CL_SCAN_DEV_COLLECT_SHA +Enables hash output in sha-collect builds - for internal use only
  • +
  • CL_SCAN_DEV_COLLECT_PERFORMANCE_INFO +Collect performance timings
  • +
+

All functions return CL_CLEAN when the file seems clean, CL_VIRUS when a virus is detected and another value on failure.

+
        ...
+        const char *virname;
+
+    if((ret = cl_scanfile("/tmp/test.exe", &virname, NULL, engine,
+    &options)) == CL_VIRUS) {
+        printf("Virus detected: %s\n", virname);
+    } else {
+        printf("No virus detected.\n");
+        if(ret != CL_CLEAN)
+            printf("Error: %s\n", cl_strerror(ret));
+    }
+
+

Memory

+

Because the engine structure occupies a few megabytes of system memory, you should release it with cl_engine_free() if you no longer need to scan files.

+

Forking daemons

+

If you’re using libclamav with a forking daemon you should call srand() inside a forked child before making any calls to the libclamav functions. This will avoid possible collisions with temporary filenames created by other processes of the daemon. This procedure is not required for multi-threaded daemons.

+

clamav-config

+

Use clamav-config to check compilation information for libclamav.

+
    $ clamav-config --libs
+    -L/usr/local/lib -lz -lbz2 -lgmp -lpthread
+
+    $ clamav-config --cflags
+    -I/usr/local/include -g -O2
+
+

Example

+

You will find an example scanner application in the clamav source under ./examples.

+

In ClamaV 0.104+, you can build the example programs alongside ClamAV by configuring with -D ENABLE_EXAMPLES=ON.

+

Or, if you have ClamAV already installed, execute the following to compile it:

+
gcc -Wall ex1.c -o ex1 -lclamav
+
+

CVD format

+

CVD (ClamAV Virus Database) is a digitally signed tarball containing one or more databases. The header is a 512-bytes long string with colon separated fields:

+
    ClamAV-VDB:build time:version:number of signatures:functionality
+    level required:MD5 checksum:digital signature:builder name:build time (sec)
+
+

sigtool --info displays detailed information on CVD files:

+
    $ sigtool -i daily.cvd
+    File: daily.cvd
+    Build time: 10 Mar 2008 10:45 +0000
+    Version: 6191
+    Signatures: 59084
+    Functionality level: 26
+    Builder: ccordes
+    MD5: 6e6e29dae36b4b7315932c921e568330
+    Digital signature: zz9irc9irupR3z7yX6J+OR6XdFPUat4HIM9ERn3kAcOWpcMFxq
+    Fs4toG5WJsHda0Jj92IUusZ7wAgYjpai1Nr+jFfXHsJxv0dBkS5/XWMntj0T1ctNgqmiF
+    +RLU6V0VeTl4Oej3Aya0cVpd9K4XXevEO2eTTvzWNCAq0ZzWNdjc
+    Verification OK.
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Development/performance-profiling.html clamav-0.103.5+dfsg/docs/html/manual/Development/performance-profiling.html --- clamav-0.103.2+dfsg/docs/html/manual/Development/performance-profiling.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Development/performance-profiling.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,348 @@ + + + + + + Performance Profiling - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Performance Profiling

+

Flame Graph Profiling

+

FlameGraph is a great tool for generating interactive flamegraphs based collected profiling data. The github page has thorough documentation on how to use the tool, but an overview is presented below:

+

First, install perf, which on Linux can be done via:

+
sudo apt-get install linux-tools-common linux-tools-generic linux-tools-`uname -r`
+
+
+

Tip: If you're on Windows using WSL2 with Ubuntu 20.04, you may find that the above fails with this error message:

+
E: Unable to locate package linux-tools-4.19.104-microsoft-standard
+E: Couldn't find any package by glob 'linux-tools-4.19.104-microsoft-standard'
+E: Couldn't find any package by regex 'linux-tools-4.19.104-microsoft-standard'
+
+

You may have luck building and installing perf yourself using Microsoft's WSL2 Linux Kernel sources. At the time of writing, this doesn't work with the master branch because the perf build appears to be broken with newer versions of glibc. Instead, we can use the linux-msft-wsl-5.10.16.3 tag:

+
sudo apt install flex bison
+git clone https://github.com/microsoft/WSL2-Linux-Kernel --depth 1 --branch linux-msft-wsl-5.10.16.3
+cd WSL2-Linux-Kernel/tools/perf
+make -j8
+sudo cp perf /usr/local/bin
+
+

Technique courtesy of: https://gist.github.com/abel0b/b1881e41b9e1c4b16d84e5e083c38a13

+
+

Modify the system settings to allow perf record to be run by a standard user:

+
sudo su     # Run the following as root
+cat /proc/sys/kernel/perf_event_paranoid
+echo "1" > /proc/sys/kernel/perf_event_paranoid
+exit
+
+

Invoke clamscan via perf record as follows, and run perf script to collect the profiling data. Note that in this example, ClamAV was compiled in a build subdirectory and installed to build/install:

+
perf record -F 100 -g -- ./install/bin/clamscan -d ./unit_tests/clamav.hdb --allmatch ./test/
+perf script > /tmp/out.perf
+
+

The -F parameter indicates how many samples should be collected during program execution. If your scan will take a long time to run, a lower value should be sufficient. Otherwise, consider choosing a higher value (on Ubuntu 18.04, 7250 is the max frequency, but it can be increased via /proc/sys/kernel/perf_event_max_sample_rate.

+

Clone out the FlameGraph project and run the following commands inside the FlameGraph directory to generate the flame graph:

+
git clone https://github.com/brendangregg/FlameGraph.git
+cd FlameGraph
+
+perl stackcollapse-perf.pl /tmp/out.perf > /tmp/out.folded
+perl flamegraph.pl /tmp/out.folded > /tmp/test.svg
+
+

The SVG that is generated is interactive, but some viewers don't support this. +Be sure to open it in a web browser like Chrome to be able to take full advantage of it.

+

Here's an example flamegraph generated by scanning ClamAV test files:

+

+ + Example Flamegraph + +

+

Call Graph Profiling - Callgrind

+

Callgrind is a profiling tool included with valgrind. This can be done by prepending valgrind --tool=callgrind to the clamscan command.

+

kcachegrind is a follow-on tool that will graphically present the profiling data and allow you to explore it visually, although if you don't already use KDE you'll have to install lots of extra packages to use it.

+

System Call Tracing / Fault Injection

+

strace can be used to track the system calls that are performed and provide the number of calls / time spent in each system call. This can be done by prepending strace -c to a clamscan command. Results will look something like this:

+
    % time     seconds  usecs/call     calls    errors syscall
+    ------ ----------- ----------- --------- --------- ----------------
+    95.04    0.831430          13     62518           read
+    3.22    0.028172          14      2053           munmap
+    0.69    0.006005           3      2102           mmap
+    0.28    0.002420           7       344           pread64
+    0.16    0.001415           5       305         1 openat
+    0.13    0.001108           3       405           write
+    0.11    0.000932          23        40           mprotect
+    0.07    0.000632           2       310           close
+    0.07    0.000583           9        67        30 access
+    0.05    0.000395           1       444           lseek
+    0.04    0.000344           2       162           fstat
+    0.04    0.000338           1       253           brk
+    0.03    0.000262           1       422           fcntl
+    0.02    0.000218          16        14           futex
+    0.01    0.000119           1       212           getpid
+    0.01    0.000086          14         6           getdents
+    0.00    0.000043           7         6           dup
+    0.00    0.000040           1        31           unlink
+    0.00    0.000038          19         2           rt_sigaction
+    0.00    0.000037          19         2           rt_sigprocmask
+    0.00    0.000029           1        37           stat
+    0.00    0.000022          11         2           prlimit64
+    0.00    0.000021          21         1           sysinfo
+    0.00    0.000020           1        33           clock_gettime
+    0.00    0.000019          19         1           arch_prctl
+    0.00    0.000018          18         1           set_tid_address
+    0.00    0.000018          18         1           set_robust_list
+    0.00    0.000013           0        60           lstat
+    0.00    0.000011           0        65           madvise
+    0.00    0.000002           0        68           geteuid
+    0.00    0.000000           0         1           execve
+    0.00    0.000000           0         1           uname
+    0.00    0.000000           0         1           getcwd
+    ------ ----------- ----------- --------- --------- ----------------
+    100.00    0.874790                 69970        31 total
+
+

strace can also be used for cool things like system call fault injection. For instance, let's say you are curious whether the read bytecode API call is implemented in such a way that the underlying read system call could handle EINTR being returned (which can happen periodically). To test this, write the following bytecode rule:

+
    VIRUSNAME_PREFIX("BC.Heuristic.Test.Read.Passed")
+    VIRUSNAMES("")
+    TARGET(0)
+
+    SIGNATURES_DECL_BEGIN
+    DECLARE_SIGNATURE(zeroes)
+    SIGNATURES_DECL_END
+
+    SIGNATURES_DEF_BEGIN
+    DEFINE_SIGNATURE(zeroes, "0:0000")
+    SIGNATURES_DEF_END
+
+    bool logical_trigger()
+    {
+        return matches(Signatures.zeroes);
+    }
+
+    #define READ_S(value, size) if (read(value, size) != size) return 0;
+
+    int entrypoint(void)
+    {
+        char buffer[65536];
+        int i;
+
+        for (i = 0; i < 256; i++)
+        {
+            debug(i);
+            debug("\n");
+            READ_S(buffer, sizeof(buffer));
+        }
+
+        foundVirus("");
+        return 0;
+    }
+
+

Compiled the rule, and make a test file to match against it. Then run it under strace to determine what underlying read system call is being used for the bytecode read function:

+
clambc-compiler read_test.bc
+dd if=/dev/zero of=/tmp/zeroes bs=65535 count=256
+strace clamscan -d read_test.cbc --bytecode-unsigned /tmp/zeroes
+
+

It uses pread64 under the hood, so the following command could be used for fault injection:

+
strace -e fault=pread64:error=EINTR:when=20+10 clamscan -d read_test.cbc --bytecode-unsigned /tmp/zeroes
+
+

This command tells strace to skip the first 20 pread64 calls (these appear to be used by the loader, which didn't seem to handle EINTR correctly) but to inject EINTR for every 10th call afterward. We can see the injection in action and that the system call is retried successfully:

+
    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15007744) = 65536
+    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15073280) = 65536
+    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15138816) = 65536
+    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15204352) = 65536
+    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15269888) = 65536
+    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15335424) = 65536
+    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15400960) = 65536
+    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15466496) = 65536
+    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15532032) = 65536
+    pread64(3, 0x7f6a7ff43000, 65536, 15597568) = -1 EINTR (Interrupted system call) (INJECTED)
+    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15597568) = 65536
+
+

More documentation on using strace to perform system call fault injection, see this presentation from FOSDEM 2017.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Development/personal-forks.html clamav-0.103.5+dfsg/docs/html/manual/Development/personal-forks.html --- clamav-0.103.2+dfsg/docs/html/manual/Development/personal-forks.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Development/personal-forks.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,257 @@ + + + + + + Working with Your Fork - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Working with a Your Own Fork of the ClamAV repository

+

A "fork" on GitHub is a personal playground. Though the word "fork" in the concept of open source traditionally referred to creating (and maintaining) a new variant of a project, forks on GitHub/GitLab/BitBucket/etc these days typically refer to a personal copy of the project where a user can test modifications to fix a bug or add a feature before contributing it back to the project in the form of a "pull request".

+

Create and Maintain a Personal Fork

+

You may only have one for any project, but it is very easy to create:

+

Create a Fork

+

You can rename it as needed so you won't confused a clone of your fork with that of the upstream clamav repository. Go to the "Settings" page and change the name to add your name in a suffix:

+

Change Fork Name

+

You're free to add or delete branches in your fork as you see fit, but I would advise against adding your own commits to the existing branches. The existing branches, particularly the default branch* are a reference from which you can create your own branches for your work. Adding your own commits to the existing branches will break your ability to synchronize with the upstream Cisco-Talos/clamav repository, and without more advanced Git experience you won't be able to correct it.

+
+

Tip: If you've managed to screw up the commit history in your fork to the point where you don't know how to fix it, you can always delete your fork and create a new one.

+
+

Your fork is a snapshot of the upstream clamav repository at the moment at which you created it. Left unmaintained, your fork's default branch will get left behind. Unlike BitBucket, GitHub will not sync branches for you automatically. If your branch is behind, it is simple to sync the branch on your fork using GitHub's GUI by pressing the "Fetch Upstream" button:

+

Sync and Merge

+

You can sync other branches too. Simply switch branches to the desired branch and press "Fetch Upstream" again.

+
+

Disclaimer: The ClamAV project has a history of changing default branches for development on each feature version. We've found that this causes more trouble than it is worth, and we intend to stop doing that after 0.104. Right now, the default branch is dev/0.104. After 0.104 is complete, it will be changed to main and we'll stop changing it. For more details, see the work flow changes in the section below.

+

Tip: After we change the default branch for Cisco-Talos/clamav to main, you'll need to change your default branch too (it won't switch in your fork just because we changed the default in the upstream repo).

+
+

Working with a Clone of your Fork on the Command Line

+

If you don't already have an SSH key for your GitHub account, I recommend creating one. Navigate to your Account Settings and under SSH and GPG keys, click the "New SSH key" button. If you're unfamiliar with how to generate an SSH key, there's a nearby link "generating SSH keys" with additional instructions.

+

Next, clone your ClamAV fork. Use the "Code" button on the default page for your fork to copy the "SSH" URL. If you don't want to use an SSH key for GitHub authentication, use the HTTPS URL instead:

+

Clone your Fork

+

Now open up a terminal and type:

+
git clone <paste that Git URL>
+cd clamav-YourNameHere
+
+

Create a branch off of the default branch where you will work. If working on a GitHub Issue, Bugzilla Bug, or JIRA task*, the following branch name prefixes will help you and others identify the branch:

+
    +
  • For GitHub Issues: issue-####-short-description
  • +
  • For Bugzilla Bugs: bb-####-short-description
  • +
  • For JIRA task: CLAM-####-short-description
  • +
+
+

Note: *The ClamAV JIRA task tracker is not accessible outside of the Cisco network.

+
+

Create your working branch:

+
git checkout -b issue-####-short-description
+
+

You're now ready to make edits to the source. Be sure your changes match our code format style. The easiest way is to install clang-format and enable "Format On Save" in your text editor.

+

When you have made your changes, run:

+
git add -u
+git commit
+
+

Leave a meaningful git commit message that has a high-level descriptive title, and a more technical message body describing why the change was needed what your commit does to resolve it.

+

Run this to upload your commit to your fork on GitHub:

+
git push -u origin <the_name_of_your_branch>
+
+

The -u origin argument will enable tracking between your local branch and your remote branch. In the future you will only need to do git push and it will know where to push it.

+

Rebase your development branch with the upstream main branch and resolving merge conflicts

+

If you don't already have it:

+
git remote add upstream git@github.com:Cisco-Talos/clamav.git
+
+

Then run:

+
git fetch upstream
+git rebase upstream/main
+
+

If you have any merge conflicts, you'll now have the opportunity to fix them. After every conflict is resolved and you've saved the files in question, run git add <the resolved files> and then run git rebase --continue.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Development/testing-pull-requests.html clamav-0.103.5+dfsg/docs/html/manual/Development/testing-pull-requests.html --- clamav-0.103.2+dfsg/docs/html/manual/Development/testing-pull-requests.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Development/testing-pull-requests.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,223 @@ + + + + + + Reviewing Pull Requests - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Reviewing & Testing Pull Requests

+

The tools available on GitHub are fantastic for reviewing code changes, but less so for checking out those code changes on your local machine to build and test them.

+

With a simple tweak to your .git/config file in a clone of the Cisco-Talos/clamav repository, you can simply check out another developer's pull request as a branch to build and test it.

+

First, if you don't already have a clone of the upstream repository, do this:

+
git clone https://github.com/Cisco-Talos/clamav.git
+
+

Then, in your favorite text editor, open .git/config and add the following line at the end of the [remote "origin"] section. Don't delete or replace anything, just add the following line:

+
fetch = +refs/pull/*/head:refs/remotes/origin/PR-*
+
+
+

Tip: The above works for GitHub Cloud and GitHub Enterprise servers. If you happen to work with GitLab or BitBucket, you can do this instead for the same effect:

+

For BitBucket repos:

+
fetch = +refs/pull-requests/*/from:refs/remotes/origin/PR-*
+
+

For GitLab repos:

+
fetch = +refs/merge-requests/*/head:refs/remotes/origin/PR-*
+
+
+

Now you may run the following to test the pull request. Substitute # with the pull request number found on the website:

+
git fetch
+git checkout PR-#
+
+

At this point, you should find yourself in a branch containing the PR contributor's changes.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Development/tips-and-tricks.html clamav-0.103.5+dfsg/docs/html/manual/Development/tips-and-tricks.html --- clamav-0.103.2+dfsg/docs/html/manual/Development/tips-and-tricks.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Development/tips-and-tricks.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,290 @@ + + + + + + Dev Tips & Tricks - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Development Tips & Tricks

+

The following are a collection of tips that may help you be a more productive ClamAV developer.

+ +

Downloading the Official Ruleset

+

If you plan to use custom rules for testing, you can invoke clamscan via ./installed/bin/clamscan, specifying your custom rule files via -d parameters.

+

If you want to download the official ruleset to use with clamscan, do the following:

+
    +
  1. Run mkdir -p installed/share/clamav
  2. +
  3. Comment out line 8 of etc/freshclam.conf.sample
  4. +
  5. Run ./installed/bin/freshclam --config-file etc/freshclam.conf.sample
  6. +
+

General Debugging

+

NOTE: Some of the debugging/profiling tools mentioned in the sections below are specific to Linux

+

Useful clamscan Flags

+

The following are useful flags to include when debugging clamscan:

+
    +
  • +

    --debug --verbose: Print lots of helpful debug information

    +
  • +
  • +

    --gen-json: Print some additional debug information in a JSON format

    +
  • +
  • +

    --statistics=pcre --statistics=bytecode: Print execution statistics on any PCRE and bytecode rules that were evaluated

    +
  • +
  • +

    --dev-performance: Print per-file statistics regarding how long scanning took and the times spent in various scanning stages

    +
  • +
  • +

    --alert-broken: This will attempt to detect broken executable files. If an executable is determined to be broken, some functionality might not get invoked for the sample, and this could be an indication of an issue parsing the PE header or file. This causes those binary to generate an alert instead of just continuing on. This flag replaces the --detect-broken flag from releases prior to 0.101.

    +
  • +
  • +

    --max-filesize=2000M --max-scansize=2000M --max-files=2000000 --max-recursion=2000000 --max-embeddedpe=2000M --max-htmlnormalize=2000000 --max-htmlnotags=2000000 --max-scriptnormalize=2000000 --max-ziptypercg=2000000 --max-partitions=2000000 --max-iconspe=2000000 --max-rechwp3=2000000 --pcre-match-limit=2000000 --pcre-recmatch-limit=2000000 --pcre-max-filesize=2000M --max-scantime=2000000:

    +

    Effectively disables all file limits and maximums for scanning. This is useful if you'd like to ensure that all files in a set get scanned, and would prefer clam to just run slowly or crash rather than skip a file because it encounters one of these thresholds

    +
  • +
+

The following are useful flags to include when debugging rules that you're +writing:

+
    +
  • +

    -d: Allows you to specify a custom ClamAV rule file from the command line

    +
  • +
  • +

    --bytecode-unsigned: If you are testing custom bytecode rules, you'll need this flag so that clamscan actually runs the bytecode signature

    +
  • +
  • +

    --all-match: Allows multiple signatures to match on a file being scanned

    +
  • +
  • +

    --leave-temps --tmpdir=/tmp: By default, ClamAV will attempt to extract embedded files that it finds, normalize certain text files before looking for matches, and unpack packed executables that it has unpacking support for. These flags tell ClamAV to write these intermediate files out to the directory specified. Usually when a file is written, it will mention the file name in the --debug output, so you can have some idea at what stage in the scanning process a tmp file was created.

    +
  • +
  • +

    --dump-certs: For signed PE files that match a rule, display information about the certificates stored within the binary.

    +
    +

    Note: sigtool has this functionality as well and doesn't require a rule match to view the cert data

    +
    +
  • +
+

Using gdb

+

Given that you might want to pass a lot of arguments to gdb, consider taking advantage of the --args parameter. For example:

+
gdb --args ./installed/bin/clamscan -d /tmp/test.ldb -d /tmp/block_list.crb -d --dumpcerts --debug --verbose --max-filesize=2000M --max-scansize=2000M --max-files=2000000 --max-recursion=2000000 --max-embeddedpe=2000M --max-iconspe=2000000 f8f101166fec5785b4e240e4b9e748fb6c14fdc3cd7815d74205fc59ce121515
+
+

When using ClamAV without libclamav statically linked, if you set breakpoints on libclamav functions by name, you'll need to make sure to indicate that the breakpoints should be resolved after libraries have been loaded.

+

For other documentation about how to use gdb, check out the following resources:

+ +

Hunting for Memory Leaks

+

You can easily hunt for memory leaks with valgrind. Check out this guide to get started: Valgrind Quick Start

+

If checking for leaks, be sure to run clamscan with samples that will hit as many of the unique code paths in the code you are testing. An example invocation is as follows:

+
valgrind --leak-check=full ./installed/bin/clamscan -d /tmp/test.ldb --leave-temps --tempdir /tmp/test --debug --verbose /tmp/upx-samples/ > /tmp/upx-results-2.txt 2>&1
+
+

Alternatively, on Linux, you can use glibc's built-in leak checking functionality:

+
MALLOC_CHECK_=7 ./installed/bin/clamscan
+
+

See the mallopt man page for more details

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Development.html clamav-0.103.5+dfsg/docs/html/manual/Development.html --- clamav-0.103.2+dfsg/docs/html/manual/Development.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Development.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,221 @@ + + + + + + For Developers - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

ClamAV Development

+

This chapter aims to provide information useful when developing, debugging, or profiling ClamAV.

+

Pull Request Basics

+

If you're new to open source software development on GitHub, take a moment to read this quick introduction on GitHub Pull Requests.

+

ClamAV Git Work Flow

+

ClamAV's Git work flow isn't too complicated, but it is unique. See this section to get a better understanding of how we use Git.

+

Working with Your Fork

+

New core developers and community contributors can follow these steps to prepare an environment to work on ClamAV development.

+

Reviewing Pull Requests

+

If you need to review a pull request, it's best to not only eyeball the changes and review the test logs, but to also build and test the PR manually on your own computer. This section will help you check out someone else's PR so you can test it.

+

Building for Development

+

Basic instructions for building ClamAV can be in the Installing chapter and a comprehensive reference for compiling ClamAV with CMake is available in the INSTALL.md file accompanying the source code, but if you want a few extra tips on building ClamAV for development purposes, check this out.

+

Building the Installer Packages

+

If you'd like to learn how we build the installer packages and even replicate this on your own, you can read about it here.

+

Dev Tips & Tricks

+

This section contains a varied assortment of techniques you can use when coding on ClamAV to up your development game.

+

libclamav

+

Are you interested in using libclamav to scan for malware in your own C/C++ application? This section introduces the most important functions of the libclamav API. For a more comprehensive reference, inline documention can be found in the clamav.h header distributed with libclamav.

+

Contribute

+

If you're interested in contributing to ClamAV, we've assembled a page of bugs that need fixing as well as other project ideas that we feel might be great new-contributor projects.

+

If you're looking for project ideas, check out the project ideas list to find out how you might be able to help out the project!

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Installing/Add-clamav-user.html clamav-0.103.5+dfsg/docs/html/manual/Installing/Add-clamav-user.html --- clamav-0.103.2+dfsg/docs/html/manual/Installing/Add-clamav-user.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Installing/Add-clamav-user.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,255 @@ + + + + + + Add a service user account - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Add a service user account

+

If you're planning to run freshclam or clamd as a service on a Linux or Unix system, you should create a service account. The following instructions assume that you will use the an account named "clamav" for both services, although you may create a different account name for each if you wish.

+
+

Note: These instructions are mostly just for folks building & installing from source. If you installed a package from your Linux/Unix distribution, it probably created the account(s) for you.

+
+

Create a service user account (and group)

+

Linux / Unix

+

As root or with sudo, run:

+
groupadd clamav
+useradd -g clamav -s /bin/false -c "Clam Antivirus" clamav
+
+

If your operating system does not have the groupadd and useradd utilities, consult a system manual. Don’t forget to lock access to the account!

+

macOS

+

Prep by identifying an unused group id (gid), and an unused user UniqueID.

+

This command will display all current group PrimaryGroupIDs:

+
dscl . list /Groups PrimaryGroupID | tr -s ' ' | sort -n -t ' ' -k2,2
+
+

This command will display all current user UniqueIDs:

+
dscl . list /Users UniqueID | tr -s ' ' | sort -n -t ' ' -k2,2
+
+

Then, these commands can be used to create the clamav group and clamav user.

+
sudo dscl . create /Groups/clamav
+sudo dscl . create /Groups/clamav RealName "Clam Antivirus Group"
+sudo dscl . create /Groups/clamav gid 799           # Ensure this is unique!
+sudo dscl . create /Users/clamav
+sudo dscl . create /Users/clamav RealName "Clam Antivirus User"
+sudo dscl . create /Users/clamav UserShell /bin/false
+sudo dscl . create /Users/clamav UniqueID 599       # Ensure this is unique!
+sudo dscl . create /Users/clamav PrimaryGroupID 799 # Must match the above gid!
+
+

About how the service accounts are used

+

At present, the behavior differs slightly between clamd and freshclam. When run as root:

+
    +
  • +

    freshclam will always switch to run as the "DatabaseOwner" user account. The default account name is "clamav", or may be customized by specifying the "DatabaseOwner" setting in freshclam.conf.

    +
  • +
  • +

    clamd will only switch to run as the "User" user account if the "User" setting is specified in clamd.conf. If you do not specify a "User" in the config, clamd will continue to run as the root user! We may change this behavior in a future version to prevent clamd from being run as root.

    +
  • +
+
+

Caution: We do not recommend running clamd as root for safety reasons because ClamAV scans untrusted files that may be malware. Always configure the "User" setting in clamd.conf if you plan to run clamd as a service.

+
+

On Unix/Linux systems, freshclam and clamd will switch to run as a different user if you start them as the root user, or using sudo. By default, that user account is named "clamav". The purpose is t

+

If you are running freshclam and clamd as root or with sudo, and you did not explicitly configure with --disable-clamav, you will want to ensure that the DatabaseOwner user specified in freshclam.conf owns the database directory so it can download signature updates.

+

The user that clamd, clamdscan, and clamscan run as may be the same user, but if it isn't -- it merely needs read access to the database directory.

+

If you choose to use the default clamav user to run freshclam and clamd, you'll need to create the clamav group and the clamav user account the first time you install ClamAV.

+

After installation: Make the service account own the database directory

+

After you've installed ClamAV, you will want to make it so that the database directory is owned by the same service account as you're using for freshclam.

+

As root or with sudo, run:

+
sudo chown -R clamav:clamav /usr/local/share/clamav
+
+

Or (if you customized the database path):

+
chown -R clamav:clamav /var/lib/clamav/
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Installing/Community-projects.html clamav-0.103.5+dfsg/docs/html/manual/Installing/Community-projects.html --- clamav-0.103.2+dfsg/docs/html/manual/Installing/Community-projects.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Installing/Community-projects.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,393 @@ + + + + + + Community Projects - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Community Projects

+
+

Disclaimer: The software listed in this section is authored by third parties and not by the ClamAV Team. Compatibility may vary.

+
+

Signatures

+

The ClamAV Team provides FreshClam for ClamAV agents to update the official signature databases and provides CVD-Update for Private Mirror administrators to update their server content.

+

Both FreshClam and CVD-Update have some limited features to update signatures from third-party sources but community tools exist that are designed for this purpose and provide a more complete experience for users that want the extra coverage.

+
+

WARNING: While there are no known vulnerabilities in using traditional content-based and hash-based ClamAV signatures, bytecode signatures are a different story. Bytecode signatures are effectively cross-platform executable plugins similar to Web Assembly (WASM) but with less sandboxing.

+

ClamScan and ClamD will not run unsigned bytecode signatures by default. Cisco-Talos' signing certificate is the only certificate trusted by ClamAV to run bytecode signatures.

+

Both ClamD and ClamScan have options to run unsigned bytecode signatures but you should NEVER enable unsigned bytecode signatures in production when using signatures from third-party sources or a malicious bytecode signature author could gain control of your systems.

+

ClamBC is a tool installed with ClamAV for testing bytecode signatures and should also NEVER be used to run signatures from an unknown or untrusted source.

+
+

Fangfrish

+

Fangfrisch (German for "freshly caught") is a sibling of the Clam Anti-Virus FreshClam utility. It allows downloading virus definition files that are not official ClamAV canon, e.g. from Sanesecurity, URLhaus and others. Fangfrisch was designed with security in mind, to be run by an unprivileged user only.

+

Detailed documentation is available online.

+

Get fangfrish

+

Mail Filters

+

ClamAV is popular for filtering mail. The ClamAV Team maintains ClamAV-Milter, which is a filter for the Sendmail mail transfer agent and the ClamAV community have created a wide variety of other tools to use ClamAV with different mail transfer agents.

+

Generic Mail Transfer Agents

+

amavisd-new | clamd, clamscan

+

amavisd-new is a high-performance interface between mailer (MTA) and content checkers: virus scanners, and/or SpamAssassin. It is written in Perl for maintainability, without paying a significant price for speed. It talks to MTA via (E)SMTP or LMTP, or by using helper programs. Best with Postfix, fine with dual-sendmail setup and Exim v4, works with sendmail/milter, or with any MTA as a SMTP relay. For Courier and qmail MTA integration there is a patch in the distributed package.

+

amavisd-new is a rewritten version of Amavis and is maintained by Mark Martinec.

+

ClamScan is enabled automatically if clamscan binary is found at amavisd-new startup time. ClamD is activated by uncommenting its entry in the @av_scanners list in the file /etc/amavisd.conf.

+

Get amavisd-new

+

Sendmail

+

MIMEDefang | clamscan, clamd

+

MIMEDefang is an efficient mail scanner for Sendmail/milter written in C, Perl.

+

Get MIMEDefang

+

Postfix

+

ClamSMTP | clamd

+

ClamSMTP is an SMTP filter for Postfix and other mail servers that checks for viruses using the ClamAV anti-virus software. It aims to be lightweight, reliable, and simple rather than have a myriad of options. Written in C without major dependencies.

+

Get ClamSMTP

+

Clapf | libclamav

+

Clapf is a clamav based virus scanning and anti-spam content filter for Postfix.

+

Get clapf

+

Exim

+

Starting with release 4.50, Exim natively supports ClamAV.

+

Get exim

+

Others

+

Mail Avenger | clamscan

+

Mail Avenger is a highly-configurable SMTP server. It allows you to reject spam during mail transactions, before spooling messages in your local mail queue. You can specify site-wide default policies for filtering mail, but individual users can also craft their own policies by creating avenger scripts in their home directories.

+

Get Mail Avenger

+

MailScanner | clamscan

+

MailScanner scans all e-mail for viruses, spam and attacks against security vulnerabilities. It is not tied to any particular virus scanner, but can be used with any combination of 14 different virus scanners, allowing sites to choose the best of breed virus scanner.

+

Get Mail Scanner

+

Sagator | clamscan, clamd, libclamav

+

Sagator is an email antivirus/antispam gateway. Its modular architecture can use any combination of antivirus/spamchecker according to configuration.

+

Get Sagator

+

Courier-MTA | libclamav, clamavd

+

Courier MTA includes four filers.

+

courier-pythonfilter by Gordon Messner. Included in a Python filter suite, it uses pyClamAV (libclamav with python)

+

Courier::Filter::Module::ClamAVd by Julian Mehnle. A Perl module for use with Courier::Filter, using clamavd.

+

ClamCour by Tony Di Monaco. A C++ (with Boost) multithreaded filter using libclamav

+

avfilter by Alessandro Vesely. A C forking filter using libclamav.

+

Get Courier-MTA

+

Haraka | clamd

+

Haraka is a robust MTA written in node.js, with a modular architecture that lets plugins control nearly every aspect of the SMTP conversation. There is a large selection of included plugins, including a clamav plugin (docs, source) that filters messages using clamd.

+

Haraka is attractive to two audiences:

+
    +
  1. +

    Anyone managing mail systems with thousands or tens-of-thousands of concurrent incoming SMTP connections (like Craigslist) and wants to do with it fewer racks of servers.

    +
  2. +
  3. +

    Developers who need more control over mail routing, filtering, and processing than can be easily or efficiently handled with traditional (milter-based) MTAs.

    +
  4. +
+

Get Haraka

+

Web & FTP Tools

+

Clammit | clamd

+

Clammit is a proxy that will perform virus scans of files uploaded via http requests, including multipart/form-data. If a virus exists, it will reject the request out of hand. If no virus exists, the request is then forwarded to the application and it's response returned in the upstream direction.

+

As the name implies, Clammit offloads the virus detection to the ClamAV virus detection server (clamd).

+

Get Clammit

+

Clara

+

Serverless, real-time, ClamAV+Yara scanning for your S3 Buckets

+

Get Clara

+

bucket-antivirus-function

+

Scan new objects added to any s3 bucket using AWS Lambda.

+

Get bucket-antivirus-function

+

cdk-serverless-clamscan

+

An aws-cdk construct that uses ClamAV® to scan objects in Amazon S3 for viruses. The construct provides a flexible interface for a system to act based on the results of a ClamAV virus scan.

+

Get cdk-serverless-clamscan

+

Antivirus for Amazon S3

+

A CloudFormation template to create an EC2 scanner cluster for S3 buckets.

+

Get Antivirus for Amazon S3

+

HAVP | libclamav

+

HAVP is a proxy with an antivirus filter. It does not cache or filter content. At the moment the complete traffic is scanned. A reason for that is the chance of malicious code in nearly every filetypes e.g. HTML (JavaScript) or Jpeg.

+

Get HAVP

+

mod_clamav | libclamav, clamd

+

mod_clamav is an Apache virus scanning filter. It was written and is currently maintained by Andreas Müller. The project is very well documented and the installation is quite easy.

+

Get mod_clamav

+

phpMussel | clamav

+

phpMussel is a PHP-based script based upon ClamAV signatures designed to detect trojans, viruses, malware and other threats within files uploaded to your system wherever the script is hooked. Written by Maikuolan

+

Get phpMussel

+

SpamAssassin - ClamAVPlugin | clamd

+

A ClamAV plug in fpr SpamAssassin 3.X

+

Get ClamAVPlugin

+

clamav-rest

+

Simple ClamAV REST proxy. Builds on top of clamav-java which is a minimal Java client for ClamAV.

+

Get clamav-rest

+

Filesystem & On-Access Scanning

+

Clam Sentinel

+

Clam sentinel is a program that detects file system changes and automatically scans the files added or modified using ClamWin. Require the installation of ClamWin. For Microsoft Windows 98/98SE/Me/2000/XP/Vista, Windows 7 and Windows 8.1.

+

Get Clam Sentinel

+

ClamFS | clamd

+

ClamFS is a FUSE-based user-space file system for Linux and BSD with on-access anti-virus file scanning through clamd daemon (a file scanning service developed by ClamAV Project).

+

Features:

+
    +
  • Scans files using ClamAV
  • +
  • User-space file system (no kernel patches, modules, recompilations, etc.)
  • +
  • Based on libFUSE version 3 (until version 1.1.0 on libFUSE v2)
  • +
  • Implements all clamd scan modes: fname, fdpass and stream
  • +
  • Supports remote clamd instances in stream mode over TCP/IP socket
  • +
  • Caches scan results in a LRU cache with time-based and out-of-memory expiration
  • +
  • Configuration stored in XML files
  • +
  • Supports ulockmgr
  • +
  • Sends mails to administrator when detects virus
  • +
+

Get ClamFS

+

Avfs | ClamAV

+

Avfs, a true on-access anti-virus file system that incrementally scans files and prevents infected data from being committed to disk. Avfs is a stackable file system and therefore can add virus detection to any other file system: Ext3, NFS, etc. Avfs supports forensic modes that can prevent a virus from reaching the disk or automatically create versions of potentially infected files to allow safe recovery. Avfs can also quarantine infected files on disk and isolate them from user processes.

+

Avfs uses a matching algorithm that is derived from ClamAV but with changes to optimize scan time for larger signature sets. Though this project does not appear to be maintained or used elsewhere, the research was really good work and may inspire optimizations in ClamAV in the future.

+

More about Avfs

+

Mail User Agents

+

Claws Mail

+

Claws Mail is a user-friendly, lightweight, and fast email client. Claws Mail has a ClamD plugin for scanning received messages using ClamAV.

+

Get Claws Mail

+

Kmail | clamscan

+

Mail is a fully-featured email client that fits nicely into the K Desktop Environment, KDE. It supports attachment scanning with clamscan.

+

Get Kmail

+

Open Webmail modules | clamscan

+

Open WebMail by default can use ClamAV as the external viruscheck module to scan messages fetched from pop3 servers or all incoming messages. If a message or its attachments is found to have virus, Open WebMail will move the message from INBOX to the VIRUS folder automatically.

+

Get Open Webmail

+

ClamAV Bindings

+

Rust

+

clamav-rs | libclamav

+

A safe Rust binding for libclamav. clamav-rs uses clamav-sys to wrap the libclamav C API.

+

Get clamav-rs

+

clamav-sys | libclamav

+

clamav-sys is a minimal Rust interface around libclamav. This package is not supposed to be used stand-alone, but only through its safe wrapper, clamav-rs.

+

Get clamav-sys

+

rust-clamav | libclamav

+

Like clamav-rs. rust-clamav is a safe library for interacting with libclamav from Rust. The low-level C API is wrapped in idomatic and safe Rust code.

+

Get rust-clamav

+

Perl

+

File::Scan::ClamAV | clamd

+

A Perl module for interacting with ClamD. File::Scan::ClamAV will connect to a local Clam Anti-Virus clamd service and send commands.

+

Get File::Scan::ClamAV

+

Mail::ClamAV | libclamav

+

Perl extension for the ClamAV virus scanner.

+

Get Mail::ClamAV

+

Ruby

+

Clamby | clamscan + freshclam

+

Ruby binding for scanning file uploads using ClamScan. If you have a file upload on your site and you do not scan the files for viruses then you not only compromise your software, but also the users of the software and their files. This gem's function is to simply scan a given file.

+

Get Clamby

+

ClamAV::Client | clamd

+

ClamAV::Client is a client library that can talk to the clam daemon.

+

Get ClamAV::Client

+

PHP

+

PHP ClamAV | clamd

+

PHP Client to connect to ClamAV daemon over TCP or using a local socket from command line and scan your storage files for viruses.

+

Get PHP ClamAV

+

PHP ClamAV Scan | clamd

+

A simple PHP class for scanning files using a LOCAL ClamAV/clamd install either via a socket file or network socket (windows). Can either be used on its own or dropped into a Codeigniter app as a library. The main reason this was created was because the legacy php-clamav module is not compatible with PHP 7 and all other options I found were either not drop in compatible with CodeIgniter or were designed for use with Composer.

+

Get PHP ClamAV

+

Python

+

clamd | clamd

+

clamd is a portable Python module to use the ClamAV anti-virus engine on Windows, Linux, MacOSX and other platforms. It requires a running instance of the clamd daemon.

+

This is a fork of pyClamd v0.2.0 created by Philippe Lagadec and published on his website: http://www.decalage.info/en/python/pyclamd which in turn is a slightly improved version of pyClamd v0.1.1 created by Alexandre Norman and published on his website: http://xael.org/norman/python/pyclamd/

+

Get clamd

+

Python ClamAV | libclamav

+

Python wrapper for libclamav using ctypes. Python ClamAV is a part of the ClamWin project.

+

Get Python ClamAV

+

pyClamd | clamd

+

Add virus detection capabilities to your python software in an efficient and easy way.

+

Get pyClamd

+

Java

+

clamav-java

+

Simple ClamAV Java client. See also ClamAV REST service which builds on top of this.

+

Get clamav-java

+

Miscellaneous Tools

+

IPCop | ClamAV

+

IPCop Linux is a complete Linux Distribution whose sole purpose is to protect the networks it is installed on. ClamAV is included.

+

Get IPCop

+

Endian Firewall | ClamAV

+

Endian Firewall Community (EFW) is a turn-key Linux security distribution that can transform any bare-metal appliance into a full-featured Unified Threat Management solution. Endian is designed to be the easiest security product to install, configure and use!

+

Get Endian Firewall

+

ClamTK | ClamAV

+

ClamTk is a GUI front-end for ClamAV using gtk2-perl. It is designed to be an easy-to-use, on-demand scanner for Linux systems. ClamTk has been ported to Fedora, Debian, RedHat, openSUSE, ALT Linux, Ubuntu, CentOS, Gentoo, Archlinux, Mandriva, PCLinuxOS, FreeBSD, and others.

+

Get ClamTK

+

ClamWin | ClamAV

+

ClamWin is a Free Antivirus program for Microsoft Windows 10 / 8 / 7 / Vista / XP / Me / 2000 / 98 and Windows Server 2012, 2008 and 2003.

+

Get ClamWin

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Installing/Docker.html clamav-0.103.5+dfsg/docs/html/manual/Installing/Docker.html --- clamav-0.103.2+dfsg/docs/html/manual/Installing/Docker.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Installing/Docker.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,449 @@ + + + + + + Docker - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

ClamAV in Docker

+

ClamAV can be run within a Docker container. This provides isolation from other processes by running it in a containerized environment. If new or unfamiliar with Docker, containers or cgroups see docker.com.

+

The official images on Docker Hub

+

ClamAV image tags on Docker Hub follow this naming convention:

+
    +
  • +

    clamav/clamav:<version>: A release preloaded with signature databases.

    +

    Using this container will save the ClamAV project some bandwidth. Use this if you will keep the image around so that you don't download the entire database set every time you start a new container. Updating with FreshClam from existing databases set does not use much data.

    +
  • +
  • +

    clamav/clamav:<version>_base: A release with no signature databases.

    +

    Use this container only if you mount a volume in your container under /var/lib/clamav to persist your signature database databases. This method is the best option because it will reduce data costs for ClamAV and for the Docker registry, but it does require advanced familiarity with Linux and Docker.

    +
    +

    Caution: Using this image without mounting an existing database directory will cause FreshClam to download the entire database set each time you start a new container.

    +
    +
  • +
+

You can use the unstable version (i.e. clamav/clamav:unstable or +clamav/clamav:unstable_base) to try the latest from our development branch.

+

Building the ClamAV image

+

While it is recommended to pull the image from our Docker Hub registry, some may want to build the image locally instead. All that is needed is:

+
docker build --tag "clamav:TICKET-123" .
+
+

in the current directory. This will build the ClamAV image and tag it with the name "clamav:TICKET-123". Any name can generally be used and it is this name that needs to be referred to later when running the image.

+

Running ClamD

+

To run clamd in a Docker container, first, an image either has to be built or pulled from a Docker registry.

+

Running ClamD using the official ClamAV images from Docker Hub

+

To pull the ClamAV "unstable" image from Docker Hub, run:

+
docker pull clamav/clamav:unstable
+
+
+

Tip: Substitute unstable with a different version as needed.

+
+

To pull and run the official ClamAV images from the Docker Hub registry, try the following command:

+
docker run \
+    --interactive \
+    --tty \
+    --rm \
+    --name "clam_container_01" \
+    clamav/clamav:unstable
+
+

The above creates an interactive container with the current TTY connected to it. This is optional but useful when getting started as it allows one to directly see the output and, in the case of clamd, send ctrl-c to close the container. The --rm parameter ensures the container is cleaned up again after it exits and the --name parameter names the container, so it can be referenced through other (Docker) commands, as several containers of the same image can be started without conflicts.

+
+

Note: Pulling is not always required. docker run will pull the image if it cannot be found locally. docker run --pull always will always pull beforehand to ensure the most up-to-date container is being used. Do not use --pull always with the larger ClamAV images.

+
+
+

Tip: It's common to see -it instead of --interactive --tty.

+
+

Running ClamD using a Locally Built Image

+

You can run a container using an image built locally (see "Building the ClamAV Image"). Just run:

+
docker run -it --rm \
+    --name "clam_container_01" \
+    clamav:TICKET-123
+
+

Persisting the virus database (volume)

+

The virus database in /var/lib/clamav is by default unique to each container and thus is normally not shared. For simple setups this is fine, where only one instance of clamd is expected to run in a dockerized environment. However some use cases may want to efficiently share the database or at least persist it across short-lived ClamAV containers.

+

To do so, you have two options:

+
    +
  1. +

    Create a Docker volume using the +docker volume command. +Volumes are completely managed by Docker and are the best choice for creating a persistent database volume.

    +

    For example, create a "clam_db" volume:

    +
    docker volume create clam_db
    +
    +

    Then start one or more containers using this volume. The first container to use a new database volume will download the full database set. Subsequent containers will use the existing databases and may update them as needed:

    +
    docker run -it --rm \
    +    --name "clam_container_01" \
    +    --mount source=clam_db,target=/var/lib/clamav \
    +    clamav/clamav:unstable_base
    +
    +
  2. +
  3. +

    Create a Bind Mount that maps a file system directory to a path within the container. Bind Mounts depend on the directory structure, permissions, and operating system of the Docker host machine.

    +

    Run the container with these arguments to mount the a directory from your host environment as a volume in the container.

    +
        --mount type=bind,source=/path/to/databases,target=/var/lib/clamav
    +
    +

    When doing this, it's best to use the <version>_base image tags so as to save on bandwith. E.g.:

    +
    docker run -it --rm \
    +    --name "clam_container_01" \
    +    --mount type=bind,source=/path/to/databases,target=/var/lib/clamav \
    +    clamav/clamav:unstable_base
    +
    +
    +

    Disclaimer: When using a Bind Mount, the container's entrypoint script will change ownership of this directory to its "clamav" user. This enables FreshClam and ClamD with the required permissions to read and write to the directory, though these changes will also affect those files on the host.

    +
    +
  4. +
+

If you're thinking about running multiple containers that share a single database volume, here are some notes on how this might work.

+

Running Clam(D)Scan

+

Scanning files using clamscan or clamdscan is possible in various ways with Docker. This section briefly describes them, but the other sections of this document are best read before hand to better understand some of the concepts.

+

One important aspect is however to realize that Docker by default does not have access to any of the hosts files. And so to scan these within Docker, they need to be mounted with a bind mount to be made accessible.

+

For example, running the container with these arguments ...

+
    --mount type=bind,source=/path/to/scan,target=/scandir
+    --mount type=bind,source=/path/to/scan,target=/scandir
+
+

... would make the hosts file/directory /path/to/scan available in the container as /scandir and thus invoking clamscan would thus be done on /scandir.

+

Note that while technically possible to run either scanners via docker exec this is not described as it is unlikely the container has access to the files to be scanned.

+

ClamScan

+

Using clamscan outside of the Docker container is how normally clamscan is invoked. To make use of the available shared dockerized resources however, it is possible to expose the virus database and share that for example. E.g. it could be possible to run a Docker container with only the freshclam daemon running, and share the virus database directory /var/lib/clamav. This could be useful for file servers for example, where only clamscan is installed on the host, and freshclam is managed in a Docker container.

+
+

Note: Running the freshclam daemon separated from clamd is less recommended, unless the clamd socket is shared with freshclam as freshclam would not be able to inform clamd of database updates.

+
+

Dockerized ClamScan

+

To run clamscan in a Docker container, the Docker container can be invoked as:

+
docker run -it --rm \
+    --mount type=bind,source=/path/to/scan,target=/scandir \
+    clamav/clamav:unstable \
+    clamscan /scandir
+
+

However, this will use whatever signatures are found in the image, which may be slightly out of date. If using clamscan in this way, it would be best to use a database volume that is up-to-date so that you scan with the latest signatures. E.g.:

+
docker run -it --rm \
+    --mount type=bind,source=/path/to/scan,target=/scandir \
+    --mount type=bind,source=/path/to/databases,target=/var/lib/clamav \
+    clamav/clamav:unstable_base \
+    clamscan /scandir
+
+

ClamDScan

+

As with clamscan, clamdscan can also be run when installed on the host, by connecting to the dockerized clamd. This can be done by either pointing clamdscan to the exposed TCP/UDP port or unix socket.

+

Dockerized ClamDScan

+

Running both clamd and clamdscan is also easily possible, as all that is needed is the shared socket between the two containers. The only cavaet here is to:

+
    +
  1. mount the files to be scanned in the container that will run clamd, or
  2. +
  3. mount the files to be scanned in the container that will clamdscan run if using clamdscan --stream. The --stream option will be slower, but enables submitting files from a different machine on a network.
  4. +
+

For example:

+
docker run -it --rm \
+    --mount type=bind,source=/path/to/scan,target=/scandir \
+    --mount type=bind,source=/var/lib/docker/data/clamav/sockets/,target=/run/clamav/ \
+    clamav/clamav:unstable
+
+
docker run -it --rm \
+    --mount type=bind,source=/path/to/scan,target=/scandir \
+    --mount type=bind,source=/var/lib/docker/data/clamav/sockets/,target=/run/clamav/ \
+    clamav/clamav:unstable_base \
+    clamdscan /scandir
+
+

Controlling the container

+

The ClamAV container actually runs both freshclam and clamd daemons by default. Optionally available to the container is ClamAV's milter daemon. To control the behavior of the services started within the container, the following flags can be passed to the docker run command with the --env (-e) parameter.

+
    +
  • CLAMAV_NO_CLAMD [true|false] Do not start clamd. +(default: start clamd)
  • +
  • CLAMAV_NO_FRESHCLAMD [true|false] Do not start the freshclam daemon. +(default: start the freshclam daemon)
  • +
  • CLAMAV_NO_MILTERD [true|false] Do not start the clamav-milter daemon. +(default: start the clamav-milter daemon )
  • +
  • CLAMD_STARTUP_TIMEOUT [integer] Seconds to wait for clamd to start. +(default: 1800)
  • +
  • FRESHCLAM_CHECKS [integer] freshclam daily update frequency. +(default: once per day)
  • +
+

So to additionally also enable clamav-milter, the following flag can be added:

+
    --env 'CLAMAV_NO_MILTERD=false'
+
+

Further more, all of the configuration files that live in /etc/clamav can be overridden by doing a volume-mount to the specific file. The following argument can be added for this purpose. The example uses the entire configuration directory, but this can be supplied multiple times if individual files deem to be replaced.

+
    --mount type=bind,source=/full/path/to/clamav/,target/etc/clamav
+
+
+

Note: Even when disabling the freshclam daemon, freshclam will always run at least once during container startup if there is no virus database. While not recommended, the virus database location itself /var/lib/clamav/ could be a persistent Docker volume. This however is slightly more advanced and out of scope of this document.

+
+

Connecting to the container

+

Executing commands within a running container

+

To connect to a running ClamAV container, docker exec can be used to run a command on an already running container. To do so, the name needs to be either obtained from docker ps or supplied during container start via the --name parameter. The most interesting command in this case can be clamdtop.

+
docker exec --interactive --tty "clamav_container_01" clamdtop
+
+

Alternatively, a shell can be started to inspect and run commands within the +container as well.

+
docker exec --interactive --tty "clamav_container_01" /bin/sh
+
+

Unix sockets

+

The default socket for clamd is located inside the container as /run/clamav/clamd.sock and can be connected to when exposed via a Docker volume mount. To ensure, that clamd within the container can freely create and remove the socket, the path for the socket is to be volume-mounted, to expose it for others on the same host to use. The following volume can be used for this purpose. Do ensure that the directory on the host actually exists and clamav inside the container has permission to access it. Caution is required when managing permissions, as incorrect permission could open clamd for anyone on the host system.

+
    --mount type=bind,source=/var/lib/docker/data/clamav/sockets/,target=/run/clamav/
+
+

With the socket exposed to the host, any other service can now talk to clamd as well. If for example clamdtop where installed on the local host, calling

+
clamdtop "/var/lib/docker/data/clamav/sockets/clamd.sock"
+
+

should work just fine. Likewise, running clamdtop in a different container, but sharing the socket will equally work. While clamdtop works well as an example here, it is of course important to realize, this can also be used to connect a mail server to clamd.

+

TCP

+

ClamAV in the official Docker images is configured to listen for TCP connections on these ports:

+
    +
  • clamd: 3310
  • +
  • clamav-milter: 7357
  • +
+

While clamd and clamav-milter will listen on the above TCP ports, Docker does not expose these by default to the host. Only within containers can these ports be accessed. To expose, or "publish", these ports to the host, and thus potentially over the (inter)network, the --publish (or --publish-all) flag to docker run can be used. While more advanced/secure mappings can be done as per documentation, the basic way is to --publish [<host_port>:]<container_port> to make the port available to the host.

+
    --publish 13310:3310 \
+    --publish 7357
+
+

The above would thus publish:

+
    +
  • clamd port 3310 as 13310 on the host
  • +
  • milter port 7357 as a random to the host. The random port can be inspected via docker ps.
  • +
+

But if you're just running one ClamAV container, you probably will just want to use the default port numbers, which are the same port numbers suggested in the clamd.conf.sample file provided with ClamAV:

+
    --publish 3310:3310 \
+    --publish 7357:7357
+
+
+

Warning: Extreme caution is to be taken when using clamd over TCP as there are no protections on that level. All traffic is un-encrypted. Extra care is to be taken when using TCP communications.

+
+

Container ClamD health-check

+

Docker has the ability to run simple ping checks on services running inside containers. If clamd is running inside the container, Docker will on occasion send a ping to clamd on the default port and wait for the pong from clamd. If clamd fails to respond, Docker will treat this as an error. The healthcheck results can be viewed with docker inspect.

+

Performance

+

The performance impact of running clamd in Docker is negligible. Docker is in essence just a wrapper around Linux's cgroups and cgroups can be thought of as chroot or FreeBSD's jail. All code is executed on the host without any translation. Docker does however do some isolation (through cgroups) to isolate the various systems somewhat.

+

Of course, nothing in life is free, and so there is some overhead. Disk-space being the most prominent one. The Docker container might have some duplication of files for example between the host and the container. Further more, also RAM memory may be duplicated for each instance, as there is no RAM-deduplication. Both of which can be solved on the host however. A filesystem that supports disk-deduplication and a memory manager that does RAM-deduplication.

+

The base container in itself is already very small ~16 MiB, at the time of thiswriting, this cost is still very tiny, where the advantages are very much worththe cost in general.

+

The container including the virus database is about ~240 MiB at the time of this writing.

+

Bandwidth

+

Please, be kind when using 'free' bandwidth, both for the virus databases but also the Docker registry. Try not to download the entire database set or the larger ClamAV database images on a regular basis.

+

Advanced container configurations

+

Multiple containers sharing the same mounted databases

+

You can run multiple containers that share the same database volume, but be aware that the FreshClam daemons on each would compete to update the databases. Most likely, one would update the databases and trigger its ClamD to load the new databases, while the others would be oblivious to the new databases and would continue with the old signatures until the next ClamD self-check.

+

This is fine, honestly. It won't take that long before the new signatures are detected by ClamD's self-check and the databases are reloaded automatically.

+

To reload the databases on all ClamD containers immediately after an update, you could disable the FreshClam daemon when you start the containers. Later, use docker exec to perform an update and again as needed to have ClamD load updated databases.

+
+

Note: This really isn't necessary but you could do this if you wish.

+
+

Exactly how you orchestrate this will depend on your environment. You might do something along these lines:

+
    +
  1. +

    Create a "clam_db" volume, if you don't already have one:

    +
    docker volume create clam_db
    +
    +
  2. +
  3. +

    Start your containers:

    +
    docker run -it --rm \
    +    --name "clam_container_01" \
    +    --mount source=clam_db,target=/var/lib/clamav \
    +    --env 'CLAMAV_NO_FRESHCLAMD=true' \
    +    clamav/clamav:unstable_base
    +
    +

    Wait for the first one to download the databases (if it's a new database volume). Then start more:

    +
    docker run -it --rm \
    +    --name "clam_container_02" \
    +    --mount source=clam_db,target=/var/lib/clamav \
    +    --env 'CLAMAV_NO_FRESHCLAMD=true' \
    +    clamav/clamav:unstable_base
    +
    +
  4. +
  5. +

    Check for updates, as needed:

    +
    docker exec -it clam_container_01 freshclam --on-update-execute=EXIT_1 || \
    +if [ $? == 1 ]; then \
    +    docker exec -it clam_container_01 clamdscan --reload; \
    +    docker exec -it clam_container_02 clamdscan --reload; \
    +fi
    +
    +
  6. +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Installing/Installing-from-source-Unix.html clamav-0.103.5+dfsg/docs/html/manual/Installing/Installing-from-source-Unix.html --- clamav-0.103.2+dfsg/docs/html/manual/Installing/Installing-from-source-Unix.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Installing/Installing-from-source-Unix.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,465 @@ + + + + + + Unix from source (v0.104+) - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Building ClamAV with CMake (v0.104 and newer)

+

The following are instructions to build ClamAV version 0.104 and newer using CMake.

+
+

Tip: If you wish to build ClamAV version 0.103 or older from source, follow these instructions to build ClamAV using Autotools.

+
+ +
+

Note: Some of the dependencies are optional if you elect to not build all of the command line applications, or elect to only build the libclamav library. Specifically:

+
    +
  • libcurl: required for libfreshclam, freshclam, clamsubmit, clamonacc
  • +
  • ncurses: required for clamdtop
  • +
+

For more information about customized builds and which dependencies can be skipped, please see the INSTALL.md document accompanying the source code.

+
+

Install prerequisites

+
+

Note: Many of the instructions below rely on Python 3's Pip package manager to install CMake. This is because many distributions do not provide a new enough version of CMake required to build ClamAV.

+
+
+

Tip: The Python 3 pytest package is recommended in the instructions below in case the unit tests fail so that the test output is easy to read. You're welcome to skip it. However, if you have Python 2's pytest installed but not Python 3's pytest, the tests may fail to run.

+
+

Alpine

+

As root or with sudo, run:

+
apk update && apk add \
+  `# install tools` \
+  g++ gcc gdb make cmake py3-pytest python3 valgrind \
+  `# install clamav dependencies` \
+  bzip2-dev check-dev curl-dev json-c-dev libmilter-dev libxml2-dev \
+  linux-headers ncurses-dev openssl-dev pcre2-dev zlib-dev
+
+

Redhat / Centos / Fedora

+

For Centos 8, you will probably need to run this to enable EPEL & PowerTools. +As root or with sudo, run:

+
dnf install -y epel-release
+dnf install -y dnf-plugins-core
+dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
+dnf config-manager --set-enabled PowerTools | \
+  dnf config-manager --set-enabled powertools | true
+
+

As root or with sudo, run:

+
dnf install -y \
+  `# install tools` \
+  gcc gcc-c++ make python3 python3-pip valgrind \
+  `# install clamav dependencies` \
+  bzip2-devel check-devel json-c-devel libcurl-devel libxml2-devel \
+  ncurses-devel openssl-devel pcre2-devel sendmail-devel zlib-devel
+
+
+

Note: If you get dnf: command not found, use yum instead.

+
+

As a regular user, run:

+
python3 -m pip install --user cmake pytest
+
+
+

Tip: If you don't have a user account, e.g. in a Docker container, run:

+
python3 -m pip install cmake pytest
+
+
+

SUSE / openSUSE

+

As root or with sudo, run:

+
zypper install -y \
+  `# install tools` \
+  gcc gcc-c++ make python3 python3-pip valgrind \
+  `# install clamav dependencies` \
+  libbz2-devel check-devel libjson-c-devel libcurl-devel libxml2-devel \
+  ncurses-devel libopenssl-devel pcre2-devel sendmail-devel zlib-devel
+
+

As a regular user, run:

+
python3 -m pip install --user cmake pytest
+
+
+

Tip: If you don't have a user account, e.g. in a Docker container, run:

+
python3 -m pip install cmake pytest
+
+
+

Ubuntu / Debian

+

As root or with sudo, run:

+
apt-get update && apt-get install -y \
+  `# install tools` \
+  gcc make pkg-config python3 python3-pip python3-pytest valgrind \
+  `# install clamav dependencies` \
+  check libbz2-dev libcurl4-openssl-dev libjson-c-dev libmilter-dev \
+  libncurses5-dev libpcre2-dev libssl-dev libxml2-dev zlib1g-dev
+
+

As a regular user, run:

+
python3 -m pip install --user cmake
+
+
+

Tip: If you don't have a user account, e.g. in a Docker container, run:

+
python3 -m pip install cmake
+
+
+

macOS

+

The following instructions require you to install HomeBrew to install tools and library dependencies.

+
brew update
+
+packages=(
+  # install tools
+  python3 cmake
+  # install clamav dependencies
+  bzip2 check curl-openssl json-c libxml2 ncurses openssl@1.1 pcre2 zlib
+)
+for item in "${packages[@]}"; do
+  brew install $item || true; brew upgrade $item || brew upgrade $item
+done
+
+python3 -m pip install --user cmake pytest
+
+
+

Note: You may also need to install pkg-config if not already present on your system. You can use Homebrew to do this with: brew install pkg-config

+
+

FreeBSD

+

As root or with sudo, run:

+
pkg install -y \
+  `# install tools` \
+  gmake cmake pkgconf py38-pip python38 \
+  `# install clamav dependencies` \
+  bzip2 check curl json-c libmilter libxml2 ncurses pcre2
+
+

Now as a regular user, run:

+
python3.8 -m pip install --user pytest
+
+
+

Tip: If you don't have a user account, e.g. in a Docker container, run:

+
python3 -m pip install pytest
+
+
+

Adding new system user and group

+

If installing to the system, and if you intend to run freshclam or clamd as as service, you should create a service account before compiling and installing ClamAV.

+

Follow these steps to create a service account.

+

Download the source code

+

Download the source from the clamav.net downloads page.

+

Extract the archive:

+
tar xzf clamav-[ver].tar.gz
+cd clamav-[ver]
+
+

Build ClamAV

+

First, make a "build" subdirectory. This will enable you to easily delete your build files if something goes wrong and you need to re-configure and try again.

+
mkdir build && cd build
+
+

Next, select the build options you desire. For a full list of configuration options, see the "Custom CMake options" section in the INSTALL.md file included with the source code.

+

To help you get started, here are some popular build configurations.

+

The Default Build

+

The default build type is RelWithDebInfo, that is "Release mode with Debugging symbols". It will install to /usr/local.

+
cmake ..
+cmake --build .
+ctest
+sudo cmake --build . --target install
+
+
+

Tip: If building for macOS, you may need to override the system provided LibreSSL with the OpenSSL you installed using Homebrew. +For example:

+
cmake .. \
+  -D CMAKE_INSTALL_PREFIX=/usr/local/clamav                                    \
+  -D OPTIMIZE=OFF                                                              \
+  -D OPENSSL_ROOT_DIR=/usr/local/opt/openssl@1.1/                              \
+  -D OPENSSL_CRYPTO_LIBRARY=/usr/local/opt/openssl@1.1/lib/libcrypto.1.1.dylib \
+  -D OPENSSL_SSL_LIBRARY=/usr/local/opt/openssl@1.1/lib/libssl.1.1.dylib
+make
+sudo make install
+
+
+

A Linux Distribution-style Build

+

This build type mimics the layout you may be familiar with if installing a ClamAV package on Debian, Ubuntu, Alpine, and some other distributions:

+
cmake .. \
+    -D CMAKE_INSTALL_PREFIX=/usr \
+    -D CMAKE_INSTALL_LIBDIR=lib \
+    -D APP_CONFIG_DIRECTORY=/etc/clamav \
+    -D DATABASE_DIRECTORY=/var/lib/clamav \
+    -D ENABLE_JSON_SHARED=OFF
+cmake --build .
+ctest
+sudo cmake --build . --target install
+
+

Using the above example:

+
    +
  • +

    CMAKE_INSTALL_PREFIX - The install "prefix" will be /usr.

    +
  • +
  • +

    CMAKE_INSTALL_LIBDIR - The library directory will be lib (i.e. /usr/lib).

    +

    This may be the default anyways, but you may want to specify if CMake tries to install to lib64 and if lib64 is not desired.

    +
  • +
  • +

    APP_CONFIG_DIRECTORY - The config directory will be /etc/clamav.

    +

    Note: This absolute path is non-portable.

    +
  • +
  • +

    DATABASE_DIRECTORY - The database directory will be /var/lib/clamav.

    +

    Note: This absolute path is non-portable.

    +
  • +
+
+

Tip: Setting ENABLE_JSON_SHARED=OFF is preferred, but it will require json-c version 0.15 or newer unless you build json-c yourself with custom options. If json-c 0.15+ is not available to you, you may omit the option and just use the json-c shared library. But be warned that downstream applications which use libclamav.so may crash if they also use a different JSON library.

+
+

Some other popular configuration options include:

+
    +
  • +

    CMAKE_INSTALL_DOCDIR - Specify exact documentation subdirectory, relative to the install prefix. The default may vary depending on your system and how you install CMake.

    +

    E.g., -D CMAKE_INSTALL_DOCDIR=share/doc/packages/clamav

    +
  • +
  • +

    CMAKE_SKIP_RPATH - If enabled, no RPATH is built into anything. This may be required when building packages for some Linux distributions. See the CMake wiki for more detail about CMake's RPATH handling.

    +

    E.g., -D CMAKE_SKIP_RPATH=ON

    +
  • +
+

Please see the CMake documentation for more instructions on how to customize the install paths.

+

A Build for Development

+

This suggested development configuration generates a Ninja-based build system instead of the default Makefile-based build system. Ninja is faster than Make, but you will have to install "ninja" (or "ninja-build"). With the following commands, ClamAV will be compiled in Debug mode with optimizations disabled. It will install to an "install" subdirectory and SystemD integration is disabled so that sudo is not required for the install and SystemD unit files are not installed to the system. This build also enables building a static libclamav.a library as well as building the example applications.

+
cmake .. -G Ninja \
+    -D CMAKE_BUILD_TYPE=Debug \
+    -D OPTIMIZE=OFF \
+    -D CMAKE_INSTALL_PREFIX=`pwd`/install \
+    -D ENABLE_EXAMPLES=ON \
+    -D ENABLE_STATIC_LIB=ON \
+    -D ENABLE_SYSTEMD=OFF
+cmake --build .
+ctest --verbose
+cmake --build . --target install
+
+

You can find additional instructions in our Development chapter.

+

About the tests

+

ClamAV's public test suite is run using ctest. On Linux systems, our build system will detect if you have Valgrind. If installed, each test will run a second time using Valgrind to check for leaks.

+

If a test fails, please report the issue on GitHub. You will find .log files for each of the tests in the build/unit_tests directory. The output from ctest --verbose may give us enough information, but if not it could be helpful to zip up the .log files and attach them to the ticket.

+

Un-install

+

CMake doesn't provide a simple command to uninstall. However, CMake does build an install_manifest.txt file when you do the install. You can use the manifest to remove the installed files.

+

You will find the manifest in the directory where you compiled ClamAV. If you followed the recommendations (above), then you will find it at <clamav source directory>/build/install_manifest.txt.

+

Feel free to inspect the file so you're comfortable knowing what you're about to delete.

+

Open a terminal and cd to that <clamav source directory>/build directory. Then run:

+
xargs rm < install_manifest.txt
+
+

This will leave behind the directories, and will leave behind any files added after install including the signature databases and any config files. You will have to delete these extra files yourself.

+
+

Tip: You may need to use sudo, depending on where you installed to.

+
+

What now?

+

Now that ClamAV is installed, you will want to customize your configuration and perhaps set up some scanning automation and alerting mechanisms.

+

Continue on to "Configuration"...

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Installing/Installing-from-source-Unix-old.html clamav-0.103.5+dfsg/docs/html/manual/Installing/Installing-from-source-Unix-old.html --- clamav-0.103.2+dfsg/docs/html/manual/Installing/Installing-from-source-Unix-old.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Installing/Installing-from-source-Unix-old.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,372 @@ + + + + + + Unix from source (v0.103-) - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Building ClamAV with Autotools (v0.103 and older)

+

The following are instructions to build ClamAV version 0.103 and older using Autotools.

+ +
+

Note: Some of the dependencies are optional if you elect to not build all of the command line applications, or elect to only build the libclamav library. Specifically:

+
    +
  • libcurl: required for libfreshclam, freshclam, clamsubmit, clamonacc
  • +
  • json-c: required for clamsubmit, optional for libclamav
  • +
  • ncurses: required for clamdtop
  • +
+
+

Install prerequisites

+

Alpine

+

As root or with sudo, run:

+
apk update && apk add \
+  `# install tools` \
+  g++ gcc gdb make valgrind \
+  `# install clamav dependencies` \
+  bzip2-dev check-dev curl-dev json-c-dev libmilter-dev libxml2-dev \
+  linux-headers ncurses-dev openssl-dev pcre2-dev zlib-dev
+
+

Redhat / Centos / Fedora

+

For Centos 8, you will probably need to run this to enable EPEL & PowerTools. +As root or with sudo, run:

+
dnf install -y epel-release
+dnf install -y dnf-plugins-core
+dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
+dnf config-manager --set-enabled PowerTools | \
+  dnf config-manager --set-enabled powertools | true
+
+

As root or with sudo, run:

+
dnf install -y \
+  `# install tools` \
+  gcc gcc-c++ make valgrind \
+  `# install clamav dependencies` \
+  bzip2-devel check-devel json-c-devel libcurl-devel libxml2-devel \
+  ncurses-devel openssl-devel pcre2-devel sendmail-devel zlib-devel
+
+
+

Note: If you get dnf: command not found, use yum instead.

+
+
+

Tip: You need to run autogen.sh if you're not building from a release tarball from clamav.net. If so, visit the developer section to find out what packages are required to run autogen.sh

+
+

Ubuntu / Debian

+

As root or with sudo, run:

+
apt-get update && apt-get install -y \
+  `# install tools` \
+  gcc make pkg-config valgrind \
+  `# install clamav dependencies` \
+  check libbz2-dev libcurl4-openssl-dev libjson-c-dev libmilter-dev \
+  libncurses5-dev libpcre2-dev libssl-dev libxml2-dev zlib1g-dev
+
+
+

Tip: You need to run autogen.sh if you're not building from a release tarball from clamav.net. If so, visit the developer section to find out what packages are required to run autogen.sh

+
+

macOS

+

The following instructions require you to install HomeBrew to install tools and library dependencies.

+
# Install XCode's Command Line Tools
+xcode-select --install
+
+brew update
+
+packages=(
+  # install tools
+  autoconf automake m4
+  # install clamav dependencies
+  bzip2 check curl-openssl json-c libxml2 ncurses openssl@1.1 pcre2 zlib
+)
+for item in "${packages[@]}"; do
+  brew install $item || true; brew upgrade $item || brew upgrade $item
+done
+
+

FreeBSD

+

As root or with sudo, run:

+
pkg install -y \
+  `# install tools` \
+  gmake pkgconf \
+  `# install clamav dependencies` \
+  bzip2 check curl json-c libmilter libxml2 ncurses pcre2
+
+

Adding new system user and group

+

If installing to the system, and if you intend to run freshclam or clamd as as service, you should create a service account before compiling and installing ClamAV.

+

Follow these steps to create a service account.

+

Download the source code

+

Download the source from the clamav.net downloads page.

+

Extract the archive:

+
tar xzf clamav-[ver].tar.gz
+cd clamav-[ver]
+
+

Build ClamAV

+

First, make a "build" subdirectory. This will enable you to easily delete your build files if something goes wrong and you need to re-configure and try again.

+
mkdir build && cd build
+
+
+

Note: The instructions in this page assume you're building from our source clamav-[ver].tar.gz file. If you aren't, you may need to install extra build tools (autoconf, automake, m4, libtool, and pkg-config/pkgconfig/pkgconf) then run:

+
../autogen.sh
+
+
+

Next, select the build options you desire. For a full list of configuration options, run:

+
../configure --help
+
+

To help you get started, here are some popular build configurations.

+

The Default Build

+

The default build type is "RelWithDebInfo", that is "Release mode with Debugging symbols". It will install to /usr/local.

+
../configure
+make
+make check VG=1
+sudo make install
+
+

A Linux Distribution-style Build

+

This build type mimics the layout you may be familiar with if installing a ClamAV package on Debian, Ubuntu, Alpine, and some other distributions. This will be a "release build" (no debugging symbols, optimizations enabled) and will install to /usr. The config directory will be /etc/clamav and the database directory will be /var/lib/clamav.

+
../configure \
+    --prefix=/usr
+    --sysconfdir=/etc/clamav \
+    --with-dbdir=/var/lib/clamav \
+    --with-libjson-static=/path/to/libjson-c.a \
+    --enable-milter
+make
+make check VG=1
+sudo make install
+
+
+

Note: Setting ENABLE_JSON_SHARED=OFF is preferred, but it will require json-c version 0.15 or newer. If json-c 0.15+ is not available to you, you may omit the option and just use the json-c shared library. But be warned that downstream applications which use libclamav.so may crash if they also use a different JSON library.

+
+

A Build for Development

+

With the following commands, ClamAV will be compiled with debugging symbols and with optimizations disabled. It will install to an "install" subdirectory and SystemD integration is disabled so that sudo is not required for the install and SystemD unit files are not installed to the system.

+
CFLAGS="-Wall -Wextra -ggdb -O0" CXXFLAGS="-Wall -Wextra -ggdb -O0" ../configure \
+    --prefix=`pwd`/install \
+    --with-systemdsystemunitdir=no
+make -j12
+make check VG=1
+sudo make install
+
+

About the tests

+

ClamAV's public test suite is run using make check. On Linux systems, the VG=1 argument will enable extra tests that use Valgrind to check for leaks.

+

If a test fails, please report the issue on GitHub. You will find .log files generated by the tests in the build/unit_tests directory. The output from make check VG=1 may give us enough information, but if not it could be helpful to zip up the .log files and attach them to the ticket.

+

Un-install

+

Run make uninstall to remove the installed files.

+

This will leave behind the directories, and will leave behind any files added after install including the signature databases and any config files. You will have to delete these extra files yourself.

+
+

Tip: You may need to use sudo, depending on where you installed to.

+
+

What now?

+

Now that ClamAV is installed, you will want to customize your configuration and perhaps set up some scanning automation and alerting mechanisms.

+

Continue on to "Configuration"...

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Installing/Installing-from-source-Windows.html clamav-0.103.5+dfsg/docs/html/manual/Installing/Installing-from-source-Windows.html --- clamav-0.103.2+dfsg/docs/html/manual/Installing/Installing-from-source-Windows.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Installing/Installing-from-source-Windows.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,383 @@ + + + + + + Windows from source - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Installing ClamAV on Windows from Source

+

The following are instructions to build ClamAV version 0.104 and newer using CMake.

+
+

Tip: If you wish to build ClamAV from source in ClamAV version 0.103 and older, you'll have to use the Visual Studio solution, please see the Win32 ClamAV Build Instructions located in our source release materials on ClamAV.net and on GitHub.

+
+ +
+

Note: Some of the dependencies are optional if you elect to not build all of the command line applications, or elect to only build the libclamav library. Specifically:

+
    +
  • libcurl: required for libfreshclam, freshclam, clamsubmit
  • +
  • ncurses: required for clamdtop
  • +
+

For more information about customized builds and which dependencies can be skipped, please see the INSTALL.md document accompanying the source code.

+
+

Install prerequisites

+

The following commands for building on Windows are written for Powershell.

+

At a minimum you will need Visual Studio 2015 or newer, and CMake. If you want to build the installer, you'll also need WiX Toolset.

+

If you're using Chocolatey, you can install CMake and WiX simply like this:

+
choco install cmake wixtoolset
+
+

If you're using Mussels to build the library dependencies (see below), then you may also need to install Netwide Assembler (NASM) and ActivePerl. These are also simple to install using Chocolatey:

+
choco install nasm activeperl
+
+

Then open a new terminal so that CMake and WiX will be in your $PATH.

+

Building the library dependencies

+

There are two options for building and supplying the library dependencies. These are Mussels and vcpkg.

+

Mussels is an open source project developed in-house by the ClamAV team. It offers great flexibility for defining your own collections (cookbooks) of build instructions (recipes) instead of solely relying on a centralized repository of ports. And unlike vcpkg, Mussels does not implement CMake build tooling for projects that don't support CMake, but instead leverages whatever build system is provided by the project. This means that Mussels builds may require installing additional tools, like NMake and ActivePerl rather than simply requiring CMake. The advantage is that you'll be building those projects the same way that those developers intended, and that Mussels recipes are generally very light weight. Mussels has some sharp edges because it's a newer and much smaller project than vcpkg.

+

Vcpkg is an open source project developed by Microsoft and is heavily oriented towards CMake projects. Vcpkg offers a very large collection of "ports" for almost any project you may need to build. It is very easy to get started with vcpkg.

+

Mussels is the preferred tool to supply the library dependencies at least until such time as the vcpkg Debug-build libclamav unit test heap-corruption crash is resolved (see below).

+

Details for how to use Mussels and vcpkg will be provided with the build instructions (below), as the instructions differ significantly depending on which you choose.

+
+

Tip: Installing the Python 3 pytest package is also recommended in case the unit tests fail so that the test output is easy to read. You're welcome to skip it. However, if you have Python 2's pytest installed but not Python 3's pytest, the tests may fail to run.

+

You can install pytest by running:

+
python3 -m pip install --user pytest
+
+
+

Download the source code

+

Download the source from the clamav.net downloads page.

+

Extract the archive. You should be able to right click on it and extract it to a folder, then in that folder, do the same for the clamav-[ver].tar file.

+

The rest of the instructions will assume you've opened Powershell in the clamav source directory.

+

Build ClamAV

+

First, make a "build" subdirectory. This will enable you to easily delete your build files if something goes wrong and you need to re-configure and try again.

+
mkdir build && cd build
+
+

Building with Mussels

+

Building the library dependencies with Mussels

+

Much like vcpkg, Mussels can be used to automatically build the ClamAV library dependencies. Unlike vcpkg, Mussels does not provide a mechanism for CMake to automatically detect the +library paths.

+

To build the library dependencies with Mussels, use Python's pip package manager to install Mussels:

+
python3 -m pip install mussels
+
+
+

Important: Always run mussels or msl in a small sub-directory. Mussels will recursively search your current directory for YAML recipe files. In a large directory, such as your home directory, this may take a long time.

+
+

Update the Mussels cookbooks to get the latest build recipes and set the +clamav cookbook to be trusted:

+
msl update
+msl cookbook trust clamav
+
+

Use msl list if you wish to see the recipes provided by the clamav cookbook.

+

To build with Mussels, you may need to install a few extra tools required to build some of the libraries. These include NASM and ActivePerl. See install prerequisites, above.

+

Build the clamav_deps recipe to compile ClamAV's library dependencies. By default, Mussels will install them to ~\.mussels\install\<target>

+
msl build clamav_deps
+
+

If this worked, you should be ready to build ClamAV.

+
+

Tip: You can also build for 32-bit systems, using msl build clamav_deps -t x86.

+
+

Building ClamAV

+

To configure the project, run the following, substiting "Visual Studio 16 2019" with your Visual Studio version:

+
cmake ..  -G "Visual Studio 16 2019" -A x64 `
+  -D JSONC_INCLUDE_DIR="$home\.mussels\install\x64\include\json-c"         `
+  -D JSONC_LIBRARY="$home\.mussels\install\x64\lib\json-c.lib"             `
+  -D ENABLE_JSON_SHARED=OFF                                              `
+  -D BZIP2_INCLUDE_DIR="$home\.mussels\install\x64\include"                `
+  -D BZIP2_LIBRARY_RELEASE="$home\.mussels\install\x64\lib\libbz2.lib"     `
+  -D CURL_INCLUDE_DIR="$home\.mussels\install\x64\include"                 `
+  -D CURL_LIBRARY="$home\.mussels\install\x64\lib\libcurl_imp.lib"         `
+  -D OPENSSL_ROOT_DIR="$home\.mussels\install\x64"                         `
+  -D OPENSSL_INCLUDE_DIR="$home\.mussels\install\x64\include"              `
+  -D OPENSSL_CRYPTO_LIBRARY="$home\.mussels\install\x64\lib\libcrypto.lib" `
+  -D OPENSSL_SSL_LIBRARY="$home\.mussels\install\x64\lib\libssl.lib"       `
+  -D ZLIB_LIBRARY="$home\.mussels\install\x64\lib\libssl.lib"              `
+  -D LIBXML2_INCLUDE_DIR="$home\.mussels\install\x64\include"              `
+  -D LIBXML2_LIBRARY="$home\.mussels\install\x64\lib\libxml2.lib"          `
+  -D PCRE2_INCLUDE_DIR="$home\.mussels\install\x64\include"                `
+  -D PCRE2_LIBRARY="$home\.mussels\install\x64\lib\pcre2-8.lib"            `
+  -D CURSES_INCLUDE_DIR="$home\.mussels\install\x64\include"               `
+  -D CURSES_LIBRARY="$home\.mussels\install\x64\lib\pdcurses.lib"          `
+  -D PThreadW32_INCLUDE_DIR="$home\.mussels\install\x64\include"           `
+  -D PThreadW32_LIBRARY="$home\.mussels\install\x64\lib\pthreadVC2.lib"    `
+  -D ZLIB_INCLUDE_DIR="$home\.mussels\install\x64\include"                 `
+  -D ZLIB_LIBRARY="$home\.mussels\install\x64\lib\zlibstatic.lib"          `
+  -D LIBCHECK_INCLUDE_DIR="$home\.mussels\install\x64\include"             `
+  -D LIBCHECK_LIBRARY="$home\.mussels\install\x64\lib\checkDynamic.lib"    `
+  -D CMAKE_INSTALL_PREFIX="install"
+
+
+

Tip: You have to drop the -A x64 arguments if you're building for 32-bits (or specify -A win32) and substitute x64 with x86 in the library paths.

+
+

Now, go ahead and build the project:

+
cmake --build . --config RelWithDebInfo
+
+
+

Tip: If you're having include-path issues when building, try building with +detailed verbosity so you can verify that the paths are correct:

+
+
cmake --build . --config RelWithDebInfo -- /verbosity:detailed
+
+

You can run the test suite with ctest:

+
ctest -C RelWithDebInfo
+
+

And you can install to the install (set above) like this:

+
cmake --build . --config RelWithDebInfo --target install
+
+
+

Tip: For a full list of configuration options, see the "Custom CMake Config Options" section of the INSTALL.md file included with the source code.

+
+

Building with vcpkg

+

vcpkg can be used to build the ClamAV library dependencies automatically.

+

vcpkg integrates really well with CMake, enabling CMake to find your compiled libraries automatically, so you don't have to specify the include & library paths manually as you do when using Mussels.

+
+

DISCLAIMER: There is a known issue with the unit tests when building with vcpkg in Debug mode. When you run the libclamav unit tests (check_clamav), the program will crash and a popup will claim there was heap corruption. If > you use Task Manager to kill the check_clamav.exe process, the rest of the tests pass just fine. This issue does not occur when using Mussels to supply the library dependencies. Commenting out the following lines in readdb.c resolves the heap corruption crash when running check_clamav, but of course introduces a memory leak:

+
    if (engine->stats_data)
+        free(engine->stats_data);
+
+

If anyone has time to figure out the real cause of the vcpkg Debug-build crash in check_clamav, it would be greatly appreciated.

+
+

You'll need to install vcpkg. See the vcpkg README for installation instructions.

+

Once installed, set the variable $VCPKG_PATH to the location where you installed vcpkg:

+
$VCPKG_PATH="..." # Path to your vcpkg installation
+
+

By default, CMake and vcpkg build for 32-bit. If you want to build for 64-bit, set the VCPKG_DEFAULT_TRIPLET environment variable:

+
$env:VCPKG_DEFAULT_TRIPLET="x64-windows"
+
+

Next, use vcpkg to build the required library dependencies:

+
& "$VCPKG_PATH\vcpkg" install 'curl[openssl]' 'json-c' 'libxml2' 'pcre2' 'pthreads' 'zlib' 'pdcurses' 'bzip2' 'check'
+
+

Now configure the ClamAV build using the CMAKE_TOOLCHAIN_FILE variable which will enable CMake to automatically find the libraries we built with vcpkg.

+
cmake .. -A x64 `
+  -D CMAKE_TOOLCHAIN_FILE="$VCPKG_PATH\scripts\buildsystems\vcpkg.cmake" `
+  -D CMAKE_INSTALL_PREFIX="install"
+
+

Now, go ahead and build the project:

+
cmake --build . --config RelWithDebInfo
+
+

You can run the test suite with ctest:

+
ctest -C RelWithDebInfo
+
+

And you can install to the install directory (set above) like this:

+
cmake --build . --config RelWithDebInfo --target install
+
+

Build the Installer

+

To build the installer, you must have WIX Toolset installed. If you're using Chocolatey, you can install it simply with choco install wixtoolset and then open a new terminal so that WIX will be in your PATH.

+
cpack -C RelWithDebInfo
+
+

What now?

+

Now that ClamAV is installed, you will want to customize your configuration and perhaps set up some scanning automation and alerting mechanisms.

+

Continue on to "Configuration"...

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Installing/Packages.html clamav-0.103.5+dfsg/docs/html/manual/Installing/Packages.html --- clamav-0.103.2+dfsg/docs/html/manual/Installing/Packages.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Installing/Packages.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,412 @@ + + + + + + Packages - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

ClamAV Packages

+

Many Linux and Unix distributions offer one or more ClamAV packages to make it easy for you to install ClamAV.

+

These packages are usually well maintained but if you find an issue with one, please consider helping the volunteers that maintain the packages.

+
+

Disclaimer: ClamAV packages may vary somewhat from the upstream version. +Some examples:

+
    +
  • +

    The database and application config paths may vary:

    +
      +
    • +

      A default from-source install will go in /usr/local, with:

      +
        +
      • applications in /usr/local/bin
      • +
      • daemons in /usr/local/sbin
      • +
      • libraries in /usr/local/lib
      • +
      • headers in /usr/local/include
      • +
      • configs in /usr/local/etc/
      • +
      • databases in /usr/local/share/clamav/
      • +
      +
    • +
    • +

      A Linux package install will probably go in /usr, with:

      +
        +
      • applications in /usr/bin
      • +
      • daemons in /usr/sbin
      • +
      • libraries in /usr/lib
      • +
      • headers in /usr/include
      • +
      • configs in /etc/clamav
      • +
      • databases in /var/lib/clamav
      • +
      +
    • +
    +
  • +
  • +

    As of 0.103.x, a from-source install requires the user create a config for FreshClam, ClamD, and ClamAV-Milter in order to use each application. A package install, however, is likely to come pre-configured. Users may wish to modify the configs as needed.

    +
  • +
  • +

    Package installs sometimes carry extra patches for issues affecting their distribution, for issues the ClamAV developers haven't had time to fix or are unaware of, and for security issues when distributing older versions that are no longer maintained by the ClamAV developers.

    +
  • +
  • +

    Some distributions parcel up ClamAV components into separate packages. You don't necessarily need all of the packages. If this applies to your package system, you may need to review the applications described in the scanning instructions to understand which features you will need.

    +
  • +
+
+
+

Acknowledgments: Thank you to all of the volunteers who maintain these packages! We appreciate your help!

+
+

The Packages

+

Debian

+

Debian splits up ClamAV into a selection of different packages.

+

Realistically, you probably only need to apt install clamav and probably apt install clamav-daemon. If you require support for scanning compressed RAR files you first need to enable the "non-free" archive.*

+

The full list of packages includes:

+
    +
  • clamav - command-line interface
  • +
  • clamav-base - base package
  • +
  • clamav-daemon - scanner daemon
  • +
  • clamav-docs - documentation
  • +
  • clamav-freshclam - virus database update utility
  • +
  • clamav-milter - sendmail integration
  • +
  • clamav-testfiles - test files
  • +
  • libclamav-dev - development files
  • +
  • libclamav9 - library
  • +
  • libclamunrar9 - unrar support
  • +
+
+

* RAR Support: ClamAV's RAR support comes from UnRAR, which is open-source but not entirely free in so far as its license restricts users from reverse engineering it to create RAR archives. For this reason, it is bundled separately in the "non-free" archive. Enable it by adding "non-free" to /etc/apt/sources.list. Eg:

+

deb http://http.us.debian.org/debian stable main contrib non-free

+

Then you can install the RAR-plugin using: apt install libclamunrar9

+
+

There are a variety of other ClamAV related projects as well. Run apt search clamav to see a larger list.

+

To test the installation, you can try to scan the test files in the clamav-testfiles package.

+
+

Note: Debian packages are maintained by Debian's ClamAV Team.

+

The package maintainers can be reached at clamav-devel at lists.alith.debian.org. More info at tracker.debian.org/pkg/clamav.

+

Patches: https://salsa.debian.org/clamav-team/clamav/tree/unstable/debian/patches

+
+

Ubuntu

+

Ubuntu's ClamAV packages are derived from the Debian packages (above). See the Debian instructions for installation details.

+
+

RAR Support: As with Debian, RAR support is not included in the base package. Users that desire RAR support will have to install libclamunar9 separately. Unlike with Debian, there is no need to enable "non-free" packages for this to work.

+
+
+

Note: Ubuntu packages are curated by Ubuntu Developers. +Package source: https://packages.ubuntu.com/source/clamav

+
+

openSUSE

+

openSUSE provides two packages:

+
    +
  • clamav - The clamav package
  • +
  • clamav-devel - The clamav package plus headers for software development.
  • +
+

RPM download

+

Find these packages at under http://download.opensuse.org/repositories/security +Eg.:

+
    +
  • http://download.opensuse.org/repositories/security/openSUSE_Leap_15.3/x86_64/clamav-0.103.1-lp153.234.4.x86_64.rpm.mirrorlist
  • +
  • http://download.opensuse.org/repositories/security/openSUSE_Leap_15.3/x86_64/clamav-devel-0.103.1-lp153.234.4.x86_64.rpm.mirrorlist
  • +
+

Use the update variant for openSUSE, add it to your installation as another repository using YaST or zypper and give it a higher priority (lower number) than the repository that delivers the official updates.

+
+

Tip: RPMs of new ClamAV versions for existing SUSE products are provided through the respective online update channels. As these packages have to go through QA, it usually takes some time for a new ClamAV source release to appear as an official RPM. For those who want the newest version, packages are available from the security project in the openSUSE Build Service.

+
+

Zypper

+

Install ClamAV with zypper:

+
  zypper install -y clamav
+
+
+

Note: openSUSE packages are maintained by Reinhard Max.

+
+

EPEL: Fedora, RHEL, and CentOS

+

EPEL creates ClamAV packages for Fedora (as well as EPEL-enabled CentOS and RHEL). For more information on EPEL, visit their wiki.

+

To enable EPEL for CentOS:

+
dnf install -y epel-release
+
+

EPEL offers a selection of packages to install ClamAV:

+
    +
  • clamd - The Clam AntiVirus Daemon
  • +
  • clamav - End-user tools for the Clam Antivirus scanner
  • +
  • clamav-data - Virus signature data for the Clam Antivirus scanner
  • +
  • clamav-devel - Header files and libraries for the Clam Antivirus scanner
  • +
  • clamav-lib - Dynamic libraries for the Clam Antivirus scanner
  • +
  • clamav-milter - Milter module for the Clam Antivirus scanner
  • +
  • clamav-update - Auto-updater for the Clam Antivirus scanner data-files
  • +
+

Most users will only need to run:

+
dnf install -y clamav clamd clamav-update
+
+
+

Tips

+

CentOS: On Community Enterprise Operating System (CentOS) the ClamAV package requires the Extra Packages for Enterprise Linux (EPEL) repository.

+

RHEL: On RedHat Enterprise Linux (RHEL) the EPEL release package has to be installed either manually or through RHN.

+

Fedora: Fedora packages can be found at https://src.fedoraproject.org/rpms/clamav

+

Fedora's packaging is more customized than most. Please review the RPM notes when troubleshooting your Fedora package configuration.

+
+

Gentoo

+

ClamAV is available in portage under /usr/portage/app-antivirus/clamav

+

To install, run:

+
emerge clamav
+
+

For more details, see the package entry on Portage.

+

FreeBSD, OpenBSD, NetBSD

+

Although all these systems offer the possibility to use ports or pkgsrc, you can install the pre-built package:

+

FreeBSD

+

FreeBSD offers two ClamAV ports (packages):

+
    +
  • clamav
  • +
  • clamav-devel
  • +
+

To install, run:

+
pkg install clamav
+
+
+

Note: For more details, see:

+
    +
  • https://www.freshports.org/security/clamav
  • +
  • https://www.freshports.org/security/clamav-devel
  • +
+
+

OpenBSD

+

To install, run:

+
  pkg_add clamav
+
+

NetBSD

+

To install, run:

+
  pkgin install clamav
+
+

Solaris

+

OpenCSW is a community software project for Solaris 8+ on both Sparc and x86. It packages more than 2000 popular open source titles and they can all easily be installed with dependency handling via pkgutil which is modeled after Debian's apt-get.

+
pkgutil -i clamav
+
+
+

Note: The package can be found on OpenCSW though it is unfortuantely quite out-of-date.

+
+
+

Disclaimer: ClamAV is also no longer supported on Solaris because Solaris is proprietary, less commonly used, and difficult to work with. Future versions of ClamAV will depend on components written in the Rust programming language, which also does not support building directly on Solaris. It is likely that ClamAV will no longer work on Solaris in the future.

+
+

Slackware

+

You can download ClamAV builds for Slackware from https://slackbuilds.org/repository/14.2/system/clamav/

+

Download the package, and as root, install it like so (substituting the appropriate filename):

+
  installpkg clamav.tar.gz
+
+

macOS

+

ClamAV can be easily installed on macOS using one of these popular package managers:

+ +

Homebrew

+

Install Homebrew if you don't already have it. Then run:

+
  brew install clamav
+
+

Homebrew installs versioned packages to /usr/local/Cellar/<pacakge>/<version> with symlinks in /usr/local/opt/<pacakge> to the current version. Symlinks for ClamAV's executables will be placed in /usr/local/bin to add them to your PATH. ClamAV's config files will be placed in /usr/local/etc/clamav.

+

As with most other installation methods, you may need to do the following at a minimum before you can run freshclam, clamscan, or use clamdscan with clamd:

+
    +
  1. Create /usr/local/etc/clamav/freshclam.conf from /usr/local/etc/clamav/freshclam.conf.sample.
  2. +
  3. Remove or comment-out the Example line from freshclam.conf
  4. +
  5. Run freshclam to download the latest malware definitions.
  6. +
+

If you wish to run clamd you'll also need to create /usr/local/etc/clamav/clamd.conf from /usr/local/etc/clamav/clamd.conf.sample, and configure clamd.conf with Local/Unix socket settings (preferred), or TCP socket settings.

+

MacPorts

+

Install MacPorts if you don't already have it. Then run:

+
  sudo port install clamav
+
+

MacPorts installs versioned packages to /opt/local/. ClamAV's config files will be placed in /opt/local/etc.

+

As with most other installation methods, you may need to do the following at a minimum before you can run freshclam, clamscan, or use clamdscan with clamd:

+
    +
  1. Create /opt/local/etc/freshclam.conf from /opt/local/etc/freshclam.conf.sample.
  2. +
  3. Remove or comment-out the Example line from freshclam.conf
  4. +
  5. Run freshclam to download the latest malware definitions.
  6. +
+

If you wish to run clamd you'll also need to create /opt/local/etc/clamd.conf from /opt/local/etc/clamd.conf.sample, and configure clamd.conf with Local/Unix socket settings (preferred), or TCP socket settings.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Installing.html clamav-0.103.5+dfsg/docs/html/manual/Installing.html --- clamav-0.103.2+dfsg/docs/html/manual/Installing.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Installing.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,294 @@ + + + + + + Installing - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Installing ClamAV

+ +

Installing with a Package Manager

+

ClamAV is widely available from third party package managers for most operating systems. This is often the quickest way to install ClamAV. It will make also upgrades easier.

+

Check out the Packages page to find installation instructions for your system.

+

Installing with an Installer

+

Pre-compiled packages provided on the clamav.net downloads page have all external library dependencies statically compiled in.

+

These installers likely differ from packages provided by other packaging tools in that you will need to create and configure the freshclam.conf and clamd.conf files. You may also need to add a clamav service user account and adjust the permissions on the database directory. We hope to round out these sharp corners in the future and to make setup more convenient, but for now be advised that setup from one of these packages is a little bit more work than you may be used to.

+

If you're interested in learning how these packages were built, you can check out these development instructions.

+
+

Note: In the event that a vulnerability is found in one of the dependencies that may impact ClamAV, we will publish new packages with updated dependencies as soon as we're able.

+
+

Linux (.deb, .rpm)

+

Beginning with ClamAV 0.104, we offer Debian and RPM packages for x86_64 (64bit) and i686 (32bit) architectures. This will make it easier to get the latest version in the event that a package for your distribution is not readily available and you would prefer not to build ClamAV from source.

+
+

Note: These packages do not presently include clamav-milter. You can help help us add clamav-milter to the packages by developing a Mussels recipe for building the libmilter.a static library and contributing it to our Mussels cookbook.

+
+

RPM packages (for CentOS, Redhat, Fedora, SUSE, etc.)

+

These are compiled on CentOS 7. They should be compatible with all RPM-based linux distributions running glibc version 2.17 or newer.

+

To install, download the package for your system use yum or dnf to install the package. For example:

+
sudo dnf install ~/Downloads/clamav-0.104.0-rc2.linux.x86_64.rpm
+
+

You can verify that the package was installed using:

+
dnf info clamav
+
+

This package installs to /usr/local.

+

Unlike packages provided by Debian or other distributions, this package does not presently include a preconfigured freshclam.conf, clamd.conf, database directory, or clamav user accounts for FreshClam and ClamD. You can follow these instructions to configure FreshClam and ClamD. You can follow these instructions to create the clamav user account for running FreshClam and ClamD services.

+

And uninstall the package with:

+
sudo dnf remove ~/Downloads/clamav-0.104.0-rc2.linux.x86_64.rpm
+
+

DEB packages (for Debian, Ubuntu, Mint, etc.)

+

These are compiled on Ubuntu 16.04, and have all external library dependencies statically compiled in. They should be compatible with all Debian-based linux distributions running glibc version 2.23 or newer.

+
sudo apt install ~/Downloads/clamav-0.104.0-rc2.libnux.x86_64.deb
+
+

You can verify that the package was installed using:

+
apt info clamav
+
+

This package installs to /usr/local.

+

Unlike packages provided by Debian or other distributions, this package does not presently include a preconfigured freshclam.conf, clamd.conf, database directory, or clamav user accounts for FreshClam and ClamD. You can follow these instructions to configure FreshClam and ClamD. You can follow these instructions to create the clamav user account for running FreshClam and ClamD services.

+

And uninstall the package with:

+
sudo apt remove clamav
+
+

macOS

+

Beginning with ClamAV 0.104, we offer a PKG installer for macOS. These are universal binaries built for Intel x86_64 and Apple M1 arm64 processors.

+
+

Disclaimer: The release materials for 0.104.0-rc2 are not signed or notarized. We are working on adding signing and notarization to our CI processes, but for now you may be unable to use this PKG installer on macOS Big Sur or newer.

+
+

To install, download the macOS .pkg installer. Double-click the installer and follow the directions.

+

This package installs to /usr/local/clamav. This is not in the default system PATH environment variable. You may wish to add /usr/local/clamav/bin and /usr/local/clamav/sbin to your PATH so you can run the ClamAV programs without entering the full path. To do this add this line to ~/.zshrc:

+
export PATH=/usr/local/clamav/bin:/usr/local/clamav/sbin:$PATH
+
+

Then run source ~/.zshrc or open a new terminal.

+

Unlike packages provided by Homebrew, this package does not presently include a preconfigured freshclam.conf, clamd.conf, or database directory. You can follow these instructions to configure FreshClam and ClamD.

+

macOS package installers do not provide a mechanism for automatically uninstalling the package. In the future, we hope to add a script to aid with uninstallation. But for now, to make it easier to remove, our macOS installer installs to /usr/local/clamav. To uninstall, all you need to do is run:

+
sudo rm -rf /usr/local/clamav
+
+

Windows

+

The ClamAV team provides official ClamAV builds for Windows on the ClamAV downloads page. You can choose between a traditional executable installer or a portable install ZIP package.

+

To use the executable installer, double-click the installer and follow the instructions.

+

To install from a ZIP package, unzip the portable install package to any directory.

+

Official ClamAV Docker Images

+

There are now official ClamAV images on Docker Hub. You can find the images on Docker Hub under clamav.

+

At present we offer images with builds of the latest development version. We call this "unstable". ClamAV 0.104 will be the first stable release that we'll publish on Docker Hub.. Once published 0.104.0+ will be available using a Docker image tag with the specific version number, or using "stable" to get the latest stable release.

+

Check out the Docker page to learn how to install and use ClamAV with Docker.

+

Installing from Source

+

If you need, you can also compile and install ClamAV from source:

+ +

What now?

+

Now that ClamAV is installed, you will want to customize your configuration and perhaps set up some scanning automation and alerting mechanisms.

+

Continue on to "Configuration"...

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/OnAccess.html clamav-0.103.5+dfsg/docs/html/manual/OnAccess.html --- clamav-0.103.2+dfsg/docs/html/manual/OnAccess.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/OnAccess.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,321 @@ + + + + + + On-Access Scanning - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

On-Access Scanning

+

Purpose

+

This guide is for users interested in leveraging and understanding ClamAV's On-Access Scanning feature. It will walk through how to set up and use the On-Access Scanner and step through some common issues and their solutions.

+

Requirements

+

On-Access is only available on Linux systems. On Linux, On-Access requires a kernel version >= 3.8. This is because it leverages a kernel api called fanotify to block processes from attempting to access malicious files. This prevention occurs in kernel-space, and thus offers stronger protection than a purely user-space solution.

+

For Versions >= 0.102.0

+

It also requires Curl version >= 7.45 to ensure support for all curl options used by clamonacc. Users on Linux operating systems that package older versions of libcurl have a number of options:

+
    +
  1. Wait for your package maintainer to provide a newer version of libcurl.
  2. +
  3. Install a newer version of libcurl from source.
  4. +
  5. Disable installation of clamonacc and On-Access Scanning capabilities with the ./configure flag --disable-clamonacc.
  6. +
+

General Use

+

To use ClamAV's On-Access Scanner, operation will vary depending on version.

+

For Versions >= 0.102.0

+

You will need to run the clamd and clamonacc applications side by side. First, you will need to configure and run clamd. For instructions on how to do that, see the clamd configuration guide. One important thing to note while configuring clamd.conf is that--like clamdscan--the clamonacc application will connect to clamd using the clamd.conf settings for either LocalSocket or TCPAddr/TCPSocket. Another important thing to note, is that when using a clamd.conf that specifies a LocalSocket, then clamd will need to be run under a user with the right permissions to scan the files you plan on including in your watch-path.

+

Next, you will need to configure clamonacc. For a very simple configuration, follow these steps:

+
1. Open `clamd.conf` for editing
+2. Specify the path(s) you would like to recursively watch by setting the `OnAccessIncludePath` option
+3. Set `OnAccessPrevention` to `yes`
+4. Check what username `clamd` is running under
+5. Set `OnAccessExcludeUname` to `clamd`'s uname
+6. Save your work and close `clamd.conf`
+
+

For slightly more nuanced configurations, which may be adapted to your use case better, please check out the recipe guide below.

+

Then, run clamonacc with elevated permissions:

+
sudo clamonacc
+
+

If all went well, the On-Access scanner will fork to the background, and will now be actively protecting the path(s) specified with OnAccessIncludePath. You can test this by dropping an eicar file into the specified path, and attempting to read/access it (e.g. cat eicar.txt). This will result in an "Operation not permitted" message, triggered by fanotify blocking the access attempt at the kernel level.

+

Finally, you will have to restart both clamd and clamonacc. If default clamonacc performance is not to your liking, and your system has the resources available, we reccomend increasing the values for the following clamd.conf configuration options to increase performance:

+
    +
  • MaxQueue
  • +
  • MaxThreads
  • +
  • OnAccessMaxThreads
  • +
+

For Versions <= 0.101.x

+

You will only need to run the clamd application in older versions. First, +we reccomend you configure clamd for your environment. For instructions on how +to do that, see the clamd configuration guide.

+

Next, you will need to configure On Access Scanning using the clamd.conf file. For a very simple configuration follow these steps:

+
1. Open `clamd.conf` for editing
+2. Set the `ScanOnAccess` option to `yes`
+3. Specify the path(s) you would like to recursively watch by setting the `OnAccessIncludePath` option
+4. Set `OnAccessPrevention` to `yes`
+6. Save your work and close `clamd.conf`
+
+

For slightly more nuanced configurations, which may be adapted to your use case better, please check out the recipe guide below.

+

Then, run clamd with elevated permissions:

+
sudo clamd
+
+

If all went well, the On-Access scanner will fork to the background, and will now be actively protecting the path(s) specified with OnAccessIncludePath. You can test this by dropping an eicar file into the specified path, and attempting to read/access it (e.g. cat eicar.txt). This will result in an "Operation not permitted" message, triggered by fanotify blocking the access attempt at the kernel level.

+

Troubleshooting

+

Some OS distributors have disabled fanotify, despite kernel support. You can check for fanotify support on your kernel by running the command:

+
cat /boot/config-<kernel_version> | grep FANOTIFY
+
+

You should see the following:

+
CONFIG_FANOTIFY=y
+CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y
+
+

If you see this...

+
CONFIG_FANOTIFY_ACCESS_PERMISSIONS is not set
+
+

... then ClamAV's On-Access Scanner will still function, scanning and alerting on files normally in real time. However, it will be unable to block access attempts on malicious files. We call this notify-only mode.

+

ClamAV's On-Access Scanning system uses a scheme called Dynamic Directory Determination (DDD for short) which is a shorthand way of saying that it tracks the layout of every directory specified with OnAccessIncludePath dynamically, and recursively, in real time. It does this by leveraging inotify which by default has a limited number of watch-points available for use by a process at any given time. Given the complexity of some directory hierarchies, ClamAV may warn you that it has exhausted its supply of inotify watch-points (8192 by default). To increase the number of inotify watch-points available for use by ClamAV (to 524288), run the following command:

+
echo 524288 | sudo tee -a /proc/sys/fs/inotify/max_user_watches
+
+

The OnAccessIncludePath option will not accept / as a valid path. This is because fanotify works by blocking a process' access to a file until a access_ok or access_denied determination has been made by the original fanotify calling process. Thus, by placing fanotify watch-points on the entire filesystem, key system files may have their access blocked to key processes at the kernel level, which will result in a system lockup.

+

This restriction was made to prevent users from "shooting themselves in the foot." However, clever users will find it's possible to circumvent this restriction by using multiple OnAccessIncludePath options to recursively protect most of the filesystem anyways, or better still, simply the paths they truly care about.

+

The OnAccessMountPath option uses a different fanotify api configuration which makes it incompatible with OnAccessIncludePath and the DDD System. Therefore, inotify watch-point limitations will not be a concern when using this option. Unfortunately, this also means that the following options cannot be used in conjunction with OnAccessMountPath:

+
    +
  • OnAccessExtraScanning - is built around catching inotify events.
  • +
  • OnAccessExcludePath - is built upon the DDD System.
  • +
  • OnAccessPrevention - would lock up the system if / was selected for OnAccessMountPath. If you need OnAccessPrevention, you should use OnAccessIncludePath instead of OnAccessMountPath.
  • +
+

Configuration and Recipes

+

More nuanced behavior can be coerced from ClamAV's On-Access Scanner via careful modification to clamd.conf. Each option related to On-Access Scanning is easily identified by looking for the OnAccess prefix pre-pended to each option. The default clamd.conf file contains descriptions of each option, along with any documented limitations or safety features.

+

Below are examples of common use cases, recipes for the correct minimal configuration, and the expected behavioral result.

+

Use Case 0x0

+
    +
  • User needs to watch the entire file system, but blocking malicious access attempts isn't a concern
  • +
+
    ScanOnAccess yes ## versions <= 0.101.x
+    OnAccessMountPath /
+    OnAccessExcludeRootUID yes
+    OnAccessExcludeUname clamav ## versions >= 0.102
+
+

This configuration will put the On-Access Scanner into notify-only mode. It will also ensure only non-root, non-clam, user processes will trigger scans against the filesystem.

+

Use Case 0x1

+
    +
  • System Administrator needs to watch the home directory of multiple Users, but not all users. Blocking access attempts is un-needed.
  • +
+
    ScanOnAccess yes ## versions <= 0.101.x
+    OnAccessIncludePath /home
+    OnAccessExcludePath /home/user2
+    OnAccessExcludePath /home/user4
+    OnAccessExcludeUname clamav ## versions >= 0.102
+
+

With this configuration, the On-Access Scanner will watch the entirety of the /home directory recursively in notify-only mode. However, it will recursively exclude the /home/user2 and /home/user4 directories.

+

Use Case 0x2

+
    +
  • The user needs to protect a single directory non-recursively and ensure all access attempts on malicious files are blocked.
  • +
+
    ScanOnAccess yes ## versions <= 0.101.x
+    OnAccessIncludePath /home/user/Downloads
+    OnAccessExcludeUname clamav ## versions >= 0.102
+    OnAccessPrevention yes
+    OnAccessDisableDDD yes
+
+

The configuration above will result in non-recursive real-time protection of the /home/user/Downloads directory by ClamAV's On-Access Scanner. Any access attempts that ClamAV detects on malicious files within the top level of the directory hierarchy will be blocked by fanotify at the kernel level.

+

Command Line Options for Versions >= 0.102

+

Beyond clamd.conf configuration, you can change the behavior of the On-Access scanner by passing in a number of command line options. A list of all options can be retrieved with --help, but below is a list and explanation of some of options you might find most useful.

+
    +
  • --log=FILE (-l FILE) - passing this option is important if you want a record of scan results, otherwise clamonacc will operate silently.
  • +
  • --verbose (-v) - primarily for debugging as this will increase the amount of noise in your log by quite a lot, but useful for troubleshooting potential connection problems
  • +
  • --foreground (-F) - forces clamonacc to not for the background, which is useful for debugging potential issues with during startup or runtime
  • +
  • --include-list=FILE (-e FILE) - allows users to pass a list of directories for clamonacc to watch, each directory must be a full path and separated by a newline
  • +
  • --exclude-list=FILE (-e FILE) - same as include-list option, but for excluding at startup
  • +
  • --remove - after an infected verdict, an attempt will be made to remove the infected file
  • +
  • --move=DIRECTORY - just like the remove option, but infected file will be moved to the specified quarantine location instead
  • +
  • --copy=DIRECTORY - just like the move, except infected file is also left in place
  • +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Signatures/AllowLists.html clamav-0.103.5+dfsg/docs/html/manual/Signatures/AllowLists.html --- clamav-0.103.2+dfsg/docs/html/manual/Signatures/AllowLists.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Signatures/AllowLists.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,231 @@ + + + + + + Allow Lists - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Allow list databases

+

File allow lists

+

To allow a specific file use the MD5 signature format and place it inside a database file with the extension of .fp (for "false positive"). To allow a specific file with the SHA1 or SHA256 file hash signature format, place the signature inside a database file with the extension of .sfp (for "SHA false positive").

+

To generate FP or SFP signatures, try something like this...

+

MD5:

+
sigtool --md5 /path/to/false/positive/file >> /path/to/databases/false-positives.fp
+
+

SHA256:

+
sigtool --sha256 /path/to/false/positive/file >> /path/to/databases/false-positives.sfp
+
+

Here's an example adding the EICAR test file to an allow list by generating a sha256 false positive signature:

+
❯ clamscan ~/Downloads/eicar.com
+/mnt/c/Users/micah/Downloads/eicar.com: Win.Test.EICAR_HDB-1 FOUND
+
+...
+
+❯ sigtool --sha256 ~/Downloads/eicar.com >> /var/lib/clamav/false-positives.sfp
+
+❯ clamscan ~/Downloads/eicar.com
+/mnt/c/Users/micah/Downloads/eicar.com: OK
+...
+
+

Signature ignore lists

+

To ignore a specific signature from the database you just add the signature name into a local file with the .ign2 extension and store it inside the database directory.

+

E.g:

+
Eicar-Test-Signature
+
+

Additionally, you can follow the signature name with the MD5 of the entire database entry for this signature. In such a case, the signature will no longer be ignored when its entry in the database gets modified (eg. the signature gets updated to avoid false alerts). E.g:

+
Eicar-Test-Signature:bc356bae4c42f19a3de16e333ba3569c
+
+

Historically, signature ignores were added to .ign files. This format is still functional, though it has been replaced by the .ign2 database.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Signatures/AuthenticodeRules.html clamav-0.103.5+dfsg/docs/html/manual/Signatures/AuthenticodeRules.html --- clamav-0.103.2+dfsg/docs/html/manual/Signatures/AuthenticodeRules.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Signatures/AuthenticodeRules.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,242 @@ + + + + + + Trusted and Revoked EXE Certificates - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Trusted and Revoked Certificates

+

Clamav 0.98 checks signed PE files for certificates and verifies each certificate in the chain against a database of trusted and revoked certificates. The signature format is

+
Name;Trusted;Subject;Serial;Pubkey;Exponent;CodeSign;TimeSign;CertSign;
+NotBefore;Comment[;minFL[;maxFL]]
+
+

where the corresponding fields are:

+
    +
  • +

    Name: name of the entry

    +
  • +
  • +

    Trusted: bit field, specifying whether the cert is trusted. 1 for trusted. 0 for revoked

    +
  • +
  • +

    Subject: sha1 of the Subject field in hex

    +
  • +
  • +

    Serial: the serial number as clamscan --debug --verbose reports

    +
  • +
  • +

    Pubkey: the public key in hex

    +
  • +
  • +

    Exponent: the exponent in hex. Currently ignored and hardcoded to 010001 (in hex)

    +
  • +
  • +

    CodeSign: bit field, specifying whether this cert can sign code. 1 for true, 0 for false

    +
  • +
  • +

    TimeSign: bit field. 1 for true, 0 for false

    +
  • +
  • +

    CertSign: bit field, specifying whether this cert can sign other certs. 1 for true, 0 for false

    +
  • +
  • +

    NotBefore: integer, cert should not be added before this variable. Defaults to 0 if left empty

    +
  • +
  • +

    Comment: comments for this entry

    +
  • +
+

The signatures for certs are stored inside .crb files.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Signatures/BodySignatureFormat.html clamav-0.103.5+dfsg/docs/html/manual/Signatures/BodySignatureFormat.html --- clamav-0.103.2+dfsg/docs/html/manual/Signatures/BodySignatureFormat.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Signatures/BodySignatureFormat.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,295 @@ + + + + + + Content-based Signature Format - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Body-based Signature Content Format

+

ClamAV stores all body-based (content-based) signatures in a hexadecimal format, with exception to ClamAV's YARA rule support. In this section by a hex-signature we mean a fragment of malware’s body converted into a hexadecimal string which can be additionally extended using various wildcards.

+

Hexadecimal format

+

You can use sigtool --hex-dump to convert any data into a hex-string:

+
zolw@localhost:/tmp/test$ sigtool --hex-dump
+How do I look in hex?
+486f7720646f2049206c6f6f6b20696e206865783f0a
+
+

Wildcards

+

ClamAV supports the following wildcards for hex-signatures:

+
    +
  • +

    ??

    +

    Match any byte.

    +
  • +
  • +

    a?

    +

    Match a high nibble (the four high bits).

    +
  • +
  • +

    ?a

    +

    Match a low nibble (the four low bits).

    +
  • +
  • +

    *

    +

    Match any number of bytes.

    +
  • +
  • +

    {n}

    +

    Match n bytes.

    +
  • +
  • +

    {-n}

    +

    Match n or less bytes.

    +
  • +
  • +

    {n-}

    +

    Match n or more bytes.

    +
  • +
  • +

    {n-m}

    +

    Match between n and m bytes (where m > n).

    +
  • +
  • +

    HEXSIG[x-y]aa or aa[x-y]HEXSIG

    +

    Match aa anchored to a hex-signature, see Bugzilla ticket 776 for discussion and +examples.

    +
  • +
+

The range signatures * and {} virtually separate a hex-signature into two parts, eg. aabbcc*bbaacc is treated as two sub-signatures aabbcc and bbaacc with any number of bytes between them. It’s a requirement that each sub-signature includes a block of two static characters somewhere in its body. Note that there is one exception to this restriction; that is when the range wildcard is of the form {n} with n<128. In this case, ClamAV uses an optimization and translates {n} to the string consisting of n ?? character wildcards. Character wildcards do not divide hex signatures into two parts and so the two static character requirement does not apply.

+

Character classes

+

ClamAV supports the following character classes for hex-signatures:

+
    +
  • +

    (B)

    +

    Match word boundary (including file boundaries).

    +
  • +
  • +

    (L)

    +

    Match CR, CRLF or file boundaries.

    +
  • +
  • +

    (W)

    +

    Match a non-alphanumeric character.

    +
  • +
+

Alternate strings

+
    +
  • +

    Single-byte alternates (clamav-0.96) (aa|bb|cc|...) or !(aa|bb|cc|...) Match a member from a set of bytes (eg: aa, bb, cc, ...).

    +
      +
    • Negation operation can be applied to match any non-member, assumed to be one-byte in length.
    • +
    • Signature modifiers and wildcards cannot be applied.
    • +
    +
  • +
  • +

    Multi-byte fixed length alternates (aaaa|bbbb|cccc|...) or !(aaaa|bbbb|cccc|...) Match a member from a set of multi-byte alternates (eg: aaaa, bbbb, cccc, ...) of n-length.

    +
      +
    • All set members must be the same length.
    • +
    • Negation operation can be applied to match any non-member, assumed to be n-bytes in length (clamav-0.98.2).
    • +
    • Signature modifiers and wildcards cannot be applied.
    • +
    +
  • +
  • +

    Generic alternates (clamav-0.99) (alt1|alt2|alt3|...) Match a member from a set of alternates (eg: alt1, alt2, alt3, ...) that can be of variable lengths.

    +
      +
    • Negation operation cannot be applied.
    • +
    • Signature modifiers and nibble wildcards (eg: ??, a?, ?a) can be applied.
    • +
    • Ranged wildcards (eg: {n-m}) are limited to a fixed range of less than 128 bytes (eg: {1} -> {127}).
    • +
    +
  • +
+
+

Note: Using signature modifiers and wildcards classifies the alternate type to be a generic alternate. Thus single-byte alternates and multi-byte fixed length alternates can use signature modifiers and wildcards but will be classified as generic alternate. This means that negation cannot be applied in this situation and there is a slight performance impact.

+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Signatures/BytecodeSignatures.html clamav-0.103.5+dfsg/docs/html/manual/Signatures/BytecodeSignatures.html --- clamav-0.103.2+dfsg/docs/html/manual/Signatures/BytecodeSignatures.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Signatures/BytecodeSignatures.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,206 @@ + + + + + + Bytecode Signatures - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Bytecode Signatures

+

Bytecode Signatures are the means by which more complex matching can be performed by writing C code to parse sample content at various stages in file extraction.

+

It is less complicated than it sounds. Essentially the signature author writes a function in C is compiled down to an intermediate language called "bytecode". This bytecode is encoded in ASCII .cbc file and distributed in bytecode.[cvd|cld]. When the database is loaded, ClamAV can interpret this bytecode to execute the function.

+

Bytecode functions are provided with a set of APIs that may be used to access the sample data, and to access what metadata ClamAV already has concerning the sample.

+

The function may at any time call an API to flag the sample as malicious, and may provide the signature/virus name at that time. This means a single bytecode signature (function) is written to handle a given file type and may trigger different alerts with different signature names as additional malicious characteristics for the file type are identified. That isn't to say that only one bytecode signature may be assigned to a given filetype, but that a single author may find it to be more efficient to use a bytecode signature to identify more than one type of malware.

+

The specifics on how to write and compile bytecode signatures are outside of the scope of this documentation. Extensive documentation on ClamAV Bytecode Signatures are provided with the ClamAV Bytecode Compiler.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Signatures/ContainerMetadata.html clamav-0.103.5+dfsg/docs/html/manual/Signatures/ContainerMetadata.html --- clamav-0.103.2+dfsg/docs/html/manual/Signatures/ContainerMetadata.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Signatures/ContainerMetadata.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,252 @@ + + + + + + Container Metadata Signatures - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Signatures based on container metadata

+

ClamAV 0.96 allows creating generic signatures matching files stored inside different container types which meet specific conditions. The signature format is:

+
    VirusName:ContainerType:ContainerSize:FileNameREGEX:
+    FileSizeInContainer:FileSizeReal:IsEncrypted:FilePos:
+    Res1:Res2[:MinFL[:MaxFL]]
+
+

where the corresponding fields are:

+
    +
  • +

    VirusName: Virus name to be displayed when signature matches.

    +
  • +
  • +

    ContainerType: The file type containing the target file. For example:

    +
      +
    • CL_TYPE_ZIP,
    • +
    • CL_TYPE_RAR,
    • +
    • CL_TYPE_ARJ,
    • +
    • CL_TYPE_MSCAB,
    • +
    • CL_TYPE_7Z,
    • +
    • CL_TYPE_MAIL,
    • +
    • CL_TYPE_(POSIX|OLD)_TAR,
    • +
    • CL_TYPE_CPIO_(OLD|ODC|NEWC|CRC)
    • +
    +

    Use * as a wild card to indicate that container type may be any file type. +For a full list of ClamAV file types, see the ClamAV File Types Reference.

    +
  • +
  • +

    ContainerSize: size of the container file itself (eg. size of the zip archive) specified in bytes as absolute value or range x-y.

    +
  • +
  • +

    FileNameREGEX: regular expression describing name of the target file

    +
  • +
  • +

    FileSizeInContainer: usually compressed size; for MAIL, TAR and CPIO == FileSizeReal; specified in bytes as absolute value or range.

    +
  • +
  • +

    FileSizeReal: usually uncompressed size; for MAIL, TAR and CPIO == FileSizeInContainer; absolute value or range.

    +
  • +
  • +

    IsEncrypted: 1 if the target file is encrypted, 0 if it’s not and * to ignore

    +
  • +
  • +

    FilePos: file position in container (counting from 1); absolute value or range.

    +
  • +
  • +

    Res1: when ContainerType is CL_TYPE_ZIP or CL_TYPE_RAR this field is treated as a CRC sum of the target file specified in hexadecimal format; for other container types it’s ignored.

    +
  • +
  • +

    Res2: not used as of ClamAV 0.96.

    +
  • +
+

The signatures for container files are stored inside .cdb files.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Signatures/DatabaseInfo.html clamav-0.103.5+dfsg/docs/html/manual/Signatures/DatabaseInfo.html --- clamav-0.103.2+dfsg/docs/html/manual/Signatures/DatabaseInfo.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Signatures/DatabaseInfo.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,208 @@ + + + + + + CVD Info File - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Database Info

+

The .info file format specifies information about the other database files unpacked from a CVD or CLD database archive. This file exists for the purposes of validating the correctness of the official ClamAV database container files and cannot be loaded a la carte.

+

The format is simply:

+
name:size:sha256
+
+

name: The database file name.

+

size: The size in bytes of the database.

+

sha256: A SHA256 hash of the database.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Signatures/DynamicConfig.html clamav-0.103.5+dfsg/docs/html/manual/Signatures/DynamicConfig.html --- clamav-0.103.2+dfsg/docs/html/manual/Signatures/DynamicConfig.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Signatures/DynamicConfig.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,256 @@ + + + + + + Dynamic Configuration Settings - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Dynamic Configuration (DCONF)

+

ClamAV supports a limited set of configuration options that may be enabled or disabled via settings in the *.cfg database. At this time, these settings are distributed in daily.cfg.

+

The goal of DCONF is to enable the ClamAV team to rapidly disable new or experimental features for specific ClamAV versions if a significant defect is discovered after release.

+

This database is small, and the settings are largely vestigial. The team has not had a need to disable many features in a long time, and so the ClamAV versions in the settings at this time should no longer be in use.

+

The strings and values referenced in daily.cfg are best cross-referenced with the macros and structures defined here:

+
    +
  • https://github.com/Cisco-Talos/clamav/blob/main/libclamav/dconf.h#L49
  • +
  • https://github.com/Cisco-Talos/clamav/blob/main/libclamav/dconf.c#L54
  • +
+

The format for a DCONF signature is:

+
Category:Flags:StartFlevel:EndFlevel
+
+

Category may be one of:

+
    +
  • PE
  • +
  • ELF
  • +
  • MACHO
  • +
  • ARCHIVE
  • +
  • DOCUMENT
  • +
  • MAIL
  • +
  • OTHER
  • +
  • PHISHING
  • +
  • BYTECODE
  • +
  • STATS
  • +
  • PCRE
  • +
+

Flags:

+

Every feature that may be configured via DCONF is listed in struct dconf_module modules in libclamav/dconf.c. Any given feature may be default-on or default-off. Default-on features have the 4th field set to a 1 and default off are set to 0. The Flags field for a given Category overrides the defaults for all of the options listed under that category.

+

A settings of 0x0, for example, means that all options the category be disabled.

+

The macros listed in libclamav/dconf.h will help you identify which bits to set to get the desired results.

+

StartFlevel:

+

This is the FLEVEL of the minimum ClamAV engine for which you want the settings to be in effect.

+

EndFlevel:

+

This is the FLEVEL of the maximum ClamAV engine for which you want the settings to be in effect. You may wish to select 255 to override the defaults of future releases.

+

Example

+

Consider the OTHER_CONF_PDFNAMEOBJ option in the category OTHER.

+
#define OTHER_CONF_UUENC        0x1     // Default: 1
+#define OTHER_CONF_SCRENC       0x2     // Default: 1
+#define OTHER_CONF_RIFF         0x4     // Default: 1
+#define OTHER_CONF_JPEG         0x8     // Default: 1
+#define OTHER_CONF_CRYPTFF      0x10    // Default: 1
+#define OTHER_CONF_DLP          0x20    // Default: 1
+#define OTHER_CONF_MYDOOMLOG    0x40    // Default: 1
+#define OTHER_CONF_PREFILTERING 0x80    // Default: 1
+#define OTHER_CONF_PDFNAMEOBJ   0x100   // Default: 1
+#define OTHER_CONF_PRTNINTXN    0x200   // Default: 1
+#define OTHER_CONF_LZW          0x400   // Default: 1
+
+

All of the OTHER options, including OTHER_CONF_PDFNAMEOBJ are default-on. To disable the option for ClamAV v0.100.X but leave the other options in their default settings, we would need to set the flags to:

+
0110 1111 1111
+   ^pdfnameobj off
+
+

Or in hex: 0x6FF

+

The example setting to place in daily.cfg then woudl be:

+
OTHER:0x6FF:90:99
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Signatures/EncryptedArchives.html clamav-0.103.5+dfsg/docs/html/manual/Signatures/EncryptedArchives.html --- clamav-0.103.2+dfsg/docs/html/manual/Signatures/EncryptedArchives.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Signatures/EncryptedArchives.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,229 @@ + + + + + + Archive Passwords (experimental) - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Passwords for archive files [experimental]

+

ClamAV allows for users to specify password attempts for certain password-compatible archives. Passwords will be attempted in order of appearance in the password signature file which use the extension of .pwdb. If no passwords apply or none are provided, ClamAV will default to the original behavior of parsing the file.

+

Currently, as of ClamAV 0.99 [flevel 81], only .zip archives using the traditional PKWARE encryption are supported. The signature format is:

+
SignatureName;TargetDescriptionBlock;PWStorageType;Password
+
+

where:

+
    +
  • +

    SignatureName: name to be displayed during debug when a password is successful

    +
  • +
  • +

    TargetDescriptionBlock: provides information about the engine and target file with comma separated Arg:Val pairs

    +
      +
    • Engine:X-Y: Required engine functionality level. See the FLEVEL reference for details.
    • +
    • Container:CL_TYPE_*: File type of applicable containers
    • +
    +
  • +
  • +

    PWStorageType: determines how the password field is parsed

    +
      +
    • 0 = cleartext
    • +
    • 1 = hex
    • +
    +
  • +
  • +

    Password: value used in password attempt

    +
  • +
+

The signatures for password attempts are stored inside .pwdb files.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Signatures/ExtendedSignatures.html clamav-0.103.5+dfsg/docs/html/manual/Signatures/ExtendedSignatures.html --- clamav-0.103.2+dfsg/docs/html/manual/Signatures/ExtendedSignatures.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Signatures/ExtendedSignatures.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,226 @@ + + + + + + Extended Signatures - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Extended signature format

+

The extended signature format is ClamAV's most basic type of body-based signature since the deprecation of the original .db database format.

+

Extended sigantures allow for specification of additional information beyond just hexidecimal content such as a file "target type", virus offset, or engine functionality level (FLEVEL), making the detection more reliable.

+

The format is:

+
MalwareName:TargetType:Offset:HexSignature[:min_flevel:[max_flevel]]
+
+

MalwareName: The virus name. Should conform to the standards defined here.

+

TargetType: A number specifying the type of the target file: Target Types

+

Offset: An asterisk or a decimal number n possibly combined with a special modifier:

+
    +
  • * = any
  • +
  • n = absolute offset
  • +
  • EOF-n = end of file minus n bytes
  • +
+

Signatures for PE, ELF and Mach-O files additionally support:

+
    +
  • EP+n = entry point plus n bytes (EP+0 for EP)
  • +
  • EP-n = entry point minus n bytes
  • +
  • Sx+n = start of section x’s (counted from 0) data plus n bytes
  • +
  • SEx = entire section x (offset must lie within section boundaries)
  • +
  • SL+n = start of last section plus n bytes
  • +
+

All the above offsets except * can be turned into floating offsets and represented as Offset,MaxShift where MaxShift is an unsigned integer. A floating offset will match every offset between Offset and Offset+MaxShift, eg. 10,5 will match all offsets from 10 to 15 and EP+n,y will match all offsets from EP+n to EP+n+y. Versions of ClamAV older than 0.91 will silently ignore the MaxShift extension and only use Offset. Optional MinFL and MaxFL parameters can restrict the signature to specific engine releases. All signatures in the extended format must be placed inside *.ndb files.

+

HexSignature: The body-based content matching format.

+

min_flevel: (optional) The minimum ClamAV engine that the file type signature works with. See the FLEVEL reference for details. To be used in the event that file type support has been recently added.

+

max_flevel: (optional, requires min_flevel) The maximum ClamAV engine that the file type signature works with. To be used in the event that file type support has been recently removed.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Signatures/FileTypeMagic.html clamav-0.103.5+dfsg/docs/html/manual/Signatures/FileTypeMagic.html --- clamav-0.103.2+dfsg/docs/html/manual/Signatures/FileTypeMagic.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Signatures/FileTypeMagic.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,220 @@ + + + + + + File Type Recognition - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

File Type Magic

+

ClamAV's primary mechanism for determining file types is to match the file with a File Type Magic signature. These file type signatures are compiled into ClamAV, and may also be overridden dynamically using the definition founds found in a *.ftm file.

+

The ClamAV standard signature database includes these definitions in daily.ftm.

+

The signature format is not too disimilar from NDB body-based signatures.

+

The format is:

+
magictype:offset:magicbytes:name:rtype:type[:min_flevel[:max_flevel]]
+
+

Where:

+

magictype: Supported magic types include:

+
    +
  • 0 - direct memory comparison of magicbytes for file types
  • +
  • 1 - The magicbytes use the body-based content matching format.
  • +
  • 4 - direct memory comparison of magicbytes for partition types (HFS+, HFSX)
  • +
+

offset: The offset from start of the file to match against. May be * if magictype is 1.

+

name: A descriptive name for the file type.

+

rtype: Previously detected file type. Usually CL_TYPE_ANY as a wild-card.

+

type: The CL_TYPE corresponding with the file type signature. See the CL_TYPE reference for details.

+

min_flevel: (optional) The minimum ClamAV engine that the file type signature works with. See the FLEVEL reference for details. To be used in the event that file type support has been recently added.

+

max_flevel: (optional, requires min_flevel) The maximum ClamAV engine that the file type signature works with. To be used in the event that file type support has been recently removed.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Signatures/HashSignatures.html clamav-0.103.5+dfsg/docs/html/manual/Signatures/HashSignatures.html --- clamav-0.103.2+dfsg/docs/html/manual/Signatures/HashSignatures.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Signatures/HashSignatures.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,242 @@ + + + + + + Hash-based Signatures - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

File hash signatures

+

The easiest way to create signatures for ClamAV is to use filehash checksums, however this method can be only used against static malware.

+

MD5 hash-based signatures

+

To create a MD5 signature for test.exe use the --md5 option of +sigtool:

+
zolw@localhost:/tmp/test$ sigtool --md5 test.exe > test.hdb
+zolw@localhost:/tmp/test$ cat test.hdb
+48c4533230e1ae1c118c741c0db19dfb:17387:test.exe
+
+

That’s it! The signature is ready for use:

+
zolw@localhost:/tmp/test$ clamscan -d test.hdb test.exe
+test.exe: test.exe FOUND
+
+----------- SCAN SUMMARY -----------
+Known viruses: 1
+Scanned directories: 0
+Engine version: 0.92.1
+Scanned files: 1
+Infected files: 1
+Data scanned: 0.02 MB
+Time: 0.024 sec (0 m 0 s)
+
+

You can change the name (by default sigtool uses the name of the file) and place it inside a *.hdb file. A single database file can include any number of signatures. To get them automatically loaded each time clamscan/clamd starts just copy the database file(s) into the local virus database directory (eg. /usr/local/share/clamav).

+

The hash-based signatures shall not be used for text files, HTML and any other data that gets internally preprocessed before pattern matching. If you really want to use a hash signature in such a case, run clamscan with --debug and --leave-temps flags as described above and create a signature for a preprocessed file left in /tmp. Please keep in mind that a hash signature will stop matching as soon as a single byte changes in the target file.

+

SHA1 and SHA256 hash-based signatures

+

ClamAV 0.98 has also added support for SHA1 and SHA256 file checksums. The format is the same as for MD5 file checksum. It can differentiate between them based on the length of the hash string in the signature. For best backwards compatibility, these should be placed inside a *.hsb file. The format is:

+
HashString:FileSize:MalwareName
+
+

Hash signatures with unknown size

+

ClamAV 0.98 has also added support for hash signatures where the size is not known but the hash is. It is much more performance-efficient to use signatures with specific sizes, so be cautious when using this feature. For these cases, the ’*’ character can be used in the size field. To ensure proper backwards compatibility with older versions of ClamAV, these signatures must have a minimum functional level of 73 or higher. Signatures that use the wildcard size without this level set will be rejected as malformed.

+

Sample .hsb signature matching any size:

+
HashString:*:MalwareName:73
+
+

Sample .msb signature matching any size:

+
*:PESectionHash:MalwareName:73
+
+

PE section based hash signatures

+

You can create a hash signature for a specific section in a PE file. Such signatures shall be stored inside .mdb (MD5) and .msb files in the following format:

+
PESectionSize:PESectionHash:MalwareName
+
+

The easiest way to generate MD5 based section signatures is to extract target PE sections into separate files and then run sigtool with the option --mdb

+

ClamAV also has support for SHA1 and SHA256 section based signatures. The format is the same as for MD5 PE section based signatures. It can differentiate between them based on the length of the hash string in the signature. For best backwards compatibility, these should be placed inside a *.msb file.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Signatures/LogicalSignatures.html clamav-0.103.5+dfsg/docs/html/manual/Signatures/LogicalSignatures.html --- clamav-0.103.2+dfsg/docs/html/manual/Signatures/LogicalSignatures.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Signatures/LogicalSignatures.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,573 @@ + + + + + + Logical Signatures - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Logical signatures

+

Logical signatures allow combining of multiple signatures in extended format using logical operators. They can provide both more detailed and flexible pattern matching. The logical sigs are stored inside *.ldb files in the following format:

+
SignatureName;TargetDescriptionBlock;LogicalExpression;Subsig0;
+Subsig1;Subsig2;...
+
+

where:

+
    +
  • +

    TargetDescriptionBlock provides information about the engine and target file with comma separated Arg:Val pairs. For args where Val is a range, the minimum and maximum values should be expressed as min-max.

    +
  • +
  • +

    LogicalExpression specifies the logical expression describing the relationship between Subsig0...SubsigN. Basis clause: 0,1,...,N decimal indexes are SUB-EXPRESSIONS representing Subsig0, Subsig1,...,SubsigN respectively. Inductive clause: if A and B are SUB-EXPRESSIONS and X, Y are decimal numbers then (A&B), (A|B), A=X, A=X,Y, A>X, A>X,Y, A<X and A<X,Y are SUB-EXPRESSIONS

    +
  • +
  • +

    SubsigN is n-th subsignature in extended format possibly preceded with an offset. There can be specified up to 64 subsigs.

    +
  • +
+

Keywords used in TargetDescriptionBlock:

+
    +
  • +

    Target:X: A number specifying the type of the target file: Target Types.

    +
  • +
  • +

    Engine:X-Y: Required engine functionality level (range; 0.96). Note that if the Engine keyword is used, it must be the first one in the TargetDescriptionBlock for backwards compatibility. See the FLEVEL reference for details.

    +
  • +
  • +

    FileSize:X-Y: Required file size (range in bytes; 0.96)

    +
  • +
  • +

    EntryPoint: Entry point offset (range in bytes; 0.96)

    +
  • +
  • +

    NumberOfSections: Required number of sections in executable (range; 0.96)

    +
  • +
  • +

    Container:CL_TYPE_*: File type of the container which stores the scanned file.

    +

    Specifying CL_TYPE_ANY matches on root objects only (i.e. the target file is explicitely not in a container). Chances slim that you would want to use CL_TYPE_ANY in a signature, because placing the malicious file in an archive will then prevent it from alerting.

    +

    Every ClamAV file type has the potential to be a container for additional files, although some are more likely than others. When a file is parsed and data in the file is identified to be scanned as a unique type, that parent file becomes a container the moment the embedded content is scanned. For a list of possible CL_TYPEs, refer to the File Types Reference.

    +
  • +
  • +

    Intermediates:CL_TYPE_*>CL_TYPE_*: Specify one or more layers of file types containing the scanned file. This is an alternative to using Container.

    +

    You may specify up to 16 layers of file types separated by ’>’ in top-down order. Note that the ’>’ separator is not needed if you only specify a single container. The last type should be the immediate container containing the malicious file. Unlike with the Container option, CL_TYPE_ANY can be used as a wildcard file type. (expr; 0.100.0)

    +

    For a list of possible CL_TYPEs, refer to the File Types Reference.

    +
  • +
  • +

    IconGroup1: Icon group name 1 from .idb signature Required engine functionality (range; 0.96)

    +
  • +
  • +

    IconGroup2: Icon group name 2 from .idb signature Required engine functionality (range; 0.96)

    +
  • +
+

Modifiers for subexpressions:

+
    +
  • +

    A=X: If the SUB-EXPRESSION A refers to a single signature then this signature must get matched exactly X times; if it refers to a (logical) block of signatures then this block must generate exactly X matches (with any of its sigs).

    +
  • +
  • +

    A=0 specifies negation (signature or block of signatures cannot be matched)

    +
  • +
  • +

    A=X,Y: If the SUB-EXPRESSION A refers to a single signature then this signature must be matched exactly X times; if it refers to a (logical) block of signatures then this block must generate X matches and at least Y different signatures must get matched.

    +
  • +
  • +

    A>X: If the SUB-EXPRESSION A refers to a single signature then this signature must get matched more than X times; if it refers to a (logical) block of signatures then this block must generate more than X matches (with any of its sigs).

    +
  • +
  • +

    A>X,Y: If the SUB-EXPRESSION A refers to a single signature then this signature must get matched more than X times; if it refers to a (logical) block of signatures then this block must generate more than X matches and at least Y different signatures must be matched.

    +
  • +
  • +

    A<X: Just like A>Z above with the change of "more" to "less".

    +

    If the SUB-EXPRESSION A refers to a single signature then this signature must get matched less than X times; if it refers to a (logical) block of signatures then this block must generate less than X matches (with any of its sigs).

    +
  • +
  • +

    A<X,Y: Similar to A>X,Y. If the SUB-EXPRESSION A refers to a single signature then this signature must get matched less than X times; if it refers to a (logical) block of signatures then this block must generate less than X matches and at least Y different signatures must be matched.

    +
  • +
+

Examples:

+
Sig1;Target:0;(0&1&2&3)&(4|1);6b6f74656b;616c61;7a6f6c77;7374656
+6616e;deadbeef
+
+Sig2;Target:0;((0|1|2)>5,2)&(3|1);6b6f74656b;616c61;7a6f6c77;737
+46566616e
+
+Sig3;Target:0;((0|1|2|3)=2)&(4|1);6b6f74656b;616c61;7a6f6c77;737
+46566616e;deadbeef
+
+Sig4;Engine:51-255,Target:1;((0|1)&(2|3))&4;EP+123:33c06834f04100
+f2aef7d14951684cf04100e8110a00;S2+78:22??232c2d252229{-15}6e6573
+(63|64)61706528;S3+50:68efa311c3b9963cb1ee8e586d32aeb9043e;f9c58
+dcf43987e4f519d629b103375;SL+550:6300680065005c0046006900
+
+

Subsignature Modifiers

+

ClamAV (clamav-0.99) supports a number of additional subsignature +modifiers for logical signatures. This is done by specifying :: +followed by a number of characters representing the desired options. +Signatures using subsignature modifiers require Engine:81-255 for +backwards-compatibility.

+
    +
  • +

    Case-Insensitive [i]

    +

    Specifying the i modifier causes ClamAV to match all alphabetic hex bytes as case-insensitive. All patterns in ClamAV are case-sensitive by default.

    +
  • +
  • +

    Wide [w]

    +

    Specifying the w causes ClamAV to match all hex bytes encoded with two bytes per character. Note this simply interweaves each character with NULL characters and does not truly support UTF-16 characters. Wildcards for ’wide’ subsignatures are not treated as wide (i.e. there can be an odd number of intermittent characters). This can be combined with a to search for patterns in both wide and ascii.

    +
  • +
  • +

    Fullword [f]

    +

    Match subsignature as a fullword (delimited by non-alphanumeric characters).

    +
  • +
  • +

    Ascii [a]

    +

    Match subsignature as ascii characters. This can be combined with w to search for patterns in both ascii and wide.

    +
  • +
+

Examples:

+
    +
  • Match 'AAAA'(nocase) and 'BBBBBB'(nocase)
  • +
+
clamav-nocase-A;Engine:81-255,Target:0;0&1;41414141::i;424242424242::i
+
+
    +
  • Match 'AAA' and 'hello'(fullword)
  • +
+
clamav-fullword-A;Engine:81-255,Target:0;0&1;414141;68656c6c6f::f
+
+
    +
  • Match 'AAA' and 'hello'(fullword nocase)
  • +
+
clamav-fullword-B;Engine:81-255,Target:0;0&1;414141;68656c6c6f::fi
+
+
    +
  • Match 'AAA' and 'hello'(wide ascii)
  • +
+
clamav-wide-B2;Engine:81-255,Target:0;0&1;414141;68656c6c6f::wa
+
+
    +
  • Match 'AAA' and 'hello'(nocase wide fullword ascii)
  • +
+
clamav-wide-C0;Engine:81-255,Target:0;0&1;414141;68656c6c6f::iwfa
+
+

Special Subsignature Types

+

Macro subsignatures

+

Introduced in ClamAV 0.96

+

Format: ${min-max}MACROID$

+

Macro subsignatures are used to combine a number of existing extended +signatures (.ndb) into a on-the-fly generated alternate string logical +signature (.ldb). Signatures using macro subsignatures require +Engine:51-255 for backwards-compatibility.

+

Example:

+
test.ldb:
+    TestMacro;Engine:51-255,Target:0;0&1;616161;${6-7}12$
+
+test.ndb:
+    D1:0:$12:626262
+    D2:0:$12:636363
+    D3:0:$30:626264
+
+

The example logical signature TestMacro is functionally equivalent +to:

+
TestMacro;Engine:51-255,Target:0;0;616161{3-4}(626262|636363)
+
+
    +
  • +

    MACROID points to a group of signatures; there can be at most 32 macro groups.

    +
      +
    • In the example, MACROID is 12 and both D1 and D2 are members of macro group 12. D3 is a member of separate macro group 30.
    • +
    +
  • +
  • +

    {min-max} specifies the offset range at which one of the group signatures should match; the offset range is relative to the starting offset of the preceding subsignature. This means a macro subsignature cannot be the first subsignature.

    +
      +
    • In the example, {min-max} is {6-7} and it is relative to the start of a 616161 match.
    • +
    +
  • +
  • +

    For more information and examples please see https://bugzilla.clamav.net/show_bug.cgi?id=164.

    +
  • +
+

Byte Compare Subsignatures

+

Introduced in ClamAV 0.101

+

Format: subsigid_trigger(offset#byte_options#comparisons)

+

Byte compare subsignatures can be used to evaluate a numeric value at a given offset from the start of another (matched) subsignature within the same logical signature. These are executed after all other subsignatures within the logical subsignature are fired, with the exception of PCRE subsignatures. They can evaluate offsets only from a single referenced subsignature, and that subsignature must give a valid match for the evaluation to occur.

+
    +
  • +

    subsigid_trigger is a required field and may refer to any single non-PCRE, non-Byte Compare subsignature within the lsig. The byte compare subsig will evaluate if subsigid_trigger matches. Triggering on multiple subsigs or logic based triggering is not currently supported.

    +
  • +
  • +

    offset is a required field that consists of an offset_modifier and a numeric offset (hex or decimal offsets are okay).

    +
      +
    • +

      offset_modifier can be either >> or << where the former denotes a positive offset and the latter denotes a negative offset. The offset is calculated from the start of subsigid_trigger, which allows for byte extraction before the specified match, after the match, and within the match itself.

      +
    • +
    • +

      offset must be a positive hex or decimal value. This will be the number of bytes from the start of the referenced subsigid_trigger match within the file buffer to begin the comparison.

      +
    • +
    +
  • +
  • +

    byte_options are used to specify the numeric type and endianess of the extracted byte sequence in that order as well as the number of bytes to be read. By default ClamAV will attempt to matchup up to the number of byte specified, unless the e (exact) option is specified or the numeric type is b (binary). This field follows the form [h|d|a|i][l|b][e]num_bytes

    +
      +
    • +

      h|d|a|i where h specifies the byte sequence will be in hex, d decimal, a automatic detection of hex or decimal at runtime, and i signifies raw binary data.

      +
    • +
    • +

      l|b where l specifies the byte sequence will be in little endian order and b big endian. If decimal d is specified, big-endian is implied and using l will result in a malformed database error.

      +
    • +
    • +

      e specifies that ClamAV will only evaluate the comparison if it can extract the exact number of bytes specified. This option is implicitly declared when using the i flag.

      +
    • +
    • +

      num_bytes specifies the number of bytes to extract. This can be a hex or decimal value. If i is specified only 1, 2, 4, and 8 are valid options.

      +
    • +
    +
  • +
  • +

    comparisons are a required field which denotes how to evaluate the extracted byte sequence. Each Byte Compare signature can have one or two comparison_sets separated by a comma. Each comparison_set consists of a Comparison_symbol and a Comparison_value and takes the form Comparison_symbolComparison_value. Thus, comparisons takes the form comparison_set[,comparison_set]

    +
      +
    • +

      Comparison_symbol denotes the type of comparison to be done. The supported comparison symbols are <, >, =.

      +
    • +
    • +

      Comparison_value is a required field which must be a numeric hex or decimal value. If all other conditions are met, the byte compare subsig will evalutate the extracted byte sequence against this number based on the provided comparison_symbol.

      +
    • +
    +
  • +
+

PCRE subsignatures

+

Introduced in ClamAV 0.99

+

Format: Trigger/PCRE/[Flags]

+

PCRE subsignatures are used within a logical signature (.ldb) to specify regex matches that execute once triggered by a conditional based on preceding subsignatures. Signatures using PCRE subsignatures require Engine:81-255 for backwards-compatibility.

+
    +
  • +

    Trigger is a required field that is a valid LogicalExpression and may refer to any subsignatures that precede this subsignature. Triggers cannot be self-referential and cannot refer to subsequent subsignatures.

    +
  • +
  • +

    PCRE is the expression representing the regex to execute. PCRE must be delimited by ’/’ and usage of ’/’ within the expression need to be escaped. For backward compatibility, ’;’ within the expression must be expressed as ’\x3B’. PCRE cannot be empty and (?UTF*) control sequence is not allowed. If debug is specified, named capture groups are displayed in a post-execution report.

    +
  • +
  • +

    Flags are a series of characters which affect the compilation and execution of PCRE within the PCRE compiler and the ClamAV engine. This field is optional.

    +
      +
    • +

      g [CLAMAV_GLOBAL] specifies to search for ALL matches of PCRE (default is to search for first match). NOTE: INCREASES the time needed to run the PCRE.

      +
    • +
    • +

      r [CLAMAV_ROLLING] specifies to use the given offset as the starting location to search for a match as opposed to the only location; applies to subsigs without maxshifts. By default, in order to facilatate normal ClamAV offset behavior, PCREs are auto-anchored (only attempt match on first offset); using the rolling option disables the auto-anchoring.

      +
    • +
    • +

      e [CLAMAV_ENCOMPASS] specifies to CONFINE matching between the specified offset and maxshift; applies only when maxshift is specified.

      +
      +

      Note: DECREASES time needed to run the PCRE.

      +
      +
    • +
    • +

      i [PCRE_CASELESS]

      +
    • +
    • +

      s [PCRE_DOTALL]

      +
    • +
    • +

      m [PCRE_MULTILINE]

      +
    • +
    • +

      x [PCRE_EXTENDED]

      +
    • +
    • +

      A [PCRE_ANCHORED]

      +
    • +
    • +

      E [PCRE_DOLLAR_ENODNLY]

      +
    • +
    • +

      U [PCRE_UNGREEDY]

      +
    • +
    +
  • +
+

Examples:

+
Find.All.ClamAV;Engine:81-255,Target:0;1;6265676c6164697427736e6f7462797465636f6465;0/clamav/g
+
+Find.ClamAV.OnlyAt.299;Engine:81-255,Target:0;2;7374756c747a67657473;7063726572656765786c6f6c;299:0&1/clamav/
+
+Find.ClamAV.StartAt.300;Engine:81-255,Target:0;3;616c61696e;62756731393238;636c6f736564;300:0&1&2/clamav/r
+
+Find.All.Encompassed.ClamAV;Engine:81-255,Target:0;3;7768796172656e2774;796f757573696e67;79617261;200,300:0&1&2/clamav/ge
+
+Named.CapGroup.Pcre;Engine:81-255,Target:0;3;636f75727479617264;616c62756d;74657272696572;50:0&1&2/variable=(?&lt;nilshell&gt;.{16})end/gr
+
+Firefox.TreeRange.UseAfterFree;Engine:81-255,Target:0,Engine:81-255;0&1&2;2e766965772e73656c656374696f6e;2e696e76616c696461746553656c656374696f6e;0&1/\x2Eview\x2Eselection.*?\x2Etree\s*\x3D\s*null.*?\x2Einvalidate/smi
+
+Firefox.IDB.UseAfterFree;Engine:81-255,Target:0;0&1;4944424b657952616e6765;0/^\x2e(only|lowerBound|upperBound|bound)\x28.*?\x29.*?\x2e(lower|upper|lowerOpen|upperOpen)/smi
+
+Firefox.boundElements;Engine:81-255,Target:0;0&1&2;6576656e742e6
+26f756e64456c656d656e7473;77696e646f772e636c6f7365;0&1/on(load|click)\s*=\s*\x22?window\.close\s*\x28/si
+
+

Signatures for Version Information (VI) metadata in PE files

+

Starting with ClamAV 0.96 it is possible to easily match certain information built into PE files (executables and dynamic link libraries). Whenever you lookup the properties of a PE executable file in windows, you are presented with a bunch of details about the file itself.

+

These info are stored in a special area of the file resources which goes under the name of VS_VERSION_INFORMATION (or versioninfo for short). It is divided into 2 parts. The first part (which is rather uninteresting) is really a bunch of numbers and flags indicating the product and file version. It was originally intended for use with installers which, after parsing it, should be able to determine whether a certain executable or library are to be upgraded/overwritten or are already up to date. Suffice to say, this approach never really worked and is generally never used.

+

The second block is much more interesting: it is a simple list of key/value strings, intended for user information and completely ignored by the OS. For example, if you look at ping.exe you can see the company being "Microsoft Corporation", the description "TCP/IP Ping command", the internal name "ping.exe" and so on... Depending on the OS version, some keys may be given peculiar visibility in the file properties dialog, however they are internally all the same.

+

To match a versioninfo key/value pair, the special file offset anchor VI was introduced. This is similar to the other anchors (like EP and SL) except that, instead of matching the hex pattern against a single offset, it checks it against each and every key/value pair in the file. The VI token doesn’t need nor accept a +/- offset like e.g. EP+1. As for the hex signature itself, it’s just the utf16 dump of the key and value. Only the ?? and (aa|bb) wildcards are allowed in the signature. Usually, you don’t need to bother figuring it out: each key/value pair together with the corresponding VI-based signature is printed by clamscan when the --debug option is given.

+

For example clamscan --debug freecell.exe produces:

+
[...]
+Recognized MS-EXE/DLL file
+in cli_peheader
+versioninfo_cb: type: 10, name: 1, lang: 410, rva: 9608
+cli_peheader: parsing version info @ rva 9608 (1/1)
+VersionInfo (d2de): 'CompanyName'='Microsoft Corporation' -
+VI:43006f006d00700061006e0079004e0061006d006500000000004d006900
+630072006f0073006f0066007400200043006f00720070006f0072006100740
+069006f006e000000
+VersionInfo (d32a): 'FileDescription'='Entertainment Pack
+FreeCell Game' - VI:460069006c006500440065007300630072006900700
+0740069006f006e000000000045006e007400650072007400610069006e006d
+0065006e00740020005000610063006b0020004600720065006500430065006
+c006c002000470061006d0065000000
+VersionInfo (d396): 'FileVersion'='5.1.2600.0 (xpclient.010817
+-1148)' - VI:460069006c006500560065007200730069006f006e00000000
+0035002e0031002e0032003600300030002e003000200028007800700063006
+c00690065006e0074002e003000310030003800310037002d00310031003400
+380029000000
+VersionInfo (d3fa): 'InternalName'='freecell' - VI:49006e007400
+650072006e0061006c004e0061006d006500000066007200650065006300650
+06c006c000000
+VersionInfo (d4ba): 'OriginalFilename'='freecell' - VI:4f007200
+6900670069006e0061006c00460069006c0065006e0061006d0065000000660
+0720065006500630065006c006c000000
+VersionInfo (d4f6): 'ProductName'='Sistema operativo Microsoft
+Windows' - VI:500072006f0064007500630074004e0061006d00650000000
+000530069007300740065006d00610020006f00700065007200610074006900
+76006f0020004d006900630072006f0073006f0066007400ae0020005700690
+06e0064006f0077007300ae000000
+VersionInfo (d562): 'ProductVersion'='5.1.2600.0' - VI:50007200
+6f006400750063007400560065007200730069006f006e00000035002e00310
+02e0032003600300030002e0030000000
+[...]
+
+

Although VI-based signatures are intended for use in logical signatures you can test them using ordinary .ndb files. For example:

+
my_test_vi_sig:1:VI:paste_your_hex_sig_here
+
+

Final note. If you want to decode a VI-based signature into a human readable form you can use:

+
echo hex_string | xxd -r -p | strings -el
+
+

For example:

+
echo 460069006c0065004400650073006300720069007000740069006f006e000000000045006e007400650072007400610069006e006d0065006e00740020005000610063006b0020004600720065006500430065006c006c00200047006100
+6d0065000000 | xxd -r -p | strings -el
+FileDescription
+Entertainment Pack FreeCell Game
+
+

Icon Signatures for PE files

+

While Icon Signatures are stored in a .idb file, they are a feature of Logical Signatures.

+

ClamAV 0.96 includes an approximate/fuzzy icon matcher to help detecting malicious executables disguising themselves as innocent looking image files, office documents and the like.

+

Icon matching is only triggered by Logical Signatures (.ldb) using the special attribute tokens IconGroup1 or IconGroup2. These identify two (optional) groups of icons defined in a .idb database file. The format of the .idb file is:

+
ICONNAME:GROUP1:GROUP2:ICON_HASH
+
+

where:

+
    +
  • +

    ICON_NAME is a unique string identifier for a specific icon,

    +
  • +
  • +

    GROUP1 is a string identifier for the first group of icons (IconGroup1)

    +
  • +
  • +

    GROUP2 is a string identifier for the second group of icons (IconGroup2),

    +
  • +
  • +

    ICON_HASH is a fuzzy hash of the icon image

    +
  • +
+

The ICON_HASH field can be obtained from the debug output of libclamav. For example:

+
LibClamAV debug: ICO SIGNATURE:
+ICON_NAME:GROUP1:GROUP2:18e2e0304ce60a0cc3a09053a30000414100057e000afe0000e 80006e510078b0a08910d11ad04105e0811510f084e01040c080a1d0b0021000a39002a41
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Signatures/PhishSigs.html clamav-0.103.5+dfsg/docs/html/manual/Signatures/PhishSigs.html --- clamav-0.103.2+dfsg/docs/html/manual/Signatures/PhishSigs.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Signatures/PhishSigs.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,822 @@ + + + + + + Phishing Signatures - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Phishing Signatures

+

Table of Contents

+ +

Database file format

+

PDB format

+

This file contains urls/hosts that are target of phishing attempts. It contains lines in the following format:

+
R[Filter]:RealURL:DisplayedURL[:FuncLevelSpec]
+H[Filter]:DisplayedHostname[:FuncLevelSpec]
+
+
    +
  • +

    R

    +

    Regular expression, for the concatenated URL. The last 3 characters of the regular expression cannot regex special characters and much be an exact match.

    +
  • +
  • +

    H

    +

    Matches the DisplayedHostname as a simple pattern (literally, no regular expression).

    +
      +
    • +

      The pattern can match either the full hostname.

      +
    • +
    • +

      Or a subdomain of the specified hostname.

      +
    • +
    • +

      To avoid false matches in case of subdomain matches, the engine checks that there is a dot(.) or a space( ) before the matched portion.

      +
    • +
    +
  • +
  • +

    Filter

    +

    Is ignored for R and H for compatibility reasons.

    +
  • +
  • +

    RealURL

    +

    Is the URL the user is sent to, example: href attribute of an html anchor (<a> tag).

    +
  • +
  • +

    DisplayedURL

    +

    Is the URL description displayed to the user, where its claimed they are sent, example: contents of an html anchor (<a> tag).

    +
  • +
  • +

    DisplayedHostname

    +

    Is the hostname portion of the DisplayedURL.

    +
  • +
  • +

    FuncLevelSpec

    +

    An (optional) functionality level, 2 formats are possible:

    +
      +
    • +

      minlevel all engines having functionality level >= minlevel will load this line.

      +
    • +
    • +

      minlevel-maxlevel engines with functionality level >= minlevel, and < maxlevel will load this line.

      +
    • +
    +
  • +
+

GDB format

+

This file contains URL hashes in the following format:

+
S:P:HostPrefix[:FuncLevelSpec]
+S:F:Sha256hash[:FuncLevelSpec]
+S1:P:HostPrefix[:FuncLevelSpec]
+S1:F:Sha256hash[:FuncLevelSpec]
+S2:P:HostPrefix[:FuncLevelSpec]
+S2:F:Sha256hash[:FuncLevelSpec]
+S:W:Sha256hash[:FuncLevelSpec]
+
+
    +
  • +

    S:

    +

    These are hashes for Google Safe Browsing - malware sites, and should not be used for other purposes.

    +
  • +
  • +

    S2:

    +

    These are hashes for Google Safe Browsing - phishing sites, and should not be used for other purposes.

    +
  • +
  • +

    S1:

    +

    Hashes for blocking phishing sites. Virus name: Phishing.URL.Blocked.

    +
  • +
  • +

    S:W:

    +

    Locally allowed hashes.

    +
  • +
  • +

    HostPrefix

    +

    4-byte prefix of the sha256 hash of the last 2 or 3 components of the hostname. If prefix doesn’t match, no further lookups are performed.

    +
  • +
  • +

    Sha256hash

    +

    sha256 hash of the canonicalized URL, or a sha256 hash of its prefix/suffix according to the Google Safe Browsing “Performing Lookups” rules. There should be a corresponding :P:HostkeyPrefix entry for the hash to be taken into consideration.

    +
  • +
+

To see which hash/URL matched, look at the clamscan --debug output, and look for the following strings: Looking up hash, prefix matched, and Hash matched. To ignore .gdb entries, create a local.gdb file, and adding a line S:W:<HASH>.

+

WDB format

+

This file contains url pairs for links that may look suspicious but are safe and should be allowed. It contains lines in the following format:

+
X:RealURL:DisplayedURL[:FuncLevelSpec]
+M:RealHostname:DisplayedHostname[:FuncLevelSpec]
+
+
    +
  • +

    X

    +

    Regular expression, for the entire URL, not just the hostname.

    +
      +
    • +

      The regular expression is by default anchored to start-of-line and end-of-line, as if you have used ^RegularExpression$

      +
    • +
    • +

      A trailing / is automatically added both to the regex, and the input string to avoid false matches.

      +
    • +
    • +

      The regular expression matches the concatenation of the RealURL, a colon(:), and the DisplayedURL as a single string. It doesn’t separately match RealURL and DisplayedURL!

      +
    • +
    • +

      The last 3 characters of the regular expression cannot regex special characters and much be an exact match.

      +
    • +
    +
  • +
  • +

    M

    +

    Matches hostname, or subdomain of it, see notes for H above.

    +
  • +
+

Hints

+
    +
  • +

    Empty lines are ignored

    +
  • +
  • +

    The colons are mandatory

    +
  • +
  • +

    Don’t leave extra spaces on the end of a line!

    +
  • +
  • +

    If any of the lines don’t conform to this format, ClamAV will abort with a Malformed Database Error

    +
  • +
  • +

    See section Extraction-of-RealURL for more details on RealURL/DisplayedURL

    +
  • +
+

Examples of PDB signatures

+

To check for phishing mails that target amazon.com, or subdomains of amazon.com:

+
H:amazon.com
+
+

To do the same, but for amazon.co.uk:

+
H:amazon.co.uk
+
+

You can limit the signatures to certain engine versions. For example...

+
    +
  1. +

    Restrict so that engine versions 20 through 30 can load it, but not 31+:

    +
    H:amazon.co.uk:20-30
    +
    +
  2. +
  3. +

    Restrict so that engine versions >= 20 can load it:

    +
    H:amazon.co.uk:20-
    +
    +
  4. +
  5. +

    Restrict so that engine versions <= 20 can load it:

    +
    H:amazon.co.uk:0-20
    +
    +
  6. +
+

In a real situation, you’d probably use the second form. A situation like that would be if you are using a feature of the signatures not available in earlier versions, or if earlier versions have bugs with your signature. Its neither case here, the above examples are for illustrative purposes only.

+

Examples of WDB signatures

+

To allow Amazon’s country specific domains and amazon.com, to mix domain names in DisplayedURL, and RealURL:

+
X:.+\.amazon\.(at|ca|co\.uk|co\.jp|de|fr)([/?].*)?:.+\.amazon\.com([/?].*)?:17-
+
+

Explanation of this signature:

+
    +
  • +

    X:

    +

    this is a regular expression

    +
  • +
  • +

    :17-

    +

    load signature only for engines with functionality level >= 17

    +
  • +
+

The regular expression is the following (X:, :17- stripped, and a / appended)

+
.+\.amazon\.(at|ca|co\.uk|co\.jp|de|fr)([/?].*)?:.+\.amazon\.com([/?].*)?/
+
+

Explanation of this regular expression (note that it is a single regular expression, and not 2 regular expressions splitted at the :).

+
    +
  • +

    .+

    +

    any subdomain of

    +
  • +
  • +

    \.amazon\.

    +

    domain we are allowing (RealURL part)

    +
  • +
  • +

    (at|ca|co\.uk|co\.jp|de|fr)

    +

    country-domains: at, ca, co.uk, co.jp, de, fr

    +
  • +
  • +

    ([/?].*)?

    +

    recomended way to end the real-url, this protects against embedded URLs (evilurl.example.com/amazon.co.uk/)

    +
  • +
  • +

    :

    +

    RealURL and DisplayedURL are concatenated via a :, so match a literal : here

    +
  • +
  • +

    .+

    +

    any subdomain of

    +
  • +
  • +

    \.amazon\.com

    +

    allowed DisplayedURL

    +
  • +
  • +

    ([/?].*)?

    +

    recommended way to end displayed url part, to protect against embedded URLs

    +
  • +
  • +

    /

    +

    automatically added to further protect against embedded URLs

    +
  • +
+

When you add an entry, make sure you check that both domains are owned by the same entity. This signature allows links claiming to point to amazon.com (DisplayedURL), when in fact they really go to a country-specific domain of amazon (RealURL).

+

Example for how the URL extractor works

+

Consider the following HTML file:

+
<html>
+<a href="http://1.realurl.example.com/">
+  1.displayedurl.example.com
+</a>
+<a href="http://2.realurl.example.com">
+  2 d<b>i<p>splayedurl.e</b>xa<i>mple.com
+</a>
+<a href="http://3.realurl.example.com">
+  3.nested.example.com
+  <a href="http://4.realurl.example.com">
+    4.displayedurl.example.com
+  </a>
+</a>
+<form action="http://5.realurl.example.com">
+  sometext
+  <img src="http://5.displayedurl.example.com/img0.gif"/>
+  <a href="http://5.form.nested.displayedurl.example.com">
+    5.form.nested.link-displayedurl.example.com
+  </a>
+</form>
+<a href="http://6.realurl.example.com">
+  6.displ
+  <img src="6.displayedurl.example.com/img1.gif"/>
+  ayedurl.example.com
+</a>
+<a href="http://7.realurl.example.com">
+  <iframe src="http://7.displayedurl.example.com">
+</a>
+
+

The phishing engine extract the following +RealURL/DisplayedURL pairs from it:

+
http://1.realurl.example.com/
+1.displayedurl.example.com
+
+http://2.realurl.example.com
+2displayedurl.example.com
+
+http://3.realurl.example.com
+3.nested.example.com
+
+http://4.realurl.example.com
+4.displayedurl.example.com
+
+http://5.realurl.example.com
+http://5.displayedurl.example.com/img0.gif
+
+http://5.realurl.example.com
+http://5.form.nested.displayedurl.example.com
+
+http://5.form.nested.displayedurl.example.com
+5.form.nested.link-displayedurl.example.com
+
+http://6.realurl.example.com
+6.displayedurl.example.com
+
+http://6.realurl.example.com
+6.displayedurl.example.com/img1.gif
+
+

How matching works

+

RealURL, DisplayedURL concatenation

+

The phishing detection module processes pairs of RealURL/DisplayedURL. Matching against daily.wdb is done as follows: the RealURL is concatenated with a :, and with the DisplayedURL, then that line is matched against the lines in daily.wdb/daily.pdb

+

So if you have this line in daily.wdb:

+
M:www.google.ro:www.google.com
+
+

This href: <a href='http://www.google.ro'>www.google.com</a> then it will be allowed, but: <a href='http://images.google.com'>www.google.com</a> will not.

+

What happens when a match is found

+

In the case of the allow list, a match means that the RealURL/DisplayedURL combination is considered clean, and no further checks are performed on it.

+

In the case of the domain list, a match means that the RealURL/DisplayedURL is going to be checked for phishing attempts.

+

Furthermore you can restrict what checks are to be performed by specifying the 3-digit hexnumber.

+

Extraction of RealURL, DisplayedURL from HTML tags

+

The html parser extracts pairs of RealURL/DisplayedURL based on the following rules.

+

After URLs have been extracted, they are normalized, and cut after the hostname. http://test.example.com/path/somecgi?queryparameters becomes http://test.example.com/

+
    +
  • +

    a

    +

    (anchor) the href is the RealURL, its contents is the DisplayedURL

    +
      +
    • +

      contents

      +

      is the tag-stripped contents of the <a> tags, so for example <b> tags are stripped (but not their contents)

      +
    • +
    +

    nesting another <a> tag withing an <a> tag (besides being invalid html) is treated as a </a><a...

    +
  • +
  • +

    form

    +

    the action attribute is the RealURL, and a nested <a> tag is the DisplayedURL.

    +
  • +
  • +

    img/area

    +

    if nested within an <a> tag, the RealURL is the href of the a tag, and the src/dynsrc/area is the DisplayedURL of the img

    +

    if nested withing a form tag, then the action attribute of the form tag is the RealURL.

    +
  • +
  • +

    iframe

    +

    if nested withing an <a> tag the src attribute is the DisplayedURL, and the href of its parent a tag is the RealURL.

    +

    if nested withing a form tag, then the action attribute of the form tag is the RealURL.

    +
  • +
+

Example

+

Consider this html file:

+
<a href=”evilurl”>www.paypal.com</a>
+<a href=”evilurl2” title=”www.ebay.com”>click here to sign in</a>
+<form action=”evilurl_form”>
+Please sign in to <a href=”cgi.ebay.com”>Ebay</a>using this form
+<input type=’text’ name=’username’>Username</input>
+....
+</form>
+<a href=”evilurl”><img src=”images.paypal.com/secure.jpg”></a>
+
+

The resulting RealURL/DisplayedURL pairs will be (note that one tag can generate multiple pairs):

+
    +
  • +

    evilurl / www.paypal.com

    +
  • +
  • +

    evilurl2 / click here to sign in

    +
  • +
  • +

    evilurl2 / www.ebay.com

    +
  • +
  • +

    evilurl_form / cgi.ebay.com

    +
  • +
  • +

    cgi.ebay.com / Ebay

    +
  • +
  • +

    evilurl / image.paypal.com/secure.jpg

    +
  • +
+

Simple patterns

+

Simple patterns are matched literally, i.e. if you say:

+
www.google.com
+
+

it is going to match www.google.com, and only that. The . (dot) character has no special meaning (see the section on regexes [sec:Regular-expressions] for how the .(dot) character behaves there)

+

Regular expressions

+

POSIX regular expressions are supported, and you can consider that internally it is wrapped by ^, and $. In other words, this means that the regular expression has to match the entire concatenated (see section RealURL,-DisplayedURL-concatenation for details on concatenation) url.

+

It is recomended that you read section Introduction-to-regular to learn how to write regular expressions, and then come back and read this for hints.

+

Be advised that clamav contains an internal, very basic regex matcher to reduce the load on the regex matching core. Thus it is recomended that you avoid using regex syntax not supported by it at the very beginning of regexes (at least the first few characters).

+

Currently the clamav regex matcher supports:

+
    +
  • +

    . (dot) character

    +
  • +
  • +

    \ (escaping special characters)

    +
  • +
  • +

    | (pipe) alternatives

    +
  • +
  • +

    [] (character classes)

    +
  • +
  • +

    () (parenthesis for grouping, but no group extraction is performed)

    +
  • +
  • +

    other non-special characters

    +
  • +
+

Thus the following are not supported:

+
    +
  • +

    + repetition

    +
  • +
  • +

    * repetition

    +
  • +
  • +

    {} repetition

    +
  • +
  • +

    backreferences

    +
  • +
  • +

    lookaround

    +
  • +
  • +

    other “advanced” features not listed in the supported list ;)

    +
  • +
+

This however shouldn’t discourage you from using the “not directly supported features “, because if the internal engine encounters unsupported syntax, it passes it on to the POSIX regex core (beginning from the first unsupported token, everything before that is still processed by the internal matcher). An example might make this more clear:

+
*www\(\backslash\).google\(\backslash\).(com|ro|it) (\[a-zA-Z\])+\(\backslash\).google\(\backslash\).(com|ro|it)*
+
+

Everything till (\[a-zA-Z\])+ is processed internally, that parenthesis (and everything beyond) is processed by the posix core.

+

Examples of url pairs that match:

+
    +
  • +

    www.google.ro images.google.ro

    +
  • +
  • +

    www.google.com images.google.ro

    +
  • +
+

Example of url pairs that don’t match:

+
    +
  • +

    www.google.ro images1.google.ro

    +
  • +
  • +

    images.google.com image.google.com

    +
  • +
+

Flags

+

Flags are a binary OR of the following numbers:

+ + + + + + + + + + + +
FlagValue
HOST_SUFFICIENT1
DOMAIN_SUFFICIENT2
DO_REVERSE_LOOKUP4
CHECK_REDIR8
CHECK_SSL16
CHECK_CLOAKING32
CLEANUP_URL64
CHECK_DOMAIN_REVERSE128
CHECK_IMG_URL256
DOMAINLIST_REQUIRED512
+

The names of the constants are self-explanatory.

+

These constants are defined in libclamav/phishcheck.h, you can check there for the latest flags.

+

There is a default set of flags that are enabled, these are currently:

+
( CLEANUP_URL | CHECK_SSL | CHECK_CLOAKING | CHECK_IMG_URL )
+
+

ssl checking is performed only for a tags currently.

+

You must decide for each line in the domain list if you want to filter any flags (that is you don’t want certain checks to be done), and then calculate the binary OR of those constants, and then convert it into a 3-digit hexnumber. For example you devide that domain_sufficient shouldn’t be used for ebay.com, and you don’t want to check images either, so you come up with this flag number: 2|256 => 258(decimal) => 102(hexadecimal)

+

So you add this line to daily.wdb:

+
R102 www.ebay.com .+
+
+

Introduction to regular expressions

+

Recomended reading:

+ +

Special characters

+
    +
  • +

    [

    +

    the opening square bracket - it marks the beginning of a character class, see sectionCharacter-classes

    +
  • +
  • +

    \

    +

    the backslash - escapes special characters, see section Escaping

    +
  • +
  • +

    ^

    +

    the caret - matches the beginning of a line (not needed in clamav regexes, this is implied)

    +
  • +
  • +

    $

    +

    the dollar sign - matches the end of a line (not needed in clamav regexes, this is implied)

    +
  • +
  • +

    .

    +

    the period or dot - matches any character

    +
  • +
  • +

    |

    +

    the vertical bar or pipe symbol - matches either of the token on its left and right side, see Alternation

    +
  • +
  • +

    ?

    +

    the question mark - matches optionally the left-side token, see Optional-matching, and Repetition

    +
  • +
  • +

    *

    +

    the asterisk or star - matches 0 or more occurences of the left-side token, see Optional-matching, and Repetition

    +
  • +
  • +

    +

    +

    the plus sign - matches 1 or more occurences of the left-side token, see Optional-matching, and Repetition

    +
  • +
  • +

    (

    +

    the opening round bracket - marks beginning of a group, see section Groups

    +
  • +
  • +

    )

    +

    the closing round bracket - marks end of a group, see sectionGroups

    +
  • +
+

Character classes

+

Escaping

+

Escaping has two purposes:

+
    +
  • +

    It allows you to actually match the special characters themselves, for example to match the literal +, you would write \+

    +
  • +
  • +

    It also allows you to match non-printable characters, such as the tab (\t) and newline (\n)

    +
  • +
+

However since non-printable characters are not valid inside an url, you won’t have a reason to use them.

+

Alternation

+

Optional matching, and repetition

+

Groups

+

Groups are usually used together with repetition, or alternation. For example: (com|it)+ means: match 1 or more repetitions of com or it, that is it matches: com, it, comcom, comcomcom, comit, itit, ititcom,... you get the idea.

+

Groups can also be used to extract substring, but this is not supported by the ClamAV, and not needed either in this case.

+

How to create database files

+

How to create and maintain the allow list (daily.wdb)

+

If the phishing code claims that a certain mail is phishing, but it's not, you have 2 choices:

+
    +
  • +

    Examine your rules daily.pdb, and fix them if necessary (see: How to create database files)

    +
  • +
  • +

    Add it to the allow list (discussed here)

    +
  • +
+

Lets assume you are having problems because of links like this in a mail:

+
<a href=''http://69.0.241.57/bCentral/L.asp?L=XXXXXXXX''>`
+  http://www.bcentral.it/`
+</a>`
+
+

After investigating those sites further, you decide they are no threat, and create a line like this in daily.wdb:

+
R http://www\(\backslash\).bcentral\(\backslash\).it/.+
+http://69\(\backslash\).0\(\backslash\).241\(\backslash\).57/bCentral/L\(\backslash\).asp?L=.+
+
+
+

Note: urls like the above can be used to track unique mail recipients, and thus know if somebody actually reads mails (so they can send more spam). However since this site required no authentication information, it is safe from a phishing point of view.

+
+

How to create and maintain the domain list (daily.pdb)

+

When not using --phish-scan-alldomains (production environments for example), you need to decide which urls you are going to check.

+

Although at a first glance it might seem a good idea to check everything, it would produce false positives. Particularly newsletters, ads, etc. are likely to use URLs that look like phishing attempts.

+

Lets assume that you’ve recently seen many phishing attempts claiming they come from Paypal. Thus you need to add paypal to daily.pdb:

+
R .+ .+\(\backslash\).paypal\(\backslash\).com
+
+

The above line will block (detect as phishing) mails that contain urls that claim to lead to paypal, but they don’t in fact.

+

Be careful not to create regexes that match a too broad range of urls though.

+

Dealing with false positives, and undetected phishing mails

+

False positives

+

Whenever you see a false positive (mail that is detected as phishing, but its not), ou might need to modify daily.pdb (if one of yours rules in there are too broad), or you need to add the url to daily.wdb. If you think the algorithm is incorrect, please file a bug report.

+

Undetected phish mails

+

If the mail is detected, if yes, then you need to add an appropriate line to daily.pdb (see How to create database files).

+

If the mail is not detected, then try using:

+
$clamscan/clamscan --debug undetected.eml | less
+
+

Then see what urls are being checked, see if any of them is in an allow list, see if all urls are detected, etc.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Signatures/YaraRules.html clamav-0.103.5+dfsg/docs/html/manual/Signatures/YaraRules.html --- clamav-0.103.2+dfsg/docs/html/manual/Signatures/YaraRules.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Signatures/YaraRules.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,246 @@ + + + + + + YARA Rules - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Using YARA rules in ClamAV

+

ClamAV can process YARA rules. ClamAV virus database file names ending with .yar or .yara are parsed as YARA rule files. The link to the YARA rule grammar documentation may be found at https://virustotal.github.io/yara/. There are currently a few limitations on using YARA rules within ClamAV:

+
    +
  • +

    YARA modules are not yet supported by ClamAV. This includes the “import” keyword and any YARA module-specific keywords.

    +
  • +
  • +

    Global rules (global keyword) are not supported by ClamAV.

    +
  • +
  • +

    External variables(contains and matches keywords) are not supported.

    +
  • +
  • +

    YARA rules pre-compiled with the yarac command are not supported.

    +
  • +
  • +

    As in the ClamAV logical and extended signature formats, YARA strings and segments of strings separated by wild cards must represent at least two octets of data.

    +
  • +
  • +

    There is a maximum of 64 strings per YARA rule.

    +
  • +
  • +

    YARA rules in ClamAV must contain at least one literal, hexadecimal, or regular expression string.

    +
  • +
+

In addition, there are a few more ClamAV processing modes that may affect the outcome of YARA rules.

+
    +
  • +

    File decomposition and decompression - Since ClamAV uses file decomposition and decompression to find viruses within de-archived and uncompressed inner files, YARA rules executed by ClamAV will match against these files as well.

    +
  • +
  • +

    Normalization - By default, ClamAV normalizes HTML, JavaScript, and ASCII text files. YARA rules in ClamAV will match against the normalized result. The effects of normalization of these file types may be captured using clamscan --leave-temps --tempdir=mytempdir. YARA rules may then be written using the normalized file(s) found in mytempdir. Alternatively, starting with ClamAV 0.100.0, clamscan --normalize=no will prevent normalization and only scan the raw file. To obtain similar behavior prior to 0.99.2, use clamscan --scan-html=no. The corresponding parameters for clamd.conf are Normalize and ScanHTML.

    +
  • +
  • +

    YARA conditions driven by string matches - All YARA conditions are driven by string matches in ClamAV. This saves from executing every YARA rule on every file. Any YARA condition may be augmented with a string match clause which is always true, such as:

    +
  • +
+
rule CheckFileSize
+{
+  strings:
+    $abc = "abc"
+  condition:
+    ($abc or not $abc) and filesize < 200KB
+}
+
+

This will ensure that the YARA condition always performs the desired action (checking the file size in this example),

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Signatures.html clamav-0.103.5+dfsg/docs/html/manual/Signatures.html --- clamav-0.103.2+dfsg/docs/html/manual/Signatures.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Signatures.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,564 @@ + + + + + + Signatures - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Creating signatures for ClamAV

+

Table Of Contents

+ +

Introduction

+

In order to detect malware and other file-based threats, ClamAV relies on signatures to differentiate clean and malicious/unwanted files. ClamAV signatures are primarily text-based and conform to one of the ClamAV-specific signature formats associated with a given method of detection. These formats are explained in the Signature formats section below. In addition, ClamAV 0.99 and above support signatures written in the YARA format. More information on this can be found in the Using YARA rules in ClamAV section.

+

The ClamAV project distributes a collection of signatures in the form of CVD (ClamAV Virus Database) files. The CVD file format provides a digitally-signed container that encapsulates the signatures and ensures that they can't be modified by a malicious third-party. This signature set is actively maintained by Cisco Talos and can be downloaded using the freshclam application that ships with ClamAV. For more details on this, see the CVD file section.

+

Database formats

+

ClamAV CVD and CLD database archives may be unpacked to the current directory using sigtool -u <database name>. For more details on inspecting CVD and CLD files, see Inspecting signatures inside a CVD file. Once unpacked, you'll observe a large collection of database files with various extensions described below.

+

The CVD and CLD database archives may be supplemented with custom database files in the formats described to gain additional detection functionality. This is done simply by adding files of the following formats to the database directory, typically /usr/local/share/clamav or "C:\Program Files\ClamAV\database". Alternatively, clamd and clamscan can be instructed to load the database from an alternative database file or database directory manually using the clamd DatabaseDirectory config option or the clamscan -d command line option.

+

Settings databases

+

ClamAV provides a handful of configuration related databases along side the signature definitions.

+

*.cfg: Dynamic config settings

+

*.cat *.crb: Trusted and revoked PE certs

+

*.ftm: File Type Magic (FTM)

+

Signature databases

+
+

Note: Signature databases with an extension ending in u are only loaded when Potentially Unwanted Application (PUA) signatures are enabled (default: off).

+
+

Body-based Signatures

+

Body-based signature content is a definition that matches not based on a hash but based on the specific sequences of bytes exhibited by the target file.

+

ClamAV body-based signature content has a special format to allow regex-like matching of data that is not entirely known. This format is used extensively in both Extended Signatures and Logical Signatures.

+

*.ndb *.ndu: Extended signatures

+

*.ldb *.ldu; *.idb: Logical Signatures

+

*.cdb: Container Metadata Signatures

+

*.cbc: Bytecode Signatures

+

*.pdb *.gdb *.wdb: Phishing URL Signatures)

+

Hash-based Signatures

+

*.hdb *.hsb *.hdu *.hsu: File hash signatures

+

*.mdb *.msb *.mdu *.msu: PE section hash signatures

+

Hash-based Signature format

+

Alternative signature support

+

*.yar *.yara: YARA rules

+

Other database files

+

*.fp *.sfp *.ign *.ign2: allowed files, ignored signatures

+

*.pwdb: Encrypted archive passwords

+

*.info: Database information`

+

Signature names

+

ClamAV uses the following prefixes for signature names:

+
    +
  • Worm for Internet worms
  • +
  • Trojan for backdoor programs
  • +
  • Adware for adware
  • +
  • Flooder for flooders
  • +
  • HTML for HTML files
  • +
  • Email for email messages
  • +
  • IRC for IRC trojans
  • +
  • JS for Java Script malware
  • +
  • PHP for PHP malware
  • +
  • ASP for ASP malware
  • +
  • VBS for VBS malware
  • +
  • BAT for BAT malware
  • +
  • W97M, W2000M for Word macro viruses
  • +
  • X97M, X2000M for Excel macro viruses
  • +
  • O97M, O2000M for generic Office macro viruses
  • +
  • DoS for Denial of Service attack software
  • +
  • DOS for old DOS malware
  • +
  • Exploit for popular exploits
  • +
  • VirTool for virus construction kits
  • +
  • Dialer for dialers
  • +
  • Joke for hoaxes
  • +
+

Important rules of the naming convention:

+
    +
  • Always use a -zippwd suffix in the malware name for ZIP container metadata (.cdb) signatures,
  • +
  • Always use a -rarpwd suffix in the malware name for RAR container metadata (.cdb) signatures,
  • +
  • Only use alphanumeric characters, dash (-), dot (.), underscores (_) in malware names, never use space, apostrophe or quote mark.
  • +
+

Signature Writing Tips and Tricks

+

Testing rules with clamscan

+

To test a new signature, first create a text file with the extension corresponding to the signature type (Ex: .ldb for logical signatures). Then, add the signature as it's own line within the file. This file can be passed to clamscan via the -d option, which tells ClamAV to load signatures from the file specified. If the signature is not formatted correctly, ClamAV will display an error - run clamscan with --debug --verbose to see additional information about the error message. Some common causes of errors include:

+
    +
  • The signature file has the incorrect extension type for the signatures contained within
  • +
  • The file has one or more blank lines
  • +
  • For logical signatures, a semicolon exists at the end of the file
  • +
+

If the rule is formatted correctly, clamscan will load the signature(s) in and scan any files specified via the command line invocation (or all files in the current directory if none are specified). A successful detection will look like the following:

+
clamscan -d test.ldb text.exe
+test.exe: Win.Malware.Agent.UNOFFICIAL FOUND
+
+----------- SCAN SUMMARY -----------
+Known viruses: 1
+Engine version: 0.100.0
+Scanned directories: 0
+Scanned files: 1
+Infected files: 1
+Data scanned: 17.45 MB
+Data read: 17.45 MB (ratio 1.00:1)
+Time: 0.400 sec (0 m 0 s)
+
+

If the rule did not match as intended:

+
    +
  • The file may have exceeded one or more of the default scanning limits built-in to ClamAV. Try running clamscan with the following options to see if raising the limits addresses the issue: --max-filesize=2000M --max-scansize=2000M --max-files=2000000 --max-recursion=2000000 --max-embeddedpe=2000M --max-htmlnormalize=2000000 --max-htmlnotags=2000000 --max-scriptnormalize=2000000 --max-ziptypercg=2000000 --max-partitions=2000000 --max-iconspe=2000000 --max-rechwp3=2000000 --pcre-match-limit=2000000 --pcre-recmatch-limit=2000000 --pcre-max-filesize=2000M.
  • +
  • If matching on HTML or text files, ClamAV might be performing normalization that causes the content of the scanned file to change. See the HTML and Text file sections for more details.
  • +
  • libclamav may have been unable to unpack or otherwise process the file. See Debug information from libclamav for more details.
  • +
+

NOTE: If you run clamscan with a -d flag, ClamAV will not load in the signatures downloaded via freshclam. This means that:

+
    +
  • some of ClamAV's unpacking support might be disabled, since some unpackers are implemented as bytecode signatures
  • +
  • PE certificate trust verification based on Authenticode signatures won't work, since this functionality relies on .crb rules
  • +
+

If any of this functionality is needed, load in the CVD files manually with additional -d flags.

+

Debug information from libclamav

+

In order to create efficient signatures for ClamAV it’s important to understand how the engine handles input files. The best way to see how it works is having a look at the debug information from libclamav. You can do it by calling clamscan with the --debug and --leave-temps flags. The first switch makes clamscan display all the interesting information from libclamav and the second one avoids deleting temporary files so they can be analyzed further.

+

The now important part of the info is:

+
    $ clamscan --debug attachment.exe
+    [...]
+    LibClamAV debug: Recognized MS-EXE/DLL file
+    LibClamAV debug: Matched signature for file type PE
+    LibClamAV debug: File type: Executable
+
+

The engine recognized a windows executable.

+
    LibClamAV debug: Machine type: 80386
+    LibClamAV debug: NumberOfSections: 3
+    LibClamAV debug: TimeDateStamp: Fri Jan 10 04:57:55 2003
+    LibClamAV debug: SizeOfOptionalHeader: e0
+    LibClamAV debug: File format: PE
+    LibClamAV debug: MajorLinkerVersion: 6
+    LibClamAV debug: MinorLinkerVersion: 0
+    LibClamAV debug: SizeOfCode: 0x9000
+    LibClamAV debug: SizeOfInitializedData: 0x1000
+    LibClamAV debug: SizeOfUninitializedData: 0x1e000
+    LibClamAV debug: AddressOfEntryPoint: 0x27070
+    LibClamAV debug: BaseOfCode: 0x1f000
+    LibClamAV debug: SectionAlignment: 0x1000
+    LibClamAV debug: FileAlignment: 0x200
+    LibClamAV debug: MajorSubsystemVersion: 4
+    LibClamAV debug: MinorSubsystemVersion: 0
+    LibClamAV debug: SizeOfImage: 0x29000
+    LibClamAV debug: SizeOfHeaders: 0x400
+    LibClamAV debug: NumberOfRvaAndSizes: 16
+    LibClamAV debug: Subsystem: Win32 GUI
+    LibClamAV debug: ------------------------------------
+    LibClamAV debug: Section 0
+    LibClamAV debug: Section name: UPX0
+    LibClamAV debug: Section data (from headers - in memory)
+    LibClamAV debug: VirtualSize: 0x1e000 0x1e000
+    LibClamAV debug: VirtualAddress: 0x1000 0x1000
+    LibClamAV debug: SizeOfRawData: 0x0 0x0
+    LibClamAV debug: PointerToRawData: 0x400 0x400
+    LibClamAV debug: Section's memory is executable
+    LibClamAV debug: Section's memory is writeable
+    LibClamAV debug: ------------------------------------
+    LibClamAV debug: Section 1
+    LibClamAV debug: Section name: UPX1
+    LibClamAV debug: Section data (from headers - in memory)
+    LibClamAV debug: VirtualSize: 0x9000 0x9000
+    LibClamAV debug: VirtualAddress: 0x1f000 0x1f000
+    LibClamAV debug: SizeOfRawData: 0x8200 0x8200
+    LibClamAV debug: PointerToRawData: 0x400 0x400
+    LibClamAV debug: Section's memory is executable
+    LibClamAV debug: Section's memory is writeable
+    LibClamAV debug: ------------------------------------
+    LibClamAV debug: Section 2
+    LibClamAV debug: Section name: UPX2
+    LibClamAV debug: Section data (from headers - in memory)
+    LibClamAV debug: VirtualSize: 0x1000 0x1000
+    LibClamAV debug: VirtualAddress: 0x28000 0x28000
+    LibClamAV debug: SizeOfRawData: 0x200 0x1ff
+    LibClamAV debug: PointerToRawData: 0x8600 0x8600
+    LibClamAV debug: Section's memory is writeable
+    LibClamAV debug: ------------------------------------
+    LibClamAV debug: EntryPoint offset: 0x8470 (33904)
+
+

The section structure displayed above suggests the executable is packed +with UPX.

+
    LibClamAV debug: ------------------------------------
+    LibClamAV debug: EntryPoint offset: 0x8470 (33904)
+    LibClamAV debug: UPX/FSG/MEW: empty section found - assuming
+                    compression
+    LibClamAV debug: UPX: bad magic - scanning for imports
+    LibClamAV debug: UPX: PE structure rebuilt from compressed file
+    LibClamAV debug: UPX: Successfully decompressed with NRV2B
+    LibClamAV debug: UPX/FSG: Decompressed data saved in
+                    /tmp/clamav-90d2d25c9dca42bae6fa9a764a4bcede
+    LibClamAV debug: ***** Scanning decompressed file *****
+    LibClamAV debug: Recognized MS-EXE/DLL file
+    LibClamAV debug: Matched signature for file type PE
+
+

Indeed, libclamav recognizes the UPX data and saves the decompressed +(and rebuilt) executable into +/tmp/clamav-90d2d25c9dca42bae6fa9a764a4bcede. Then it continues by +scanning this new file:

+
    LibClamAV debug: File type: Executable
+    LibClamAV debug: Machine type: 80386
+    LibClamAV debug: NumberOfSections: 3
+    LibClamAV debug: TimeDateStamp: Thu Jan 27 11:43:15 2011
+    LibClamAV debug: SizeOfOptionalHeader: e0
+    LibClamAV debug: File format: PE
+    LibClamAV debug: MajorLinkerVersion: 6
+    LibClamAV debug: MinorLinkerVersion: 0
+    LibClamAV debug: SizeOfCode: 0xc000
+    LibClamAV debug: SizeOfInitializedData: 0x19000
+    LibClamAV debug: SizeOfUninitializedData: 0x0
+    LibClamAV debug: AddressOfEntryPoint: 0x7b9f
+    LibClamAV debug: BaseOfCode: 0x1000
+    LibClamAV debug: SectionAlignment: 0x1000
+    LibClamAV debug: FileAlignment: 0x1000
+    LibClamAV debug: MajorSubsystemVersion: 4
+    LibClamAV debug: MinorSubsystemVersion: 0
+    LibClamAV debug: SizeOfImage: 0x26000
+    LibClamAV debug: SizeOfHeaders: 0x1000
+    LibClamAV debug: NumberOfRvaAndSizes: 16
+    LibClamAV debug: Subsystem: Win32 GUI
+    LibClamAV debug: ------------------------------------
+    LibClamAV debug: Section 0
+    LibClamAV debug: Section name: .text
+    LibClamAV debug: Section data (from headers - in memory)
+    LibClamAV debug: VirtualSize: 0xc000 0xc000
+    LibClamAV debug: VirtualAddress: 0x1000 0x1000
+    LibClamAV debug: SizeOfRawData: 0xc000 0xc000
+    LibClamAV debug: PointerToRawData: 0x1000 0x1000
+    LibClamAV debug: Section contains executable code
+    LibClamAV debug: Section's memory is executable
+    LibClamAV debug: ------------------------------------
+    LibClamAV debug: Section 1
+    LibClamAV debug: Section name: .rdata
+    LibClamAV debug: Section data (from headers - in memory)
+    LibClamAV debug: VirtualSize: 0x2000 0x2000
+    LibClamAV debug: VirtualAddress: 0xd000 0xd000
+    LibClamAV debug: SizeOfRawData: 0x2000 0x2000
+    LibClamAV debug: PointerToRawData: 0xd000 0xd000
+    LibClamAV debug: ------------------------------------
+    LibClamAV debug: Section 2
+    LibClamAV debug: Section name: .data
+    LibClamAV debug: Section data (from headers - in memory)
+    LibClamAV debug: VirtualSize: 0x17000 0x17000
+    LibClamAV debug: VirtualAddress: 0xf000 0xf000
+    LibClamAV debug: SizeOfRawData: 0x17000 0x17000
+    LibClamAV debug: PointerToRawData: 0xf000 0xf000
+    LibClamAV debug: Section's memory is writeable
+    LibClamAV debug: ------------------------------------
+    LibClamAV debug: EntryPoint offset: 0x7b9f (31647)
+    LibClamAV debug: Bytecode executing hook id 257 (0 hooks)
+    attachment.exe: OK
+    [...]
+
+

No additional files get created by libclamav. By writing a signature for the decompressed file you have more chances that the engine will detect the target data when it gets compressed with another packer.

+

This method should be applied to all files for which you want to create signatures. By analyzing the debug information you can quickly see how the engine recognizes and preprocesses the data and what additional files get created. Signatures created for bottom-level temporary files are usually more generic and should help detecting the same malware in different forms.

+

Writing signatures for special files

+

HTML

+

ClamAV contains HTML normalization code which makes it easier to write signatures for HTML data that might differ based on white space, capitalization, and other insignificant differences. Running sigtool --html-normalise on a HTML file can be used to see what a file's contents will look like after normalization. This command should generate the following files:

+
    +
  • +

    nocomment.html - the file is normalized, lower-case, with all comments and superfluous white space removed

    +
  • +
  • +

    notags.html - as above but with all HTML tags removed

    +
  • +
  • +

    javascript - any script contents are normalized and the results appended to this file

    +
  • +
+

The code automatically decodes JScript.encode parts and char ref’s (e.g. &#102;). To create a successful signature for the input file type, the rule must match on the contents of one of the created files. Signatures matching on normalized HTML should have a target type of 3. For reference, see Target Types.

+

Text files

+

Similarly to HTML all ASCII text files get normalized (converted to lower-case, all superfluous white space and control characters removed, etc.) before scanning. Running sigtool --ascii-normalise on a text file will result in a normalized version being written to the file named normalised\_text. Rules matching on normalized ASCII text should have a target type of 7. For reference, see Target Types.

+

Compressed Portable Executable files

+

If the file is compressed with UPX, FSG, Petite or another PE packer supported by libclamav, ClamAV will attempt to automatically unpack the executable and evaluate signatures against the unpacked executable. To inspect the executable that results from ClamAV's unpacking process, run clamscan with --debug --leave-temps. Example output for a FSG compressed file:

+
    LibClamAV debug: UPX/FSG/MEW: empty section found - assuming compression
+    LibClamAV debug: FSG: found old EP @119e0
+    LibClamAV debug: FSG: Unpacked and rebuilt executable saved in
+    /tmp/clamav-f592b20f9329ac1c91f0e12137bcce6c
+
+

In the example above, /tmp/clamav-f592b20f9329ac1c91f0e12137bcce6c is the unpacked executable, and a signature can be written based off of this file.

+

Using sigtool

+

sigtool pulls in libclamav and provides shortcuts to doing tasks that clamscan does behind the scenes. These can be really useful when writing a signature or trying to get information about a signature that might be causing FPs or performance problems.

+

The following sigtool flags can be especially useful for signature writing:

+
    +
  • +

    --md5 / --sha1 / --sha256: Generate the MD5/SHA1/SHA256 hash and calculate the file size, outputting both as a properly-formatted .hdb/.hsb signature

    +
  • +
  • +

    --mdb: Generate section hashes of the specified file. This is useful when generating .mdb signatures.

    +
  • +
  • +

    --decode: Given a ClamAV signature from STDIN, show a more user-friendly representation of it. An example usage of this flag is cat test.ldb | sigtool --decode.

    +
  • +
  • +

    --hex-dump: Given a sequence of bytes from STDIN, print the hex equivalent. An example usage of this flag is echo -n "Match on this" | sigtool --hex-dump.

    +
  • +
  • +

    --html-normalise: Normalize the specified HTML file in the way that clamscan will before looking for rule matches. Writing signatures off of these files makes it easier to write rules for target type HTML (you'll know what white space, capitalization, etc. to expect). See the HTML section for more details.

    +
  • +
  • +

    --ascii-normalise: Normalize the specified ASCII text file in the way that clamscan will before looking for rule matches. Writing signatures off of this normalized file data makes it easier to write rules for target type Txt (you'll know what white space, capitalization, etc. to expect). See the Text files sectino for more details.

    +
  • +
  • +

    --print-certs: Print the Authenticode signatures of any PE files specified. +This is useful when writing signature-based .crb rule files.

    +
  • +
  • +

    --vba: Extract VBA/Word6 macro code

    +
  • +
  • +

    --test-sigs: Given a signature and a sample, determine whether the signature matches and, if so, display the offset into the file where the match occurred. This can be useful for investigating false positive matches in clean files.

    +
  • +
+

Inspecting signatures inside a CVD file

+

CVD (ClamAV Virus Database) is a digitally signed container that includes signature databases in various text formats. The header of the container is a 512 bytes long string with colon separated fields:

+
    ClamAV-VDB:build time:version:number of signatures:functionality level required:MD5 checksum:digital signature:builder name:build time (sec)
+
+

sigtool --info displays detailed information about a given CVD file:

+
    zolw@localhost:/usr/local/share/clamav$ sigtool -i main.cvd
+    File: main.cvd
+    Build time: 09 Dec 2007 15:50 +0000
+    Version: 45
+    Signatures: 169676
+    Functionality level: 21
+    Builder: sven
+    MD5: b35429d8d5d60368eea9630062f7c75a
+    Digital signature: dxsusO/HWP3/GAA7VuZpxYwVsE9b+tCk+tPN6OyjVF/U8
+    JVh4vYmW8mZ62ZHYMlM903TMZFg5hZIxcjQB3SX0TapdF1SFNzoWjsyH53eXvMDY
+    eaPVNe2ccXLfEegoda4xU2TezbGfbSEGoU1qolyQYLX674sNA2Ni6l6/CEKYYh
+    Verification OK.
+
+

The ClamAV project distributes a number of CVD files, including main.cvd and daily.cvd.

+

To view the signature associated with a given detection name, the CVD files can be unpacked and the underlying text files searched for a rule definition using a tool like grep. To do this, use sigtool's --unpack flag as follows:

+
    $ mkdir /tmp/clamav-sigs
+    $ cd /tmp/clamav-sigs/
+    $ sigtool --unpack /var/lib/clamav/main.cvd
+    $ ls
+    COPYING   main.fp   main.hsb   main.mdb  main.ndb
+    main.crb  main.hdb  main.info  main.msb  main.sfp
+
+

External tools

+

Below are tools that can be helpful when writing ClamAV signatures:

+
    +
  • CASC - CASC is a plugin for IDA Pro that allows the user to highlight sections of code and create a signature based on the underlying instructions (with options to ignore bytes associated with registers, addresses, and offsets). It also contains SigAlyzer, a tool to take an existing signature and locate the regions within the binary that match the subsignatures.
  • +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Usage/Configuration.html clamav-0.103.5+dfsg/docs/html/manual/Usage/Configuration.html --- clamav-0.103.2+dfsg/docs/html/manual/Usage/Configuration.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Usage/Configuration.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,411 @@ + + + + + + Configuration - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Configuration

+

Table Of Contents

+ +

First Time Set-Up

+

Depending on your install method and your operating system, some configuration options may have been pre-configured. For example a clamav install on Ubuntu with apt install will place configs in /etc/clamav.

+

However, it is likely that you will need to create new config files or modify the existing ones with custom settings that make the most sense for your use case. A from-source install will require you to create a freshclam.conf before you can use FreshClam, a clamd.conf before you can use ClamD, and a clamav-milter.conf before you can use ClamAV-Milter.

+

A default install from source will place the example configs in /usr/local/etc/ on Unix/Linux systems and in the install directory under conf_examples on Windows. These examples demonstrate each of the options and may help you decide how to configure ClamAV to suit your needs. But again the location of these examples may vary depending on how you installed ClamAV. To continue with the Ubuntu example, you may find the FreshClam config from an apt install in /usr/share/doc/clamav-freshclam/examples/. So if you're unsure where the example configs are on your system, you may wish to use ClamConf to generate them.

+

Here are some quick steps to get you started.

+

Unix

+

Run these to generate example configs, if needed:

+
clamconf -g freshclam.conf > freshclam.conf
+clamconf -g clamd.conf > clamd.conf
+clamconf -g clamav-milter.conf > clamav-milter.conf
+
+

Or if you have the examples already, copy them to drop the .example extension:

+
cp freshclam.conf.example freshclam.conf
+cp clamd.conf.example clamd.conf
+cp clamav-milter.conf.example clamav-milter.conf
+
+

Next up, edit the configs you need. There are tips below for each of freshclam.conf, clamd.conf, and clamav-milter.

+

Windows

+

In a PowerShell terminal in the install directory, perform the following tasks:

+

Run:

+
copy .\conf_examples\freshclam.conf.sample .\freshclam.conf
+copy .\conf_examples\clamd.conf.sample .\clamd.conf
+
+

Run:

+
write.exe .\freshclam.conf
+
+

WordPad will pop up. Delete the line that says "Example". You may also wish to set additional options to enable features or alter default behavior, such as the receive-timeout. Save the file and close WordPad.

+

Run:

+
write.exe .\clamd.conf
+
+

WordPad will pop up. Delete the line that says "Example". You may also wish to set additional options to enable features or alter default behavior, such as enabling logging. Save the file and close WordPad.

+

Additional notes about the config files and database directories

+

The install directory is but one of a few locations ClamAV may search for configs and for signature databases.

+

Config files path search order:

+
    +
  1. The content of the registry key: +"HKEY_LOCAL_MACHINE/Software/ClamAV/ConfDir"
  2. +
  3. The directory where libclamav.dll is located: +"C:\Program Files\ClamAV"
  4. +
  5. "C:\ClamAV"
  6. +
+

Database files path search order:

+
    +
  1. The content of the registry key: +"HKEY_LOCAL_MACHINE/Software/ClamAV/DataDir"
  2. +
  3. The directory "database" inside the directory where libclamav.dll is located: +"C:\Program Files\ClamAV\database"
  4. +
  5. "C:\ClamAV\db"
  6. +
+

freshclam.conf

+

freshclam is the automatic database update tool for Clam AntiVirus. It can be configured to work in two modes:

+
    +
  • interactive - on demand from command line
  • +
  • daemon - silently in the background
  • +
+

freshclam is an advanced tool: it supports scripted updates (instead of transferring the whole CVD file at each update it only transfers the differences between the latest and the current database via a special script), database version checks through DNS, proxy servers (with authentication), digital signatures and various error scenarios.

+

Quick test: run freshclam (as superuser) with no parameters and check the output.

+
freshclam
+
+
+

Tip: Depending on how you installed Freshclam and depending on which version of ClamAV you're running, you may encounter errors the first time you run Freshclam. See the Freshclam section of our FAQ for help!

+
+

If everything is OK you may create the log file in /var/log (ensure the directory is owned either by clamav or whichever user freshclam will be running as):

+
touch /var/log/freshclam.log
+chmod 600 /var/log/freshclam.log
+chown clamav /var/log/freshclam.log
+
+

Now you should edit the configuration file freshclam.conf and point the UpdateLogFile directive to the log file. Finally, to run freshclam in the daemon mode, execute:

+
freshclam -d
+
+

The other way is to use the cron daemon. You have to add the following line to the crontab of root or clamav user:

+
N * * * *   /usr/local/bin/freshclam --quiet
+
+

to check for a new database every hour. N should be a number between 3 and 57 of your choice. Please don’t choose any multiple of 10, because there are already too many clients using those time slots. Proxy settings are only configurable via the configuration file and freshclam will require strict permission settings for the config file when HTTPProxyPassword is turned on.

+
HTTPProxyServer myproxyserver.com
+HTTPProxyPort 1234
+HTTPProxyUsername myusername
+HTTPProxyPassword mypass
+
+

Other freshclam.conf settings

+

If your freshclam.conf was derived from the freshclam.conf.sample, you should find many other options that are simply commented out. If not, seek out the freshclam.conf.sample file, or on Linux/Unix systems run man freshclam.conf.

+

Take the time to look through the options. You can enable the sample options by deleting the # comment characters.

+

Some popular options to enable include:

+
    +
  • LogTime
  • +
  • LogRotate
  • +
  • NotifyClamd
  • +
  • DatabaseOwner
  • +
+

clamd.conf

+

Currently, ClamAV requires users to edit their clamd.conf.example file before they can run the daemon. At a bare minimum, users will need to comment out the line that reads "Example", else clamd will consider the configuration invalid, ala:

+
# Comment or remove the line below.
+#Example
+
+

You will also need to rename clamd.conf.example to clamd.conf via:

+
mv ./clamd.conf.example ./clamd.conf
+
+

If you are setting up a simple, local clamd instance then some other configuration options of interests to you will be as follows:

+
# Path to a local socket file the daemon will listen on.
+# Default: disabled (must be specified by a user)
+LocalSocket /tmp/clamd.socket
+
+...
+
+# Sets the permissions on the unix socket to the specified mode.
+# Default: disabled (socket is world accessible)
+LocalSocketMode 660
+
+

Beyond that, clamd.conf is well commented and configuration should be straightforward.

+

If needed, you can find out even more about the formatting and options available in clamd.conf with the command:

+
man clamd.conf
+
+

Other clamd.conf settings

+

If your clamd.conf was derived from the clamd.conf.sample, you should find many other options that are simply commented out. If not, seek out the clamd.conf.sample file, or on Linux/Unix systems run man clamd.conf.

+

Take the time to look through the options. You can enable the sample options by deleting the # comment characters.

+

Some popular options to enable include:

+
    +
  • LogTime
  • +
  • LogClean
  • +
  • LogRotate
  • +
  • User
  • +
  • ScanOnAccess +
      +
    • OnAccessIncludePath
    • +
    • OnAccessExcludePath
    • +
    • OnAccessPrevention
    • +
    +
  • +
+

On-Access Scanning

+

You can configure On-Access Scanning through clamd.conf. Configuration for On-Access Scanning starts in the second half of clamd.conf.sample starting with "On-access Scan Settings". All options are grouped acording to use and roughly ordered by importance in those groupings. Please carefully read the explanation of each option to see if it might be of use to you.

+

Also read the on-access section of the Usage manual for further details on using On-Access Scanning.

+

clamav-milter.conf

+

ClamAV includes a mail filtering tool called clamav-milter. This tool interfaces directly with clamd, and thus requires a working clamd instance to run. However, clamav-milter's configuration and log files are separate from that of clamd.

+

Ensuring ClamAV compiles with clamav-milter must be done at configure time with the command:

+
./configure [options] --enable-milter
+
+

This requires having the milter library installed on your system. If libmilter is not installed, ./configure will exit with this error message:

+
checking for mi_stop in -lmilter... no
+configure: error: Cannot find libmilter
+
+

While not necessarily complicated, setting up the clamav-milter is an involved process. Thus, we recommend consulting your MTA’s manual on how to best connect ClamAV with the clamav-milter.

+

Users and on user privileges

+

If you are running freshclam and clamd as root or with sudo, and you did not explicitly configure with --disable-clamav, you will want to ensure that the DatabaseOwner user specified in freshclam.conf owns the database directory so it can download signature updates.

+

The user that clamd, clamdscan, and clamscan run as may be the same user, but if it isn't -- it merely needs read access to the database directory.

+

If you choose to use the default clamav user to run freshclam and clamd, you'll need to create the clamav group and the clamav user account the first time you install ClamAV.

+
groupadd clamav
+useradd -g clamav -s /bin/false -c "Clam Antivirus" clamav
+
+

Finally, you will want to set user ownership of the database directory. For example:

+
sudo chown -R clamav:clamav /usr/local/share/clamav
+
+

Configure SELinux for ClamAV

+

Certain distributions (notably RedHat variants) when operating with SELinux enabled use the non-standard antivirus_can_scan_system SELinux option instead of clamd_can_scan_system.

+

At this time, libclamav only sets the clamd_can_scan_system option, so you may need to manually enable antivirus_can_scan_system. If you don't perform this step, freshclam will log something like this when it tests the newly downloaded signature databases:

+
During database load : LibClamAV Warning: RWX mapping denied: Can't allocate RWX Memory: Permission denied
+
+

To allow ClamAV to operate under SELinux, run the following:

+
setsebool -P antivirus_can_scan_system 1
+
+

ClamConf

+

clamconf is a tool ClamAV provides for checking your entire system configuration, as it relates to your ClamAV installation. When run, it displays values used when configuring ClamAV at compilation time, important OS details, the contents (and validity) of both clamd.conf and freshclam.conf, along with other important engine, database, platform, and build information.

+

It can also generate example configuration files for clamd.conf and freshclam.conf.

+

To use clamconf, and see all the information it provides, simply run the following command:

+
clamconf
+
+

For more detailed information on clamconf, run:

+
clamconf --help
+
+

or on Unix systems:

+
man clamconf
+
+

Next Steps

+

Now that you have the config file basics, it's time to learn about signature databases and how to keep yours up-to-date.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Usage/ReportABug.html clamav-0.103.5+dfsg/docs/html/manual/Usage/ReportABug.html --- clamav-0.103.2+dfsg/docs/html/manual/Usage/ReportABug.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Usage/ReportABug.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,254 @@ + + + + + + Report a Bug - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

How to Report A Bug

+
+

Warning Against Accidental Vulnerability Disclosure

+

You may not realize it, but your bug could be a security issue. Please review our Security Policy to determine if your bug is a security issue before you create a public ticket on GitHub issues.

+

GitHub Issues are public reports and security issues must be reported in private.

+
+

Steps Before You Report

+

If you find a bug in ClamAV, please do the following before you submit a bug report:

+
    +
  1. +

    Verify if the bug exists in the most recent stable release or ideally check if the bug exists in the latest unreleased code in Git before reporting the issue.

    +
  2. +
  3. +

    Review the open issues to make sure someone else hasn't already reported the same issue.

    +
    +

    Tip: Before switching to GitHub Issues, ClamAV used Bugzilla. You can also review older open tickets from the Bugzilla archive.

    +
    +
  4. +
  5. +

    Collect the required information, described below, to include with your report.

    +
  6. +
  7. +

    Create a new ticket on GitHub.

    +
  8. +
+

Please do not submit bugs for third party software.

+

Required Information

+

Please be sure to include all of the following information so that we can effectively address your bug:

+
    +
  • +

    ClamAV version, settings, and system details:

    +

    On the command line, run:

    +
    clamconf -n
    +
    +

    ClamConf will print out configuration information and some system information. The -n option prints out only non-default options. This information will help the team identify possible triggers for your bug.

    +
  • +
  • +

    3rd party signatures:

    +

    Please tell us if you are using any unofficial signature databases, that is anything other than main.cvd/cld, daily.cvd/cld, and bytecode.cvd/cld

    +
  • +
  • +

    How to reproduce the problem:

    +

    Include specific steps needed to reproduce the issue.

    +

    If the issue is reproducible only when scanning a specific file, attach it to the ticket.

    +

    Large Files: The maximum size for file attachments on GitHub Issues is 25MB and the maximum size for images is 10MB. If the file is too big to mail it, you can upload it to a password protected website and send us the URL and the credentials to access it.

    +

    If your file must be kept confidential you can reach out on the ClamAV Discord chat server to exchange email addresses and to share the zipped file or to share the zip password.

    +
    +

    CAUTION: Don’t forget to encrypt it or you may cause damage to the mail servers between you and us!

    +

    On the command line, run:

    +
    zip -P virus -e file.zip file.ext
    +
    +
    +
  • +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Usage/Scanning.html clamav-0.103.5+dfsg/docs/html/manual/Usage/Scanning.html --- clamav-0.103.2+dfsg/docs/html/manual/Usage/Scanning.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Usage/Scanning.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,401 @@ + + + + + + Scanning - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Scanning

+

Table Of Contents

+ +
+

Tip: The commands on Windows are generally the same, but you may need to add the .exe extension to run the ClamAV applications.

+
+

Daemon

+

ClamD

+

clamd is a multi-threaded daemon that uses libclamav to scan files for viruses. Scanning behavior can be fully configured to fit most needs by modifying clamd.conf.

+

As clamd requires a virus signature database to run, we recommend setting up ClamAV's official signatures before running clamd using freshclam.

+

The daemon works by listening for commands on the sockets specified in clamd.conf. Listening is supported over both unix local sockets and TCP sockets.

+

IMPORTANT: clamd does not currently protect or authenticate traffic coming over the TCP socket, meaning it will accept any and all of the following commands listed from any source. Thus, we strongly recommend following best networking practices when setting up your clamd instance. I.e. don't expose your TCP socket to the Internet.

+

Here is a quick list of the commands accepted by clamd over the socket.

+
    +
  • PING
  • +
  • VERSION
  • +
  • RELOAD
  • +
  • SHUTDOWN
  • +
  • SCAN file/directory
  • +
  • RAWSCAN file/directory
  • +
  • CONTSCAN file/directory
  • +
  • MULTISCAN file/directory
  • +
  • ALLMATCHSCAN file/directory
  • +
  • INSTREAM
  • +
  • FILDES
  • +
  • STATS
  • +
  • IDSESSION, END
  • +
+

As with most ClamAV tools, you can find out more about these by invoking the command:

+
man clamd
+
+

The daemon also handles the following signals as so:

+
    +
  • SIGTERM - perform a clean exit
  • +
  • SIGHUP - reopen the log file
  • +
  • SIGUSR2 - reload the database
  • +
+

It should be noted that clamd should not be started using the shell operator & or other external tools which would start it as a background process. Instead, you should run clamd which will load the database and then daemonize itself (unless you have specified otherwise in clamd.conf). After that, clamd is ready to accept connections and perform file scanning.

+

Once you have set up your configuration to your liking, and understand how you will be sending commands to the daemon, running clamd itself is simple. Simply execute the command:

+
clamd
+
+

ClamDScan

+

clamdscan is a clamd client, which greatly simplifies the task of scanning files with clamd. It sends commands to the clamd daemon across the socket specified in clamd.conf and generates a scan report after all requested scanning has been completed by the daemon.

+

Thus, to run clamdscan, you must have an instance of clamd already running as well.

+

Please keep in mind, that as a simple scanning client, clamdscan cannot change scanning and engine configurations. These are tied to the clamd instance and the configuration you set up in clamd.conf. Therefore, while clamdscan will accept many of the same commands as its sister tool clamscan, it will simply ignore most of them as (by design) no mechanism exists to make ClamAV engine configuration changes over the clamd socket.

+

Again, running clamdscan, once you have a working clamd instance, is simple:

+
clamdscan [*options*] [*file/directory/-*]
+
+

ClamDTop

+

clamdtop is a tool to monitor one or multiple instances of clamd. It has a colorized ncurses interface, which shows each job queued, memory usage, and information about the loaded signature database for the connected clamd instance(s). By default it will attempt to connect to the local clamd as defined in clamd.conf. However, you can specify other clamd instances at the command line.

+

To learn more, use the commands

+
man clamdtop
+
+

or

+
clamdtop --help
+
+

On-Access Scanning

+

The ClamOnAcc application provides On-Access Scanning for Linux systems. On-Access Scanning is a form of real-time protection that uses ClamD to scan files when they're accessed.

+

ClamOnAcc (v0.102+)

+

ClamAV's On-Access Scanning (clamonacc) is a client that runs in its own application alongside, but separately from the clamd instance. The On-Access Scanner is capable of preventing access to/from any malicious files it discovers--based on the verdict it receives from clamd--but by default it is configured to run in notify-only mode, which means it will simply alert the user if a malicious file is detected, then take any additional actions that the user may have specified at the command line, but it will not actively prevent processes from reading or writing to that file.

+
+

Disclaimer: Enabling Prevention mode will seriously impact performance if used on commonly accessed directories.

+
+
+

Tip: You can run ClamOnAcc multiple times simultaneously, each with a different config. If you want to enable Prevention-mode for one directory, while sticking to notify-only mode for any other monitored directories, that's an option!

+
+

On-Access Scanning is primarily set up through clamd.conf. However, you can learn more about all the configuration and command line options available to you by reading the On-Access Scanning User Guide.

+

Once you have set up the On-Access Scanner (and clamd) to your liking, you will first need to run clamd before you can start it. If your clamd instance is local, it is required you run clamd as a user that is excluded (via OnAccessExcludeUname or OnAccessExcludeUID) from On-Access scanning events (e.g.) to prevent clamonacc from triggering events endlessly as it sends scan requests to clamd:

+
su - clamav -c "/usr/local/bin/clamd
+
+

After the daemon is running, you can start the On-Access Scanner. clamonacc must be run as root in order to utilize its kernel event detection and intervention features:

+
sudo clamonacc
+
+

It will run a number of startup checks to test for a sane configuration, and ensure it can connect to clamd, and if everything checks out clamonacc will automatically fork to the background and begin monitoring your system for events.

+

ClamD (v0.101)

+

In older versions, ClamAV's On-Access Scanner is a thread that runs within a clamd instance. The On-Access Scanner is capable of blocking access to/from any malicious files it discovers--based on the verdict it finds using the engine it shares with clamd--but by default it is configured to run in notify-only mode, which means it will simply alert the user if a malicious file is detected, but it will not actively prevent processes from reading or writing to that file.

+

On-Access Scanning is primarily set up through clamd.conf. However, you can learn more about all the configuration and command line options available to you by reading the On-Access Scanning User Guide.

+

Once you have set up the On-Access Scanner to your liking, you will need to run clamd with elevated permissions to start it.

+
sudo clamd
+
+

One-Time Scanning

+

ClamScan

+

clamscan is a command line tool which uses libclamav to scan files and/or directories for viruses. Unlike clamdscan, clamscan does not require a running clamd instance to function. Instead, clamscan will create a new engine and load in the virus database each time it is run. It will then scan the files and/or directories specified at the command line, create a scan report, and exit.

+

By default, when loading databases, clamscan will check the location to which freshclam installed the virus database signatures. This behavior, along with a myriad of other scanning and engine controls, can be modified by providing flags and other options at the command line.

+

There are too many options to list all of them here. So we'll only cover a few common and more interesting ones:

+
    +
  • --log=FILE - save scan report to FILE
  • +
  • --database=FILE/DIR - load virus database from FILE or load all supported db files from DIR
  • +
  • --official-db-only[=yes/no(*)] - only load official signatures
  • +
  • --max-filesize=#n - files larger than this will be skipped and assumed clean
  • +
  • --max-scansize=#n - the maximum amount of data to scan for each container file
  • +
  • --leave-temps[=yes/no(*)]- do not remove temporary files
  • +
  • --file-list=FILE - scan files from FILE
  • +
  • --quiet - only output error messages
  • +
  • --bell - sound bell on virus detection
  • +
  • --cross-fs[=yes(*)/no] - scan files and directories on other filesystems
  • +
  • --move=DIRECTORY - move infected files into DIRECTORY
  • +
  • --copy=DIRECTORY - copy infected files into DIRECTORY
  • +
  • --bytecode-timeout=N - set bytecode timeout (in milliseconds)
  • +
  • --heuristic-alerts[=yes(*)/no] - toggles heuristic alerts
  • +
  • --alert-encrypted[=yes/no(*)] - alert on encrypted archives and documents
  • +
  • --nocerts - disable authenticode certificate chain verification in PE files
  • +
  • --disable-cache - disable caching and cache checks for hash sums of scanned files
  • +
+

To learn more about the options available when using clamscan please reference:

+
man clamscan
+
+

and

+
clamscan --help
+
+

Otherwise, the general usage of clamscan is:

+
clamscan [options] [file/directory/-]
+
+

Some basic scans

+

Run this to scan the files in the current directory:

+
clamscan .
+
+

This will scan the current directory. At the end of the scan, it will display a summary. If you notice in the clamscan output, it only scanned something like 60 files, even though there are more files in subdirectories. By default, clamscan will only scan files in the current directory.

+

Run this to scan all the files in the current directory:

+
clamscan --recursive .
+
+

Run this to scan ALL the files on your system, it will take quite a while. Keep in mind that you can cancel it at any time by pressing Ctrl-C:

+

Linux/Unix:

+
clamscan --recursive /
+
+

Windows:

+
clamscan.exe --recursive C:\
+
+

Process Memory Scanning

+
+

Note: This feature requires Windows and ClamAV version 0.105 or newer. You must also be running ClamAV as Administrator.

+
+

clamscan and clamdscan are able to scan the virtual memory of currently executing processes. To do so, use the --memory option:

+
clamscan --memory
+
+

The --kill and --unload options allow for killing/unloading infected loaded modules.

+

Disclaimers

+
+

Disclaimer: ClamAV doesn't have a "quick scan" mode. ClamAV is malware detection toolkit, not an endpoint security suite. It's up to you to decide what to scan. A full system scan is going to take a long time with ClamAV or with any anti-virus software.

+
+
+

Disclaimer 2: ClamScan, ClamOnAcc, and ClamDScan each include --remove options for deleting any file which alerts during a scan. This is generally a terrible idea, unless you're monitoring an upload/downloads directory. False positives happen! You do not want to have the wrong file accidentally deleted. Instead, consider using --move or perhaps just --copy and set up script with the ClamD VirusEvent feature to notify you when something has been detected.

+
+

Windows-specific Issues

+

Globbing

+

Since the Windows command prompt doesn't take care of wildcard expansion, minimal emulation of unix glob() is performed internally. It supports * and ? only.

+

File paths

+

Please always use the backslash as the path separator. SMB Network shares and UNC paths are supported.

+

Socket and libclamav API Input

+

The Windows version of ClamAV requires all the input to be UTF-8 encoded.

+

This affects:

+
    +
  • The API, notably the cl_scanfile() function
  • +
  • ClamD socket input, e.g. the commands SCAN, CONTSCAN, MUTLISCAN, etc.
  • +
  • ClamD socket output, i.e replies to the above queries
  • +
+

For legacy reasons ANSI (i.e. CP_ACP) input will still be accepted and processed as before, but with two important remarks:

+
    +
  1. Socket replies to ANSI queries will still be UTF-8 encoded.
  2. +
  3. ANSI sequences which are also valid UTF-8 sequences will be handled as UTF-8.
  4. +
+

As a side note, console output (stdin and stderr) will always be OEM encoded, even when redirected to a file.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Usage/Services.html clamav-0.103.5+dfsg/docs/html/manual/Usage/Services.html --- clamav-0.103.2+dfsg/docs/html/manual/Usage/Services.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Usage/Services.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,219 @@ + + + + + + Running ClamAV Services - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Running ClamAV Services

+

Table Of Contents

+ + +

Windows Services

+
+

Note: This feature requires ClamAV version 0.104 or newer.

+
+

On Windows, clamd and freshclam have options that enable them to run in the background.

+

To install the services, first use the command:

+
clamd --install-service
+
+

Then use net start clamd and net stop clamd to start/stop the service.

+

To uninstall the service, use:

+
clamd --uninstall-service
+
+

Services can also be managed via the Services application on Windows.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Usage/SignatureManagement.html clamav-0.103.5+dfsg/docs/html/manual/Usage/SignatureManagement.html --- clamav-0.103.2+dfsg/docs/html/manual/Usage/SignatureManagement.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Usage/SignatureManagement.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,276 @@ + + + + + + Updating Signature Databases - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Signature Testing and Management

+

Table Of Contents

+ +
+

Tip: The commands on Windows are generally the same, but you may need to add the .exe extension to run the ClamAV applications.

+
+

FreshClam

+

Before you can start the ClamAV scanning engine (using either clamd or clamscan), you must first have ClamAV Virus Database (.cvd) file(s) installed in the appropriate location on your system.

+

The tool freshclam is used to download and update ClamAV’s official virus signature databases. While easy to use in its base configuration, freshclam does require a working freshclam.conf configuration file to run (the location of which can be passed in via command line if the default search location does not fit your needs).

+

Once you have a valid configuration file, you can invoke FreshClam with the following command:

+
freshclam
+
+

By default, freshclam will then attempt to connect to ClamAV's virus signature database distribution network. If no databases exist in the directory specified, freshclam will do a fresh download of the requested databases. Otherwise, freshclam will attempt to update existing databases, pairing them against downloaded cdiffs. If a database is found to be corrupted, it is not updated and instead replaced with a fresh download.

+

Of course, all this behavior--and more--can be changed to suit your needs by modifying freshclam.conf and/or using various command line options.

+

You can find more information about FreshClam with the commands:

+

Unix/Linux:

+
freshclam --help
+
+

Or (Unix/Linux only):

+
man freshclam
+
+
+

Tip: Newer versions of FreshClam will create your database directory if it doesn't already exist. Older versions won't, and may fail unless you create it first.

+
+
+

Important: It is common on Ubuntu after a fresh install to see the following error the first time you use ClamAV:

+
freshclam
+
+freshclam: error while loading shared libraries: libclamav.so.7: cannot open shared object   file: No such file or directory
+
+

You can fix this error by using ldconfig to rebuild the library search path.

+
sudo ldconfig
+
+
+

If you are having issues updating the signature databases with freshclam, please review the freshclam FAQ.

+

SigTool

+

ClamAV provides sigtool as a command-line testing tool for assisting users in their efforts creating and working with virus signatures. While sigtool has many uses--including crafting signatures--of particular note, is sigtool's ability to help users and analysts in determining if a file detected by libclamav's virus signatures is a false positive.

+

This can be accomplished by using the command:

+
sigtool --unpack=FILE
+
+

Where FILE points to your virus signature databases. Then, once sigtool has finished unpacking the database into the directory from which you ran the command, you can search for the offending signature name (provided either by clamscan scan reports or clamd logs). As an example:

+
grep "Win.Test.EICAR" ./*
+
+

Or, do all that in one step with:

+
sigtool --find="Win.Test.EICAR"
+
+

This should give you the offending signature(s) in question, which can then be included as part of your false positive report.

+

To learn more in depth information on how sigtool can be used to help create virus signatures and work with malicious (and non-malicious) files please reference the many online tutorials on the topic.

+

Otherwise, information on available sigtool functions can be easily referenced with:

+
sigtool --help
+
+

Or (Unix/Linux only):

+
man sigtool
+
+

ClamBC

+

clambc is Clam Anti-Virus’ bytecode signature testing tool. It can be used to test newly crafted bytecode signatures or to help verify existing bytecode is executing against a sample as expected.

+

For more detailed help, please use:

+
clambc --help
+
+

Or (Unix/Linux only):

+
man clambc
+
+

Next Steps

+

Now that you know more about FreshClam and tools to work with the signature databases, it's time to run your first scan.

+

Create your own signatures

+

There is a whole community of malware researchers and signature writers. If you'd like to learn how to craft your own signatures, you can!

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/manual/Usage.html clamav-0.103.5+dfsg/docs/html/manual/Usage.html --- clamav-0.103.2+dfsg/docs/html/manual/Usage.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/manual/Usage.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,239 @@ + + + + + + Usage - ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

Usage

+

Table Of Contents

+ +

Purpose

+

This user guide presents an overview of the various ways that libclamav can be used through the tools provided by ClamAV. To learn more about how to better use each facet of ClamAV that interests you, please follow the links provided.

+

Daemon

+

The ClamAV Daemon, or clamd, is a multi-threaded daemon that uses libclamav to scan files for viruses. ClamAV provides a number of tools which interface with this daemon. They are, as follows:

+ +

Scanner

+

ClamAV also provides a command-line tool for simple scanning tasks with libclamav called clamscan. Unlike the daemon, clamscan is not a persistent process and is best suited for use cases where one-time scanning with minimal setup is needed.

+

Signature Testing and Management

+

A number of tools allow for testing and management of signatures. Of note are the following:

+
    +
  • clambc - specifically for testing bytecode
  • +
  • sigtool - for general signature testing and analysis
  • +
  • freshclam - used to update signature database sets to the latest version
  • +
+

Configuration

+

The more complex tools ClamAV provides each require some degree of configuration. ClamAV supplies two example configuration files:

+
    +
  • clamd.conf - for configuring the behavior of the ClamAV Daemon clamd and associated tools
  • +
  • freschclam.conf - for configuring the behavior of the signature database update tool, freshclam
  • +
+

ClamAV also provides a mail filtering tool called clamav-milter which can be attached to a clamd instance for mail scanning purposes.

+

Additionally, a tool called clamconf allows users to check the configurations used by each other tool, pulling information from the configuration files listed above, alongside other relevant information.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/mark.min.js clamav-0.103.5+dfsg/docs/html/mark.min.js --- clamav-0.103.2+dfsg/docs/html/mark.min.js 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/mark.min.js 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,7 @@ +/*!*************************************************** +* mark.js v8.11.1 +* https://markjs.io/ +* Copyright (c) 2014–2018, Julian Kühnel +* Released under the MIT license https://git.io/vwTVl +*****************************************************/ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Mark=t()}(this,function(){"use strict";var e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},t=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},n=function(){function e(e,t){for(var n=0;n1&&void 0!==arguments[1])||arguments[1],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:5e3;t(this,e),this.ctx=n,this.iframes=r,this.exclude=i,this.iframesTimeout=o}return n(e,[{key:"getContexts",value:function(){var e=[];return(void 0!==this.ctx&&this.ctx?NodeList.prototype.isPrototypeOf(this.ctx)?Array.prototype.slice.call(this.ctx):Array.isArray(this.ctx)?this.ctx:"string"==typeof this.ctx?Array.prototype.slice.call(document.querySelectorAll(this.ctx)):[this.ctx]:[]).forEach(function(t){var n=e.filter(function(e){return e.contains(t)}).length>0;-1!==e.indexOf(t)||n||e.push(t)}),e}},{key:"getIframeContents",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:function(){},r=void 0;try{var i=e.contentWindow;if(r=i.document,!i||!r)throw new Error("iframe inaccessible")}catch(e){n()}r&&t(r)}},{key:"isIframeBlank",value:function(e){var t="about:blank",n=e.getAttribute("src").trim();return e.contentWindow.location.href===t&&n!==t&&n}},{key:"observeIframeLoad",value:function(e,t,n){var r=this,i=!1,o=null,a=function a(){if(!i){i=!0,clearTimeout(o);try{r.isIframeBlank(e)||(e.removeEventListener("load",a),r.getIframeContents(e,t,n))}catch(e){n()}}};e.addEventListener("load",a),o=setTimeout(a,this.iframesTimeout)}},{key:"onIframeReady",value:function(e,t,n){try{"complete"===e.contentWindow.document.readyState?this.isIframeBlank(e)?this.observeIframeLoad(e,t,n):this.getIframeContents(e,t,n):this.observeIframeLoad(e,t,n)}catch(e){n()}}},{key:"waitForIframes",value:function(e,t){var n=this,r=0;this.forEachIframe(e,function(){return!0},function(e){r++,n.waitForIframes(e.querySelector("html"),function(){--r||t()})},function(e){e||t()})}},{key:"forEachIframe",value:function(t,n,r){var i=this,o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:function(){},a=t.querySelectorAll("iframe"),s=a.length,c=0;a=Array.prototype.slice.call(a);var u=function(){--s<=0&&o(c)};s||u(),a.forEach(function(t){e.matches(t,i.exclude)?u():i.onIframeReady(t,function(e){n(t)&&(c++,r(e)),u()},u)})}},{key:"createIterator",value:function(e,t,n){return document.createNodeIterator(e,t,n,!1)}},{key:"createInstanceOnIframe",value:function(t){return new e(t.querySelector("html"),this.iframes)}},{key:"compareNodeIframe",value:function(e,t,n){if(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_PRECEDING){if(null===t)return!0;if(t.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING)return!0}return!1}},{key:"getIteratorNode",value:function(e){var t=e.previousNode();return{prevNode:t,node:null===t?e.nextNode():e.nextNode()&&e.nextNode()}}},{key:"checkIframeFilter",value:function(e,t,n,r){var i=!1,o=!1;return r.forEach(function(e,t){e.val===n&&(i=t,o=e.handled)}),this.compareNodeIframe(e,t,n)?(!1!==i||o?!1===i||o||(r[i].handled=!0):r.push({val:n,handled:!0}),!0):(!1===i&&r.push({val:n,handled:!1}),!1)}},{key:"handleOpenIframes",value:function(e,t,n,r){var i=this;e.forEach(function(e){e.handled||i.getIframeContents(e.val,function(e){i.createInstanceOnIframe(e).forEachNode(t,n,r)})})}},{key:"iterateThroughNodes",value:function(e,t,n,r,i){for(var o,a=this,s=this.createIterator(t,e,r),c=[],u=[],l=void 0,h=void 0;void 0,o=a.getIteratorNode(s),h=o.prevNode,l=o.node;)this.iframes&&this.forEachIframe(t,function(e){return a.checkIframeFilter(l,h,e,c)},function(t){a.createInstanceOnIframe(t).forEachNode(e,function(e){return u.push(e)},r)}),u.push(l);u.forEach(function(e){n(e)}),this.iframes&&this.handleOpenIframes(c,e,n,r),i()}},{key:"forEachNode",value:function(e,t,n){var r=this,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:function(){},o=this.getContexts(),a=o.length;a||i(),o.forEach(function(o){var s=function(){r.iterateThroughNodes(e,o,t,n,function(){--a<=0&&i()})};r.iframes?r.waitForIframes(o,s):s()})}}],[{key:"matches",value:function(e,t){var n="string"==typeof t?[t]:t,r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.oMatchesSelector||e.webkitMatchesSelector;if(r){var i=!1;return n.every(function(t){return!r.call(e,t)||(i=!0,!1)}),i}return!1}}]),e}(),o=function(){function e(n){t(this,e),this.opt=r({},{diacritics:!0,synonyms:{},accuracy:"partially",caseSensitive:!1,ignoreJoiners:!1,ignorePunctuation:[],wildcards:"disabled"},n)}return n(e,[{key:"create",value:function(e){return"disabled"!==this.opt.wildcards&&(e=this.setupWildcardsRegExp(e)),e=this.escapeStr(e),Object.keys(this.opt.synonyms).length&&(e=this.createSynonymsRegExp(e)),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.setupIgnoreJoinersRegExp(e)),this.opt.diacritics&&(e=this.createDiacriticsRegExp(e)),e=this.createMergedBlanksRegExp(e),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.createJoinersRegExp(e)),"disabled"!==this.opt.wildcards&&(e=this.createWildcardsRegExp(e)),e=this.createAccuracyRegExp(e),new RegExp(e,"gm"+(this.opt.caseSensitive?"":"i"))}},{key:"escapeStr",value:function(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}},{key:"createSynonymsRegExp",value:function(e){var t=this.opt.synonyms,n=this.opt.caseSensitive?"":"i",r=this.opt.ignoreJoiners||this.opt.ignorePunctuation.length?"\0":"";for(var i in t)if(t.hasOwnProperty(i)){var o=t[i],a="disabled"!==this.opt.wildcards?this.setupWildcardsRegExp(i):this.escapeStr(i),s="disabled"!==this.opt.wildcards?this.setupWildcardsRegExp(o):this.escapeStr(o);""!==a&&""!==s&&(e=e.replace(new RegExp("("+this.escapeStr(a)+"|"+this.escapeStr(s)+")","gm"+n),r+"("+this.processSynonyms(a)+"|"+this.processSynonyms(s)+")"+r))}return e}},{key:"processSynonyms",value:function(e){return(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.setupIgnoreJoinersRegExp(e)),e}},{key:"setupWildcardsRegExp",value:function(e){return(e=e.replace(/(?:\\)*\?/g,function(e){return"\\"===e.charAt(0)?"?":""})).replace(/(?:\\)*\*/g,function(e){return"\\"===e.charAt(0)?"*":""})}},{key:"createWildcardsRegExp",value:function(e){var t="withSpaces"===this.opt.wildcards;return e.replace(/\u0001/g,t?"[\\S\\s]?":"\\S?").replace(/\u0002/g,t?"[\\S\\s]*?":"\\S*")}},{key:"setupIgnoreJoinersRegExp",value:function(e){return e.replace(/[^(|)\\]/g,function(e,t,n){var r=n.charAt(t+1);return/[(|)\\]/.test(r)||""===r?e:e+"\0"})}},{key:"createJoinersRegExp",value:function(e){var t=[],n=this.opt.ignorePunctuation;return Array.isArray(n)&&n.length&&t.push(this.escapeStr(n.join(""))),this.opt.ignoreJoiners&&t.push("\\u00ad\\u200b\\u200c\\u200d"),t.length?e.split(/\u0000+/).join("["+t.join("")+"]*"):e}},{key:"createDiacriticsRegExp",value:function(e){var t=this.opt.caseSensitive?"":"i",n=this.opt.caseSensitive?["aàáảãạăằắẳẵặâầấẩẫậäåāą","AÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćč","CÇĆČ","dđď","DĐĎ","eèéẻẽẹêềếểễệëěēę","EÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïī","IÌÍỈĨỊÎÏĪ","lł","LŁ","nñňń","NÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøō","OÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rř","RŘ","sšśșş","SŠŚȘŞ","tťțţ","TŤȚŢ","uùúủũụưừứửữựûüůū","UÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿ","YÝỲỶỸỴŸ","zžżź","ZŽŻŹ"]:["aàáảãạăằắẳẵặâầấẩẫậäåāąAÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćčCÇĆČ","dđďDĐĎ","eèéẻẽẹêềếểễệëěēęEÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïīIÌÍỈĨỊÎÏĪ","lłLŁ","nñňńNÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøōOÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rřRŘ","sšśșşSŠŚȘŞ","tťțţTŤȚŢ","uùúủũụưừứửữựûüůūUÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿYÝỲỶỸỴŸ","zžżźZŽŻŹ"],r=[];return e.split("").forEach(function(i){n.every(function(n){if(-1!==n.indexOf(i)){if(r.indexOf(n)>-1)return!1;e=e.replace(new RegExp("["+n+"]","gm"+t),"["+n+"]"),r.push(n)}return!0})}),e}},{key:"createMergedBlanksRegExp",value:function(e){return e.replace(/[\s]+/gim,"[\\s]+")}},{key:"createAccuracyRegExp",value:function(e){var t=this,n=this.opt.accuracy,r="string"==typeof n?n:n.value,i="";switch(("string"==typeof n?[]:n.limiters).forEach(function(e){i+="|"+t.escapeStr(e)}),r){case"partially":default:return"()("+e+")";case"complementary":return"()([^"+(i="\\s"+(i||this.escapeStr("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~¡¿")))+"]*"+e+"[^"+i+"]*)";case"exactly":return"(^|\\s"+i+")("+e+")(?=$|\\s"+i+")"}}}]),e}(),a=function(){function a(e){t(this,a),this.ctx=e,this.ie=!1;var n=window.navigator.userAgent;(n.indexOf("MSIE")>-1||n.indexOf("Trident")>-1)&&(this.ie=!0)}return n(a,[{key:"log",value:function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"debug",r=this.opt.log;this.opt.debug&&"object"===(void 0===r?"undefined":e(r))&&"function"==typeof r[n]&&r[n]("mark.js: "+t)}},{key:"getSeparatedKeywords",value:function(e){var t=this,n=[];return e.forEach(function(e){t.opt.separateWordSearch?e.split(" ").forEach(function(e){e.trim()&&-1===n.indexOf(e)&&n.push(e)}):e.trim()&&-1===n.indexOf(e)&&n.push(e)}),{keywords:n.sort(function(e,t){return t.length-e.length}),length:n.length}}},{key:"isNumeric",value:function(e){return Number(parseFloat(e))==e}},{key:"checkRanges",value:function(e){var t=this;if(!Array.isArray(e)||"[object Object]"!==Object.prototype.toString.call(e[0]))return this.log("markRanges() will only accept an array of objects"),this.opt.noMatch(e),[];var n=[],r=0;return e.sort(function(e,t){return e.start-t.start}).forEach(function(e){var i=t.callNoMatchOnInvalidRanges(e,r),o=i.start,a=i.end;i.valid&&(e.start=o,e.length=a-o,n.push(e),r=a)}),n}},{key:"callNoMatchOnInvalidRanges",value:function(e,t){var n=void 0,r=void 0,i=!1;return e&&void 0!==e.start?(r=(n=parseInt(e.start,10))+parseInt(e.length,10),this.isNumeric(e.start)&&this.isNumeric(e.length)&&r-t>0&&r-n>0?i=!0:(this.log("Ignoring invalid or overlapping range: "+JSON.stringify(e)),this.opt.noMatch(e))):(this.log("Ignoring invalid range: "+JSON.stringify(e)),this.opt.noMatch(e)),{start:n,end:r,valid:i}}},{key:"checkWhitespaceRanges",value:function(e,t,n){var r=void 0,i=!0,o=n.length,a=t-o,s=parseInt(e.start,10)-a;return(r=(s=s>o?o:s)+parseInt(e.length,10))>o&&(r=o,this.log("End range automatically set to the max value of "+o)),s<0||r-s<0||s>o||r>o?(i=!1,this.log("Invalid range: "+JSON.stringify(e)),this.opt.noMatch(e)):""===n.substring(s,r).replace(/\s+/g,"")&&(i=!1,this.log("Skipping whitespace only range: "+JSON.stringify(e)),this.opt.noMatch(e)),{start:s,end:r,valid:i}}},{key:"getTextNodes",value:function(e){var t=this,n="",r=[];this.iterator.forEachNode(NodeFilter.SHOW_TEXT,function(e){r.push({start:n.length,end:(n+=e.textContent).length,node:e})},function(e){return t.matchesExclude(e.parentNode)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT},function(){e({value:n,nodes:r})})}},{key:"matchesExclude",value:function(e){return i.matches(e,this.opt.exclude.concat(["script","style","title","head","html"]))}},{key:"wrapRangeInTextNode",value:function(e,t,n){var r=this.opt.element?this.opt.element:"mark",i=e.splitText(t),o=i.splitText(n-t),a=document.createElement(r);return a.setAttribute("data-markjs","true"),this.opt.className&&a.setAttribute("class",this.opt.className),a.textContent=i.textContent,i.parentNode.replaceChild(a,i),o}},{key:"wrapRangeInMappedTextNode",value:function(e,t,n,r,i){var o=this;e.nodes.every(function(a,s){var c=e.nodes[s+1];if(void 0===c||c.start>t){if(!r(a.node))return!1;var u=t-a.start,l=(n>a.end?a.end:n)-a.start,h=e.value.substr(0,a.start),f=e.value.substr(l+a.start);if(a.node=o.wrapRangeInTextNode(a.node,u,l),e.value=h+f,e.nodes.forEach(function(t,n){n>=s&&(e.nodes[n].start>0&&n!==s&&(e.nodes[n].start-=l),e.nodes[n].end-=l)}),n-=l,i(a.node.previousSibling,a.start),!(n>a.end))return!1;t=a.end}return!0})}},{key:"wrapGroups",value:function(e,t,n,r){return r((e=this.wrapRangeInTextNode(e,t,t+n)).previousSibling),e}},{key:"separateGroups",value:function(e,t,n,r,i){for(var o=t.length,a=1;a-1&&r(t[a],e)&&(e=this.wrapGroups(e,s,t[a].length,i))}return e}},{key:"wrapMatches",value:function(e,t,n,r,i){var o=this,a=0===t?0:t+1;this.getTextNodes(function(t){t.nodes.forEach(function(t){t=t.node;for(var i=void 0;null!==(i=e.exec(t.textContent))&&""!==i[a];){if(o.opt.separateGroups)t=o.separateGroups(t,i,a,n,r);else{if(!n(i[a],t))continue;var s=i.index;if(0!==a)for(var c=1;c=n[1]?(e.length>n[1]&&(r="invalid"),n.shift(),n.shift(),this.next=n.shift()):this.next="",r},regex:/"#*/,next:"start"},{defaultToken:"string.quoted.raw.source.rust"}]},{token:"string.quoted.double.source.rust",regex:'"',push:[{token:"string.quoted.double.source.rust",regex:'"',next:"pop"},{token:"constant.character.escape.source.rust",regex:s},{defaultToken:"string.quoted.double.source.rust"}]},{token:["keyword.source.rust","text","entity.name.function.source.rust"],regex:"\\b(fn)(\\s+)((?:r#)?[a-zA-Z_][a-zA-Z0-9_]*)"},{token:"support.constant",regex:"\\b[a-zA-Z_][\\w\\d]*::"},{token:"keyword.source.rust",regex:"\\b(?:abstract|alignof|as|become|box|break|catch|continue|const|crate|default|do|dyn|else|enum|extern|for|final|if|impl|in|let|loop|macro|match|mod|move|mut|offsetof|override|priv|proc|pub|pure|ref|return|self|sizeof|static|struct|super|trait|type|typeof|union|unsafe|unsized|use|virtual|where|while|yield)\\b"},{token:"storage.type.source.rust",regex:"\\b(?:Self|isize|usize|char|bool|u8|u16|u32|u64|u128|f16|f32|f64|i8|i16|i32|i64|i128|str|option|either|c_float|c_double|c_void|FILE|fpos_t|DIR|dirent|c_char|c_schar|c_uchar|c_short|c_ushort|c_int|c_uint|c_long|c_ulong|size_t|ptrdiff_t|clock_t|time_t|c_longlong|c_ulonglong|intptr_t|uintptr_t|off_t|dev_t|ino_t|pid_t|mode_t|ssize_t)\\b"},{token:"variable.language.source.rust",regex:"\\bself\\b"},{token:"comment.line.doc.source.rust",regex:"//!.*$"},{token:"comment.line.double-dash.source.rust",regex:"//.*$"},{token:"comment.start.block.source.rust",regex:"/\\*",stateName:"comment",push:[{token:"comment.start.block.source.rust",regex:"/\\*",push:"comment"},{token:"comment.end.block.source.rust",regex:"\\*/",next:"pop"},{defaultToken:"comment.block.source.rust"}]},{token:"keyword.operator",regex:/\$|[-=]>|[-+%^=!&|<>]=?|[*/](?![*/])=?/},{token:"punctuation.operator",regex:/[?:,;.]/},{token:"paren.lparen",regex:/[\[({]/},{token:"paren.rparen",regex:/[\])}]/},{token:"constant.language.source.rust",regex:"\\b(?:true|false|Some|None|Ok|Err)\\b"},{token:"support.constant.source.rust",regex:"\\b(?:EXIT_FAILURE|EXIT_SUCCESS|RAND_MAX|EOF|SEEK_SET|SEEK_CUR|SEEK_END|_IOFBF|_IONBF|_IOLBF|BUFSIZ|FOPEN_MAX|FILENAME_MAX|L_tmpnam|TMP_MAX|O_RDONLY|O_WRONLY|O_RDWR|O_APPEND|O_CREAT|O_EXCL|O_TRUNC|S_IFIFO|S_IFCHR|S_IFBLK|S_IFDIR|S_IFREG|S_IFMT|S_IEXEC|S_IWRITE|S_IREAD|S_IRWXU|S_IXUSR|S_IWUSR|S_IRUSR|F_OK|R_OK|W_OK|X_OK|STDIN_FILENO|STDOUT_FILENO|STDERR_FILENO)\\b"},{token:"meta.preprocessor.source.rust",regex:"\\b\\w\\(\\w\\)*!|#\\[[\\w=\\(\\)_]+\\]\\b"},{token:"constant.numeric.source.rust",regex:/\b(?:0x[a-fA-F0-9_]+|0o[0-7_]+|0b[01_]+|[0-9][0-9_]*(?!\.))(?:[iu](?:size|8|16|32|64|128))?\b/},{token:"constant.numeric.source.rust",regex:/\b(?:[0-9][0-9_]*)(?:\.[0-9][0-9_]*)?(?:[Ee][+-][0-9][0-9_]*)?(?:f32|f64)?\b/}]},this.normalizeRules()};o.metaData={fileTypes:["rs","rc"],foldingStartMarker:"^.*\\bfn\\s*(\\w+\\s*)?\\([^\\)]*\\)(\\s*\\{[^\\}]*)?\\s*$",foldingStopMarker:"^\\s*\\}",name:"Rust",scopeName:"source.rust"},r.inherits(o,i),t.RustHighlightRules=o}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/([\{\[\(])[^\}\]\)]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{\(]*([\}\]\)])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),ace.define("ace/mode/rust",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/rust_highlight_rules","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./rust_highlight_rules").RustHighlightRules,o=e("./folding/cstyle").FoldMode,u=function(){this.HighlightRules=s,this.foldingRules=new o,this.$behaviour=this.$defaultBehaviour};r.inherits(u,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/",nestable:!0},this.$quotes={'"':'"'},this.$id="ace/mode/rust"}.call(u.prototype),t.Mode=u}); (function() { + ace.require(["ace/mode/rust"], function(m) { + if (typeof module == "object" && typeof exports == "object" && module) { + module.exports = m; + } + }); + })(); diff -Nru clamav-0.103.2+dfsg/docs/html/print.html clamav-0.103.5+dfsg/docs/html/print.html --- clamav-0.103.2+dfsg/docs/html/print.html 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/print.html 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,7643 @@ + + + + + + ClamAV Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+

ClamAV

+

+ + Maeve, the ClamAV mascot + +

+

ClamAV is an open source (GPLv2) anti-virus toolkit, designed especially for e-mail scanning on mail gateways. It provides a number of utilities including a flexible and scalable multi-threaded daemon, a command line scanner and advanced tool for automatic database updates. The core of the package is an anti-virus engine available in a form of shared library.

+
+

Tip: ClamAV is not a traditional anti-virus or endpoint security suite. For a fully featured modern endpoint security suite, check out Cisco Secure Endpoint. See "related products", below, for more details.

+
+

ClamAV is brought to you by Cisco Systems, Inc.

+

Community Projects

+

ClamAV has a diverse ecosystem of community projects, products, and other tools that either depend on ClamAV to provide malware detection capabilities or supplement ClamAV with new features such as improved support for 3rd party signature databases, graphical user interfaces (GUI), and more.

+

Features

+
    +
  • ClamAV is designed to scan files quickly.
  • +
  • Real time protection (Linux only). The ClamOnAcc client for the ClamD scanning daemon provides on-access scanning on modern versions of Linux. This includes an optional capability to block file access until a file has been scanned (on-access prevention).
  • +
  • ClamAV detects millions of viruses, worms, trojans, and other malware, including Microsoft Office macro viruses, mobile malware, and other threats.
  • +
  • ClamAV's bytecode signature runtime, powered by either LLVM or our custom bytecode interpreter, allows the ClamAV signature writers to create and distribute very complex detection routines and remotely enhance the scanner’s functionality.
  • +
  • Signed signature databases ensure that ClamAV will only execute trusted signature definitions.
  • +
  • ClamAV scans within archives and compressed files but also protects against archive bombs. Built-in archive extraction capabilities include: +
      +
    • Zip (including SFX, excluding some newer or more complex extensions)
    • +
    • RAR (including SFX, most versions)
    • +
    • 7Zip
    • +
    • ARJ (including SFX)
    • +
    • Tar
    • +
    • CPIO
    • +
    • Gzip
    • +
    • Bzip2
    • +
    • DMG
    • +
    • IMG
    • +
    • ISO 9660
    • +
    • PKG
    • +
    • HFS+ partition
    • +
    • HFSX partition
    • +
    • APM disk image
    • +
    • GPT disk image
    • +
    • MBR disk image
    • +
    • XAR
    • +
    • XZ
    • +
    • Microsoft OLE2 (Office documments)
    • +
    • Microsoft OOXML (Office documments)
    • +
    • Microsoft Cabinet Files (including SFX)
    • +
    • Microsoft CHM (Compiled HTML)
    • +
    • Microsoft SZDD compression format
    • +
    • HWP (Hangul Word Processor documents)
    • +
    • BinHex
    • +
    • SIS (SymbianOS packages)
    • +
    • AutoIt
    • +
    • InstallShield
    • +
    • ESTsoft EGG
    • +
    +
  • +
  • Supports Windows executable file parsing, also known as Portable Executables (PE) both 32/64-bit, including PE files that are compressed or obfuscated with: +
      +
    • AsPack
    • +
    • UPX
    • +
    • FSG
    • +
    • Petite
    • +
    • PeSpin
    • +
    • NsPack
    • +
    • wwpack32
    • +
    • MEW
    • +
    • Upack
    • +
    • Y0da Cryptor
    • +
    +
  • +
  • Supports ELF and Mach-O files (both 32 and 64-bit)
  • +
  • Supports almost all mail file formats
  • +
  • Support for other special files/formats includes: +
      +
    • HTML
    • +
    • RTF
    • +
    • PDF
    • +
    • Files encrypted with CryptFF and ScrEnc
    • +
    • uuencode
    • +
    • TNEF (winmail.dat)
    • +
    +
  • +
  • Advanced database updater with support for scripted updates, digital signatures and DNS based database version queries
  • +
+
+

Disclaimer: Many of the above file formats continue to evolve. Executable packing and obfuscation tools in particular are constantly changing. We cannot guarantee that we can unpack or extract every version or variant of the listed formats.

+
+

License

+

ClamAV is licensed under the GNU General Public License, Version 2.

+

Supported platforms

+

Clam AntiVirus is highly cross-platform. The development team cannot test every OS, so we have chosen to test ClamAV using the two most recent Long Term Support (LTS) versions of each of the most popular desktop operating systems. Our regularly tested operating systems include:

+
    +
  • GNU/Linux +
      +
    • Alpine +
        +
      • 3.11 (64bit)
      • +
      +
    • +
    • Ubuntu +
        +
      • 18.04 (64bit, 32bit)
      • +
      • 20.04 (64bit)
      • +
      +
    • +
    • Debian +
        +
      • 9 (64bit, 32bit)
      • +
      • 10 (64bit, 32bit)
      • +
      +
    • +
    • CentOS +
        +
      • 7 (64bit, 32bit)
      • +
      • 8 (64bit)
      • +
      +
    • +
    • Fedora +
        +
      • 30 (64bit)
      • +
      • 31 (64bit)
      • +
      +
    • +
    • openSUSE +
        +
      • Leap (64bit)
      • +
      +
    • +
    +
  • +
  • UNIX +
      +
    • FreeBSD +
        +
      • 11 (64bit)
      • +
      • 12 (64bit)
      • +
      +
    • +
    • macOS +
        +
      • 10.13 High Sierra (x86_64)
      • +
      • 10.15 Catalina (x86_64)
      • +
      • 11.5 Big Sur (x86_64, arm64)
      • +
      +
    • +
    +
  • +
  • Windows +
      +
    • 7 (64bit, 32bit)
    • +
    • 10 (64bit, 32bit)
    • +
    +
  • +
+ +

The following minimum recommended system requirements are for using ClamScan or ClamD applications with the standard ClamAV signature database provided by Cisco.

+

Minimum recommended RAM for ClamAV:

+
    +
  • FreeBSD and Linux server edition: 2 GiB+
  • +
  • Linux non-server edition: 2 GiB+
  • +
  • Windows 7 & 10 32-bit: 2 GiB+
  • +
  • Windows 7 & 10 64-bit: 3 GiB+
  • +
  • macOS: 3 GiB+
  • +
+

Minimum recommended CPU for ClamAV:

+
    +
  • 1 CPU at 2.0 Ghz+
  • +
+

Minimum available hard disk space required:

+

For the ClamAV application we recommend having 5 GB of free space available. This recommendation is in addition to the recommended disk space for each OS.

+
+

Note: The tests to determine these minimum requirements were performed on systems that were not running other applications. If other applications are being run on the system, additional resources will be required in addition to our recommended minimums.

+
+

Mailing Lists and Chat

+

If you have a trouble installing or using ClamAV try asking on our mailing lists. There are four lists available:

+
    +
  • clamav-announce (at) lists.clamav.net +
      +
    • info about new versions, moderated.
    • +
    • Subscribers are not allowed to post to this mailing list.
    • +
    +
  • +
  • clamav-users (at) lists.clamav.net +
      +
    • user questions
    • +
    +
  • +
  • clamav-devel (at) lists.clamav.net +
      +
    • technical discussions
    • +
    +
  • +
  • clamav-virusdb (at) lists.clamav.net +
      +
    • database update announcements, moderated
    • +
    +
  • +
  • clamav-binary (at) lists.clamav.net +
      +
    • discussion and announcements for package maintainers
    • +
    +
  • +
+

You can subscribe and search the mailing list archives here.

+

You can also join the community on our ClamAV Discord chat server.

+

Submitting New or Otherwise Undetected Malware

+

If you've got a virus which is not detected by the current version of ClamAV using the latest signature databases, please submit the sample for review at our website:

+

https://www.clamav.net/reports/malware

+

Likewise, if you have a benign file that is flagging as a virus and you wish to report a False Positive, please submit the sample for review at our website:

+

https://www.clamav.net/reports/fp

+ +

Cisco Secure Endpoint (formerly AMP for Endpoints) is Cisco's cloud-based security suite for commercial and enterprise customers. Secure Endpoint is available for Windows, Linux, and macOS and provides superior malware detection capabilities, behavioral monitoring, dynamic file analysis, endpoint isolation, analytics, and threat hunting. Secure Endpoint sports a modern administrative web interface (dashboard).

+

Immunet is a cloud-based antivirus application for Windows that is free for non-commercial use. Immunet offers great malware detection efficacy but, as a completely free product, Immunet's does not have same features or the quality user experience that Secure Endpoint offers. There is an Immunet user forum but Cisco offers no official user support.

+

+ + Cisco Systems, Inc + +

+

Installing ClamAV

+ +

Installing with a Package Manager

+

ClamAV is widely available from third party package managers for most operating systems. This is often the quickest way to install ClamAV. It will make also upgrades easier.

+

Check out the Packages page to find installation instructions for your system.

+

Installing with an Installer

+

Pre-compiled packages provided on the clamav.net downloads page have all external library dependencies statically compiled in.

+

These installers likely differ from packages provided by other packaging tools in that you will need to create and configure the freshclam.conf and clamd.conf files. You may also need to add a clamav service user account and adjust the permissions on the database directory. We hope to round out these sharp corners in the future and to make setup more convenient, but for now be advised that setup from one of these packages is a little bit more work than you may be used to.

+

If you're interested in learning how these packages were built, you can check out these development instructions.

+
+

Note: In the event that a vulnerability is found in one of the dependencies that may impact ClamAV, we will publish new packages with updated dependencies as soon as we're able.

+
+

Linux (.deb, .rpm)

+

Beginning with ClamAV 0.104, we offer Debian and RPM packages for x86_64 (64bit) and i686 (32bit) architectures. This will make it easier to get the latest version in the event that a package for your distribution is not readily available and you would prefer not to build ClamAV from source.

+
+

Note: These packages do not presently include clamav-milter. You can help help us add clamav-milter to the packages by developing a Mussels recipe for building the libmilter.a static library and contributing it to our Mussels cookbook.

+
+

RPM packages (for CentOS, Redhat, Fedora, SUSE, etc.)

+

These are compiled on CentOS 7. They should be compatible with all RPM-based linux distributions running glibc version 2.17 or newer.

+

To install, download the package for your system use yum or dnf to install the package. For example:

+
sudo dnf install ~/Downloads/clamav-0.104.0-rc2.linux.x86_64.rpm
+
+

You can verify that the package was installed using:

+
dnf info clamav
+
+

This package installs to /usr/local.

+

Unlike packages provided by Debian or other distributions, this package does not presently include a preconfigured freshclam.conf, clamd.conf, database directory, or clamav user accounts for FreshClam and ClamD. You can follow these instructions to configure FreshClam and ClamD. You can follow these instructions to create the clamav user account for running FreshClam and ClamD services.

+

And uninstall the package with:

+
sudo dnf remove ~/Downloads/clamav-0.104.0-rc2.linux.x86_64.rpm
+
+

DEB packages (for Debian, Ubuntu, Mint, etc.)

+

These are compiled on Ubuntu 16.04, and have all external library dependencies statically compiled in. They should be compatible with all Debian-based linux distributions running glibc version 2.23 or newer.

+
sudo apt install ~/Downloads/clamav-0.104.0-rc2.libnux.x86_64.deb
+
+

You can verify that the package was installed using:

+
apt info clamav
+
+

This package installs to /usr/local.

+

Unlike packages provided by Debian or other distributions, this package does not presently include a preconfigured freshclam.conf, clamd.conf, database directory, or clamav user accounts for FreshClam and ClamD. You can follow these instructions to configure FreshClam and ClamD. You can follow these instructions to create the clamav user account for running FreshClam and ClamD services.

+

And uninstall the package with:

+
sudo apt remove clamav
+
+

macOS

+

Beginning with ClamAV 0.104, we offer a PKG installer for macOS. These are universal binaries built for Intel x86_64 and Apple M1 arm64 processors.

+
+

Disclaimer: The release materials for 0.104.0-rc2 are not signed or notarized. We are working on adding signing and notarization to our CI processes, but for now you may be unable to use this PKG installer on macOS Big Sur or newer.

+
+

To install, download the macOS .pkg installer. Double-click the installer and follow the directions.

+

This package installs to /usr/local/clamav. This is not in the default system PATH environment variable. You may wish to add /usr/local/clamav/bin and /usr/local/clamav/sbin to your PATH so you can run the ClamAV programs without entering the full path. To do this add this line to ~/.zshrc:

+
export PATH=/usr/local/clamav/bin:/usr/local/clamav/sbin:$PATH
+
+

Then run source ~/.zshrc or open a new terminal.

+

Unlike packages provided by Homebrew, this package does not presently include a preconfigured freshclam.conf, clamd.conf, or database directory. You can follow these instructions to configure FreshClam and ClamD.

+

macOS package installers do not provide a mechanism for automatically uninstalling the package. In the future, we hope to add a script to aid with uninstallation. But for now, to make it easier to remove, our macOS installer installs to /usr/local/clamav. To uninstall, all you need to do is run:

+
sudo rm -rf /usr/local/clamav
+
+

Windows

+

The ClamAV team provides official ClamAV builds for Windows on the ClamAV downloads page. You can choose between a traditional executable installer or a portable install ZIP package.

+

To use the executable installer, double-click the installer and follow the instructions.

+

To install from a ZIP package, unzip the portable install package to any directory.

+

Official ClamAV Docker Images

+

There are now official ClamAV images on Docker Hub. You can find the images on Docker Hub under clamav.

+

At present we offer images with builds of the latest development version. We call this "unstable". ClamAV 0.104 will be the first stable release that we'll publish on Docker Hub.. Once published 0.104.0+ will be available using a Docker image tag with the specific version number, or using "stable" to get the latest stable release.

+

Check out the Docker page to learn how to install and use ClamAV with Docker.

+

Installing from Source

+

If you need, you can also compile and install ClamAV from source:

+ +

What now?

+

Now that ClamAV is installed, you will want to customize your configuration and perhaps set up some scanning automation and alerting mechanisms.

+

Continue on to "Configuration"...

+

ClamAV Packages

+

Many Linux and Unix distributions offer one or more ClamAV packages to make it easy for you to install ClamAV.

+

These packages are usually well maintained but if you find an issue with one, please consider helping the volunteers that maintain the packages.

+
+

Disclaimer: ClamAV packages may vary somewhat from the upstream version. +Some examples:

+
    +
  • +

    The database and application config paths may vary:

    +
      +
    • +

      A default from-source install will go in /usr/local, with:

      +
        +
      • applications in /usr/local/bin
      • +
      • daemons in /usr/local/sbin
      • +
      • libraries in /usr/local/lib
      • +
      • headers in /usr/local/include
      • +
      • configs in /usr/local/etc/
      • +
      • databases in /usr/local/share/clamav/
      • +
      +
    • +
    • +

      A Linux package install will probably go in /usr, with:

      +
        +
      • applications in /usr/bin
      • +
      • daemons in /usr/sbin
      • +
      • libraries in /usr/lib
      • +
      • headers in /usr/include
      • +
      • configs in /etc/clamav
      • +
      • databases in /var/lib/clamav
      • +
      +
    • +
    +
  • +
  • +

    As of 0.103.x, a from-source install requires the user create a config for FreshClam, ClamD, and ClamAV-Milter in order to use each application. A package install, however, is likely to come pre-configured. Users may wish to modify the configs as needed.

    +
  • +
  • +

    Package installs sometimes carry extra patches for issues affecting their distribution, for issues the ClamAV developers haven't had time to fix or are unaware of, and for security issues when distributing older versions that are no longer maintained by the ClamAV developers.

    +
  • +
  • +

    Some distributions parcel up ClamAV components into separate packages. You don't necessarily need all of the packages. If this applies to your package system, you may need to review the applications described in the scanning instructions to understand which features you will need.

    +
  • +
+
+
+

Acknowledgments: Thank you to all of the volunteers who maintain these packages! We appreciate your help!

+
+

The Packages

+

Debian

+

Debian splits up ClamAV into a selection of different packages.

+

Realistically, you probably only need to apt install clamav and probably apt install clamav-daemon. If you require support for scanning compressed RAR files you first need to enable the "non-free" archive.*

+

The full list of packages includes:

+
    +
  • clamav - command-line interface
  • +
  • clamav-base - base package
  • +
  • clamav-daemon - scanner daemon
  • +
  • clamav-docs - documentation
  • +
  • clamav-freshclam - virus database update utility
  • +
  • clamav-milter - sendmail integration
  • +
  • clamav-testfiles - test files
  • +
  • libclamav-dev - development files
  • +
  • libclamav9 - library
  • +
  • libclamunrar9 - unrar support
  • +
+
+

* RAR Support: ClamAV's RAR support comes from UnRAR, which is open-source but not entirely free in so far as its license restricts users from reverse engineering it to create RAR archives. For this reason, it is bundled separately in the "non-free" archive. Enable it by adding "non-free" to /etc/apt/sources.list. Eg:

+

deb http://http.us.debian.org/debian stable main contrib non-free

+

Then you can install the RAR-plugin using: apt install libclamunrar9

+
+

There are a variety of other ClamAV related projects as well. Run apt search clamav to see a larger list.

+

To test the installation, you can try to scan the test files in the clamav-testfiles package.

+
+

Note: Debian packages are maintained by Debian's ClamAV Team.

+

The package maintainers can be reached at clamav-devel at lists.alith.debian.org. More info at tracker.debian.org/pkg/clamav.

+

Patches: https://salsa.debian.org/clamav-team/clamav/tree/unstable/debian/patches

+
+

Ubuntu

+

Ubuntu's ClamAV packages are derived from the Debian packages (above). See the Debian instructions for installation details.

+
+

RAR Support: As with Debian, RAR support is not included in the base package. Users that desire RAR support will have to install libclamunar9 separately. Unlike with Debian, there is no need to enable "non-free" packages for this to work.

+
+
+

Note: Ubuntu packages are curated by Ubuntu Developers. +Package source: https://packages.ubuntu.com/source/clamav

+
+

openSUSE

+

openSUSE provides two packages:

+
    +
  • clamav - The clamav package
  • +
  • clamav-devel - The clamav package plus headers for software development.
  • +
+

RPM download

+

Find these packages at under http://download.opensuse.org/repositories/security +Eg.:

+
    +
  • http://download.opensuse.org/repositories/security/openSUSE_Leap_15.3/x86_64/clamav-0.103.1-lp153.234.4.x86_64.rpm.mirrorlist
  • +
  • http://download.opensuse.org/repositories/security/openSUSE_Leap_15.3/x86_64/clamav-devel-0.103.1-lp153.234.4.x86_64.rpm.mirrorlist
  • +
+

Use the update variant for openSUSE, add it to your installation as another repository using YaST or zypper and give it a higher priority (lower number) than the repository that delivers the official updates.

+
+

Tip: RPMs of new ClamAV versions for existing SUSE products are provided through the respective online update channels. As these packages have to go through QA, it usually takes some time for a new ClamAV source release to appear as an official RPM. For those who want the newest version, packages are available from the security project in the openSUSE Build Service.

+
+

Zypper

+

Install ClamAV with zypper:

+
  zypper install -y clamav
+
+
+

Note: openSUSE packages are maintained by Reinhard Max.

+
+

EPEL: Fedora, RHEL, and CentOS

+

EPEL creates ClamAV packages for Fedora (as well as EPEL-enabled CentOS and RHEL). For more information on EPEL, visit their wiki.

+

To enable EPEL for CentOS:

+
dnf install -y epel-release
+
+

EPEL offers a selection of packages to install ClamAV:

+
    +
  • clamd - The Clam AntiVirus Daemon
  • +
  • clamav - End-user tools for the Clam Antivirus scanner
  • +
  • clamav-data - Virus signature data for the Clam Antivirus scanner
  • +
  • clamav-devel - Header files and libraries for the Clam Antivirus scanner
  • +
  • clamav-lib - Dynamic libraries for the Clam Antivirus scanner
  • +
  • clamav-milter - Milter module for the Clam Antivirus scanner
  • +
  • clamav-update - Auto-updater for the Clam Antivirus scanner data-files
  • +
+

Most users will only need to run:

+
dnf install -y clamav clamd clamav-update
+
+
+

Tips

+

CentOS: On Community Enterprise Operating System (CentOS) the ClamAV package requires the Extra Packages for Enterprise Linux (EPEL) repository.

+

RHEL: On RedHat Enterprise Linux (RHEL) the EPEL release package has to be installed either manually or through RHN.

+

Fedora: Fedora packages can be found at https://src.fedoraproject.org/rpms/clamav

+

Fedora's packaging is more customized than most. Please review the RPM notes when troubleshooting your Fedora package configuration.

+
+

Gentoo

+

ClamAV is available in portage under /usr/portage/app-antivirus/clamav

+

To install, run:

+
emerge clamav
+
+

For more details, see the package entry on Portage.

+

FreeBSD, OpenBSD, NetBSD

+

Although all these systems offer the possibility to use ports or pkgsrc, you can install the pre-built package:

+

FreeBSD

+

FreeBSD offers two ClamAV ports (packages):

+
    +
  • clamav
  • +
  • clamav-devel
  • +
+

To install, run:

+
pkg install clamav
+
+
+

Note: For more details, see:

+
    +
  • https://www.freshports.org/security/clamav
  • +
  • https://www.freshports.org/security/clamav-devel
  • +
+
+

OpenBSD

+

To install, run:

+
  pkg_add clamav
+
+

NetBSD

+

To install, run:

+
  pkgin install clamav
+
+

Solaris

+

OpenCSW is a community software project for Solaris 8+ on both Sparc and x86. It packages more than 2000 popular open source titles and they can all easily be installed with dependency handling via pkgutil which is modeled after Debian's apt-get.

+
pkgutil -i clamav
+
+
+

Note: The package can be found on OpenCSW though it is unfortuantely quite out-of-date.

+
+
+

Disclaimer: ClamAV is also no longer supported on Solaris because Solaris is proprietary, less commonly used, and difficult to work with. Future versions of ClamAV will depend on components written in the Rust programming language, which also does not support building directly on Solaris. It is likely that ClamAV will no longer work on Solaris in the future.

+
+

Slackware

+

You can download ClamAV builds for Slackware from https://slackbuilds.org/repository/14.2/system/clamav/

+

Download the package, and as root, install it like so (substituting the appropriate filename):

+
  installpkg clamav.tar.gz
+
+

macOS

+

ClamAV can be easily installed on macOS using one of these popular package managers:

+ +

Homebrew

+

Install Homebrew if you don't already have it. Then run:

+
  brew install clamav
+
+

Homebrew installs versioned packages to /usr/local/Cellar/<pacakge>/<version> with symlinks in /usr/local/opt/<pacakge> to the current version. Symlinks for ClamAV's executables will be placed in /usr/local/bin to add them to your PATH. ClamAV's config files will be placed in /usr/local/etc/clamav.

+

As with most other installation methods, you may need to do the following at a minimum before you can run freshclam, clamscan, or use clamdscan with clamd:

+
    +
  1. Create /usr/local/etc/clamav/freshclam.conf from /usr/local/etc/clamav/freshclam.conf.sample.
  2. +
  3. Remove or comment-out the Example line from freshclam.conf
  4. +
  5. Run freshclam to download the latest malware definitions.
  6. +
+

If you wish to run clamd you'll also need to create /usr/local/etc/clamav/clamd.conf from /usr/local/etc/clamav/clamd.conf.sample, and configure clamd.conf with Local/Unix socket settings (preferred), or TCP socket settings.

+

MacPorts

+

Install MacPorts if you don't already have it. Then run:

+
  sudo port install clamav
+
+

MacPorts installs versioned packages to /opt/local/. ClamAV's config files will be placed in /opt/local/etc.

+

As with most other installation methods, you may need to do the following at a minimum before you can run freshclam, clamscan, or use clamdscan with clamd:

+
    +
  1. Create /opt/local/etc/freshclam.conf from /opt/local/etc/freshclam.conf.sample.
  2. +
  3. Remove or comment-out the Example line from freshclam.conf
  4. +
  5. Run freshclam to download the latest malware definitions.
  6. +
+

If you wish to run clamd you'll also need to create /opt/local/etc/clamd.conf from /opt/local/etc/clamd.conf.sample, and configure clamd.conf with Local/Unix socket settings (preferred), or TCP socket settings.

+

ClamAV in Docker

+

ClamAV can be run within a Docker container. This provides isolation from other processes by running it in a containerized environment. If new or unfamiliar with Docker, containers or cgroups see docker.com.

+

The official images on Docker Hub

+

ClamAV image tags on Docker Hub follow this naming convention:

+
    +
  • +

    clamav/clamav:<version>: A release preloaded with signature databases.

    +

    Using this container will save the ClamAV project some bandwidth. Use this if you will keep the image around so that you don't download the entire database set every time you start a new container. Updating with FreshClam from existing databases set does not use much data.

    +
  • +
  • +

    clamav/clamav:<version>_base: A release with no signature databases.

    +

    Use this container only if you mount a volume in your container under /var/lib/clamav to persist your signature database databases. This method is the best option because it will reduce data costs for ClamAV and for the Docker registry, but it does require advanced familiarity with Linux and Docker.

    +
    +

    Caution: Using this image without mounting an existing database directory will cause FreshClam to download the entire database set each time you start a new container.

    +
    +
  • +
+

You can use the unstable version (i.e. clamav/clamav:unstable or +clamav/clamav:unstable_base) to try the latest from our development branch.

+

Building the ClamAV image

+

While it is recommended to pull the image from our Docker Hub registry, some may want to build the image locally instead. All that is needed is:

+
docker build --tag "clamav:TICKET-123" .
+
+

in the current directory. This will build the ClamAV image and tag it with the name "clamav:TICKET-123". Any name can generally be used and it is this name that needs to be referred to later when running the image.

+

Running ClamD

+

To run clamd in a Docker container, first, an image either has to be built or pulled from a Docker registry.

+

Running ClamD using the official ClamAV images from Docker Hub

+

To pull the ClamAV "unstable" image from Docker Hub, run:

+
docker pull clamav/clamav:unstable
+
+
+

Tip: Substitute unstable with a different version as needed.

+
+

To pull and run the official ClamAV images from the Docker Hub registry, try the following command:

+
docker run \
+    --interactive \
+    --tty \
+    --rm \
+    --name "clam_container_01" \
+    clamav/clamav:unstable
+
+

The above creates an interactive container with the current TTY connected to it. This is optional but useful when getting started as it allows one to directly see the output and, in the case of clamd, send ctrl-c to close the container. The --rm parameter ensures the container is cleaned up again after it exits and the --name parameter names the container, so it can be referenced through other (Docker) commands, as several containers of the same image can be started without conflicts.

+
+

Note: Pulling is not always required. docker run will pull the image if it cannot be found locally. docker run --pull always will always pull beforehand to ensure the most up-to-date container is being used. Do not use --pull always with the larger ClamAV images.

+
+
+

Tip: It's common to see -it instead of --interactive --tty.

+
+

Running ClamD using a Locally Built Image

+

You can run a container using an image built locally (see "Building the ClamAV Image"). Just run:

+
docker run -it --rm \
+    --name "clam_container_01" \
+    clamav:TICKET-123
+
+

Persisting the virus database (volume)

+

The virus database in /var/lib/clamav is by default unique to each container and thus is normally not shared. For simple setups this is fine, where only one instance of clamd is expected to run in a dockerized environment. However some use cases may want to efficiently share the database or at least persist it across short-lived ClamAV containers.

+

To do so, you have two options:

+
    +
  1. +

    Create a Docker volume using the +docker volume command. +Volumes are completely managed by Docker and are the best choice for creating a persistent database volume.

    +

    For example, create a "clam_db" volume:

    +
    docker volume create clam_db
    +
    +

    Then start one or more containers using this volume. The first container to use a new database volume will download the full database set. Subsequent containers will use the existing databases and may update them as needed:

    +
    docker run -it --rm \
    +    --name "clam_container_01" \
    +    --mount source=clam_db,target=/var/lib/clamav \
    +    clamav/clamav:unstable_base
    +
    +
  2. +
  3. +

    Create a Bind Mount that maps a file system directory to a path within the container. Bind Mounts depend on the directory structure, permissions, and operating system of the Docker host machine.

    +

    Run the container with these arguments to mount the a directory from your host environment as a volume in the container.

    +
        --mount type=bind,source=/path/to/databases,target=/var/lib/clamav
    +
    +

    When doing this, it's best to use the <version>_base image tags so as to save on bandwith. E.g.:

    +
    docker run -it --rm \
    +    --name "clam_container_01" \
    +    --mount type=bind,source=/path/to/databases,target=/var/lib/clamav \
    +    clamav/clamav:unstable_base
    +
    +
    +

    Disclaimer: When using a Bind Mount, the container's entrypoint script will change ownership of this directory to its "clamav" user. This enables FreshClam and ClamD with the required permissions to read and write to the directory, though these changes will also affect those files on the host.

    +
    +
  4. +
+

If you're thinking about running multiple containers that share a single database volume, here are some notes on how this might work.

+

Running Clam(D)Scan

+

Scanning files using clamscan or clamdscan is possible in various ways with Docker. This section briefly describes them, but the other sections of this document are best read before hand to better understand some of the concepts.

+

One important aspect is however to realize that Docker by default does not have access to any of the hosts files. And so to scan these within Docker, they need to be mounted with a bind mount to be made accessible.

+

For example, running the container with these arguments ...

+
    --mount type=bind,source=/path/to/scan,target=/scandir
+    --mount type=bind,source=/path/to/scan,target=/scandir
+
+

... would make the hosts file/directory /path/to/scan available in the container as /scandir and thus invoking clamscan would thus be done on /scandir.

+

Note that while technically possible to run either scanners via docker exec this is not described as it is unlikely the container has access to the files to be scanned.

+

ClamScan

+

Using clamscan outside of the Docker container is how normally clamscan is invoked. To make use of the available shared dockerized resources however, it is possible to expose the virus database and share that for example. E.g. it could be possible to run a Docker container with only the freshclam daemon running, and share the virus database directory /var/lib/clamav. This could be useful for file servers for example, where only clamscan is installed on the host, and freshclam is managed in a Docker container.

+
+

Note: Running the freshclam daemon separated from clamd is less recommended, unless the clamd socket is shared with freshclam as freshclam would not be able to inform clamd of database updates.

+
+

Dockerized ClamScan

+

To run clamscan in a Docker container, the Docker container can be invoked as:

+
docker run -it --rm \
+    --mount type=bind,source=/path/to/scan,target=/scandir \
+    clamav/clamav:unstable \
+    clamscan /scandir
+
+

However, this will use whatever signatures are found in the image, which may be slightly out of date. If using clamscan in this way, it would be best to use a database volume that is up-to-date so that you scan with the latest signatures. E.g.:

+
docker run -it --rm \
+    --mount type=bind,source=/path/to/scan,target=/scandir \
+    --mount type=bind,source=/path/to/databases,target=/var/lib/clamav \
+    clamav/clamav:unstable_base \
+    clamscan /scandir
+
+

ClamDScan

+

As with clamscan, clamdscan can also be run when installed on the host, by connecting to the dockerized clamd. This can be done by either pointing clamdscan to the exposed TCP/UDP port or unix socket.

+

Dockerized ClamDScan

+

Running both clamd and clamdscan is also easily possible, as all that is needed is the shared socket between the two containers. The only cavaet here is to:

+
    +
  1. mount the files to be scanned in the container that will run clamd, or
  2. +
  3. mount the files to be scanned in the container that will clamdscan run if using clamdscan --stream. The --stream option will be slower, but enables submitting files from a different machine on a network.
  4. +
+

For example:

+
docker run -it --rm \
+    --mount type=bind,source=/path/to/scan,target=/scandir \
+    --mount type=bind,source=/var/lib/docker/data/clamav/sockets/,target=/run/clamav/ \
+    clamav/clamav:unstable
+
+
docker run -it --rm \
+    --mount type=bind,source=/path/to/scan,target=/scandir \
+    --mount type=bind,source=/var/lib/docker/data/clamav/sockets/,target=/run/clamav/ \
+    clamav/clamav:unstable_base \
+    clamdscan /scandir
+
+

Controlling the container

+

The ClamAV container actually runs both freshclam and clamd daemons by default. Optionally available to the container is ClamAV's milter daemon. To control the behavior of the services started within the container, the following flags can be passed to the docker run command with the --env (-e) parameter.

+
    +
  • CLAMAV_NO_CLAMD [true|false] Do not start clamd. +(default: start clamd)
  • +
  • CLAMAV_NO_FRESHCLAMD [true|false] Do not start the freshclam daemon. +(default: start the freshclam daemon)
  • +
  • CLAMAV_NO_MILTERD [true|false] Do not start the clamav-milter daemon. +(default: start the clamav-milter daemon )
  • +
  • CLAMD_STARTUP_TIMEOUT [integer] Seconds to wait for clamd to start. +(default: 1800)
  • +
  • FRESHCLAM_CHECKS [integer] freshclam daily update frequency. +(default: once per day)
  • +
+

So to additionally also enable clamav-milter, the following flag can be added:

+
    --env 'CLAMAV_NO_MILTERD=false'
+
+

Further more, all of the configuration files that live in /etc/clamav can be overridden by doing a volume-mount to the specific file. The following argument can be added for this purpose. The example uses the entire configuration directory, but this can be supplied multiple times if individual files deem to be replaced.

+
    --mount type=bind,source=/full/path/to/clamav/,target/etc/clamav
+
+
+

Note: Even when disabling the freshclam daemon, freshclam will always run at least once during container startup if there is no virus database. While not recommended, the virus database location itself /var/lib/clamav/ could be a persistent Docker volume. This however is slightly more advanced and out of scope of this document.

+
+

Connecting to the container

+

Executing commands within a running container

+

To connect to a running ClamAV container, docker exec can be used to run a command on an already running container. To do so, the name needs to be either obtained from docker ps or supplied during container start via the --name parameter. The most interesting command in this case can be clamdtop.

+
docker exec --interactive --tty "clamav_container_01" clamdtop
+
+

Alternatively, a shell can be started to inspect and run commands within the +container as well.

+
docker exec --interactive --tty "clamav_container_01" /bin/sh
+
+

Unix sockets

+

The default socket for clamd is located inside the container as /run/clamav/clamd.sock and can be connected to when exposed via a Docker volume mount. To ensure, that clamd within the container can freely create and remove the socket, the path for the socket is to be volume-mounted, to expose it for others on the same host to use. The following volume can be used for this purpose. Do ensure that the directory on the host actually exists and clamav inside the container has permission to access it. Caution is required when managing permissions, as incorrect permission could open clamd for anyone on the host system.

+
    --mount type=bind,source=/var/lib/docker/data/clamav/sockets/,target=/run/clamav/
+
+

With the socket exposed to the host, any other service can now talk to clamd as well. If for example clamdtop where installed on the local host, calling

+
clamdtop "/var/lib/docker/data/clamav/sockets/clamd.sock"
+
+

should work just fine. Likewise, running clamdtop in a different container, but sharing the socket will equally work. While clamdtop works well as an example here, it is of course important to realize, this can also be used to connect a mail server to clamd.

+

TCP

+

ClamAV in the official Docker images is configured to listen for TCP connections on these ports:

+
    +
  • clamd: 3310
  • +
  • clamav-milter: 7357
  • +
+

While clamd and clamav-milter will listen on the above TCP ports, Docker does not expose these by default to the host. Only within containers can these ports be accessed. To expose, or "publish", these ports to the host, and thus potentially over the (inter)network, the --publish (or --publish-all) flag to docker run can be used. While more advanced/secure mappings can be done as per documentation, the basic way is to --publish [<host_port>:]<container_port> to make the port available to the host.

+
    --publish 13310:3310 \
+    --publish 7357
+
+

The above would thus publish:

+
    +
  • clamd port 3310 as 13310 on the host
  • +
  • milter port 7357 as a random to the host. The random port can be inspected via docker ps.
  • +
+

But if you're just running one ClamAV container, you probably will just want to use the default port numbers, which are the same port numbers suggested in the clamd.conf.sample file provided with ClamAV:

+
    --publish 3310:3310 \
+    --publish 7357:7357
+
+
+

Warning: Extreme caution is to be taken when using clamd over TCP as there are no protections on that level. All traffic is un-encrypted. Extra care is to be taken when using TCP communications.

+
+

Container ClamD health-check

+

Docker has the ability to run simple ping checks on services running inside containers. If clamd is running inside the container, Docker will on occasion send a ping to clamd on the default port and wait for the pong from clamd. If clamd fails to respond, Docker will treat this as an error. The healthcheck results can be viewed with docker inspect.

+

Performance

+

The performance impact of running clamd in Docker is negligible. Docker is in essence just a wrapper around Linux's cgroups and cgroups can be thought of as chroot or FreeBSD's jail. All code is executed on the host without any translation. Docker does however do some isolation (through cgroups) to isolate the various systems somewhat.

+

Of course, nothing in life is free, and so there is some overhead. Disk-space being the most prominent one. The Docker container might have some duplication of files for example between the host and the container. Further more, also RAM memory may be duplicated for each instance, as there is no RAM-deduplication. Both of which can be solved on the host however. A filesystem that supports disk-deduplication and a memory manager that does RAM-deduplication.

+

The base container in itself is already very small ~16 MiB, at the time of thiswriting, this cost is still very tiny, where the advantages are very much worththe cost in general.

+

The container including the virus database is about ~240 MiB at the time of this writing.

+

Bandwidth

+

Please, be kind when using 'free' bandwidth, both for the virus databases but also the Docker registry. Try not to download the entire database set or the larger ClamAV database images on a regular basis.

+

Advanced container configurations

+

Multiple containers sharing the same mounted databases

+

You can run multiple containers that share the same database volume, but be aware that the FreshClam daemons on each would compete to update the databases. Most likely, one would update the databases and trigger its ClamD to load the new databases, while the others would be oblivious to the new databases and would continue with the old signatures until the next ClamD self-check.

+

This is fine, honestly. It won't take that long before the new signatures are detected by ClamD's self-check and the databases are reloaded automatically.

+

To reload the databases on all ClamD containers immediately after an update, you could disable the FreshClam daemon when you start the containers. Later, use docker exec to perform an update and again as needed to have ClamD load updated databases.

+
+

Note: This really isn't necessary but you could do this if you wish.

+
+

Exactly how you orchestrate this will depend on your environment. You might do something along these lines:

+
    +
  1. +

    Create a "clam_db" volume, if you don't already have one:

    +
    docker volume create clam_db
    +
    +
  2. +
  3. +

    Start your containers:

    +
    docker run -it --rm \
    +    --name "clam_container_01" \
    +    --mount source=clam_db,target=/var/lib/clamav \
    +    --env 'CLAMAV_NO_FRESHCLAMD=true' \
    +    clamav/clamav:unstable_base
    +
    +

    Wait for the first one to download the databases (if it's a new database volume). Then start more:

    +
    docker run -it --rm \
    +    --name "clam_container_02" \
    +    --mount source=clam_db,target=/var/lib/clamav \
    +    --env 'CLAMAV_NO_FRESHCLAMD=true' \
    +    clamav/clamav:unstable_base
    +
    +
  4. +
  5. +

    Check for updates, as needed:

    +
    docker exec -it clam_container_01 freshclam --on-update-execute=EXIT_1 || \
    +if [ $? == 1 ]; then \
    +    docker exec -it clam_container_01 clamdscan --reload; \
    +    docker exec -it clam_container_02 clamdscan --reload; \
    +fi
    +
    +
  6. +
+

Building ClamAV with CMake (v0.104 and newer)

+

The following are instructions to build ClamAV version 0.104 and newer using CMake.

+
+

Tip: If you wish to build ClamAV version 0.103 or older from source, follow these instructions to build ClamAV using Autotools.

+
+ +
+

Note: Some of the dependencies are optional if you elect to not build all of the command line applications, or elect to only build the libclamav library. Specifically:

+
    +
  • libcurl: required for libfreshclam, freshclam, clamsubmit, clamonacc
  • +
  • ncurses: required for clamdtop
  • +
+

For more information about customized builds and which dependencies can be skipped, please see the INSTALL.md document accompanying the source code.

+
+

Install prerequisites

+
+

Note: Many of the instructions below rely on Python 3's Pip package manager to install CMake. This is because many distributions do not provide a new enough version of CMake required to build ClamAV.

+
+
+

Tip: The Python 3 pytest package is recommended in the instructions below in case the unit tests fail so that the test output is easy to read. You're welcome to skip it. However, if you have Python 2's pytest installed but not Python 3's pytest, the tests may fail to run.

+
+

Alpine

+

As root or with sudo, run:

+
apk update && apk add \
+  `# install tools` \
+  g++ gcc gdb make cmake py3-pytest python3 valgrind \
+  `# install clamav dependencies` \
+  bzip2-dev check-dev curl-dev json-c-dev libmilter-dev libxml2-dev \
+  linux-headers ncurses-dev openssl-dev pcre2-dev zlib-dev
+
+

Redhat / Centos / Fedora

+

For Centos 8, you will probably need to run this to enable EPEL & PowerTools. +As root or with sudo, run:

+
dnf install -y epel-release
+dnf install -y dnf-plugins-core
+dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
+dnf config-manager --set-enabled PowerTools | \
+  dnf config-manager --set-enabled powertools | true
+
+

As root or with sudo, run:

+
dnf install -y \
+  `# install tools` \
+  gcc gcc-c++ make python3 python3-pip valgrind \
+  `# install clamav dependencies` \
+  bzip2-devel check-devel json-c-devel libcurl-devel libxml2-devel \
+  ncurses-devel openssl-devel pcre2-devel sendmail-devel zlib-devel
+
+
+

Note: If you get dnf: command not found, use yum instead.

+
+

As a regular user, run:

+
python3 -m pip install --user cmake pytest
+
+
+

Tip: If you don't have a user account, e.g. in a Docker container, run:

+
python3 -m pip install cmake pytest
+
+
+

SUSE / openSUSE

+

As root or with sudo, run:

+
zypper install -y \
+  `# install tools` \
+  gcc gcc-c++ make python3 python3-pip valgrind \
+  `# install clamav dependencies` \
+  libbz2-devel check-devel libjson-c-devel libcurl-devel libxml2-devel \
+  ncurses-devel libopenssl-devel pcre2-devel sendmail-devel zlib-devel
+
+

As a regular user, run:

+
python3 -m pip install --user cmake pytest
+
+
+

Tip: If you don't have a user account, e.g. in a Docker container, run:

+
python3 -m pip install cmake pytest
+
+
+

Ubuntu / Debian

+

As root or with sudo, run:

+
apt-get update && apt-get install -y \
+  `# install tools` \
+  gcc make pkg-config python3 python3-pip python3-pytest valgrind \
+  `# install clamav dependencies` \
+  check libbz2-dev libcurl4-openssl-dev libjson-c-dev libmilter-dev \
+  libncurses5-dev libpcre2-dev libssl-dev libxml2-dev zlib1g-dev
+
+

As a regular user, run:

+
python3 -m pip install --user cmake
+
+
+

Tip: If you don't have a user account, e.g. in a Docker container, run:

+
python3 -m pip install cmake
+
+
+

macOS

+

The following instructions require you to install HomeBrew to install tools and library dependencies.

+
brew update
+
+packages=(
+  # install tools
+  python3 cmake
+  # install clamav dependencies
+  bzip2 check curl-openssl json-c libxml2 ncurses openssl@1.1 pcre2 zlib
+)
+for item in "${packages[@]}"; do
+  brew install $item || true; brew upgrade $item || brew upgrade $item
+done
+
+python3 -m pip install --user cmake pytest
+
+
+

Note: You may also need to install pkg-config if not already present on your system. You can use Homebrew to do this with: brew install pkg-config

+
+

FreeBSD

+

As root or with sudo, run:

+
pkg install -y \
+  `# install tools` \
+  gmake cmake pkgconf py38-pip python38 \
+  `# install clamav dependencies` \
+  bzip2 check curl json-c libmilter libxml2 ncurses pcre2
+
+

Now as a regular user, run:

+
python3.8 -m pip install --user pytest
+
+
+

Tip: If you don't have a user account, e.g. in a Docker container, run:

+
python3 -m pip install pytest
+
+
+

Adding new system user and group

+

If installing to the system, and if you intend to run freshclam or clamd as as service, you should create a service account before compiling and installing ClamAV.

+

Follow these steps to create a service account.

+

Download the source code

+

Download the source from the clamav.net downloads page.

+

Extract the archive:

+
tar xzf clamav-[ver].tar.gz
+cd clamav-[ver]
+
+

Build ClamAV

+

First, make a "build" subdirectory. This will enable you to easily delete your build files if something goes wrong and you need to re-configure and try again.

+
mkdir build && cd build
+
+

Next, select the build options you desire. For a full list of configuration options, see the "Custom CMake options" section in the INSTALL.md file included with the source code.

+

To help you get started, here are some popular build configurations.

+

The Default Build

+

The default build type is RelWithDebInfo, that is "Release mode with Debugging symbols". It will install to /usr/local.

+
cmake ..
+cmake --build .
+ctest
+sudo cmake --build . --target install
+
+
+

Tip: If building for macOS, you may need to override the system provided LibreSSL with the OpenSSL you installed using Homebrew. +For example:

+
cmake .. \
+  -D CMAKE_INSTALL_PREFIX=/usr/local/clamav                                    \
+  -D OPTIMIZE=OFF                                                              \
+  -D OPENSSL_ROOT_DIR=/usr/local/opt/openssl@1.1/                              \
+  -D OPENSSL_CRYPTO_LIBRARY=/usr/local/opt/openssl@1.1/lib/libcrypto.1.1.dylib \
+  -D OPENSSL_SSL_LIBRARY=/usr/local/opt/openssl@1.1/lib/libssl.1.1.dylib
+make
+sudo make install
+
+
+

A Linux Distribution-style Build

+

This build type mimics the layout you may be familiar with if installing a ClamAV package on Debian, Ubuntu, Alpine, and some other distributions:

+
cmake .. \
+    -D CMAKE_INSTALL_PREFIX=/usr \
+    -D CMAKE_INSTALL_LIBDIR=lib \
+    -D APP_CONFIG_DIRECTORY=/etc/clamav \
+    -D DATABASE_DIRECTORY=/var/lib/clamav \
+    -D ENABLE_JSON_SHARED=OFF
+cmake --build .
+ctest
+sudo cmake --build . --target install
+
+

Using the above example:

+
    +
  • +

    CMAKE_INSTALL_PREFIX - The install "prefix" will be /usr.

    +
  • +
  • +

    CMAKE_INSTALL_LIBDIR - The library directory will be lib (i.e. /usr/lib).

    +

    This may be the default anyways, but you may want to specify if CMake tries to install to lib64 and if lib64 is not desired.

    +
  • +
  • +

    APP_CONFIG_DIRECTORY - The config directory will be /etc/clamav.

    +

    Note: This absolute path is non-portable.

    +
  • +
  • +

    DATABASE_DIRECTORY - The database directory will be /var/lib/clamav.

    +

    Note: This absolute path is non-portable.

    +
  • +
+
+

Tip: Setting ENABLE_JSON_SHARED=OFF is preferred, but it will require json-c version 0.15 or newer unless you build json-c yourself with custom options. If json-c 0.15+ is not available to you, you may omit the option and just use the json-c shared library. But be warned that downstream applications which use libclamav.so may crash if they also use a different JSON library.

+
+

Some other popular configuration options include:

+
    +
  • +

    CMAKE_INSTALL_DOCDIR - Specify exact documentation subdirectory, relative to the install prefix. The default may vary depending on your system and how you install CMake.

    +

    E.g., -D CMAKE_INSTALL_DOCDIR=share/doc/packages/clamav

    +
  • +
  • +

    CMAKE_SKIP_RPATH - If enabled, no RPATH is built into anything. This may be required when building packages for some Linux distributions. See the CMake wiki for more detail about CMake's RPATH handling.

    +

    E.g., -D CMAKE_SKIP_RPATH=ON

    +
  • +
+

Please see the CMake documentation for more instructions on how to customize the install paths.

+

A Build for Development

+

This suggested development configuration generates a Ninja-based build system instead of the default Makefile-based build system. Ninja is faster than Make, but you will have to install "ninja" (or "ninja-build"). With the following commands, ClamAV will be compiled in Debug mode with optimizations disabled. It will install to an "install" subdirectory and SystemD integration is disabled so that sudo is not required for the install and SystemD unit files are not installed to the system. This build also enables building a static libclamav.a library as well as building the example applications.

+
cmake .. -G Ninja \
+    -D CMAKE_BUILD_TYPE=Debug \
+    -D OPTIMIZE=OFF \
+    -D CMAKE_INSTALL_PREFIX=`pwd`/install \
+    -D ENABLE_EXAMPLES=ON \
+    -D ENABLE_STATIC_LIB=ON \
+    -D ENABLE_SYSTEMD=OFF
+cmake --build .
+ctest --verbose
+cmake --build . --target install
+
+

You can find additional instructions in our Development chapter.

+

About the tests

+

ClamAV's public test suite is run using ctest. On Linux systems, our build system will detect if you have Valgrind. If installed, each test will run a second time using Valgrind to check for leaks.

+

If a test fails, please report the issue on GitHub. You will find .log files for each of the tests in the build/unit_tests directory. The output from ctest --verbose may give us enough information, but if not it could be helpful to zip up the .log files and attach them to the ticket.

+

Un-install

+

CMake doesn't provide a simple command to uninstall. However, CMake does build an install_manifest.txt file when you do the install. You can use the manifest to remove the installed files.

+

You will find the manifest in the directory where you compiled ClamAV. If you followed the recommendations (above), then you will find it at <clamav source directory>/build/install_manifest.txt.

+

Feel free to inspect the file so you're comfortable knowing what you're about to delete.

+

Open a terminal and cd to that <clamav source directory>/build directory. Then run:

+
xargs rm < install_manifest.txt
+
+

This will leave behind the directories, and will leave behind any files added after install including the signature databases and any config files. You will have to delete these extra files yourself.

+
+

Tip: You may need to use sudo, depending on where you installed to.

+
+

What now?

+

Now that ClamAV is installed, you will want to customize your configuration and perhaps set up some scanning automation and alerting mechanisms.

+

Continue on to "Configuration"...

+

Building ClamAV with Autotools (v0.103 and older)

+

The following are instructions to build ClamAV version 0.103 and older using Autotools.

+ +
+

Note: Some of the dependencies are optional if you elect to not build all of the command line applications, or elect to only build the libclamav library. Specifically:

+
    +
  • libcurl: required for libfreshclam, freshclam, clamsubmit, clamonacc
  • +
  • json-c: required for clamsubmit, optional for libclamav
  • +
  • ncurses: required for clamdtop
  • +
+
+

Install prerequisites

+

Alpine

+

As root or with sudo, run:

+
apk update && apk add \
+  `# install tools` \
+  g++ gcc gdb make valgrind \
+  `# install clamav dependencies` \
+  bzip2-dev check-dev curl-dev json-c-dev libmilter-dev libxml2-dev \
+  linux-headers ncurses-dev openssl-dev pcre2-dev zlib-dev
+
+

Redhat / Centos / Fedora

+

For Centos 8, you will probably need to run this to enable EPEL & PowerTools. +As root or with sudo, run:

+
dnf install -y epel-release
+dnf install -y dnf-plugins-core
+dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
+dnf config-manager --set-enabled PowerTools | \
+  dnf config-manager --set-enabled powertools | true
+
+

As root or with sudo, run:

+
dnf install -y \
+  `# install tools` \
+  gcc gcc-c++ make valgrind \
+  `# install clamav dependencies` \
+  bzip2-devel check-devel json-c-devel libcurl-devel libxml2-devel \
+  ncurses-devel openssl-devel pcre2-devel sendmail-devel zlib-devel
+
+
+

Note: If you get dnf: command not found, use yum instead.

+
+
+

Tip: You need to run autogen.sh if you're not building from a release tarball from clamav.net. If so, visit the developer section to find out what packages are required to run autogen.sh

+
+

Ubuntu / Debian

+

As root or with sudo, run:

+
apt-get update && apt-get install -y \
+  `# install tools` \
+  gcc make pkg-config valgrind \
+  `# install clamav dependencies` \
+  check libbz2-dev libcurl4-openssl-dev libjson-c-dev libmilter-dev \
+  libncurses5-dev libpcre2-dev libssl-dev libxml2-dev zlib1g-dev
+
+
+

Tip: You need to run autogen.sh if you're not building from a release tarball from clamav.net. If so, visit the developer section to find out what packages are required to run autogen.sh

+
+

macOS

+

The following instructions require you to install HomeBrew to install tools and library dependencies.

+
# Install XCode's Command Line Tools
+xcode-select --install
+
+brew update
+
+packages=(
+  # install tools
+  autoconf automake m4
+  # install clamav dependencies
+  bzip2 check curl-openssl json-c libxml2 ncurses openssl@1.1 pcre2 zlib
+)
+for item in "${packages[@]}"; do
+  brew install $item || true; brew upgrade $item || brew upgrade $item
+done
+
+

FreeBSD

+

As root or with sudo, run:

+
pkg install -y \
+  `# install tools` \
+  gmake pkgconf \
+  `# install clamav dependencies` \
+  bzip2 check curl json-c libmilter libxml2 ncurses pcre2
+
+

Adding new system user and group

+

If installing to the system, and if you intend to run freshclam or clamd as as service, you should create a service account before compiling and installing ClamAV.

+

Follow these steps to create a service account.

+

Download the source code

+

Download the source from the clamav.net downloads page.

+

Extract the archive:

+
tar xzf clamav-[ver].tar.gz
+cd clamav-[ver]
+
+

Build ClamAV

+

First, make a "build" subdirectory. This will enable you to easily delete your build files if something goes wrong and you need to re-configure and try again.

+
mkdir build && cd build
+
+
+

Note: The instructions in this page assume you're building from our source clamav-[ver].tar.gz file. If you aren't, you may need to install extra build tools (autoconf, automake, m4, libtool, and pkg-config/pkgconfig/pkgconf) then run:

+
../autogen.sh
+
+
+

Next, select the build options you desire. For a full list of configuration options, run:

+
../configure --help
+
+

To help you get started, here are some popular build configurations.

+

The Default Build

+

The default build type is "RelWithDebInfo", that is "Release mode with Debugging symbols". It will install to /usr/local.

+
../configure
+make
+make check VG=1
+sudo make install
+
+

A Linux Distribution-style Build

+

This build type mimics the layout you may be familiar with if installing a ClamAV package on Debian, Ubuntu, Alpine, and some other distributions. This will be a "release build" (no debugging symbols, optimizations enabled) and will install to /usr. The config directory will be /etc/clamav and the database directory will be /var/lib/clamav.

+
../configure \
+    --prefix=/usr
+    --sysconfdir=/etc/clamav \
+    --with-dbdir=/var/lib/clamav \
+    --with-libjson-static=/path/to/libjson-c.a \
+    --enable-milter
+make
+make check VG=1
+sudo make install
+
+
+

Note: Setting ENABLE_JSON_SHARED=OFF is preferred, but it will require json-c version 0.15 or newer. If json-c 0.15+ is not available to you, you may omit the option and just use the json-c shared library. But be warned that downstream applications which use libclamav.so may crash if they also use a different JSON library.

+
+

A Build for Development

+

With the following commands, ClamAV will be compiled with debugging symbols and with optimizations disabled. It will install to an "install" subdirectory and SystemD integration is disabled so that sudo is not required for the install and SystemD unit files are not installed to the system.

+
CFLAGS="-Wall -Wextra -ggdb -O0" CXXFLAGS="-Wall -Wextra -ggdb -O0" ../configure \
+    --prefix=`pwd`/install \
+    --with-systemdsystemunitdir=no
+make -j12
+make check VG=1
+sudo make install
+
+

About the tests

+

ClamAV's public test suite is run using make check. On Linux systems, the VG=1 argument will enable extra tests that use Valgrind to check for leaks.

+

If a test fails, please report the issue on GitHub. You will find .log files generated by the tests in the build/unit_tests directory. The output from make check VG=1 may give us enough information, but if not it could be helpful to zip up the .log files and attach them to the ticket.

+

Un-install

+

Run make uninstall to remove the installed files.

+

This will leave behind the directories, and will leave behind any files added after install including the signature databases and any config files. You will have to delete these extra files yourself.

+
+

Tip: You may need to use sudo, depending on where you installed to.

+
+

What now?

+

Now that ClamAV is installed, you will want to customize your configuration and perhaps set up some scanning automation and alerting mechanisms.

+

Continue on to "Configuration"...

+

Installing ClamAV on Windows from Source

+

The following are instructions to build ClamAV version 0.104 and newer using CMake.

+
+

Tip: If you wish to build ClamAV from source in ClamAV version 0.103 and older, you'll have to use the Visual Studio solution, please see the Win32 ClamAV Build Instructions located in our source release materials on ClamAV.net and on GitHub.

+
+ +
+

Note: Some of the dependencies are optional if you elect to not build all of the command line applications, or elect to only build the libclamav library. Specifically:

+
    +
  • libcurl: required for libfreshclam, freshclam, clamsubmit
  • +
  • ncurses: required for clamdtop
  • +
+

For more information about customized builds and which dependencies can be skipped, please see the INSTALL.md document accompanying the source code.

+
+

Install prerequisites

+

The following commands for building on Windows are written for Powershell.

+

At a minimum you will need Visual Studio 2015 or newer, and CMake. If you want to build the installer, you'll also need WiX Toolset.

+

If you're using Chocolatey, you can install CMake and WiX simply like this:

+
choco install cmake wixtoolset
+
+

If you're using Mussels to build the library dependencies (see below), then you may also need to install Netwide Assembler (NASM) and ActivePerl. These are also simple to install using Chocolatey:

+
choco install nasm activeperl
+
+

Then open a new terminal so that CMake and WiX will be in your $PATH.

+

Building the library dependencies

+

There are two options for building and supplying the library dependencies. These are Mussels and vcpkg.

+

Mussels is an open source project developed in-house by the ClamAV team. It offers great flexibility for defining your own collections (cookbooks) of build instructions (recipes) instead of solely relying on a centralized repository of ports. And unlike vcpkg, Mussels does not implement CMake build tooling for projects that don't support CMake, but instead leverages whatever build system is provided by the project. This means that Mussels builds may require installing additional tools, like NMake and ActivePerl rather than simply requiring CMake. The advantage is that you'll be building those projects the same way that those developers intended, and that Mussels recipes are generally very light weight. Mussels has some sharp edges because it's a newer and much smaller project than vcpkg.

+

Vcpkg is an open source project developed by Microsoft and is heavily oriented towards CMake projects. Vcpkg offers a very large collection of "ports" for almost any project you may need to build. It is very easy to get started with vcpkg.

+

Mussels is the preferred tool to supply the library dependencies at least until such time as the vcpkg Debug-build libclamav unit test heap-corruption crash is resolved (see below).

+

Details for how to use Mussels and vcpkg will be provided with the build instructions (below), as the instructions differ significantly depending on which you choose.

+
+

Tip: Installing the Python 3 pytest package is also recommended in case the unit tests fail so that the test output is easy to read. You're welcome to skip it. However, if you have Python 2's pytest installed but not Python 3's pytest, the tests may fail to run.

+

You can install pytest by running:

+
python3 -m pip install --user pytest
+
+
+

Download the source code

+

Download the source from the clamav.net downloads page.

+

Extract the archive. You should be able to right click on it and extract it to a folder, then in that folder, do the same for the clamav-[ver].tar file.

+

The rest of the instructions will assume you've opened Powershell in the clamav source directory.

+

Build ClamAV

+

First, make a "build" subdirectory. This will enable you to easily delete your build files if something goes wrong and you need to re-configure and try again.

+
mkdir build && cd build
+
+

Building with Mussels

+

Building the library dependencies with Mussels

+

Much like vcpkg, Mussels can be used to automatically build the ClamAV library dependencies. Unlike vcpkg, Mussels does not provide a mechanism for CMake to automatically detect the +library paths.

+

To build the library dependencies with Mussels, use Python's pip package manager to install Mussels:

+
python3 -m pip install mussels
+
+
+

Important: Always run mussels or msl in a small sub-directory. Mussels will recursively search your current directory for YAML recipe files. In a large directory, such as your home directory, this may take a long time.

+
+

Update the Mussels cookbooks to get the latest build recipes and set the +clamav cookbook to be trusted:

+
msl update
+msl cookbook trust clamav
+
+

Use msl list if you wish to see the recipes provided by the clamav cookbook.

+

To build with Mussels, you may need to install a few extra tools required to build some of the libraries. These include NASM and ActivePerl. See install prerequisites, above.

+

Build the clamav_deps recipe to compile ClamAV's library dependencies. By default, Mussels will install them to ~\.mussels\install\<target>

+
msl build clamav_deps
+
+

If this worked, you should be ready to build ClamAV.

+
+

Tip: You can also build for 32-bit systems, using msl build clamav_deps -t x86.

+
+

Building ClamAV

+

To configure the project, run the following, substiting "Visual Studio 16 2019" with your Visual Studio version:

+
cmake ..  -G "Visual Studio 16 2019" -A x64 `
+  -D JSONC_INCLUDE_DIR="$home\.mussels\install\x64\include\json-c"         `
+  -D JSONC_LIBRARY="$home\.mussels\install\x64\lib\json-c.lib"             `
+  -D ENABLE_JSON_SHARED=OFF                                              `
+  -D BZIP2_INCLUDE_DIR="$home\.mussels\install\x64\include"                `
+  -D BZIP2_LIBRARY_RELEASE="$home\.mussels\install\x64\lib\libbz2.lib"     `
+  -D CURL_INCLUDE_DIR="$home\.mussels\install\x64\include"                 `
+  -D CURL_LIBRARY="$home\.mussels\install\x64\lib\libcurl_imp.lib"         `
+  -D OPENSSL_ROOT_DIR="$home\.mussels\install\x64"                         `
+  -D OPENSSL_INCLUDE_DIR="$home\.mussels\install\x64\include"              `
+  -D OPENSSL_CRYPTO_LIBRARY="$home\.mussels\install\x64\lib\libcrypto.lib" `
+  -D OPENSSL_SSL_LIBRARY="$home\.mussels\install\x64\lib\libssl.lib"       `
+  -D ZLIB_LIBRARY="$home\.mussels\install\x64\lib\libssl.lib"              `
+  -D LIBXML2_INCLUDE_DIR="$home\.mussels\install\x64\include"              `
+  -D LIBXML2_LIBRARY="$home\.mussels\install\x64\lib\libxml2.lib"          `
+  -D PCRE2_INCLUDE_DIR="$home\.mussels\install\x64\include"                `
+  -D PCRE2_LIBRARY="$home\.mussels\install\x64\lib\pcre2-8.lib"            `
+  -D CURSES_INCLUDE_DIR="$home\.mussels\install\x64\include"               `
+  -D CURSES_LIBRARY="$home\.mussels\install\x64\lib\pdcurses.lib"          `
+  -D PThreadW32_INCLUDE_DIR="$home\.mussels\install\x64\include"           `
+  -D PThreadW32_LIBRARY="$home\.mussels\install\x64\lib\pthreadVC2.lib"    `
+  -D ZLIB_INCLUDE_DIR="$home\.mussels\install\x64\include"                 `
+  -D ZLIB_LIBRARY="$home\.mussels\install\x64\lib\zlibstatic.lib"          `
+  -D LIBCHECK_INCLUDE_DIR="$home\.mussels\install\x64\include"             `
+  -D LIBCHECK_LIBRARY="$home\.mussels\install\x64\lib\checkDynamic.lib"    `
+  -D CMAKE_INSTALL_PREFIX="install"
+
+
+

Tip: You have to drop the -A x64 arguments if you're building for 32-bits (or specify -A win32) and substitute x64 with x86 in the library paths.

+
+

Now, go ahead and build the project:

+
cmake --build . --config RelWithDebInfo
+
+
+

Tip: If you're having include-path issues when building, try building with +detailed verbosity so you can verify that the paths are correct:

+
+
cmake --build . --config RelWithDebInfo -- /verbosity:detailed
+
+

You can run the test suite with ctest:

+
ctest -C RelWithDebInfo
+
+

And you can install to the install (set above) like this:

+
cmake --build . --config RelWithDebInfo --target install
+
+
+

Tip: For a full list of configuration options, see the "Custom CMake Config Options" section of the INSTALL.md file included with the source code.

+
+

Building with vcpkg

+

vcpkg can be used to build the ClamAV library dependencies automatically.

+

vcpkg integrates really well with CMake, enabling CMake to find your compiled libraries automatically, so you don't have to specify the include & library paths manually as you do when using Mussels.

+
+

DISCLAIMER: There is a known issue with the unit tests when building with vcpkg in Debug mode. When you run the libclamav unit tests (check_clamav), the program will crash and a popup will claim there was heap corruption. If > you use Task Manager to kill the check_clamav.exe process, the rest of the tests pass just fine. This issue does not occur when using Mussels to supply the library dependencies. Commenting out the following lines in readdb.c resolves the heap corruption crash when running check_clamav, but of course introduces a memory leak:

+
    if (engine->stats_data)
+        free(engine->stats_data);
+
+

If anyone has time to figure out the real cause of the vcpkg Debug-build crash in check_clamav, it would be greatly appreciated.

+
+

You'll need to install vcpkg. See the vcpkg README for installation instructions.

+

Once installed, set the variable $VCPKG_PATH to the location where you installed vcpkg:

+
$VCPKG_PATH="..." # Path to your vcpkg installation
+
+

By default, CMake and vcpkg build for 32-bit. If you want to build for 64-bit, set the VCPKG_DEFAULT_TRIPLET environment variable:

+
$env:VCPKG_DEFAULT_TRIPLET="x64-windows"
+
+

Next, use vcpkg to build the required library dependencies:

+
& "$VCPKG_PATH\vcpkg" install 'curl[openssl]' 'json-c' 'libxml2' 'pcre2' 'pthreads' 'zlib' 'pdcurses' 'bzip2' 'check'
+
+

Now configure the ClamAV build using the CMAKE_TOOLCHAIN_FILE variable which will enable CMake to automatically find the libraries we built with vcpkg.

+
cmake .. -A x64 `
+  -D CMAKE_TOOLCHAIN_FILE="$VCPKG_PATH\scripts\buildsystems\vcpkg.cmake" `
+  -D CMAKE_INSTALL_PREFIX="install"
+
+

Now, go ahead and build the project:

+
cmake --build . --config RelWithDebInfo
+
+

You can run the test suite with ctest:

+
ctest -C RelWithDebInfo
+
+

And you can install to the install directory (set above) like this:

+
cmake --build . --config RelWithDebInfo --target install
+
+

Build the Installer

+

To build the installer, you must have WIX Toolset installed. If you're using Chocolatey, you can install it simply with choco install wixtoolset and then open a new terminal so that WIX will be in your PATH.

+
cpack -C RelWithDebInfo
+
+

What now?

+

Now that ClamAV is installed, you will want to customize your configuration and perhaps set up some scanning automation and alerting mechanisms.

+

Continue on to "Configuration"...

+

Community Projects

+
+

Disclaimer: The software listed in this section is authored by third parties and not by the ClamAV Team. Compatibility may vary.

+
+

Signatures

+

The ClamAV Team provides FreshClam for ClamAV agents to update the official signature databases and provides CVD-Update for Private Mirror administrators to update their server content.

+

Both FreshClam and CVD-Update have some limited features to update signatures from third-party sources but community tools exist that are designed for this purpose and provide a more complete experience for users that want the extra coverage.

+
+

WARNING: While there are no known vulnerabilities in using traditional content-based and hash-based ClamAV signatures, bytecode signatures are a different story. Bytecode signatures are effectively cross-platform executable plugins similar to Web Assembly (WASM) but with less sandboxing.

+

ClamScan and ClamD will not run unsigned bytecode signatures by default. Cisco-Talos' signing certificate is the only certificate trusted by ClamAV to run bytecode signatures.

+

Both ClamD and ClamScan have options to run unsigned bytecode signatures but you should NEVER enable unsigned bytecode signatures in production when using signatures from third-party sources or a malicious bytecode signature author could gain control of your systems.

+

ClamBC is a tool installed with ClamAV for testing bytecode signatures and should also NEVER be used to run signatures from an unknown or untrusted source.

+
+

Fangfrish

+

Fangfrisch (German for "freshly caught") is a sibling of the Clam Anti-Virus FreshClam utility. It allows downloading virus definition files that are not official ClamAV canon, e.g. from Sanesecurity, URLhaus and others. Fangfrisch was designed with security in mind, to be run by an unprivileged user only.

+

Detailed documentation is available online.

+

Get fangfrish

+

Mail Filters

+

ClamAV is popular for filtering mail. The ClamAV Team maintains ClamAV-Milter, which is a filter for the Sendmail mail transfer agent and the ClamAV community have created a wide variety of other tools to use ClamAV with different mail transfer agents.

+

Generic Mail Transfer Agents

+

amavisd-new | clamd, clamscan

+

amavisd-new is a high-performance interface between mailer (MTA) and content checkers: virus scanners, and/or SpamAssassin. It is written in Perl for maintainability, without paying a significant price for speed. It talks to MTA via (E)SMTP or LMTP, or by using helper programs. Best with Postfix, fine with dual-sendmail setup and Exim v4, works with sendmail/milter, or with any MTA as a SMTP relay. For Courier and qmail MTA integration there is a patch in the distributed package.

+

amavisd-new is a rewritten version of Amavis and is maintained by Mark Martinec.

+

ClamScan is enabled automatically if clamscan binary is found at amavisd-new startup time. ClamD is activated by uncommenting its entry in the @av_scanners list in the file /etc/amavisd.conf.

+

Get amavisd-new

+

Sendmail

+

MIMEDefang | clamscan, clamd

+

MIMEDefang is an efficient mail scanner for Sendmail/milter written in C, Perl.

+

Get MIMEDefang

+

Postfix

+

ClamSMTP | clamd

+

ClamSMTP is an SMTP filter for Postfix and other mail servers that checks for viruses using the ClamAV anti-virus software. It aims to be lightweight, reliable, and simple rather than have a myriad of options. Written in C without major dependencies.

+

Get ClamSMTP

+

Clapf | libclamav

+

Clapf is a clamav based virus scanning and anti-spam content filter for Postfix.

+

Get clapf

+

Exim

+

Starting with release 4.50, Exim natively supports ClamAV.

+

Get exim

+

Others

+

Mail Avenger | clamscan

+

Mail Avenger is a highly-configurable SMTP server. It allows you to reject spam during mail transactions, before spooling messages in your local mail queue. You can specify site-wide default policies for filtering mail, but individual users can also craft their own policies by creating avenger scripts in their home directories.

+

Get Mail Avenger

+

MailScanner | clamscan

+

MailScanner scans all e-mail for viruses, spam and attacks against security vulnerabilities. It is not tied to any particular virus scanner, but can be used with any combination of 14 different virus scanners, allowing sites to choose the best of breed virus scanner.

+

Get Mail Scanner

+

Sagator | clamscan, clamd, libclamav

+

Sagator is an email antivirus/antispam gateway. Its modular architecture can use any combination of antivirus/spamchecker according to configuration.

+

Get Sagator

+

Courier-MTA | libclamav, clamavd

+

Courier MTA includes four filers.

+

courier-pythonfilter by Gordon Messner. Included in a Python filter suite, it uses pyClamAV (libclamav with python)

+

Courier::Filter::Module::ClamAVd by Julian Mehnle. A Perl module for use with Courier::Filter, using clamavd.

+

ClamCour by Tony Di Monaco. A C++ (with Boost) multithreaded filter using libclamav

+

avfilter by Alessandro Vesely. A C forking filter using libclamav.

+

Get Courier-MTA

+

Haraka | clamd

+

Haraka is a robust MTA written in node.js, with a modular architecture that lets plugins control nearly every aspect of the SMTP conversation. There is a large selection of included plugins, including a clamav plugin (docs, source) that filters messages using clamd.

+

Haraka is attractive to two audiences:

+
    +
  1. +

    Anyone managing mail systems with thousands or tens-of-thousands of concurrent incoming SMTP connections (like Craigslist) and wants to do with it fewer racks of servers.

    +
  2. +
  3. +

    Developers who need more control over mail routing, filtering, and processing than can be easily or efficiently handled with traditional (milter-based) MTAs.

    +
  4. +
+

Get Haraka

+

Web & FTP Tools

+

Clammit | clamd

+

Clammit is a proxy that will perform virus scans of files uploaded via http requests, including multipart/form-data. If a virus exists, it will reject the request out of hand. If no virus exists, the request is then forwarded to the application and it's response returned in the upstream direction.

+

As the name implies, Clammit offloads the virus detection to the ClamAV virus detection server (clamd).

+

Get Clammit

+

Clara

+

Serverless, real-time, ClamAV+Yara scanning for your S3 Buckets

+

Get Clara

+

bucket-antivirus-function

+

Scan new objects added to any s3 bucket using AWS Lambda.

+

Get bucket-antivirus-function

+

cdk-serverless-clamscan

+

An aws-cdk construct that uses ClamAV® to scan objects in Amazon S3 for viruses. The construct provides a flexible interface for a system to act based on the results of a ClamAV virus scan.

+

Get cdk-serverless-clamscan

+

Antivirus for Amazon S3

+

A CloudFormation template to create an EC2 scanner cluster for S3 buckets.

+

Get Antivirus for Amazon S3

+

HAVP | libclamav

+

HAVP is a proxy with an antivirus filter. It does not cache or filter content. At the moment the complete traffic is scanned. A reason for that is the chance of malicious code in nearly every filetypes e.g. HTML (JavaScript) or Jpeg.

+

Get HAVP

+

mod_clamav | libclamav, clamd

+

mod_clamav is an Apache virus scanning filter. It was written and is currently maintained by Andreas Müller. The project is very well documented and the installation is quite easy.

+

Get mod_clamav

+

phpMussel | clamav

+

phpMussel is a PHP-based script based upon ClamAV signatures designed to detect trojans, viruses, malware and other threats within files uploaded to your system wherever the script is hooked. Written by Maikuolan

+

Get phpMussel

+

SpamAssassin - ClamAVPlugin | clamd

+

A ClamAV plug in fpr SpamAssassin 3.X

+

Get ClamAVPlugin

+

clamav-rest

+

Simple ClamAV REST proxy. Builds on top of clamav-java which is a minimal Java client for ClamAV.

+

Get clamav-rest

+

Filesystem & On-Access Scanning

+

Clam Sentinel

+

Clam sentinel is a program that detects file system changes and automatically scans the files added or modified using ClamWin. Require the installation of ClamWin. For Microsoft Windows 98/98SE/Me/2000/XP/Vista, Windows 7 and Windows 8.1.

+

Get Clam Sentinel

+

ClamFS | clamd

+

ClamFS is a FUSE-based user-space file system for Linux and BSD with on-access anti-virus file scanning through clamd daemon (a file scanning service developed by ClamAV Project).

+

Features:

+
    +
  • Scans files using ClamAV
  • +
  • User-space file system (no kernel patches, modules, recompilations, etc.)
  • +
  • Based on libFUSE version 3 (until version 1.1.0 on libFUSE v2)
  • +
  • Implements all clamd scan modes: fname, fdpass and stream
  • +
  • Supports remote clamd instances in stream mode over TCP/IP socket
  • +
  • Caches scan results in a LRU cache with time-based and out-of-memory expiration
  • +
  • Configuration stored in XML files
  • +
  • Supports ulockmgr
  • +
  • Sends mails to administrator when detects virus
  • +
+

Get ClamFS

+

Avfs | ClamAV

+

Avfs, a true on-access anti-virus file system that incrementally scans files and prevents infected data from being committed to disk. Avfs is a stackable file system and therefore can add virus detection to any other file system: Ext3, NFS, etc. Avfs supports forensic modes that can prevent a virus from reaching the disk or automatically create versions of potentially infected files to allow safe recovery. Avfs can also quarantine infected files on disk and isolate them from user processes.

+

Avfs uses a matching algorithm that is derived from ClamAV but with changes to optimize scan time for larger signature sets. Though this project does not appear to be maintained or used elsewhere, the research was really good work and may inspire optimizations in ClamAV in the future.

+

More about Avfs

+

Mail User Agents

+

Claws Mail

+

Claws Mail is a user-friendly, lightweight, and fast email client. Claws Mail has a ClamD plugin for scanning received messages using ClamAV.

+

Get Claws Mail

+

Kmail | clamscan

+

Mail is a fully-featured email client that fits nicely into the K Desktop Environment, KDE. It supports attachment scanning with clamscan.

+

Get Kmail

+

Open Webmail modules | clamscan

+

Open WebMail by default can use ClamAV as the external viruscheck module to scan messages fetched from pop3 servers or all incoming messages. If a message or its attachments is found to have virus, Open WebMail will move the message from INBOX to the VIRUS folder automatically.

+

Get Open Webmail

+

ClamAV Bindings

+

Rust

+

clamav-rs | libclamav

+

A safe Rust binding for libclamav. clamav-rs uses clamav-sys to wrap the libclamav C API.

+

Get clamav-rs

+

clamav-sys | libclamav

+

clamav-sys is a minimal Rust interface around libclamav. This package is not supposed to be used stand-alone, but only through its safe wrapper, clamav-rs.

+

Get clamav-sys

+

rust-clamav | libclamav

+

Like clamav-rs. rust-clamav is a safe library for interacting with libclamav from Rust. The low-level C API is wrapped in idomatic and safe Rust code.

+

Get rust-clamav

+

Perl

+

File::Scan::ClamAV | clamd

+

A Perl module for interacting with ClamD. File::Scan::ClamAV will connect to a local Clam Anti-Virus clamd service and send commands.

+

Get File::Scan::ClamAV

+

Mail::ClamAV | libclamav

+

Perl extension for the ClamAV virus scanner.

+

Get Mail::ClamAV

+

Ruby

+

Clamby | clamscan + freshclam

+

Ruby binding for scanning file uploads using ClamScan. If you have a file upload on your site and you do not scan the files for viruses then you not only compromise your software, but also the users of the software and their files. This gem's function is to simply scan a given file.

+

Get Clamby

+

ClamAV::Client | clamd

+

ClamAV::Client is a client library that can talk to the clam daemon.

+

Get ClamAV::Client

+

PHP

+

PHP ClamAV | clamd

+

PHP Client to connect to ClamAV daemon over TCP or using a local socket from command line and scan your storage files for viruses.

+

Get PHP ClamAV

+

PHP ClamAV Scan | clamd

+

A simple PHP class for scanning files using a LOCAL ClamAV/clamd install either via a socket file or network socket (windows). Can either be used on its own or dropped into a Codeigniter app as a library. The main reason this was created was because the legacy php-clamav module is not compatible with PHP 7 and all other options I found were either not drop in compatible with CodeIgniter or were designed for use with Composer.

+

Get PHP ClamAV

+

Python

+

clamd | clamd

+

clamd is a portable Python module to use the ClamAV anti-virus engine on Windows, Linux, MacOSX and other platforms. It requires a running instance of the clamd daemon.

+

This is a fork of pyClamd v0.2.0 created by Philippe Lagadec and published on his website: http://www.decalage.info/en/python/pyclamd which in turn is a slightly improved version of pyClamd v0.1.1 created by Alexandre Norman and published on his website: http://xael.org/norman/python/pyclamd/

+

Get clamd

+

Python ClamAV | libclamav

+

Python wrapper for libclamav using ctypes. Python ClamAV is a part of the ClamWin project.

+

Get Python ClamAV

+

pyClamd | clamd

+

Add virus detection capabilities to your python software in an efficient and easy way.

+

Get pyClamd

+

Java

+

clamav-java

+

Simple ClamAV Java client. See also ClamAV REST service which builds on top of this.

+

Get clamav-java

+

Miscellaneous Tools

+

IPCop | ClamAV

+

IPCop Linux is a complete Linux Distribution whose sole purpose is to protect the networks it is installed on. ClamAV is included.

+

Get IPCop

+

Endian Firewall | ClamAV

+

Endian Firewall Community (EFW) is a turn-key Linux security distribution that can transform any bare-metal appliance into a full-featured Unified Threat Management solution. Endian is designed to be the easiest security product to install, configure and use!

+

Get Endian Firewall

+

ClamTK | ClamAV

+

ClamTk is a GUI front-end for ClamAV using gtk2-perl. It is designed to be an easy-to-use, on-demand scanner for Linux systems. ClamTk has been ported to Fedora, Debian, RedHat, openSUSE, ALT Linux, Ubuntu, CentOS, Gentoo, Archlinux, Mandriva, PCLinuxOS, FreeBSD, and others.

+

Get ClamTK

+

ClamWin | ClamAV

+

ClamWin is a Free Antivirus program for Microsoft Windows 10 / 8 / 7 / Vista / XP / Me / 2000 / 98 and Windows Server 2012, 2008 and 2003.

+

Get ClamWin

+

Add a service user account

+

If you're planning to run freshclam or clamd as a service on a Linux or Unix system, you should create a service account. The following instructions assume that you will use the an account named "clamav" for both services, although you may create a different account name for each if you wish.

+
+

Note: These instructions are mostly just for folks building & installing from source. If you installed a package from your Linux/Unix distribution, it probably created the account(s) for you.

+
+

Create a service user account (and group)

+

Linux / Unix

+

As root or with sudo, run:

+
groupadd clamav
+useradd -g clamav -s /bin/false -c "Clam Antivirus" clamav
+
+

If your operating system does not have the groupadd and useradd utilities, consult a system manual. Don’t forget to lock access to the account!

+

macOS

+

Prep by identifying an unused group id (gid), and an unused user UniqueID.

+

This command will display all current group PrimaryGroupIDs:

+
dscl . list /Groups PrimaryGroupID | tr -s ' ' | sort -n -t ' ' -k2,2
+
+

This command will display all current user UniqueIDs:

+
dscl . list /Users UniqueID | tr -s ' ' | sort -n -t ' ' -k2,2
+
+

Then, these commands can be used to create the clamav group and clamav user.

+
sudo dscl . create /Groups/clamav
+sudo dscl . create /Groups/clamav RealName "Clam Antivirus Group"
+sudo dscl . create /Groups/clamav gid 799           # Ensure this is unique!
+sudo dscl . create /Users/clamav
+sudo dscl . create /Users/clamav RealName "Clam Antivirus User"
+sudo dscl . create /Users/clamav UserShell /bin/false
+sudo dscl . create /Users/clamav UniqueID 599       # Ensure this is unique!
+sudo dscl . create /Users/clamav PrimaryGroupID 799 # Must match the above gid!
+
+

About how the service accounts are used

+

At present, the behavior differs slightly between clamd and freshclam. When run as root:

+
    +
  • +

    freshclam will always switch to run as the "DatabaseOwner" user account. The default account name is "clamav", or may be customized by specifying the "DatabaseOwner" setting in freshclam.conf.

    +
  • +
  • +

    clamd will only switch to run as the "User" user account if the "User" setting is specified in clamd.conf. If you do not specify a "User" in the config, clamd will continue to run as the root user! We may change this behavior in a future version to prevent clamd from being run as root.

    +
  • +
+
+

Caution: We do not recommend running clamd as root for safety reasons because ClamAV scans untrusted files that may be malware. Always configure the "User" setting in clamd.conf if you plan to run clamd as a service.

+
+

On Unix/Linux systems, freshclam and clamd will switch to run as a different user if you start them as the root user, or using sudo. By default, that user account is named "clamav". The purpose is t

+

If you are running freshclam and clamd as root or with sudo, and you did not explicitly configure with --disable-clamav, you will want to ensure that the DatabaseOwner user specified in freshclam.conf owns the database directory so it can download signature updates.

+

The user that clamd, clamdscan, and clamscan run as may be the same user, but if it isn't -- it merely needs read access to the database directory.

+

If you choose to use the default clamav user to run freshclam and clamd, you'll need to create the clamav group and the clamav user account the first time you install ClamAV.

+

After installation: Make the service account own the database directory

+

After you've installed ClamAV, you will want to make it so that the database directory is owned by the same service account as you're using for freshclam.

+

As root or with sudo, run:

+
sudo chown -R clamav:clamav /usr/local/share/clamav
+
+

Or (if you customized the database path):

+
chown -R clamav:clamav /var/lib/clamav/
+
+

Usage

+

Table Of Contents

+ +

Purpose

+

This user guide presents an overview of the various ways that libclamav can be used through the tools provided by ClamAV. To learn more about how to better use each facet of ClamAV that interests you, please follow the links provided.

+

Daemon

+

The ClamAV Daemon, or clamd, is a multi-threaded daemon that uses libclamav to scan files for viruses. ClamAV provides a number of tools which interface with this daemon. They are, as follows:

+ +

Scanner

+

ClamAV also provides a command-line tool for simple scanning tasks with libclamav called clamscan. Unlike the daemon, clamscan is not a persistent process and is best suited for use cases where one-time scanning with minimal setup is needed.

+

Signature Testing and Management

+

A number of tools allow for testing and management of signatures. Of note are the following:

+
    +
  • clambc - specifically for testing bytecode
  • +
  • sigtool - for general signature testing and analysis
  • +
  • freshclam - used to update signature database sets to the latest version
  • +
+

Configuration

+

The more complex tools ClamAV provides each require some degree of configuration. ClamAV supplies two example configuration files:

+
    +
  • clamd.conf - for configuring the behavior of the ClamAV Daemon clamd and associated tools
  • +
  • freschclam.conf - for configuring the behavior of the signature database update tool, freshclam
  • +
+

ClamAV also provides a mail filtering tool called clamav-milter which can be attached to a clamd instance for mail scanning purposes.

+

Additionally, a tool called clamconf allows users to check the configurations used by each other tool, pulling information from the configuration files listed above, alongside other relevant information.

+

Configuration

+

Table Of Contents

+ +

First Time Set-Up

+

Depending on your install method and your operating system, some configuration options may have been pre-configured. For example a clamav install on Ubuntu with apt install will place configs in /etc/clamav.

+

However, it is likely that you will need to create new config files or modify the existing ones with custom settings that make the most sense for your use case. A from-source install will require you to create a freshclam.conf before you can use FreshClam, a clamd.conf before you can use ClamD, and a clamav-milter.conf before you can use ClamAV-Milter.

+

A default install from source will place the example configs in /usr/local/etc/ on Unix/Linux systems and in the install directory under conf_examples on Windows. These examples demonstrate each of the options and may help you decide how to configure ClamAV to suit your needs. But again the location of these examples may vary depending on how you installed ClamAV. To continue with the Ubuntu example, you may find the FreshClam config from an apt install in /usr/share/doc/clamav-freshclam/examples/. So if you're unsure where the example configs are on your system, you may wish to use ClamConf to generate them.

+

Here are some quick steps to get you started.

+

Unix

+

Run these to generate example configs, if needed:

+
clamconf -g freshclam.conf > freshclam.conf
+clamconf -g clamd.conf > clamd.conf
+clamconf -g clamav-milter.conf > clamav-milter.conf
+
+

Or if you have the examples already, copy them to drop the .example extension:

+
cp freshclam.conf.example freshclam.conf
+cp clamd.conf.example clamd.conf
+cp clamav-milter.conf.example clamav-milter.conf
+
+

Next up, edit the configs you need. There are tips below for each of freshclam.conf, clamd.conf, and clamav-milter.

+

Windows

+

In a PowerShell terminal in the install directory, perform the following tasks:

+

Run:

+
copy .\conf_examples\freshclam.conf.sample .\freshclam.conf
+copy .\conf_examples\clamd.conf.sample .\clamd.conf
+
+

Run:

+
write.exe .\freshclam.conf
+
+

WordPad will pop up. Delete the line that says "Example". You may also wish to set additional options to enable features or alter default behavior, such as the receive-timeout. Save the file and close WordPad.

+

Run:

+
write.exe .\clamd.conf
+
+

WordPad will pop up. Delete the line that says "Example". You may also wish to set additional options to enable features or alter default behavior, such as enabling logging. Save the file and close WordPad.

+

Additional notes about the config files and database directories

+

The install directory is but one of a few locations ClamAV may search for configs and for signature databases.

+

Config files path search order:

+
    +
  1. The content of the registry key: +"HKEY_LOCAL_MACHINE/Software/ClamAV/ConfDir"
  2. +
  3. The directory where libclamav.dll is located: +"C:\Program Files\ClamAV"
  4. +
  5. "C:\ClamAV"
  6. +
+

Database files path search order:

+
    +
  1. The content of the registry key: +"HKEY_LOCAL_MACHINE/Software/ClamAV/DataDir"
  2. +
  3. The directory "database" inside the directory where libclamav.dll is located: +"C:\Program Files\ClamAV\database"
  4. +
  5. "C:\ClamAV\db"
  6. +
+

freshclam.conf

+

freshclam is the automatic database update tool for Clam AntiVirus. It can be configured to work in two modes:

+
    +
  • interactive - on demand from command line
  • +
  • daemon - silently in the background
  • +
+

freshclam is an advanced tool: it supports scripted updates (instead of transferring the whole CVD file at each update it only transfers the differences between the latest and the current database via a special script), database version checks through DNS, proxy servers (with authentication), digital signatures and various error scenarios.

+

Quick test: run freshclam (as superuser) with no parameters and check the output.

+
freshclam
+
+
+

Tip: Depending on how you installed Freshclam and depending on which version of ClamAV you're running, you may encounter errors the first time you run Freshclam. See the Freshclam section of our FAQ for help!

+
+

If everything is OK you may create the log file in /var/log (ensure the directory is owned either by clamav or whichever user freshclam will be running as):

+
touch /var/log/freshclam.log
+chmod 600 /var/log/freshclam.log
+chown clamav /var/log/freshclam.log
+
+

Now you should edit the configuration file freshclam.conf and point the UpdateLogFile directive to the log file. Finally, to run freshclam in the daemon mode, execute:

+
freshclam -d
+
+

The other way is to use the cron daemon. You have to add the following line to the crontab of root or clamav user:

+
N * * * *   /usr/local/bin/freshclam --quiet
+
+

to check for a new database every hour. N should be a number between 3 and 57 of your choice. Please don’t choose any multiple of 10, because there are already too many clients using those time slots. Proxy settings are only configurable via the configuration file and freshclam will require strict permission settings for the config file when HTTPProxyPassword is turned on.

+
HTTPProxyServer myproxyserver.com
+HTTPProxyPort 1234
+HTTPProxyUsername myusername
+HTTPProxyPassword mypass
+
+

Other freshclam.conf settings

+

If your freshclam.conf was derived from the freshclam.conf.sample, you should find many other options that are simply commented out. If not, seek out the freshclam.conf.sample file, or on Linux/Unix systems run man freshclam.conf.

+

Take the time to look through the options. You can enable the sample options by deleting the # comment characters.

+

Some popular options to enable include:

+
    +
  • LogTime
  • +
  • LogRotate
  • +
  • NotifyClamd
  • +
  • DatabaseOwner
  • +
+

clamd.conf

+

Currently, ClamAV requires users to edit their clamd.conf.example file before they can run the daemon. At a bare minimum, users will need to comment out the line that reads "Example", else clamd will consider the configuration invalid, ala:

+
# Comment or remove the line below.
+#Example
+
+

You will also need to rename clamd.conf.example to clamd.conf via:

+
mv ./clamd.conf.example ./clamd.conf
+
+

If you are setting up a simple, local clamd instance then some other configuration options of interests to you will be as follows:

+
# Path to a local socket file the daemon will listen on.
+# Default: disabled (must be specified by a user)
+LocalSocket /tmp/clamd.socket
+
+...
+
+# Sets the permissions on the unix socket to the specified mode.
+# Default: disabled (socket is world accessible)
+LocalSocketMode 660
+
+

Beyond that, clamd.conf is well commented and configuration should be straightforward.

+

If needed, you can find out even more about the formatting and options available in clamd.conf with the command:

+
man clamd.conf
+
+

Other clamd.conf settings

+

If your clamd.conf was derived from the clamd.conf.sample, you should find many other options that are simply commented out. If not, seek out the clamd.conf.sample file, or on Linux/Unix systems run man clamd.conf.

+

Take the time to look through the options. You can enable the sample options by deleting the # comment characters.

+

Some popular options to enable include:

+
    +
  • LogTime
  • +
  • LogClean
  • +
  • LogRotate
  • +
  • User
  • +
  • ScanOnAccess +
      +
    • OnAccessIncludePath
    • +
    • OnAccessExcludePath
    • +
    • OnAccessPrevention
    • +
    +
  • +
+

On-Access Scanning

+

You can configure On-Access Scanning through clamd.conf. Configuration for On-Access Scanning starts in the second half of clamd.conf.sample starting with "On-access Scan Settings". All options are grouped acording to use and roughly ordered by importance in those groupings. Please carefully read the explanation of each option to see if it might be of use to you.

+

Also read the on-access section of the Usage manual for further details on using On-Access Scanning.

+

clamav-milter.conf

+

ClamAV includes a mail filtering tool called clamav-milter. This tool interfaces directly with clamd, and thus requires a working clamd instance to run. However, clamav-milter's configuration and log files are separate from that of clamd.

+

Ensuring ClamAV compiles with clamav-milter must be done at configure time with the command:

+
./configure [options] --enable-milter
+
+

This requires having the milter library installed on your system. If libmilter is not installed, ./configure will exit with this error message:

+
checking for mi_stop in -lmilter... no
+configure: error: Cannot find libmilter
+
+

While not necessarily complicated, setting up the clamav-milter is an involved process. Thus, we recommend consulting your MTA’s manual on how to best connect ClamAV with the clamav-milter.

+

Users and on user privileges

+

If you are running freshclam and clamd as root or with sudo, and you did not explicitly configure with --disable-clamav, you will want to ensure that the DatabaseOwner user specified in freshclam.conf owns the database directory so it can download signature updates.

+

The user that clamd, clamdscan, and clamscan run as may be the same user, but if it isn't -- it merely needs read access to the database directory.

+

If you choose to use the default clamav user to run freshclam and clamd, you'll need to create the clamav group and the clamav user account the first time you install ClamAV.

+
groupadd clamav
+useradd -g clamav -s /bin/false -c "Clam Antivirus" clamav
+
+

Finally, you will want to set user ownership of the database directory. For example:

+
sudo chown -R clamav:clamav /usr/local/share/clamav
+
+

Configure SELinux for ClamAV

+

Certain distributions (notably RedHat variants) when operating with SELinux enabled use the non-standard antivirus_can_scan_system SELinux option instead of clamd_can_scan_system.

+

At this time, libclamav only sets the clamd_can_scan_system option, so you may need to manually enable antivirus_can_scan_system. If you don't perform this step, freshclam will log something like this when it tests the newly downloaded signature databases:

+
During database load : LibClamAV Warning: RWX mapping denied: Can't allocate RWX Memory: Permission denied
+
+

To allow ClamAV to operate under SELinux, run the following:

+
setsebool -P antivirus_can_scan_system 1
+
+

ClamConf

+

clamconf is a tool ClamAV provides for checking your entire system configuration, as it relates to your ClamAV installation. When run, it displays values used when configuring ClamAV at compilation time, important OS details, the contents (and validity) of both clamd.conf and freshclam.conf, along with other important engine, database, platform, and build information.

+

It can also generate example configuration files for clamd.conf and freshclam.conf.

+

To use clamconf, and see all the information it provides, simply run the following command:

+
clamconf
+
+

For more detailed information on clamconf, run:

+
clamconf --help
+
+

or on Unix systems:

+
man clamconf
+
+

Next Steps

+

Now that you have the config file basics, it's time to learn about signature databases and how to keep yours up-to-date.

+

Signature Testing and Management

+

Table Of Contents

+ +
+

Tip: The commands on Windows are generally the same, but you may need to add the .exe extension to run the ClamAV applications.

+
+

FreshClam

+

Before you can start the ClamAV scanning engine (using either clamd or clamscan), you must first have ClamAV Virus Database (.cvd) file(s) installed in the appropriate location on your system.

+

The tool freshclam is used to download and update ClamAV’s official virus signature databases. While easy to use in its base configuration, freshclam does require a working freshclam.conf configuration file to run (the location of which can be passed in via command line if the default search location does not fit your needs).

+

Once you have a valid configuration file, you can invoke FreshClam with the following command:

+
freshclam
+
+

By default, freshclam will then attempt to connect to ClamAV's virus signature database distribution network. If no databases exist in the directory specified, freshclam will do a fresh download of the requested databases. Otherwise, freshclam will attempt to update existing databases, pairing them against downloaded cdiffs. If a database is found to be corrupted, it is not updated and instead replaced with a fresh download.

+

Of course, all this behavior--and more--can be changed to suit your needs by modifying freshclam.conf and/or using various command line options.

+

You can find more information about FreshClam with the commands:

+

Unix/Linux:

+
freshclam --help
+
+

Or (Unix/Linux only):

+
man freshclam
+
+
+

Tip: Newer versions of FreshClam will create your database directory if it doesn't already exist. Older versions won't, and may fail unless you create it first.

+
+
+

Important: It is common on Ubuntu after a fresh install to see the following error the first time you use ClamAV:

+
freshclam
+
+freshclam: error while loading shared libraries: libclamav.so.7: cannot open shared object   file: No such file or directory
+
+

You can fix this error by using ldconfig to rebuild the library search path.

+
sudo ldconfig
+
+
+

If you are having issues updating the signature databases with freshclam, please review the freshclam FAQ.

+

SigTool

+

ClamAV provides sigtool as a command-line testing tool for assisting users in their efforts creating and working with virus signatures. While sigtool has many uses--including crafting signatures--of particular note, is sigtool's ability to help users and analysts in determining if a file detected by libclamav's virus signatures is a false positive.

+

This can be accomplished by using the command:

+
sigtool --unpack=FILE
+
+

Where FILE points to your virus signature databases. Then, once sigtool has finished unpacking the database into the directory from which you ran the command, you can search for the offending signature name (provided either by clamscan scan reports or clamd logs). As an example:

+
grep "Win.Test.EICAR" ./*
+
+

Or, do all that in one step with:

+
sigtool --find="Win.Test.EICAR"
+
+

This should give you the offending signature(s) in question, which can then be included as part of your false positive report.

+

To learn more in depth information on how sigtool can be used to help create virus signatures and work with malicious (and non-malicious) files please reference the many online tutorials on the topic.

+

Otherwise, information on available sigtool functions can be easily referenced with:

+
sigtool --help
+
+

Or (Unix/Linux only):

+
man sigtool
+
+

ClamBC

+

clambc is Clam Anti-Virus’ bytecode signature testing tool. It can be used to test newly crafted bytecode signatures or to help verify existing bytecode is executing against a sample as expected.

+

For more detailed help, please use:

+
clambc --help
+
+

Or (Unix/Linux only):

+
man clambc
+
+

Next Steps

+

Now that you know more about FreshClam and tools to work with the signature databases, it's time to run your first scan.

+

Create your own signatures

+

There is a whole community of malware researchers and signature writers. If you'd like to learn how to craft your own signatures, you can!

+

Scanning

+

Table Of Contents

+ +
+

Tip: The commands on Windows are generally the same, but you may need to add the .exe extension to run the ClamAV applications.

+
+

Daemon

+

ClamD

+

clamd is a multi-threaded daemon that uses libclamav to scan files for viruses. Scanning behavior can be fully configured to fit most needs by modifying clamd.conf.

+

As clamd requires a virus signature database to run, we recommend setting up ClamAV's official signatures before running clamd using freshclam.

+

The daemon works by listening for commands on the sockets specified in clamd.conf. Listening is supported over both unix local sockets and TCP sockets.

+

IMPORTANT: clamd does not currently protect or authenticate traffic coming over the TCP socket, meaning it will accept any and all of the following commands listed from any source. Thus, we strongly recommend following best networking practices when setting up your clamd instance. I.e. don't expose your TCP socket to the Internet.

+

Here is a quick list of the commands accepted by clamd over the socket.

+
    +
  • PING
  • +
  • VERSION
  • +
  • RELOAD
  • +
  • SHUTDOWN
  • +
  • SCAN file/directory
  • +
  • RAWSCAN file/directory
  • +
  • CONTSCAN file/directory
  • +
  • MULTISCAN file/directory
  • +
  • ALLMATCHSCAN file/directory
  • +
  • INSTREAM
  • +
  • FILDES
  • +
  • STATS
  • +
  • IDSESSION, END
  • +
+

As with most ClamAV tools, you can find out more about these by invoking the command:

+
man clamd
+
+

The daemon also handles the following signals as so:

+
    +
  • SIGTERM - perform a clean exit
  • +
  • SIGHUP - reopen the log file
  • +
  • SIGUSR2 - reload the database
  • +
+

It should be noted that clamd should not be started using the shell operator & or other external tools which would start it as a background process. Instead, you should run clamd which will load the database and then daemonize itself (unless you have specified otherwise in clamd.conf). After that, clamd is ready to accept connections and perform file scanning.

+

Once you have set up your configuration to your liking, and understand how you will be sending commands to the daemon, running clamd itself is simple. Simply execute the command:

+
clamd
+
+

ClamDScan

+

clamdscan is a clamd client, which greatly simplifies the task of scanning files with clamd. It sends commands to the clamd daemon across the socket specified in clamd.conf and generates a scan report after all requested scanning has been completed by the daemon.

+

Thus, to run clamdscan, you must have an instance of clamd already running as well.

+

Please keep in mind, that as a simple scanning client, clamdscan cannot change scanning and engine configurations. These are tied to the clamd instance and the configuration you set up in clamd.conf. Therefore, while clamdscan will accept many of the same commands as its sister tool clamscan, it will simply ignore most of them as (by design) no mechanism exists to make ClamAV engine configuration changes over the clamd socket.

+

Again, running clamdscan, once you have a working clamd instance, is simple:

+
clamdscan [*options*] [*file/directory/-*]
+
+

ClamDTop

+

clamdtop is a tool to monitor one or multiple instances of clamd. It has a colorized ncurses interface, which shows each job queued, memory usage, and information about the loaded signature database for the connected clamd instance(s). By default it will attempt to connect to the local clamd as defined in clamd.conf. However, you can specify other clamd instances at the command line.

+

To learn more, use the commands

+
man clamdtop
+
+

or

+
clamdtop --help
+
+

On-Access Scanning

+

The ClamOnAcc application provides On-Access Scanning for Linux systems. On-Access Scanning is a form of real-time protection that uses ClamD to scan files when they're accessed.

+

ClamOnAcc (v0.102+)

+

ClamAV's On-Access Scanning (clamonacc) is a client that runs in its own application alongside, but separately from the clamd instance. The On-Access Scanner is capable of preventing access to/from any malicious files it discovers--based on the verdict it receives from clamd--but by default it is configured to run in notify-only mode, which means it will simply alert the user if a malicious file is detected, then take any additional actions that the user may have specified at the command line, but it will not actively prevent processes from reading or writing to that file.

+
+

Disclaimer: Enabling Prevention mode will seriously impact performance if used on commonly accessed directories.

+
+
+

Tip: You can run ClamOnAcc multiple times simultaneously, each with a different config. If you want to enable Prevention-mode for one directory, while sticking to notify-only mode for any other monitored directories, that's an option!

+
+

On-Access Scanning is primarily set up through clamd.conf. However, you can learn more about all the configuration and command line options available to you by reading the On-Access Scanning User Guide.

+

Once you have set up the On-Access Scanner (and clamd) to your liking, you will first need to run clamd before you can start it. If your clamd instance is local, it is required you run clamd as a user that is excluded (via OnAccessExcludeUname or OnAccessExcludeUID) from On-Access scanning events (e.g.) to prevent clamonacc from triggering events endlessly as it sends scan requests to clamd:

+
su - clamav -c "/usr/local/bin/clamd
+
+

After the daemon is running, you can start the On-Access Scanner. clamonacc must be run as root in order to utilize its kernel event detection and intervention features:

+
sudo clamonacc
+
+

It will run a number of startup checks to test for a sane configuration, and ensure it can connect to clamd, and if everything checks out clamonacc will automatically fork to the background and begin monitoring your system for events.

+

ClamD (v0.101)

+

In older versions, ClamAV's On-Access Scanner is a thread that runs within a clamd instance. The On-Access Scanner is capable of blocking access to/from any malicious files it discovers--based on the verdict it finds using the engine it shares with clamd--but by default it is configured to run in notify-only mode, which means it will simply alert the user if a malicious file is detected, but it will not actively prevent processes from reading or writing to that file.

+

On-Access Scanning is primarily set up through clamd.conf. However, you can learn more about all the configuration and command line options available to you by reading the On-Access Scanning User Guide.

+

Once you have set up the On-Access Scanner to your liking, you will need to run clamd with elevated permissions to start it.

+
sudo clamd
+
+

One-Time Scanning

+

ClamScan

+

clamscan is a command line tool which uses libclamav to scan files and/or directories for viruses. Unlike clamdscan, clamscan does not require a running clamd instance to function. Instead, clamscan will create a new engine and load in the virus database each time it is run. It will then scan the files and/or directories specified at the command line, create a scan report, and exit.

+

By default, when loading databases, clamscan will check the location to which freshclam installed the virus database signatures. This behavior, along with a myriad of other scanning and engine controls, can be modified by providing flags and other options at the command line.

+

There are too many options to list all of them here. So we'll only cover a few common and more interesting ones:

+
    +
  • --log=FILE - save scan report to FILE
  • +
  • --database=FILE/DIR - load virus database from FILE or load all supported db files from DIR
  • +
  • --official-db-only[=yes/no(*)] - only load official signatures
  • +
  • --max-filesize=#n - files larger than this will be skipped and assumed clean
  • +
  • --max-scansize=#n - the maximum amount of data to scan for each container file
  • +
  • --leave-temps[=yes/no(*)]- do not remove temporary files
  • +
  • --file-list=FILE - scan files from FILE
  • +
  • --quiet - only output error messages
  • +
  • --bell - sound bell on virus detection
  • +
  • --cross-fs[=yes(*)/no] - scan files and directories on other filesystems
  • +
  • --move=DIRECTORY - move infected files into DIRECTORY
  • +
  • --copy=DIRECTORY - copy infected files into DIRECTORY
  • +
  • --bytecode-timeout=N - set bytecode timeout (in milliseconds)
  • +
  • --heuristic-alerts[=yes(*)/no] - toggles heuristic alerts
  • +
  • --alert-encrypted[=yes/no(*)] - alert on encrypted archives and documents
  • +
  • --nocerts - disable authenticode certificate chain verification in PE files
  • +
  • --disable-cache - disable caching and cache checks for hash sums of scanned files
  • +
+

To learn more about the options available when using clamscan please reference:

+
man clamscan
+
+

and

+
clamscan --help
+
+

Otherwise, the general usage of clamscan is:

+
clamscan [options] [file/directory/-]
+
+

Some basic scans

+

Run this to scan the files in the current directory:

+
clamscan .
+
+

This will scan the current directory. At the end of the scan, it will display a summary. If you notice in the clamscan output, it only scanned something like 60 files, even though there are more files in subdirectories. By default, clamscan will only scan files in the current directory.

+

Run this to scan all the files in the current directory:

+
clamscan --recursive .
+
+

Run this to scan ALL the files on your system, it will take quite a while. Keep in mind that you can cancel it at any time by pressing Ctrl-C:

+

Linux/Unix:

+
clamscan --recursive /
+
+

Windows:

+
clamscan.exe --recursive C:\
+
+

Process Memory Scanning

+
+

Note: This feature requires Windows and ClamAV version 0.105 or newer. You must also be running ClamAV as Administrator.

+
+

clamscan and clamdscan are able to scan the virtual memory of currently executing processes. To do so, use the --memory option:

+
clamscan --memory
+
+

The --kill and --unload options allow for killing/unloading infected loaded modules.

+

Disclaimers

+
+

Disclaimer: ClamAV doesn't have a "quick scan" mode. ClamAV is malware detection toolkit, not an endpoint security suite. It's up to you to decide what to scan. A full system scan is going to take a long time with ClamAV or with any anti-virus software.

+
+
+

Disclaimer 2: ClamScan, ClamOnAcc, and ClamDScan each include --remove options for deleting any file which alerts during a scan. This is generally a terrible idea, unless you're monitoring an upload/downloads directory. False positives happen! You do not want to have the wrong file accidentally deleted. Instead, consider using --move or perhaps just --copy and set up script with the ClamD VirusEvent feature to notify you when something has been detected.

+
+

Windows-specific Issues

+

Globbing

+

Since the Windows command prompt doesn't take care of wildcard expansion, minimal emulation of unix glob() is performed internally. It supports * and ? only.

+

File paths

+

Please always use the backslash as the path separator. SMB Network shares and UNC paths are supported.

+

Socket and libclamav API Input

+

The Windows version of ClamAV requires all the input to be UTF-8 encoded.

+

This affects:

+
    +
  • The API, notably the cl_scanfile() function
  • +
  • ClamD socket input, e.g. the commands SCAN, CONTSCAN, MUTLISCAN, etc.
  • +
  • ClamD socket output, i.e replies to the above queries
  • +
+

For legacy reasons ANSI (i.e. CP_ACP) input will still be accepted and processed as before, but with two important remarks:

+
    +
  1. Socket replies to ANSI queries will still be UTF-8 encoded.
  2. +
  3. ANSI sequences which are also valid UTF-8 sequences will be handled as UTF-8.
  4. +
+

As a side note, console output (stdin and stderr) will always be OEM encoded, even when redirected to a file.

+

On-Access Scanning

+

Purpose

+

This guide is for users interested in leveraging and understanding ClamAV's On-Access Scanning feature. It will walk through how to set up and use the On-Access Scanner and step through some common issues and their solutions.

+

Requirements

+

On-Access is only available on Linux systems. On Linux, On-Access requires a kernel version >= 3.8. This is because it leverages a kernel api called fanotify to block processes from attempting to access malicious files. This prevention occurs in kernel-space, and thus offers stronger protection than a purely user-space solution.

+

For Versions >= 0.102.0

+

It also requires Curl version >= 7.45 to ensure support for all curl options used by clamonacc. Users on Linux operating systems that package older versions of libcurl have a number of options:

+
    +
  1. Wait for your package maintainer to provide a newer version of libcurl.
  2. +
  3. Install a newer version of libcurl from source.
  4. +
  5. Disable installation of clamonacc and On-Access Scanning capabilities with the ./configure flag --disable-clamonacc.
  6. +
+

General Use

+

To use ClamAV's On-Access Scanner, operation will vary depending on version.

+

For Versions >= 0.102.0

+

You will need to run the clamd and clamonacc applications side by side. First, you will need to configure and run clamd. For instructions on how to do that, see the clamd configuration guide. One important thing to note while configuring clamd.conf is that--like clamdscan--the clamonacc application will connect to clamd using the clamd.conf settings for either LocalSocket or TCPAddr/TCPSocket. Another important thing to note, is that when using a clamd.conf that specifies a LocalSocket, then clamd will need to be run under a user with the right permissions to scan the files you plan on including in your watch-path.

+

Next, you will need to configure clamonacc. For a very simple configuration, follow these steps:

+
1. Open `clamd.conf` for editing
+2. Specify the path(s) you would like to recursively watch by setting the `OnAccessIncludePath` option
+3. Set `OnAccessPrevention` to `yes`
+4. Check what username `clamd` is running under
+5. Set `OnAccessExcludeUname` to `clamd`'s uname
+6. Save your work and close `clamd.conf`
+
+

For slightly more nuanced configurations, which may be adapted to your use case better, please check out the recipe guide below.

+

Then, run clamonacc with elevated permissions:

+
sudo clamonacc
+
+

If all went well, the On-Access scanner will fork to the background, and will now be actively protecting the path(s) specified with OnAccessIncludePath. You can test this by dropping an eicar file into the specified path, and attempting to read/access it (e.g. cat eicar.txt). This will result in an "Operation not permitted" message, triggered by fanotify blocking the access attempt at the kernel level.

+

Finally, you will have to restart both clamd and clamonacc. If default clamonacc performance is not to your liking, and your system has the resources available, we reccomend increasing the values for the following clamd.conf configuration options to increase performance:

+
    +
  • MaxQueue
  • +
  • MaxThreads
  • +
  • OnAccessMaxThreads
  • +
+

For Versions <= 0.101.x

+

You will only need to run the clamd application in older versions. First, +we reccomend you configure clamd for your environment. For instructions on how +to do that, see the clamd configuration guide.

+

Next, you will need to configure On Access Scanning using the clamd.conf file. For a very simple configuration follow these steps:

+
1. Open `clamd.conf` for editing
+2. Set the `ScanOnAccess` option to `yes`
+3. Specify the path(s) you would like to recursively watch by setting the `OnAccessIncludePath` option
+4. Set `OnAccessPrevention` to `yes`
+6. Save your work and close `clamd.conf`
+
+

For slightly more nuanced configurations, which may be adapted to your use case better, please check out the recipe guide below.

+

Then, run clamd with elevated permissions:

+
sudo clamd
+
+

If all went well, the On-Access scanner will fork to the background, and will now be actively protecting the path(s) specified with OnAccessIncludePath. You can test this by dropping an eicar file into the specified path, and attempting to read/access it (e.g. cat eicar.txt). This will result in an "Operation not permitted" message, triggered by fanotify blocking the access attempt at the kernel level.

+

Troubleshooting

+

Some OS distributors have disabled fanotify, despite kernel support. You can check for fanotify support on your kernel by running the command:

+
cat /boot/config-<kernel_version> | grep FANOTIFY
+
+

You should see the following:

+
CONFIG_FANOTIFY=y
+CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y
+
+

If you see this...

+
CONFIG_FANOTIFY_ACCESS_PERMISSIONS is not set
+
+

... then ClamAV's On-Access Scanner will still function, scanning and alerting on files normally in real time. However, it will be unable to block access attempts on malicious files. We call this notify-only mode.

+

ClamAV's On-Access Scanning system uses a scheme called Dynamic Directory Determination (DDD for short) which is a shorthand way of saying that it tracks the layout of every directory specified with OnAccessIncludePath dynamically, and recursively, in real time. It does this by leveraging inotify which by default has a limited number of watch-points available for use by a process at any given time. Given the complexity of some directory hierarchies, ClamAV may warn you that it has exhausted its supply of inotify watch-points (8192 by default). To increase the number of inotify watch-points available for use by ClamAV (to 524288), run the following command:

+
echo 524288 | sudo tee -a /proc/sys/fs/inotify/max_user_watches
+
+

The OnAccessIncludePath option will not accept / as a valid path. This is because fanotify works by blocking a process' access to a file until a access_ok or access_denied determination has been made by the original fanotify calling process. Thus, by placing fanotify watch-points on the entire filesystem, key system files may have their access blocked to key processes at the kernel level, which will result in a system lockup.

+

This restriction was made to prevent users from "shooting themselves in the foot." However, clever users will find it's possible to circumvent this restriction by using multiple OnAccessIncludePath options to recursively protect most of the filesystem anyways, or better still, simply the paths they truly care about.

+

The OnAccessMountPath option uses a different fanotify api configuration which makes it incompatible with OnAccessIncludePath and the DDD System. Therefore, inotify watch-point limitations will not be a concern when using this option. Unfortunately, this also means that the following options cannot be used in conjunction with OnAccessMountPath:

+
    +
  • OnAccessExtraScanning - is built around catching inotify events.
  • +
  • OnAccessExcludePath - is built upon the DDD System.
  • +
  • OnAccessPrevention - would lock up the system if / was selected for OnAccessMountPath. If you need OnAccessPrevention, you should use OnAccessIncludePath instead of OnAccessMountPath.
  • +
+

Configuration and Recipes

+

More nuanced behavior can be coerced from ClamAV's On-Access Scanner via careful modification to clamd.conf. Each option related to On-Access Scanning is easily identified by looking for the OnAccess prefix pre-pended to each option. The default clamd.conf file contains descriptions of each option, along with any documented limitations or safety features.

+

Below are examples of common use cases, recipes for the correct minimal configuration, and the expected behavioral result.

+

Use Case 0x0

+
    +
  • User needs to watch the entire file system, but blocking malicious access attempts isn't a concern
  • +
+
    ScanOnAccess yes ## versions <= 0.101.x
+    OnAccessMountPath /
+    OnAccessExcludeRootUID yes
+    OnAccessExcludeUname clamav ## versions >= 0.102
+
+

This configuration will put the On-Access Scanner into notify-only mode. It will also ensure only non-root, non-clam, user processes will trigger scans against the filesystem.

+

Use Case 0x1

+
    +
  • System Administrator needs to watch the home directory of multiple Users, but not all users. Blocking access attempts is un-needed.
  • +
+
    ScanOnAccess yes ## versions <= 0.101.x
+    OnAccessIncludePath /home
+    OnAccessExcludePath /home/user2
+    OnAccessExcludePath /home/user4
+    OnAccessExcludeUname clamav ## versions >= 0.102
+
+

With this configuration, the On-Access Scanner will watch the entirety of the /home directory recursively in notify-only mode. However, it will recursively exclude the /home/user2 and /home/user4 directories.

+

Use Case 0x2

+
    +
  • The user needs to protect a single directory non-recursively and ensure all access attempts on malicious files are blocked.
  • +
+
    ScanOnAccess yes ## versions <= 0.101.x
+    OnAccessIncludePath /home/user/Downloads
+    OnAccessExcludeUname clamav ## versions >= 0.102
+    OnAccessPrevention yes
+    OnAccessDisableDDD yes
+
+

The configuration above will result in non-recursive real-time protection of the /home/user/Downloads directory by ClamAV's On-Access Scanner. Any access attempts that ClamAV detects on malicious files within the top level of the directory hierarchy will be blocked by fanotify at the kernel level.

+

Command Line Options for Versions >= 0.102

+

Beyond clamd.conf configuration, you can change the behavior of the On-Access scanner by passing in a number of command line options. A list of all options can be retrieved with --help, but below is a list and explanation of some of options you might find most useful.

+
    +
  • --log=FILE (-l FILE) - passing this option is important if you want a record of scan results, otherwise clamonacc will operate silently.
  • +
  • --verbose (-v) - primarily for debugging as this will increase the amount of noise in your log by quite a lot, but useful for troubleshooting potential connection problems
  • +
  • --foreground (-F) - forces clamonacc to not for the background, which is useful for debugging potential issues with during startup or runtime
  • +
  • --include-list=FILE (-e FILE) - allows users to pass a list of directories for clamonacc to watch, each directory must be a full path and separated by a newline
  • +
  • --exclude-list=FILE (-e FILE) - same as include-list option, but for excluding at startup
  • +
  • --remove - after an infected verdict, an attempt will be made to remove the infected file
  • +
  • --move=DIRECTORY - just like the remove option, but infected file will be moved to the specified quarantine location instead
  • +
  • --copy=DIRECTORY - just like the move, except infected file is also left in place
  • +
+

Running ClamAV Services

+

Table Of Contents

+ + +

Windows Services

+
+

Note: This feature requires ClamAV version 0.104 or newer.

+
+

On Windows, clamd and freshclam have options that enable them to run in the background.

+

To install the services, first use the command:

+
clamd --install-service
+
+

Then use net start clamd and net stop clamd to start/stop the service.

+

To uninstall the service, use:

+
clamd --uninstall-service
+
+

Services can also be managed via the Services application on Windows.

+

How to Report A Bug

+
+

Warning Against Accidental Vulnerability Disclosure

+

You may not realize it, but your bug could be a security issue. Please review our Security Policy to determine if your bug is a security issue before you create a public ticket on GitHub issues.

+

GitHub Issues are public reports and security issues must be reported in private.

+
+

Steps Before You Report

+

If you find a bug in ClamAV, please do the following before you submit a bug report:

+
    +
  1. +

    Verify if the bug exists in the most recent stable release or ideally check if the bug exists in the latest unreleased code in Git before reporting the issue.

    +
  2. +
  3. +

    Review the open issues to make sure someone else hasn't already reported the same issue.

    +
    +

    Tip: Before switching to GitHub Issues, ClamAV used Bugzilla. You can also review older open tickets from the Bugzilla archive.

    +
    +
  4. +
  5. +

    Collect the required information, described below, to include with your report.

    +
  6. +
  7. +

    Create a new ticket on GitHub.

    +
  8. +
+

Please do not submit bugs for third party software.

+

Required Information

+

Please be sure to include all of the following information so that we can effectively address your bug:

+
    +
  • +

    ClamAV version, settings, and system details:

    +

    On the command line, run:

    +
    clamconf -n
    +
    +

    ClamConf will print out configuration information and some system information. The -n option prints out only non-default options. This information will help the team identify possible triggers for your bug.

    +
  • +
  • +

    3rd party signatures:

    +

    Please tell us if you are using any unofficial signature databases, that is anything other than main.cvd/cld, daily.cvd/cld, and bytecode.cvd/cld

    +
  • +
  • +

    How to reproduce the problem:

    +

    Include specific steps needed to reproduce the issue.

    +

    If the issue is reproducible only when scanning a specific file, attach it to the ticket.

    +

    Large Files: The maximum size for file attachments on GitHub Issues is 25MB and the maximum size for images is 10MB. If the file is too big to mail it, you can upload it to a password protected website and send us the URL and the credentials to access it.

    +

    If your file must be kept confidential you can reach out on the ClamAV Discord chat server to exchange email addresses and to share the zipped file or to share the zip password.

    +
    +

    CAUTION: Don’t forget to encrypt it or you may cause damage to the mail servers between you and us!

    +

    On the command line, run:

    +
    zip -P virus -e file.zip file.ext
    +
    +
    +
  • +
+

Creating signatures for ClamAV

+

Table Of Contents

+ +

Introduction

+

In order to detect malware and other file-based threats, ClamAV relies on signatures to differentiate clean and malicious/unwanted files. ClamAV signatures are primarily text-based and conform to one of the ClamAV-specific signature formats associated with a given method of detection. These formats are explained in the Signature formats section below. In addition, ClamAV 0.99 and above support signatures written in the YARA format. More information on this can be found in the Using YARA rules in ClamAV section.

+

The ClamAV project distributes a collection of signatures in the form of CVD (ClamAV Virus Database) files. The CVD file format provides a digitally-signed container that encapsulates the signatures and ensures that they can't be modified by a malicious third-party. This signature set is actively maintained by Cisco Talos and can be downloaded using the freshclam application that ships with ClamAV. For more details on this, see the CVD file section.

+

Database formats

+

ClamAV CVD and CLD database archives may be unpacked to the current directory using sigtool -u <database name>. For more details on inspecting CVD and CLD files, see Inspecting signatures inside a CVD file. Once unpacked, you'll observe a large collection of database files with various extensions described below.

+

The CVD and CLD database archives may be supplemented with custom database files in the formats described to gain additional detection functionality. This is done simply by adding files of the following formats to the database directory, typically /usr/local/share/clamav or "C:\Program Files\ClamAV\database". Alternatively, clamd and clamscan can be instructed to load the database from an alternative database file or database directory manually using the clamd DatabaseDirectory config option or the clamscan -d command line option.

+

Settings databases

+

ClamAV provides a handful of configuration related databases along side the signature definitions.

+

*.cfg: Dynamic config settings

+

*.cat *.crb: Trusted and revoked PE certs

+

*.ftm: File Type Magic (FTM)

+

Signature databases

+
+

Note: Signature databases with an extension ending in u are only loaded when Potentially Unwanted Application (PUA) signatures are enabled (default: off).

+
+

Body-based Signatures

+

Body-based signature content is a definition that matches not based on a hash but based on the specific sequences of bytes exhibited by the target file.

+

ClamAV body-based signature content has a special format to allow regex-like matching of data that is not entirely known. This format is used extensively in both Extended Signatures and Logical Signatures.

+

*.ndb *.ndu: Extended signatures

+

*.ldb *.ldu; *.idb: Logical Signatures

+

*.cdb: Container Metadata Signatures

+

*.cbc: Bytecode Signatures

+

*.pdb *.gdb *.wdb: Phishing URL Signatures)

+

Hash-based Signatures

+

*.hdb *.hsb *.hdu *.hsu: File hash signatures

+

*.mdb *.msb *.mdu *.msu: PE section hash signatures

+

Hash-based Signature format

+

Alternative signature support

+

*.yar *.yara: YARA rules

+

Other database files

+

*.fp *.sfp *.ign *.ign2: allowed files, ignored signatures

+

*.pwdb: Encrypted archive passwords

+

*.info: Database information`

+

Signature names

+

ClamAV uses the following prefixes for signature names:

+
    +
  • Worm for Internet worms
  • +
  • Trojan for backdoor programs
  • +
  • Adware for adware
  • +
  • Flooder for flooders
  • +
  • HTML for HTML files
  • +
  • Email for email messages
  • +
  • IRC for IRC trojans
  • +
  • JS for Java Script malware
  • +
  • PHP for PHP malware
  • +
  • ASP for ASP malware
  • +
  • VBS for VBS malware
  • +
  • BAT for BAT malware
  • +
  • W97M, W2000M for Word macro viruses
  • +
  • X97M, X2000M for Excel macro viruses
  • +
  • O97M, O2000M for generic Office macro viruses
  • +
  • DoS for Denial of Service attack software
  • +
  • DOS for old DOS malware
  • +
  • Exploit for popular exploits
  • +
  • VirTool for virus construction kits
  • +
  • Dialer for dialers
  • +
  • Joke for hoaxes
  • +
+

Important rules of the naming convention:

+
    +
  • Always use a -zippwd suffix in the malware name for ZIP container metadata (.cdb) signatures,
  • +
  • Always use a -rarpwd suffix in the malware name for RAR container metadata (.cdb) signatures,
  • +
  • Only use alphanumeric characters, dash (-), dot (.), underscores (_) in malware names, never use space, apostrophe or quote mark.
  • +
+

Signature Writing Tips and Tricks

+

Testing rules with clamscan

+

To test a new signature, first create a text file with the extension corresponding to the signature type (Ex: .ldb for logical signatures). Then, add the signature as it's own line within the file. This file can be passed to clamscan via the -d option, which tells ClamAV to load signatures from the file specified. If the signature is not formatted correctly, ClamAV will display an error - run clamscan with --debug --verbose to see additional information about the error message. Some common causes of errors include:

+
    +
  • The signature file has the incorrect extension type for the signatures contained within
  • +
  • The file has one or more blank lines
  • +
  • For logical signatures, a semicolon exists at the end of the file
  • +
+

If the rule is formatted correctly, clamscan will load the signature(s) in and scan any files specified via the command line invocation (or all files in the current directory if none are specified). A successful detection will look like the following:

+
clamscan -d test.ldb text.exe
+test.exe: Win.Malware.Agent.UNOFFICIAL FOUND
+
+----------- SCAN SUMMARY -----------
+Known viruses: 1
+Engine version: 0.100.0
+Scanned directories: 0
+Scanned files: 1
+Infected files: 1
+Data scanned: 17.45 MB
+Data read: 17.45 MB (ratio 1.00:1)
+Time: 0.400 sec (0 m 0 s)
+
+

If the rule did not match as intended:

+
    +
  • The file may have exceeded one or more of the default scanning limits built-in to ClamAV. Try running clamscan with the following options to see if raising the limits addresses the issue: --max-filesize=2000M --max-scansize=2000M --max-files=2000000 --max-recursion=2000000 --max-embeddedpe=2000M --max-htmlnormalize=2000000 --max-htmlnotags=2000000 --max-scriptnormalize=2000000 --max-ziptypercg=2000000 --max-partitions=2000000 --max-iconspe=2000000 --max-rechwp3=2000000 --pcre-match-limit=2000000 --pcre-recmatch-limit=2000000 --pcre-max-filesize=2000M.
  • +
  • If matching on HTML or text files, ClamAV might be performing normalization that causes the content of the scanned file to change. See the HTML and Text file sections for more details.
  • +
  • libclamav may have been unable to unpack or otherwise process the file. See Debug information from libclamav for more details.
  • +
+

NOTE: If you run clamscan with a -d flag, ClamAV will not load in the signatures downloaded via freshclam. This means that:

+
    +
  • some of ClamAV's unpacking support might be disabled, since some unpackers are implemented as bytecode signatures
  • +
  • PE certificate trust verification based on Authenticode signatures won't work, since this functionality relies on .crb rules
  • +
+

If any of this functionality is needed, load in the CVD files manually with additional -d flags.

+

Debug information from libclamav

+

In order to create efficient signatures for ClamAV it’s important to understand how the engine handles input files. The best way to see how it works is having a look at the debug information from libclamav. You can do it by calling clamscan with the --debug and --leave-temps flags. The first switch makes clamscan display all the interesting information from libclamav and the second one avoids deleting temporary files so they can be analyzed further.

+

The now important part of the info is:

+
    $ clamscan --debug attachment.exe
+    [...]
+    LibClamAV debug: Recognized MS-EXE/DLL file
+    LibClamAV debug: Matched signature for file type PE
+    LibClamAV debug: File type: Executable
+
+

The engine recognized a windows executable.

+
    LibClamAV debug: Machine type: 80386
+    LibClamAV debug: NumberOfSections: 3
+    LibClamAV debug: TimeDateStamp: Fri Jan 10 04:57:55 2003
+    LibClamAV debug: SizeOfOptionalHeader: e0
+    LibClamAV debug: File format: PE
+    LibClamAV debug: MajorLinkerVersion: 6
+    LibClamAV debug: MinorLinkerVersion: 0
+    LibClamAV debug: SizeOfCode: 0x9000
+    LibClamAV debug: SizeOfInitializedData: 0x1000
+    LibClamAV debug: SizeOfUninitializedData: 0x1e000
+    LibClamAV debug: AddressOfEntryPoint: 0x27070
+    LibClamAV debug: BaseOfCode: 0x1f000
+    LibClamAV debug: SectionAlignment: 0x1000
+    LibClamAV debug: FileAlignment: 0x200
+    LibClamAV debug: MajorSubsystemVersion: 4
+    LibClamAV debug: MinorSubsystemVersion: 0
+    LibClamAV debug: SizeOfImage: 0x29000
+    LibClamAV debug: SizeOfHeaders: 0x400
+    LibClamAV debug: NumberOfRvaAndSizes: 16
+    LibClamAV debug: Subsystem: Win32 GUI
+    LibClamAV debug: ------------------------------------
+    LibClamAV debug: Section 0
+    LibClamAV debug: Section name: UPX0
+    LibClamAV debug: Section data (from headers - in memory)
+    LibClamAV debug: VirtualSize: 0x1e000 0x1e000
+    LibClamAV debug: VirtualAddress: 0x1000 0x1000
+    LibClamAV debug: SizeOfRawData: 0x0 0x0
+    LibClamAV debug: PointerToRawData: 0x400 0x400
+    LibClamAV debug: Section's memory is executable
+    LibClamAV debug: Section's memory is writeable
+    LibClamAV debug: ------------------------------------
+    LibClamAV debug: Section 1
+    LibClamAV debug: Section name: UPX1
+    LibClamAV debug: Section data (from headers - in memory)
+    LibClamAV debug: VirtualSize: 0x9000 0x9000
+    LibClamAV debug: VirtualAddress: 0x1f000 0x1f000
+    LibClamAV debug: SizeOfRawData: 0x8200 0x8200
+    LibClamAV debug: PointerToRawData: 0x400 0x400
+    LibClamAV debug: Section's memory is executable
+    LibClamAV debug: Section's memory is writeable
+    LibClamAV debug: ------------------------------------
+    LibClamAV debug: Section 2
+    LibClamAV debug: Section name: UPX2
+    LibClamAV debug: Section data (from headers - in memory)
+    LibClamAV debug: VirtualSize: 0x1000 0x1000
+    LibClamAV debug: VirtualAddress: 0x28000 0x28000
+    LibClamAV debug: SizeOfRawData: 0x200 0x1ff
+    LibClamAV debug: PointerToRawData: 0x8600 0x8600
+    LibClamAV debug: Section's memory is writeable
+    LibClamAV debug: ------------------------------------
+    LibClamAV debug: EntryPoint offset: 0x8470 (33904)
+
+

The section structure displayed above suggests the executable is packed +with UPX.

+
    LibClamAV debug: ------------------------------------
+    LibClamAV debug: EntryPoint offset: 0x8470 (33904)
+    LibClamAV debug: UPX/FSG/MEW: empty section found - assuming
+                    compression
+    LibClamAV debug: UPX: bad magic - scanning for imports
+    LibClamAV debug: UPX: PE structure rebuilt from compressed file
+    LibClamAV debug: UPX: Successfully decompressed with NRV2B
+    LibClamAV debug: UPX/FSG: Decompressed data saved in
+                    /tmp/clamav-90d2d25c9dca42bae6fa9a764a4bcede
+    LibClamAV debug: ***** Scanning decompressed file *****
+    LibClamAV debug: Recognized MS-EXE/DLL file
+    LibClamAV debug: Matched signature for file type PE
+
+

Indeed, libclamav recognizes the UPX data and saves the decompressed +(and rebuilt) executable into +/tmp/clamav-90d2d25c9dca42bae6fa9a764a4bcede. Then it continues by +scanning this new file:

+
    LibClamAV debug: File type: Executable
+    LibClamAV debug: Machine type: 80386
+    LibClamAV debug: NumberOfSections: 3
+    LibClamAV debug: TimeDateStamp: Thu Jan 27 11:43:15 2011
+    LibClamAV debug: SizeOfOptionalHeader: e0
+    LibClamAV debug: File format: PE
+    LibClamAV debug: MajorLinkerVersion: 6
+    LibClamAV debug: MinorLinkerVersion: 0
+    LibClamAV debug: SizeOfCode: 0xc000
+    LibClamAV debug: SizeOfInitializedData: 0x19000
+    LibClamAV debug: SizeOfUninitializedData: 0x0
+    LibClamAV debug: AddressOfEntryPoint: 0x7b9f
+    LibClamAV debug: BaseOfCode: 0x1000
+    LibClamAV debug: SectionAlignment: 0x1000
+    LibClamAV debug: FileAlignment: 0x1000
+    LibClamAV debug: MajorSubsystemVersion: 4
+    LibClamAV debug: MinorSubsystemVersion: 0
+    LibClamAV debug: SizeOfImage: 0x26000
+    LibClamAV debug: SizeOfHeaders: 0x1000
+    LibClamAV debug: NumberOfRvaAndSizes: 16
+    LibClamAV debug: Subsystem: Win32 GUI
+    LibClamAV debug: ------------------------------------
+    LibClamAV debug: Section 0
+    LibClamAV debug: Section name: .text
+    LibClamAV debug: Section data (from headers - in memory)
+    LibClamAV debug: VirtualSize: 0xc000 0xc000
+    LibClamAV debug: VirtualAddress: 0x1000 0x1000
+    LibClamAV debug: SizeOfRawData: 0xc000 0xc000
+    LibClamAV debug: PointerToRawData: 0x1000 0x1000
+    LibClamAV debug: Section contains executable code
+    LibClamAV debug: Section's memory is executable
+    LibClamAV debug: ------------------------------------
+    LibClamAV debug: Section 1
+    LibClamAV debug: Section name: .rdata
+    LibClamAV debug: Section data (from headers - in memory)
+    LibClamAV debug: VirtualSize: 0x2000 0x2000
+    LibClamAV debug: VirtualAddress: 0xd000 0xd000
+    LibClamAV debug: SizeOfRawData: 0x2000 0x2000
+    LibClamAV debug: PointerToRawData: 0xd000 0xd000
+    LibClamAV debug: ------------------------------------
+    LibClamAV debug: Section 2
+    LibClamAV debug: Section name: .data
+    LibClamAV debug: Section data (from headers - in memory)
+    LibClamAV debug: VirtualSize: 0x17000 0x17000
+    LibClamAV debug: VirtualAddress: 0xf000 0xf000
+    LibClamAV debug: SizeOfRawData: 0x17000 0x17000
+    LibClamAV debug: PointerToRawData: 0xf000 0xf000
+    LibClamAV debug: Section's memory is writeable
+    LibClamAV debug: ------------------------------------
+    LibClamAV debug: EntryPoint offset: 0x7b9f (31647)
+    LibClamAV debug: Bytecode executing hook id 257 (0 hooks)
+    attachment.exe: OK
+    [...]
+
+

No additional files get created by libclamav. By writing a signature for the decompressed file you have more chances that the engine will detect the target data when it gets compressed with another packer.

+

This method should be applied to all files for which you want to create signatures. By analyzing the debug information you can quickly see how the engine recognizes and preprocesses the data and what additional files get created. Signatures created for bottom-level temporary files are usually more generic and should help detecting the same malware in different forms.

+

Writing signatures for special files

+

HTML

+

ClamAV contains HTML normalization code which makes it easier to write signatures for HTML data that might differ based on white space, capitalization, and other insignificant differences. Running sigtool --html-normalise on a HTML file can be used to see what a file's contents will look like after normalization. This command should generate the following files:

+
    +
  • +

    nocomment.html - the file is normalized, lower-case, with all comments and superfluous white space removed

    +
  • +
  • +

    notags.html - as above but with all HTML tags removed

    +
  • +
  • +

    javascript - any script contents are normalized and the results appended to this file

    +
  • +
+

The code automatically decodes JScript.encode parts and char ref’s (e.g. &#102;). To create a successful signature for the input file type, the rule must match on the contents of one of the created files. Signatures matching on normalized HTML should have a target type of 3. For reference, see Target Types.

+

Text files

+

Similarly to HTML all ASCII text files get normalized (converted to lower-case, all superfluous white space and control characters removed, etc.) before scanning. Running sigtool --ascii-normalise on a text file will result in a normalized version being written to the file named normalised\_text. Rules matching on normalized ASCII text should have a target type of 7. For reference, see Target Types.

+

Compressed Portable Executable files

+

If the file is compressed with UPX, FSG, Petite or another PE packer supported by libclamav, ClamAV will attempt to automatically unpack the executable and evaluate signatures against the unpacked executable. To inspect the executable that results from ClamAV's unpacking process, run clamscan with --debug --leave-temps. Example output for a FSG compressed file:

+
    LibClamAV debug: UPX/FSG/MEW: empty section found - assuming compression
+    LibClamAV debug: FSG: found old EP @119e0
+    LibClamAV debug: FSG: Unpacked and rebuilt executable saved in
+    /tmp/clamav-f592b20f9329ac1c91f0e12137bcce6c
+
+

In the example above, /tmp/clamav-f592b20f9329ac1c91f0e12137bcce6c is the unpacked executable, and a signature can be written based off of this file.

+

Using sigtool

+

sigtool pulls in libclamav and provides shortcuts to doing tasks that clamscan does behind the scenes. These can be really useful when writing a signature or trying to get information about a signature that might be causing FPs or performance problems.

+

The following sigtool flags can be especially useful for signature writing:

+
    +
  • +

    --md5 / --sha1 / --sha256: Generate the MD5/SHA1/SHA256 hash and calculate the file size, outputting both as a properly-formatted .hdb/.hsb signature

    +
  • +
  • +

    --mdb: Generate section hashes of the specified file. This is useful when generating .mdb signatures.

    +
  • +
  • +

    --decode: Given a ClamAV signature from STDIN, show a more user-friendly representation of it. An example usage of this flag is cat test.ldb | sigtool --decode.

    +
  • +
  • +

    --hex-dump: Given a sequence of bytes from STDIN, print the hex equivalent. An example usage of this flag is echo -n "Match on this" | sigtool --hex-dump.

    +
  • +
  • +

    --html-normalise: Normalize the specified HTML file in the way that clamscan will before looking for rule matches. Writing signatures off of these files makes it easier to write rules for target type HTML (you'll know what white space, capitalization, etc. to expect). See the HTML section for more details.

    +
  • +
  • +

    --ascii-normalise: Normalize the specified ASCII text file in the way that clamscan will before looking for rule matches. Writing signatures off of this normalized file data makes it easier to write rules for target type Txt (you'll know what white space, capitalization, etc. to expect). See the Text files sectino for more details.

    +
  • +
  • +

    --print-certs: Print the Authenticode signatures of any PE files specified. +This is useful when writing signature-based .crb rule files.

    +
  • +
  • +

    --vba: Extract VBA/Word6 macro code

    +
  • +
  • +

    --test-sigs: Given a signature and a sample, determine whether the signature matches and, if so, display the offset into the file where the match occurred. This can be useful for investigating false positive matches in clean files.

    +
  • +
+

Inspecting signatures inside a CVD file

+

CVD (ClamAV Virus Database) is a digitally signed container that includes signature databases in various text formats. The header of the container is a 512 bytes long string with colon separated fields:

+
    ClamAV-VDB:build time:version:number of signatures:functionality level required:MD5 checksum:digital signature:builder name:build time (sec)
+
+

sigtool --info displays detailed information about a given CVD file:

+
    zolw@localhost:/usr/local/share/clamav$ sigtool -i main.cvd
+    File: main.cvd
+    Build time: 09 Dec 2007 15:50 +0000
+    Version: 45
+    Signatures: 169676
+    Functionality level: 21
+    Builder: sven
+    MD5: b35429d8d5d60368eea9630062f7c75a
+    Digital signature: dxsusO/HWP3/GAA7VuZpxYwVsE9b+tCk+tPN6OyjVF/U8
+    JVh4vYmW8mZ62ZHYMlM903TMZFg5hZIxcjQB3SX0TapdF1SFNzoWjsyH53eXvMDY
+    eaPVNe2ccXLfEegoda4xU2TezbGfbSEGoU1qolyQYLX674sNA2Ni6l6/CEKYYh
+    Verification OK.
+
+

The ClamAV project distributes a number of CVD files, including main.cvd and daily.cvd.

+

To view the signature associated with a given detection name, the CVD files can be unpacked and the underlying text files searched for a rule definition using a tool like grep. To do this, use sigtool's --unpack flag as follows:

+
    $ mkdir /tmp/clamav-sigs
+    $ cd /tmp/clamav-sigs/
+    $ sigtool --unpack /var/lib/clamav/main.cvd
+    $ ls
+    COPYING   main.fp   main.hsb   main.mdb  main.ndb
+    main.crb  main.hdb  main.info  main.msb  main.sfp
+
+

External tools

+

Below are tools that can be helpful when writing ClamAV signatures:

+
    +
  • CASC - CASC is a plugin for IDA Pro that allows the user to highlight sections of code and create a signature based on the underlying instructions (with options to ignore bytes associated with registers, addresses, and offsets). It also contains SigAlyzer, a tool to take an existing signature and locate the regions within the binary that match the subsignatures.
  • +
+

Database Info

+

The .info file format specifies information about the other database files unpacked from a CVD or CLD database archive. This file exists for the purposes of validating the correctness of the official ClamAV database container files and cannot be loaded a la carte.

+

The format is simply:

+
name:size:sha256
+
+

name: The database file name.

+

size: The size in bytes of the database.

+

sha256: A SHA256 hash of the database.

+

Dynamic Configuration (DCONF)

+

ClamAV supports a limited set of configuration options that may be enabled or disabled via settings in the *.cfg database. At this time, these settings are distributed in daily.cfg.

+

The goal of DCONF is to enable the ClamAV team to rapidly disable new or experimental features for specific ClamAV versions if a significant defect is discovered after release.

+

This database is small, and the settings are largely vestigial. The team has not had a need to disable many features in a long time, and so the ClamAV versions in the settings at this time should no longer be in use.

+

The strings and values referenced in daily.cfg are best cross-referenced with the macros and structures defined here:

+
    +
  • https://github.com/Cisco-Talos/clamav/blob/main/libclamav/dconf.h#L49
  • +
  • https://github.com/Cisco-Talos/clamav/blob/main/libclamav/dconf.c#L54
  • +
+

The format for a DCONF signature is:

+
Category:Flags:StartFlevel:EndFlevel
+
+

Category may be one of:

+
    +
  • PE
  • +
  • ELF
  • +
  • MACHO
  • +
  • ARCHIVE
  • +
  • DOCUMENT
  • +
  • MAIL
  • +
  • OTHER
  • +
  • PHISHING
  • +
  • BYTECODE
  • +
  • STATS
  • +
  • PCRE
  • +
+

Flags:

+

Every feature that may be configured via DCONF is listed in struct dconf_module modules in libclamav/dconf.c. Any given feature may be default-on or default-off. Default-on features have the 4th field set to a 1 and default off are set to 0. The Flags field for a given Category overrides the defaults for all of the options listed under that category.

+

A settings of 0x0, for example, means that all options the category be disabled.

+

The macros listed in libclamav/dconf.h will help you identify which bits to set to get the desired results.

+

StartFlevel:

+

This is the FLEVEL of the minimum ClamAV engine for which you want the settings to be in effect.

+

EndFlevel:

+

This is the FLEVEL of the maximum ClamAV engine for which you want the settings to be in effect. You may wish to select 255 to override the defaults of future releases.

+

Example

+

Consider the OTHER_CONF_PDFNAMEOBJ option in the category OTHER.

+
#define OTHER_CONF_UUENC        0x1     // Default: 1
+#define OTHER_CONF_SCRENC       0x2     // Default: 1
+#define OTHER_CONF_RIFF         0x4     // Default: 1
+#define OTHER_CONF_JPEG         0x8     // Default: 1
+#define OTHER_CONF_CRYPTFF      0x10    // Default: 1
+#define OTHER_CONF_DLP          0x20    // Default: 1
+#define OTHER_CONF_MYDOOMLOG    0x40    // Default: 1
+#define OTHER_CONF_PREFILTERING 0x80    // Default: 1
+#define OTHER_CONF_PDFNAMEOBJ   0x100   // Default: 1
+#define OTHER_CONF_PRTNINTXN    0x200   // Default: 1
+#define OTHER_CONF_LZW          0x400   // Default: 1
+
+

All of the OTHER options, including OTHER_CONF_PDFNAMEOBJ are default-on. To disable the option for ClamAV v0.100.X but leave the other options in their default settings, we would need to set the flags to:

+
0110 1111 1111
+   ^pdfnameobj off
+
+

Or in hex: 0x6FF

+

The example setting to place in daily.cfg then woudl be:

+
OTHER:0x6FF:90:99
+
+

Trusted and Revoked Certificates

+

Clamav 0.98 checks signed PE files for certificates and verifies each certificate in the chain against a database of trusted and revoked certificates. The signature format is

+
Name;Trusted;Subject;Serial;Pubkey;Exponent;CodeSign;TimeSign;CertSign;
+NotBefore;Comment[;minFL[;maxFL]]
+
+

where the corresponding fields are:

+
    +
  • +

    Name: name of the entry

    +
  • +
  • +

    Trusted: bit field, specifying whether the cert is trusted. 1 for trusted. 0 for revoked

    +
  • +
  • +

    Subject: sha1 of the Subject field in hex

    +
  • +
  • +

    Serial: the serial number as clamscan --debug --verbose reports

    +
  • +
  • +

    Pubkey: the public key in hex

    +
  • +
  • +

    Exponent: the exponent in hex. Currently ignored and hardcoded to 010001 (in hex)

    +
  • +
  • +

    CodeSign: bit field, specifying whether this cert can sign code. 1 for true, 0 for false

    +
  • +
  • +

    TimeSign: bit field. 1 for true, 0 for false

    +
  • +
  • +

    CertSign: bit field, specifying whether this cert can sign other certs. 1 for true, 0 for false

    +
  • +
  • +

    NotBefore: integer, cert should not be added before this variable. Defaults to 0 if left empty

    +
  • +
  • +

    Comment: comments for this entry

    +
  • +
+

The signatures for certs are stored inside .crb files.

+

File Type Magic

+

ClamAV's primary mechanism for determining file types is to match the file with a File Type Magic signature. These file type signatures are compiled into ClamAV, and may also be overridden dynamically using the definition founds found in a *.ftm file.

+

The ClamAV standard signature database includes these definitions in daily.ftm.

+

The signature format is not too disimilar from NDB body-based signatures.

+

The format is:

+
magictype:offset:magicbytes:name:rtype:type[:min_flevel[:max_flevel]]
+
+

Where:

+

magictype: Supported magic types include:

+
    +
  • 0 - direct memory comparison of magicbytes for file types
  • +
  • 1 - The magicbytes use the body-based content matching format.
  • +
  • 4 - direct memory comparison of magicbytes for partition types (HFS+, HFSX)
  • +
+

offset: The offset from start of the file to match against. May be * if magictype is 1.

+

name: A descriptive name for the file type.

+

rtype: Previously detected file type. Usually CL_TYPE_ANY as a wild-card.

+

type: The CL_TYPE corresponding with the file type signature. See the CL_TYPE reference for details.

+

min_flevel: (optional) The minimum ClamAV engine that the file type signature works with. See the FLEVEL reference for details. To be used in the event that file type support has been recently added.

+

max_flevel: (optional, requires min_flevel) The maximum ClamAV engine that the file type signature works with. To be used in the event that file type support has been recently removed.

+

Allow list databases

+

File allow lists

+

To allow a specific file use the MD5 signature format and place it inside a database file with the extension of .fp (for "false positive"). To allow a specific file with the SHA1 or SHA256 file hash signature format, place the signature inside a database file with the extension of .sfp (for "SHA false positive").

+

To generate FP or SFP signatures, try something like this...

+

MD5:

+
sigtool --md5 /path/to/false/positive/file >> /path/to/databases/false-positives.fp
+
+

SHA256:

+
sigtool --sha256 /path/to/false/positive/file >> /path/to/databases/false-positives.sfp
+
+

Here's an example adding the EICAR test file to an allow list by generating a sha256 false positive signature:

+
❯ clamscan ~/Downloads/eicar.com
+/mnt/c/Users/micah/Downloads/eicar.com: Win.Test.EICAR_HDB-1 FOUND
+
+...
+
+❯ sigtool --sha256 ~/Downloads/eicar.com >> /var/lib/clamav/false-positives.sfp
+
+❯ clamscan ~/Downloads/eicar.com
+/mnt/c/Users/micah/Downloads/eicar.com: OK
+...
+
+

Signature ignore lists

+

To ignore a specific signature from the database you just add the signature name into a local file with the .ign2 extension and store it inside the database directory.

+

E.g:

+
Eicar-Test-Signature
+
+

Additionally, you can follow the signature name with the MD5 of the entire database entry for this signature. In such a case, the signature will no longer be ignored when its entry in the database gets modified (eg. the signature gets updated to avoid false alerts). E.g:

+
Eicar-Test-Signature:bc356bae4c42f19a3de16e333ba3569c
+
+

Historically, signature ignores were added to .ign files. This format is still functional, though it has been replaced by the .ign2 database.

+

File hash signatures

+

The easiest way to create signatures for ClamAV is to use filehash checksums, however this method can be only used against static malware.

+

MD5 hash-based signatures

+

To create a MD5 signature for test.exe use the --md5 option of +sigtool:

+
zolw@localhost:/tmp/test$ sigtool --md5 test.exe > test.hdb
+zolw@localhost:/tmp/test$ cat test.hdb
+48c4533230e1ae1c118c741c0db19dfb:17387:test.exe
+
+

That’s it! The signature is ready for use:

+
zolw@localhost:/tmp/test$ clamscan -d test.hdb test.exe
+test.exe: test.exe FOUND
+
+----------- SCAN SUMMARY -----------
+Known viruses: 1
+Scanned directories: 0
+Engine version: 0.92.1
+Scanned files: 1
+Infected files: 1
+Data scanned: 0.02 MB
+Time: 0.024 sec (0 m 0 s)
+
+

You can change the name (by default sigtool uses the name of the file) and place it inside a *.hdb file. A single database file can include any number of signatures. To get them automatically loaded each time clamscan/clamd starts just copy the database file(s) into the local virus database directory (eg. /usr/local/share/clamav).

+

The hash-based signatures shall not be used for text files, HTML and any other data that gets internally preprocessed before pattern matching. If you really want to use a hash signature in such a case, run clamscan with --debug and --leave-temps flags as described above and create a signature for a preprocessed file left in /tmp. Please keep in mind that a hash signature will stop matching as soon as a single byte changes in the target file.

+

SHA1 and SHA256 hash-based signatures

+

ClamAV 0.98 has also added support for SHA1 and SHA256 file checksums. The format is the same as for MD5 file checksum. It can differentiate between them based on the length of the hash string in the signature. For best backwards compatibility, these should be placed inside a *.hsb file. The format is:

+
HashString:FileSize:MalwareName
+
+

Hash signatures with unknown size

+

ClamAV 0.98 has also added support for hash signatures where the size is not known but the hash is. It is much more performance-efficient to use signatures with specific sizes, so be cautious when using this feature. For these cases, the ’*’ character can be used in the size field. To ensure proper backwards compatibility with older versions of ClamAV, these signatures must have a minimum functional level of 73 or higher. Signatures that use the wildcard size without this level set will be rejected as malformed.

+

Sample .hsb signature matching any size:

+
HashString:*:MalwareName:73
+
+

Sample .msb signature matching any size:

+
*:PESectionHash:MalwareName:73
+
+

PE section based hash signatures

+

You can create a hash signature for a specific section in a PE file. Such signatures shall be stored inside .mdb (MD5) and .msb files in the following format:

+
PESectionSize:PESectionHash:MalwareName
+
+

The easiest way to generate MD5 based section signatures is to extract target PE sections into separate files and then run sigtool with the option --mdb

+

ClamAV also has support for SHA1 and SHA256 section based signatures. The format is the same as for MD5 PE section based signatures. It can differentiate between them based on the length of the hash string in the signature. For best backwards compatibility, these should be placed inside a *.msb file.

+

Body-based Signature Content Format

+

ClamAV stores all body-based (content-based) signatures in a hexadecimal format, with exception to ClamAV's YARA rule support. In this section by a hex-signature we mean a fragment of malware’s body converted into a hexadecimal string which can be additionally extended using various wildcards.

+

Hexadecimal format

+

You can use sigtool --hex-dump to convert any data into a hex-string:

+
zolw@localhost:/tmp/test$ sigtool --hex-dump
+How do I look in hex?
+486f7720646f2049206c6f6f6b20696e206865783f0a
+
+

Wildcards

+

ClamAV supports the following wildcards for hex-signatures:

+
    +
  • +

    ??

    +

    Match any byte.

    +
  • +
  • +

    a?

    +

    Match a high nibble (the four high bits).

    +
  • +
  • +

    ?a

    +

    Match a low nibble (the four low bits).

    +
  • +
  • +

    *

    +

    Match any number of bytes.

    +
  • +
  • +

    {n}

    +

    Match n bytes.

    +
  • +
  • +

    {-n}

    +

    Match n or less bytes.

    +
  • +
  • +

    {n-}

    +

    Match n or more bytes.

    +
  • +
  • +

    {n-m}

    +

    Match between n and m bytes (where m > n).

    +
  • +
  • +

    HEXSIG[x-y]aa or aa[x-y]HEXSIG

    +

    Match aa anchored to a hex-signature, see Bugzilla ticket 776 for discussion and +examples.

    +
  • +
+

The range signatures * and {} virtually separate a hex-signature into two parts, eg. aabbcc*bbaacc is treated as two sub-signatures aabbcc and bbaacc with any number of bytes between them. It’s a requirement that each sub-signature includes a block of two static characters somewhere in its body. Note that there is one exception to this restriction; that is when the range wildcard is of the form {n} with n<128. In this case, ClamAV uses an optimization and translates {n} to the string consisting of n ?? character wildcards. Character wildcards do not divide hex signatures into two parts and so the two static character requirement does not apply.

+

Character classes

+

ClamAV supports the following character classes for hex-signatures:

+
    +
  • +

    (B)

    +

    Match word boundary (including file boundaries).

    +
  • +
  • +

    (L)

    +

    Match CR, CRLF or file boundaries.

    +
  • +
  • +

    (W)

    +

    Match a non-alphanumeric character.

    +
  • +
+

Alternate strings

+
    +
  • +

    Single-byte alternates (clamav-0.96) (aa|bb|cc|...) or !(aa|bb|cc|...) Match a member from a set of bytes (eg: aa, bb, cc, ...).

    +
      +
    • Negation operation can be applied to match any non-member, assumed to be one-byte in length.
    • +
    • Signature modifiers and wildcards cannot be applied.
    • +
    +
  • +
  • +

    Multi-byte fixed length alternates (aaaa|bbbb|cccc|...) or !(aaaa|bbbb|cccc|...) Match a member from a set of multi-byte alternates (eg: aaaa, bbbb, cccc, ...) of n-length.

    +
      +
    • All set members must be the same length.
    • +
    • Negation operation can be applied to match any non-member, assumed to be n-bytes in length (clamav-0.98.2).
    • +
    • Signature modifiers and wildcards cannot be applied.
    • +
    +
  • +
  • +

    Generic alternates (clamav-0.99) (alt1|alt2|alt3|...) Match a member from a set of alternates (eg: alt1, alt2, alt3, ...) that can be of variable lengths.

    +
      +
    • Negation operation cannot be applied.
    • +
    • Signature modifiers and nibble wildcards (eg: ??, a?, ?a) can be applied.
    • +
    • Ranged wildcards (eg: {n-m}) are limited to a fixed range of less than 128 bytes (eg: {1} -> {127}).
    • +
    +
  • +
+
+

Note: Using signature modifiers and wildcards classifies the alternate type to be a generic alternate. Thus single-byte alternates and multi-byte fixed length alternates can use signature modifiers and wildcards but will be classified as generic alternate. This means that negation cannot be applied in this situation and there is a slight performance impact.

+
+

Logical signatures

+

Logical signatures allow combining of multiple signatures in extended format using logical operators. They can provide both more detailed and flexible pattern matching. The logical sigs are stored inside *.ldb files in the following format:

+
SignatureName;TargetDescriptionBlock;LogicalExpression;Subsig0;
+Subsig1;Subsig2;...
+
+

where:

+
    +
  • +

    TargetDescriptionBlock provides information about the engine and target file with comma separated Arg:Val pairs. For args where Val is a range, the minimum and maximum values should be expressed as min-max.

    +
  • +
  • +

    LogicalExpression specifies the logical expression describing the relationship between Subsig0...SubsigN. Basis clause: 0,1,...,N decimal indexes are SUB-EXPRESSIONS representing Subsig0, Subsig1,...,SubsigN respectively. Inductive clause: if A and B are SUB-EXPRESSIONS and X, Y are decimal numbers then (A&B), (A|B), A=X, A=X,Y, A>X, A>X,Y, A<X and A<X,Y are SUB-EXPRESSIONS

    +
  • +
  • +

    SubsigN is n-th subsignature in extended format possibly preceded with an offset. There can be specified up to 64 subsigs.

    +
  • +
+

Keywords used in TargetDescriptionBlock:

+
    +
  • +

    Target:X: A number specifying the type of the target file: Target Types.

    +
  • +
  • +

    Engine:X-Y: Required engine functionality level (range; 0.96). Note that if the Engine keyword is used, it must be the first one in the TargetDescriptionBlock for backwards compatibility. See the FLEVEL reference for details.

    +
  • +
  • +

    FileSize:X-Y: Required file size (range in bytes; 0.96)

    +
  • +
  • +

    EntryPoint: Entry point offset (range in bytes; 0.96)

    +
  • +
  • +

    NumberOfSections: Required number of sections in executable (range; 0.96)

    +
  • +
  • +

    Container:CL_TYPE_*: File type of the container which stores the scanned file.

    +

    Specifying CL_TYPE_ANY matches on root objects only (i.e. the target file is explicitely not in a container). Chances slim that you would want to use CL_TYPE_ANY in a signature, because placing the malicious file in an archive will then prevent it from alerting.

    +

    Every ClamAV file type has the potential to be a container for additional files, although some are more likely than others. When a file is parsed and data in the file is identified to be scanned as a unique type, that parent file becomes a container the moment the embedded content is scanned. For a list of possible CL_TYPEs, refer to the File Types Reference.

    +
  • +
  • +

    Intermediates:CL_TYPE_*>CL_TYPE_*: Specify one or more layers of file types containing the scanned file. This is an alternative to using Container.

    +

    You may specify up to 16 layers of file types separated by ’>’ in top-down order. Note that the ’>’ separator is not needed if you only specify a single container. The last type should be the immediate container containing the malicious file. Unlike with the Container option, CL_TYPE_ANY can be used as a wildcard file type. (expr; 0.100.0)

    +

    For a list of possible CL_TYPEs, refer to the File Types Reference.

    +
  • +
  • +

    IconGroup1: Icon group name 1 from .idb signature Required engine functionality (range; 0.96)

    +
  • +
  • +

    IconGroup2: Icon group name 2 from .idb signature Required engine functionality (range; 0.96)

    +
  • +
+

Modifiers for subexpressions:

+
    +
  • +

    A=X: If the SUB-EXPRESSION A refers to a single signature then this signature must get matched exactly X times; if it refers to a (logical) block of signatures then this block must generate exactly X matches (with any of its sigs).

    +
  • +
  • +

    A=0 specifies negation (signature or block of signatures cannot be matched)

    +
  • +
  • +

    A=X,Y: If the SUB-EXPRESSION A refers to a single signature then this signature must be matched exactly X times; if it refers to a (logical) block of signatures then this block must generate X matches and at least Y different signatures must get matched.

    +
  • +
  • +

    A>X: If the SUB-EXPRESSION A refers to a single signature then this signature must get matched more than X times; if it refers to a (logical) block of signatures then this block must generate more than X matches (with any of its sigs).

    +
  • +
  • +

    A>X,Y: If the SUB-EXPRESSION A refers to a single signature then this signature must get matched more than X times; if it refers to a (logical) block of signatures then this block must generate more than X matches and at least Y different signatures must be matched.

    +
  • +
  • +

    A<X: Just like A>Z above with the change of "more" to "less".

    +

    If the SUB-EXPRESSION A refers to a single signature then this signature must get matched less than X times; if it refers to a (logical) block of signatures then this block must generate less than X matches (with any of its sigs).

    +
  • +
  • +

    A<X,Y: Similar to A>X,Y. If the SUB-EXPRESSION A refers to a single signature then this signature must get matched less than X times; if it refers to a (logical) block of signatures then this block must generate less than X matches and at least Y different signatures must be matched.

    +
  • +
+

Examples:

+
Sig1;Target:0;(0&1&2&3)&(4|1);6b6f74656b;616c61;7a6f6c77;7374656
+6616e;deadbeef
+
+Sig2;Target:0;((0|1|2)>5,2)&(3|1);6b6f74656b;616c61;7a6f6c77;737
+46566616e
+
+Sig3;Target:0;((0|1|2|3)=2)&(4|1);6b6f74656b;616c61;7a6f6c77;737
+46566616e;deadbeef
+
+Sig4;Engine:51-255,Target:1;((0|1)&(2|3))&4;EP+123:33c06834f04100
+f2aef7d14951684cf04100e8110a00;S2+78:22??232c2d252229{-15}6e6573
+(63|64)61706528;S3+50:68efa311c3b9963cb1ee8e586d32aeb9043e;f9c58
+dcf43987e4f519d629b103375;SL+550:6300680065005c0046006900
+
+

Subsignature Modifiers

+

ClamAV (clamav-0.99) supports a number of additional subsignature +modifiers for logical signatures. This is done by specifying :: +followed by a number of characters representing the desired options. +Signatures using subsignature modifiers require Engine:81-255 for +backwards-compatibility.

+
    +
  • +

    Case-Insensitive [i]

    +

    Specifying the i modifier causes ClamAV to match all alphabetic hex bytes as case-insensitive. All patterns in ClamAV are case-sensitive by default.

    +
  • +
  • +

    Wide [w]

    +

    Specifying the w causes ClamAV to match all hex bytes encoded with two bytes per character. Note this simply interweaves each character with NULL characters and does not truly support UTF-16 characters. Wildcards for ’wide’ subsignatures are not treated as wide (i.e. there can be an odd number of intermittent characters). This can be combined with a to search for patterns in both wide and ascii.

    +
  • +
  • +

    Fullword [f]

    +

    Match subsignature as a fullword (delimited by non-alphanumeric characters).

    +
  • +
  • +

    Ascii [a]

    +

    Match subsignature as ascii characters. This can be combined with w to search for patterns in both ascii and wide.

    +
  • +
+

Examples:

+
    +
  • Match 'AAAA'(nocase) and 'BBBBBB'(nocase)
  • +
+
clamav-nocase-A;Engine:81-255,Target:0;0&1;41414141::i;424242424242::i
+
+
    +
  • Match 'AAA' and 'hello'(fullword)
  • +
+
clamav-fullword-A;Engine:81-255,Target:0;0&1;414141;68656c6c6f::f
+
+
    +
  • Match 'AAA' and 'hello'(fullword nocase)
  • +
+
clamav-fullword-B;Engine:81-255,Target:0;0&1;414141;68656c6c6f::fi
+
+
    +
  • Match 'AAA' and 'hello'(wide ascii)
  • +
+
clamav-wide-B2;Engine:81-255,Target:0;0&1;414141;68656c6c6f::wa
+
+
    +
  • Match 'AAA' and 'hello'(nocase wide fullword ascii)
  • +
+
clamav-wide-C0;Engine:81-255,Target:0;0&1;414141;68656c6c6f::iwfa
+
+

Special Subsignature Types

+

Macro subsignatures

+

Introduced in ClamAV 0.96

+

Format: ${min-max}MACROID$

+

Macro subsignatures are used to combine a number of existing extended +signatures (.ndb) into a on-the-fly generated alternate string logical +signature (.ldb). Signatures using macro subsignatures require +Engine:51-255 for backwards-compatibility.

+

Example:

+
test.ldb:
+    TestMacro;Engine:51-255,Target:0;0&1;616161;${6-7}12$
+
+test.ndb:
+    D1:0:$12:626262
+    D2:0:$12:636363
+    D3:0:$30:626264
+
+

The example logical signature TestMacro is functionally equivalent +to:

+
TestMacro;Engine:51-255,Target:0;0;616161{3-4}(626262|636363)
+
+
    +
  • +

    MACROID points to a group of signatures; there can be at most 32 macro groups.

    +
      +
    • In the example, MACROID is 12 and both D1 and D2 are members of macro group 12. D3 is a member of separate macro group 30.
    • +
    +
  • +
  • +

    {min-max} specifies the offset range at which one of the group signatures should match; the offset range is relative to the starting offset of the preceding subsignature. This means a macro subsignature cannot be the first subsignature.

    +
      +
    • In the example, {min-max} is {6-7} and it is relative to the start of a 616161 match.
    • +
    +
  • +
  • +

    For more information and examples please see https://bugzilla.clamav.net/show_bug.cgi?id=164.

    +
  • +
+

Byte Compare Subsignatures

+

Introduced in ClamAV 0.101

+

Format: subsigid_trigger(offset#byte_options#comparisons)

+

Byte compare subsignatures can be used to evaluate a numeric value at a given offset from the start of another (matched) subsignature within the same logical signature. These are executed after all other subsignatures within the logical subsignature are fired, with the exception of PCRE subsignatures. They can evaluate offsets only from a single referenced subsignature, and that subsignature must give a valid match for the evaluation to occur.

+
    +
  • +

    subsigid_trigger is a required field and may refer to any single non-PCRE, non-Byte Compare subsignature within the lsig. The byte compare subsig will evaluate if subsigid_trigger matches. Triggering on multiple subsigs or logic based triggering is not currently supported.

    +
  • +
  • +

    offset is a required field that consists of an offset_modifier and a numeric offset (hex or decimal offsets are okay).

    +
      +
    • +

      offset_modifier can be either >> or << where the former denotes a positive offset and the latter denotes a negative offset. The offset is calculated from the start of subsigid_trigger, which allows for byte extraction before the specified match, after the match, and within the match itself.

      +
    • +
    • +

      offset must be a positive hex or decimal value. This will be the number of bytes from the start of the referenced subsigid_trigger match within the file buffer to begin the comparison.

      +
    • +
    +
  • +
  • +

    byte_options are used to specify the numeric type and endianess of the extracted byte sequence in that order as well as the number of bytes to be read. By default ClamAV will attempt to matchup up to the number of byte specified, unless the e (exact) option is specified or the numeric type is b (binary). This field follows the form [h|d|a|i][l|b][e]num_bytes

    +
      +
    • +

      h|d|a|i where h specifies the byte sequence will be in hex, d decimal, a automatic detection of hex or decimal at runtime, and i signifies raw binary data.

      +
    • +
    • +

      l|b where l specifies the byte sequence will be in little endian order and b big endian. If decimal d is specified, big-endian is implied and using l will result in a malformed database error.

      +
    • +
    • +

      e specifies that ClamAV will only evaluate the comparison if it can extract the exact number of bytes specified. This option is implicitly declared when using the i flag.

      +
    • +
    • +

      num_bytes specifies the number of bytes to extract. This can be a hex or decimal value. If i is specified only 1, 2, 4, and 8 are valid options.

      +
    • +
    +
  • +
  • +

    comparisons are a required field which denotes how to evaluate the extracted byte sequence. Each Byte Compare signature can have one or two comparison_sets separated by a comma. Each comparison_set consists of a Comparison_symbol and a Comparison_value and takes the form Comparison_symbolComparison_value. Thus, comparisons takes the form comparison_set[,comparison_set]

    +
      +
    • +

      Comparison_symbol denotes the type of comparison to be done. The supported comparison symbols are <, >, =.

      +
    • +
    • +

      Comparison_value is a required field which must be a numeric hex or decimal value. If all other conditions are met, the byte compare subsig will evalutate the extracted byte sequence against this number based on the provided comparison_symbol.

      +
    • +
    +
  • +
+

PCRE subsignatures

+

Introduced in ClamAV 0.99

+

Format: Trigger/PCRE/[Flags]

+

PCRE subsignatures are used within a logical signature (.ldb) to specify regex matches that execute once triggered by a conditional based on preceding subsignatures. Signatures using PCRE subsignatures require Engine:81-255 for backwards-compatibility.

+
    +
  • +

    Trigger is a required field that is a valid LogicalExpression and may refer to any subsignatures that precede this subsignature. Triggers cannot be self-referential and cannot refer to subsequent subsignatures.

    +
  • +
  • +

    PCRE is the expression representing the regex to execute. PCRE must be delimited by ’/’ and usage of ’/’ within the expression need to be escaped. For backward compatibility, ’;’ within the expression must be expressed as ’\x3B’. PCRE cannot be empty and (?UTF*) control sequence is not allowed. If debug is specified, named capture groups are displayed in a post-execution report.

    +
  • +
  • +

    Flags are a series of characters which affect the compilation and execution of PCRE within the PCRE compiler and the ClamAV engine. This field is optional.

    +
      +
    • +

      g [CLAMAV_GLOBAL] specifies to search for ALL matches of PCRE (default is to search for first match). NOTE: INCREASES the time needed to run the PCRE.

      +
    • +
    • +

      r [CLAMAV_ROLLING] specifies to use the given offset as the starting location to search for a match as opposed to the only location; applies to subsigs without maxshifts. By default, in order to facilatate normal ClamAV offset behavior, PCREs are auto-anchored (only attempt match on first offset); using the rolling option disables the auto-anchoring.

      +
    • +
    • +

      e [CLAMAV_ENCOMPASS] specifies to CONFINE matching between the specified offset and maxshift; applies only when maxshift is specified.

      +
      +

      Note: DECREASES time needed to run the PCRE.

      +
      +
    • +
    • +

      i [PCRE_CASELESS]

      +
    • +
    • +

      s [PCRE_DOTALL]

      +
    • +
    • +

      m [PCRE_MULTILINE]

      +
    • +
    • +

      x [PCRE_EXTENDED]

      +
    • +
    • +

      A [PCRE_ANCHORED]

      +
    • +
    • +

      E [PCRE_DOLLAR_ENODNLY]

      +
    • +
    • +

      U [PCRE_UNGREEDY]

      +
    • +
    +
  • +
+

Examples:

+
Find.All.ClamAV;Engine:81-255,Target:0;1;6265676c6164697427736e6f7462797465636f6465;0/clamav/g
+
+Find.ClamAV.OnlyAt.299;Engine:81-255,Target:0;2;7374756c747a67657473;7063726572656765786c6f6c;299:0&1/clamav/
+
+Find.ClamAV.StartAt.300;Engine:81-255,Target:0;3;616c61696e;62756731393238;636c6f736564;300:0&1&2/clamav/r
+
+Find.All.Encompassed.ClamAV;Engine:81-255,Target:0;3;7768796172656e2774;796f757573696e67;79617261;200,300:0&1&2/clamav/ge
+
+Named.CapGroup.Pcre;Engine:81-255,Target:0;3;636f75727479617264;616c62756d;74657272696572;50:0&1&2/variable=(?&lt;nilshell&gt;.{16})end/gr
+
+Firefox.TreeRange.UseAfterFree;Engine:81-255,Target:0,Engine:81-255;0&1&2;2e766965772e73656c656374696f6e;2e696e76616c696461746553656c656374696f6e;0&1/\x2Eview\x2Eselection.*?\x2Etree\s*\x3D\s*null.*?\x2Einvalidate/smi
+
+Firefox.IDB.UseAfterFree;Engine:81-255,Target:0;0&1;4944424b657952616e6765;0/^\x2e(only|lowerBound|upperBound|bound)\x28.*?\x29.*?\x2e(lower|upper|lowerOpen|upperOpen)/smi
+
+Firefox.boundElements;Engine:81-255,Target:0;0&1&2;6576656e742e6
+26f756e64456c656d656e7473;77696e646f772e636c6f7365;0&1/on(load|click)\s*=\s*\x22?window\.close\s*\x28/si
+
+

Signatures for Version Information (VI) metadata in PE files

+

Starting with ClamAV 0.96 it is possible to easily match certain information built into PE files (executables and dynamic link libraries). Whenever you lookup the properties of a PE executable file in windows, you are presented with a bunch of details about the file itself.

+

These info are stored in a special area of the file resources which goes under the name of VS_VERSION_INFORMATION (or versioninfo for short). It is divided into 2 parts. The first part (which is rather uninteresting) is really a bunch of numbers and flags indicating the product and file version. It was originally intended for use with installers which, after parsing it, should be able to determine whether a certain executable or library are to be upgraded/overwritten or are already up to date. Suffice to say, this approach never really worked and is generally never used.

+

The second block is much more interesting: it is a simple list of key/value strings, intended for user information and completely ignored by the OS. For example, if you look at ping.exe you can see the company being "Microsoft Corporation", the description "TCP/IP Ping command", the internal name "ping.exe" and so on... Depending on the OS version, some keys may be given peculiar visibility in the file properties dialog, however they are internally all the same.

+

To match a versioninfo key/value pair, the special file offset anchor VI was introduced. This is similar to the other anchors (like EP and SL) except that, instead of matching the hex pattern against a single offset, it checks it against each and every key/value pair in the file. The VI token doesn’t need nor accept a +/- offset like e.g. EP+1. As for the hex signature itself, it’s just the utf16 dump of the key and value. Only the ?? and (aa|bb) wildcards are allowed in the signature. Usually, you don’t need to bother figuring it out: each key/value pair together with the corresponding VI-based signature is printed by clamscan when the --debug option is given.

+

For example clamscan --debug freecell.exe produces:

+
[...]
+Recognized MS-EXE/DLL file
+in cli_peheader
+versioninfo_cb: type: 10, name: 1, lang: 410, rva: 9608
+cli_peheader: parsing version info @ rva 9608 (1/1)
+VersionInfo (d2de): 'CompanyName'='Microsoft Corporation' -
+VI:43006f006d00700061006e0079004e0061006d006500000000004d006900
+630072006f0073006f0066007400200043006f00720070006f0072006100740
+069006f006e000000
+VersionInfo (d32a): 'FileDescription'='Entertainment Pack
+FreeCell Game' - VI:460069006c006500440065007300630072006900700
+0740069006f006e000000000045006e007400650072007400610069006e006d
+0065006e00740020005000610063006b0020004600720065006500430065006
+c006c002000470061006d0065000000
+VersionInfo (d396): 'FileVersion'='5.1.2600.0 (xpclient.010817
+-1148)' - VI:460069006c006500560065007200730069006f006e00000000
+0035002e0031002e0032003600300030002e003000200028007800700063006
+c00690065006e0074002e003000310030003800310037002d00310031003400
+380029000000
+VersionInfo (d3fa): 'InternalName'='freecell' - VI:49006e007400
+650072006e0061006c004e0061006d006500000066007200650065006300650
+06c006c000000
+VersionInfo (d4ba): 'OriginalFilename'='freecell' - VI:4f007200
+6900670069006e0061006c00460069006c0065006e0061006d0065000000660
+0720065006500630065006c006c000000
+VersionInfo (d4f6): 'ProductName'='Sistema operativo Microsoft
+Windows' - VI:500072006f0064007500630074004e0061006d00650000000
+000530069007300740065006d00610020006f00700065007200610074006900
+76006f0020004d006900630072006f0073006f0066007400ae0020005700690
+06e0064006f0077007300ae000000
+VersionInfo (d562): 'ProductVersion'='5.1.2600.0' - VI:50007200
+6f006400750063007400560065007200730069006f006e00000035002e00310
+02e0032003600300030002e0030000000
+[...]
+
+

Although VI-based signatures are intended for use in logical signatures you can test them using ordinary .ndb files. For example:

+
my_test_vi_sig:1:VI:paste_your_hex_sig_here
+
+

Final note. If you want to decode a VI-based signature into a human readable form you can use:

+
echo hex_string | xxd -r -p | strings -el
+
+

For example:

+
echo 460069006c0065004400650073006300720069007000740069006f006e000000000045006e007400650072007400610069006e006d0065006e00740020005000610063006b0020004600720065006500430065006c006c00200047006100
+6d0065000000 | xxd -r -p | strings -el
+FileDescription
+Entertainment Pack FreeCell Game
+
+

Icon Signatures for PE files

+

While Icon Signatures are stored in a .idb file, they are a feature of Logical Signatures.

+

ClamAV 0.96 includes an approximate/fuzzy icon matcher to help detecting malicious executables disguising themselves as innocent looking image files, office documents and the like.

+

Icon matching is only triggered by Logical Signatures (.ldb) using the special attribute tokens IconGroup1 or IconGroup2. These identify two (optional) groups of icons defined in a .idb database file. The format of the .idb file is:

+
ICONNAME:GROUP1:GROUP2:ICON_HASH
+
+

where:

+
    +
  • +

    ICON_NAME is a unique string identifier for a specific icon,

    +
  • +
  • +

    GROUP1 is a string identifier for the first group of icons (IconGroup1)

    +
  • +
  • +

    GROUP2 is a string identifier for the second group of icons (IconGroup2),

    +
  • +
  • +

    ICON_HASH is a fuzzy hash of the icon image

    +
  • +
+

The ICON_HASH field can be obtained from the debug output of libclamav. For example:

+
LibClamAV debug: ICO SIGNATURE:
+ICON_NAME:GROUP1:GROUP2:18e2e0304ce60a0cc3a09053a30000414100057e000afe0000e 80006e510078b0a08910d11ad04105e0811510f084e01040c080a1d0b0021000a39002a41
+
+

Extended signature format

+

The extended signature format is ClamAV's most basic type of body-based signature since the deprecation of the original .db database format.

+

Extended sigantures allow for specification of additional information beyond just hexidecimal content such as a file "target type", virus offset, or engine functionality level (FLEVEL), making the detection more reliable.

+

The format is:

+
MalwareName:TargetType:Offset:HexSignature[:min_flevel:[max_flevel]]
+
+

MalwareName: The virus name. Should conform to the standards defined here.

+

TargetType: A number specifying the type of the target file: Target Types

+

Offset: An asterisk or a decimal number n possibly combined with a special modifier:

+
    +
  • * = any
  • +
  • n = absolute offset
  • +
  • EOF-n = end of file minus n bytes
  • +
+

Signatures for PE, ELF and Mach-O files additionally support:

+
    +
  • EP+n = entry point plus n bytes (EP+0 for EP)
  • +
  • EP-n = entry point minus n bytes
  • +
  • Sx+n = start of section x’s (counted from 0) data plus n bytes
  • +
  • SEx = entire section x (offset must lie within section boundaries)
  • +
  • SL+n = start of last section plus n bytes
  • +
+

All the above offsets except * can be turned into floating offsets and represented as Offset,MaxShift where MaxShift is an unsigned integer. A floating offset will match every offset between Offset and Offset+MaxShift, eg. 10,5 will match all offsets from 10 to 15 and EP+n,y will match all offsets from EP+n to EP+n+y. Versions of ClamAV older than 0.91 will silently ignore the MaxShift extension and only use Offset. Optional MinFL and MaxFL parameters can restrict the signature to specific engine releases. All signatures in the extended format must be placed inside *.ndb files.

+

HexSignature: The body-based content matching format.

+

min_flevel: (optional) The minimum ClamAV engine that the file type signature works with. See the FLEVEL reference for details. To be used in the event that file type support has been recently added.

+

max_flevel: (optional, requires min_flevel) The maximum ClamAV engine that the file type signature works with. To be used in the event that file type support has been recently removed.

+

Using YARA rules in ClamAV

+

ClamAV can process YARA rules. ClamAV virus database file names ending with .yar or .yara are parsed as YARA rule files. The link to the YARA rule grammar documentation may be found at https://virustotal.github.io/yara/. There are currently a few limitations on using YARA rules within ClamAV:

+
    +
  • +

    YARA modules are not yet supported by ClamAV. This includes the “import” keyword and any YARA module-specific keywords.

    +
  • +
  • +

    Global rules (global keyword) are not supported by ClamAV.

    +
  • +
  • +

    External variables(contains and matches keywords) are not supported.

    +
  • +
  • +

    YARA rules pre-compiled with the yarac command are not supported.

    +
  • +
  • +

    As in the ClamAV logical and extended signature formats, YARA strings and segments of strings separated by wild cards must represent at least two octets of data.

    +
  • +
  • +

    There is a maximum of 64 strings per YARA rule.

    +
  • +
  • +

    YARA rules in ClamAV must contain at least one literal, hexadecimal, or regular expression string.

    +
  • +
+

In addition, there are a few more ClamAV processing modes that may affect the outcome of YARA rules.

+
    +
  • +

    File decomposition and decompression - Since ClamAV uses file decomposition and decompression to find viruses within de-archived and uncompressed inner files, YARA rules executed by ClamAV will match against these files as well.

    +
  • +
  • +

    Normalization - By default, ClamAV normalizes HTML, JavaScript, and ASCII text files. YARA rules in ClamAV will match against the normalized result. The effects of normalization of these file types may be captured using clamscan --leave-temps --tempdir=mytempdir. YARA rules may then be written using the normalized file(s) found in mytempdir. Alternatively, starting with ClamAV 0.100.0, clamscan --normalize=no will prevent normalization and only scan the raw file. To obtain similar behavior prior to 0.99.2, use clamscan --scan-html=no. The corresponding parameters for clamd.conf are Normalize and ScanHTML.

    +
  • +
  • +

    YARA conditions driven by string matches - All YARA conditions are driven by string matches in ClamAV. This saves from executing every YARA rule on every file. Any YARA condition may be augmented with a string match clause which is always true, such as:

    +
  • +
+
rule CheckFileSize
+{
+  strings:
+    $abc = "abc"
+  condition:
+    ($abc or not $abc) and filesize < 200KB
+}
+
+

This will ensure that the YARA condition always performs the desired action (checking the file size in this example),

+

Phishing Signatures

+

Table of Contents

+ +

Database file format

+

PDB format

+

This file contains urls/hosts that are target of phishing attempts. It contains lines in the following format:

+
R[Filter]:RealURL:DisplayedURL[:FuncLevelSpec]
+H[Filter]:DisplayedHostname[:FuncLevelSpec]
+
+
    +
  • +

    R

    +

    Regular expression, for the concatenated URL. The last 3 characters of the regular expression cannot regex special characters and much be an exact match.

    +
  • +
  • +

    H

    +

    Matches the DisplayedHostname as a simple pattern (literally, no regular expression).

    +
      +
    • +

      The pattern can match either the full hostname.

      +
    • +
    • +

      Or a subdomain of the specified hostname.

      +
    • +
    • +

      To avoid false matches in case of subdomain matches, the engine checks that there is a dot(.) or a space( ) before the matched portion.

      +
    • +
    +
  • +
  • +

    Filter

    +

    Is ignored for R and H for compatibility reasons.

    +
  • +
  • +

    RealURL

    +

    Is the URL the user is sent to, example: href attribute of an html anchor (<a> tag).

    +
  • +
  • +

    DisplayedURL

    +

    Is the URL description displayed to the user, where its claimed they are sent, example: contents of an html anchor (<a> tag).

    +
  • +
  • +

    DisplayedHostname

    +

    Is the hostname portion of the DisplayedURL.

    +
  • +
  • +

    FuncLevelSpec

    +

    An (optional) functionality level, 2 formats are possible:

    +
      +
    • +

      minlevel all engines having functionality level >= minlevel will load this line.

      +
    • +
    • +

      minlevel-maxlevel engines with functionality level >= minlevel, and < maxlevel will load this line.

      +
    • +
    +
  • +
+

GDB format

+

This file contains URL hashes in the following format:

+
S:P:HostPrefix[:FuncLevelSpec]
+S:F:Sha256hash[:FuncLevelSpec]
+S1:P:HostPrefix[:FuncLevelSpec]
+S1:F:Sha256hash[:FuncLevelSpec]
+S2:P:HostPrefix[:FuncLevelSpec]
+S2:F:Sha256hash[:FuncLevelSpec]
+S:W:Sha256hash[:FuncLevelSpec]
+
+
    +
  • +

    S:

    +

    These are hashes for Google Safe Browsing - malware sites, and should not be used for other purposes.

    +
  • +
  • +

    S2:

    +

    These are hashes for Google Safe Browsing - phishing sites, and should not be used for other purposes.

    +
  • +
  • +

    S1:

    +

    Hashes for blocking phishing sites. Virus name: Phishing.URL.Blocked.

    +
  • +
  • +

    S:W:

    +

    Locally allowed hashes.

    +
  • +
  • +

    HostPrefix

    +

    4-byte prefix of the sha256 hash of the last 2 or 3 components of the hostname. If prefix doesn’t match, no further lookups are performed.

    +
  • +
  • +

    Sha256hash

    +

    sha256 hash of the canonicalized URL, or a sha256 hash of its prefix/suffix according to the Google Safe Browsing “Performing Lookups” rules. There should be a corresponding :P:HostkeyPrefix entry for the hash to be taken into consideration.

    +
  • +
+

To see which hash/URL matched, look at the clamscan --debug output, and look for the following strings: Looking up hash, prefix matched, and Hash matched. To ignore .gdb entries, create a local.gdb file, and adding a line S:W:<HASH>.

+

WDB format

+

This file contains url pairs for links that may look suspicious but are safe and should be allowed. It contains lines in the following format:

+
X:RealURL:DisplayedURL[:FuncLevelSpec]
+M:RealHostname:DisplayedHostname[:FuncLevelSpec]
+
+
    +
  • +

    X

    +

    Regular expression, for the entire URL, not just the hostname.

    +
      +
    • +

      The regular expression is by default anchored to start-of-line and end-of-line, as if you have used ^RegularExpression$

      +
    • +
    • +

      A trailing / is automatically added both to the regex, and the input string to avoid false matches.

      +
    • +
    • +

      The regular expression matches the concatenation of the RealURL, a colon(:), and the DisplayedURL as a single string. It doesn’t separately match RealURL and DisplayedURL!

      +
    • +
    • +

      The last 3 characters of the regular expression cannot regex special characters and much be an exact match.

      +
    • +
    +
  • +
  • +

    M

    +

    Matches hostname, or subdomain of it, see notes for H above.

    +
  • +
+

Hints

+
    +
  • +

    Empty lines are ignored

    +
  • +
  • +

    The colons are mandatory

    +
  • +
  • +

    Don’t leave extra spaces on the end of a line!

    +
  • +
  • +

    If any of the lines don’t conform to this format, ClamAV will abort with a Malformed Database Error

    +
  • +
  • +

    See section Extraction-of-RealURL for more details on RealURL/DisplayedURL

    +
  • +
+

Examples of PDB signatures

+

To check for phishing mails that target amazon.com, or subdomains of amazon.com:

+
H:amazon.com
+
+

To do the same, but for amazon.co.uk:

+
H:amazon.co.uk
+
+

You can limit the signatures to certain engine versions. For example...

+
    +
  1. +

    Restrict so that engine versions 20 through 30 can load it, but not 31+:

    +
    H:amazon.co.uk:20-30
    +
    +
  2. +
  3. +

    Restrict so that engine versions >= 20 can load it:

    +
    H:amazon.co.uk:20-
    +
    +
  4. +
  5. +

    Restrict so that engine versions <= 20 can load it:

    +
    H:amazon.co.uk:0-20
    +
    +
  6. +
+

In a real situation, you’d probably use the second form. A situation like that would be if you are using a feature of the signatures not available in earlier versions, or if earlier versions have bugs with your signature. Its neither case here, the above examples are for illustrative purposes only.

+

Examples of WDB signatures

+

To allow Amazon’s country specific domains and amazon.com, to mix domain names in DisplayedURL, and RealURL:

+
X:.+\.amazon\.(at|ca|co\.uk|co\.jp|de|fr)([/?].*)?:.+\.amazon\.com([/?].*)?:17-
+
+

Explanation of this signature:

+
    +
  • +

    X:

    +

    this is a regular expression

    +
  • +
  • +

    :17-

    +

    load signature only for engines with functionality level >= 17

    +
  • +
+

The regular expression is the following (X:, :17- stripped, and a / appended)

+
.+\.amazon\.(at|ca|co\.uk|co\.jp|de|fr)([/?].*)?:.+\.amazon\.com([/?].*)?/
+
+

Explanation of this regular expression (note that it is a single regular expression, and not 2 regular expressions splitted at the :).

+
    +
  • +

    .+

    +

    any subdomain of

    +
  • +
  • +

    \.amazon\.

    +

    domain we are allowing (RealURL part)

    +
  • +
  • +

    (at|ca|co\.uk|co\.jp|de|fr)

    +

    country-domains: at, ca, co.uk, co.jp, de, fr

    +
  • +
  • +

    ([/?].*)?

    +

    recomended way to end the real-url, this protects against embedded URLs (evilurl.example.com/amazon.co.uk/)

    +
  • +
  • +

    :

    +

    RealURL and DisplayedURL are concatenated via a :, so match a literal : here

    +
  • +
  • +

    .+

    +

    any subdomain of

    +
  • +
  • +

    \.amazon\.com

    +

    allowed DisplayedURL

    +
  • +
  • +

    ([/?].*)?

    +

    recommended way to end displayed url part, to protect against embedded URLs

    +
  • +
  • +

    /

    +

    automatically added to further protect against embedded URLs

    +
  • +
+

When you add an entry, make sure you check that both domains are owned by the same entity. This signature allows links claiming to point to amazon.com (DisplayedURL), when in fact they really go to a country-specific domain of amazon (RealURL).

+

Example for how the URL extractor works

+

Consider the following HTML file:

+
<html>
+<a href="http://1.realurl.example.com/">
+  1.displayedurl.example.com
+</a>
+<a href="http://2.realurl.example.com">
+  2 d<b>i<p>splayedurl.e</b>xa<i>mple.com
+</a>
+<a href="http://3.realurl.example.com">
+  3.nested.example.com
+  <a href="http://4.realurl.example.com">
+    4.displayedurl.example.com
+  </a>
+</a>
+<form action="http://5.realurl.example.com">
+  sometext
+  <img src="http://5.displayedurl.example.com/img0.gif"/>
+  <a href="http://5.form.nested.displayedurl.example.com">
+    5.form.nested.link-displayedurl.example.com
+  </a>
+</form>
+<a href="http://6.realurl.example.com">
+  6.displ
+  <img src="6.displayedurl.example.com/img1.gif"/>
+  ayedurl.example.com
+</a>
+<a href="http://7.realurl.example.com">
+  <iframe src="http://7.displayedurl.example.com">
+</a>
+
+

The phishing engine extract the following +RealURL/DisplayedURL pairs from it:

+
http://1.realurl.example.com/
+1.displayedurl.example.com
+
+http://2.realurl.example.com
+2displayedurl.example.com
+
+http://3.realurl.example.com
+3.nested.example.com
+
+http://4.realurl.example.com
+4.displayedurl.example.com
+
+http://5.realurl.example.com
+http://5.displayedurl.example.com/img0.gif
+
+http://5.realurl.example.com
+http://5.form.nested.displayedurl.example.com
+
+http://5.form.nested.displayedurl.example.com
+5.form.nested.link-displayedurl.example.com
+
+http://6.realurl.example.com
+6.displayedurl.example.com
+
+http://6.realurl.example.com
+6.displayedurl.example.com/img1.gif
+
+

How matching works

+

RealURL, DisplayedURL concatenation

+

The phishing detection module processes pairs of RealURL/DisplayedURL. Matching against daily.wdb is done as follows: the RealURL is concatenated with a :, and with the DisplayedURL, then that line is matched against the lines in daily.wdb/daily.pdb

+

So if you have this line in daily.wdb:

+
M:www.google.ro:www.google.com
+
+

This href: <a href='http://www.google.ro'>www.google.com</a> then it will be allowed, but: <a href='http://images.google.com'>www.google.com</a> will not.

+

What happens when a match is found

+

In the case of the allow list, a match means that the RealURL/DisplayedURL combination is considered clean, and no further checks are performed on it.

+

In the case of the domain list, a match means that the RealURL/DisplayedURL is going to be checked for phishing attempts.

+

Furthermore you can restrict what checks are to be performed by specifying the 3-digit hexnumber.

+

Extraction of RealURL, DisplayedURL from HTML tags

+

The html parser extracts pairs of RealURL/DisplayedURL based on the following rules.

+

After URLs have been extracted, they are normalized, and cut after the hostname. http://test.example.com/path/somecgi?queryparameters becomes http://test.example.com/

+
    +
  • +

    a

    +

    (anchor) the href is the RealURL, its contents is the DisplayedURL

    +
      +
    • +

      contents

      +

      is the tag-stripped contents of the <a> tags, so for example <b> tags are stripped (but not their contents)

      +
    • +
    +

    nesting another <a> tag withing an <a> tag (besides being invalid html) is treated as a </a><a...

    +
  • +
  • +

    form

    +

    the action attribute is the RealURL, and a nested <a> tag is the DisplayedURL.

    +
  • +
  • +

    img/area

    +

    if nested within an <a> tag, the RealURL is the href of the a tag, and the src/dynsrc/area is the DisplayedURL of the img

    +

    if nested withing a form tag, then the action attribute of the form tag is the RealURL.

    +
  • +
  • +

    iframe

    +

    if nested withing an <a> tag the src attribute is the DisplayedURL, and the href of its parent a tag is the RealURL.

    +

    if nested withing a form tag, then the action attribute of the form tag is the RealURL.

    +
  • +
+

Example

+

Consider this html file:

+
<a href=”evilurl”>www.paypal.com</a>
+<a href=”evilurl2” title=”www.ebay.com”>click here to sign in</a>
+<form action=”evilurl_form”>
+Please sign in to <a href=”cgi.ebay.com”>Ebay</a>using this form
+<input type=’text’ name=’username’>Username</input>
+....
+</form>
+<a href=”evilurl”><img src=”images.paypal.com/secure.jpg”></a>
+
+

The resulting RealURL/DisplayedURL pairs will be (note that one tag can generate multiple pairs):

+
    +
  • +

    evilurl / www.paypal.com

    +
  • +
  • +

    evilurl2 / click here to sign in

    +
  • +
  • +

    evilurl2 / www.ebay.com

    +
  • +
  • +

    evilurl_form / cgi.ebay.com

    +
  • +
  • +

    cgi.ebay.com / Ebay

    +
  • +
  • +

    evilurl / image.paypal.com/secure.jpg

    +
  • +
+

Simple patterns

+

Simple patterns are matched literally, i.e. if you say:

+
www.google.com
+
+

it is going to match www.google.com, and only that. The . (dot) character has no special meaning (see the section on regexes [sec:Regular-expressions] for how the .(dot) character behaves there)

+

Regular expressions

+

POSIX regular expressions are supported, and you can consider that internally it is wrapped by ^, and $. In other words, this means that the regular expression has to match the entire concatenated (see section RealURL,-DisplayedURL-concatenation for details on concatenation) url.

+

It is recomended that you read section Introduction-to-regular to learn how to write regular expressions, and then come back and read this for hints.

+

Be advised that clamav contains an internal, very basic regex matcher to reduce the load on the regex matching core. Thus it is recomended that you avoid using regex syntax not supported by it at the very beginning of regexes (at least the first few characters).

+

Currently the clamav regex matcher supports:

+
    +
  • +

    . (dot) character

    +
  • +
  • +

    \ (escaping special characters)

    +
  • +
  • +

    | (pipe) alternatives

    +
  • +
  • +

    [] (character classes)

    +
  • +
  • +

    () (parenthesis for grouping, but no group extraction is performed)

    +
  • +
  • +

    other non-special characters

    +
  • +
+

Thus the following are not supported:

+
    +
  • +

    + repetition

    +
  • +
  • +

    * repetition

    +
  • +
  • +

    {} repetition

    +
  • +
  • +

    backreferences

    +
  • +
  • +

    lookaround

    +
  • +
  • +

    other “advanced” features not listed in the supported list ;)

    +
  • +
+

This however shouldn’t discourage you from using the “not directly supported features “, because if the internal engine encounters unsupported syntax, it passes it on to the POSIX regex core (beginning from the first unsupported token, everything before that is still processed by the internal matcher). An example might make this more clear:

+
*www\(\backslash\).google\(\backslash\).(com|ro|it) (\[a-zA-Z\])+\(\backslash\).google\(\backslash\).(com|ro|it)*
+
+

Everything till (\[a-zA-Z\])+ is processed internally, that parenthesis (and everything beyond) is processed by the posix core.

+

Examples of url pairs that match:

+
    +
  • +

    www.google.ro images.google.ro

    +
  • +
  • +

    www.google.com images.google.ro

    +
  • +
+

Example of url pairs that don’t match:

+
    +
  • +

    www.google.ro images1.google.ro

    +
  • +
  • +

    images.google.com image.google.com

    +
  • +
+

Flags

+

Flags are a binary OR of the following numbers:

+ + + + + + + + + + + +
FlagValue
HOST_SUFFICIENT1
DOMAIN_SUFFICIENT2
DO_REVERSE_LOOKUP4
CHECK_REDIR8
CHECK_SSL16
CHECK_CLOAKING32
CLEANUP_URL64
CHECK_DOMAIN_REVERSE128
CHECK_IMG_URL256
DOMAINLIST_REQUIRED512
+

The names of the constants are self-explanatory.

+

These constants are defined in libclamav/phishcheck.h, you can check there for the latest flags.

+

There is a default set of flags that are enabled, these are currently:

+
( CLEANUP_URL | CHECK_SSL | CHECK_CLOAKING | CHECK_IMG_URL )
+
+

ssl checking is performed only for a tags currently.

+

You must decide for each line in the domain list if you want to filter any flags (that is you don’t want certain checks to be done), and then calculate the binary OR of those constants, and then convert it into a 3-digit hexnumber. For example you devide that domain_sufficient shouldn’t be used for ebay.com, and you don’t want to check images either, so you come up with this flag number: 2|256 => 258(decimal) => 102(hexadecimal)

+

So you add this line to daily.wdb:

+
R102 www.ebay.com .+
+
+

Introduction to regular expressions

+

Recomended reading:

+ +

Special characters

+
    +
  • +

    [

    +

    the opening square bracket - it marks the beginning of a character class, see sectionCharacter-classes

    +
  • +
  • +

    \

    +

    the backslash - escapes special characters, see section Escaping

    +
  • +
  • +

    ^

    +

    the caret - matches the beginning of a line (not needed in clamav regexes, this is implied)

    +
  • +
  • +

    $

    +

    the dollar sign - matches the end of a line (not needed in clamav regexes, this is implied)

    +
  • +
  • +

    .

    +

    the period or dot - matches any character

    +
  • +
  • +

    |

    +

    the vertical bar or pipe symbol - matches either of the token on its left and right side, see Alternation

    +
  • +
  • +

    ?

    +

    the question mark - matches optionally the left-side token, see Optional-matching, and Repetition

    +
  • +
  • +

    *

    +

    the asterisk or star - matches 0 or more occurences of the left-side token, see Optional-matching, and Repetition

    +
  • +
  • +

    +

    +

    the plus sign - matches 1 or more occurences of the left-side token, see Optional-matching, and Repetition

    +
  • +
  • +

    (

    +

    the opening round bracket - marks beginning of a group, see section Groups

    +
  • +
  • +

    )

    +

    the closing round bracket - marks end of a group, see sectionGroups

    +
  • +
+

Character classes

+

Escaping

+

Escaping has two purposes:

+
    +
  • +

    It allows you to actually match the special characters themselves, for example to match the literal +, you would write \+

    +
  • +
  • +

    It also allows you to match non-printable characters, such as the tab (\t) and newline (\n)

    +
  • +
+

However since non-printable characters are not valid inside an url, you won’t have a reason to use them.

+

Alternation

+

Optional matching, and repetition

+

Groups

+

Groups are usually used together with repetition, or alternation. For example: (com|it)+ means: match 1 or more repetitions of com or it, that is it matches: com, it, comcom, comcomcom, comit, itit, ititcom,... you get the idea.

+

Groups can also be used to extract substring, but this is not supported by the ClamAV, and not needed either in this case.

+

How to create database files

+

How to create and maintain the allow list (daily.wdb)

+

If the phishing code claims that a certain mail is phishing, but it's not, you have 2 choices:

+
    +
  • +

    Examine your rules daily.pdb, and fix them if necessary (see: How to create database files)

    +
  • +
  • +

    Add it to the allow list (discussed here)

    +
  • +
+

Lets assume you are having problems because of links like this in a mail:

+
<a href=''http://69.0.241.57/bCentral/L.asp?L=XXXXXXXX''>`
+  http://www.bcentral.it/`
+</a>`
+
+

After investigating those sites further, you decide they are no threat, and create a line like this in daily.wdb:

+
R http://www\(\backslash\).bcentral\(\backslash\).it/.+
+http://69\(\backslash\).0\(\backslash\).241\(\backslash\).57/bCentral/L\(\backslash\).asp?L=.+
+
+
+

Note: urls like the above can be used to track unique mail recipients, and thus know if somebody actually reads mails (so they can send more spam). However since this site required no authentication information, it is safe from a phishing point of view.

+
+

How to create and maintain the domain list (daily.pdb)

+

When not using --phish-scan-alldomains (production environments for example), you need to decide which urls you are going to check.

+

Although at a first glance it might seem a good idea to check everything, it would produce false positives. Particularly newsletters, ads, etc. are likely to use URLs that look like phishing attempts.

+

Lets assume that you’ve recently seen many phishing attempts claiming they come from Paypal. Thus you need to add paypal to daily.pdb:

+
R .+ .+\(\backslash\).paypal\(\backslash\).com
+
+

The above line will block (detect as phishing) mails that contain urls that claim to lead to paypal, but they don’t in fact.

+

Be careful not to create regexes that match a too broad range of urls though.

+

Dealing with false positives, and undetected phishing mails

+

False positives

+

Whenever you see a false positive (mail that is detected as phishing, but its not), ou might need to modify daily.pdb (if one of yours rules in there are too broad), or you need to add the url to daily.wdb. If you think the algorithm is incorrect, please file a bug report.

+

Undetected phish mails

+

If the mail is detected, if yes, then you need to add an appropriate line to daily.pdb (see How to create database files).

+

If the mail is not detected, then try using:

+
$clamscan/clamscan --debug undetected.eml | less
+
+

Then see what urls are being checked, see if any of them is in an allow list, see if all urls are detected, etc.

+

Bytecode Signatures

+

Bytecode Signatures are the means by which more complex matching can be performed by writing C code to parse sample content at various stages in file extraction.

+

It is less complicated than it sounds. Essentially the signature author writes a function in C is compiled down to an intermediate language called "bytecode". This bytecode is encoded in ASCII .cbc file and distributed in bytecode.[cvd|cld]. When the database is loaded, ClamAV can interpret this bytecode to execute the function.

+

Bytecode functions are provided with a set of APIs that may be used to access the sample data, and to access what metadata ClamAV already has concerning the sample.

+

The function may at any time call an API to flag the sample as malicious, and may provide the signature/virus name at that time. This means a single bytecode signature (function) is written to handle a given file type and may trigger different alerts with different signature names as additional malicious characteristics for the file type are identified. That isn't to say that only one bytecode signature may be assigned to a given filetype, but that a single author may find it to be more efficient to use a bytecode signature to identify more than one type of malware.

+

The specifics on how to write and compile bytecode signatures are outside of the scope of this documentation. Extensive documentation on ClamAV Bytecode Signatures are provided with the ClamAV Bytecode Compiler.

+

Signatures based on container metadata

+

ClamAV 0.96 allows creating generic signatures matching files stored inside different container types which meet specific conditions. The signature format is:

+
    VirusName:ContainerType:ContainerSize:FileNameREGEX:
+    FileSizeInContainer:FileSizeReal:IsEncrypted:FilePos:
+    Res1:Res2[:MinFL[:MaxFL]]
+
+

where the corresponding fields are:

+
    +
  • +

    VirusName: Virus name to be displayed when signature matches.

    +
  • +
  • +

    ContainerType: The file type containing the target file. For example:

    +
      +
    • CL_TYPE_ZIP,
    • +
    • CL_TYPE_RAR,
    • +
    • CL_TYPE_ARJ,
    • +
    • CL_TYPE_MSCAB,
    • +
    • CL_TYPE_7Z,
    • +
    • CL_TYPE_MAIL,
    • +
    • CL_TYPE_(POSIX|OLD)_TAR,
    • +
    • CL_TYPE_CPIO_(OLD|ODC|NEWC|CRC)
    • +
    +

    Use * as a wild card to indicate that container type may be any file type. +For a full list of ClamAV file types, see the ClamAV File Types Reference.

    +
  • +
  • +

    ContainerSize: size of the container file itself (eg. size of the zip archive) specified in bytes as absolute value or range x-y.

    +
  • +
  • +

    FileNameREGEX: regular expression describing name of the target file

    +
  • +
  • +

    FileSizeInContainer: usually compressed size; for MAIL, TAR and CPIO == FileSizeReal; specified in bytes as absolute value or range.

    +
  • +
  • +

    FileSizeReal: usually uncompressed size; for MAIL, TAR and CPIO == FileSizeInContainer; absolute value or range.

    +
  • +
  • +

    IsEncrypted: 1 if the target file is encrypted, 0 if it’s not and * to ignore

    +
  • +
  • +

    FilePos: file position in container (counting from 1); absolute value or range.

    +
  • +
  • +

    Res1: when ContainerType is CL_TYPE_ZIP or CL_TYPE_RAR this field is treated as a CRC sum of the target file specified in hexadecimal format; for other container types it’s ignored.

    +
  • +
  • +

    Res2: not used as of ClamAV 0.96.

    +
  • +
+

The signatures for container files are stored inside .cdb files.

+

Passwords for archive files [experimental]

+

ClamAV allows for users to specify password attempts for certain password-compatible archives. Passwords will be attempted in order of appearance in the password signature file which use the extension of .pwdb. If no passwords apply or none are provided, ClamAV will default to the original behavior of parsing the file.

+

Currently, as of ClamAV 0.99 [flevel 81], only .zip archives using the traditional PKWARE encryption are supported. The signature format is:

+
SignatureName;TargetDescriptionBlock;PWStorageType;Password
+
+

where:

+
    +
  • +

    SignatureName: name to be displayed during debug when a password is successful

    +
  • +
  • +

    TargetDescriptionBlock: provides information about the engine and target file with comma separated Arg:Val pairs

    +
      +
    • Engine:X-Y: Required engine functionality level. See the FLEVEL reference for details.
    • +
    • Container:CL_TYPE_*: File type of applicable containers
    • +
    +
  • +
  • +

    PWStorageType: determines how the password field is parsed

    +
      +
    • 0 = cleartext
    • +
    • 1 = hex
    • +
    +
  • +
  • +

    Password: value used in password attempt

    +
  • +
+

The signatures for password attempts are stored inside .pwdb files.

+

ClamAV Development

+

This chapter aims to provide information useful when developing, debugging, or profiling ClamAV.

+

Pull Request Basics

+

If you're new to open source software development on GitHub, take a moment to read this quick introduction on GitHub Pull Requests.

+

ClamAV Git Work Flow

+

ClamAV's Git work flow isn't too complicated, but it is unique. See this section to get a better understanding of how we use Git.

+

Working with Your Fork

+

New core developers and community contributors can follow these steps to prepare an environment to work on ClamAV development.

+

Reviewing Pull Requests

+

If you need to review a pull request, it's best to not only eyeball the changes and review the test logs, but to also build and test the PR manually on your own computer. This section will help you check out someone else's PR so you can test it.

+

Building for Development

+

Basic instructions for building ClamAV can be in the Installing chapter and a comprehensive reference for compiling ClamAV with CMake is available in the INSTALL.md file accompanying the source code, but if you want a few extra tips on building ClamAV for development purposes, check this out.

+

Building the Installer Packages

+

If you'd like to learn how we build the installer packages and even replicate this on your own, you can read about it here.

+

Dev Tips & Tricks

+

This section contains a varied assortment of techniques you can use when coding on ClamAV to up your development game.

+

libclamav

+

Are you interested in using libclamav to scan for malware in your own C/C++ application? This section introduces the most important functions of the libclamav API. For a more comprehensive reference, inline documention can be found in the clamav.h header distributed with libclamav.

+

Contribute

+

If you're interested in contributing to ClamAV, we've assembled a page of bugs that need fixing as well as other project ideas that we feel might be great new-contributor projects.

+

If you're looking for project ideas, check out the project ideas list to find out how you might be able to help out the project!

+

GitHub Pull Request Basics

+

Like most projects on GitHub, we use a pull request (PR) work flow for contributions to the clamav Git repository. This is true both for contributions from the community as well as for those from the core development team here at Cisco.

+

Historically, our team PRs have been reviewed and merged on an internal upstream Git repository, though we are now migrating to do all work on GitHub directly, with a private backup fork of the project used exclusively for review of security-related fixes and in preparation of each security patch version release.

+

The general pattern for working with Pull Requests on GitHub is this:

+
    +
  1. +

    You create a fork of the project repository on GitHub.

    +
  2. +
  3. +

    You clone (download) a copy of your fork on your computer.

    +
  4. +
  5. +

    In your local fork, you create a new branch for the feature or bug fix.

    +
  6. +
  7. +

    For each change, you create a Git commit. Your commits may include incomplete work as you're going, so you can save your work each day, in the end you should edit or squash those commits down so that each of your commits represent a single completed task and that the build is never broken for any given commit.

    +
  8. +
  9. +

    You push your commits to your remote fork on GitHub as needed, both to back up your work and to prepare for submitting a pull request for team review. If you've pushed commits and then edited or squashed those commits, you may have a conflict and may need to "force push" in order to update your remote fork.

    +
  10. +
  11. +

    When ready, submit a pull request, either by using the link provided by Git when you do a push, or by using the GitHub website. Use the pull request description to include instructions for how to test your changes!

    +
  12. +
  13. +

    There are also some automated tests that get run when you submit a pull request, but you may need to re-run them if something goes wrong with the test infrastructure. Review the test results and work with the other developers to fix any issues with your pull request so it can be approved and merged.

    +
  14. +
+

If you're new to all of this, have a look at GitHub's flow guide for their general recommendations on a basic PR work flow. Then continue in the next section to learn about ClamAV's work flow.

+

ClamAV Git Work Flow

+

ClamAV's Git work flow isn't very complicated, but it is more structured than most. It looks like this. Note that in the diagrams below, merged branches are regular merges and will add all of the commits from the source branch to the destination branch. The diagram doesn't show all the merged commits, for simplicity:

+

Git Work Flow

+

main:

+

the development branch. testing is done in pull-requests (PR's), so this branch should be stable, though we make no guarantees.

+

rel/0.104, rel/1.0:

+

Feature release branches. These always contain the latest stable patch versions for each feature release. When development on a feature release (E.g. dev/0.104) or a patch release (E.g. dev/0.104.1) is complete, they are merged here and tagged.

+

dev/0.104.1:

+

A development branch used to test hotfixes prior to a patch release

+

sec/dev/0.104.1:

+

A private development branch used to test security-related hotfixes prior to a patch release. This branch will be rebased like any feature branch as needed up until the release at which point it is merged into the dev/0.104.1 branch and the dev/0.104.1 branch is merged into the rel/0.104 branch and tagged as "clamav-0.104.1".

+

feature/blah:

+

A long-running branch for adding a major feature. It may be rebased several times with the default branch before > it is ready to merge.

+

CLAM-####-description, issue-####-description, bb####-description:

+

A branch for working a JIRA task, GitHub issue, or Bugzilla Bug. These are typically only found in a personal > fork and appear as pull requests from the fork to the upstream clamav repository.

+

Working with a Your Own Fork of the ClamAV repository

+

A "fork" on GitHub is a personal playground. Though the word "fork" in the concept of open source traditionally referred to creating (and maintaining) a new variant of a project, forks on GitHub/GitLab/BitBucket/etc these days typically refer to a personal copy of the project where a user can test modifications to fix a bug or add a feature before contributing it back to the project in the form of a "pull request".

+

Create and Maintain a Personal Fork

+

You may only have one for any project, but it is very easy to create:

+

Create a Fork

+

You can rename it as needed so you won't confused a clone of your fork with that of the upstream clamav repository. Go to the "Settings" page and change the name to add your name in a suffix:

+

Change Fork Name

+

You're free to add or delete branches in your fork as you see fit, but I would advise against adding your own commits to the existing branches. The existing branches, particularly the default branch* are a reference from which you can create your own branches for your work. Adding your own commits to the existing branches will break your ability to synchronize with the upstream Cisco-Talos/clamav repository, and without more advanced Git experience you won't be able to correct it.

+
+

Tip: If you've managed to screw up the commit history in your fork to the point where you don't know how to fix it, you can always delete your fork and create a new one.

+
+

Your fork is a snapshot of the upstream clamav repository at the moment at which you created it. Left unmaintained, your fork's default branch will get left behind. Unlike BitBucket, GitHub will not sync branches for you automatically. If your branch is behind, it is simple to sync the branch on your fork using GitHub's GUI by pressing the "Fetch Upstream" button:

+

Sync and Merge

+

You can sync other branches too. Simply switch branches to the desired branch and press "Fetch Upstream" again.

+
+

Disclaimer: The ClamAV project has a history of changing default branches for development on each feature version. We've found that this causes more trouble than it is worth, and we intend to stop doing that after 0.104. Right now, the default branch is dev/0.104. After 0.104 is complete, it will be changed to main and we'll stop changing it. For more details, see the work flow changes in the section below.

+

Tip: After we change the default branch for Cisco-Talos/clamav to main, you'll need to change your default branch too (it won't switch in your fork just because we changed the default in the upstream repo).

+
+

Working with a Clone of your Fork on the Command Line

+

If you don't already have an SSH key for your GitHub account, I recommend creating one. Navigate to your Account Settings and under SSH and GPG keys, click the "New SSH key" button. If you're unfamiliar with how to generate an SSH key, there's a nearby link "generating SSH keys" with additional instructions.

+

Next, clone your ClamAV fork. Use the "Code" button on the default page for your fork to copy the "SSH" URL. If you don't want to use an SSH key for GitHub authentication, use the HTTPS URL instead:

+

Clone your Fork

+

Now open up a terminal and type:

+
git clone <paste that Git URL>
+cd clamav-YourNameHere
+
+

Create a branch off of the default branch where you will work. If working on a GitHub Issue, Bugzilla Bug, or JIRA task*, the following branch name prefixes will help you and others identify the branch:

+
    +
  • For GitHub Issues: issue-####-short-description
  • +
  • For Bugzilla Bugs: bb-####-short-description
  • +
  • For JIRA task: CLAM-####-short-description
  • +
+
+

Note: *The ClamAV JIRA task tracker is not accessible outside of the Cisco network.

+
+

Create your working branch:

+
git checkout -b issue-####-short-description
+
+

You're now ready to make edits to the source. Be sure your changes match our code format style. The easiest way is to install clang-format and enable "Format On Save" in your text editor.

+

When you have made your changes, run:

+
git add -u
+git commit
+
+

Leave a meaningful git commit message that has a high-level descriptive title, and a more technical message body describing why the change was needed what your commit does to resolve it.

+

Run this to upload your commit to your fork on GitHub:

+
git push -u origin <the_name_of_your_branch>
+
+

The -u origin argument will enable tracking between your local branch and your remote branch. In the future you will only need to do git push and it will know where to push it.

+

Rebase your development branch with the upstream main branch and resolving merge conflicts

+

If you don't already have it:

+
git remote add upstream git@github.com:Cisco-Talos/clamav.git
+
+

Then run:

+
git fetch upstream
+git rebase upstream/main
+
+

If you have any merge conflicts, you'll now have the opportunity to fix them. After every conflict is resolved and you've saved the files in question, run git add <the resolved files> and then run git rebase --continue.

+

Reviewing & Testing Pull Requests

+

The tools available on GitHub are fantastic for reviewing code changes, but less so for checking out those code changes on your local machine to build and test them.

+

With a simple tweak to your .git/config file in a clone of the Cisco-Talos/clamav repository, you can simply check out another developer's pull request as a branch to build and test it.

+

First, if you don't already have a clone of the upstream repository, do this:

+
git clone https://github.com/Cisco-Talos/clamav.git
+
+

Then, in your favorite text editor, open .git/config and add the following line at the end of the [remote "origin"] section. Don't delete or replace anything, just add the following line:

+
fetch = +refs/pull/*/head:refs/remotes/origin/PR-*
+
+
+

Tip: The above works for GitHub Cloud and GitHub Enterprise servers. If you happen to work with GitLab or BitBucket, you can do this instead for the same effect:

+

For BitBucket repos:

+
fetch = +refs/pull-requests/*/from:refs/remotes/origin/PR-*
+
+

For GitLab repos:

+
fetch = +refs/merge-requests/*/head:refs/remotes/origin/PR-*
+
+
+

Now you may run the following to test the pull request. Substitute # with the pull request number found on the website:

+
git fetch
+git checkout PR-#
+
+

At this point, you should find yourself in a branch containing the PR contributor's changes.

+

Building for Development

+

The following are instructions for building ClamAV using CMake or Autotools with recommendations specific to ClamAV software development.

+ +

Build dependencies

+

Instructions to install the build dependencies for each major operating system and distribution can be found in our install from source instructions. See:

+ +

For development, you may also need to install the following...

+

For Linux/UNIX

+
    +
  • +

    git

    +
  • +
  • +

    autoconf, automake, libtool, m4 (For 0.103 and older, only):

    +

    These four packages make up "Autotools" and are required to run the ./autogen.sh script that generates the ./configure tool for configuring an Autotools build. You will need these packakges if building from a git clone, but the 0.103 release tarballs have this stuff pre-generated for normal users.

    +
  • +
  • +

    valgrind:

    +

    Valgrind is used to enhance our unit tests and feature tests. It will be automatically when running ctest for CMake builds, or when running make check VG=1 for Autotools builds.

    +
  • +
  • +

    pytest (For 0.104 and newer):

    +

    Pytest is used in our test suite for CMake builds to get better test output when analyzing test failures. Having pytest

    +
  • +
  • +

    bison and flex:

    +

    Bison and Flex re-generating the Yara rule parser, used by our "maintainer mode" in the CMake build system (v0.104+), and used by default in the Autotools build system (v0.103-).

    +
  • +
  • +

    gperf:

    +

    GPerf is another tool used by our "maintainer mode". It is used to generate code for our javascript normalizer. *Enabling GPerf in the "maintainer mode" for the CMake build system (v0.104+) is still a to-do.

    +
  • +
  • +

    ninja or ninja-build (For 0.104 and newer):

    +

    Ninja is a build system that CMake can use in place of Makefiles (UNIX) or Visual Studio (Windows). It's faster than both, particularly as compared with Visual Studio. If you're compiling ClamAV frequently, you may find it very helpful. The only real downside is that it's highly multithreaded so errors and warnings tend to get mixed up and it can be hard to inspect when you're having build failures.

    +
  • +
+

For Windows

+
    +
  • +

    Git for Windows.

    +
  • +
  • +

    Windows Terminal:

    +

    Windows Terminal is Microsoft's new open source terminal program. It's quite nice, and miles better than using a traditional CMD prompt or Powershell.

    +
  • +
  • +

    Visual Studio:

    +

    2019 Community Edition is fine. It may also possible to build using Clang + Ninja. Investigate at your peril.

    +
  • +
  • +

    Visual Studio Code:

    +

    I recommend a modern text editor like VSCode instead of working with Visual Studio directly. Visual Studio is powerful, but can be slow and unwieldy. Those coming from a Unix/Linux background may find that the Vim plugin is particularly nice.

    +
  • +
  • +

    Chocolatey:

    +

    Chocolatey is an application package manager for Windows that makes it easy to install stuff on Windows. After you install Chocolatey, you can use it to install CMake, WiX, Flex, Bison, Perl, and Nasm very simply, like this:

    +
    choco install cmake
    +
    +
  • +
  • +

    Wix Toolset:

    +

    If you want to build the installer, you'll also need WiX Toolset. If not, you can skip it. You can install it with Chocolatey:

    +
    choco install wixtoolset
    +
    +
  • +
  • +

    bison and flex:

    +

    As mentioned in the Linux/Unix section, Bison and Flex may be needed if working with the yara parser module. You can install it with Chocolatey:

    +
    choco install winflexbison
    +
    +
  • +
  • +

    ActivePerl and Netwide Assembler (NASM):

    +

    Perl and NASM are required to compile openssl from source if using Mussels to build your dependencies. If using vcpkg, you can skip them. You can install these tools using Chocolatey:

    +
    choco install activeperl nasm
    +
    +
  • +
+

We support two options for sourcing and building ClamAV library dependencies:

+
    +
  1. vcpkg
  2. +
  3. Mussels
  4. +
+

For basic builds, vcpkg should do just fine*, and is easier to get started.

+

If you want to customize how the dependencies are built, use Mussels. The ClamAV project uses Mussels to build the installers available on our website. The recipes to build the ClamAV dependencies, the definitions for finding and using the required development tools are hosted in the ClamAV Mussels Cookbook.

+
+

* There is a known issue with the unit tests when building with vcpkg in Debug mode. When you run the libclamav unit tests (check_clamav), the program will crash and a popup will claim there was heap corruption. If you use Task Manager to kill the check_clamav.exe process, the rest of the tests pass just fine. This issue does not occur when using Mussels to supply the library dependencies. Commenting out the following lines in readdb.c resolves the heap corruption crash when running check_clamav, but of course introduces a memory leak:

+
    if (engine->stats_data)
+        free(engine->stats_data);
+
+

If anyone has time to figure out the real cause of the vcpkg Debug-build crash +in check_clamav, it would be greatly appreciated.

+
+

Download the Source

+

If you don't already have the source, use Git to clone it:

+
git clone https://github.com/Cisco-Talos/clamav.git
+cd clamav
+
+

If you intend to make changes and submit a pull request, fork the Cisco-Talos/clamav repo first and then clone your fork of the repository instead:

+
git clone https://github.com/<yourusername>/clamav.git clamav-fork
+cd clamav-fork
+
+

Building ClamAV with CMake (v0.104 and newer)

+

ClamAV versions 0.103+ provide CMake build tooling. In 0.103, this is for experimental and development purposes only. Autotools should be used for production builds. In 0.104+, CMake is the only build system. Autotools and Visual Studio build systems have been removed.

+

To get started, first review the official build isntructions. Then return hrefer for some tips on building for development. For a complete reference of ClamAV build configuration options, see the INSTALL.md file located in the clamav repository.

+

The following instructions assume you have installed CMake, Ninja, and either GCC, Clang, or Visual Studio 2015 or newer.

+

Linux/Unix

+

Linux/Unix builds are often much simpler because the system's package manager can provide all of the dependencies for your. But if you want, you can build them yourself, using a tool like Mussels. The instructions listed in the Windows section are nearly identical. With Mussels on Linux, Unix, and macOS you can even use the host-static target to build all of the dependencies as static libraries to make ClamAV more portable and easier to package. Feel free to reach out on Discord if you want to learn more.

+

Configure (generate the build system):

+
cmake .. -G Ninja                   \
+    -D CMAKE_BUILD_TYPE="Debug"     \
+    -D OPTIMIZE=OFF                 \
+    -D CMAKE_INSTALL_PREFIX=install \
+    -D ENABLE_MILTER=ON             \
+    -D ENABLE_EXAMPLES=ON           \
+    -D ENABLE_STATIC_LIB=ON         \
+    -D ENABLE_SYSTEMD=OFF
+
+

Build:

+
ninja
+
+

Install (to the CMAKE_INSTALL_PREFIX directory):

+
ninja install
+
+

Windows

+

vcpkg

+

vcpkg can be used to build the ClamAV library dependencies automatically. See the vcpkg README for installation instructions.

+

Once installed, set the variable $VCPKG_PATH to the location where you installed vcpkg:

+
$VCPKG_PATH="..." # Path to your vcpkg installation
+
+

By default, CMake and vcpkg build for 32-bit. If you want to build for 64-bit, set the VCPKG_DEFAULT_TRIPLET environment variable:

+
$env:VCPKG_DEFAULT_TRIPLET="x64-windows"
+
+

Now run the following to build ClamAV's library dependencies:

+
& "$VCPKG_PATH\vcpkg" install 'curl[openssl]' 'json-c' 'libxml2' 'pcre2' 'pthreads' 'zlib' 'pdcurses' 'bzip2' 'check'
+
+

Finally, you can use the following to build ClamAV using Ninja for super fast builds. Replace "2019" and "Community" with different versions or editions as needed to match your Visual Studio installation.

+

Configure (generate the build system):

+
pushd "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools"
+cmd /c "VsDevCmd.bat -arch=amd64 & set" |
+foreach {
+  if ($_ -match "=") {
+    $v = $_.split("="); set-item -force -path "ENV:\$($v[0])"  -value "$($v[1])"
+  }
+}
+popd
+Write-Host "`nVisual Studio 2019 Command Prompt variables set." -ForegroundColor Yellow
+
+cmake .. -G Ninja                                                          `
+    -D CMAKE_BUILD_TYPE="Debug"                                             `
+    -D CMAKE_TOOLCHAIN_FILE="$VCPKG_PATH\scripts\buildsystems\vcpkg.cmake"  `
+    -D CMAKE_INSTALL_PREFIX="install"
+
+

Build:

+
ninja
+
+

Install (to the CMAKE_INSTALL_PREFIX directory):

+
ninja install
+
+
+

Tip: I like to place the "Configure" script in a configure-vcpkg.ps1 script file in my home directory. This way I can simply run ~\configure-vcpkg.ps1 followed by ninja to do a build.

+
+

Mussels

+

Here are some tips for using Mussels to build the clamav dependencies and then for building ClamAV with those dependencies.

+

Install Mussels

+
python3 -m pip install --user mussels
+
+

After the install, pip may suggest you add a "Scripts" directory to your PATH environment variable. I strongly suggest that you do this, so you can run msl for mussels commands instead of having to type python3 -m mussels for every command.

+

Now to use Mussels, run:

+
# This requires Git, and will clone the the "clamav" and "scrapbook" cookbooks.
+msl up
+
+# This is to enable running the scripts in the clamav cookbook to build the clamav dependencies.
+msl cookbook trust -y clamav
+
+# This is just to get you in a small directory so Mussels don't spend forever searching your harddrive for build recipes.
+mkdir tmp && cd tmp
+
+# First try a dry-run. If you're missing any tools, it will tell you.
+# If you have everything, it will give you a list of the order it plans to build everything.
+msl build clamav_deps --dry-run
+
+# Then do the build.
+msl build clamav_deps
+
+# You could also have it build clamav for you too, but that's not so useful for development work.
+msl build clamav
+
+

Mussels isn't great for doing repeated builds, because it tends to retry every build in the dependency chain, and that will take some time. Mussels also builds from a downloaded source tarball, and not from a local Git repository. If you're interested in working on improvement Mussels for development purposes and smoothing over some of these sharp edges, we'd love the help.

+

So assuming you've now build the clamav dependencies, you should be ready to build ClamAV. Replace "2019" and "Community" with different versions or editions as needed to match your Visual Studio installation

+

Configure (generate the build system):

+
pushd "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools"
+cmd /c "VsDevCmd.bat -arch=amd64 & set" |
+foreach {
+  if ($_ -match "=") {
+    $v = $_.split("="); set-item -force -path "ENV:\$($v[0])"  -value "$($v[1])"
+  }
+}
+popd
+Write-Host "`nVisual Studio 2019 Command Prompt variables set." -ForegroundColor Yellow
+
+cmake ..  -G Ninja -D CMAKE_BUILD_TYPE="Debug" `
+     -D ENABLE_EXAMPLES=OFF                                                 `
+     -D JSONC_INCLUDE_DIR="$home\.mussels\install\x64\include\json-c"         `
+     -D JSONC_LIBRARY="$home\.mussels\install\x64\lib\json-c.lib"             `
+     -D ENABLE_JSON_SHARED=OFF                                              `
+     -D BZIP2_INCLUDE_DIR="$home\.mussels\install\x64\include"                `
+     -D BZIP2_LIBRARY_RELEASE="$home\.mussels\install\x64\lib\libbz2.lib"     `
+     -D CURL_INCLUDE_DIR="$home\.mussels\install\x64\include"                 `
+     -D CURL_LIBRARY="$home\.mussels\install\x64\lib\libcurl_imp.lib"         `
+     -D OPENSSL_ROOT_DIR="$home\.mussels\install\x64"                         `
+     -D OPENSSL_INCLUDE_DIR="$home\.mussels\install\x64\include"              `
+     -D OPENSSL_CRYPTO_LIBRARY="$home\.mussels\install\x64\lib\libcrypto.lib" `
+     -D ZLIB_LIBRARY="$home\.mussels\install\x64\lib\libssl.lib"              `
+     -D LIBXML2_INCLUDE_DIR="$home\.mussels\install\x64\include\libxml2"      `
+     -D LIBXML2_LIBRARY="$home\.mussels\install\x64\lib\libxml2.lib"          `
+     -D PCRE2_INCLUDE_DIR="$home\.mussels\install\x64\include"                `
+     -D PCRE2_LIBRARY="$home\.mussels\install\x64\lib\pcre2-8.lib"            `
+     -D CURSES_INCLUDE_DIR="$home\.mussels\install\x64\include"               `
+     -D CURSES_LIBRARY="$home\.mussels\install\x64\lib\pdcurses.lib"          `
+     -D PThreadW32_INCLUDE_DIR="$home\.mussels\install\x64\include"           `
+     -D PThreadW32_LIBRARY="$home\.mussels\install\x64\lib\pthreadVC2.lib"    `
+     -D ZLIB_INCLUDE_DIR="$home\.mussels\install\x64\include"                 `
+     -D ZLIB_LIBRARY="$home\.mussels\install\x64\lib\zlibstatic.lib"          `
+     -D LIBCHECK_INCLUDE_DIR="$home\.mussels\install\x64\include"             `
+     -D LIBCHECK_LIBRARY="$home\.mussels\install\x64\lib\checkDynamic.lib"    `
+     -D CMAKE_INSTALL_PREFIX="install"
+
+

Build:

+
ninja
+
+

Install (to the CMAKE_INSTALL_PREFIX directory):

+
ninja install
+
+
+

Tip: I like to place the "Configure" script in a configure-mussels.ps1 script file in my home directory. This way I can simply run ~\configure-mussels.ps1 followed by ninja to do a build.

+
+

Testing with CTest

+

ClamAV version 0.104+ will include unit tests, integration tests, & feature tests performed via CMake's ctest toolset. All tests are executed within through ctest but within a Python test framework build around Python's unittest module. See clamav/unit_tests/testcase.py. Python 3.5+ is required.

+
+

Note: Valgrind tests are performed on Linux if Valgrind is installed.

+
+

Unit Tests

+

The libclamav unit tests use the libcheck framework. There are presently no unit tests for libfreshclam. See clamav/unit_tests/check_clamav.c for the libclamav unit tests.

+

Integration Tests

+

ClamAV is presently light on integration tests for libclamav, though you may think of the application feature as integration tests, because the apps integrate libclamav. Tests for additional features not easily exercised via the existing applications could be added by creating new example applications in clamav/examples and exercising those programs in new CTest tests. See clamav/unit_tests/CMakeLists.txt and clamav/examples/CMakeLists.txt for details.

+

Feature Tests

+

ClamAV primarily has feature tests for ClamD and ClamScan, though basic version tests do exist for FreshClam and SigTool as well. See clamav/unit_tests/CMakeLists.txt and clamav/unit_tests/clamscan_test.py for an example.

+

Building ClamAV with Autotools (v0.103 and older)

+

Running autogen.sh

+

ClamAV versions 0.103+ will require you to run autogen.sh before running configure when building from a git clone. The files generated by Autotools, such as configure, are no longer stored in the Git repo. When you run autogen.sh it will generate those files for you.

+
./autogen.sh
+
+

To run autogen.sh, you will need some extra tools:

+
    +
  • autoconf
  • +
  • automake
  • +
  • libtool
  • +
  • m4
  • +
  • pkg-config
  • +
+

The packages to install are...

+

Redhat / Centos / Fedora

+
dnf/yum install -y \
+  autoconf autoreconf automake libtool libtool-ltdl-devel m4 pkg-config
+
+

Debian / Ubuntu

+
apt-get install -y \
+  autoconf autoconf-archive automake libtool libltdl-dev m4 pkg-config
+
+

Running configure

+

To ensure that build artifacts don't clutter the source code directory, create a subdirectory named build.

+
mkdir build
+cd build
+
+

For a basic build, just run ../configure. If you've installed the dependencies with your platforms respective package manager, it should detect the dependencies automatically. macOS users will need to use this option to properly detect openssl --with-openssl=/usr/local/opt/openssl@1.1.

+

Run ../configure --help to see a full list of options. The following suggestions will help you get started:

+
    +
  • +

    Modify the CFLAGS, CXXFLAGS, OBJCFLAGS variables as follows (assuming you're build with gcc):

    +
      +
    • +

      Include gdb debugging information (-ggdb). This will make it easier to debug with gdb.

      +
    • +
    • +

      Disable optimizations (-O0). This will ensure the line numbers you see in gdb match up with what is actually being executed.

      +
    • +
    +

    Example:

    +
    CFLAGS="-ggdb -O0" CXXFLAGS="-ggdb -O0" OBJCFLAGS="-ggdb -O0" ../configure
    +
    +

    NOTE: Setting OBJCFLAGS is needed because currently, clamsubmit gets built with the Objective-C compiler. See this Stack Overflow post for a discussion of why this occurs.

    +
  • +
  • +

    Run configure with the following options:

    +
      +
    • +

      --prefix=`pwd`/../installed: This will cause make install to install into the specified directory (a directory named installed in the root of the ClamAV source code directory).

      +
    • +
    • +

      --enable-debug: This will define CL_DEBUG, which mostly just enables additional print statements that are useful for debugging.

      +
    • +
    • +

      --enable-check: Enables the unit tests, which can be run with make check.

      +
    • +
    • +

      --enable-coverage: If using gcc, sets -fprofile-arcs -ftest-coverage so that code coverage metrics will get generated when the program is run. Note that the code inserted to store program flow data may show up in any generated flame graphs or profiling output, so if you don't care about code coverage, omit this.

      +
    • +
    • +

      --enable-libjson: Enables libjson, which enables the --gen-json option. The json output contains additional metadata that might be helpful when debugging.

      +
    • +
    • +

      --with-systemdsystemunitdir=no: Don't try to register clamd as a systemd service (on systems that use systemd). You likely don't want this development build of clamd to register as a service, and this eliminates the need to run make install with sudo.

      +
    • +
    • +

      You might want to include the following flags also so that the optional functionality is enabled: --enable-experimental --enable-clamdtop --enable-milter --enable-xml --enable-pcre. Note that this may require you to install additional development libraries.

      +
    • +
    • +

      --enable-llvm --with-system-llvm=no: When LLVM is enabled, LLVM provides the capability to just-in-time compile ClamAV bytecode signatures. Without LLVM, ClamAV uses a built-in bytecode interpreter to execute bytecode signatures. With LLVM, options, "system LLVM" and "internal LLVM". The bytecode interpreter is somewhat slower than using LLVM, though the results are the same. At present only LLVM versions up to LLVM 3.6.2 are supported by ClamAV, and LLVM 3.6.2 is old enough that newer distributions no longer provide it. Therefore, we recommend using the --enable-llvm --with-system-llvm=no configure option to use the "internal LLVM". It is worth noting that the internal LLVM can take a while to build, and that the JIT compilation process for loading bytecode signatures also takes a while when starting clamd or clamdscan. For compile speed and clamscan load speed, you may wish to instead ouse --disable-llvm.

      +
    • +
    +
  • +
+

Altogether, the following configure command can be used:

+
CFLAGS="-ggdb -O0" CXXFLAGS="-ggdb -O0" OBJCFLAGS="-ggdb -O0" ../configure --prefix=`pwd`/../installed --enable-debug --enable-check --enable-coverage --enable-libjson --with-systemdsystemunitdir=no --enable-experimental --enable-clamdtop --enable-xml --enable-pcre --enable-llvm --with-system-llvm=no
+
+

NOTE: It is possible to build libclamav as a static library and have it statically linked into clamscan/clamd (to do this, run ../configure with --enable-static --disable-shared). This is useful for using tools like gprof that do not support profiling code in shared objects. However, there are two drawbacks to doing this:

+
    +
  • +

    clamscan/clamd will not be able to extract files from RAR archives. Based on the software license of the unrar library that ClamAV uses, the library can only be dynamically loaded. ClamAV will attempt to dlopen the unrar library shared object and will continue on without RAR extraction support if the library can't be found (or if it doesn't get built, which is what happens if you indicate that shared libraries should not be built).

    +
  • +
  • +

    If you make changes to libclamav, you'll need to make clean, make, and make install again to have clamscan/clamd rebuilt using the new libclamav.a. The makefiles don't seem to know to rebuild clamscan/clamd when libclamav.a changes (TODO, fix this).

    +
  • +
+

Running make

+

Run the following to finishing building. -j2 in the code below is used to indicate that the build process should use 2 cores. Increase this if your machine is more powerful.

+
make -j2
+make install
+
+

The ClamAV executables will get installed in ../installed/bin/, so to invoke clamscan do:

+
cd ..
+./installed/bin/clamscan
+
+

Testing with make check

+

You can run make check to run the unit tests and feature tests.

+

Unlike with the CTest tool used for CMake builds, you must use make check VG=1 if you wish to run extra tests using Valgrind (must be installed).

+

Building the Installer Packages

+

ClamAV's installer packages are compiled in a Jenkins CI environment in the Cisco-Talos development network. For each supported OS / packaging system / architecture, we have a computer (or VM) that maintains a copy of ClamAV's external library dependencies. These libraries are recompiled using Mussels any time there is a change to the recipes in our "clamav" Mussels cookbook.

+

Instructions follow for how you can build the installer packages in much the same way that we do.

+

Linux

+
+

Tip: Using an older version of Linux is best. ClamAV's only dependency will be on glibc, which is forwards compatible. That is to say that if you build the installer on an older version of Linux, it should install and run on a new version of Linux. The opposite is not true.

+
+

First, install Mussels:

+
python3 -m pip install --user mussels
+
+

Mussels also requires Git, so if you don't have that installed, install it now.

+

Now to use Mussels, run:

+
# This requires Git, and will clone the the "clamav" and "scrapbook" cookbooks.
+msl up
+
+# This is to enable running the scripts in the clamav cookbook to build the clamav dependencies.
+msl cookbook trust -y clamav
+
+# This is just to get you in a small directory so Mussels don't spend forever searching your harddrive for build recipes.
+mkdir tmp && cd tmp
+
+# First try a dry-run. If you're missing any tools, it will tell you.
+# If you have everything, it will give you a list of the order it plans to build everything.
+msl build -t host-static clamav_deps --dry-run
+
+# Then do the build.
+msl build -t host-static clamav_deps
+
+
+

Tip: On some systems you may encounter this error:

+
RuntimeError: Click will abort further execution because Python was configured to use ASCII as encoding for the environment. Consult https://click.palletsprojects.com/unicode-support/ for mitigation steps.
+
+This system lists some UTF-8 supporting locales that you can pick from. The following suitable locales were discovered: en_AG.utf8, en_AU.utf8, en_BW.utf8, en_CA.utf8, en_DK.utf8, en_GB.utf8, en_HK.utf8, en_IE.utf8, en_IN.utf8, en_NG.utf8, en_NZ.utf8, en_PH.utf8, en_SG.utf8, en_US.utf8, en_ZA.utf8, en_ZM.utf8, en_ZW.utf8
+
+

To resolve this, pick a local and set it, like this:

+
export LC_ALL=en_US.utf8
+
+

After you've set LC_ALL to your desired locale, re-run the above msl commands. You should see it run Git to update the cookbooks without error.

+
+

So assuming you've now build the clamav dependencies, you should be ready to build ClamAV.

+

In a Git clone of the clamav source (or the extracted source tarball from a release), create a build subdirectory and open a terminal in that build directory.

+

Run the following...

+

Configure (generate the build system):

+
cmake .. \
+    -D CMAKE_FIND_PACKAGE_PREFER_CONFIG=TRUE                                          \
+    -D CMAKE_PREFIX_PATH="$HOME/.mussels/install/host-static"                         \
+    -D CMAKE_INSTALL_PREFIX="/usr/local"                                              \
+    -D CPACK_PACKAGING_INSTALL_PREFIX="/usr/local"                                    \
+    -D CPACK_DEBIAN_PACKAGE_RELEASE=1                                                 \
+    -D CPACK_RPM_PACKAGE_RELEASE=1                                                    \
+    -D CMAKE_MODULE_PATH="$HOME/.mussels/install/host-static/lib/cmake"               \
+    -D CMAKE_BUILD_TYPE=RelWithDebInfo                                                \
+    -D ENABLE_EXAMPLES=OFF                                                            \
+    -D ENABLE_MILTER=OFF                                                              \
+    -D JSONC_INCLUDE_DIR="$HOME/.mussels/install/host-static/include/json-c"          \
+    -D JSONC_LIBRARY="$HOME/.mussels/install/host-static/lib/libjson-c.a"             \
+    -D ENABLE_JSON_SHARED=OFF                                                         \
+    -D BZIP2_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"                 \
+    -D BZIP2_LIBRARY_RELEASE="$HOME/.mussels/install/host-static/lib/libbz2_static.a" \
+    -D OPENSSL_ROOT_DIR="$HOME/.mussels/install/host-static"                          \
+    -D OPENSSL_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"               \
+    -D OPENSSL_CRYPTO_LIBRARY="$HOME/.mussels/install/host-static/lib/libcrypto.a"    \
+    -D OPENSSL_SSL_LIBRARY="$HOME/.mussels/install/host-static/lib/libssl.a"          \
+    -D LIBXML2_INCLUDE_DIR="$HOME/.mussels/install/host-static/include/libxml2"       \
+    -D LIBXML2_LIBRARY="$HOME/.mussels/install/host-static/lib/libxml2.a"             \
+    -D PCRE2_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"                 \
+    -D PCRE2_LIBRARY="$HOME/.mussels/install/host-static/lib/libpcre2-8.a"            \
+    -D NCURSES_INCLUDE_DIR="$HOME/.mussels/install/host-static/include/ncurses"       \
+    -D CURSES_LIBRARY="$HOME/.mussels/install/host-static/lib/libncurses.a"           \
+    -D ZLIB_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"                  \
+    -D ZLIB_LIBRARY="$HOME/.mussels/install/host-static/lib/libz.a"                   \
+    -D LIBCHECK_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"              \
+    -D LIBCHECK_LIBRARY="$HOME/.mussels/install/host-static/lib/libcheck.a"
+
+
+

Tip: Note the use of CPACK_DEBIAN_PACKAGE_RELEASE and CPACK_RPM_PACKAGE_RELEASE. Feel free to only use the one you need for whichever platform you're targeting. You should increase the release version number if re-releasing a new package of the same ClamAV version.

+
+

Build:

+
make -j12
+
+

It's a good idea to run the public test suite at this point:

+
ctest -V
+
+

To make the RPM package for RPM-based distributions, you'll need the rpmbuild tool, which you can install with yum install rpm-build. Then run:

+
cpack -G RPM
+
+

To make the DEB package for Debian-based distributions, run:

+
cpack -G DEB
+
+

macOS

+
+

Note: The macOS instructions depend on Xcode, which is required to build the arm64 + x86_64 "universal" binaries. You may need to install it from the macOS app store. Be sure to run it once to accept the EULA before you proceed.

+
+

First, install Mussels:

+
python3 -m pip install --user mussels
+
+

Mussels also requires Git, so if you don't have that installed, install it now.

+

Now to use Mussels, run:

+
# This requires Git, and will clone the the "clamav" and "scrapbook" cookbooks.
+msl up
+
+# This is to enable running the scripts in the clamav cookbook to build the clamav dependencies.
+msl cookbook trust -y clamav
+
+# This is just to get you in a small directory so Mussels don't spend forever searching your harddrive for build recipes.
+mkdir tmp && cd tmp
+
+# First try a dry-run. If you're missing any tools, it will tell you.
+# If you have everything, it will give you a list of the order it plans to build everything.
+msl build -t host-static clamav_deps --dry-run
+
+# Then do the build.
+msl build -t host-static clamav_deps
+
+

So assuming you've now build the clamav dependencies, you should be ready to build ClamAV.

+

In a Git clone of the clamav source (or the extracted source tarball from a release), create a build subdirectory and open a terminal in that build directory.

+

Run the following...

+

Configure (generate the build system):

+
cmake .. \
+    -G Xcode                                                                            \
+    -D CLAMAV_SIGN_FILE=ON                                                              \
+    -D CMAKE_OSX_ARCHITECTURES="arm64;x86_64"                                           \
+    -D CMAKE_FIND_PACKAGE_PREFER_CONFIG=TRUE                                            \
+    -D CMAKE_PREFIX_PATH="$HOME/.mussels/install/host-static"                           \
+    -D CMAKE_INSTALL_PREFIX="/usr/local/clamav"                                         \
+    -D CPACK_PACKAGING_INSTALL_PREFIX="/usr/local"                                      \
+    -D CMAKE_MODULE_PATH="$HOME/.mussels/install/host-static/lib/cmake"                 \
+    -D ENABLE_EXAMPLES=OFF                                                              \
+    -D JSONC_INCLUDE_DIR="$HOME/.mussels/install/host-static/include/json-c"            \
+    -D JSONC_LIBRARY="$HOME/.mussels/install/host-static/lib/libjson-c.a"               \
+    -D ENABLE_JSON_SHARED=OFF                                                           \
+    -D BZIP2_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"                   \
+    -D BZIP2_LIBRARY_RELEASE="$HOME/.mussels/install/host-static/lib/libbz2_static.a"   \
+    -D OPENSSL_ROOT_DIR="$HOME/.mussels/install/host-static"                            \
+    -D OPENSSL_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"                 \
+    -D OPENSSL_CRYPTO_LIBRARY="$HOME/.mussels/install/host-static/lib/libcrypto.a"      \
+    -D OPENSSL_SSL_LIBRARY="$HOME/.mussels/install/host-static/lib/libssl.a"            \
+    -D LIBXML2_INCLUDE_DIR="$HOME/.mussels/install/host-static/include/libxml2"         \
+    -D LIBXML2_LIBRARY="$HOME/.mussels/install/host-static/lib/libxml2.a"               \
+    -D PCRE2_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"                   \
+    -D PCRE2_LIBRARY="$HOME/.mussels/install/host-static/lib/libpcre2-8.a"              \
+    -D CURSES_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"                  \
+    -D CURSES_LIBRARY="$HOME/.mussels/install/host-static/lib/libncurses.a"             \
+    -D ZLIB_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"                    \
+    -D ZLIB_LIBRARY="$HOME/.mussels/install/host-static/lib/libz.a"                     \
+    -D LIBCHECK_INCLUDE_DIR="$HOME/.mussels/install/host-static/include"                \
+    -D LIBCHECK_LIBRARY="$HOME/.mussels/install/host-static/lib/libcheck.a"
+
+

Build:

+
cmake --build . --config RelWithDebInfo
+
+

It's a good idea to run the public test suite at this point:

+
ctest -C RelWithDebInfo -V
+
+

Now, to make the installer, just run:

+
cpack -C RelWithDebInfo
+
+

This will generate the PKG installer package.

+

Windows

+

For tips on installing development tools for Windows, see the development build instructions.

+

First, install Mussels:

+
python3 -m pip install --user mussels
+
+

Mussels also requires Git, so if you don't have that installed, install it now.

+
+

Tip: You may receive a warning that installed scripts are not in your PATH environment variable. I strongly recommend adding the Scripts directory described to your PATH. After which, you may run Mussels using msl on the command line instead of typing python3 -m mussels, which is exceedingly tedious ;-).

+
+

Now to use Mussels, run:

+
# This requires Git, and will clone the the "clamav" and "scrapbook" cookbooks.
+msl up
+
+# This is to enable running the scripts in the clamav cookbook to build the clamav dependencies.
+msl cookbook trust -y clamav
+
+# This is just to get you in a small directory so Mussels don't spend forever searching your harddrive for build recipes.
+mkdir tmp && cd tmp
+
+# First try a dry-run. If you're missing any tools, it will tell you.
+# If you have everything, it will give you a list of the order it plans to build everything.
+msl build clamav_deps --dry-run
+
+# Then do the build.
+msl build clamav_deps
+
+# By default, this build will be for x64*
+# If you want to build for x86, run:
+msl build -t x86 clamav_deps
+
+
+

*For Windows, our recipes only provide two build targets: x64 and x86. We don't as of yet have an x64-static variant for the recipes. You'll probably want x64, which is the default.

+
+

So assuming you've now build the clamav dependencies, you should be ready to build ClamAV.

+

In a Git clone of the clamav source (or the extracted source tarball from a release), create a build subdirectory and open a Powershell terminal in that build directory.

+

Run the following, replacing "2019" and "Community" with different versions or editions as needed to match your Visual Studio installation...

+

Configure (generate the build system):

+
pushd "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools"
+cmd /c "VsDevCmd.bat -arch=amd64 & set" |
+foreach {
+  if ($_ -match "=") {
+    $v = $_.split("="); set-item -force -path "ENV:\$($v[0])"  -value "$($v[1])"
+  }
+}
+popd
+Write-Host "`nVisual Studio Command Prompt variables set." -ForegroundColor Yellow
+
+cmake ..  -G Ninja -D CMAKE_BUILD_TYPE="RelWithDebInfo" `
+    -D ENABLE_EXAMPLES=OFF                                                   `
+    -D ENABLE_JSON_SHARED=OFF                                                `
+    -D JSONC_INCLUDE_DIR="$home\.mussels\install\x64\include\json-c"         `
+    -D JSONC_LIBRARY="$home\.mussels\install\x64\lib\json-c.lib"             `
+    -D BZIP2_INCLUDE_DIR="$home\.mussels\install\x64\include"                `
+    -D BZIP2_LIBRARY_RELEASE="$home\.mussels\install\x64\lib\libbz2.lib"     `
+    -D CURL_INCLUDE_DIR="$home\.mussels\install\x64\include"                 `
+    -D CURL_LIBRARY="$home\.mussels\install\x64\lib\libcurl_imp.lib"         `
+    -D OPENSSL_ROOT_DIR="$home\.mussels\install\x64"                         `
+    -D OPENSSL_INCLUDE_DIR="$home\.mussels\install\x64\include"              `
+    -D OPENSSL_CRYPTO_LIBRARY="$home\.mussels\install\x64\lib\libcrypto.lib" `
+    -D ZLIB_LIBRARY="$home\.mussels\install\x64\lib\libssl.lib"              `
+    -D LIBXML2_INCLUDE_DIR="$home\.mussels\install\x64\include\libxml2"      `
+    -D LIBXML2_LIBRARY="$home\.mussels\install\x64\lib\libxml2.lib"          `
+    -D PCRE2_INCLUDE_DIR="$home\.mussels\install\x64\include"                `
+    -D PCRE2_LIBRARY="$home\.mussels\install\x64\lib\pcre2-8.lib"            `
+    -D CURSES_INCLUDE_DIR="$home\.mussels\install\x64\include"               `
+    -D CURSES_LIBRARY="$home\.mussels\install\x64\lib\pdcurses.lib"          `
+    -D PThreadW32_INCLUDE_DIR="$home\.mussels\install\x64\include"           `
+    -D PThreadW32_LIBRARY="$home\.mussels\install\x64\lib\pthreadVC2.lib"    `
+    -D ZLIB_INCLUDE_DIR="$home\.mussels\install\x64\include"                 `
+    -D ZLIB_LIBRARY="$home\.mussels\install\x64\lib\zlibstatic.lib"          `
+    -D LIBCHECK_INCLUDE_DIR="$home\.mussels\install\x64\include"             `
+    -D LIBCHECK_LIBRARY="$home\.mussels\install\x64\lib\checkDynamic.lib"
+
+

Build:

+
ninja
+
+

It's a good idea to run the public test suite at this point:

+
ctest -C RelWithDebInfo -V
+
+

Now, to make the installer, just run:

+
cpack -C RelWithDebInfo
+
+

This will generate both the ZIP and the MSI installer packages.

+

Development Tips & Tricks

+

The following are a collection of tips that may help you be a more productive ClamAV developer.

+ +

Downloading the Official Ruleset

+

If you plan to use custom rules for testing, you can invoke clamscan via ./installed/bin/clamscan, specifying your custom rule files via -d parameters.

+

If you want to download the official ruleset to use with clamscan, do the following:

+
    +
  1. Run mkdir -p installed/share/clamav
  2. +
  3. Comment out line 8 of etc/freshclam.conf.sample
  4. +
  5. Run ./installed/bin/freshclam --config-file etc/freshclam.conf.sample
  6. +
+

General Debugging

+

NOTE: Some of the debugging/profiling tools mentioned in the sections below are specific to Linux

+

Useful clamscan Flags

+

The following are useful flags to include when debugging clamscan:

+
    +
  • +

    --debug --verbose: Print lots of helpful debug information

    +
  • +
  • +

    --gen-json: Print some additional debug information in a JSON format

    +
  • +
  • +

    --statistics=pcre --statistics=bytecode: Print execution statistics on any PCRE and bytecode rules that were evaluated

    +
  • +
  • +

    --dev-performance: Print per-file statistics regarding how long scanning took and the times spent in various scanning stages

    +
  • +
  • +

    --alert-broken: This will attempt to detect broken executable files. If an executable is determined to be broken, some functionality might not get invoked for the sample, and this could be an indication of an issue parsing the PE header or file. This causes those binary to generate an alert instead of just continuing on. This flag replaces the --detect-broken flag from releases prior to 0.101.

    +
  • +
  • +

    --max-filesize=2000M --max-scansize=2000M --max-files=2000000 --max-recursion=2000000 --max-embeddedpe=2000M --max-htmlnormalize=2000000 --max-htmlnotags=2000000 --max-scriptnormalize=2000000 --max-ziptypercg=2000000 --max-partitions=2000000 --max-iconspe=2000000 --max-rechwp3=2000000 --pcre-match-limit=2000000 --pcre-recmatch-limit=2000000 --pcre-max-filesize=2000M --max-scantime=2000000:

    +

    Effectively disables all file limits and maximums for scanning. This is useful if you'd like to ensure that all files in a set get scanned, and would prefer clam to just run slowly or crash rather than skip a file because it encounters one of these thresholds

    +
  • +
+

The following are useful flags to include when debugging rules that you're +writing:

+
    +
  • +

    -d: Allows you to specify a custom ClamAV rule file from the command line

    +
  • +
  • +

    --bytecode-unsigned: If you are testing custom bytecode rules, you'll need this flag so that clamscan actually runs the bytecode signature

    +
  • +
  • +

    --all-match: Allows multiple signatures to match on a file being scanned

    +
  • +
  • +

    --leave-temps --tmpdir=/tmp: By default, ClamAV will attempt to extract embedded files that it finds, normalize certain text files before looking for matches, and unpack packed executables that it has unpacking support for. These flags tell ClamAV to write these intermediate files out to the directory specified. Usually when a file is written, it will mention the file name in the --debug output, so you can have some idea at what stage in the scanning process a tmp file was created.

    +
  • +
  • +

    --dump-certs: For signed PE files that match a rule, display information about the certificates stored within the binary.

    +
    +

    Note: sigtool has this functionality as well and doesn't require a rule match to view the cert data

    +
    +
  • +
+

Using gdb

+

Given that you might want to pass a lot of arguments to gdb, consider taking advantage of the --args parameter. For example:

+
gdb --args ./installed/bin/clamscan -d /tmp/test.ldb -d /tmp/block_list.crb -d --dumpcerts --debug --verbose --max-filesize=2000M --max-scansize=2000M --max-files=2000000 --max-recursion=2000000 --max-embeddedpe=2000M --max-iconspe=2000000 f8f101166fec5785b4e240e4b9e748fb6c14fdc3cd7815d74205fc59ce121515
+
+

When using ClamAV without libclamav statically linked, if you set breakpoints on libclamav functions by name, you'll need to make sure to indicate that the breakpoints should be resolved after libraries have been loaded.

+

For other documentation about how to use gdb, check out the following resources:

+ +

Hunting for Memory Leaks

+

You can easily hunt for memory leaks with valgrind. Check out this guide to get started: Valgrind Quick Start

+

If checking for leaks, be sure to run clamscan with samples that will hit as many of the unique code paths in the code you are testing. An example invocation is as follows:

+
valgrind --leak-check=full ./installed/bin/clamscan -d /tmp/test.ldb --leave-temps --tempdir /tmp/test --debug --verbose /tmp/upx-samples/ > /tmp/upx-results-2.txt 2>&1
+
+

Alternatively, on Linux, you can use glibc's built-in leak checking functionality:

+
MALLOC_CHECK_=7 ./installed/bin/clamscan
+
+

See the mallopt man page for more details

+

Performance Profiling

+

Flame Graph Profiling

+

FlameGraph is a great tool for generating interactive flamegraphs based collected profiling data. The github page has thorough documentation on how to use the tool, but an overview is presented below:

+

First, install perf, which on Linux can be done via:

+
sudo apt-get install linux-tools-common linux-tools-generic linux-tools-`uname -r`
+
+
+

Tip: If you're on Windows using WSL2 with Ubuntu 20.04, you may find that the above fails with this error message:

+
E: Unable to locate package linux-tools-4.19.104-microsoft-standard
+E: Couldn't find any package by glob 'linux-tools-4.19.104-microsoft-standard'
+E: Couldn't find any package by regex 'linux-tools-4.19.104-microsoft-standard'
+
+

You may have luck building and installing perf yourself using Microsoft's WSL2 Linux Kernel sources. At the time of writing, this doesn't work with the master branch because the perf build appears to be broken with newer versions of glibc. Instead, we can use the linux-msft-wsl-5.10.16.3 tag:

+
sudo apt install flex bison
+git clone https://github.com/microsoft/WSL2-Linux-Kernel --depth 1 --branch linux-msft-wsl-5.10.16.3
+cd WSL2-Linux-Kernel/tools/perf
+make -j8
+sudo cp perf /usr/local/bin
+
+

Technique courtesy of: https://gist.github.com/abel0b/b1881e41b9e1c4b16d84e5e083c38a13

+
+

Modify the system settings to allow perf record to be run by a standard user:

+
sudo su     # Run the following as root
+cat /proc/sys/kernel/perf_event_paranoid
+echo "1" > /proc/sys/kernel/perf_event_paranoid
+exit
+
+

Invoke clamscan via perf record as follows, and run perf script to collect the profiling data. Note that in this example, ClamAV was compiled in a build subdirectory and installed to build/install:

+
perf record -F 100 -g -- ./install/bin/clamscan -d ./unit_tests/clamav.hdb --allmatch ./test/
+perf script > /tmp/out.perf
+
+

The -F parameter indicates how many samples should be collected during program execution. If your scan will take a long time to run, a lower value should be sufficient. Otherwise, consider choosing a higher value (on Ubuntu 18.04, 7250 is the max frequency, but it can be increased via /proc/sys/kernel/perf_event_max_sample_rate.

+

Clone out the FlameGraph project and run the following commands inside the FlameGraph directory to generate the flame graph:

+
git clone https://github.com/brendangregg/FlameGraph.git
+cd FlameGraph
+
+perl stackcollapse-perf.pl /tmp/out.perf > /tmp/out.folded
+perl flamegraph.pl /tmp/out.folded > /tmp/test.svg
+
+

The SVG that is generated is interactive, but some viewers don't support this. +Be sure to open it in a web browser like Chrome to be able to take full advantage of it.

+

Here's an example flamegraph generated by scanning ClamAV test files:

+

+ + Example Flamegraph + +

+

Call Graph Profiling - Callgrind

+

Callgrind is a profiling tool included with valgrind. This can be done by prepending valgrind --tool=callgrind to the clamscan command.

+

kcachegrind is a follow-on tool that will graphically present the profiling data and allow you to explore it visually, although if you don't already use KDE you'll have to install lots of extra packages to use it.

+

System Call Tracing / Fault Injection

+

strace can be used to track the system calls that are performed and provide the number of calls / time spent in each system call. This can be done by prepending strace -c to a clamscan command. Results will look something like this:

+
    % time     seconds  usecs/call     calls    errors syscall
+    ------ ----------- ----------- --------- --------- ----------------
+    95.04    0.831430          13     62518           read
+    3.22    0.028172          14      2053           munmap
+    0.69    0.006005           3      2102           mmap
+    0.28    0.002420           7       344           pread64
+    0.16    0.001415           5       305         1 openat
+    0.13    0.001108           3       405           write
+    0.11    0.000932          23        40           mprotect
+    0.07    0.000632           2       310           close
+    0.07    0.000583           9        67        30 access
+    0.05    0.000395           1       444           lseek
+    0.04    0.000344           2       162           fstat
+    0.04    0.000338           1       253           brk
+    0.03    0.000262           1       422           fcntl
+    0.02    0.000218          16        14           futex
+    0.01    0.000119           1       212           getpid
+    0.01    0.000086          14         6           getdents
+    0.00    0.000043           7         6           dup
+    0.00    0.000040           1        31           unlink
+    0.00    0.000038          19         2           rt_sigaction
+    0.00    0.000037          19         2           rt_sigprocmask
+    0.00    0.000029           1        37           stat
+    0.00    0.000022          11         2           prlimit64
+    0.00    0.000021          21         1           sysinfo
+    0.00    0.000020           1        33           clock_gettime
+    0.00    0.000019          19         1           arch_prctl
+    0.00    0.000018          18         1           set_tid_address
+    0.00    0.000018          18         1           set_robust_list
+    0.00    0.000013           0        60           lstat
+    0.00    0.000011           0        65           madvise
+    0.00    0.000002           0        68           geteuid
+    0.00    0.000000           0         1           execve
+    0.00    0.000000           0         1           uname
+    0.00    0.000000           0         1           getcwd
+    ------ ----------- ----------- --------- --------- ----------------
+    100.00    0.874790                 69970        31 total
+
+

strace can also be used for cool things like system call fault injection. For instance, let's say you are curious whether the read bytecode API call is implemented in such a way that the underlying read system call could handle EINTR being returned (which can happen periodically). To test this, write the following bytecode rule:

+
    VIRUSNAME_PREFIX("BC.Heuristic.Test.Read.Passed")
+    VIRUSNAMES("")
+    TARGET(0)
+
+    SIGNATURES_DECL_BEGIN
+    DECLARE_SIGNATURE(zeroes)
+    SIGNATURES_DECL_END
+
+    SIGNATURES_DEF_BEGIN
+    DEFINE_SIGNATURE(zeroes, "0:0000")
+    SIGNATURES_DEF_END
+
+    bool logical_trigger()
+    {
+        return matches(Signatures.zeroes);
+    }
+
+    #define READ_S(value, size) if (read(value, size) != size) return 0;
+
+    int entrypoint(void)
+    {
+        char buffer[65536];
+        int i;
+
+        for (i = 0; i < 256; i++)
+        {
+            debug(i);
+            debug("\n");
+            READ_S(buffer, sizeof(buffer));
+        }
+
+        foundVirus("");
+        return 0;
+    }
+
+

Compiled the rule, and make a test file to match against it. Then run it under strace to determine what underlying read system call is being used for the bytecode read function:

+
clambc-compiler read_test.bc
+dd if=/dev/zero of=/tmp/zeroes bs=65535 count=256
+strace clamscan -d read_test.cbc --bytecode-unsigned /tmp/zeroes
+
+

It uses pread64 under the hood, so the following command could be used for fault injection:

+
strace -e fault=pread64:error=EINTR:when=20+10 clamscan -d read_test.cbc --bytecode-unsigned /tmp/zeroes
+
+

This command tells strace to skip the first 20 pread64 calls (these appear to be used by the loader, which didn't seem to handle EINTR correctly) but to inject EINTR for every 10th call afterward. We can see the injection in action and that the system call is retried successfully:

+
    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15007744) = 65536
+    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15073280) = 65536
+    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15138816) = 65536
+    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15204352) = 65536
+    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15269888) = 65536
+    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15335424) = 65536
+    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15400960) = 65536
+    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15466496) = 65536
+    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15532032) = 65536
+    pread64(3, 0x7f6a7ff43000, 65536, 15597568) = -1 EINTR (Interrupted system call) (INJECTED)
+    pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 15597568) = 65536
+
+

More documentation on using strace to perform system call fault injection, see this presentation from FOSDEM 2017.

+

Computing Code Coverage

+

Code coverage when using CMake (v0.104 and newer)

+
+

TO-DO: Help us figure out how to compute code coverage and document the process here.

+
+

Code coverage when using Autotools (v0.103 and older)

+

gcov/lcov can be used to produce a code coverage report indicating which lines of code were executed on a single run or by multiple runs of clamscan. NOTE: for these metrics to be collected, ClamAV needs to have been configured with the --enable-coverage option.

+

First, run the following to zero out all of the performance metrics:

+
lcov -z --directory . --output-file coverage.lcov.data
+
+

Next, run ClamAV through whatever test cases you have. Then, run lcov again to collect the coverage data as follows:

+
lcov -c --directory . --output-file coverage.lcov.data
+
+

Finally, run the genhtml tool that ships with lcov to produce the code coverage report:

+
genhtml coverage.lcov.data --output-directory report
+
+

The report directory will have an index.html page which can be loaded into any web browser.

+

For more information, visit the lcov webpage

+

Building and Testing ClamAV with Fuzzing Sanitizers

+

Build & Reproduce Fuzz Reports using OSS-Fuzz Tools

+

Check out the google/oss-fuzz repository and the clamav repo side by side. .

+

Then inside the oss-fuzz directory, run:

+
python3 infra/helper.py build_image clamav
+python3 infra/helper.py build_fuzzers --sanitizer address clamav ../clamav
+python3 infra/helper.py reproduce clamav clamav_scanmap_fuzzer /path/to/poc_file
+
+
+

Tip: You can substitute address argument in the second command that selects the Address Sanitizer (ASan) with memory or undefined to use the Memory Sanitizer (MSan) or the Undefined Behavior Sanitizer (UBSan).

+
+

See https://google.github.io/oss-fuzz/advanced-topics/reproducing/ for more details.

+

Build & Test Fuzz Targets Sanitizers in CMake (v0.104 and newer)

+

Inside your clamav git clone, run:

+
mkdir build-fuzz && cd build-fuzz
+
+export CC=`which clang` && \
+    export CXX=`which clang++` && \
+    export SANITIZER=address && \
+    cmake .. -G Ninja \
+        -D OPTIMIZE=OFF \
+        -D CMAKE_BUILD_TYPE="Debug" \
+        -D ENABLE_FUZZ=ON \
+    && \
+    ninja
+
+

This will build the fuzz target executables in the build-fuzz/fuzz/ directory. You can run them just like a regular program, passing the file as input.

+

E.g.:

+
./fuzz/clamav_scanfile_fuzzer /path/to/poc_file
+
+

ClamAV with Address Sanitizer (ASAN) in Autotools (v0.103 and older)

+

Building ClamAV with ASAN support can be extremely useful in detecting memory corruption and memory leaks. To build with ASAN, use a ..\configure line like the following:

+
CFLAGS="-ggdb -O0 -fsanitize=address -fno-omit-frame-pointer" LDFLAGS="-fsanitize=address" CXXFLAGS="-ggdb -O0 -fsanitize=address -fno-omit-frame-pointer" OBJCFLAGS="-ggdb -O0 -fsanitize=address -fno-omit-frame-pointer" ../configure --prefix=`pwd`/../installed --enable-debug --enable-libjson --with-systemdsystemunitdir=no --enable-experimental --enable-clamdtop --enable-libjson --enable-xml --enable-pcre --disable-llvm
+
+

Libclamav

+

Libclamav provides an easy and effective way to add a virus protection into your software. The library is thread-safe and transparently recognizes and scans within archives, mail files, MS Office document files, executables and other special formats.

+

License

+

Libclamav is licensed under the GNU GPL v2 license. This means you are not allowed to link commercial, closed-source software against it. All software using libclamav must be GPL compliant.

+

Supported formats and features

+

Executables

+

The library has a built-in support for 32- and 64-bit Portable Executable, ELF and Mach-O files. Additionally, it can handle PE files compressed or obfuscated with the following tools:

+
    +
  • Aspack (2.12)
  • +
  • UPX +
      +
    • PE (Windows) built-in
    • +
    • ELF, Mach-O enabled via bytecode signatures
    • +
    +
  • +
  • FSG (1.3, 1.31, 1.33, 2.0)
  • +
  • Petite (2.x)
  • +
  • PeSpin (1.1)
  • +
  • NsPack
  • +
  • wwpack32 (1.20)
  • +
  • MEW
  • +
  • Upack
  • +
  • Y0da Cryptor (1.3)
  • +
+

Mail files

+

Libclamav can handle almost every mail file format including TNEF (winmail.dat) attachments.

+

Archives and compressed files

+

The following archive and compression formats are supported by internal handlers:

+
    +
  • Zip (+ SFX)
  • +
  • RAR (+ SFX)
  • +
  • 7Zip
  • +
  • Tar
  • +
  • CPIO
  • +
  • Gzip
  • +
  • Bzip2
  • +
  • DMG
  • +
  • IMG
  • +
  • ISO 9660
  • +
  • PKG
  • +
  • HFS+ partition
  • +
  • HFSX partition
  • +
  • APM disk image
  • +
  • GPT disk image
  • +
  • MBR disk image
  • +
  • XAR
  • +
  • XZ
  • +
  • MS OLE2
  • +
  • MS Cabinet Files (+ SFX)
  • +
  • MS CHM (Compiled HTML)
  • +
  • MS SZDD compression format
  • +
  • BinHex
  • +
  • SIS (SymbianOS packages)
  • +
  • AutoIt
  • +
  • NSIS
  • +
  • InstallShield
  • +
+

Documents

+

The most popular file formats are supported:

+
    +
  • MS Office and MacOffice files
  • +
  • RTF
  • +
  • PDF
  • +
  • HTML
  • +
+

In the case of Office, RTF and PDF files, libclamav will only extract the embedded objects and will not decode the text data itself. The text decoding and normalization is only performed for HTML files.

+

Data Loss Prevention

+

Libclamav includes a DLP module which can detect the following credit card issuers: AMEX, VISA, MasterCard, Discover, Diner’s Club, and JCB and U.S. social security numbers inside text files.

+

Future versions of Libclamav may include additional features to detect other credit cards and other forms of PII (Personally Identifiable Information) which may be transmitted without the benefit of being encrypted.

+

Others

+

Libclamav can handle various obfuscators, encoders, files vulnerable to security risks such as:

+
    +
  • JPEG (exploit detection)
  • +
  • RIFF (exploit detection)
  • +
  • uuencode
  • +
  • ScrEnc obfuscation
  • +
  • CryptFF
  • +
+

API

+

Header file

+

Every program using libclamav must include the header file clamav.h:

+
    #include "clamav.h"
+
+

Initialization

+

Before using libclamav, you should call cl_init() to initialize it. CL_INIT_DEFAULT is a macro that can be passed to cl_init() representing the default initialization settings. When it’s done, you’re ready to create a new scan engine by calling cl_engine_new(). To free resources allocated by the engine use cl_engine_free(). Function prototypes:

+
    int cl_init(unsigned int options);
+    struct cl_engine *cl_engine_new(void);
+    int cl_engine_free(struct cl_engine *engine);
+
+

cl_init() and cl_engine_free() return CL_SUCCESS on success or another code on error. cl_engine_new() return a pointer or NULL if there’s not enough memory to allocate a new engine structure.

+

Database loading

+

The following set of functions provides an interface for loading the virus database:

+
    const char *cl_retdbdir(void);
+
+    int cl_load(const char *path, struct cl_engine *engine,
+            unsigned int *signo, unsigned int options);
+
+

cl_retdbdir() returns the default (hardcoded) path to the directory with ClamAV databases. cl_load() loads a single database file or all databases from a given directory (when path points to a directory). The second argument is used for passing in the pointer to the engine that should be previously allocated with cl_engine_new(). A number of loaded signatures will be added to signo. The last argument can pass the following flags:

+
    +
  • CL_DB_STDOPT +This is an alias for a recommended set of scan options.
  • +
  • CL_DB_PHISHING +Load phishing signatures.
  • +
  • CL_DB_PHISHING_URLS +Initialize the phishing detection module and load .wdb and .pdb +files.
  • +
  • CL_DB_PUA +Load signatures for Potentially Unwanted Applications.
  • +
  • CL_DB_OFFICIAL_ONLY +Only load official signatures from digitally signed databases.
  • +
  • CL_DB_BYTECODE +Load bytecode.
  • +
+

cl_load() returns CL_SUCCESS on success and another code on failure.

+
        ...
+        struct cl_engine *engine;
+        unsigned int sigs = 0;
+        int ret;
+
+    if((ret = cl_init(CL_INIT_DEFAULT)) != CL_SUCCESS) {
+        printf("cl_init() error: %s\n", cl_strerror(ret));
+        return 1;
+    }
+
+    if(!(engine = cl_engine_new())) {
+        printf("Can't create new engine\n");
+        return 1;
+    }
+
+    ret = cl_load(cl_retdbdir(), engine, &sigs, CL_DB_STDOPT);
+
+

Database verification

+

The cl_load() API will verify that the database is signed and is correct, although it will also return CL_SUCCESS for non-database files that of course cannot be loaded.

+

You can, however, use the cl_cvdverify() API to verify a database directly:

+
/**
+ * @brief Verify a CVD file by loading and unloading it.
+ *
+ * @param file          Filepath of CVD file.
+ * @return cl_error_t   CL_SUCCESS if success, else a CL_E* error code.
+ */
+extern cl_error_t cl_cvdverify(const char *file);
+
+

As the comment block explains, this will load-test the database. Be advised that for some larger databases, this may use a fair bit system RAM.

+

Error handling

+

Use cl_strerror() to convert error codes into human readable messages. The function returns a statically allocated string:

+
    if(ret != CL_SUCCESS) {
+        printf("cl_load() error: %s\n", cl_strerror(ret));
+        cl_engine_free(engine);
+        return 1;
+    }
+
+

Engine structure

+

When all required databases are loaded you should prepare the detection engine by calling cl_engine_compile(). In case of failure you should still free the memory allocated to the engine with cl_engine_free():

+
    int cl_engine_compile(struct cl_engine *engine);
+
+

In our example:

+
    if((ret = cl_engine_compile(engine)) != CL_SUCCESS) {
+        printf("cl_engine_compile() error: %s\n", cl_strerror(ret));
+        cl_engine_free(engine);
+        return 1;
+    }
+
+

Limits

+

When you create a new engine with cl_engine_new(), it will have all internal settings set to default values as recommended by the ClamAV authors. It’s possible to check and modify the values (numerical and strings) using the following set of functions:

+
    int cl_engine_set_num(struct cl_engine *engine,
+    enum cl_engine_field field, long long num);
+
+    long long cl_engine_get_num(const struct cl_engine *engine,
+    enum cl_engine_field field, int *err);
+
+    int cl_engine_set_str(struct cl_engine *engine,
+    enum cl_engine_field field, const char *str);
+
+    const char *cl_engine_get_str(const struct cl_engine *engine,
+    enum cl_engine_field field, int *err);
+
+

Please don’t modify the default values unless you know what you’re doing. Refer to the ClamAV sources (clamscan, clamd) for examples.

+

Database checks

+

It’s very important to keep the internal instance of the database up to date. You can watch database changes with the cl_stat..() family of functions.

+
    int cl_statinidir(const char *dirname, struct cl_stat *dbstat);
+    int cl_statchkdir(const struct cl_stat *dbstat);
+    int cl_statfree(struct cl_stat *dbstat);
+
+

Initialization:

+
        ...
+        struct cl_stat dbstat;
+
+    memset(&dbstat, 0, sizeof(struct cl_stat));
+    cl_statinidir(dbdir, &dbstat);
+
+

To check for a change you just need to call cl_statchkdir and check its return value (0 - no change, 1 - some change occurred). Remember to reset the cl_stat structure after reloading the database.

+
    if(cl_statchkdir(&dbstat) == 1) {
+        reload_database...;
+        cl_statfree(&dbstat);
+        cl_statinidir(cl_retdbdir(), &dbstat);
+    }
+
+

Libclamav includes and additional call to check the number of signatures that can be loaded from a given directory:

+
    int cl_countsigs(const char *path, unsigned int countoptions,
+        unsigned int *sigs);
+
+

The first argument points to the database directory, the second one specifies what signatures should be counted: CL_COUNTSIGS_OFFICIAL (official signatures), CL_COUNTSIGS_UNOFFICIAL (third party signatures), CL_COUNTSIGS_ALL (all signatures). The last argument points to the counter to which the number of detected signatures will be added (therefore the counter should be initially set to 0). The call returns CL_SUCCESS or an error code.

+

Data scan functions

+

It’s possible to scan a file or descriptor using:

+
    int cl_scanfile(
+        const char *filename,
+        const char **virname,
+        unsigned long int *scanned,
+        const struct cl_engine *engine,
+        struct cl_scan_options *options);
+
+    int cl_scandesc(
+        int desc,
+        const char *filename,
+        const char **virname,
+        unsigned long int *scanned,
+        const struct cl_engine *engine,
+        struct cl_scan_options *options);
+
+

Both functions will store a virus name under the pointer virname, the virus name is part of the engine structure and must not be released directly. If the third argument (scanned) is not NULL, the functions will increase its value with the size of scanned data (in CL_COUNT_PRECISION units). The last argument (options) requires a pointer to a data structure that specifies the scan options. The data structure should be memset() Each variable in the structure is a bit-flag field. The structure definition is:

+
    struct cl_scan_options {
+        uint32_t general;
+        uint32_t parse;
+        uint32_t alert;
+        uint32_t heuristic_alert;
+        uint32_t mail;
+        uint32_t dev;
+    };
+
+

Supported flags for each of the fields are as follows:

+

general - General scanning options.

+
    +
  • CL_SCAN_GENERAL_ALLMATCHES +Scan in all-match mode
  • +
  • CL_SCAN_GENERAL_COLLECT_METADATA +Collect metadata (--gen-json)
  • +
  • CL_SCAN_GENERAL_HEURISTICS +Option to enable heuristic alerts. Required for any of the heuristic alerting options to work.
  • +
+

parse - Options to enable/disable specific parsing capabilities. Generally you will want to enable all parsers. The easiest way to do this is to set the parse flags to ~0.

+
    +
  • CL_SCAN_PARSE_ARCHIVE +This flag enables transparent scanning of various archive formats.
  • +
  • CL_SCAN_PARSE_ELF +Enable support for ELF files.
  • +
  • CL_SCAN_PARSE_PDF +Enables scanning within PDF files.
  • +
  • CL_SCAN_PARSE_SWF +Enables scanning within SWF files, notably compressed SWF.
  • +
  • CL_SCAN_PARSE_HWP +Enables scanning of Hangul Word Processor (HWP) files.
  • +
  • CL_SCAN_PARSE_XMLDOCS +Enables scanning of XML-formatted documents (e.g. Word, Excel, PowerPoint, HWP).
  • +
  • CL_SCAN_PARSE_MAIL +Enable support for mail files.
  • +
  • CL_SCAN_PARSE_OLE2 +Enables support for OLE2 containers (used by MS Office and .msi files).
  • +
  • CL_SCAN_PARSE_HTML +This flag enables HTML normalization (including ScrEnc decryption).
  • +
  • CL_SCAN_PARSE_PE +This flag enables deep scanning of Portable Executable files and allows libclamav to unpack executables compressed with run-time unpackers.
  • +
+

heuristic - Options to enable specific heuristic alerts

+
    +
  • CL_SCAN_GENERAL_HEURISTIC_PRECEDENCE +Allow heuristic match to take precedence. When enabled, if a heuristic scan (such as phishingScan) detects a possible virus/phish it will stop scan immediately. Recommended, saves CPU scan-time. When disabled, virus/phish detected by heuristic scans will be reported only at the end of a scan. If an archive contains both a heuristically detected virus/phishing, and a real malware, the real malware will be reported.
  • +
  • CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE +With this flag the library will mark encrypted archives as viruses (encrypted .zip, .7zip, .rar).
  • +
  • CL_SCAN_HEURISTIC_ENCRYPTED_DOC +With this flag the library will mark encrypted documents as viruses (encrypted .pdf).
  • +
  • CL_SCAN_HEURISTIC_BROKEN +libclamav will try to detect broken executables and mark them as Broken.Executable.
  • +
  • CL_SCAN_HEURISTIC_EXCEEDS_MAX +Alert when the scan of any file exceeds maximums such as max-filesize, max-scansize, max-recursion level.
  • +
  • CL_SCAN_HEURISTIC_PHISHING_SSL_MISMATCH +Heuristic for phishing module: alert on SSL mismatches in URLs.
  • +
  • CL_SCAN_HEURISTIC_PHISHING_CLOAK +Heuristic for phishing module: alert on cloaked URLs.
  • +
  • CL_SCAN_HEURISTIC_MACROS +OLE2 containers, which contain VBA macros will be marked infected (Heuristics.OLE2.ContainsMacros).
  • +
  • CL_SCAN_HEURISTIC_PARTITION_INTXN +alert if partition table size doesn't make sense
  • +
  • CL_SCAN_HEURISTIC_STRUCTURED +Enable the data loss prevention (DLP) module which scans for credit card and SSN numbers. i.e. alert when detecting personal information
  • +
  • CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL +Search for [and alert when detecting] SSNs formatted as xx-yy-zzzz.
  • +
  • CL_SCAN_HEURISTIC_STRUCTURED_SSN_STRIPPED +Search for [and alert when detecting] SSNs formatted as xxyyzzzz.
  • +
+

mail - Options to enable specific mail parsing features

+
    +
  • CL_SCAN_MAIL_PARTIAL_MESSAGE +Scan RFC1341 messages split over many emails. You will need to periodically clean up $TemporaryDirectory/clamav-partial directory.
  • +
+

dev - Options designed for use by ClamAV developers

+
    +
  • CL_SCAN_DEV_COLLECT_SHA +Enables hash output in sha-collect builds - for internal use only
  • +
  • CL_SCAN_DEV_COLLECT_PERFORMANCE_INFO +Collect performance timings
  • +
+

All functions return CL_CLEAN when the file seems clean, CL_VIRUS when a virus is detected and another value on failure.

+
        ...
+        const char *virname;
+
+    if((ret = cl_scanfile("/tmp/test.exe", &virname, NULL, engine,
+    &options)) == CL_VIRUS) {
+        printf("Virus detected: %s\n", virname);
+    } else {
+        printf("No virus detected.\n");
+        if(ret != CL_CLEAN)
+            printf("Error: %s\n", cl_strerror(ret));
+    }
+
+

Memory

+

Because the engine structure occupies a few megabytes of system memory, you should release it with cl_engine_free() if you no longer need to scan files.

+

Forking daemons

+

If you’re using libclamav with a forking daemon you should call srand() inside a forked child before making any calls to the libclamav functions. This will avoid possible collisions with temporary filenames created by other processes of the daemon. This procedure is not required for multi-threaded daemons.

+

clamav-config

+

Use clamav-config to check compilation information for libclamav.

+
    $ clamav-config --libs
+    -L/usr/local/lib -lz -lbz2 -lgmp -lpthread
+
+    $ clamav-config --cflags
+    -I/usr/local/include -g -O2
+
+

Example

+

You will find an example scanner application in the clamav source under ./examples.

+

In ClamaV 0.104+, you can build the example programs alongside ClamAV by configuring with -D ENABLE_EXAMPLES=ON.

+

Or, if you have ClamAV already installed, execute the following to compile it:

+
gcc -Wall ex1.c -o ex1 -lclamav
+
+

CVD format

+

CVD (ClamAV Virus Database) is a digitally signed tarball containing one or more databases. The header is a 512-bytes long string with colon separated fields:

+
    ClamAV-VDB:build time:version:number of signatures:functionality
+    level required:MD5 checksum:digital signature:builder name:build time (sec)
+
+

sigtool --info displays detailed information on CVD files:

+
    $ sigtool -i daily.cvd
+    File: daily.cvd
+    Build time: 10 Mar 2008 10:45 +0000
+    Version: 6191
+    Signatures: 59084
+    Functionality level: 26
+    Builder: ccordes
+    MD5: 6e6e29dae36b4b7315932c921e568330
+    Digital signature: zz9irc9irupR3z7yX6J+OR6XdFPUat4HIM9ERn3kAcOWpcMFxq
+    Fs4toG5WJsHda0Jj92IUusZ7wAgYjpai1Nr+jFfXHsJxv0dBkS5/XWMntj0T1ctNgqmiF
+    +RLU6V0VeTl4Oej3Aya0cVpd9K4XXevEO2eTTvzWNCAq0ZzWNdjc
+    Verification OK.
+
+

Project Ideas

+

For ClamAV library & application projects, submit pull-requests to: https://github.com/Cisco-Talos/clamav

+

For ClamAV documentation projects, submit pull-requests to: https://github.com/Cisco-Talos/clamav-faq/pulls

+
+

Tip: If you find that any of the bugs or projects have already been completed, you can help out simply by updating the list in a pull-request to update this document.

+
+ +

Bugs

+

There's only so much our core dev team can schedule into each release. Many bugs probably won't be fixed without your help! Feel free to troll our open Bugzilla tickets and our open GitHub Issues if you're looking for project ideas!

+

Larger Projects

+

The following are a list of project ideas for someone looking to work on a larger project. +Any projects labeled "Risky" or "Exploratory" are thought to be more likely to fail, or to have significant drawbacks that will result in the new feature being ultimately rejected.

+

Please don't take it personally if the ClamAV team decide not to merge your implementation due to perceived complexity, stability, or other such concerns.

+

Contributors are expected to implement ample documentation for any new code or feature. Directions on how to test the contribution as well as unit and/or system tests will significantly help with PR review and will improve the likelihood that your contribution will be accepted.

+

Unstable or incomplete work is not likely to be accepted. The core development team has a long backlog of tasks and a curated roadmap for the next 6-12 months and will not have time to complete an unfinished project for you.

+

Contributors submitting a sizeable new feature will be asked to sign a Contributors License Agreement (CLA) before the contribution can be accepted.

+

CMake: -D MAINTAINER_MODE=ON

+

The purpose of "maintainer" build-mode is to update source generated by tools like Flex, Bison, and GPerf which are not readily accessible on every platform.

+

In this case, the project is to add GNU gperf support to the our CMake build system's Maintainer-Mode (-D MAINTAINER_MODE=ON). To complete this task, you'll need to detect GPerf when using Maintainer-Mode, and it should be required. When the build runs, it should regenerate and overwrite the libclamav/jsparse/generated files in the source directory using gperf with jsparse-keywords.gperf.

+

The contributor should add the new option to CMakeOptions.cmake and document the feature in INSTALL.cmake.md as well as in the clamav-faq repo's development.md developer documentation, after the feature has merged.

+

Category: Low-hanging fruit, Development

+

What you will learn from this project:

+
    +
  • CMake C/C++ build system skills
  • +
+

Required skills:

+
    +
  • Linux/Unix familiarity. Familiarity with compiling C/C++ projects.
  • +
+

Project Size: Small

+

CMake: -D CODE_COVERAGE=ON

+

Add a -D CODE_COVERAGE=ON option to the CMake build system which will build ClamAV with code coverage features enabled.

+

An ideal solution would support code coverage in when using GCC, Clang, and MSVC.

+

See development.md in the clamav-faq repo for additional insight on how gcov, lcov, and genhtml can be used today with the Autotools build system.

+

The contributor should add the new option to CMakeOptions.cmake and document the feature in INSTALL.cmake.md as well as in the clamav-faq repo's development.md developer documentation, after the feature has merged.

+

Category: Low-hanging fruit, Development

+

What you will learn from this project:

+
    +
  • CMake C/C++ build system skills
  • +
  • Familiarity with C/C++ code coverage
  • +
+

Required skills:

+
    +
  • Linux/Unix familiarity. Familiarity with compiling C/C++ projects.
  • +
+

Project Size: Small

+

Develop New Detection Capabilities for PE/ELF/MachO Executables

+

ClamAV parses the PE/ELF/MachO headers on executables that it scans, but doesn't make all of the data that it extracts available for use by NDB/LDB signatures. Some features that would be great to have include:

+
    +
  • The ability to distinguish between regular executables and DLLs/SOs/DYLIBs (add new keywords?)
  • +
  • Subsignature modifiers that can limit subsigs to only being evaluated against sections with memory permission flags (Read/Write/Execute). This would allow signatures to be evaluated more efficiently and also would decrease the chance of signature false positives.
  • +
  • Parsing digital signatures in signed MachO exes and evaluating against the certificate trust / block .crb rules
  • +
  • Other features that might be helpful?
  • +
+

As PE, ELF, and MachO parsing features already exist in C, C is the mostly likely language of choice. However any major new self contained code would ideally be written in Rust.

+

Category: Core Development

+

What you will learn from this project:

+
    +
  • The PE, ELF, and MachO file formats
  • +
  • How ClamAV parses executable headers, performs signature matching, and the capabilities are provided
  • +
  • How to write ClamAV signatures to match on executable files
  • +
+

Required skills:

+
    +
  • Strong C development experience
  • +
  • Rust development experince (as needed)
  • +
+

Project Size: Large

+

Develop Memory Scanning Capabilities for Unix

+

Today, ClamAV works by scanning files on disk for malware. It'd be great if ClamAV could also be used to scan process memory on a system its running on in order to detect malware that isn't present on disk.

+

The ClamAV team is already looking into integrating such a feature from clamav-win32, a project by Gianluigi Tiesi who has graciously agreed to allow us to include this memory scanning feature and others in the upstream clamav project.

+

This project would be to develop a similar capability for use on Linux and/or macOS and/or BSD Unix scanning clients.

+

As this is a relatively large new feature, an ideal solution would be written in Rust.

+

Category: Fun/Peripheral

+

What you will learn from this project:

+
    +
  • The techniques and OS APIs related to inspecting the memory of running processes
  • +
  • The security mechanisms in place to limit arbitrary access to process memory
  • +
+

Required skills:

+
    +
  • Strong Rust development experience.
  • +
  • Linux/Unix operating systems experience.
  • +
+

Project Size: Large

+

WebAssembly Runtime

+

Background: ClamAV has for a long time had runtime support for running portable plugins we call "bytecode signatures". ClamAV has a custom bytecode compiler to compile these plugins from a C-like language and uses LLVM or a homegrown "bytecode interpreter" to run the plugins. This solution is strikingly similar to a newer portable plugin technology: WebAssembly!

+

The goal of project would be to create a proof-of-concept WebAssembly (wasm) runtime in ClamAV so that "wasm signatures" could be written in Rust and executed in a wasm sandbox. As with our current bytecode signature technology, the wasm signatures would run at specific hooks in the ClamAV scanning process. They would need access to the file map (buffer) being scanned, and would be given a limited API to call into ClamAV functions.

+

For a proof-of-concept, executing a local wasm plugin that has access to the file being scanned (without copying the data) would be fine. A production solution would need to convert the wasm plugin to an ascii-text encoding so it can be distributed much the same way the current bytecode signature .cbc plugins are distributed. As with the bytecode signatures, clamscan and clamd must not load the plugins unless they've been digitally signed or the --bytecode-unsigned/BytecodeUnsigned options are set, which would disable this safety precaution.

+
+

Important Notes: The ClamAV bytecode compiler project is currently undergoing a major re-write. Once complete, the new bytecode compiler will effectively be a Python script that invokes clang with a collection of custom compiler passes that effectively compile C code into ClamAV-bytecode plugins. This project would have you extend that project to instead use rustc to compile Rust ClamAV-WASM plugins.

+
+

Category: Core Development, Fun

+

What you will learn from this project:

+
    +
  • Compilers
  • +
  • LLVM, WebAssembly JIT
  • +
  • Executable plugin sandboxing
  • +
  • Rust
  • +
+

Required skills:

+
    +
  • C/C++ development experience.
  • +
  • Rust development experience.
  • +
+

Project Size: Large

+

Add Unpacking Support for New Packers

+

ClamAV includes support for unpacking executables generated by several software packers so that malware can't use them to easily evade detection. The list of packers currently supported can be found in the Introduction of the ClamAV Manual. There are many packers out there, though, so there is always a need to write unpacking code for ones that are frequently used by malware authors. Some that are currently needed include:

+
    +
  • UPX for ELF
  • +
  • MPRESS (although we do have some bytecode signatures for MPRESS - those might be sufficient)
  • +
  • If anyone is interested in this, we can analyze thousands of samples and identify more candidates for this list
  • +
+

Improvements to existing executable (PE/ELF/MachO) parsing code would likely be in C, but any new standalone modules would ideally be written in Rust.

+

Category: Fun/Peripheral

+

What you will learn from this project:

+
    +
  • How packers function, the steps involved in run-time loading and fixing memory maps, and a general approach to unpacking
  • +
  • You'll gain experience reverse-engineering real-world malware
  • +
+

Required skills:

+
    +
  • C development experience.
  • +
  • Rust development experience.
  • +
+

Project Size: Large

+

Add Support for Matching on .NET Internals

+

YARA extracts certain properties of .NET executables and makes them available for signatures to use for detection: https://yara.readthedocs.io/en/v3.6.0/modules/dotnet.html

+

Can ClamAV do something similar? For instance, extract the GUIDs and allow matching on those the way we do entries in the PE VersionInfo section?

+
+

Tip: An ideal solution for this and any new file parsing feature should be written in Rust and called by our existing C code.

+
+

Category: Fun/Peripheral

+

What you will learn from this project:

+
    +
  • How .NET executables are structured, and how they work internally
  • +
  • How to write .NET applications (for testing)
  • +
  • You'll also test your code against real-world malware, and perform reverse-engineering of samples as needed (if they break your code).
  • +
+

Required skills:

+
    +
  • C development experience.
  • +
  • Rust development experience.
  • +
  • Any prior experience in the areas listed above is a plus.
  • +
+

Project Size: Large

+

Extract Macros from OXML docs

+

ClamAV and SigTool currently support parsing OLE Office files to decompress and extract macros for scanning. The newer version OOXML Office files do not have this support, resulting in detection possible for macros in these documents. The ability to both extract and scan macros would enable better coverage. This might mean creating a new target type to prevent creating two signatures one for OLE macros and another for OOXML macros.

+
+

Tip: An ideal solution for this and any new file parsing feature should be written in Rust and called by our existing C code.

+
+

Category:

+

What you will learn from this project:

+
    +
  • ClamAV and SigTool internals
  • +
  • Office document macro compression (RLE compression)
  • +
  • Macro storage in OOXML files
  • +
+

Required skills:

+
    +
  • C development experience.
  • +
  • Rust development experience.
  • +
  • Any prior experience in the areas listed above is a plus.
  • +
+

Project Size: Medium

+

Dynamically add new file types simply by adding file type magic (.ftm) signatures

+

Known file types are currently baked into each ClamAV versions along with file type magic signatures. See filetypes_int.h, filetypes.h, and filetypes.c. The hardcoded signature definitions for these hardcoded types are generally overridden by daily.ftm, a component of daily.cvd used to tweak file type identification definitions after release.

+

This project would be to re-architect how file types are stored in libclamav so new file types can be dynamically added when daily.ftm (or some other .ftm file) is loaded. Supplemental .ftm files should supplement the existing file type definitions, allowing an extra.ftm file to be tested alongside daily.cvd.

+

This new capability when combined with the ability to register bytecode signatures as new file type scanners will dramatically increase the ability to extend ClamAV functionality between major version updates. Even when combined with logical signatures that target specific file types (using the proposed new Type: keyword instead of Target:, see below project idea), will allow creative analysts to write more compact and efficient logical signatures.

+

Category: Fun, Core Development

+

What you will learn from this project:

+
    +
  • Software architecture experience.
  • +
+

Required skills:

+
    +
  • C development experience.
  • +
+

Project Size: Medium

+

Register scanners for each file type, Write bytecode "signature" scanners.

+

Bytecode signatures are the portable executable plugin format for ClamAV. If ClamAV file types each had one or more* linked list of file type handlers ("scanners"), then a bytecode API could be added to register a bytecode signature as a new scanner for a file type.

+

This project should be completed after the project to dynamically add new file types with new file type magic signatures (above). This new scanning architecture would be really powerful way to add features to the product without requiring a major version update. When combined with the project to run WebAssembly signatures written in Rust (project idea above) -- this plugin-based scanner feature would have the potential to become the fastest and safest way to add new capabilities to ClamAV.

+

Example use case:

+

One example use case of this feature would be to alert on the malicious use of crypto miner wallet IDs.

+

Cryptomining malware has become increasingly prevalent with the rise in cryptocurrency prices, and we have thousands of wallet identifiers known to be associated with malicious cryptomining campaigns. We don't have a robust way of using these IDs for detection, though, because we only want to raise an alert if the ID appears to be used in a malicious way (Ex: hardcoded into a mining application or as part of a coin miner configuration file) and not in legitimate ways (Ex: blog posts about campaigns or wallet block lists used by the mining pools).

+

The two use-cases that we want to alert on are miner config files and executables with the embedded wallet identifier. We could have two .ftm rules (one for each case) that indicate a CL_TYPE_MINER or something like that, and then scanning execution for CL_TYPE_MINER can go to the bytecode sig to perform any other checks that may be necessary.

+

*Additional Considerations: ClamAV has several locations in the scanning process for invoking file type scanners:

+
    +
  1. After initial file type identification, and before the "raw scan". In cli_magic_scan().
  2. +
  3. Once for each embedded file types found when using scanraw() to also match on embedded type recognition signatures*. In scanraw(). +
      +
    • *Embedded type recognition signature matching is a feature used to identify self-extracting archives and some harder to identify file formats, like XML-based office document formats, DMG files, master boot records (MBR), etc. It isn't used for some archive and disk image formats that we'll unpack later anyways because they cause excessive type false positives and duplicate file scanning. A common example without this safety measure was duplicate file extraction and scanning of zip file entries found in a tarball.
    • +
    +
  4. +
  5. After scanning all of the found embedded types (above). At the end of scanraw(). These could probably be moved to (4) if it is deemed safe to remove the 1st "safety measure" call to scanraw() in cli_magic_scan() (i.e we'd only call scanraw() once, ever).
  6. +
  7. Again, after the call to scanraw() at the bottom of cli_magic_scan(), for types that have bytecode hooks that won't execute unless a logical signature matches, requiring scanraw() to perform matching first.
  8. +
+

Considering that there are 3 or 4 placement options for scanners, it may be required to have 3 (or 4) different lists to add to when registering a new scanner to indicate when to run the scanner in the scanning process. An enum argument for the function would indicate which list to add it to. If inserting the new scanner for a given type from the front of the list, and only invoking the next scanner if the first one returns CL_EPARSE or CL_EFORMAT, then a scanner registration could be used to override an existing/built-in one or supplement it, whichever is desired.

+

This project would would require coming up with a common file-type-scanner API for all scanners (including bytecode scanners), and would enable moving all file-type-scanners out of scanners.c and into a new file for each in a scanners subdirectory. A separate parsers subdirectory should be added at this time and each file type parser would be moved there. The distinction between a "scanner" and a "parser" is this. A scanner uses a parser to extract bits to be scanned. A parser may simply be something like an archive extraction library. In some cases, particularly in internally developed code, the distinction may be less clear and so the entire thing may be better placed under the scanners directory as the entry-point will doubtless need to use the common file-type-scanner API.

+

This project will also require creating lots of regression tests for file type identification to ensure that the new architecture doesn't accidentally misclassify or fail to scan certain files.

+

The majority of the work won't actually change ClamAV's behavior, which may seem frustrating, but the end goal is super cool. Code cleanup and organization along the way will also make a meaningful difference. This project could be split into pieces:

+
    +
  1. Establish a common file type scanner function API and reorganize the scanners and parsers as described above.
  2. +
  3. Convert the API into a callback function pointer definition and create a registration API. Add a set of scanner callback lists to each file type. The built-in scanners should be initialized either at compile time or at least when libclamav is initialized, depending on the chosen design.
  4. +
+

Category: Very Fun, Core Development

+

What you will learn from this project:

+
    +
  • Software architecture experience
  • +
  • How to write ClamAV signatures (bytecode and LDB sigs)
  • +
  • You'll test your code against real-world malware, and can do reverse engineering if you'd like to expand the initial coinminer classification logic.
  • +
+

Required skills:

+
    +
  • Strong C development experience.
  • +
  • Any prior experience in the areas listed below is a plus.
  • +
+

Project Size: Very Large

+

Limit logical signature alerts based on file type

+

ClamAV signatures have a "Target Type" which is an integer type which can be used in signatures to limit signature matches to specific file types. ClamAV also categorizes signature patterns into two different Aho-Corasick pattern-matching trie's by Target Type. Target Type 1 (Windows executables (EXE/DLL/SYS/etc.) go in one trie, and everything else goes in the other trie. Unfortunately, not every file type has an associated target type. In addition, while it's conceivable to be able to add new text-based file types dynamically (see the above project idea about file type magic signatures), it is less feasible to dynamically add new numerical target types.

+

For some advanced reading, see:

+
    +
  • <appendix/FileTypes.md>
  • +
  • <manual/Signatures/LogicalSignatures.md>
  • +
+

This project is to add a new "Type:" keyword to the TargetDescriptionBlock for Logical Signature (.ldb) to limit logical signature alerts to specific file types, much like you currently can do with Target Types ("Target:"), Container File Types ("Container:"), and Container Intermediate Types ("Intermediates:"). While this isn't expected to improve scan times, it should reduce overall signature size as analysts will no longer need to duplicate the file-type-magic signature in order to limit alerting on a signature match by file type.

+

To illustrate, this is the file type magic signature for a Microsoft Shortcut File, aka CL_TYPE_LNK:

+
0:0:4C0000000114020000000000C000000000000046:Microsoft Windows Shortcut File:CL_TYPE_ANY:CL_TYPE_LNK:100
+
+

Though we can classify a file as CL_TYPE_LNK and even unpack the file with custom scanner using that type, there is presently no way to write a signature for CL_TYPE_LNK files without duplicating the 0:4C0000000114020000000000C000000000000046 bit.

+

At present a signature to alert on a "malicious" shortcut containing 0xdeadbeef might look like this:

+
SignatureName;Target:0;(0&1);0:4C0000000114020000000000C000000000000046;deadbeef
+
+

After this change, the signature could instead read:

+
SignatureName;Target:0,Type:CL_TYPE_LNK;(0);deadbeef
+
+

Category: Low-hanging Fruit, Core Development

+

What you will learn from this project:

+
    +
  • Knowledge of ClamAV's signature databases, and logical signature evaluation.
  • +
+

Required skills:

+
    +
  • C development experience.
  • +
+

Project Size: Small

+

libclamav Callback Function to Request Additional File

+

Add a callback function to give libclamav file parsers the ability to request additional file data from the scanning application -- I.e. clamscan and clamd (and by extension clamdscan & clamonacc).

+

This feature would enable support for split-archive scans, if all components of the split archive are present and available to the scanning application. To make this work for clamdscan+clamd, or clamonacc+clamd, the request would also have to be relayed by clamd over the socket API to the scanning client, and the client would have to respond with additional data, filepath, or file descriptor for clamd to provide via the callback to file parser.

+
+

Disclaimer: It's entirely likely that this idea is bogus and wouldn't work over the clamd+clamdscan socket API. This task would require a fair amount exploratory coding.

+
+

When a file is scanned, the scanner (eg cli_scanrar) may call a callback function provided by clamscan or clamd to request scan access to other files by name, with the expectation that it would receive an fmap in response. Specifically, when the first file in a split archive is scanned, the parser could request fmaps for subsequent files to provide to the archive extraction library. Direct scanning of files other than the first file in a split archive will skip, because they are split and are not the first file.

+

Category: Risky/Exploratory, Core Development

+

What you will learn from this project:

+
    +
  • ClamAV and SigTool internals
  • +
  • Socket programming
  • +
+

Required skills:

+
    +
  • C and C++ development experience.
  • +
+

Project Size: Large

+

Clam AntiVirus Frequently Asked Questions

+

If you're unable to find an answer to your question in the FAQ, you can seek help in our clamav-users mailing list, on our Discord server, or by submitting an issue on GitHub. The mailing list archives and existing Github issues (open or closed) may also have an answer to your question.

+

Please consider contributing answered questions back to this FAQ, and improving the quality of these answers, by submitting pull requests to our documentation source repository.

+

Table Of Contents

+
    +
  1. Selecting the Right Version of ClamAV for You
  2. +
  3. FreshClam (Signature Updater)
  4. +
  5. Signature Database (CVD)
  6. +
  7. Misc
  8. +
  9. Mailing Lists
  10. +
  11. Safe Browsing
  12. +
  13. Troubleshooting
  14. +
  15. Interpreting Scan Alerts
  16. +
  17. Upgrading
  18. +
  19. Win32
  20. +
  21. ClamAV EOL Policy
  22. +
  23. PUA (Potentially Unwanted Application)
  24. +
  25. Ignore
  26. +
  27. Uninstall
  28. +
+

ClamAV FAQ © 2021 Cisco Systems, Inc.

+

This document is distributed under the terms of the GNU General Public License v2.

+

Clam AntiVirus is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

+

ClamAV and Clam AntiVirus are trademarks of Cisco Systems, Inc.

+

Which Version of ClamAV should I use?

+

Official ClamAV releases are available for download from ClamAV.net.

+

Stable release

+

For critical systems, please install using the latest stable release.

+

Beta and Release candidates Programs

+

If your system is not critical, we occasionally release beta and release candidate (RC) versions of the next feature release. Your participation in our beta and release candidate programs is always appreciated.

+

FreshClam FAQ

+

The following FAQ should help you understand why freshclam may have failed to fetch the latest updates.

+

If you're unable to find an answer to your question in the FAQ, you can seek help in our clamav-users mailing list, on our Discord server, or by submitting an issue on GitHub. The mailing list archives and existing Github issues (open or closed) may also have an answer to your question.

+

Please consider contributing answered questions back to this FAQ, and improving the quality of these answers, by submitting pull requests to our documentation source repository.

+

Failed to get information about user "clamav"

+

If you've installed ClamAV and are running Freshclam as root or with sudo but don't have a clamav user account for Freshclam to run as, you may encounter this error:

+
ERROR: Failed to get information about user "clamav".
+Create the "clamav" user account for freshclam to use, or set the DatabaseOwner config option in freshclam.conf to a different user.
+For more information, see https://docs.clamav.net/manual/Installing/Installing-from-source-Unix.html
+ERROR: Initialization error!
+
+

You can resolve this issue by following these steps to create a clamav service account.

+

Can't create freshclam.dat in /usr/local/share/clamav

+

If the database directory exists but is not owned by the user account that Freshclam is being run as, you may encounter this error:

+
ERROR: Can't create freshclam.dat in /usr/local/share/clamav
+Hint: The database directory must be writable for UID 1000 or GID 1000
+ERROR: Failed to save freshclam.dat!
+WARNING: Failed to create a new freshclam.dat!
+ERROR: initialize: libfreshclam init failed.
+ERROR: Initialization error!
+
+

To resolve this issue, change ownership of the directory to the appropriate user account.

+

For example if you're running Freshclam under your user account "bob", something like this may resolve the issue:

+
sudo chown -R bob /usr/local/share/clamav
+
+

If running Freshclam as root (or with sudo), then Freshclam will try to automatically switch to run as the clamav user, or whichever user is specified as the DatabaseOwner in freshclam.conf. Run this to resolve the issue:

+
sudo chown -R clamav /usr/local/share/clamav
+
+

Problem with the SSL CA cert

+

On Linux/Unix systems, Freshclam uses openssl to validate certificates for TLS connections. It relies on finding the openssl CA bundle in the default path. You may encounter the following error if you're missing the ca-certificates package, or on some distributions where the path to the CA bundle has been customized:

+
WARNING: Download failed (77) WARNING:  Message: Problem with the SSL CA cert (path? access rights?
+WARNING: Can't download daily.cvd from https://database.clamav.net/daily.cvd
+
+

First you may try installing the ca-certificates package. If that is already installed, or the issue persists, then you may need to set the CURL_CA_BUNDLE environment variable to direct Freshclam to the path of the CA bundle on your system.

+

For example, on openSUSE you may need to set CURL_CA_BUNDLE=/var/lib/ca-certificates/ca-bundle.pem. You can test this by running:

+
CURL_CA_BUNDLE=/var/lib/ca-certificates/ca-bundle.pem freshclam
+
+

If this resolves the issue, you may wish to export the CURL_CA_BUNDLE variable in your .bashrc file or the equivalent for your shell.

+
+

Tip: The CURL_CA_BUNDLE variable is also used by ClamSubmit.

+
+

Invalid DNS reply. Falling back to HTTP mode or ERROR: Can't query current.cvd.clamav.net

+

There is a problem with your DNS server. Please check the entries in /etc/resolv.conf and verify that you can resolve the TXT record manually:

+

$ host -t txt current.cvd.clamav.net

+

If you can't, it means your network is broken. You'll be still able to download the updates, but you'll waste a lot of bandwidth checking for updates. Please note that some not RFC compliant DNS servers (namely the one shipped with the Alcatel (now Thomson) SpeedTouch 510 modem) can't resolve TXT record. If that's the case, please recompile ClamAV with the flag --enable-dns-fix.

+

ERROR: Connection with ??? failed

+

Either your dns servers are not working or you are blocking port 53/tcp. You should manually check that you can resolve hostnames with:

+

$ host database.clamav.net

+

If it doesn't work, check your dns settings in /etc/resolv.conf. If it works, check that you can receive dns answers longer than 512 bytes, e.g. check that your firewall is not blocking packets which originate from port 53/tcp. An easy way to find it out is:

+

$ dig @ns1.clamav.net db.us.big.clamav.net

+

WARNING: Incremental update failed, trying to download daily.cvd

+

For some reason, incremental update failed. FreshClam can recover from this situation by downloading the whole daily.cvd.

+

Database update process failed: Downloaded database had lower version than advertised

+

For some reason, the content delivery network is not serving the latest updates yet. If you experience this problem, please report the issue on GitHub Issues.

+

Update failed. Your network may be down or the ClamAV database content delivery network is experiencing an outage

+

It's not your lucky day. Your network may be down or the ClamAV database content delivery network is experiencing an outage. Please wait a few minutes and try again. Remember to pass the -v option to freshclam.

+

Update failed. Updating too frequently with an outdated version

+

Starting from ClamAV 0.9x, whenever your ClamAV engine becomes outdated and the difference between the functionality level required by the CVD and the functionality level supported by your ClamAV engine is more than 3, freshclam will refuse to check for updates more often than 6 times per day.

+

The reason for this is that bandwidth can be expensive. It is not helpful to generate extra traffic on our content delivery network if you cannot take advantage of all the signatures anyway. If you really care about catching as much malware as possible and you want to check for updates more often than 6 times per day, then you should also not run such an old version of ClamAV.

+

Your ClamAV installation is OUTDATED

+

This message does NOT indicate that you are unable to download the latest CVD update! You'll get this message whenever a new version of ClamAV is released. In order to detect all the latest viruses, it's not enough to keep your database up to date. You also need to run the latest version of the scanner.

+

WARNING: Current functionality level = 1, required = 2

+

The functionality level of the database determines which scanner engine version is required to use all of its signatures. If you don't upgrade immediately you will still be able to download the latest CVD updates but the engine won't be able to use ALL of them.

+

Ignoring mirror <IP> (has connected too many times with an outdated version)

+

If you are experiencing this problem, please do the following: Stop the freshclam daemon if it's running, delete both mirrors.dat and daily.cvd, then restart the freshclam daemon. FreshClam will then download a new daily.cvd and will be up-to-date.

+

HTTP Error Codes

+

If you are receiving a 403, 503, or 1020 error codes when downloading from Cloudflare, then you are either explicitly blocked, using an EOL'ed version of ClamAV or you are downloading incorrectly.

+

If FreshClam is failing and you're not sure why, you may run freshclam -v for "Verbose Mode" to see the HTTP request & response details (ClamAV 0.102+).

+

After checking that you are using a current version of ClamAV, please discontinue whatever method of download you are using and immediately move to using either FreshClam or cvdupdate. These are the two supported methods for downloading AV updates from ClamAV. All other methods may be rate limited, or blocked at our discretion. Use of Wget, Curl, or other command line tools that are scripted are explicitly denied.

+

If you are receiving a 429, that means you are rate limited. You're downloading too fast or too much. Please use Freshclam or cvdupdate. If you are using a shared hosting provider, like Amazon AWS, Google Cloud Computing, Oracle, Azure, etc, you will most likely be rate limited, however cvdupdate should handle this gracefully. If you continue to receive these, we recommend you try from a different external IP address.

+

If you are receiving a 403 specifically on the safebrowsing.cvd file, please read this blog post immediately!

+

Are you running a version of FreshClam/ClamAV lower than 0.103.2? If so, you should immediately upgrade to at least 0.103.2.

+

If you have checked all of the above and you are still seeing errors, please open a ticket using the below link.

+ +

Please report freshclam update failures or issues on GitHub Issues.

+

ClamAV Virus Database FAQ

+

The following FAQ should help you understand how ClamAV CVD signature databases work and any issues you may experience working with them.

+

If you're unable to find an answer to your question in the FAQ, you can seek help in our clamav-users mailing list, on our Discord server, or by submitting an issue on GitHub. The mailing list archives and existing Github issues (open or closed) may also have an answer to your question.

+

Please consider contributing answered questions back to this FAQ, and improving the quality of these answers, by submitting pull requests to our documentation source repository.

+

How do I keep my virus database up to date?

+

ClamAV comes with FreshClam, a tool which periodically checks for new database releases and keeps your database up to date. It is encouraged that you update to at least version 0.103.2, which respects our bandwidth limitations.

+

How often is the virus database updated?

+

The virus database is usually updated once or twice per day. Sign up for our VirusDB mailing list to see our response times to new threats. The virusdb team tries to keep up with the latest threats in the wild. You can contribute to make the virusdb updating process more efficient by submitting samples of viruses via our "Contact" page on ClamAV.net.

+

The last CVD update crashed my ClamAV installation. Why?

+

Before publishing a CVD update, we verify that it can be correctly loaded by the last two stable release series of ClamAV.

+

The last CVD update detects a lot of false positives on my system. Why?

+

Before publishing a CVD update, we test it for false positives using the latest stable release of ClamAV. If you want to avoid problems with false positives, you must run the latest stable version of ClamAV. Please stay tuned to our EOL policy for what versions are actively supported.

+

I tried to submit a sample through the web interface, but it said the sample is already recognized by ClamAV. My ClamScan tells me it's not. I have already updated my database and ClamAV engine, what's wrong with my setup?

+

Please run ClamScan with the --alert-broken option. Also check that FreshClam and ClamScan are using the same path for storing/reading the database.

+

I found an infected file in my HD/USB/mailbox, but ClamAV doesn't recognize it yet. Can you help me?

+

Our virus database is kept up to date with the help of the community. Whenever you find a new virus which is not detected by ClamAV you should complete this form. The virusdb team will review your submission and update the database if necessary. Before submitting a new sample: - check that the value of DatabaseDirectory, in both clamd.conf and freshclam.conf, is the same - and update your database by running FreshClam to ensure you've scanned it with the latest virus database.

+

I'm running ClamAV on a lot of clients on my local network. Can I serve the cvd files from a local server so that each client doesn't have to download them from your servers?

+

Sure, you can find more details on our Private Local Mirror page.

+
    +
  1. +

    If you want to take advantage of incremental updates, install a proxy server and then configure your FreshClam clients to use it (watch for the HTTPProxyServer parameter in freshclam.conf).

    +
  2. +
  3. +

    The second possible solution is to:

    +
  4. +
+
    +
  • +

    Configure a local webserver on one of your machines (say machine1.mylan)

    +
  • +
  • +

    Let FreshClam download the *.cvd files from http://database.clamav.net to the webserver's DocumentRoot.

    +
  • +
  • +

    Finally, change freshclam.conf on your clients so that it includes:

    +
    DatabaseMirror machine1.mylan
    +
    +ScriptedUpdates off
    +
    +

    First the database will be downloaded to the local webserver and then the other clients on the network will update their copy of the database from it.

    +

    Important: For this to work, you have to add ScriptedUpdates off on all of your machines!

    +
  • +
+

I can't wait for you to update the database! I need to use the new signature NOW!

+

No problem, save your own signatures in a text file with the appropriate extension (see our signature writing documentation for more information). Put the signature files in the same directory where the .cvd files are located. This is typically /usr/local/share/clamav or /var/lib/clamav. ClamAV will load it after the official .cvd files. You do not need to sign your custom database files.

+

Can I download the virus database manually?

+

This practice is discouraged, please use either FreshClam or CVDUpdate to update your definitions. Please check out our FreshClam FAQ and our Private Mirror Documentation for further information and links to CVDUpdate.

+

I am getting error codes such as 403, 429, etc when FreshClam (or other update system) attempts to download updates

+

Are you attempting to download safebrowsing.cvd and getting a 403? If so, take a look at this blog post, otherwise check out our Freshclam FAQ under the section on "Error Codes".

+
+

For other questions regarding issues with FreshClam, see our FreshClam Troubleshooting FAQ.

+

Miscellaneous FAQ

+

If you're unable to find an answer to your question in our FAQ, you can seek help in our clamav-users mailing list, on our Discord server, or by submitting an issue on GitHub. The mailing list archives and existing Github issues (open or closed) may also have an answer to your question.

+

Please consider contributing answered questions back to this FAQ, and improving the quality of these answers, by submitting pull requests to our documentation source repository.

+

I see you have Bugzilla AND GitHub Issues. Which one should I use?

+

Please use GitHub Issues. We're slowly phasing out Bugzilla. But please read the Security Policy and do not disclose anything in a ticket that should be kept private or that may represent a vulnerability.

+

I reported a bug on Bugzilla, but no one can see it. What do I do?

+

Bugs reported to ClamAV's Bugzilla ticket tracker are private by default. Our policy is to make tickets public if they are not security-related. If you believe your bug report isn't security-related, please ping the ticket requesting that the ticket be made public. Chances are high that we simply forgot to remove the tag when the report came in.

+

Where can I find the bug ticket for a security bug fix?

+

Our policy is to make security-related bugs public either: +A. fourteen (14) days after a security patch version has been published with a fix for the bug, or +B. after the non-disclosure agreement with a vulnerability reporter has expired and the bug details are otherwise already public.

+

This policy serves to reduce the risk that a malicious party would find enough details in the ticket to craft their own exploit for the bug before users have had an opportunity to upgrade to a patched version.

+

Where can I find a test file to prove that a security bug doesn't affect me or has been fixed in my version?

+

We do not share test files for security bugs. Sharing these files puts users at risk for at least two reasons:

+
    +
  1. Sharing a test file outside of our organization increases the risk of it falling into the wrong hands and being used to craft an exploit.
  2. +
  3. Test files generated by a fuzzer are designed to demonstrate a bug in a specific test environment and do the bare minimum necessary to trigger the bug. Scanning a test file outside of that environment is unlikely to demonstrate the issue EVEN THOUGH THE ISSUE STILL EXISTS. A clever adversary may very well be able to craft a bigger and better exploit for that issue that does affect your unpatched system. Testing with the original fuzzer-generated file is most likely to give you a false sense of security.
  4. +
+

Can phishing be considered one kind of spam? ClamAV should not detect it as some kind of malware.

+

Starting from release 0.90, ClamAV allows you to choose whether to detect phish as some kind of malware or not. This should put an end to the endless threads on our mailing lists. So long, and thanks for all the phish.

+

Why is my legitimate HTML newsletter/email detected by ClamAV as Phishing.Heuristics.Email.SpoofedDomain?

+

If it contains links in the form of href="http://yourdomain.example.tld"> otherdomain.tld, where otherdomain.tld (ProtectedDomain) doesn't belong to you and is listed in ClamAV database (like amazon.com, ebay.com, ...) then ClamAV detects it as a phishing attempt.

+

My legitimate emails from yourdomain.tld are detected as Phishing.Heuristics.Email.SpoofedDomain

+

Please submit a sample, marking it as a false positive, phishing. If it's really a false positive, we will add an allow list entry for it.

+

Can I convert the new database format to the old one?

+

Yes, install a recent version of sigtool and run:

+

sigtool --unpack-current daily.cvd; sigtool --unpack-current main.cvd

+

How do I read inside the CVD files?

+

See previous FAQ.

+

I'm using ClamAV in a production environment and a brand new virus is not being recognized by ClamAV. How long do I have to wait before ClamAV can start filtering the virus?

+

No time at all! Find a signature for that virus and modify your virus database accordingly (see our signature writing documentation for details). +Remember to submit the sample to the virusdb team.

+

Why is ClamAV calling the XXX virus with another name?

+

This usually happens when we add a signature before other AV vendors. No well-known name is available at that moment so we have to invent one. Renaming the virus after a few days would just confuse people more, so we usually keep on using our name for that virus. The only exception is when a new name is established soon after the signature addition.

+

I get many false positives of Oversized.zip

+

Whenever a file exceeds ArchiveMaxCompressionRatio (see clamd.conf man page), it's considered a logic bomb and marked as Oversized.zip. Try increasing your ArchiveMaxCompressionRatio setting.

+

What is PUA? I get a lot of false positives named PUA.

+

With the release of ClamAV 0.91.2 we introduce the option to scan for Potentially Unwanted Applications.

+

The PUA database contains detection for applications that are not malicious by itself but can be used in a malicious or unwanted context. As an example: A tool to retrieve passwords from a system can be useful as long as the person who uses it, is authorized to do so. However, the same tool can be used to steal passwords from a system. To make use of the PUA database you can use the --detect-pua switch for clamscan or enable it in the config file for ClamD (add: DetectPUA yes).

+

At this point we DO NOT recommend using it in production environments, because the detection may be too aggressive and lead to false positives. In one of the next releases we will provide additional features for fine-tuning allowing better adjustments to different setups. NOTE: A detection as PUA does NOT tell if an application is good or bad. All it says is, that a file MAY BE unwanted or MAYBE could compromise your system security and it MAY BE a good idea to check it twice.

+

Can ClamAV disinfect files?

+

No, it can't. We will add support for disinfecting OLE2 files in one of the next stable releases. There are no plans for disinfecting other types of files. There are many reasons for it: cleaning viruses from files is virtually pointless these days. It is very seldom that there is anything useful left after cleaning, and even if there is, would you trust it?

+

When using clamscan, is there a way to know which message within an mbox is infected?

+

There are two solutions: Run clamscan --debug, look for Deal with email number xxx Alternatively you can convert the mbox to Maildir format, run clamscan on it and then convert it back to mbox format. There are many tools available which can convert to and from Maildir format: formail, mbox2maildir and maildir2mbox

+

What platforms does it support?

+

Clam AntiVirus works with Linux®, Solaris, FreeBSD, OpenBSD, NetBSD, Mac OS X, Cygwin B20 on multiple architectures such as Intel, Alpha, Sparc, Cobalt MIPS boxes, PowerPC, RISC 6000.

+

Where can I find more information about ClamAV?

+

Please read the complete documentation in pdf/ps format. You will find it inside the package or in the documentation section of this website. You can also try searching the mailing list archives. If you can't find the answer, you can ask for support on the clamav-users mailing-list, but please before doing it, search the archives! Also, make sure that you don't send HTML messages and that you don't top post: these violate the netiquette and lessen your chances of being answered.

+

Mailing Lists FAQ

+

If you're unable to find an answer to your question in our FAQ, you can seek help in our clamav-users mailing list, on our Discord server, or by submitting an issue on GitHub. The mailing list archives and existing Github issues (open or closed) may also have an answer to your question.

+

Please consider contributing answered questions back to this FAQ, and improving the quality of these answers, by submitting pull requests to our documentation source repository.

+

Where can I ask questions about using ClamAV?

+

Subscribe to our clamav-users mailing-list.

+

I want to take part to the development of ClamAV. Where can I get more info?

+

Subscribe to the clamav-devel mailing-list.

+

The mailing-lists generate too many messages per day. I can't handle them. What shall I do?

+

There are two possible solutions:

+
    +
  • Go to the mailing-list mailman interface, click on Unsubscribe or edit options, and turn digest mode on.
  • +
  • Access the mailing-lists using a "news reader".
  • +
+

I sent a message to one of ClamAV's mailing-lists, but the mail was rejected/held for approval. Why?

+

Only subscribers are allowed to post to the mailing-list. This is done to avoid spammers.

+

I read the mailing-list from the Gmane news gateway. Can I post to the mailing-list?

+

See previous FAQ.

+

I've been unsubscribed from one of the mailing-lists. What happened?

+

There are two possible reasons: If your account generates too many bounces you'll be automatically unsubscribed. Please subscribe again with a more reliable account. If we receive even one out of office notification from your vacation program, your address will be unsubscribed and banned from our mailing-lists forever. Sorry for that, there are just too many stupid people out there.

+

How do I disable mail delivery from the mailing-list I'm subscribed to?

+

Suppose you are subscribed to clamav-users. Go to clamav-users and enter your mail address at the bottom of the page. Click on Unsubscribe or edit options. At the next page enter your password and press Log in. Under Your clamav-users Subscription Options choose Disabled opposite Mail delivery and press Submit My Changes at the bottom of the page.

+

Safebrowsing

+

About

+

Google Safe Browsing may be used to get advanced protection against emails with links to suspicious websites.

+

Current Status

+

The Safe Browsing feature has now been spun off into a related project.

+

ClamAV previously provided a "safebrowsing" signature database derived from Google's Safe Browsing API using our own account and API key. Due to changes to the terms of service, we can no longer provide the signature content for you. Users that desire Safe Browsing coverage for non-commercial purposes can still generate their own safebrowsing signature database using the clamav-safebrowsing tools with a Google Safe Browsing API key.

+

Briefly, clamav-safebrowsing tools are needed to:

+
    +
  1. +

    Download the data from Google to a local MySQL database using Google's API [*];

    +
  2. +
  3. +

    Produce a local copy of the safebrowsing database file in a form suitable for use by the ClamAV tools;

    +
  4. +
+

You will have to decide how to distribute this database file to the systems which need it; and to notify any clamd daemons of the change so that they load the new signatures.

+

[*] For efficiency, the API permits downloading differences, in much the same way that ClamAV itself uses .cdiff files. The clamav-safebrowsing tool uses a MySQL database to store the Safe Browsing data so it can apply these differences the next time you use it.

+

For more information, please visit the ClamAV Safebrowsing project on Github.

+

History

+

ClamAV 0.95 introduced an optional "safebrowsing" signature database to provide advanced protection against emails with links to suspicious websites. This was generated using Google's Safe Browsing API.

+

The "safebrowsing" signature database which was distributed in the same way as the other ClamAV database files via the ClamAV mirror network. Downloading the database was disabled by default, and the feature was to be enabled only with extreme caution. In order to enable this feature it was necessary to add the option SafeBrowsing Yes to freshclam.conf.

+

As of Nov. 11, 2019, we stopped updating the "safebrowsing" signature database because Google announced changes to their Safe Browsing API terms of service. Google now requires commercial users to use the Google Web Risk API, a for-profit feature, instead of the Safe Browsing API. Though ClamAV itself is free and open-source, we cannot continue to provide Google Safe Browsing data to the general public.

+

Troubleshooting FAQ

+

The following questions and answers may help you troubleshoot issues you may encounter when using ClamAV.

+

If you're unable to find an answer to your question in our FAQ, you can seek help in our clamav-users mailing list, on our Discord server, or by submitting an issue on GitHub. The mailing list archives and existing Github issues (open or closed) may also have an answer to your question.

+

Please consider contributing answered questions back to this FAQ, and improving the quality of these answers, by submitting pull requests to our documentation source repository.

+

After ClamAV is installed, then what? How do I update / refresh the virus database?

+

You will need to edit the freshclam.conf.example file located in /usr/local/etc. Once that is done, you will need to run a sudo freshclam to download the signatures. You will need to run the command to update signatures often so that ClamAV has the most up to date signatures.

+

How many times per hour shall I run FreshClam?

+

You can check for database update as often as 4 times per hour provided that you have the following options in freshclam.conf:

+
DNSDatabaseInfo current.cvd.clamav.net
+
+DatabaseMirror database.clamav.net
+
+

I get this error when running FreshClam: Invalid DNS reply. Falling back to HTTP mode or ERROR: Can't query current.cvd.clamav.net . What does it mean?

+

There is a problem with your DNS server. Please check the entries in /etc/resolv.conf and verify that you can resolve the TXT record manually:

+
host -t txt current.cvd.clamav.net
+
+

If you can't, it means your network is broken. You'll be still able to download the updates, but you'll waste a lot of bandwidth checking for updates.

+

What does WARNING: DNS record is older than 3 hours mean?

+

FreshClam attempts to detect potential problems with DNS caches and switches to use HTTPS if something looks suspicious. If this message appears seldomly, you can safely ignore it. If you get the error every time you run FreshClam, check your system clock. If it is set correctly, check your dns settings. If those didn't help, try putting this at the top of your cronjob:

+
host -t txt current.cvd.clamav.net; perl -e 'printf "%d\n", time;'
+
+

The 4th field of the first line should be less than 3 ∗ 3600 behind the output of the second line. If not, you have a caching DNS server somewhere misbehaving.

+

I get this error when running FreshClam: ERROR: Connection with ??? failed . What shall I do?

+

Either your dns servers are not working or you are blocking port 53/tcp. You should manually check that you can resolve host names with:

+
host database.clamav.net
+
+

If it doesn't work, check your dns settings in /etc/resolv.conf. If it works, check that you can receive dns answers longer than 512 bytes, e.g. check that your firewall is not blocking packets which originate from port 53/tcp.

+

An easy way to find it out is:

+
dig @ns1.clamav.net db.us.big.clamav.net
+
+

How do I know if my IP address has been blocked?

+

Run FreshClam in verbose-mode (-v) to view the HTTP requests and responses. If you're seeing an HTTP 403 response, then you may have been blocked. If think you've been blocked, feel free to contact us for help getting un-blocked. For more information about HTTP error codes, see the FreshClam FAQ

+

I can't resolve current.cvd.clamav.net! Is there a problem with your/my DNS servers?

+

current.cvd.clamav.net has got only a TXT record, not a type A record! Try this command:

+
$ host -t txt current.cvd.clamav.net
+
+

Please note that some not RFC compliant DNS servers (namely the one shipped with the Alcatel (now Thomson) SpeedTouch 510 modem) can't resolve TXT record. If that's the case, please recompile ClamAV with the flag --enable-dns-fix if using ./configure or -D ENABLE_FRESHCLAM_DNS_FIX=ON if using CMake.

+
+

For other questions regarding issues with the signature databases, see our Virus Database FAQ.

+

Interpreting Scan Alerts FAQ

+

If you're unable to find an answer to your question in our FAQ, you can seek help in our clamav-users mailing list, on our Discord server, or by submitting an issue on GitHub. The mailing list archives and existing Github issues (open or closed) may also have an answer to your question.

+

Please consider contributing answered questions back to this FAQ, and improving the quality of these answers, by submitting pull requests to our documentation source repository.

+

ClamAV alerted on a file during a scan. What do I do?

+

ClamAV may have found a malicious or suspicious file. However, you're probably asking yourself if the alert is a False Positive (FP). It may well be, so don't just delete the file out-of-hand.

+

ClamAV alerted on a file in the clamav source code. Am I infected?

+

If you scan the build directory for ClamAV, you may see an alert on a ClamAV test file, like this:

+
clamav-0.104.1/build/unit_tests/input/clamav_hdb_scanfiles/clam.chm: Clamav.Test.File-6 FOUND
+
+

You can savely ignore this alert. The files found under the clamav unit_tests/input in the build directory are supposed to alert, to demonstrate correct file parser functionality.

+

Online Research

+

First, consider the file itself and whether or not the alert makes sense. If you're concerned, start by searching the name of the signature on Google. If FP's are being reported, you may see others complaining about the same thing, or you may be able to get an understanding of what the signature is trying to find.

+

Technical Investigation

+

Second, if you're technically inclined, you may want to try to read the signature details to understand how it works and what, specifically, it's alerting on. Take heed, this investigation might leave you more confused than when you started. ClamAV doesn't post write-ups on how each signature in-part because a good number of our signatures these days are generated automatically and not by a human mind.

+
    +
  1. +

    Start by opening a command prompt in a new empty directory, for example:

    +
    user@laptop:~$  mkdir /tmp/sigdump
    +
    +user@laptop:~$  cd /tmp/sigdump
    +
    +
  2. +
  3. +

    Use the sigtool program to unpack the ClamAV databases into their separate components. SigTool should be installed alongside clamscan, probably in /usr/local/bin/sigtool. The ClamAV databases are traditionally installed in /usr/local/share/clamav although if you installed from a package manager, your paths may vary:

    +
    user@laptop:/tmp/sigdump$  sigtool -u /usr/local/share/clamav/main.cvd
    +
    +user@laptop:/tmp/sigdump$  sigtool -u /usr/local/share/clamav/daily.cvd        # May be: daily.cld
    +
    +user@laptop:/tmp/sigdump$  sigtool -u /usr/local/share/clamav/bytecode.cvd     # May be: bytecode.cld
    +
    +
  4. +
  5. +

    Use ls to verify that you've successfully unpacked the databases:

    +
    user@laptop:/tmp/sigdump$  ls
    +
    +3986187.cbc    3986230.cbc    3986303.cbc    4553522.cbc    6335443.cbc    6399052.cbc    daily.cfg      daily.msb
    +3986188.cbc    3986231.cbc    3986305.cbc    4970075.cbc    6335540.cbc    6404655.cbc    daily.crb      daily.msu
    +3986206.cbc    3986232.cbc    3986306.cbc    5044126.cbc    6335560.cbc    6428210.cbc    daily.fp       daily.ndb
    +3986212.cbc    3986233.cbc    3986310.cbc    5588995.cbc    6335564.cbc    6428556.cbc    daily.ftm      daily.ndu
    +3986214.cbc    3986234.cbc    3986321.cbc    5819336.cbc    6335669.cbc    6441308.cbc    daily.hdb      daily.pdb
    +3986215.cbc    3986235.cbc    3986322.cbc    5999914.cbc    6336023.cbc    6442366.cbc    daily.hdu      daily.sfp
    +3986216.cbc    3986236.cbc    3986327.cbc    5999936.cbc    6336035.cbc    6447941.cbc    daily.hsb      daily.wdb
    +3986217.cbc    3986242.cbc    3986328.cbc    6300337.cbc    6336074.cbc    6453673.cbc    daily.hsu      main.crb
    +3986218.cbc    3986244.cbc    3986334.cbc    6311970.cbc    6336259.cbc    6471051.cbc    daily.idb      main.fp
    +3986219.cbc    3986249.cbc    3986337.cbc    6316126.cbc    6336260.cbc    6497366.cbc    daily.ign      main.hdb
    +3986220.cbc    3986259.cbc    4306126.cbc    6324281.cbc    6336630.cbc    6539706.cbc    daily.ign2     main.hsb
    +3986221.cbc    3986282.cbc    4306157.cbc    6327695.cbc    6336737.cbc    6566834.cbc    daily.info     main.info
    +3986222.cbc    3986283.cbc    4307467.cbc    6329916.cbc    6336739.cbc    6614848.cbc    daily.ldb      main.mdb
    +3986223.cbc    3986289.cbc    4416867.cbc    6329917.cbc    6364361.cbc    COPYING        daily.ldu      main.msb
    +3986224.cbc    3986292.cbc    4510302.cbc    6335400.cbc    6380163.cbc    bytecode.info  daily.mdb      main.ndb
    +3986229.cbc    3986301.cbc    4526683.cbc    6335427.cbc    6395243.cbc    daily.cdb      daily.mdu      main.sfp
    +
    +
  6. +
  7. +

    Use grep to search for the signature in question. For example:

    +
    user@laptop:/tmp/sigdump$  grep -r Win.Downloader.DDECmdExec-6683887-5
    +
    +Win.Downloader.DDECmdExec-6683887-5;Engine:81-255,Target:0;4;0:1f8b;0:255044462d;0:4d5a{-100}50450000;7c27{-255}2721;(0=0&1=0&2=0)&3/(?<!\x20)[=+\-@]\s*?(\w+\s*?\x28)?.{0,50}(certutil|cmd|cmstp|cscript|dnscmd|msiexec|netsh|regsvr32|rpcping|rundll32|schtasks|telnet|tscon|tsdiscon|wmic|wscript).{0,50}\|\s*?\x27[^\x27]{5,255}\x27\s*?\x21/i
    +
    +
  8. +
  9. +

    Reading ClamAV signatures is hard. You can familiarize yourself with the ClamAV signature format by reading the documentation on writing ClamAV Signatures.

    +

    To get a jump start, you can make sigtool print out a more human readable represenation of what the signature is looking for. Pipe the output from grep directly into sigtool by using the --decode-sigs option:

    +
    user@laptop:/tmp/sigdump$  grep Win.Downloader.DDECmdExec-6683887-5 -r . | ../../bin/sigtool --decode-sigs
    +
    +

    The output will look something like this:

    +
        VIRUS NAME: ./daily.ldb:Win.Downloader.DDECmdExec-6683887-5
    +    TDB: Engine:81-255,Target:0
    +    LOGICAL EXPRESSION: 4
    +     * SUBSIG ID 0
    +     +-> OFFSET: 0
    +     +-> SIGMOD: NONE
    +     +-> DECODED SUBSIGNATURE:
    +    �
    +     * SUBSIG ID 1
    +     +-> OFFSET: 0
    +     +-> SIGMOD: NONE
    +     +-> DECODED SUBSIGNATURE:
    +    %PDF-
    +     * SUBSIG ID 2
    +     +-> OFFSET: 0
    +     +-> SIGMOD: NONE
    +     +-> DECODED SUBSIGNATURE:
    +    MZ{WILDCARD_ANY_STRING(LENGTH<=100)}PE
    +     * SUBSIG ID 3
    +     +-> OFFSET: ANY
    +     +-> SIGMOD: NONE
    +     +-> DECODED SUBSIGNATURE:
    +    |'{WILDCARD_ANY_STRING(LENGTH<=255)}'!
    +     * SUBSIG ID 4
    +     +-> OFFSET: ANY
    +     +-> SIGMOD: NONE
    +     +-> DECODED SUBSIGNATURE:
    +         +-> TRIGGER: (0=0&1=0&2=0)&3
    +         +-> REGEX: (?<!\x20)[=+\-@]\s*?(\w+\s*?\x28)?.{0,50}(certutil|cmd|cmstp|cscript|dnscmd|msiexec|netsh|regsvr32|rpcping|rundll32|schtasks|telnet|tscon|`tsdiscon|wmic|wscript).{0,50}\|\s*?\x27[^\x27]{5,255}\x27\s*?\x21
    +         +-> CFLAGS: i
    +
    +
  10. +
  11. +

    Interpret the results. ClamAV signatures can be as simple as a hash-based signature of a known-malicious file, but they can also be a complex logical test. You may not learn enough to make an educated decision. The above example is a pretty complicated one, so I will try to walk you through it.

    +

    You can see that there are 5 subsignatures (numbered 0 - 4). The LOGICICAL EXPRESSION indicates which subsignature(s) matter and why. This could be something like 0 AND 1 to indicate that 2 subsignatures must both trigger in order for the overall signature to alert. In this case, only subsignature 4 is required by the LOGICAL EXPRESSION.

    +

    If you look at SUBSIG ID 4, you'll see that has a has a TRIGGER which acts in much the same way as the above LOGICAL EXPRESSION. If the subsignatures in the logical expression are satisfied, then the regular expression REGEX will be tested. If the regular expression matches, then the SUBSIG ID 4 will trigger and the overall signature will alert.

    +
  12. +
+

Reporting

+

If you believe that the signature alerted on a benign file, please report the False Positive so our analysts can refine or remove the faulty signature. You can report false positives on our website or you can submit the report using the clamsubmit command-line program.

+

If you're concerned that the file may be malicious, and aren't comfortable quarantining and/or deleting the file, feel free to ask in the user mailing lists for advice. Please subscribe to clamav-users and then post a message to all the list members by sending an email to clamav-users -at- lists -dot- clamav -dot- net.

+

Upgrading ClamAV

+

ClamAV from Packages

+

If you installed from a package, we suggest you find the approved package from your distro provider and install that. The ClamAV team does not maintain individual packages for every distribution build. +If there are no new packages, you have three options:

+
    +
  • Wait
  • +
  • Build ClamAV Package
  • +
  • Install ClamAV From Source
  • +
+

Install ClamAV From Source

+

If you installed from sources, first uninstall the old version.

+

Then, compile and install the new one.

+

Depending on your installation method, you might want to backup configuration (located in /usr/local/etc by default) and signature database (located in /usr/local/share/clamav by default). Don't forget to restore backups before starting up updated ClamAV.

+

Backup your database signature (located in /usr/local/share/clamav by default) before upgrading to newer ClamAV version. Restore the backed up database signature before running the updated version. This is to avoid getting the /usr/local/share/clamav not locked error message when doing freshclam.

+

Webmin and Yum

+

To obtain a new version:

+
yum list clamav
+
+yum update clamav
+
+

If everything updated properly, run freshclam to update your signature database.

+

What does "WARNING: Current functionality level = 1, required = 2" mean?

+

The functionality level of the database determines which scanner engine version is required to use all of its signatures. If you don't upgrade immediately you will be missing the latest viruses.

+

What does "Your ClamAV installation is OUTDATED" mean?

+

You'll get this message whenever a new version of ClamAV is released. In order to detect all the latest viruses, it's not enough to keep your database up to date. You also need to run the latest version of the scanner. You can download the sources of the latest release from our website. If you are afraid to break something while upgrading, use the pre-compiled packages for your operating system/distribution. Remember: running the latest stable release also improves stability.

+

I upgraded to the latest stable version but I still get the message "Your ClamAV installation is OUTDATED", why?

+

Make sure there is really only one version of ClamAV installed on your system:

+
whereis freshclam
+
+whereis clamscan
+
+

Also make sure that you haven't got old libraries (libclamav.so*) lying around your filesystem. You can verify it using:

+
ldd $(which freshclam)
+
+

What does "Malformed hexstring: This ClamAV version has reached End of Life" mean?

+

Please refer to our End-of-Life (EOL) policy.

+

How do I verify the integrity of ClamAV sources?

+

Using GnuPG you can easily verify the authenticity of your stable release downloads by using the following method: Download the Talos PGP public key from the VRT labs site. Import the key into your local public keyring:

+
gpg --import vrt.gpg
+
+

Download the stable release AND the corresponding .sig file to the same directory. Verify that the stable release download is signed with the Talos PGP public key:

+
gpg --verify clamav-X.XX.tar.gz.sig
+
+

Please note that the resulting output should look like the following:

+
    gpg: Signature made Wed Jan 24 19:31:26 2018 EST
+    gpg:                using RSA key F13F9E16BCA5BFAD
+    gpg: Good signature from "Talos (Talos, Cisco Systems Inc.) [email address]" [unknown]
+
+

For other PGP implementation, please refer to their manual.

+

Where can I get the latest release, beta, or release candidate of ClamAV?

+

Visit the source download page.

+

Is my compiler/hardware/operating system supported by ClamAV?

+

ClamAV supports a wide variety of compilers, hardware and operating systems. Our core compiler is GCC with Linux on 32 and 64 bit Intel platforms, LLVM/Clang on macOS, and MSVC on Windows.

+

ClamAV on Microsoft Windows FAQ

+

ClamAV offers a versions of ClamAV for Microsoft Windows compatible with both 32bit and 64bit versions of Windows 7 and newer.

+

We provide both two install packages for Windows:

+
    +
  • +

    Portable Install Package

    +
      +
    • Zip file containing all you need to run ClamAV without running an installer.
    • +
    +
  • +
  • +

    Install Program

    +
      +
    • Traditional executable installer that will install ClamAV in the "Program Files" directory.
    • +
    +
  • +
+

In addition to the above ClamAV versions that run on Windows, Cisco offers two more endpoint security products powered in-part by ClamAV. These are:

+
    +
  • +

    Immunet

    +
      +
    • Immunet is a malware and antivirus protection system that utilizes cloud computing to provide enhanced community-based security.
    • +
    • Immunet is free for non-commercial use.
    • +
    • Commercial users should consider Cisco AMP.
    • +
    +
  • +
  • +

    Cisco Advanced Malware Protection (AMP) for Endpoints

    +
      +
    • AMP for Endpoints prevents threats at point of entry, then continuously tracks every file it lets onto your endpoints. AMP can uncover even the most advanced threats, including fileless malware and ransomware.
    • +
    • AMP for Endpoints is subscription-based, managed through a web-based management console, and may be deployed on a variety of platforms to include Windows, macOS, and Linux operating systems.
    • +
    • For details, please visit the AMP for Endpoints website.
    • +
    +
  • +
+

What is the difference between ClamAV, Immunet, and ClamWin?

+

ClamAV is available for Windows with the same scanning and detection capabilities available when using ClamAV on macOS and Linux, with exception to the On-Access Scanning feature (Linux).

+

Immunet is a real-time fully featured desktop AV solution. It contains cloud based detection technologies and the enterprise grade ClamAV detection engine. The product is produced and maintained by Cisco which owns both ClamAV and Immunet.

+

ClamWin is a free application that provides a graphical front-end for ClamAV, similar to ClamTk. ClamWin is maintained by ClamWin Pty Ltd., and has no association with ClamAV, Cisco, or the Immunet product. Additionally, ClamWin does not contain an on-access real-time scanner, and can only be used to manually scan files.

+

Is Immunet free for commercial use?

+

Immunet is not free for commercial use. AMP for Endpoints is our product that replaced the commercial version. If you are interested in deploying this in a commercial environment, we highly suggest checking that out.

+

Will Immunet send any sensitive data from my computer to the cloud?

+

Immunet sends information about the files its scanning back to the cloud. This information is in the form of SHA hashes and file heuristics. Currently, this information is only collected for Windows PE files, or in other terms what most people refer to as executable files. No information is collected for other types of files, like Word, Excel, or PDF. Additionally, in some situations the entire PE file will be uploaded to the Cloud to determine if it is malicious. +For a complete overview please see the privacy policy.

+

Are you going to make use of the Cloud in the *nix version of ClamAV?

+

The simple answer is “yes”. The complex answer is “when”. We are currently working on that roadmap and will keep everyone informed as that information becomes available.

+

Can I use Immunet with my current AV solution?

+

Yes. In fact it is encouraged.

+

Where should I report false positives or undetected malware?

+

Please report Undetected Malware here.

+

Please report False Positives here.

+

Are there 64 bit versions of ClamAV for Windows as well as 32 bit?

+

Yes. You can find both versions at ClamAV/downloads.

+

Potentially Unwanted Applications (PUA)

+

ClamAV supports the detection of Potentially Unwanted Applications (PUA).

+

PUA Config Options

+

You can customize PUA detection for ClamD with these clamd.conf options:

+
  DetectPUA yes        # Detect Possibly Unwanted Applications
+  ExcludePUA CAT       # Skip PUA sigs of category CAT
+  IncludePUA CAT       # Load PUA sigs of category CAT
+
+

You can customize PUA detection for ClamScan with these command-line options:

+
  --detect-pua         # Detect Possibly Unwanted Applications
+  --exclude-pua=CAT    # Skip PUA sigs of category CAT
+  --include-pua=CAT    # Load PUA sigs of category CAT
+
+

The category name is a string match with the 2nd token in a PUA.* signature name.

+
  PUA.category.subcategory.description-version
+
+

Some examples:

+
    +
  • +

    PUA.Win.Packer.BorlandDelphi-5 : The category name is Win.

    +
  • +
  • +

    PUA.Cert.Revoked.PEAuthenticode-5750538-0 : The category name is Cert.

    +
  • +
+

There is presently no support for including or excluding by subcategory.

+

Current PUA Categories

+

PUA categories are the product of signature naming conventions. These vary over time as new signatures are added.

+
+

Disclaimer: PUA signatures are not as carefully curated as malware signatures because they are not as commonly used. You should expect more false positives when using PUA signatures. Further, inclusion or exclusion of specific categories may not be very intuitive or predictable. Specifically, excluding the Win category will not exclude all Windows application PUA signatures. There are undoubtedly more Windows PUA signatures in the Packed, Tool, Spy, NetTool, etc categories that target Windows applications. Similarly, excluding the Packed category will not guarantee that you exclude signatures like PUA.Win.Packer.Whatever-0123. In short, the inclusion and exclusion of PUA signatures will likely be frustrating. Improvements to PUA include/exclude options to support subcategories as well as SigTool features to enumerate current PUA categories and subcategories would be a good candidate for a community contribution project.

+
+
+

Disclaimer 2: The Virus/Ransomeware/Trojan/etc malware categories or subcategories for PUA signatures were mistakenly selected by automated tools. Those tools have since been fixed and no new signatures should appear with these names. The existing malware-name categories for these PUA signatures are expected to be removed/renamed as time permits.

+
+

The following is a snapshot of the PUA signature name categories and subcategories from daily.cvd & main.cvd (Jan 29, 2020):

+
PUA.Andr.Adware
+PUA.Andr.Downloader
+PUA.Andr.Dropper
+PUA.Andr.Tool
+PUA.Andr.Trojan
+PUA.Andr.Virus
+PUA.Cert.Revoked
+PUA.Doc.Dropper
+PUA.Doc.Packed
+PUA.Doc.Tool
+PUA.Doc.Trojan
+PUA.Email.Phishing
+PUA.Email.Trojan
+PUA.Embedded.File
+PUA.Html.Exploit
+PUA.Html.Tool
+PUA.Html.Trojan
+PUA.Java.Exploit
+PUA.Java.Packer
+PUA.Js.Exploit
+PUA.Osx.File
+PUA.Osx.Trojan
+PUA.Packed.Tool
+PUA.Pdf.Exploit
+PUA.Pdf.Trojan
+PUA.Php.Trojan
+PUA.Rtf.Exploit
+PUA.Spy.Tool
+PUA.Swf.Spyware
+PUA.Tool.Countermeasure
+PUA.Tool.Tool
+PUA.Unix.Adware
+PUA.Unix.Coinminer
+PUA.Unix.Downloader
+PUA.Unix.File
+PUA.Unix.Malware
+PUA.Unix.Tool
+PUA.Unix.Trojan
+PUA.Unix.Virus
+PUA.Win.Adware
+PUA.Win.Coinminer
+PUA.Win.Downloader
+PUA.Win.Dropper
+PUA.Win.Exploit
+PUA.Win.File
+PUA.Win.Ircbot
+PUA.Win.Joke
+PUA.Win.Keylogger
+PUA.Win.Malware
+PUA.Win.Packed
+PUA.Win.Packer
+PUA.Win.Proxy
+PUA.Win.Ransomware
+PUA.Win.Spyware
+PUA.Win.Tool
+PUA.Win.Trojan
+PUA.Win.Virus
+
+

PUA Category Descriptions

+

The following category descriptions should give you some idea of how the PUA signature naming conventions are used. Please note this list is not exhaustive. As noted above, PUA signatures are not as carefully curated and there will be exceptions:

+
    +
  • +

    Andr

    +

    Potentially unwanted applications for Android mobile devices.

    +
  • +
  • +

    Java

    +

    Potentially unwanted applications written for the Java runtime.

    +
  • +
  • +

    NetTool

    +

    Applications that can be used to sniff, filter, manipulate or scan network traffic or networks. While a network scanner - for example - can be a extremely helpful tool for admins, you may not want to see an average user playing around with it. Same goes for tools like netcat and the like.

    +
  • +
  • +

    P2P

    +

    Peer to Peer clients can be used to generate a lot of unwanted traffic and sometimes it happens that copyrights are violated by downloading copyright protected content (Music, Movies) - therefore we consider them possibly unwanted as well.

    +
  • +
  • +

    Packed

    +

    This is a detection for files that use some kind of runtime packer. A runtime packer can be used to reduce the size of executable files without the need for an external unpacker. While this can't be considered malicious in general, runtime packers are widely used with malicious files since they can prevent a already known malware from detection by an anti-virus product.

    +
  • +
  • +

    PwTool

    +

    Password tools are all applications that can be used to recover or decrypt passwords for various applications - like mail clients or system passwords. Such tools can be quite helpful if a password is lost, however, it can also be used to spy out passwords.

    +
  • +
  • +

    IRC

    +

    IRC Clients can be a productivity killer and depending on the client - a powerful platform for malicious scripts (take mIRC for example).

    +
  • +
  • +

    Osx

    +

    Potentially unwanted applications for macOS systems.

    +
  • +
  • +

    RAT

    +

    Remote Access Trojans are used to remotely access systems, but can be used also by system admins, for example VNC or RAdmin.

    +
  • +
  • +

    Server

    +

    Server based badware like DistributedNet.

    +
  • +
  • +

    Script

    +

    Known "problem" scripts written in JavaScript, ActiveX or similar.

    +
  • +
  • +

    Spy

    +

    Keyloggers, spying tools.

    +
  • +
  • +

    Tool

    +

    General system tools, like process killers/finders.

    +
  • +
  • +

    Unix

    +

    Potentially unwanted applications for Unix systems.

    +
  • +
  • +

    Win

    +

    Potentially unwanted applications for Windows systems.

    +
  • +
+

How do I ignore a ClamAV signature?

+

Creating an ignore file

+

Change Directory into the location where your ClamAV databases are stored. Create a file called ignore_list.ign2, for example, like this:

+

touch ignore_list.ign2

+

Ignore individual signatures

+

Place the signatures you'd like to ignore, each on it's own line, within the file ignore_list.ign2.

+

For example:

+

Signature.Ignore-1

+

Signature.Ignore-2

+

Uninstalling ClamAV

+

If you installed from source

+

If you installed using Autotools (0.103.0 and older)

+

If you installed from source, the easiest way to uninstall will require that same source and build configuration in order to uninstall.

+

Then run:

+
sudo make uninstall
+
+
+

Tip: If you don't have the old source / build directory from when you installed, you can get the source again and re-configure, as if for a build, so that you can use the build system to uninstall. Just be sure you use the same ./configure options as when you first installed. For example:

+
./configure
+sudo make uninstall
+
+
+

If you installed using CMake (0.104.0 and newer)

+

CMake doesn't provide a simple command to uninstall. However, CMake does build an install_manifest.txt file when you do the install. You can use the manifest to remove the installed files.

+

You will find the manifest in the directory where you compiled ClamAV. If you followed the recommendations in our Installing from Source section, then you will find it at <clamav source directory>/build/install_manifest.txt.

+

Feel free to inspect the file so you're comfortable knowing what you're about to delete.

+

Open a terminal and cd to that <clamav source directory>/build directory. Then run:

+
xargs rm < install_manifest.txt
+
+

This will leave behind the directories, and will leave behind any files added after install including the signature databases and any config files. You will have to delete these extra files yourself.

+
+

Tip: If you don't have the old source / build directory from when you installed, you can get the source again. Unlike with Autotools, you'll have to do more than just re-configure. You'll have to compile and re-install overtop of your existing install in order to get a new copy of the install_manifest.txt file. But once you do that, you should be able to use the command above to uninstall. For example:

+
cmake . &&
+make && sudo make install
+sudo xargs rm < install_manifest.txt
+
+
+

If you installed from packages

+
    +
  • +

    Debian/Ubuntu:

    +

    apt remove clamav

    +
  • +
  • +

    Redhat/Fedora:

    +

    dnf remove clamav*

    +

    or, on older systems:

    +

    yum remove clamav*

    +
  • +
  • +

    Mandriva:

    +

    urpme clamav

    +
  • +
  • +

    Gentoo:

    +

    emerge -C clamav

    +
  • +
  • +

    FreeBSD:

    +

    pkg delete clamav

    +
  • +
  • +

    OpenBSD:

    +

    pkg_delete clamav

    +
  • +
  • +

    NetBSD:

    +

    pkgin remove clamav

    +
  • +
  • +

    Slackware:

    +

    /etc/rc.d/rc.clamav stop; removepkg clamav

    +
  • +
+

Caveats

+

Make sure that you haven’t got old libraries (libclamav.so) lying around your filesystem. You can verify it using:

+
ldd `which freshclam`
+
+

Also make sure there is really only one version of ClamAV installed on your system:

+
whereis freshclam
+
+whereis clamscan
+
+

End of Life (EOL) Policy

+

This document describes the End of Life (EOL) policy for Long Term Support (LTS) feature releases and for regular (non-LTS) feature releases.

+

Skip to our Version Support Matrix to quickly check if your version is still supported.

+
+

Disclaimer: If this policy has to change due to a compatibility problem that prohibits the use of new detection technology, or impacts the stability of ClamAV infrastructure, we will announce the end of life for those versions four months before they become unsupported.

+
+

Long Term Support (LTS) Feature Releases

+

ClamAV 0.103 is the first Long Term Support (LTS) feature release.

+

LTS feature releases will be supported for at least three (3) years from the initial publication date of that LTS feature version. In other words, support for the LTS release "X.Y" starts when version "X.Y.0" is published and ends three years after.

+

Each LTS feature release will be supported with critical patch versions and access to download signatures for the duration of the three year support period.

+

A new LTS feature release will be identified approximately every two (2) years.

+

Users must stay up-to-date with the latest patch versions for continued support. As of November 3, that means version 0.103.4.

+

Regular (non-LTS) Feature Releases

+

Non-LTS feature releases will be supported with critical patch versions for at least four (4) months from the initial publication date of the next feature release or until the next-next feature release is published.

+

Non-LTS feature releases will be allowed access to download signatures until at least four (4) months after the next-next feature release is published.

+

Definitions

+
    +
  • +

    "feature release" -- A version starting with MAJOR.MINOR.0 to include all PATCH versions.

    +

    For example: 0.103.0 and 0.103.1 are both "patch versions" within the same "feature release".

    +
  • +
  • +

    "patch version" -- A specific MAJOR.MINOR.PATCH version.

    +

    For example: 0.103.1 is a "patch version" in the 0.103 "feature release".

    +
  • +
  • +

    "end of life" (EOL) -- The date after which the ClamAV Team will no longer support a feature version in any way.

    +

    After this point, all patch versions of that release may be blocked from downloading official signature content.

    +
  • +
  • +

    "long term support (LTS) release" -- A feature release that will get critical patch versions for an extended period.

    +

    The latest patch version will continue to get access to download signature databases for the duration of the support period. See above for policy details.

    +
  • +
  • +

    "regular (non-LTS) release" -- A feature release that will only be supported until a little after the next feature release.

    +

    The latest patch version will continue to get access to download signature databases a little longer than that, but users are encouraged to upgrade and may be vulnerable if a security patch is only published for the next feature release and they fail to upgrade. See above for policy details.

    +
  • +
  • +

    "support" -- The ClamAV project defines "support" in several ways:

    +
      +
    1. +

      Critical patch support:

      +

      The ClamAV Team provides critical patch versions for supported feature releases(**).

      +

      The Application Binary Interface (ABI) shall not change between patch versions within a given feature release. That is, the SONAMEs for the ClamAV libraries will remain the same and changes in a patch version will not break compatibility with older library versions.

      +

      Users are responsible for updating to the latest patch version for continued support.

      +
    2. +
    3. +

      Signature Database (CVD) Access:

      +

      Supported releases are allowed free access to download the latest signature databases.

      +

      Users must keep up-to-date with the latest patch version to maintain access to the official signature database content.

      +

      We reserve the right to block older/problematic patch versions 4 months after the release of a newer patch version.

      +
    4. +
    5. +

      New-Signature Load/Functional Testing:

      +

      The ClamAV Team will load-test new sigantures for functional correctness on all versions that are allowed access to download the official signature databases.

      +
    6. +
    7. +

      New-Signature False Positive Testing:

      +

      The ClamAV Team will perform false positive testing for new signature content, but only using the latest patch version of the latest feature release. False positive testing requires scanning large data sets. It is more time consuming than functional testing.

      +
    8. +
    +

    Cisco does not offer paid technical support or paid extended long term support for ClamAV.

    +
  • +
+

Version Support Matrix

+
+

Note: This markdown table is generated from a spreadsheet using this tool.

+
+ + + + + + + +
Feature releaseFirst PublishedLatest patch versionExpected End of Life (EOL)Signature load testing untilSignature FP testing untilDB downloads allowed untilPatch versions continue until
0.104Sep-3 20210.104.10.106 + 4 months0.106 + 4 months0.106 + 4 months0.105 + 4 months, or 0.106
0.103 LTSSep-14 20200.103.4Sep-14 2023Sep-14 20230.104 publishedSep-14 2023Sep-14 2023
0.102Oct-2 20190.102.4Jan-3 2022 (0.104 + 4 mo.)Jan-3 2022Jan-3 2022
0.101Dec-3 20180.101.5Jan-3 2022Jan-3 2022Jan-3 2022
0.100Apr-9 20180.100.3Oct-29 2021Oct-29 2021Oct-29 2021
0.99Dec-1 20150.99.4Mar-1 2021
+

Currently, every version from ClamAV 0.100 and down, including all patch versions, are unsupported, and are actively blocked from downloading new updates.

+

Additional Detail About Critical Patch Support

+

Like all bugs, security patches are first prepared for our upcoming feature release. Only security patches and other critical fixes or critical improvements are backported to previous feature releases.

+

The ClamAV Team is small and can't afford to publish patch versions for every release. To keep up momentum crafting new features and other improvements, our policy is to backport no further than 1 or 2 of the latest feature releases plus the Long Term Support (LTS) feature releases.

+

Critical Patches After a Breaking Change

+

On rare occasion we have made breaking changes to the way that users or other software interact with ClamAV or libclamav. The 0.101.0 release was one such example where we made a breaking change to the libclamav programming API. When this happens, we will provide extra time for users to upgrade to a newer feature release before we discontinue support for the older feature release.

+

The amount of extra time before we discontinue support will vary depending on the severity of the breaking change and the level of difficulty to upgrade.

+

Examples

+

Security Patches Less than four (4) Months After a Feature Release

+

If the non-disclosure / release date for a security patch falls within four (4) months since the previous feature release was published, we will craft a security patch version for the future feature release, the current feature release, the previous feature release, and any LTS feature releases.

+
+

Example: Let's say ClamAV 0.105.0 was just released and development begins on 0.106. But shortly after the release or as we're preparing for the release, it is discovered that 0.105.0 contains one or more security issues.

+

We understand with such a recent release, not everyone has had time to verify that they can indeed upgrade without some other supporting changes to their system or product.

+

In this situation, we would prepare a security patch version for:

+
    +
  • the 0.105 feature release (eg. 0.105.1),
  • +
  • for the 0.104 feature release (eg. 0.104.3),
  • +
  • and for the 0.103 LTS feature release.
  • +
+

Once the critical patch versions have been published, the same or equivalent fixes will be merged into the main branch for inclusion into the next feature release (0.106.0).

+
+

Security Patches More than four (4) Months After a Feature Release Has Been Available

+

If the non-disclosure / release date for a security patch falls after four (4) months since the previous feature release was published, we will only craft a security patch version for the future feature release, the current feature release, and any LTS feature releases.

+
+

Example: If 0.105.0 was released in January and a security issue was found in March, but the non-disclosure agreement allowed for the bug to be patched after the standard 90 days, then a security patch release will likely be prepared for release in early June.

+

This would exceed our 4-month policy, so we would publish the fix:

+
    +
  • in 0.105 (eg. 0.105.1),
  • +
  • and in 0.103 LTS, ...
  • +
+

... but would not publish a patch version for the 0.104 feature release.

+
+

Appendix

+ +

Terminology

+

General Terminology

+ + + + + + + + + + + + + +
TermDescription
adwareAdware is software that injects extra advertisements into other applications like your browser, or extra advertisements bundled by a third party into software that is ordinarily free of advertisements. Adware is often considered to be a type of Potentially Unwanted Application (PUA) when the the advertisements aren't used to fund development of the application but are injected by a third party.
bytecodeBytecode is a partially compiled executable that is platform agnostic but must be further compiled or otherwise interpreted in order to be executed. For ClamAV, "bytecode signature" refer to ClamAV plugin with a .cbc file extension. Some bytecode signatures detect specific types of malware, as the name suggests. Other bytecode signatures extend file parser support within ClamAV, enabling faster deployment of new ClamAV features.
CLDA CLD is the uncompressed ClamAV signature database archive. CLD files are created by FreshClam when a CVD or CLD database archive is updated with a CDIFF patch file.
CVDA CVD is an compressed ClamAV signature database archive that is signed and distributed by Cisco-Talos. "CVD" refers to the .cvd file extension, although when updated using a CDIFF database patch file, the extension is changed to .cld.
CDIFFA CDIFF is a patch file for a CVD or CLD database archive. CDIFFs allow for frequent updates to the ClamAV database set without requiring a large download each time.
endpointAn endpoint is a computer that a human interacts with, such as a laptop, desktop, or mobile device.
endpoint security suiteAn endpoint security suite is software that bundles a variety of services to protect a computer system. In addition to scheduled malware scanning, this may include features like a firewall, on-access scanning, network traffic monitoring, process behavioral monitoring, and other real time protection features.
false negativeA false negative is when a scan fails to alert on a file. You can report false negatives on clamav.net.
false positiveA false positive is when a scan incorrectly alerts on a file. You can report false positive detections on clamav.net.
malwareMalware is a general term for software intended to cause harm, disrupt, or gain unauthorized access to a computer system. Trojans, worms, miners, rootkits, viruses, keyloggers, and ransomware are all examples of different types of malware.
on-accessOn-access in the context of malware detection refers to a technology to scan files when they are created, opened, moved, or otherwise accessed. On-access scanning is one form of "real time protection". The scan may block access to the file and prevent access if an alert occurs, or it may simply scan as the file is being accessed to alert or take some action when the scan is complete.
PUAA Potentially Unwanted Application (PUA) or Potentially Unwanted Program (PUP) is a program that probably isn't malware, but little benefit to the user and is considered to be undesirable by most people. This may include software like crypto currency mining software, adware, and other software that may be legitimate but may also be used to take advantage of unsuspecting users. See our FAQ page about PUA signatures for more information.
+

ClamAV Components

+ + + + + + + + + + + + + + + +
ComponentDescription
clamav-configThis is a script for checking how ClamAV was compiled. clamav-config is not present on Windows installations.
clamav-milterClamAV-Milter is a daemon that performs email filter scanning through clamd.
clambc-compilerThe ClamAV Bytecode Compiler (ClamBC-Compiler or ClamBCC) is a compiler for building bytecode executable signature plugins. The Bytecode Compiler installed separately. See the ClamAV Bytecode Compiler project on Github for more details.
clambcClamBC is another signature manipulation tool specifically for bytecode signatures.
clamconfClamConf is a tool to check or generate ClamAV configuration files and collect additional information that may be needed to help remotely debug issues.
clamdClamD is a multi threaded daemon that listens for scan requests on a network socket or unix socket. Client applications for clamd include clamdtop, clamdscan, clamav-milter, and clamonacc. The socket interface is documented and there are also a variety of third-party clients for clamd. See the Community Projects page for more details.
clamdscanClamDScan is a command line program to scan files and directories through clamd.
clamdtopClamDTop is a command line GUI to monitor ClamD.
clamonaccClamOnAcc is a daemon that receives on-access events from the kernel for real-time protection scanning through ClamD. ClamOnAcc is only available for Linux at this time.
clamscanClamScan is a command line program to scan files and directories that does not require the clamd daemon.
freshclamFreshClam is the signature database (cvd) update tool.
libclamavThe ClamAV library enables you to build the ClamAV engine into your programs. The C programming API can be found in clamav.h.
libfreshclamThe FreshClam library enables you to build the signature update update features into your programs. The C programming API can be found in libfreshclam.h.
sigtoolSigTool is a signature database (cvd) manipulation tool for malware analysts and signature writers.
+

Private Local Mirrors

+

There are some situations in which it may be desirable to set up a private mirror for distributing ClamAV databases.

+

If you run ClamAV on many clients on your network, each new installation will download a copy of the database files. This is a waste of bandwidth and resources for your network and for our mirrors network.

+

Sometimes the servers which perform the scan are not directly connected to Internet and can only download updates from a server in the same network segment.

+

For people who face these problems, we recommend using one of the following solutions:

+ +

Use cvdupdate to serve whole databases and database patch files from a private mirror

+

You may use a tool named cvdupdate on a private mirror to maintain the latest CVD databases and CDIFF patch files.

+

This solution will allow you to host a mirror that functions in the same way as the official database CDN, serving CVD and CDIFF files. This means that your downstream freshclam clients will be able to update using the CDIFF patch files, which should save some bandwidth between your private mirror and your clients.

+

These instructions use a tool named cvdupdate. cvdupdate requires:

+
    +
  • Python 3.6 or newer.
  • +
  • An internet connection with DNS enabled.
  • +
  • It should work fine on Linux/Unix and on Windows.
  • +
+

IMPORTANT: Please do NOT use cvdupdate if you don't need to host a private database mirror. freshclam is far more efficient, even for a small cluster of installs, because it will update with CDIFF patches after the initial database downloads. cvdupdate, on the otherhand, will download both the new daily CDIFF and the daily CVD every day.

+

You can easily install cvdupdate using Python 3's Pip package manager:

+
pip3 install cvdupdate
+
+

(optional) Once installed, you may wish to configure where the databases are stored:

+
cvd config set --dbdir <your www path>
+
+

Now run this as often as you need, or at least once a day to download/update the databases:

+
cvd update
+
+

You may wish to set up a cron job to check for updates.

+

If you didn't set a custom database path, the databases will be stored in ~/.cvdupdate/database

+
+

Tip: You can use --help with any cvd command to learn more. For ore detailed instructions, or to report issues, please visit: https://github.com/Cisco-Talos/cvdupdate](https://github.com/Cisco-Talos/cvdupdate)

+
+

Once you have the database files, host them with your favorite webserver, or use the cvd serve test-webserver (not intended for production).

+

Next, you'll need to configure the freshclam clients so they'll update from your private mirror.

+

For freshclam.conf on your downstream freshclam clients, set:

+
# Replace `mirror.mylan` and `8000` with your domain and port number.
+DatabaseMirror http://mirror.mylan:8000
+
+

You may wish to set up a proxy to enable HTTPS. If you do, you can specify https instead of http:

+
DatabaseMirror https://mirror.mylan
+
+

You could also host the files in a subdirectory. E.g.:

+
DatabaseMirror http://mirror.mylan:8000/clamav
+
+

When you run freshclam on your client machines, they will still use a DNS query to clamav.net to find out if there should be an update before attempting to update from your private server. If your freshclam clients attempt to update before your private mirror updates, that's okay. The freshclam clients will tolerate being 1 version behind what was advertised on clamav.net.

+
+

Tip: If the freshclam clients will not have access to the internet to perform that DNS lookup, you may wish to set DNSDatabaseInfo no in your freshclam.conf file. freshclam may complain that the DNS lookup to "no" failed, which is fine. It will fall-back to checking the database version using an HTTP Range-request to your server.

+
+
+

CAUTION: If your freshclam clients cannot use DNS to check if there is an update, be certain that your private mirror's webserver supports HTTP Range-requests, or else it may serve the ENTIRE database CVD file when a freshclam client means to check if a newer version exists, and not just a small portion containing the database version.

+

The Python simple.http server does NOT support HTTP Range requests.

+
+

Use freshclam to serve only whole database files from a private mirror

+

You may use freshclam on a private mirror to maintain the latest CVD or CLD databases.

+

The freshclam program running on your private mirror will update using the CDIFF patch files. When you update a CVD database with ClamAV's CDIFF patching process, it produces a CLD "local" database. With this solution for hosting a private mirror, you will serve those CVD or CLD databases to downstream freshclam clients. Unlike when using cvdupdate, this option will not allow you to serve CDIFF patch files.

+
+

Tip: This method may be best if your public IP address is shared with other clients. At present we rate-limit CVD downloads by IP address. So if your public IP address is used by others, cvdupdate may be rate-limited when it attempts to download daily.cvd. But freshclam should never be rate limited for attempting to download the lateset CDIFF patch file.

+
+

This solution is simple to implement. But because you will not be serving CDIFF patch files, it is only effective if your clients are all on the same local network or if bandwidth between your private mirror and your clients is not an issue for you.

+

To get started, configure a local webserver on one of your machines (say mirror.mylan). Set up freshclam on that server so it downloads the database files from http://database.clamav.net and stores them in your webserver’s DocumentRoot directory.

+

For freshclam.conf on your private mirror, set:

+
# The private mirror will update from database.clamav.net.
+DatabaseMirror database.clamav.net
+
+# Customize the DatabaseDirectory so that FreshClam will update the DocumentRoot.
+DatabaseDirectory /your/server/www
+
+# Enable CLD compression to save bandwidth between your mirror and your clients.
+CompressLocalDatabase yes
+
+

Set up freshclam to run as a service or in a cron job so that your private mirror always serves the latest databases.

+

Next, you'll need to configure the freshclam clients so they'll update from your private mirror.

+

For freshclam.conf on your downstream freshclam clients, set:

+
# PrivateMirror is used instead of DatabaseMirror so that FreshClam will:
+# 1. Accept CVD or CLD files, not just CVD files.
+# 2. Use an HTTP Range-request to check if there is an update, rather than DNS.
+PrivateMirror http://mirror.mylan:8000
+
+# ScriptedUpdates is needed because you won't be serving CDIFF files.
+ScriptedUpdates no
+
+

When you run freshclam on your client machines, they should check for updates from your private server over HTTP by downloading just the database header*. If there is a new version, the client will download the whole CVD or CLD file from your private server to update.

+
+

*Important: Make sure your HTTP server will accept and handle HTTP Range requests. If yours does not, then each time a client checks for an update it will download the whole database!

+

The Python simple.http server does NOT support HTTP Range requests.

+
+

Use an HTTP proxy

+

This solution is really easy to implement and is bandwidth efficient.

+

Install a proxy server that supports caching files (e.g. squid) and then tell your freshclam clients to use it. This can be done by setting the HTTPProxyServer parameter in freshclam.conf (see man 5 freshclam.conf for the details).

+

Microsoft Authenticode Signature Verification

+

About Microsoft Authenticode

+

Authenticode is Microsoft's system for using digital signatures to ensure that programs to be run/installed on Windows systems come from a verified source and has not been modified by anyone else. At a high level, it works by having software developers:

+
    +
  1. Obtain a code-signing certificate from a certificate authority trusted by the Windows OS.
  2. +
  3. Compute digital signatures for executables and related software installation files using that certificate.
  4. +
  5. Include the signatures as part of the software execution/installation process so that Windows can use them in the verification process.
  6. +
+

In addition, Authenticode signatures can be countersigned by a time-stamping service that allows signature verification to succeed even if the code-signing certificate expires or gets revoked.

+

For more information, check-out the following resources:

+ +

Authenticode and ClamAV

+

ClamAV supports parsing the Authenticode section and performing signature verification on a given executable to determine whether it should be trusted (based on rules loaded in from ClamAV .crb files). An overview of this process, including information on the .crb file format and on how to add new trusted certificate entries, is explained in the Authenticode Certificate Chain Verification ClamAV blog post.

+

There are a few things not covered in the blog post that are worth mentioning:

+
    +
  • +

    As of ClamAV 0.102, leaf certificates (the ones actually issued to the entity signing the binary) may be used for certificate verification in addition to certificates that issued the leaf certificate (and certificates higher up in the chain) can be used.

    +
  • +
  • +

    As of ClamAV 0.102, .crb rules may also be used to block malicious executables where in previous versions these block list entries just override .crb rules that would otherwise trust a given sample.

    +
  • +
  • +

    SigTool offers the --print-certs flag, which can be used to show information about embedded Authenticode signatures without having to first match on a signature (which is currently a requirement for clamscan)

    +
  • +
  • +

    External Authenticode signatures contained in .cat files can be loaded in to ClamAV by passing a -d flag and indicating the path to the .cat file from which to load signatures. Note, however, that at least one certificate in the .cat file's certificate chain must be trusted (in other words, it must have a backing .crb trusted certificate rule.)

    +
  • +
+

Helpful Info for Working with Authenticode Signatures

+

Below is some useful information collected when improving ClamAV support for Authenticode signatures.

+

Format Specifications

+

The Windows Authenticode 2008 specification document can be found at the link below. Note, however, that it is not 100% accurate. For instance, the documented steps for computing the Authenticode hash are not correct in the case where you have sections that overlap with the PE header or with one another.

+ +

Verifying the Signature

+

On Linux, osslsigncode can be used to verify a signature:

+
    $ osslsigncode verify /path/to/signed/file
+    Current PE checksum   : 00092934
+    Calculated PE checksum: 00092934
+
+    Message digest algorithm  : SHA256
+    Current message digest    : 56924EB391B1B04572B1841ED5D5C10927CE7D6E9553A69F994B9BA855A73933
+    Calculated message digest : 56924EB391B1B04572B1841ED5D5C10927CE7D6E9553A69F994B9BA855A73933
+
+    Signature verification: ok
+
+    Number of signers: 1
+        Signer #0:
+            Subject: /C=US/ST=California/L=Mountain View/O=Google Inc/CN=Google Inc
+            Issuer : /C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 SHA256 Code Signing CA
+
+    Number of certificates: 2
+        Cert #0:
+            Subject: /C=US/ST=California/L=Mountain View/O=Google Inc/CN=Google Inc
+            Issuer : /C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 SHA256 Code Signing CA
+        Cert #1:
+            Subject: /C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 SHA256 Code Signing CA
+            Issuer : /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
+
+    Succeeded
+
+

On Windows,

+

AnalyzePESig is a great tool for displaying signature information. In addition, +signtool can be used.

+

NOTE that the machine on which these commands is run should have Internet connectivity so that revocation lists can be consulted. Otherwise, Windows may default to assuming that none of the certificates are revoked.

+

There is also the verify-sigs python script that performs verification, but this script is no longer maintained.

+

Extracting the Signature

+

On Linux, the osslsigncode command can be used to extract the contents of the PE security section:

+

osslsigncode extract-signature -in /path/to/exe -out /path/to/extracted

+
+

Note: This will also extract the 8-byte WIN_CERTIFICATE structure data. +To skip this data, use:

+
dd if=/path/to/extracted of=/path/to/extracted.p7b bs=1 skip=8`
+
+
+

Inspecting the Signature

+

On Linux, openssl has some useful functions for printing the certificate information and parsing the PKCS7 ASN1:

+
    $ openssl pkcs7 -inform der -print_certs -in extracted.p7b -noout -text
+    Certificate:
+        Data:
+            Version: 3 (0x2)
+            Serial Number:
+                2a:9c:21:ac:aa:a6:3a:3c:58:a7:b9:32:2b:ee:94:8d
+        Signature Algorithm: sha256WithRSAEncryption
+            Issuer: C=US, O=Symantec Corporation, OU=Symantec Trust Network, CN=Symantec Class 3 SHA256 Code Signing CA
+            Validity
+                Not Before: Dec 16 00:00:00 2015 GMT
+                Not After : Dec 16 23:59:59 2018 GMT
+            Subject: C=US, ST=California, L=Mountain View, O=Google Inc, CN=Google Inc
+            Subject Public Key Info:
+                Public Key Algorithm: rsaEncryption
+                    Public-Key: (2048 bit)
+                    Modulus:
+                        00:c4:0d:82:c4:41:29:28:e5:fd:0c:3f:a5:c7:0e:
+                        66:bd:a5:c4:8b:b3:8a:ac:84:03:9f:84:2e:38:df:
+                        06:b1:4e:fd:33:60:58:38:36:dd:22:cf:df:f1:50:
+                        1f:47:f1:55:05:c1:81:01:e7:28:3e:ff:5f:89:12:
+                        09:ea:df:aa:17:49:2c:71:ab:48:d1:9d:2e:f4:51:
+                        e0:03:e0:f7:16:6c:7b:0c:22:75:6d:7e:1f:49:c4:
+                        43:28:88:41:dc:6c:ed:13:2a:03:99:eb:62:14:f9:
+                        35:26:6e:12:2c:03:e2:f7:81:b9:1a:05:67:06:7c:
+                        a6:1a:5b:ed:20:15:e5:2d:83:de:8e:36:fa:1e:08:
+                        41:1c:1a:48:9f:b6:f1:c3:2f:02:13:4b:a7:ca:ba:
+                        ef:1c:58:6f:8e:d3:0f:14:a4:0b:2b:5d:ba:f4:5a:
+                        a3:0d:64:34:a5:8a:d7:8f:4d:22:66:4d:a4:ae:e1:
+                        f9:cd:c6:58:e6:c6:11:77:32:df:ba:df:39:48:8a:
+                        d1:27:d7:33:77:a8:c9:e4:5e:ed:fa:12:cf:f3:fd:
+                        fa:ee:ab:80:86:13:34:eb:5a:7e:6f:6c:1b:ee:d8:
+                        4b:b2:cc:77:98:87:ac:ca:f5:bb:64:6f:49:1e:5b:
+                        91:63:50:1f:63:2d:83:27:73:07:9f:2b:16:f4:7b:
+                        71:29
+                    Exponent: 65537 (0x10001)
+            X509v3 extensions:
+                X509v3 Basic Constraints:
+                    CA:FALSE
+                X509v3 Key Usage: critical
+                    Digital Signature
+                X509v3 Extended Key Usage:
+                    Code Signing
+                X509v3 Certificate Policies:
+                    Policy: 2.16.840.1.113733.1.7.23.3
+                    CPS: https://d.symcb.com/cps
+                    User Notice:
+                        Explicit Text: https://d.symcb.com/rpa
+
+                X509v3 Authority Key Identifier:
+                    keyid:96:3B:53:F0:79:33:97:AF:7D:83:EF:2E:2B:CC:CA:B7:86:1E:72:66
+
+                X509v3 CRL Distribution Points:
+
+                    Full Name:
+                    URI:http://sv.symcb.com/sv.crl
+
+                Authority Information Access:
+                    OCSP - URI:http://sv.symcd.com
+                    CA Issuers - URI:http://sv.symcb.com/sv.crt
+
+                Netscape Cert Type:
+                    Object Signing
+                1.3.6.1.4.1.311.2.1.27:
+                    0.......
+        Signature Algorithm: sha256WithRSAEncryption
+            23:e7:93:93:af:db:a8:4d:af:af:54:e8:d8:26:95:80:cd:23:
+            91:70:ed:0b:5b:b1:e9:d8:dd:1e:40:37:78:97:18:ed:9f:e5:
+            84:67:85:06:50:b5:f1:ab:e6:83:5a:17:7b:51:be:7f:18:c6:
+            47:5e:2b:aa:f4:a0:1f:35:3e:05:9f:43:40:f7:9f:d1:f4:e1:
+            a7:02:f3:8e:c9:71:fe:18:37:48:42:d7:e4:36:73:10:92:d4:
+            d8:d9:1c:c4:26:58:18:67:b6:24:22:69:63:02:f7:49:51:6b:
+            75:f6:b4:7d:56:ff:2c:f4:88:f7:67:6f:08:86:f3:8b:0b:30:
+            02:7f:6d:92:d9:4e:bd:99:f7:7b:74:86:0c:cb:b9:ad:2c:bf:
+            44:79:a8:00:82:9c:62:f4:aa:11:df:d2:bf:f0:e1:92:28:11:
+            90:bb:5e:33:88:86:96:4d:dd:0b:af:c3:67:a1:95:2d:44:32:
+            c6:fa:f7:b8:80:c1:4e:38:be:1f:b6:84:f7:f1:21:31:67:49:
+            a8:9f:8a:75:07:df:3b:3a:c3:ea:72:cd:40:7f:a7:da:7c:c9:
+            2e:7c:a9:0c:f1:5d:5c:82:42:62:b9:49:94:8f:70:e6:a5:c0:
+            5f:17:fb:40:36:c1:3a:89:63:03:1c:3f:66:a0:3d:8f:a1:4c:
+            4e:5c:ac:bf
+    ...
+
+
    $ openssl asn1parse -inform der -i -in extracted.p7b
+        0:d=0  hl=4 l=6984 cons: SEQUENCE
+        4:d=1  hl=2 l=   9 prim:  OBJECT            :pkcs7-signedData
+    15:d=1  hl=4 l=6969 cons:  cont [ 0 ]
+    19:d=2  hl=4 l=6965 cons:   SEQUENCE
+    23:d=3  hl=2 l=   1 prim:    INTEGER           :01
+    26:d=3  hl=2 l=  15 cons:    SET
+    28:d=4  hl=2 l=  13 cons:     SEQUENCE
+    30:d=5  hl=2 l=   9 prim:      OBJECT            :sha256
+    41:d=5  hl=2 l=   0 prim:      NULL
+    43:d=3  hl=2 l=  92 cons:    SEQUENCE
+    45:d=4  hl=2 l=  10 prim:     OBJECT            :1.3.6.1.4.1.311.2.1.4
+    57:d=4  hl=2 l=  78 cons:     cont [ 0 ]
+    59:d=5  hl=2 l=  76 cons:      SEQUENCE
+    61:d=6  hl=2 l=  23 cons:       SEQUENCE
+    63:d=7  hl=2 l=  10 prim:        OBJECT            :1.3.6.1.4.1.311.2.1.15
+    75:d=7  hl=2 l=   9 cons:        SEQUENCE
+    77:d=8  hl=2 l=   1 prim:         BIT STRING
+    80:d=8  hl=2 l=   4 cons:         cont [ 0 ]
+    82:d=9  hl=2 l=   2 cons:          cont [ 2 ]
+    84:d=10 hl=2 l=   0 prim:           cont [ 0 ]
+    86:d=6  hl=2 l=  49 cons:       SEQUENCE
+    88:d=7  hl=2 l=  13 cons:        SEQUENCE
+    90:d=8  hl=2 l=   9 prim:         OBJECT            :sha256
+    101:d=8  hl=2 l=   0 prim:         NULL
+    103:d=7  hl=2 l=  32 prim:        OCTET STRING      [HEX DUMP]:56924EB391B1B04572B1841ED5D5C10927CE7D6E9553A69F994B9BA855A73933
+    ...
+
+

On Windows, the certutil executable has a great ASN parser:

+
    C:\>certutil -asn extracted.p7b
+    0000: 30 82 1b 48                               ; SEQUENCE (1b48 Bytes)
+    0004: |  06 09                                  ; OBJECT_ID (9 Bytes)
+    0006: |  |  2a 86 48 86 f7 0d 01 07  02
+        |  |     ; 1.2.840.113549.1.7.2 PKCS 7 Signed
+    000f: |  a0 82 1b 39                            ; OPTIONAL[0] (1b39 Bytes)
+    0013: |     30 82 1b 35                         ; SEQUENCE (1b35 Bytes)
+    0017: |        02 01                            ; INTEGER (1 Bytes)
+    0019: |        |  01
+    001a: |        31 0f                            ; SET (f Bytes)
+    001c: |        |  30 0d                         ; SEQUENCE (d Bytes)
+    001e: |        |     06 09                      ; OBJECT_ID (9 Bytes)
+    0020: |        |     |  60 86 48 01 65 03 04 02  01
+        |        |     |     ; 2.16.840.1.101.3.4.2.1 sha256 (sha256NoSign)
+    0029: |        |     05 00                      ; NULL (0 Bytes)
+    002b: |        30 5c                            ; SEQUENCE (5c Bytes)
+    002d: |        |  06 0a                         ; OBJECT_ID (a Bytes)
+    002f: |        |  |  2b 06 01 04 01 82 37 02  01 04
+        |        |  |     ; 1.3.6.1.4.1.311.2.1.4 SPC_INDIRECT_DATA_OBJID
+    0039: |        |  a0 4e                         ; OPTIONAL[0] (4e Bytes)
+    003b: |        |     30 4c                      ; SEQUENCE (4c Bytes)
+    003d: |        |        30 17                   ; SEQUENCE (17 Bytes)
+    003f: |        |        |  06 0a                ; OBJECT_ID (a Bytes)
+    0041: |        |        |  |  2b 06 01 04 01 82 37 02  01 0f
+        |        |        |  |     ; 1.3.6.1.4.1.311.2.1.15 SPC_PE_IMAGE_DATA_OBJID
+    004b: |        |        |  30 09                ; SEQUENCE (9 Bytes)
+    ...
+
+

There is also a website that offers ASN1 parser and allows you to interactively +hide/view parts of the structure:

+ +

Creating Signed Executables

+

For Linux, Didier Stevens has a great post about how to create signed binaries using self-signed certificates:

+ +

On Windows, a program called signtool ships with the Windows SDK and can be used. See the following for tutorials/examples:

+ +

Samples with Interesting Authenticode Signatures

+

Below are some PE files with interesting Authenticode signatures. These are probably only interesting to other researchers who are looking at Authenticode in-depth. All samples are available via VirusTotal.

+
    +
  • +

    SHA256-based code-signing signature without a countersignature

    +
      +
    • 8886d96e9ed475e4686ffba3d242e97836de8a56b75cc915e21bb324cc89de03
    • +
    +
  • +
  • +

    SHA256-based code-signing sig and SHA1-based timestamping countersig

    +
      +
    • 20367d0e3a5ad12154095d424b8d9818c33e7d6087525e6a3304ef6c22a53665
    • +
    +
  • +
  • +

    SHA384-based cert used in the code-signing chain

    +
      +
    • 2249611fef630d666f667ac9dc7b665d3b9382956e41f10704e40bd900decbb8
    • +
    +
  • +
  • +

    Uses SHA512 to compute the Authenticode hash

    +
      +
    • eeb5469a214d5aac1dcd7e8415f93eca14edc38f47d1e360d3d97d432695207a
    • +
    +
  • +
  • +

    Signed by an MS root cert that doesn't have a KU or EKU specified

    +
      +
    • 69b61b2c00323cea3686315617d0f452e205dae10c47e02cbe1ea96fea38f582
    • +
    +
  • +
  • +

    Has a v1 x509 cert and uses MD2-based hashing

    +
      +
    • 1cb16f94cebdcad7dd05c8537375a6ff6379fcdb08528fc83889f26efaa84e2a
    • +
    +
  • +
  • +

    Countersignature with version 0 instead of version 1

    +
      +
    • 145fbbf59b1071493686bf41f4eb364627d8be3c9dc8fb927bbe853e593864ec
    • +
    +
  • +
  • +

    Countersignature with contentType == timestampToken instead of pkcs7-data

    +
      +
    • 8a364e0881fd7201cd6f0a0ff747451c9b93182d5699afb28ad8466f7f726660
    • +
    +
  • +
  • +

    Countersignature with AlgoIdentifier sha1WithRSAEncryption instead of SHA1

    +
      +
    • 2aa6b18d509090c60c3e4ecdd8aeb16e5f149807e3404c86892112710eab576d
    • +
    +
  • +
  • +

    Countersignature uses v1 x509 certs

    +
      +
    • 934860a4ac2446240e4c7053ddc27ff4c2463d4ad433cc28c9fcc2ea4690fb86
    • +
    +
  • +
  • +

    Certificate chain has old certificates without a version

    +
      +
    • 374a31b20fbafdcd31d52ae11a0dcad58baba556c8942a3cdfae0bb96ae117a1
    • +
    +
  • +
  • +

    Has extra data after the PKCS7, a violation of MS13-098

    +
      +
    • 0ee196bb23f0eafe4f61d30bf6c676fd7365cb12ae66a6bde278851e91901ac1
    • +
    +
  • +
  • +

    Authenticode signature covers data not in a section

    +
      +
    • 0123c163ac981e639565caff72ee3af2df7174613ee12003ac89124be461c6e6
    • +
    +
  • +
  • +

    Authenticode signature with a section that overlaps the PE header (UPX-packed)

    +
      +
    • 0059fb3f225c5784789622eeccb97197d591972851b63d59f5bd107ddfdb7a21
    • +
    +
  • +
  • +

    Authenticode signature with overlapping sections

    +
      +
    • 014b66cf2cef39620e9a985d237971b8cf272043e9ac372d5dcef44db754a1d2
    • +
    +
  • +
  • +

    Uses certs with no NULL after AlgorithmIdentifier OID

    +
      +
    • 66b797b3b4f99488f53c2b676610dfe9868984c779536891a8d8f73ee214bc4b
    • +
    +
  • +
  • +

    Uses an ASN1 indefinite length object

    +
      +
    • 8ca912e397a9e1b0cc54c216144ff550da9d43610392208193c0781b1aa5d695
    • +
    +
  • +
  • +

    Unexpected contentType for embedded mode signature (copied from a .cat?)

    +
      +
    • 6ed9b5f6d32f94b3d06456b176c8536be123e1047763cc0a31c6e8fd6a0242b1
    • +
    +
  • +
  • +

    Security directory appears to overlap with the PE header

    +
      +
    • ff482f69f2183b5fd3c1b45d9006156524b8f8a5f518e33d6e92ea079787e64d
    • +
    +
  • +
  • +

    x509 cert with a public key using exponent 3

    +
      +
    • 012760e582e541c6dd34a2cbd5d053f402eebcb8b60ed4a88fecb5589bd17bb9
    • +
    +
  • +
  • +

    x509 UTCDate is missing the seconds field

    +
      +
    • 05de45fd6a406dc147a4c8040a54eee947cd6eba02f28c0279ffd1a229e17130
    • +
    +
  • +
  • +

    x509 cert with a negative serial number

    +
      +
    • 6218d50eb5c898acd3482daaea8f615b4f1f87ef0d06220cc1d7f700bc35888b
    • +
    +
  • +
+

Additional References

+
    +
  • +

    What are x509 certificates? (Provides an overview of the ASN1 structure of x509 certificates)

    +
  • +
  • +

    Signed Malware (Research papers on signed malware with interactive tables of malicious code signing certs)

    +
  • +
+

ClamAV File Types

+

ClamAV has two file typing systems for filtering signature matches: Target Types and File Types.

+

Target Types

+

A Target Type is an integer that indicates which kind of file the signature will match against. Target Type notation was first created for the purposes writing efficient signatures. A signature with a target type of 0 will be run against every file type, and thus is not ideal. However, the Target Type notation is limited and it may be unavoidable.

+

Although the newer CL_TYPE string name notation has replaced the Target Type for some signature formats, many signature formats require a target type number.

+

This is the current list of available Target Types:

+ + + + + + + + + + + + + + +
Target TypeDescription
0any file
1Portable Executable, both 32- and 64-bit
2OLE2 containers, including specific macros. Primarily used by MS Office and MSI installation files
3HTML (normalized)
4Mail file
5Graphics
6ELF
7ASCII text file (normalized)
8Unused
9Mach-O files
10PDF files
11Flash files
12Java class files
+
+

Important: HTML, ASCII, Javascript are all normalized:

+
    +
  • ASCII - All lowercase.
  • +
  • HTML - Whitespace transformed to spaces, tags/tag attributes normalized, all lowercase.
  • +
  • Javascript - All strings are normalized (hex encoding is decoded), numbers are parsed and normalized, local variables/function names are normalized to n001 format, argument to eval() is parsed as JS again, unescape() is handled, some simple JS packers are handled, output is whitespace normalized.
  • +
+
+

File Types

+

ClamAV maintains it's own file typing format and assigns these types using either:

+
    +
  • +

    Evaluation of a unique sequence of bytes at the start of a file (File Type Magic).

    +
  • +
  • +

    File type indicators when parsing container files.

    +
      +
    • For example: +CL_TYPE_SCRIPT may be assigned to data contained in a PDF when the PDF indicates that a stream of bytes is "Javascript"
    • +
    +
  • +
  • +

    File type determination based on the names or characteristics contained within the file.

    +
      +
    • For example: +CL_TYPE_OOXML_WORD may be assigned to a Zip file containing files with specific names.
    • +
    +
  • +
+

ClamAV File Types are prefixed with CL_TYPE_. The following is an exhaustive list of all current file types.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CL_TYPEDescription
CL_TYPE_7Z7-Zip Archive
CL_TYPE_7ZSFXSelf-Extracting 7-Zip Archive
CL_TYPE_APMDisk Image - Apple Partition Map
CL_TYPE_ARJARJ Archive
CL_TYPE_ARJSFXSelf-Extracting ARJ Archive
CL_TYPE_AUTOITAutoIt Automation Executable
CL_TYPE_BINARY_DATAbinary data
CL_TYPE_BINHEXBinHex Macintosh 7-bit ASCII email attachment encoding
CL_TYPE_BZBZip Compressed File
CL_TYPE_CABSFXSelf-Extracting Microsoft CAB Archive
CL_TYPE_CPIO_CRCCPIO Archive (CRC)
CL_TYPE_CPIO_NEWCCPIO Archive (NEWC)
CL_TYPE_CPIO_ODCCPIO Archive (ODC)
CL_TYPE_CPIO_OLDCPIO Archive (OLD, Little Endian or Big Endian)
CL_TYPE_CRYPTFFFiles encrypted by CryptFF malware
CL_TYPE_DMGApple DMG Archive
CL_TYPE_EGGESTSoft EGG Archive, new in 0.102
CL_TYPE_ELFELF Executable (Linux/Unix program or library)
CL_TYPE_GIFGIF Graphics File, new in 0.103
CL_TYPE_GPTDisk Image - GUID Partition Table
CL_TYPE_GRAPHICSOther graphics files; BMP, JPEG2000
CL_TYPE_GZGZip Compressed File
CL_TYPE_HTML_UTF16Wide-Character / UTF16 encoded HTML
CL_TYPE_HTMLHTML data
CL_TYPE_HWP3Hangul Word Processor (3.X)
CL_TYPE_HWPOLE2Hangul Word Processor embedded OLE2
CL_TYPE_INTERNALInternal properties
CL_TYPE_ISHIELD_MSIWindows Install Shield MSI installer
CL_TYPE_ISO9660ISO 9660 file system for optical disc media
CL_TYPE_JAVAJava Class File
CL_TYPE_JPEGJPEG Graphics File, new in 0.103.1
CL_TYPE_LNKMicrosoft Windows Shortcut File
CL_TYPE_MACHO_UNIBINUniversal Binary/Java Bytecode
CL_TYPE_MACHOApple/NeXTSTEP Mach-O Executable file format
CL_TYPE_MAILEmail file
CL_TYPE_MBRDisk Image - Master Boot Record
CL_TYPE_MHTMLMHTML Saved Web Page
CL_TYPE_MSCABMicrosoft CAB Archive
CL_TYPE_MSCHMMicrosoft CHM help archive
CL_TYPE_MSEXEMicrosoft EXE / DLL Executable file
CL_TYPE_MSOLE2Microsoft OLE2 Container file
CL_TYPE_MSSZDDMicrosoft Compressed EXE
CL_TYPE_NULSFTNullSoft Scripted Installer program
CL_TYPE_OLD_TARTAR archive (old)
CL_TYPE_OOXML_HWPHangul Office Open Word Processor (5.X)
CL_TYPE_OOXML_PPTMicrosoft Office Open XML PowerPoint
CL_TYPE_OOXML_WORDMicrosoft Office Open Word 2007+
CL_TYPE_OOXML_XLMicrosoft Office Open Excel 2007+
CL_TYPE_PART_HFSPLUSApple HFS+ partition
CL_TYPE_PDFAdobe PDF document
CL_TYPE_PNGPNG Graphics File, new in 0.103
CL_TYPE_POSIX_TARTAR archive
CL_TYPE_PSPostscript
CL_TYPE_RARRAR Archive
CL_TYPE_RARSFXSelf-Extracting RAR Archive
CL_TYPE_RIFFResource Interchange File Format container formatted file
CL_TYPE_RTFRich Text Format document
CL_TYPE_SCRENCFiles encrypted by ScrEnc malware
CL_TYPE_SCRIPTGeneric type for scripts (Javascript, Python, etc)
CL_TYPE_SISSymbian OS Software Installation Script Archive
CL_TYPE_SWFAdobe Flash File (LZMA, Zlib, or uncompressed)
CL_TYPE_TEXT_ASCIIASCII text
CL_TYPE_TEXT_UTF16BEUTF-16BE text
CL_TYPE_TEXT_UTF16LEUTF-16LE text
CL_TYPE_TEXT_UTF8UTF-8 text
CL_TYPE_TIFFTIFF Graphics File (Little or Big Endian), new in 0.103.1
CL_TYPE_TNEFMicrosoft Outlook & Exchange email attachment format
CL_TYPE_UUENCODEDUUEncoded (Unix-to-Unix) binary file (Unix email attachment)
CL_TYPE_XARXAR Archive
CL_TYPE_XDPAdobe XDP - Embedded PDF
CL_TYPE_XML_HWPHangul Word Processor XML (HWPML) Document
CL_TYPE_XML_WORDMicrosoft Word 2003 XML Document
CL_TYPE_XML_XLMicrosoft Excel 2003 XML Document
CL_TYPE_XZXZ Archive
CL_TYPE_ZIPZip Archive
CL_TYPE_ZIPSFXSelf-Extracting Zip Archive
+

Versions & Functionality Levels (FLEVELs)

+

The Functionality Level (or FLEVEL) is an integer that signatures may use to define which versions of ClamAV the signature features support. It is up to the signature writers to select the correct FLEVEL or range of FLEVELs when writing a signature so that it does not cause failures in older versions of ClamAV.

+

Setting appropriate FLEVELs in signatures is particularly crucial when using features added in the last 3-4 major release versions.

+

ClamAV Version to FLEVEL chart

+
+

Note: This markdown table is generated from a spreadsheet using this tool.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Release DateReleaseFLEVELFunctionalityLevel (bytecode enum)clamav lib.sofreshclam lib.soAPI/ABI changes, major features, other notes
n/a1.0.0160FUNC_LEVEL_1_0n/an/an/an/a
n/a0.105.0150FUNC_LEVEL_0105n/an/an/an/a
Nov-20210.104.1141FUNC_LEVEL_0104_110:00:019.1.02:02:002.0.2Critical bug fixes
Sep-20210.104.0140FUNC_LEVEL_010410:00:019.1.02:02:002.0.2CMake stabilized (autotools removed); Added db load/compile/free callbacks to clamav.h API.
Nov-20210.103.4125FUNC_LEVEL_0103_49:05:009.0.52:01:002.0.1Critical bug fixes
Jun-20210.103.3124FUNC_LEVEL_0103_39:05:009.0.52:01:002.0.1Critical bug fixes
Apr-20210.103.2123FUNC_LEVEL_0103_29:05:009.0.52:01:002.0.1Security fixes
Feb-20210.103.1122FUNC_LEVEL_0103_19:05:009.0.52:01:002.0.1Fix PNG parser; Loosen GIF format validation (trailer byte); Fix scan issue on Windows RAM disks; Fix clamonacc FD-passing; Enable JPEG & TIFF validation; Add CL_TYPE_JPEG, CL_TYPE_TIFF; Adds ALERT_BROKEN_IMAGES scan heuristics option
Sep-20200.103.0120FUNC_LEVEL_01039:05:009.0.52:01:002.0.1Add CL_TYPE_PNG, CL_TYPE_GIF; Add XLM macro detection; Add bzip2 & LZMA decompression functions to bytecode API
Jul-20200.102.4115FUNC_LEVEL_0102_49:04:009.0.42:00:002.0.0Security fixes
May-20200.102.3114FUNC_LEVEL_0102_39:04:009.0.42:00:002.0.0Security fixes
Feb-20200.102.2113FUNC_LEVEL_0102_29:04:009.0.42:00:002.0.0Security fixes
Nov-20190.102.1112FUNC_LEVEL_0102_19:04:009.0.42:00:002.0.0Security fixes; Significant load time improvement for LDBs with common pattern prefixes
Oct-20190.102.0110FUNC_LEVEL_01029:04:009.0.42:00:002.0.0BytecodeKind: BC_ELF_UNPACKER, BC_MACHO_UNPACKER; Add CL_TYPE_EGG/CL_TYPE_EGG_SFX; Added scan time limit
Nov-20190.101.5106FUNC_LEVEL_0101_59:04:009.0.4Security fixes; Significant load time improvement for LDBs with common pattern prefixes
Aug-20190.101.4105FUNC_LEVEL_0101_49:04:009.0.4Security fixes; Added scan time limit, bzip vuln fix
Aug-20190.101.3102FUNC_LEVEL_0101_39:03:009.0.3Security fixes; Flevel not incremented (whoops)
Mar-20190.101.2102FUNC_LEVEL_0101_29:02:009.0.2Security fixes; Flevel not incremented (whoops)
Jan-20190.101.1102FUNC_LEVEL_0101_19:01:009.0.1Fix to clamav.h header; Adds clamav-types.h
Dec-20180.101.0100FUNC_LEVEL_01019:00:009.0.0Non-backwards compatible API/ABI change: Added filename to scanfile & scandesc, and scan options became a struct; RAR5 Support; Byte-Compare Subsigs; Add CL_TYPE_LNK
Mar-20190.100.394FUNC_LEVEL_0100_38:02:017.1.2Security fixes
Sep-20180.100.293FUNC_LEVEL_0100_28:01:017.1.1Security fixes; Some lenience changes to FreshClam
Jun-20180.100.192FUNC_LEVEL_0100_18:01:017.1.1Security fixes; Add support for HTTPS in ClamSubmit
Mar-20180.100.090FUNC_LEVEL_01008:01:017.1.1Feature release 2 years in dev't; Many improvements; Notably Container/Intermediates changes; Changes to wildcard signatures
Mar-20180.99.485FUNC_LEVEL_099_48:01:017.1.1Security fixes; Other important bug fixes
Jan-20180.99.384FUNC_LEVEL_099_38:01:017.1.1Security fixes; Minor bug fixes
May-20160.99.282FUNC_LEVEL_099_28:01:017.1.1Various bug fixes
Mar-20160.99.182FUNC_LEVEL_099_18:01:017.1.1Security fixes; HWP support
Dec-20150.99.081FUNC_LEVEL_0998:01:017.1.1Add Yara and PCRE support; Add 'other' targets type (14) for non-listed target types; Improved on-access scanning.
Apr-20150.98.780FUNC_LEVEL_098_77:26:016.1.26Security fixes; MSXML & PDF fixes
Jan-20160.98.679FUNC_LEVEL_098_67:25:016.1.25Security fixes; Other bug fixes
Nov-20140.98.579FUNC_LEVEL_098_57:22:016.1.22Added internal target type (13); File properties JSON output.
Jun-20140.98.477FUNC_LEVEL_098_47:23:016.1.23
May-20140.98.377FUNC_LEVEL_098_37:22:016.1.22
May-20140.98.277FUNC_LEVEL_098_27:22:016.1.22Add engine_options bit field (and DisableCache option); Add stats callbacks and callback context
Jan-20140.98.176FUNC_LEVEL_098_17:20:016.1.20Added XZ support and ForceToDisk scan option; Added Libxml2 dependency + XAR, DMG, HFS+/HFSX support; Added FTM type 4 (for in-buffer partition magic, analogous to type 0 for files)
Sep-20130.98.074FUNC_LEVEL_0987:18:016.1.18Add Target-Types 10 - 13 (PDF, FLASH, JAVA, and INTERNAL); Introduced all-scan options; SWF and Java targets (11 & 12); Introduced with "SE" offset modifier; Introduced with ISO9660 scanning support; Add wild card bracket notation{} for body-based signatures; Disable SWF parser
Apr-20130.97.869FUNC_LEVEL_097_87:17:016.1.17Security fixes
Mar-20130.97.768FUNC_LEVEL_097_77:16:016.1.16Security fixes
Sep-20120.97.667FUNC_LEVEL_097_67:15:016.1.15Fixed error-handling issues
Jun-20120.97.565FUNC_LEVEL_097_57:14:016.1.14First Sourcefire ClamAV release
Mar-20120.97.464FUNC_LEVEL_097_47:13:016.1.13Support comment lines in ALL DB files
Oct-20110.97.363FUNC_LEVEL_097_37:12:016.1.12
Jul-20110.97.262FUNC_LEVEL_097_27:11:016.1.11
Jun-20110.97.161FUNC_LEVEL_097_17:10:016.1.10
Feb-20110.97.060FUNC_LEVEL_0977:09:016.1.9
Nov-20100.96.558FUNC_LEVEL_096_57:07:016.1.7
Oct-20100.96.456FUNC_LEVEL_096_47:06:016.1.6Minimal FLEVEL allowed for all current bytecode signatures (quadratic load-time before this point)
Sep-20100.96.355FUNC_LEVEL_096_37:05:016.1.5
Aug-20100.96.254FUNC_LEVEL_096_27:04:016.1.4
May-20100.96.153FUNC_LEVEL_096_17:03:016.1.3
Mar-20100.9651FUNC_LEVEL_0967:02:016.1.2Add bytecode & CDB signatures, Ignores should use IGN2 (or take name field only from IGN)
Oct-20090.95.3446:05:006.0.5
Jun-20090.95.2436:04:006.0.4
Apr-20090.95.1426:03:006.0.3
Mar-20090.95416:02:006.0.2Ignores should use IGN format (including line number)
+
+

For more information on ClamAV file type support, see the File Types Reference.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru clamav-0.103.2+dfsg/docs/html/searcher.js clamav-0.103.5+dfsg/docs/html/searcher.js --- clamav-0.103.2+dfsg/docs/html/searcher.js 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/searcher.js 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1,483 @@ +"use strict"; +window.search = window.search || {}; +(function search(search) { + // Search functionality + // + // You can use !hasFocus() to prevent keyhandling in your key + // event handlers while the user is typing their search. + + if (!Mark || !elasticlunr) { + return; + } + + //IE 11 Compatibility from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith + if (!String.prototype.startsWith) { + String.prototype.startsWith = function(search, pos) { + return this.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search; + }; + } + + var search_wrap = document.getElementById('search-wrapper'), + searchbar = document.getElementById('searchbar'), + searchbar_outer = document.getElementById('searchbar-outer'), + searchresults = document.getElementById('searchresults'), + searchresults_outer = document.getElementById('searchresults-outer'), + searchresults_header = document.getElementById('searchresults-header'), + searchicon = document.getElementById('search-toggle'), + content = document.getElementById('content'), + + searchindex = null, + doc_urls = [], + results_options = { + teaser_word_count: 30, + limit_results: 30, + }, + search_options = { + bool: "AND", + expand: true, + fields: { + title: {boost: 1}, + body: {boost: 1}, + breadcrumbs: {boost: 0} + } + }, + mark_exclude = [], + marker = new Mark(content), + current_searchterm = "", + URL_SEARCH_PARAM = 'search', + URL_MARK_PARAM = 'highlight', + teaser_count = 0, + + SEARCH_HOTKEY_KEYCODE = 83, + ESCAPE_KEYCODE = 27, + DOWN_KEYCODE = 40, + UP_KEYCODE = 38, + SELECT_KEYCODE = 13; + + function hasFocus() { + return searchbar === document.activeElement; + } + + function removeChildren(elem) { + while (elem.firstChild) { + elem.removeChild(elem.firstChild); + } + } + + // Helper to parse a url into its building blocks. + function parseURL(url) { + var a = document.createElement('a'); + a.href = url; + return { + source: url, + protocol: a.protocol.replace(':',''), + host: a.hostname, + port: a.port, + params: (function(){ + var ret = {}; + var seg = a.search.replace(/^\?/,'').split('&'); + var len = seg.length, i = 0, s; + for (;i': '>', + '"': '"', + "'": ''' + }; + var repl = function(c) { return MAP[c]; }; + return function(s) { + return s.replace(/[&<>'"]/g, repl); + }; + })(); + + function formatSearchMetric(count, searchterm) { + if (count == 1) { + return count + " search result for '" + searchterm + "':"; + } else if (count == 0) { + return "No search results for '" + searchterm + "'."; + } else { + return count + " search results for '" + searchterm + "':"; + } + } + + function formatSearchResult(result, searchterms) { + var teaser = makeTeaser(escapeHTML(result.doc.body), searchterms); + teaser_count++; + + // The ?URL_MARK_PARAM= parameter belongs inbetween the page and the #heading-anchor + var url = doc_urls[result.ref].split("#"); + if (url.length == 1) { // no anchor found + url.push(""); + } + + // encodeURIComponent escapes all chars that could allow an XSS except + // for '. Due to that we also manually replace ' with its url-encoded + // representation (%27). + var searchterms = encodeURIComponent(searchterms.join(" ")).replace(/\'/g, "%27"); + + return '' + result.doc.breadcrumbs + '' + + '' + + teaser + ''; + } + + function makeTeaser(body, searchterms) { + // The strategy is as follows: + // First, assign a value to each word in the document: + // Words that correspond to search terms (stemmer aware): 40 + // Normal words: 2 + // First word in a sentence: 8 + // Then use a sliding window with a constant number of words and count the + // sum of the values of the words within the window. Then use the window that got the + // maximum sum. If there are multiple maximas, then get the last one. + // Enclose the terms in . + var stemmed_searchterms = searchterms.map(function(w) { + return elasticlunr.stemmer(w.toLowerCase()); + }); + var searchterm_weight = 40; + var weighted = []; // contains elements of ["word", weight, index_in_document] + // split in sentences, then words + var sentences = body.toLowerCase().split('. '); + var index = 0; + var value = 0; + var searchterm_found = false; + for (var sentenceindex in sentences) { + var words = sentences[sentenceindex].split(' '); + value = 8; + for (var wordindex in words) { + var word = words[wordindex]; + if (word.length > 0) { + for (var searchtermindex in stemmed_searchterms) { + if (elasticlunr.stemmer(word).startsWith(stemmed_searchterms[searchtermindex])) { + value = searchterm_weight; + searchterm_found = true; + } + }; + weighted.push([word, value, index]); + value = 2; + } + index += word.length; + index += 1; // ' ' or '.' if last word in sentence + }; + index += 1; // because we split at a two-char boundary '. ' + }; + + if (weighted.length == 0) { + return body; + } + + var window_weight = []; + var window_size = Math.min(weighted.length, results_options.teaser_word_count); + + var cur_sum = 0; + for (var wordindex = 0; wordindex < window_size; wordindex++) { + cur_sum += weighted[wordindex][1]; + }; + window_weight.push(cur_sum); + for (var wordindex = 0; wordindex < weighted.length - window_size; wordindex++) { + cur_sum -= weighted[wordindex][1]; + cur_sum += weighted[wordindex + window_size][1]; + window_weight.push(cur_sum); + }; + + if (searchterm_found) { + var max_sum = 0; + var max_sum_window_index = 0; + // backwards + for (var i = window_weight.length - 1; i >= 0; i--) { + if (window_weight[i] > max_sum) { + max_sum = window_weight[i]; + max_sum_window_index = i; + } + }; + } else { + max_sum_window_index = 0; + } + + // add around searchterms + var teaser_split = []; + var index = weighted[max_sum_window_index][2]; + for (var i = max_sum_window_index; i < max_sum_window_index+window_size; i++) { + var word = weighted[i]; + if (index < word[2]) { + // missing text from index to start of `word` + teaser_split.push(body.substring(index, word[2])); + index = word[2]; + } + if (word[1] == searchterm_weight) { + teaser_split.push("") + } + index = word[2] + word[0].length; + teaser_split.push(body.substring(word[2], index)); + if (word[1] == searchterm_weight) { + teaser_split.push("") + } + }; + + return teaser_split.join(''); + } + + function init(config) { + results_options = config.results_options; + search_options = config.search_options; + searchbar_outer = config.searchbar_outer; + doc_urls = config.doc_urls; + searchindex = elasticlunr.Index.load(config.index); + + // Set up events + searchicon.addEventListener('click', function(e) { searchIconClickHandler(); }, false); + searchbar.addEventListener('keyup', function(e) { searchbarKeyUpHandler(); }, false); + document.addEventListener('keydown', function(e) { globalKeyHandler(e); }, false); + // If the user uses the browser buttons, do the same as if a reload happened + window.onpopstate = function(e) { doSearchOrMarkFromUrl(); }; + // Suppress "submit" events so the page doesn't reload when the user presses Enter + document.addEventListener('submit', function(e) { e.preventDefault(); }, false); + + // If reloaded, do the search or mark again, depending on the current url parameters + doSearchOrMarkFromUrl(); + } + + function unfocusSearchbar() { + // hacky, but just focusing a div only works once + var tmp = document.createElement('input'); + tmp.setAttribute('style', 'position: absolute; opacity: 0;'); + searchicon.appendChild(tmp); + tmp.focus(); + tmp.remove(); + } + + // On reload or browser history backwards/forwards events, parse the url and do search or mark + function doSearchOrMarkFromUrl() { + // Check current URL for search request + var url = parseURL(window.location.href); + if (url.params.hasOwnProperty(URL_SEARCH_PARAM) + && url.params[URL_SEARCH_PARAM] != "") { + showSearch(true); + searchbar.value = decodeURIComponent( + (url.params[URL_SEARCH_PARAM]+'').replace(/\+/g, '%20')); + searchbarKeyUpHandler(); // -> doSearch() + } else { + showSearch(false); + } + + if (url.params.hasOwnProperty(URL_MARK_PARAM)) { + var words = decodeURIComponent(url.params[URL_MARK_PARAM]).split(' '); + marker.mark(words, { + exclude: mark_exclude + }); + + var markers = document.querySelectorAll("mark"); + function hide() { + for (var i = 0; i < markers.length; i++) { + markers[i].classList.add("fade-out"); + window.setTimeout(function(e) { marker.unmark(); }, 300); + } + } + for (var i = 0; i < markers.length; i++) { + markers[i].addEventListener('click', hide); + } + } + } + + // Eventhandler for keyevents on `document` + function globalKeyHandler(e) { + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.target.type === 'textarea' || e.target.type === 'text') { return; } + + if (e.keyCode === ESCAPE_KEYCODE) { + e.preventDefault(); + searchbar.classList.remove("active"); + setSearchUrlParameters("", + (searchbar.value.trim() !== "") ? "push" : "replace"); + if (hasFocus()) { + unfocusSearchbar(); + } + showSearch(false); + marker.unmark(); + } else if (!hasFocus() && e.keyCode === SEARCH_HOTKEY_KEYCODE) { + e.preventDefault(); + showSearch(true); + window.scrollTo(0, 0); + searchbar.select(); + } else if (hasFocus() && e.keyCode === DOWN_KEYCODE) { + e.preventDefault(); + unfocusSearchbar(); + searchresults.firstElementChild.classList.add("focus"); + } else if (!hasFocus() && (e.keyCode === DOWN_KEYCODE + || e.keyCode === UP_KEYCODE + || e.keyCode === SELECT_KEYCODE)) { + // not `:focus` because browser does annoying scrolling + var focused = searchresults.querySelector("li.focus"); + if (!focused) return; + e.preventDefault(); + if (e.keyCode === DOWN_KEYCODE) { + var next = focused.nextElementSibling; + if (next) { + focused.classList.remove("focus"); + next.classList.add("focus"); + } + } else if (e.keyCode === UP_KEYCODE) { + focused.classList.remove("focus"); + var prev = focused.previousElementSibling; + if (prev) { + prev.classList.add("focus"); + } else { + searchbar.select(); + } + } else { // SELECT_KEYCODE + window.location.assign(focused.querySelector('a')); + } + } + } + + function showSearch(yes) { + if (yes) { + search_wrap.classList.remove('hidden'); + searchicon.setAttribute('aria-expanded', 'true'); + } else { + search_wrap.classList.add('hidden'); + searchicon.setAttribute('aria-expanded', 'false'); + var results = searchresults.children; + for (var i = 0; i < results.length; i++) { + results[i].classList.remove("focus"); + } + } + } + + function showResults(yes) { + if (yes) { + searchresults_outer.classList.remove('hidden'); + } else { + searchresults_outer.classList.add('hidden'); + } + } + + // Eventhandler for search icon + function searchIconClickHandler() { + if (search_wrap.classList.contains('hidden')) { + showSearch(true); + window.scrollTo(0, 0); + searchbar.select(); + } else { + showSearch(false); + } + } + + // Eventhandler for keyevents while the searchbar is focused + function searchbarKeyUpHandler() { + var searchterm = searchbar.value.trim(); + if (searchterm != "") { + searchbar.classList.add("active"); + doSearch(searchterm); + } else { + searchbar.classList.remove("active"); + showResults(false); + removeChildren(searchresults); + } + + setSearchUrlParameters(searchterm, "push_if_new_search_else_replace"); + + // Remove marks + marker.unmark(); + } + + // Update current url with ?URL_SEARCH_PARAM= parameter, remove ?URL_MARK_PARAM and #heading-anchor . + // `action` can be one of "push", "replace", "push_if_new_search_else_replace" + // and replaces or pushes a new browser history item. + // "push_if_new_search_else_replace" pushes if there is no `?URL_SEARCH_PARAM=abc` yet. + function setSearchUrlParameters(searchterm, action) { + var url = parseURL(window.location.href); + var first_search = ! url.params.hasOwnProperty(URL_SEARCH_PARAM); + if (searchterm != "" || action == "push_if_new_search_else_replace") { + url.params[URL_SEARCH_PARAM] = searchterm; + delete url.params[URL_MARK_PARAM]; + url.hash = ""; + } else { + delete url.params[URL_MARK_PARAM]; + delete url.params[URL_SEARCH_PARAM]; + } + // A new search will also add a new history item, so the user can go back + // to the page prior to searching. A updated search term will only replace + // the url. + if (action == "push" || (action == "push_if_new_search_else_replace" && first_search) ) { + history.pushState({}, document.title, renderURL(url)); + } else if (action == "replace" || (action == "push_if_new_search_else_replace" && !first_search) ) { + history.replaceState({}, document.title, renderURL(url)); + } + } + + function doSearch(searchterm) { + + // Don't search the same twice + if (current_searchterm == searchterm) { return; } + else { current_searchterm = searchterm; } + + if (searchindex == null) { return; } + + // Do the actual search + var results = searchindex.search(searchterm, search_options); + var resultcount = Math.min(results.length, results_options.limit_results); + + // Display search metrics + searchresults_header.innerText = formatSearchMetric(resultcount, searchterm); + + // Clear and insert results + var searchterms = searchterm.split(' '); + removeChildren(searchresults); + for(var i = 0; i < resultcount ; i++){ + var resultElem = document.createElement('li'); + resultElem.innerHTML = formatSearchResult(results[i], searchterms); + searchresults.appendChild(resultElem); + } + + // Display results + showResults(true); + } + + fetch(path_to_root + 'searchindex.json') + .then(response => response.json()) + .then(json => init(json)) + .catch(error => { // Try to load searchindex.js if fetch failed + var script = document.createElement('script'); + script.src = path_to_root + 'searchindex.js'; + script.onload = () => init(window.search); + document.head.appendChild(script); + }); + + // Exported functions + search.hasFocus = hasFocus; +})(window.search); diff -Nru clamav-0.103.2+dfsg/docs/html/searchindex.js clamav-0.103.5+dfsg/docs/html/searchindex.js --- clamav-0.103.2+dfsg/docs/html/searchindex.js 1970-01-01 00:00:00.000000000 +0000 +++ clamav-0.103.5+dfsg/docs/html/searchindex.js 2022-01-10 23:17:46.000000000 +0000 @@ -0,0 +1 @@ +Object.assign(window.search, {"doc_urls":["Introduction.html#clamav","Introduction.html#community-projects","Introduction.html#features","Introduction.html#license","Introduction.html#supported-platforms","Introduction.html#recommended-system-requirements","Introduction.html#mailing-lists-and-chat","Introduction.html#submitting-new-or-otherwise-undetected-malware","Introduction.html#related-products","manual/Installing.html#installing-clamav","manual/Installing.html#installing-with-a-package-manager","manual/Installing.html#installing-with-an-installer","manual/Installing.html#official-clamav-docker-images","manual/Installing.html#installing-from-source","manual/Installing.html#what-now","manual/Installing/Packages.html#clamav-packages","manual/Installing/Packages.html#the-packages","manual/Installing/Docker.html#clamav-in-docker","manual/Installing/Docker.html#the-official-images-on-docker-hub","manual/Installing/Docker.html#building-the-clamav-image","manual/Installing/Docker.html#running-clamd","manual/Installing/Docker.html#running-clamdscan","manual/Installing/Docker.html#controlling-the-container","manual/Installing/Docker.html#connecting-to-the-container","manual/Installing/Docker.html#container-clamd-health-check","manual/Installing/Docker.html#performance","manual/Installing/Docker.html#bandwidth","manual/Installing/Docker.html#advanced-container-configurations","manual/Installing/Installing-from-source-Unix.html#building-clamav-with-cmake-v0104-and-newer","manual/Installing/Installing-from-source-Unix.html#install-prerequisites","manual/Installing/Installing-from-source-Unix.html#adding-new-system-user-and-group","manual/Installing/Installing-from-source-Unix.html#download-the-source-code","manual/Installing/Installing-from-source-Unix.html#build-clamav","manual/Installing/Installing-from-source-Unix.html#un-install","manual/Installing/Installing-from-source-Unix.html#what-now","manual/Installing/Installing-from-source-Unix-old.html#building-clamav-with-autotools-v0103-and-older","manual/Installing/Installing-from-source-Unix-old.html#install-prerequisites","manual/Installing/Installing-from-source-Unix-old.html#adding-new-system-user-and-group","manual/Installing/Installing-from-source-Unix-old.html#download-the-source-code","manual/Installing/Installing-from-source-Unix-old.html#build-clamav","manual/Installing/Installing-from-source-Unix-old.html#un-install","manual/Installing/Installing-from-source-Unix-old.html#what-now","manual/Installing/Installing-from-source-Windows.html#installing-clamav-on-windows-from-source","manual/Installing/Installing-from-source-Windows.html#install-prerequisites","manual/Installing/Installing-from-source-Windows.html#download-the-source-code","manual/Installing/Installing-from-source-Windows.html#build-clamav","manual/Installing/Installing-from-source-Windows.html#what-now","manual/Installing/Community-projects.html#community-projects","manual/Installing/Community-projects.html#signatures","manual/Installing/Community-projects.html#mail-filters","manual/Installing/Community-projects.html#clamav-bindings","manual/Installing/Community-projects.html#miscellaneous-tools","manual/Installing/Add-clamav-user.html#add-a-service-user-account","manual/Installing/Add-clamav-user.html#create-a-service-user-account-and-group","manual/Installing/Add-clamav-user.html#about-how-the-service-accounts-are-used","manual/Installing/Add-clamav-user.html#after-installation--make-the-service-account-own-the-database-directory","manual/Usage.html#usage","manual/Usage.html#purpose","manual/Usage.html#daemon","manual/Usage.html#scanner","manual/Usage.html#signature-testing-and-management","manual/Usage.html#configuration","manual/Usage/Configuration.html#configuration","manual/Usage/Configuration.html#first-time-set-up","manual/Usage/Configuration.html#freshclamconf","manual/Usage/Configuration.html#clamdconf","manual/Usage/Configuration.html#clamav-milterconf","manual/Usage/Configuration.html#configure-selinux-for-clamav","manual/Usage/Configuration.html#clamconf","manual/Usage/Configuration.html#next-steps","manual/Usage/SignatureManagement.html#signature-testing-and-management","manual/Usage/SignatureManagement.html#freshclam","manual/Usage/SignatureManagement.html#sigtool","manual/Usage/SignatureManagement.html#clambc","manual/Usage/SignatureManagement.html#next-steps","manual/Usage/SignatureManagement.html#create-your-own-signatures","manual/Usage/Scanning.html#scanning","manual/Usage/Scanning.html#daemon","manual/Usage/Scanning.html#one-time-scanning","manual/Usage/Scanning.html#process-memory-scanning","manual/Usage/Scanning.html#disclaimers","manual/Usage/Scanning.html#windows-specific-issues","manual/OnAccess.html#on-access-scanning","manual/OnAccess.html#purpose","manual/OnAccess.html#requirements","manual/OnAccess.html#general-use","manual/OnAccess.html#troubleshooting","manual/OnAccess.html#configuration-and-recipes","manual/OnAccess.html#command-line-options-for-versions--0102","manual/Usage/Services.html#running-clamav-services","manual/Usage/Services.html#windows-services","manual/Usage/ReportABug.html#how-to-report-a-bug","manual/Usage/ReportABug.html#warning-against-accidental-vulnerability-disclosure","manual/Usage/ReportABug.html#steps-before-you-report","manual/Usage/ReportABug.html#required-information","manual/Signatures.html#creating-signatures-for-clamav","manual/Signatures.html#introduction","manual/Signatures.html#database-formats","manual/Signatures.html#signature-writing-tips-and-tricks","manual/Signatures/DatabaseInfo.html#database-info","manual/Signatures/DynamicConfig.html#dynamic-configuration-dconf","manual/Signatures/DynamicConfig.html#example","manual/Signatures/AuthenticodeRules.html#trusted-and-revoked-certificates","manual/Signatures/FileTypeMagic.html#file-type-magic","manual/Signatures/AllowLists.html#allow-list-databases","manual/Signatures/AllowLists.html#file-allow-lists","manual/Signatures/AllowLists.html#signature-ignore-lists","manual/Signatures/HashSignatures.html#file-hash-signatures","manual/Signatures/HashSignatures.html#md5-hash-based-signatures","manual/Signatures/HashSignatures.html#sha1-and-sha256-hash-based-signatures","manual/Signatures/HashSignatures.html#hash-signatures-with-unknown-size","manual/Signatures/HashSignatures.html#pe-section-based-hash-signatures","manual/Signatures/BodySignatureFormat.html#body-based-signature-content-format","manual/Signatures/BodySignatureFormat.html#hexadecimal-format","manual/Signatures/BodySignatureFormat.html#wildcards","manual/Signatures/BodySignatureFormat.html#character-classes","manual/Signatures/BodySignatureFormat.html#alternate-strings","manual/Signatures/LogicalSignatures.html#logical-signatures","manual/Signatures/LogicalSignatures.html#subsignature-modifiers","manual/Signatures/LogicalSignatures.html#special-subsignature-types","manual/Signatures/LogicalSignatures.html#signatures-for-version-information-vi-metadata-in-pe-files","manual/Signatures/LogicalSignatures.html#icon-signatures-for-pe-files","manual/Signatures/ExtendedSignatures.html#extended-signature-format","manual/Signatures/YaraRules.html#using-yara-rules-in-clamav","manual/Signatures/PhishSigs.html#phishing-signatures","manual/Signatures/PhishSigs.html#database-file-format","manual/Signatures/PhishSigs.html#introduction-to-regular-expressions","manual/Signatures/PhishSigs.html#how-to-create-database-files","manual/Signatures/BytecodeSignatures.html#bytecode-signatures","manual/Signatures/ContainerMetadata.html#signatures-based-on-container-metadata","manual/Signatures/EncryptedArchives.html#passwords-for-archive-files-experimental","manual/Development.html#clamav-development","manual/Development.html#pull-request-basics","manual/Development.html#clamav-git-work-flow","manual/Development.html#working-with-your-fork","manual/Development.html#reviewing-pull-requests","manual/Development.html#building-for-development","manual/Development.html#building-the-installer-packages","manual/Development.html#dev-tips--tricks","manual/Development.html#libclamav","manual/Development.html#contribute","manual/Development/github-pr-basics.html#github-pull-request-basics","manual/Development/clamav-git-work-flow.html#clamav-git-work-flow","manual/Development/personal-forks.html#working-with-a-your-own-fork-of-the-clamav-repository","manual/Development/testing-pull-requests.html#reviewing--testing-pull-requests","manual/Development/development-builds.html#building-for-development","manual/Development/development-builds.html#build-dependencies","manual/Development/development-builds.html#download-the-source","manual/Development/development-builds.html#building-clamav-with-cmake-v0104-and-newer","manual/Development/development-builds.html#building-clamav-with-autotools-v0103-and-older","manual/Development/build-installer-packages.html#building-the-installer-packages","manual/Development/build-installer-packages.html#linux","manual/Development/build-installer-packages.html#macos","manual/Development/build-installer-packages.html#windows","manual/Development/tips-and-tricks.html#development-tips--tricks","manual/Development/tips-and-tricks.html#downloading-the-official-ruleset","manual/Development/tips-and-tricks.html#general-debugging","manual/Development/tips-and-tricks.html#hunting-for-memory-leaks","manual/Development/performance-profiling.html#performance-profiling","manual/Development/performance-profiling.html#flame-graph-profiling","manual/Development/performance-profiling.html#call-graph-profiling---callgrind","manual/Development/performance-profiling.html#system-call-tracing--fault-injection","manual/Development/code-coverage.html#computing-code-coverage","manual/Development/code-coverage.html#code-coverage-when-using-cmake-v0104-and-newer","manual/Development/code-coverage.html#code-coverage-when-using-autotools-v0103-and-older","manual/Development/fuzzing-sanitizers.html#building-and-testing-clamav-with-fuzzing-sanitizers","manual/Development/fuzzing-sanitizers.html#build--reproduce-fuzz-reports-using-oss-fuzz-tools","manual/Development/fuzzing-sanitizers.html#build--test-fuzz-targets-sanitizers-in-cmake-v0104-and-newer","manual/Development/fuzzing-sanitizers.html#clamav-with-address-sanitizer-asan-in-autotools-v0103-and-older","manual/Development/libclamav.html#libclamav","manual/Development/libclamav.html#license","manual/Development/libclamav.html#supported-formats-and-features","manual/Development/libclamav.html#api","manual/Development/libclamav.html#cvd-format","manual/Development/Contribute.html#project-ideas","manual/Development/Contribute.html#bugs","manual/Development/Contribute.html#larger-projects","faq/faq.html#clam-antivirus--frequently-asked-questions","faq/faq-whichversion.html#which-version-of-clamav-should-i-use","faq/faq-whichversion.html#stable-release","faq/faq-whichversion.html#beta-and-release-candidates-programs","faq/faq-freshclam.html#freshclam-faq","faq/faq-freshclam.html#failed-to-get-information-about-user-clamav","faq/faq-freshclam.html#cant-create-freshclamdat-in-usrlocalshareclamav","faq/faq-freshclam.html#problem-with-the-ssl-ca-cert","faq/faq-freshclam.html#invalid-dns-reply-falling-back-to-http-mode-or-error-cant-query-currentcvdclamavnet","faq/faq-freshclam.html#error-connection-with--failed","faq/faq-freshclam.html#warning-incremental-update-failed-trying-to-download-dailycvd","faq/faq-freshclam.html#database-update-process-failed-downloaded-database-had-lower-version-than-advertised","faq/faq-freshclam.html#update-failed-your-network-may-be-down-or-the-clamav-database-content-delivery-network-is-experiencing-an-outage","faq/faq-freshclam.html#update-failed-updating-too-frequently-with-an-outdated-version","faq/faq-freshclam.html#your-clamav-installation-is-outdated","faq/faq-freshclam.html#warning-current-functionality-level--1-required--2","faq/faq-freshclam.html#ignoring-mirror--has-connected-too-many-times-with-an-outdated-version","faq/faq-freshclam.html#http-error-codes","faq/faq-freshclam.html#for-all-other-database-update-related-failures","faq/faq-cvd.html#clamav-virus-database-faq","faq/faq-cvd.html#how-do-i-keep-my-virus-database-up-to-date","faq/faq-cvd.html#how-often-is-the-virus-database-updated","faq/faq-cvd.html#the-last-cvd-update-crashed-my-clamav-installation-why","faq/faq-cvd.html#the-last-cvd-update-detects-a-lot-of-false-positives-on-my-system-why","faq/faq-cvd.html#i-tried-to-submit-a-sample-through-the-web-interface-but-it-said-the-sample-is-already-recognized-by-clamav-my-clamscan-tells-me-its-not-i-have-already-updated-my-database-and-clamav-engine-whats-wrong-with-my-setup","faq/faq-cvd.html#i-found-an-infected-file-in-my-hdusbmailbox-but-clamav-doesnt-recognize-it-yet-can-you-help-me","faq/faq-cvd.html#im-running-clamav-on-a-lot-of-clients-on-my-local-network-can-i-serve-the-cvd-files-from-a-local-server-so-that-each-client-doesnt-have-to-download-them-from-your-servers","faq/faq-cvd.html#i-cant-wait-for-you-to-update-the-database-i-need-to-use-the-new-signature-now","faq/faq-cvd.html#can-i-download-the-virus-database-manually","faq/faq-cvd.html#i-am-getting-error-codes-such-as-403-429-etc-when-freshclam-or-other-update-system-attempts-to-download-updates","faq/faq-misc.html#miscellaneous-faq","faq/faq-misc.html#i-see-you-have-bugzilla-and-github-issues-which-one-should-i-use","faq/faq-misc.html#i-reported-a-bug-on-bugzilla-but-no-one-can-see-it-what-do-i-do","faq/faq-misc.html#where-can-i-find-the-bug-ticket-for-a-security-bug-fix","faq/faq-misc.html#where-can-i-find-a-test-file-to-prove-that-a-security-bug-doesnt-affect-me-or-has-been-fixed-in-my-version","faq/faq-misc.html#can-phishing-be-considered-one-kind-of-spam-clamav-should-not-detect-it-as-some-kind-of-malware","faq/faq-misc.html#why-is-my-legitimate-html-newsletteremail-detected-by-clamav-as-phishingheuristicsemailspoofeddomain","faq/faq-misc.html#my-legitimate-emails-from-yourdomaintld-are-detected-as-phishingheuristicsemailspoofeddomain","faq/faq-misc.html#can-i-convert-the-new-database-format-to-the-old-one","faq/faq-misc.html#how-do-i-read-inside-the-cvd-files","faq/faq-misc.html#im-using-clamav-in-a-production-environment-and-a-brand-new-virus-is-not-being-recognized-by-clamav-how-long-do-i-have-to-wait-before-clamav-can-start-filtering-the-virus","faq/faq-misc.html#why-is-clamav-calling-the-xxx-virus-with-another-name","faq/faq-misc.html#i-get-many-false-positives-of-oversizedzip","faq/faq-misc.html#what-is-pua-i-get-a-lot-of-false-positives-named-pua","faq/faq-misc.html#can-clamav-disinfect-files","faq/faq-misc.html#when-using-clamscan-is-there-a-way-to-know-which-message-within-an-mbox-is-infected","faq/faq-misc.html#what-platforms-does-it-support","faq/faq-misc.html#where-can-i-find-more-information-about-clamav","faq/faq-ml.html#mailing-lists-faq","faq/faq-ml.html#where-can-i-ask-questions-about-using-clamav","faq/faq-ml.html#i-want-to-take-part-to-the-development-of-clamav-where-can-i-get-more-info","faq/faq-ml.html#the-mailing-lists-generate-too-many-messages-per-day-i-cant-handle-them-what-shall-i-do","faq/faq-ml.html#i-sent-a-message-to-one-of-clamavs-mailing-lists-but-the-mail-was-rejectedheld-for-approval-why","faq/faq-ml.html#i-read-the-mailing-list-from-the-gmane-news-gateway-can-i-post-to-the-mailing-list","faq/faq-ml.html#ive-been-unsubscribed-from-one-of-the-mailing-lists-what-happened","faq/faq-ml.html#how-do-i-disable-mail-delivery-from-the-mailing-list-im-subscribed-to","faq/faq-safebrowsing.html#safebrowsing","faq/faq-safebrowsing.html#about","faq/faq-safebrowsing.html#current-status","faq/faq-safebrowsing.html#history","faq/faq-troubleshoot.html#troubleshooting-faq","faq/faq-troubleshoot.html#after-clamav-is-installed-then-what-how-do-i-update--refresh-the-virus-database","faq/faq-troubleshoot.html#how-many-times-per-hour-shall-i-run-freshclam","faq/faq-troubleshoot.html#i-get-this-error-when-running-freshclam--invalid-dns-reply-falling-back-to-http-mode--or--error-cant-query-currentcvdclamavnet---what-does-it-mean","faq/faq-troubleshoot.html#what-does--warning-dns-record-is-older-than-3-hours--mean","faq/faq-troubleshoot.html#i-get-this-error-when-running-freshclam--error-connection-with--failed---what-shall-i-do","faq/faq-troubleshoot.html#how-do-i-know-if-my-ip-address-has-been-blocked","faq/faq-troubleshoot.html#i-cant-resolve-currentcvdclamavnet-is-there-a-problem-with-yourmy-dns-servers","faq/faq-scan-alerts.html#interpreting-scan-alerts-faq","faq/faq-scan-alerts.html#clamav-alerted-on-a-file-during-a-scan-what-do-i-do","faq/faq-scan-alerts.html#clamav-alerted-on-a-file-in-the-clamav-source-code-am-i-infected","faq/faq-upgrade.html#upgrading-clamav","faq/faq-upgrade.html#clamav-from-packages","faq/faq-upgrade.html#install-clamav-from-source","faq/faq-upgrade.html#webmin-and-yum","faq/faq-win32.html#clamav-on-microsoft-windows-faq","faq/faq-win32.html#what-is-the-difference-between-clamav-immunet-and-clamwin","faq/faq-win32.html#is-immunet-free-for-commercial-use","faq/faq-win32.html#will-immunet-send-any-sensitive-data-from-my-computer-to-the-cloud","faq/faq-win32.html#are-you-going-to-make-use-of-the-cloud-in-the-nix-version-of-clamav","faq/faq-win32.html#can-i-use-immunet-with-my-current-av-solution","faq/faq-win32.html#where-should-i-report-false-positives-or-undetected-malware","faq/faq-win32.html#are-there-64-bit-versions-of-clamav-for-windows-as-well-as-32-bit","faq/faq-pua.html#potentially-unwanted-applications-pua","faq/faq-pua.html#pua-config-options","faq/faq-pua.html#current-pua-categories","faq/faq-pua.html#pua-category-descriptions","faq/faq-ignore.html#how-do-i-ignore-a-clamav-signature","faq/faq-ignore.html#creating-an-ignore-file","faq/faq-ignore.html#ignore-individual-signatures","faq/faq-uninstall.html#uninstalling-clamav","faq/faq-uninstall.html#if-you-installed-from-source","faq/faq-uninstall.html#if-you-installed-from-packages","faq/faq-uninstall.html#caveats","faq/faq-eol.html#end-of-life-eol-policy","faq/faq-eol.html#long-term-support-lts-feature-releases","faq/faq-eol.html#regular-non-lts-feature-releases","faq/faq-eol.html#definitions","faq/faq-eol.html#version-support-matrix","faq/faq-eol.html#additional-detail-about-critical-patch-support","appendix/Appendix.html#appendix","appendix/Terminology.html#terminology","appendix/Terminology.html#general-terminology","appendix/Terminology.html#clamav-components","appendix/CvdPrivateMirror.html#private-local-mirrors","appendix/CvdPrivateMirror.html#use-cvdupdate-to-serve-whole-databases-and-database-patch-files-from-a-private-mirror","appendix/CvdPrivateMirror.html#use-freshclam-to-serve-only-whole-database-files-from-a-private-mirror","appendix/CvdPrivateMirror.html#use-an-http-proxy","appendix/Authenticode.html#microsoft-authenticode-signature-verification","appendix/Authenticode.html#about-microsoft-authenticode","appendix/Authenticode.html#authenticode-and-clamav","appendix/Authenticode.html#helpful-info-for-working-with-authenticode-signatures","appendix/Authenticode.html#format-specifications","appendix/Authenticode.html#verifying-the-signature","appendix/Authenticode.html#extracting-the-signature","appendix/Authenticode.html#inspecting-the-signature","appendix/Authenticode.html#creating-signed-executables","appendix/Authenticode.html#samples-with-interesting-authenticode-signatures","appendix/Authenticode.html#additional-references","appendix/FileTypes.html#clamav-file-types","appendix/FileTypes.html#target-types","appendix/FileTypes.html#file-types","appendix/FunctionalityLevels.html#versions--functionality-levels-flevels","appendix/FunctionalityLevels.html#clamav-version-to-flevel-chart"],"index":{"documentStore":{"docInfo":{"0":{"body":70,"breadcrumbs":2,"title":1},"1":{"body":29,"breadcrumbs":3,"title":2},"10":{"body":24,"breadcrumbs":4,"title":3},"100":{"body":154,"breadcrumbs":7,"title":3},"101":{"body":86,"breadcrumbs":5,"title":1},"102":{"body":110,"breadcrumbs":8,"title":3},"103":{"body":140,"breadcrumbs":7,"title":3},"104":{"body":0,"breadcrumbs":6,"title":3},"105":{"body":80,"breadcrumbs":6,"title":3},"106":{"body":61,"breadcrumbs":6,"title":3},"107":{"body":13,"breadcrumbs":7,"title":3},"108":{"body":138,"breadcrumbs":8,"title":4},"109":{"body":29,"breadcrumbs":9,"title":5},"11":{"body":456,"breadcrumbs":3,"title":2},"110":{"body":59,"breadcrumbs":8,"title":4},"111":{"body":64,"breadcrumbs":9,"title":5},"112":{"body":29,"breadcrumbs":10,"title":5},"113":{"body":15,"breadcrumbs":7,"title":2},"114":{"body":124,"breadcrumbs":6,"title":1},"115":{"body":25,"breadcrumbs":7,"title":2},"116":{"body":139,"breadcrumbs":7,"title":2},"117":{"body":454,"breadcrumbs":9,"title":2},"118":{"body":148,"breadcrumbs":9,"title":2},"119":{"body":592,"breadcrumbs":10,"title":3},"12":{"body":55,"breadcrumbs":5,"title":4},"120":{"body":311,"breadcrumbs":14,"title":7},"121":{"body":89,"breadcrumbs":11,"title":4},"122":{"body":221,"breadcrumbs":10,"title":3},"123":{"body":231,"breadcrumbs":7,"title":4},"124":{"body":80,"breadcrumbs":5,"title":2},"125":{"body":1055,"breadcrumbs":6,"title":3},"126":{"body":192,"breadcrumbs":6,"title":3},"127":{"body":217,"breadcrumbs":6,"title":3},"128":{"body":131,"breadcrumbs":5,"title":2},"129":{"body":154,"breadcrumbs":8,"title":4},"13":{"body":9,"breadcrumbs":3,"title":2},"130":{"body":98,"breadcrumbs":8,"title":4},"131":{"body":9,"breadcrumbs":3,"title":2},"132":{"body":15,"breadcrumbs":4,"title":3},"133":{"body":13,"breadcrumbs":5,"title":4},"134":{"body":12,"breadcrumbs":3,"title":2},"135":{"body":24,"breadcrumbs":4,"title":3},"136":{"body":27,"breadcrumbs":3,"title":2},"137":{"body":9,"breadcrumbs":4,"title":3},"138":{"body":11,"breadcrumbs":4,"title":3},"139":{"body":23,"breadcrumbs":2,"title":1},"14":{"body":15,"breadcrumbs":2,"title":1},"140":{"body":32,"breadcrumbs":2,"title":1},"141":{"body":205,"breadcrumbs":8,"title":4},"142":{"body":153,"breadcrumbs":9,"title":4},"143":{"body":485,"breadcrumbs":7,"title":4},"144":{"body":116,"breadcrumbs":8,"title":4},"145":{"body":56,"breadcrumbs":5,"title":2},"146":{"body":441,"breadcrumbs":5,"title":2},"147":{"body":35,"breadcrumbs":5,"title":2},"148":{"body":704,"breadcrumbs":8,"title":5},"149":{"body":598,"breadcrumbs":8,"title":5},"15":{"body":154,"breadcrumbs":4,"title":2},"150":{"body":43,"breadcrumbs":7,"title":3},"151":{"body":358,"breadcrumbs":5,"title":1},"152":{"body":246,"breadcrumbs":5,"title":1},"153":{"body":305,"breadcrumbs":5,"title":1},"154":{"body":24,"breadcrumbs":7,"title":3},"155":{"body":37,"breadcrumbs":7,"title":3},"156":{"body":326,"breadcrumbs":6,"title":2},"157":{"body":62,"breadcrumbs":7,"title":3},"158":{"body":0,"breadcrumbs":5,"title":2},"159":{"body":272,"breadcrumbs":6,"title":3},"16":{"body":745,"breadcrumbs":3,"title":1},"160":{"body":32,"breadcrumbs":7,"title":4},"161":{"body":425,"breadcrumbs":8,"title":5},"162":{"body":0,"breadcrumbs":7,"title":3},"163":{"body":9,"breadcrumbs":10,"title":6},"164":{"body":84,"breadcrumbs":10,"title":6},"165":{"body":0,"breadcrumbs":8,"title":5},"166":{"body":57,"breadcrumbs":11,"title":8},"167":{"body":45,"breadcrumbs":11,"title":8},"168":{"body":62,"breadcrumbs":10,"title":7},"169":{"body":26,"breadcrumbs":3,"title":1},"17":{"body":18,"breadcrumbs":4,"title":2},"170":{"body":20,"breadcrumbs":3,"title":1},"171":{"body":219,"breadcrumbs":5,"title":3},"172":{"body":1101,"breadcrumbs":3,"title":1},"173":{"body":66,"breadcrumbs":4,"title":2},"174":{"body":105,"breadcrumbs":4,"title":2},"175":{"body":28,"breadcrumbs":3,"title":1},"176":{"body":2069,"breadcrumbs":4,"title":2},"177":{"body":164,"breadcrumbs":8,"title":5},"178":{"body":6,"breadcrumbs":10,"title":3},"179":{"body":8,"breadcrumbs":9,"title":2},"18":{"body":98,"breadcrumbs":6,"title":4},"180":{"body":19,"breadcrumbs":11,"title":4},"181":{"body":52,"breadcrumbs":8,"title":2},"182":{"body":50,"breadcrumbs":10,"title":4},"183":{"body":89,"breadcrumbs":10,"title":4},"184":{"body":96,"breadcrumbs":10,"title":4},"185":{"body":58,"breadcrumbs":17,"title":11},"186":{"body":41,"breadcrumbs":9,"title":3},"187":{"body":10,"breadcrumbs":13,"title":7},"188":{"body":14,"breadcrumbs":15,"title":9},"189":{"body":23,"breadcrumbs":17,"title":11},"19":{"body":35,"breadcrumbs":5,"title":3},"190":{"body":63,"breadcrumbs":12,"title":6},"191":{"body":29,"breadcrumbs":9,"title":3},"192":{"body":21,"breadcrumbs":13,"title":7},"193":{"body":22,"breadcrumbs":14,"title":8},"194":{"body":136,"breadcrumbs":9,"title":3},"195":{"body":8,"breadcrumbs":10,"title":4},"196":{"body":55,"breadcrumbs":10,"title":4},"197":{"body":20,"breadcrumbs":11,"title":5},"198":{"body":40,"breadcrumbs":9,"title":3},"199":{"body":13,"breadcrumbs":12,"title":6},"2":{"body":256,"breadcrumbs":2,"title":1},"20":{"body":340,"breadcrumbs":4,"title":2},"200":{"body":30,"breadcrumbs":14,"title":8},"201":{"body":14,"breadcrumbs":27,"title":21},"202":{"body":43,"breadcrumbs":14,"title":8},"203":{"body":63,"breadcrumbs":23,"title":17},"204":{"body":34,"breadcrumbs":15,"title":9},"205":{"body":20,"breadcrumbs":10,"title":4},"206":{"body":26,"breadcrumbs":19,"title":13},"207":{"body":43,"breadcrumbs":6,"title":2},"208":{"body":20,"breadcrumbs":10,"title":6},"209":{"body":35,"breadcrumbs":9,"title":5},"21":{"body":261,"breadcrumbs":4,"title":2},"210":{"body":46,"breadcrumbs":10,"title":6},"211":{"body":75,"breadcrumbs":14,"title":10},"212":{"body":20,"breadcrumbs":13,"title":9},"213":{"body":18,"breadcrumbs":10,"title":6},"214":{"body":15,"breadcrumbs":9,"title":5},"215":{"body":14,"breadcrumbs":10,"title":6},"216":{"body":3,"breadcrumbs":8,"title":4},"217":{"body":18,"breadcrumbs":22,"title":18},"218":{"body":33,"breadcrumbs":10,"title":6},"219":{"body":18,"breadcrumbs":8,"title":4},"22":{"body":146,"breadcrumbs":4,"title":2},"220":{"body":92,"breadcrumbs":10,"title":6},"221":{"body":30,"breadcrumbs":7,"title":3},"222":{"body":30,"breadcrumbs":12,"title":8},"223":{"body":25,"breadcrumbs":6,"title":2},"224":{"body":46,"breadcrumbs":8,"title":4},"225":{"body":43,"breadcrumbs":8,"title":3},"226":{"body":5,"breadcrumbs":9,"title":4},"227":{"body":5,"breadcrumbs":12,"title":7},"228":{"body":21,"breadcrumbs":15,"title":10},"229":{"body":8,"breadcrumbs":14,"title":9},"23":{"body":276,"breadcrumbs":4,"title":2},"230":{"body":3,"breadcrumbs":14,"title":9},"231":{"body":35,"breadcrumbs":11,"title":6},"232":{"body":37,"breadcrumbs":12,"title":7},"233":{"body":0,"breadcrumbs":6,"title":1},"234":{"body":11,"breadcrumbs":5,"title":0},"235":{"body":126,"breadcrumbs":7,"title":2},"236":{"body":97,"breadcrumbs":6,"title":1},"237":{"body":52,"breadcrumbs":6,"title":2},"238":{"body":23,"breadcrumbs":10,"title":6},"239":{"body":15,"breadcrumbs":11,"title":7},"24":{"body":35,"breadcrumbs":6,"title":4},"240":{"body":30,"breadcrumbs":19,"title":15},"241":{"body":62,"breadcrumbs":11,"title":7},"242":{"body":42,"breadcrumbs":11,"title":7},"243":{"body":33,"breadcrumbs":8,"title":4},"244":{"body":54,"breadcrumbs":11,"title":7},"245":{"body":43,"breadcrumbs":10,"title":4},"246":{"body":19,"breadcrumbs":11,"title":5},"247":{"body":566,"breadcrumbs":13,"title":7},"248":{"body":0,"breadcrumbs":6,"title":2},"249":{"body":27,"breadcrumbs":6,"title":2},"25":{"body":96,"breadcrumbs":3,"title":1},"250":{"body":61,"breadcrumbs":7,"title":3},"251":{"body":260,"breadcrumbs":6,"title":2},"252":{"body":134,"breadcrumbs":8,"title":4},"253":{"body":74,"breadcrumbs":9,"title":5},"254":{"body":18,"breadcrumbs":8,"title":4},"255":{"body":46,"breadcrumbs":10,"title":6},"256":{"body":14,"breadcrumbs":11,"title":7},"257":{"body":3,"breadcrumbs":9,"title":5},"258":{"body":10,"breadcrumbs":9,"title":5},"259":{"body":5,"breadcrumbs":12,"title":8},"26":{"body":21,"breadcrumbs":3,"title":1},"260":{"body":7,"breadcrumbs":11,"title":4},"261":{"body":81,"breadcrumbs":10,"title":3},"262":{"body":191,"breadcrumbs":10,"title":3},"263":{"body":215,"breadcrumbs":10,"title":3},"264":{"body":0,"breadcrumbs":7,"title":3},"265":{"body":13,"breadcrumbs":7,"title":3},"266":{"body":15,"breadcrumbs":7,"title":3},"267":{"body":0,"breadcrumbs":6,"title":2},"268":{"body":163,"breadcrumbs":6,"title":2},"269":{"body":36,"breadcrumbs":6,"title":2},"27":{"body":154,"breadcrumbs":5,"title":3},"270":{"body":25,"breadcrumbs":5,"title":1},"271":{"body":51,"breadcrumbs":10,"title":4},"272":{"body":73,"breadcrumbs":12,"title":6},"273":{"body":40,"breadcrumbs":11,"title":5},"274":{"body":270,"breadcrumbs":7,"title":1},"275":{"body":139,"breadcrumbs":9,"title":3},"276":{"body":290,"breadcrumbs":11,"title":5},"277":{"body":16,"breadcrumbs":2,"title":1},"278":{"body":0,"breadcrumbs":3,"title":1},"279":{"body":316,"breadcrumbs":4,"title":2},"28":{"body":97,"breadcrumbs":9,"title":5},"280":{"body":201,"breadcrumbs":4,"title":2},"281":{"body":69,"breadcrumbs":8,"title":3},"282":{"body":318,"breadcrumbs":15,"title":10},"283":{"body":258,"breadcrumbs":13,"title":8},"284":{"body":28,"breadcrumbs":8,"title":3},"285":{"body":0,"breadcrumbs":9,"title":4},"286":{"body":92,"breadcrumbs":7,"title":2},"287":{"body":139,"breadcrumbs":7,"title":2},"288":{"body":9,"breadcrumbs":10,"title":5},"289":{"body":30,"breadcrumbs":7,"title":2},"29":{"body":461,"breadcrumbs":6,"title":2},"290":{"body":140,"breadcrumbs":7,"title":2},"291":{"body":30,"breadcrumbs":7,"title":2},"292":{"body":517,"breadcrumbs":7,"title":2},"293":{"body":34,"breadcrumbs":8,"title":3},"294":{"body":172,"breadcrumbs":9,"title":4},"295":{"body":20,"breadcrumbs":7,"title":2},"296":{"body":12,"breadcrumbs":9,"title":3},"297":{"body":156,"breadcrumbs":8,"title":2},"298":{"body":457,"breadcrumbs":8,"title":2},"299":{"body":42,"breadcrumbs":9,"title":4},"3":{"body":9,"breadcrumbs":2,"title":1},"30":{"body":19,"breadcrumbs":9,"title":5},"300":{"body":781,"breadcrumbs":9,"title":4},"31":{"body":14,"breadcrumbs":7,"title":3},"32":{"body":393,"breadcrumbs":6,"title":2},"33":{"body":71,"breadcrumbs":6,"title":2},"34":{"body":15,"breadcrumbs":5,"title":1},"35":{"body":74,"breadcrumbs":9,"title":5},"36":{"body":283,"breadcrumbs":6,"title":2},"37":{"body":19,"breadcrumbs":9,"title":5},"38":{"body":14,"breadcrumbs":7,"title":3},"39":{"body":252,"breadcrumbs":6,"title":2},"4":{"body":87,"breadcrumbs":3,"title":2},"40":{"body":29,"breadcrumbs":6,"title":2},"41":{"body":15,"breadcrumbs":5,"title":1},"42":{"body":96,"breadcrumbs":7,"title":4},"43":{"body":243,"breadcrumbs":5,"title":2},"44":{"body":25,"breadcrumbs":6,"title":3},"45":{"body":530,"breadcrumbs":5,"title":2},"46":{"body":15,"breadcrumbs":4,"title":1},"47":{"body":11,"breadcrumbs":5,"title":2},"48":{"body":163,"breadcrumbs":4,"title":1},"49":{"body":861,"breadcrumbs":5,"title":2},"5":{"body":97,"breadcrumbs":4,"title":3},"50":{"body":279,"breadcrumbs":5,"title":2},"51":{"body":103,"breadcrumbs":5,"title":2},"52":{"body":42,"breadcrumbs":9,"title":4},"53":{"body":131,"breadcrumbs":10,"title":5},"54":{"body":148,"breadcrumbs":8,"title":3},"55":{"body":29,"breadcrumbs":11,"title":6},"56":{"body":10,"breadcrumbs":2,"title":1},"57":{"body":24,"breadcrumbs":2,"title":1},"58":{"body":36,"breadcrumbs":2,"title":1},"59":{"body":26,"breadcrumbs":2,"title":1},"6":{"body":59,"breadcrumbs":4,"title":3},"60":{"body":25,"breadcrumbs":4,"title":3},"61":{"body":65,"breadcrumbs":2,"title":1},"62":{"body":34,"breadcrumbs":3,"title":1},"63":{"body":267,"breadcrumbs":6,"title":4},"64":{"body":220,"breadcrumbs":3,"title":1},"65":{"body":178,"breadcrumbs":3,"title":1},"66":{"body":157,"breadcrumbs":4,"title":2},"67":{"body":61,"breadcrumbs":5,"title":3},"68":{"body":61,"breadcrumbs":3,"title":1},"69":{"body":13,"breadcrumbs":4,"title":2},"7":{"body":31,"breadcrumbs":6,"title":5},"70":{"body":24,"breadcrumbs":7,"title":3},"71":{"body":183,"breadcrumbs":5,"title":1},"72":{"body":113,"breadcrumbs":5,"title":1},"73":{"body":32,"breadcrumbs":5,"title":1},"74":{"body":13,"breadcrumbs":6,"title":2},"75":{"body":10,"breadcrumbs":6,"title":2},"76":{"body":45,"breadcrumbs":3,"title":1},"77":{"body":590,"breadcrumbs":3,"title":1},"78":{"body":262,"breadcrumbs":5,"title":3},"79":{"body":32,"breadcrumbs":5,"title":3},"8":{"body":79,"breadcrumbs":3,"title":2},"80":{"body":73,"breadcrumbs":3,"title":1},"81":{"body":105,"breadcrumbs":5,"title":3},"82":{"body":0,"breadcrumbs":6,"title":2},"83":{"body":21,"breadcrumbs":5,"title":1},"84":{"body":77,"breadcrumbs":5,"title":1},"85":{"body":294,"breadcrumbs":6,"title":2},"86":{"body":228,"breadcrumbs":5,"title":1},"87":{"body":196,"breadcrumbs":6,"title":2},"88":{"body":115,"breadcrumbs":9,"title":5},"89":{"body":4,"breadcrumbs":7,"title":3},"9":{"body":32,"breadcrumbs":3,"title":2},"90":{"body":43,"breadcrumbs":6,"title":2},"91":{"body":0,"breadcrumbs":5,"title":2},"92":{"body":26,"breadcrumbs":8,"title":5},"93":{"body":68,"breadcrumbs":6,"title":3},"94":{"body":127,"breadcrumbs":5,"title":2},"95":{"body":55,"breadcrumbs":4,"title":3},"96":{"body":95,"breadcrumbs":2,"title":1},"97":{"body":355,"breadcrumbs":3,"title":2},"98":{"body":1532,"breadcrumbs":5,"title":4},"99":{"body":40,"breadcrumbs":6,"title":2}},"docs":{"0":{"body":"ClamAV is an open source (GPLv2) anti-virus toolkit, designed especially for e-mail scanning on mail gateways. It provides a number of utilities including a flexible and scalable multi-threaded daemon, a command line scanner and advanced tool for automatic database updates. The core of the package is an anti-virus engine available in a form of shared library. Tip : ClamAV is not a traditional anti-virus or endpoint security suite. For a fully featured modern endpoint security suite, check out Cisco Secure Endpoint . See \"related products\" , below, for more details. ClamAV is brought to you by Cisco Systems, Inc.","breadcrumbs":"Introduction » ClamAV","id":"0","title":"ClamAV"},"1":{"body":"ClamAV has a diverse ecosystem of community projects, products, and other tools that either depend on ClamAV to provide malware detection capabilities or supplement ClamAV with new features such as improved support for 3rd party signature databases, graphical user interfaces (GUI), and more.","breadcrumbs":"Introduction » Community Projects","id":"1","title":"Community Projects"},"10":{"body":"ClamAV is widely available from third party package managers for most operating systems. This is often the quickest way to install ClamAV. It will make also upgrades easier. Check out the Packages page to find installation instructions for your system.","breadcrumbs":"Installing » Installing with a Package Manager","id":"10","title":"Installing with a Package Manager"},"100":{"body":"ClamAV supports a limited set of configuration options that may be enabled or disabled via settings in the *.cfg database. At this time, these settings are distributed in daily.cfg. The goal of DCONF is to enable the ClamAV team to rapidly disable new or experimental features for specific ClamAV versions if a significant defect is discovered after release. This database is small, and the settings are largely vestigial. The team has not had a need to disable many features in a long time, and so the ClamAV versions in the settings at this time should no longer be in use. The strings and values referenced in daily.cfg are best cross-referenced with the macros and structures defined here: https://github.com/Cisco-Talos/clamav/blob/main/libclamav/dconf.h#L49 https://github.com/Cisco-Talos/clamav/blob/main/libclamav/dconf.c#L54 The format for a DCONF signature is: Category:Flags:StartFlevel:EndFlevel Category may be one of: PE ELF MACHO ARCHIVE DOCUMENT MAIL OTHER PHISHING BYTECODE STATS PCRE Flags: Every feature that may be configured via DCONF is listed in struct dconf_module modules in libclamav/dconf.c. Any given feature may be default-on or default-off. Default-on features have the 4th field set to a 1 and default off are set to 0. The Flags field for a given Category overrides the defaults for all of the options listed under that category. A settings of 0x0, for example, means that all options the category be disabled. The macros listed in libclamav/dconf.h will help you identify which bits to set to get the desired results. StartFlevel: This is the FLEVEL of the minimum ClamAV engine for which you want the settings to be in effect. EndFlevel: This is the FLEVEL of the maximum ClamAV engine for which you want the settings to be in effect. You may wish to select 255 to override the defaults of future releases.","breadcrumbs":"Signatures » Dynamic Configuration Settings » Dynamic Configuration (DCONF)","id":"100","title":"Dynamic Configuration (DCONF)"},"101":{"body":"Consider the OTHER_CONF_PDFNAMEOBJ option in the category OTHER. #define OTHER_CONF_UUENC 0x1 // Default: 1\n#define OTHER_CONF_SCRENC 0x2 // Default: 1\n#define OTHER_CONF_RIFF 0x4 // Default: 1\n#define OTHER_CONF_JPEG 0x8 // Default: 1\n#define OTHER_CONF_CRYPTFF 0x10 // Default: 1\n#define OTHER_CONF_DLP 0x20 // Default: 1\n#define OTHER_CONF_MYDOOMLOG 0x40 // Default: 1\n#define OTHER_CONF_PREFILTERING 0x80 // Default: 1\n#define OTHER_CONF_PDFNAMEOBJ 0x100 // Default: 1\n#define OTHER_CONF_PRTNINTXN 0x200 // Default: 1\n#define OTHER_CONF_LZW 0x400 // Default: 1 All of the OTHER options, including OTHER_CONF_PDFNAMEOBJ are default-on. To disable the option for ClamAV v0.100.X but leave the other options in their default settings, we would need to set the flags to: 0110 1111 1111 ^pdfnameobj off Or in hex: 0x6FF The example setting to place in daily.cfg then woudl be: OTHER:0x6FF:90:99","breadcrumbs":"Signatures » Dynamic Configuration Settings » Example","id":"101","title":"Example"},"102":{"body":"Clamav 0.98 checks signed PE files for certificates and verifies each certificate in the chain against a database of trusted and revoked certificates. The signature format is Name;Trusted;Subject;Serial;Pubkey;Exponent;CodeSign;TimeSign;CertSign;\nNotBefore;Comment[;minFL[;maxFL]] where the corresponding fields are: Name: name of the entry Trusted: bit field, specifying whether the cert is trusted. 1 for trusted. 0 for revoked Subject: sha1 of the Subject field in hex Serial: the serial number as clamscan --debug --verbose reports Pubkey: the public key in hex Exponent: the exponent in hex. Currently ignored and hardcoded to 010001 (in hex) CodeSign: bit field, specifying whether this cert can sign code. 1 for true, 0 for false TimeSign: bit field. 1 for true, 0 for false CertSign: bit field, specifying whether this cert can sign other certs. 1 for true, 0 for false NotBefore: integer, cert should not be added before this variable. Defaults to 0 if left empty Comment: comments for this entry The signatures for certs are stored inside .crb files.","breadcrumbs":"Signatures » Trusted and Revoked EXE Certificates » Trusted and Revoked Certificates","id":"102","title":"Trusted and Revoked Certificates"},"103":{"body":"ClamAV's primary mechanism for determining file types is to match the file with a File Type Magic signature. These file type signatures are compiled into ClamAV, and may also be overridden dynamically using the definition founds found in a *.ftm file. The ClamAV standard signature database includes these definitions in daily.ftm. The signature format is not too disimilar from NDB body-based signatures. The format is: magictype:offset:magicbytes:name:rtype:type[:min_flevel[:max_flevel]] Where: magictype: Supported magic types include: 0 - direct memory comparison of magicbytes for file types 1 - The magicbytes use the body-based content matching format . 4 - direct memory comparison of magicbytes for partition types (HFS+, HFSX) offset: The offset from start of the file to match against. May be * if magictype is 1. name: A descriptive name for the file type. rtype: Previously detected file type. Usually CL_TYPE_ANY as a wild-card. type: The CL_TYPE corresponding with the file type signature. See the CL_TYPE reference for details. min_flevel: (optional) The minimum ClamAV engine that the file type signature works with. See the FLEVEL reference for details. To be used in the event that file type support has been recently added. max_flevel: (optional, requires min_flevel) The maximum ClamAV engine that the file type signature works with. To be used in the event that file type support has been recently removed.","breadcrumbs":"Signatures » File Type Recognition » File Type Magic","id":"103","title":"File Type Magic"},"104":{"body":"","breadcrumbs":"Signatures » Allow Lists » Allow list databases","id":"104","title":"Allow list databases"},"105":{"body":"To allow a specific file use the MD5 signature format and place it inside a database file with the extension of .fp (for \"false positive\"). To allow a specific file with the SHA1 or SHA256 file hash signature format, place the signature inside a database file with the extension of .sfp (for \"SHA false positive\"). To generate FP or SFP signatures, try something like this... MD5: sigtool --md5 /path/to/false/positive/file >> /path/to/databases/false-positives.fp SHA256: sigtool --sha256 /path/to/false/positive/file >> /path/to/databases/false-positives.sfp Here's an example adding the EICAR test file to an allow list by generating a sha256 false positive signature: ❯ clamscan ~/Downloads/eicar.com\n/mnt/c/Users/micah/Downloads/eicar.com: Win.Test.EICAR_HDB-1 FOUND ... ❯ sigtool --sha256 ~/Downloads/eicar.com >> /var/lib/clamav/false-positives.sfp ❯ clamscan ~/Downloads/eicar.com\n/mnt/c/Users/micah/Downloads/eicar.com: OK\n...","breadcrumbs":"Signatures » Allow Lists » File allow lists","id":"105","title":"File allow lists"},"106":{"body":"To ignore a specific signature from the database you just add the signature name into a local file with the .ign2 extension and store it inside the database directory. E.g: Eicar-Test-Signature Additionally, you can follow the signature name with the MD5 of the entire database entry for this signature. In such a case, the signature will no longer be ignored when its entry in the database gets modified (eg. the signature gets updated to avoid false alerts). E.g: Eicar-Test-Signature:bc356bae4c42f19a3de16e333ba3569c Historically, signature ignores were added to .ign files. This format is still functional, though it has been replaced by the .ign2 database.","breadcrumbs":"Signatures » Allow Lists » Signature ignore lists","id":"106","title":"Signature ignore lists"},"107":{"body":"The easiest way to create signatures for ClamAV is to use filehash checksums, however this method can be only used against static malware.","breadcrumbs":"Signatures » Hash-based Signatures » File hash signatures","id":"107","title":"File hash signatures"},"108":{"body":"To create a MD5 signature for test.exe use the --md5 option of sigtool: zolw@localhost:/tmp/test$ sigtool --md5 test.exe > test.hdb\nzolw@localhost:/tmp/test$ cat test.hdb\n48c4533230e1ae1c118c741c0db19dfb:17387:test.exe That’s it! The signature is ready for use: zolw@localhost:/tmp/test$ clamscan -d test.hdb test.exe\ntest.exe: test.exe FOUND ----------- SCAN SUMMARY -----------\nKnown viruses: 1\nScanned directories: 0\nEngine version: 0.92.1\nScanned files: 1\nInfected files: 1\nData scanned: 0.02 MB\nTime: 0.024 sec (0 m 0 s) You can change the name (by default sigtool uses the name of the file) and place it inside a *.hdb file. A single database file can include any number of signatures. To get them automatically loaded each time clamscan/clamd starts just copy the database file(s) into the local virus database directory (eg. /usr/local/share/clamav). The hash-based signatures shall not be used for text files, HTML and any other data that gets internally preprocessed before pattern matching. If you really want to use a hash signature in such a case, run clamscan with --debug and --leave-temps flags as described above and create a signature for a preprocessed file left in /tmp. Please keep in mind that a hash signature will stop matching as soon as a single byte changes in the target file.","breadcrumbs":"Signatures » Hash-based Signatures » MD5 hash-based signatures","id":"108","title":"MD5 hash-based signatures"},"109":{"body":"ClamAV 0.98 has also added support for SHA1 and SHA256 file checksums. The format is the same as for MD5 file checksum. It can differentiate between them based on the length of the hash string in the signature. For best backwards compatibility, these should be placed inside a *.hsb file. The format is: HashString:FileSize:MalwareName","breadcrumbs":"Signatures » Hash-based Signatures » SHA1 and SHA256 hash-based signatures","id":"109","title":"SHA1 and SHA256 hash-based signatures"},"11":{"body":"Pre-compiled packages provided on the clamav.net downloads page have all external library dependencies statically compiled in. These installers likely differ from packages provided by other packaging tools in that you will need to create and configure the freshclam.conf and clamd.conf files. You may also need to add a clamav service user account and adjust the permissions on the database directory. We hope to round out these sharp corners in the future and to make setup more convenient, but for now be advised that setup from one of these packages is a little bit more work than you may be used to. If you're interested in learning how these packages were built, you can check out these development instructions . Note : In the event that a vulnerability is found in one of the dependencies that may impact ClamAV, we will publish new packages with updated dependencies as soon as we're able. Linux (.deb, .rpm) Beginning with ClamAV 0.104, we offer Debian and RPM packages for x86_64 (64bit) and i686 (32bit) architectures. This will make it easier to get the latest version in the event that a package for your distribution is not readily available and you would prefer not to build ClamAV from source. Note : These packages do not presently include clamav-milter. You can help help us add clamav-milter to the packages by developing a Mussels recipe for building the libmilter.a static library and contributing it to our Mussels cookbook . RPM packages (for CentOS, Redhat, Fedora, SUSE, etc.) These are compiled on CentOS 7. They should be compatible with all RPM-based linux distributions running glibc version 2.17 or newer. To install, download the package for your system use yum or dnf to install the package. For example: sudo dnf install ~/Downloads/clamav-0.104.0-rc2.linux.x86_64.rpm You can verify that the package was installed using: dnf info clamav This package installs to /usr/local. Unlike packages provided by Debian or other distributions, this package does not presently include a preconfigured freshclam.conf, clamd.conf, database directory, or clamav user accounts for FreshClam and ClamD. You can follow these instructions to configure FreshClam and ClamD. You can follow these instructions to create the clamav user account for running FreshClam and ClamD services. And uninstall the package with: sudo dnf remove ~/Downloads/clamav-0.104.0-rc2.linux.x86_64.rpm DEB packages (for Debian, Ubuntu, Mint, etc.) These are compiled on Ubuntu 16.04, and have all external library dependencies statically compiled in. They should be compatible with all Debian-based linux distributions running glibc version 2.23 or newer. sudo apt install ~/Downloads/clamav-0.104.0-rc2.libnux.x86_64.deb You can verify that the package was installed using: apt info clamav This package installs to /usr/local. Unlike packages provided by Debian or other distributions, this package does not presently include a preconfigured freshclam.conf, clamd.conf, database directory, or clamav user accounts for FreshClam and ClamD. You can follow these instructions to configure FreshClam and ClamD. You can follow these instructions to create the clamav user account for running FreshClam and ClamD services. And uninstall the package with: sudo apt remove clamav macOS Beginning with ClamAV 0.104, we offer a PKG installer for macOS. These are universal binaries built for Intel x86_64 and Apple M1 arm64 processors. Disclaimer : The release materials for 0.104.0-rc2 are not signed or notarized. We are working on adding signing and notarization to our CI processes, but for now you may be unable to use this PKG installer on macOS Big Sur or newer. To install, download the macOS .pkg installer. Double-click the installer and follow the directions. This package installs to /usr/local/clamav. This is not in the default system PATH environment variable. You may wish to add /usr/local/clamav/bin and /usr/local/clamav/sbin to your PATH so you can run the ClamAV programs without entering the full path. To do this add this line to ~/.zshrc: export PATH=/usr/local/clamav/bin:/usr/local/clamav/sbin:$PATH Then run source ~/.zshrc or open a new terminal. Unlike packages provided by Homebrew, this package does not presently include a preconfigured freshclam.conf, clamd.conf, or database directory. You can follow these instructions to configure FreshClam and ClamD. macOS package installers do not provide a mechanism for automatically uninstalling the package. In the future, we hope to add a script to aid with uninstallation. But for now, to make it easier to remove, our macOS installer installs to /usr/local/clamav. To uninstall, all you need to do is run: sudo rm -rf /usr/local/clamav Windows The ClamAV team provides official ClamAV builds for Windows on the ClamAV downloads page . You can choose between a traditional executable installer or a portable install ZIP package. To use the executable installer, double-click the installer and follow the instructions. To install from a ZIP package, unzip the portable install package to any directory.","breadcrumbs":"Installing » Installing with an Installer","id":"11","title":"Installing with an Installer"},"110":{"body":"ClamAV 0.98 has also added support for hash signatures where the size is not known but the hash is. It is much more performance-efficient to use signatures with specific sizes, so be cautious when using this feature. For these cases, the ’*’ character can be used in the size field. To ensure proper backwards compatibility with older versions of ClamAV, these signatures must have a minimum functional level of 73 or higher. Signatures that use the wildcard size without this level set will be rejected as malformed. Sample .hsb signature matching any size: HashString:*:MalwareName:73 Sample .msb signature matching any size: *:PESectionHash:MalwareName:73","breadcrumbs":"Signatures » Hash-based Signatures » Hash signatures with unknown size","id":"110","title":"Hash signatures with unknown size"},"111":{"body":"You can create a hash signature for a specific section in a PE file. Such signatures shall be stored inside .mdb (MD5) and .msb files in the following format: PESectionSize:PESectionHash:MalwareName The easiest way to generate MD5 based section signatures is to extract target PE sections into separate files and then run sigtool with the option --mdb ClamAV also has support for SHA1 and SHA256 section based signatures. The format is the same as for MD5 PE section based signatures. It can differentiate between them based on the length of the hash string in the signature. For best backwards compatibility, these should be placed inside a *.msb file.","breadcrumbs":"Signatures » Hash-based Signatures » PE section based hash signatures","id":"111","title":"PE section based hash signatures"},"112":{"body":"ClamAV stores all body-based (content-based) signatures in a hexadecimal format, with exception to ClamAV's YARA rule support. In this section by a hex-signature we mean a fragment of malware’s body converted into a hexadecimal string which can be additionally extended using various wildcards.","breadcrumbs":"Signatures » Content-based Signature Format » Body-based Signature Content Format","id":"112","title":"Body-based Signature Content Format"},"113":{"body":"You can use sigtool --hex-dump to convert any data into a hex-string: zolw@localhost:/tmp/test$ sigtool --hex-dump\nHow do I look in hex?\n486f7720646f2049206c6f6f6b20696e206865783f0a","breadcrumbs":"Signatures » Content-based Signature Format » Hexadecimal format","id":"113","title":"Hexadecimal format"},"114":{"body":"ClamAV supports the following wildcards for hex-signatures: ?? Match any byte. a? Match a high nibble (the four high bits). ?a Match a low nibble (the four low bits). * Match any number of bytes. {n} Match n bytes. {-n} Match n or less bytes. {n-} Match n or more bytes. {n-m} Match between n and m bytes (where m > n). HEXSIG[x-y]aa or aa[x-y]HEXSIG Match aa anchored to a hex-signature, see Bugzilla ticket 776 for discussion and examples. The range signatures * and {} virtually separate a hex-signature into two parts, eg. aabbcc*bbaacc is treated as two sub-signatures aabbcc and bbaacc with any number of bytes between them. It’s a requirement that each sub-signature includes a block of two static characters somewhere in its body. Note that there is one exception to this restriction; that is when the range wildcard is of the form {n} with n<128. In this case, ClamAV uses an optimization and translates {n} to the string consisting of n ?? character wildcards. Character wildcards do not divide hex signatures into two parts and so the two static character requirement does not apply.","breadcrumbs":"Signatures » Content-based Signature Format » Wildcards","id":"114","title":"Wildcards"},"115":{"body":"ClamAV supports the following character classes for hex-signatures: (B) Match word boundary (including file boundaries). (L) Match CR, CRLF or file boundaries. (W) Match a non-alphanumeric character.","breadcrumbs":"Signatures » Content-based Signature Format » Character classes","id":"115","title":"Character classes"},"116":{"body":"Single-byte alternates (clamav-0.96) (aa|bb|cc|...) or !(aa|bb|cc|...) Match a member from a set of bytes (eg: aa, bb, cc, ...). Negation operation can be applied to match any non-member, assumed to be one-byte in length. Signature modifiers and wildcards cannot be applied. Multi-byte fixed length alternates (aaaa|bbbb|cccc|...) or !(aaaa|bbbb|cccc|...) Match a member from a set of multi-byte alternates (eg: aaaa, bbbb, cccc, ...) of n-length. All set members must be the same length. Negation operation can be applied to match any non-member, assumed to be n-bytes in length (clamav-0.98.2). Signature modifiers and wildcards cannot be applied. Generic alternates (clamav-0.99) (alt1|alt2|alt3|...) Match a member from a set of alternates (eg: alt1, alt2, alt3, ...) that can be of variable lengths. Negation operation cannot be applied. Signature modifiers and nibble wildcards (eg: ??, a?, ?a) can be applied. Ranged wildcards (eg: {n-m}) are limited to a fixed range of less than 128 bytes (eg: {1} -> {127}). Note : Using signature modifiers and wildcards classifies the alternate type to be a generic alternate. Thus single-byte alternates and multi-byte fixed length alternates can use signature modifiers and wildcards but will be classified as generic alternate. This means that negation cannot be applied in this situation and there is a slight performance impact.","breadcrumbs":"Signatures » Content-based Signature Format » Alternate strings","id":"116","title":"Alternate strings"},"117":{"body":"Logical signatures allow combining of multiple signatures in extended format using logical operators. They can provide both more detailed and flexible pattern matching. The logical sigs are stored inside *.ldb files in the following format: SignatureName;TargetDescriptionBlock;LogicalExpression;Subsig0;\nSubsig1;Subsig2;... where: TargetDescriptionBlock provides information about the engine and target file with comma separated Arg:Val pairs. For args where Val is a range, the minimum and maximum values should be expressed as min-max. LogicalExpression specifies the logical expression describing the relationship between Subsig0...SubsigN. Basis clause: 0,1,...,N decimal indexes are SUB-EXPRESSIONS representing Subsig0, Subsig1,...,SubsigN respectively. Inductive clause: if A and B are SUB-EXPRESSIONS and X, Y are decimal numbers then (A&B), (A|B), A=X, A=X,Y, A>X, A>X,Y, ACL_TYPE_*: Specify one or more layers of file types containing the scanned file. This is an alternative to using Container. You may specify up to 16 layers of file types separated by ’>’ in top-down order. Note that the ’>’ separator is not needed if you only specify a single container. The last type should be the immediate container containing the malicious file. Unlike with the Container option, CL_TYPE_ANY can be used as a wildcard file type. (expr; 0.100.0) For a list of possible CL_TYPEs, refer to the File Types Reference . IconGroup1: Icon group name 1 from .idb signature Required engine functionality (range; 0.96) IconGroup2: Icon group name 2 from .idb signature Required engine functionality (range; 0.96) Modifiers for subexpressions: A=X: If the SUB-EXPRESSION A refers to a single signature then this signature must get matched exactly X times; if it refers to a (logical) block of signatures then this block must generate exactly X matches (with any of its sigs). A=0 specifies negation (signature or block of signatures cannot be matched) A=X,Y: If the SUB-EXPRESSION A refers to a single signature then this signature must be matched exactly X times; if it refers to a (logical) block of signatures then this block must generate X matches and at least Y different signatures must get matched. A>X: If the SUB-EXPRESSION A refers to a single signature then this signature must get matched more than X times; if it refers to a (logical) block of signatures then this block must generate more than X matches (with any of its sigs). A>X,Y: If the SUB-EXPRESSION A refers to a single signature then this signature must get matched more than X times; if it refers to a (logical) block of signatures then this block must generate more than X matches and at least Y different signatures must be matched. AZ above with the change of \"more\" to \"less\". If the SUB-EXPRESSION A refers to a single signature then this signature must get matched less than X times; if it refers to a (logical) block of signatures then this block must generate less than X matches (with any of its sigs). AX,Y. If the SUB-EXPRESSION A refers to a single signature then this signature must get matched less than X times; if it refers to a (logical) block of signatures then this block must generate less than X matches and at least Y different signatures must be matched. Examples: Sig1;Target:0;(0&1&2&3)&(4|1);6b6f74656b;616c61;7a6f6c77;7374656\n6616e;deadbeef Sig2;Target:0;((0|1|2)>5,2)&(3|1);6b6f74656b;616c61;7a6f6c77;737\n46566616e Sig3;Target:0;((0|1|2|3)=2)&(4|1);6b6f74656b;616c61;7a6f6c77;737\n46566616e;deadbeef Sig4;Engine:51-255,Target:1;((0|1)&(2|3))&4;EP+123:33c06834f04100\nf2aef7d14951684cf04100e8110a00;S2+78:22??232c2d252229{-15}6e6573\n(63|64)61706528;S3+50:68efa311c3b9963cb1ee8e586d32aeb9043e;f9c58\ndcf43987e4f519d629b103375;SL+550:6300680065005c0046006900","breadcrumbs":"Signatures » Content-based Signature Format » Logical Signatures » Logical signatures","id":"117","title":"Logical signatures"},"118":{"body":"ClamAV (clamav-0.99) supports a number of additional subsignature modifiers for logical signatures. This is done by specifying :: followed by a number of characters representing the desired options. Signatures using subsignature modifiers require Engine:81-255 for backwards-compatibility. Case-Insensitive [i] Specifying the i modifier causes ClamAV to match all alphabetic hex bytes as case-insensitive. All patterns in ClamAV are case-sensitive by default. Wide [w] Specifying the w causes ClamAV to match all hex bytes encoded with two bytes per character. Note this simply interweaves each character with NULL characters and does not truly support UTF-16 characters. Wildcards for ’wide’ subsignatures are not treated as wide (i.e. there can be an odd number of intermittent characters). This can be combined with a to search for patterns in both wide and ascii. Fullword [f] Match subsignature as a fullword (delimited by non-alphanumeric characters). Ascii [a] Match subsignature as ascii characters. This can be combined with w to search for patterns in both ascii and wide. Examples: Match 'AAAA'(nocase) and 'BBBBBB'(nocase) clamav-nocase-A;Engine:81-255,Target:0;0&1;41414141::i;424242424242::i Match 'AAA' and 'hello'(fullword) clamav-fullword-A;Engine:81-255,Target:0;0&1;414141;68656c6c6f::f Match 'AAA' and 'hello'(fullword nocase) clamav-fullword-B;Engine:81-255,Target:0;0&1;414141;68656c6c6f::fi Match 'AAA' and 'hello'(wide ascii) clamav-wide-B2;Engine:81-255,Target:0;0&1;414141;68656c6c6f::wa Match 'AAA' and 'hello'(nocase wide fullword ascii) clamav-wide-C0;Engine:81-255,Target:0;0&1;414141;68656c6c6f::iwfa","breadcrumbs":"Signatures » Content-based Signature Format » Logical Signatures » Subsignature Modifiers","id":"118","title":"Subsignature Modifiers"},"119":{"body":"Macro subsignatures Introduced in ClamAV 0.96 Format: ${min-max}MACROID$ Macro subsignatures are used to combine a number of existing extended signatures (.ndb) into a on-the-fly generated alternate string logical signature (.ldb). Signatures using macro subsignatures require Engine:51-255 for backwards-compatibility. Example: test.ldb: TestMacro;Engine:51-255,Target:0;0&1;616161;${6-7}12$ test.ndb: D1:0:$12:626262 D2:0:$12:636363 D3:0:$30:626264 The example logical signature TestMacro is functionally equivalent to: TestMacro;Engine:51-255,Target:0;0;616161{3-4}(626262|636363) MACROID points to a group of signatures; there can be at most 32 macro groups. In the example, MACROID is 12 and both D1 and D2 are members of macro group 12. D3 is a member of separate macro group 30. {min-max} specifies the offset range at which one of the group signatures should match; the offset range is relative to the starting offset of the preceding subsignature. This means a macro subsignature cannot be the first subsignature. In the example, {min-max} is {6-7} and it is relative to the start of a 616161 match. For more information and examples please see https://bugzilla.clamav.net/show_bug.cgi?id=164 . Byte Compare Subsignatures Introduced in ClamAV 0.101 Format: subsigid_trigger(offset#byte_options#comparisons) Byte compare subsignatures can be used to evaluate a numeric value at a given offset from the start of another (matched) subsignature within the same logical signature. These are executed after all other subsignatures within the logical subsignature are fired, with the exception of PCRE subsignatures. They can evaluate offsets only from a single referenced subsignature, and that subsignature must give a valid match for the evaluation to occur. subsigid_trigger is a required field and may refer to any single non-PCRE, non-Byte Compare subsignature within the lsig. The byte compare subsig will evaluate if subsigid_trigger matches. Triggering on multiple subsigs or logic based triggering is not currently supported. offset is a required field that consists of an offset_modifier and a numeric offset (hex or decimal offsets are okay). offset_modifier can be either >> or << where the former denotes a positive offset and the latter denotes a negative offset. The offset is calculated from the start of subsigid_trigger, which allows for byte extraction before the specified match, after the match, and within the match itself. offset must be a positive hex or decimal value. This will be the number of bytes from the start of the referenced subsigid_trigger match within the file buffer to begin the comparison. byte_options are used to specify the numeric type and endianess of the extracted byte sequence in that order as well as the number of bytes to be read. By default ClamAV will attempt to matchup up to the number of byte specified, unless the e (exact) option is specified or the numeric type is b (binary). This field follows the form [h|d|a|i][l|b][e]num_bytes h|d|a|i where h specifies the byte sequence will be in hex, d decimal, a automatic detection of hex or decimal at runtime, and i signifies raw binary data. l|b where l specifies the byte sequence will be in little endian order and b big endian. If decimal d is specified, big-endian is implied and using l will result in a malformed database error. e specifies that ClamAV will only evaluate the comparison if it can extract the exact number of bytes specified. This option is implicitly declared when using the i flag. num_bytes specifies the number of bytes to extract. This can be a hex or decimal value. If i is specified only 1, 2, 4, and 8 are valid options. comparisons are a required field which denotes how to evaluate the extracted byte sequence. Each Byte Compare signature can have one or two comparison_sets separated by a comma. Each comparison_set consists of a Comparison_symbol and a Comparison_value and takes the form Comparison_symbolComparison_value. Thus, comparisons takes the form comparison_set[,comparison_set] Comparison_symbol denotes the type of comparison to be done. The supported comparison symbols are <, >, =. Comparison_value is a required field which must be a numeric hex or decimal value. If all other conditions are met, the byte compare subsig will evalutate the extracted byte sequence against this number based on the provided comparison_symbol. PCRE subsignatures Introduced in ClamAV 0.99 Format: Trigger/PCRE/[Flags] PCRE subsignatures are used within a logical signature (.ldb) to specify regex matches that execute once triggered by a conditional based on preceding subsignatures. Signatures using PCRE subsignatures require Engine:81-255 for backwards-compatibility. Trigger is a required field that is a valid LogicalExpression and may refer to any subsignatures that precede this subsignature. Triggers cannot be self-referential and cannot refer to subsequent subsignatures. PCRE is the expression representing the regex to execute. PCRE must be delimited by ’/’ and usage of ’/’ within the expression need to be escaped. For backward compatibility, ’;’ within the expression must be expressed as ’\\x3B’. PCRE cannot be empty and (?UTF*) control sequence is not allowed. If debug is specified, named capture groups are displayed in a post-execution report. Flags are a series of characters which affect the compilation and execution of PCRE within the PCRE compiler and the ClamAV engine. This field is optional. g [CLAMAV_GLOBAL] specifies to search for ALL matches of PCRE (default is to search for first match). NOTE: INCREASES the time needed to run the PCRE. r [CLAMAV_ROLLING] specifies to use the given offset as the starting location to search for a match as opposed to the only location; applies to subsigs without maxshifts. By default, in order to facilatate normal ClamAV offset behavior, PCREs are auto-anchored (only attempt match on first offset); using the rolling option disables the auto-anchoring. e [CLAMAV_ENCOMPASS] specifies to CONFINE matching between the specified offset and maxshift; applies only when maxshift is specified. Note : DECREASES time needed to run the PCRE. i [PCRE_CASELESS] s [PCRE_DOTALL] m [PCRE_MULTILINE] x [PCRE_EXTENDED] A [PCRE_ANCHORED] E [PCRE_DOLLAR_ENODNLY] U [PCRE_UNGREEDY] Examples: Find.All.ClamAV;Engine:81-255,Target:0;1;6265676c6164697427736e6f7462797465636f6465;0/clamav/g Find.ClamAV.OnlyAt.299;Engine:81-255,Target:0;2;7374756c747a67657473;7063726572656765786c6f6c;299:0&1/clamav/ Find.ClamAV.StartAt.300;Engine:81-255,Target:0;3;616c61696e;62756731393238;636c6f736564;300:0&1&2/clamav/r Find.All.Encompassed.ClamAV;Engine:81-255,Target:0;3;7768796172656e2774;796f757573696e67;79617261;200,300:0&1&2/clamav/ge Named.CapGroup.Pcre;Engine:81-255,Target:0;3;636f75727479617264;616c62756d;74657272696572;50:0&1&2/variable=(?<nilshell>.{16})end/gr Firefox.TreeRange.UseAfterFree;Engine:81-255,Target:0,Engine:81-255;0&1&2;2e766965772e73656c656374696f6e;2e696e76616c696461746553656c656374696f6e;0&1/\\x2Eview\\x2Eselection.*?\\x2Etree\\s*\\x3D\\s*null.*?\\x2Einvalidate/smi Firefox.IDB.UseAfterFree;Engine:81-255,Target:0;0&1;4944424b657952616e6765;0/^\\x2e(only|lowerBound|upperBound|bound)\\x28.*?\\x29.*?\\x2e(lower|upper|lowerOpen|upperOpen)/smi Firefox.boundElements;Engine:81-255,Target:0;0&1&2;6576656e742e6\n26f756e64456c656d656e7473;77696e646f772e636c6f7365;0&1/on(load|click)\\s*=\\s*\\x22?window\\.close\\s*\\x28/si","breadcrumbs":"Signatures » Content-based Signature Format » Logical Signatures » Special Subsignature Types","id":"119","title":"Special Subsignature Types"},"12":{"body":"There are now official ClamAV images on Docker Hub. You can find the images on Docker Hub under clamav . At present we offer images with builds of the latest development version. We call this \"unstable\". ClamAV 0.104 will be the first stable release that we'll publish on Docker Hub.. Once published 0.104.0+ will be available using a Docker image tag with the specific version number, or using \"stable\" to get the latest stable release. Check out the Docker page to learn how to install and use ClamAV with Docker.","breadcrumbs":"Installing » Official ClamAV Docker Images","id":"12","title":"Official ClamAV Docker Images"},"120":{"body":"Starting with ClamAV 0.96 it is possible to easily match certain information built into PE files (executables and dynamic link libraries). Whenever you lookup the properties of a PE executable file in windows, you are presented with a bunch of details about the file itself. These info are stored in a special area of the file resources which goes under the name of VS_VERSION_INFORMATION (or versioninfo for short). It is divided into 2 parts. The first part (which is rather uninteresting) is really a bunch of numbers and flags indicating the product and file version. It was originally intended for use with installers which, after parsing it, should be able to determine whether a certain executable or library are to be upgraded/overwritten or are already up to date. Suffice to say, this approach never really worked and is generally never used. The second block is much more interesting: it is a simple list of key/value strings, intended for user information and completely ignored by the OS. For example, if you look at ping.exe you can see the company being \"Microsoft Corporation\" , the description \"TCP/IP Ping command\" , the internal name \"ping.exe\" and so on... Depending on the OS version, some keys may be given peculiar visibility in the file properties dialog, however they are internally all the same. To match a versioninfo key/value pair, the special file offset anchor VI was introduced. This is similar to the other anchors (like EP and SL) except that, instead of matching the hex pattern against a single offset, it checks it against each and every key/value pair in the file. The VI token doesn’t need nor accept a +/- offset like e.g. EP+1. As for the hex signature itself, it’s just the utf16 dump of the key and value. Only the ?? and (aa|bb) wildcards are allowed in the signature. Usually, you don’t need to bother figuring it out: each key/value pair together with the corresponding VI-based signature is printed by clamscan when the --debug option is given. For example clamscan --debug freecell.exe produces: [...]\nRecognized MS-EXE/DLL file\nin cli_peheader\nversioninfo_cb: type: 10, name: 1, lang: 410, rva: 9608\ncli_peheader: parsing version info @ rva 9608 (1/1)\nVersionInfo (d2de): 'CompanyName'='Microsoft Corporation' -\nVI:43006f006d00700061006e0079004e0061006d006500000000004d006900\n630072006f0073006f0066007400200043006f00720070006f0072006100740\n069006f006e000000\nVersionInfo (d32a): 'FileDescription'='Entertainment Pack\nFreeCell Game' - VI:460069006c006500440065007300630072006900700\n0740069006f006e000000000045006e007400650072007400610069006e006d\n0065006e00740020005000610063006b0020004600720065006500430065006\nc006c002000470061006d0065000000\nVersionInfo (d396): 'FileVersion'='5.1.2600.0 (xpclient.010817\n-1148)' - VI:460069006c006500560065007200730069006f006e00000000\n0035002e0031002e0032003600300030002e003000200028007800700063006\nc00690065006e0074002e003000310030003800310037002d00310031003400\n380029000000\nVersionInfo (d3fa): 'InternalName'='freecell' - VI:49006e007400\n650072006e0061006c004e0061006d006500000066007200650065006300650\n06c006c000000\nVersionInfo (d4ba): 'OriginalFilename'='freecell' - VI:4f007200\n6900670069006e0061006c00460069006c0065006e0061006d0065000000660\n0720065006500630065006c006c000000\nVersionInfo (d4f6): 'ProductName'='Sistema operativo Microsoft\nWindows' - VI:500072006f0064007500630074004e0061006d00650000000\n000530069007300740065006d00610020006f00700065007200610074006900\n76006f0020004d006900630072006f0073006f0066007400ae0020005700690\n06e0064006f0077007300ae000000\nVersionInfo (d562): 'ProductVersion'='5.1.2600.0' - VI:50007200\n6f006400750063007400560065007200730069006f006e00000035002e00310\n02e0032003600300030002e0030000000\n[...] Although VI-based signatures are intended for use in logical signatures you can test them using ordinary .ndb files. For example: my_test_vi_sig:1:VI:paste_your_hex_sig_here Final note. If you want to decode a VI-based signature into a human readable form you can use: echo hex_string | xxd -r -p | strings -el For example: echo 460069006c0065004400650073006300720069007000740069006f006e000000000045006e007400650072007400610069006e006d0065006e00740020005000610063006b0020004600720065006500430065006c006c00200047006100\n6d0065000000 | xxd -r -p | strings -el\nFileDescription\nEntertainment Pack FreeCell Game","breadcrumbs":"Signatures » Content-based Signature Format » Logical Signatures » Signatures for Version Information (VI) metadata in PE files","id":"120","title":"Signatures for Version Information (VI) metadata in PE files"},"121":{"body":"While Icon Signatures are stored in a .idb file, they are a feature of Logical Signatures. ClamAV 0.96 includes an approximate/fuzzy icon matcher to help detecting malicious executables disguising themselves as innocent looking image files, office documents and the like. Icon matching is only triggered by Logical Signatures (.ldb) using the special attribute tokens IconGroup1 or IconGroup2. These identify two (optional) groups of icons defined in a .idb database file. The format of the .idb file is: ICONNAME:GROUP1:GROUP2:ICON_HASH where: ICON_NAME is a unique string identifier for a specific icon, GROUP1 is a string identifier for the first group of icons (IconGroup1) GROUP2 is a string identifier for the second group of icons (IconGroup2), ICON_HASH is a fuzzy hash of the icon image The ICON_HASH field can be obtained from the debug output of libclamav. For example: LibClamAV debug: ICO SIGNATURE:\nICON_NAME:GROUP1:GROUP2:18e2e0304ce60a0cc3a09053a30000414100057e000afe0000e 80006e510078b0a08910d11ad04105e0811510f084e01040c080a1d0b0021000a39002a41","breadcrumbs":"Signatures » Content-based Signature Format » Logical Signatures » Icon Signatures for PE files","id":"121","title":"Icon Signatures for PE files"},"122":{"body":"The extended signature format is ClamAV's most basic type of body-based signature since the deprecation of the original .db database format. Extended sigantures allow for specification of additional information beyond just hexidecimal content such as a file \"target type\", virus offset, or engine functionality level (FLEVEL), making the detection more reliable. The format is: MalwareName:TargetType:Offset:HexSignature[:min_flevel:[max_flevel]] MalwareName: The virus name. Should conform to the standards defined here . TargetType: A number specifying the type of the target file: Target Types Offset: An asterisk or a decimal number n possibly combined with a special modifier: * = any n = absolute offset EOF-n = end of file minus n bytes Signatures for PE, ELF and Mach-O files additionally support: EP+n = entry point plus n bytes (EP+0 for EP) EP-n = entry point minus n bytes Sx+n = start of section x’s (counted from 0) data plus n bytes SEx = entire section x (offset must lie within section boundaries) SL+n = start of last section plus n bytes All the above offsets except * can be turned into floating offsets and represented as Offset,MaxShift where MaxShift is an unsigned integer. A floating offset will match every offset between Offset and Offset+MaxShift, eg. 10,5 will match all offsets from 10 to 15 and EP+n,y will match all offsets from EP+n to EP+n+y. Versions of ClamAV older than 0.91 will silently ignore the MaxShift extension and only use Offset. Optional MinFL and MaxFL parameters can restrict the signature to specific engine releases. All signatures in the extended format must be placed inside *.ndb files. HexSignature: The body-based content matching format . min_flevel: (optional) The minimum ClamAV engine that the file type signature works with. See the FLEVEL reference for details. To be used in the event that file type support has been recently added. max_flevel: (optional, requires min_flevel) The maximum ClamAV engine that the file type signature works with. To be used in the event that file type support has been recently removed.","breadcrumbs":"Signatures » Content-based Signature Format » Extended Signatures » Extended signature format","id":"122","title":"Extended signature format"},"123":{"body":"ClamAV can process YARA rules. ClamAV virus database file names ending with .yar or .yara are parsed as YARA rule files. The link to the YARA rule grammar documentation may be found at https://virustotal.github.io/yara/ . There are currently a few limitations on using YARA rules within ClamAV: YARA modules are not yet supported by ClamAV. This includes the “import” keyword and any YARA module-specific keywords. Global rules (global keyword) are not supported by ClamAV. External variables(contains and matches keywords) are not supported. YARA rules pre-compiled with the yarac command are not supported. As in the ClamAV logical and extended signature formats, YARA strings and segments of strings separated by wild cards must represent at least two octets of data. There is a maximum of 64 strings per YARA rule. YARA rules in ClamAV must contain at least one literal, hexadecimal, or regular expression string. In addition, there are a few more ClamAV processing modes that may affect the outcome of YARA rules. File decomposition and decompression - Since ClamAV uses file decomposition and decompression to find viruses within de-archived and uncompressed inner files, YARA rules executed by ClamAV will match against these files as well. Normalization - By default, ClamAV normalizes HTML, JavaScript, and ASCII text files. YARA rules in ClamAV will match against the normalized result. The effects of normalization of these file types may be captured using clamscan --leave-temps --tempdir=mytempdir. YARA rules may then be written using the normalized file(s) found in mytempdir. Alternatively, starting with ClamAV 0.100.0, clamscan --normalize=no will prevent normalization and only scan the raw file. To obtain similar behavior prior to 0.99.2, use clamscan --scan-html=no. The corresponding parameters for clamd.conf are Normalize and ScanHTML. YARA conditions driven by string matches - All YARA conditions are driven by string matches in ClamAV. This saves from executing every YARA rule on every file. Any YARA condition may be augmented with a string match clause which is always true, such as: rule CheckFileSize\n{ strings: $abc = \"abc\" condition: ($abc or not $abc) and filesize < 200KB\n} This will ensure that the YARA condition always performs the desired action (checking the file size in this example),","breadcrumbs":"Signatures » YARA Rules » Using YARA rules in ClamAV","id":"123","title":"Using YARA rules in ClamAV"},"124":{"body":"Table of Contents Phishing Signatures Database file format PDB format GDB format WDB format Hints Examples of PDB signatures Examples of WDB signatures Example for how the URL extractor works How matching works RealURL, DisplayedURL concatenation What happens when a match is found Extraction of RealURL, DisplayedURL from HTML tags Example Simple patterns Regular expressions Flags Introduction to regular expressions Special characters Character classes Escaping Alternation Optional matching, and repetition Groups How to create database files How to create and maintain the allow list (daily.wdb) How to create and maintain the domain list (daily.pdb) Dealing with false positives, and undetected phishing mails False positives Undetected phish mails","breadcrumbs":"Signatures » Phishing Signatures » Phishing Signatures","id":"124","title":"Phishing Signatures"},"125":{"body":"PDB format This file contains urls/hosts that are target of phishing attempts. It contains lines in the following format: R[Filter]:RealURL:DisplayedURL[:FuncLevelSpec]\nH[Filter]:DisplayedHostname[:FuncLevelSpec] R Regular expression, for the concatenated URL. The last 3 characters of the regular expression cannot regex special characters and much be an exact match. H Matches the DisplayedHostname as a simple pattern (literally, no regular expression). The pattern can match either the full hostname. Or a subdomain of the specified hostname. To avoid false matches in case of subdomain matches, the engine checks that there is a dot(.) or a space( ) before the matched portion. Filter Is ignored for R and H for compatibility reasons. RealURL Is the URL the user is sent to, example: href attribute of an html anchor ( tag). DisplayedURL Is the URL description displayed to the user, where its claimed they are sent, example: contents of an html anchor ( tag). DisplayedHostname Is the hostname portion of the DisplayedURL. FuncLevelSpec An (optional) functionality level, 2 formats are possible: minlevel all engines having functionality level >= minlevel will load this line. minlevel-maxlevel engines with functionality level >= minlevel, and < maxlevel will load this line. GDB format This file contains URL hashes in the following format: S:P:HostPrefix[:FuncLevelSpec]\nS:F:Sha256hash[:FuncLevelSpec]\nS1:P:HostPrefix[:FuncLevelSpec]\nS1:F:Sha256hash[:FuncLevelSpec]\nS2:P:HostPrefix[:FuncLevelSpec]\nS2:F:Sha256hash[:FuncLevelSpec]\nS:W:Sha256hash[:FuncLevelSpec] S: These are hashes for Google Safe Browsing - malware sites, and should not be used for other purposes. S2: These are hashes for Google Safe Browsing - phishing sites, and should not be used for other purposes. S1: Hashes for blocking phishing sites. Virus name: Phishing.URL.Blocked. S:W: Locally allowed hashes. HostPrefix 4-byte prefix of the sha256 hash of the last 2 or 3 components of the hostname. If prefix doesn’t match, no further lookups are performed. Sha256hash sha256 hash of the canonicalized URL, or a sha256 hash of its prefix/suffix according to the Google Safe Browsing “Performing Lookups” rules. There should be a corresponding :P:HostkeyPrefix entry for the hash to be taken into consideration. To see which hash/URL matched, look at the clamscan --debug output, and look for the following strings: Looking up hash, prefix matched, and Hash matched. To ignore .gdb entries, create a local.gdb file, and adding a line S:W:. WDB format This file contains url pairs for links that may look suspicious but are safe and should be allowed. It contains lines in the following format: X:RealURL:DisplayedURL[:FuncLevelSpec]\nM:RealHostname:DisplayedHostname[:FuncLevelSpec] X Regular expression, for the entire URL , not just the hostname. The regular expression is by default anchored to start-of-line and end-of-line, as if you have used ^RegularExpression$ A trailing / is automatically added both to the regex, and the input string to avoid false matches. The regular expression matches the concatenation of the RealURL, a colon(:), and the DisplayedURL as a single string. It doesn’t separately match RealURL and DisplayedURL! The last 3 characters of the regular expression cannot regex special characters and much be an exact match. M Matches hostname, or subdomain of it, see notes for H above. Hints Empty lines are ignored The colons are mandatory Don’t leave extra spaces on the end of a line! If any of the lines don’t conform to this format, ClamAV will abort with a Malformed Database Error See section Extraction-of-RealURL for more details on RealURL/DisplayedURL Examples of PDB signatures To check for phishing mails that target amazon.com, or subdomains of amazon.com: H:amazon.com To do the same, but for amazon.co.uk: H:amazon.co.uk You can limit the signatures to certain engine versions . For example... Restrict so that engine versions 20 through 30 can load it, but not 31+: H:amazon.co.uk:20-30 Restrict so that engine versions >= 20 can load it: H:amazon.co.uk:20- Restrict so that engine versions <= 20 can load it: H:amazon.co.uk:0-20 In a real situation, you’d probably use the second form. A situation like that would be if you are using a feature of the signatures not available in earlier versions, or if earlier versions have bugs with your signature. Its neither case here, the above examples are for illustrative purposes only. Examples of WDB signatures To allow Amazon’s country specific domains and amazon.com, to mix domain names in DisplayedURL, and RealURL: X:.+\\.amazon\\.(at|ca|co\\.uk|co\\.jp|de|fr)([/?].*)?:.+\\.amazon\\.com([/?].*)?:17- Explanation of this signature: X: this is a regular expression :17- load signature only for engines with functionality level >= 17 The regular expression is the following (X:, :17- stripped, and a / appended) .+\\.amazon\\.(at|ca|co\\.uk|co\\.jp|de|fr)([/?].*)?:.+\\.amazon\\.com([/?].*)?/ Explanation of this regular expression (note that it is a single regular expression, and not 2 regular expressions splitted at the :). .+ any subdomain of \\.amazon\\. domain we are allowing (RealURL part) (at|ca|co\\.uk|co\\.jp|de|fr) country-domains: at, ca, co.uk, co.jp, de, fr ([/?].*)? recomended way to end the real-url, this protects against embedded URLs (evilurl.example.com/amazon.co.uk/) : RealURL and DisplayedURL are concatenated via a :, so match a literal : here .+ any subdomain of \\.amazon\\.com allowed DisplayedURL ([/?].*)? recommended way to end displayed url part, to protect against embedded URLs / automatically added to further protect against embedded URLs When you add an entry, make sure you check that both domains are owned by the same entity. This signature allows links claiming to point to amazon.com (DisplayedURL), when in fact they really go to a country-specific domain of amazon (RealURL). Example for how the URL extractor works Consider the following HTML file: \n 1.displayedurl.example.com\n\n 2 di

splayedurl.example.com\n\n 3.nested.example.com 4.displayedurl.example.com \n\n

sometext 5.form.nested.link-displayedurl.example.com \n\n 6.displ ayedurl.example.com\n\n