Bail out if a configuration file starts with a byte order mark
[exim.git] / src / src / malware.c
index b67f31598e5a1b17b93a4313af47520585f48b0c..d7be4edbb599b51a76351db980d35d0cb669c50a 100644 (file)
@@ -13,7 +13,7 @@
 #ifdef WITH_CONTENT_SCAN
 
 typedef enum {M_FPROTD, M_DRWEB, M_AVES, M_FSEC, M_KAVD, M_CMDL,
-               M_SOPHIE, M_CLAMD, M_SOCK, M_MKSD, M_AVAST} scanner_t;
+               M_SOPHIE, M_CLAMD, M_SOCK, M_MKSD, M_AVAST, M_FPROT6D} scanner_t;
 typedef enum {MC_NONE, MC_TCP, MC_UNIX, MC_STRM} contype_t;
 static struct scan
 {
@@ -34,6 +34,7 @@ static struct scan
   { M_SOCK,    US"sock",       US"/tmp/malware.sock",                MC_STRM },
   { M_MKSD,    US"mksd",       NULL,                                 MC_NONE },
   { M_AVAST,   US"avast",      US"/var/run/avast/scan.sock",         MC_STRM },
+  { M_FPROT6D, US"f-prot6d",   US"localhost 10200",                  MC_TCP },
   { -1,                NULL,           NULL, MC_NONE }         /* end-marker */
 };
 
@@ -84,6 +85,11 @@ static const uschar * ava_re_virus_str = US "(?!\\\\)\\t\\[L\\]\\d\\.\\d\\t\\d\\
 static const pcre * ava_re_clean = NULL;
 static const pcre * ava_re_virus = NULL;
 
+static const uschar * fprot6d_re_error_str = US "^\\d+\\s<(.+?)>$";
+static const uschar * fprot6d_re_virus_str = US "^\\d+\\s<infected:\\s+(.+?)>\\s+.+$";
+static const pcre * fprot6d_re_error = NULL;
+static const pcre * fprot6d_re_virus = NULL;
+
 
 
 /******************************************************************************/
@@ -108,7 +114,7 @@ BOOL malware_ok = FALSE;
 the scan directory normally for that case, but look into rigging up the
 needed header variables if not already set on the command-line? */
 extern int spool_mbox_ok;
-extern uschar spooled_message_id[17];
+extern uschar spooled_message_id[MESSAGE_ID_LENGTH+1];
 
 
 
@@ -196,7 +202,11 @@ const pcre * cre = NULL;
 if (!(list_ele = string_nextinlist(list, sep, NULL, 0)))
   *errstr = US listerr;
 else
+  {
+  DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "RE: ",
+    string_printing(list_ele));
   cre = m_pcre_compile(CUS list_ele, errstr);
+  }
 return cre;
 }
 
@@ -231,13 +241,13 @@ while ((rcv = read(fd, p, 1)) > 0)
   }
 if (!ok)
   {
-  DEBUG(D_acl) debug_printf("Malware scan: read %s (%s)\n",
+  DEBUG(D_acl) debug_printf_indent("Malware scan: read %s (%s)\n",
                rcv==0 ? "EOF" : "error", strerror(errno));
   return rcv==0 ? -1 : -2;
   }
 *p = '\0';
 
-DEBUG(D_acl) debug_printf("Malware scan: read '%s'\n", buffer);
+DEBUG(D_acl) debug_printf_indent("Malware scan: read '%s'\n", buffer);
 return p - buffer;
 }
 
@@ -407,16 +417,16 @@ is via malware(), or there's malware_in_file() used for testing/debugging.
 
 Arguments:
   malware_re    match condition for "malware="
-  eml_filename  the file holding the email to be scanned
+  scan_filename  the file holding the email to be scanned, if we're faking
+               this up for the -bmalware test, else NULL
   timeout      if nonzero, non-default timeoutl
-  faking        whether or not we're faking this up for the -bmalware test
 
 Returns:        Exim message processing code (OK, FAIL, DEFER, ...)
                 where true means malware was found (condition applies)
 */
 static int
-malware_internal(const uschar * malware_re, const uschar * eml_filename,
-  int timeout, BOOL faking)
+malware_internal(const uschar * malware_re, const uschar * scan_filename,
+  int timeout)
 {
 int sep = 0;
 const uschar *av_scanner_work = av_scanner;
@@ -429,21 +439,24 @@ struct scan * scanent;
 const uschar * scanner_options;
 int sock = -1;
 time_t tmo;
+uschar * eml_filename, * eml_dir;
+
+if (!malware_re)
+  return FAIL;         /* empty means "don't match anything" */
 
-/* make sure the eml mbox file is spooled up */
-if (!(mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL)))
+/* Ensure the eml mbox file is spooled up */
+
+if (!(mbox_file = spool_mbox(&mbox_size, scan_filename, &eml_filename)))
   return malware_errlog_defer(US"error while creating mbox spool file");
 
-/* none of our current scanners need the mbox
-   file as a stream, so we can close it right away */
-(void)fclose(mbox_file);
+/* None of our current scanners need the mbox file as a stream (they use
+the name), so we can close it right away.  Get the directory too. */
 
-if (!malware_re)
-  return FAIL;         /* empty means "don't match anything" */
+(void) fclose(mbox_file);
+eml_dir = string_copyn(eml_filename, Ustrrchr(eml_filename, '/') - eml_filename);
 
 /* parse 1st option */
-  if ( (strcmpic(malware_re, US"false") == 0) ||
-     (Ustrcmp(malware_re,"0") == 0) )
+if (strcmpic(malware_re, US"false") == 0  ||  Ustrcmp(malware_re,"0") == 0)
   return FAIL;         /* explicitly no matching */
 
 /* special cases (match anything except empty) */
@@ -463,9 +476,6 @@ if (  strcmpic(malware_re,US"true") == 0
 else if (!(re = m_pcre_compile(malware_re, &errstr)))
   return malware_errlog_defer(errstr);
 
-/* Reset sep that is set by previous string_nextinlist() call */
-sep = 0;
-
 /* if av_scanner starts with a dollar, expand it first */
 if (*av_scanner == '$')
   {
@@ -475,7 +485,7 @@ if (*av_scanner == '$')
         expand_string_message));
 
   DEBUG(D_acl)
-    debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
+    debug_printf_indent("Expanded av_scanner global: %s\n", av_scanner_work);
   /* disable result caching in this case */
   malware_name = NULL;
   malware_ok = FALSE;
@@ -497,14 +507,19 @@ if (!malware_ok)
        scanner_name));
     if (strcmpic(scanner_name, US scanent->name) != 0)
       continue;
+    DEBUG(D_acl) debug_printf_indent("Malware scan:  %s tmo=%s\n",
+      scanner_name, readconf_printtime(timeout));
+
     if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
       scanner_options = scanent->options_default;
     if (scanent->conn == MC_NONE)
       break;
+
+    DEBUG(D_acl) debug_printf_indent("%15s%10s%s\n", "", "socket: ", scanner_options);
     switch(scanent->conn)
     {
-    case MC_TCP:  sock = ip_tcpsocket(scanner_options, &errstr, 5);      break;
-    case MC_UNIX: sock = ip_unixsocket(scanner_options, &errstr);        break;
+    case MC_TCP:  sock = ip_tcpsocket(scanner_options, &errstr, 5);    break;
+    case MC_UNIX: sock = ip_unixsocket(scanner_options, &errstr);      break;
     case MC_STRM: sock = ip_streamsocket(scanner_options, &errstr, 5);  break;
     default: /* compiler quietening */ break;
     }
@@ -512,7 +527,6 @@ if (!malware_ok)
       return m_errlog_defer(scanent, CUS callout_address, errstr);
     break;
   }
-  DEBUG(D_acl) debug_printf("Malware scan: %s tmo %s\n", scanner_name, readconf_printtime(timeout));
 
   switch (scanent->scancode)
     {
@@ -535,7 +549,7 @@ if (!malware_ok)
        par_count++;
        }
       scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest);
-      DEBUG(D_acl) debug_printf("Malware scan: issuing %s: %s\n",
+      DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
        scanner_name, scanrequest);
 
       /* send scan request */
@@ -596,7 +610,8 @@ if (!malware_ok)
 
        if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1)
          {
-         int err = errno;
+         int err;
+badseek:  err = errno;
          (void)close(drweb_fd);
          return m_errlog_defer_3(scanent, NULL,
            string_sprintf("can't seek spool file %s: %s",
@@ -613,9 +628,10 @@ if (!malware_ok)
            sock);
          }
        drweb_slen = htonl(fsize);
-       lseek(drweb_fd, 0, SEEK_SET);
+       if (lseek(drweb_fd, 0, SEEK_SET) < 0)
+         goto badseek;
 
-       DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s]\n",
+       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s remote scan [%s]\n",
            scanner_name, scanner_options);
 
        /* send scan request */
@@ -630,7 +646,7 @@ if (!malware_ok)
            sock);
          }
 
-       if (!(drweb_fbuf = (uschar *) malloc (fsize_uint)))
+       if (!(drweb_fbuf = US malloc(fsize_uint)))
          {
          (void)close(drweb_fd);
          return m_errlog_defer_3(scanent, NULL,
@@ -664,7 +680,7 @@ if (!malware_ok)
        {
        drweb_slen = htonl(Ustrlen(eml_filename));
 
-       DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n",
+       DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s local scan [%s]\n",
            scanner_name, scanner_options);
 
        /* send scan request */
@@ -782,7 +798,7 @@ if (!malware_ok)
                                                eml_filename);
 
       /* and send it */
-      DEBUG(D_acl) debug_printf("Malware scan: issuing %s %s\n",
+      DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s %s\n",
        scanner_name, buf);
       if (m_sock_send(sock, buf, Ustrlen(buf), &errstr) < 0)
        return m_errlog_defer(scanent, CUS callout_address, errstr);
@@ -842,7 +858,7 @@ if (!malware_ok)
 
       malware_name = NULL;
 
-      DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
+      DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
          scanner_name, scanner_options);
       /* pass options */
       memset(av_buffer, 0, sizeof(av_buffer));
@@ -938,7 +954,7 @@ if (!malware_ok)
       if (p)
        *p = '\0';
 
-      DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
+      DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
          scanner_name, scanner_options);
 
       /* send scan request */
@@ -966,7 +982,7 @@ if (!malware_ok)
                US"reported 'kavdaemon damaged' (code 7).", sock);
       }
 
-      /* code 8 is not handled, since it is ambigous. It appears mostly on
+      /* code 8 is not handled, since it is ambiguous. It appears mostly on
       bounces where part of a file has been cut off */
 
       /* "virus found" return codes (2-4) */
@@ -1003,7 +1019,9 @@ if (!malware_ok)
              kav_re = kav_re_inf;
              }
 
-           /* read report, linewise */
+           /* read report, linewise.  Using size from stream to read amount of data
+           from same stream is safe enough. */
+           /* coverity[tainted_data] */
            while (kav_reportlen > 0)
              {
              if ((bread = recv_line(sock, tmpbuf, sizeof(tmpbuf), tmo)) < 0)
@@ -1067,7 +1085,7 @@ if (!malware_ok)
       /* redirect STDERR too */
       commandline = string_sprintf("%s 2>&1", commandline);
 
-      DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
+      DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
              scanner_name, commandline);
 
       /* store exims signal handlers */
@@ -1083,8 +1101,7 @@ if (!malware_ok)
        }
       scanner_fd = fileno(scanner_out);
 
-      file_name = string_sprintf("%s/scan/%s/%s_scanner_output",
-                               spool_directory, message_id, message_id);
+      file_name = string_sprintf("%s/%s_scanner_output", eml_dir, message_id);
 
       if (!(scanner_record = modefopen(file_name, "wb", SPOOL_MODE)))
        {
@@ -1170,7 +1187,7 @@ if (!malware_ok)
       if ((p = Ustrrchr(file_name, '/')))
        *p = '\0';
 
-      DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
+      DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan [%s]\n",
          scanner_name, scanner_options);
 
       if (  write(sock, file_name, Ustrlen(file_name)) < 0
@@ -1344,7 +1361,7 @@ if (!malware_ok)
          int i = random_number( num_servers );
          clamd_address * cd = cv[i];
 
-         DEBUG(D_acl) debug_printf("trying server name %s, port %u\n",
+         DEBUG(D_acl) debug_printf_indent("trying server name %s, port %u\n",
                         cd->hostspec, cd->tcp_port);
 
          /* Lookup the host. This is to ensure that we connect to the same IP
@@ -1400,7 +1417,7 @@ if (!malware_ok)
         * that port on a second connection; then in the scan-method-neutral
         * part, read the response back on the original connection. */
 
-       DEBUG(D_acl) debug_printf(
+       DEBUG(D_acl) debug_printf_indent(
            "Malware scan: issuing %s old-style remote scan (PORT)\n",
            scanner_name);
 
@@ -1442,7 +1459,7 @@ if (!malware_ok)
        chunks, <n> a 4-byte number (network order), terminated by a zero-length
        chunk. */
 
-       DEBUG(D_acl) debug_printf(
+       DEBUG(D_acl) debug_printf_indent(
            "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
            scanner_name);
 
@@ -1468,7 +1485,8 @@ if (!malware_ok)
          }
        if ((fsize = lseek(clam_fd, 0, SEEK_END)) < 0)
          {
-         int err = errno;
+         int err;
+b_seek:   err = errno;
          CLOSE_SOCKDATA; (void)close(clam_fd);
          return m_errlog_defer_3(scanent, NULL,
            string_sprintf("can't seek spool file %s: %s",
@@ -1484,9 +1502,10 @@ if (!malware_ok)
              eml_filename),
            sock);
          }
-       lseek(clam_fd, 0, SEEK_SET);
+       if (lseek(clam_fd, 0, SEEK_SET) < 0)
+         goto b_seek;
 
-       if (!(clamav_fbuf = (uschar *) malloc (fsize_uint)))
+       if (!(clamav_fbuf = US malloc(fsize_uint)))
          {
          CLOSE_SOCKDATA; (void)close(clam_fd);
          return m_errlog_defer_3(scanent, NULL,
@@ -1553,7 +1572,7 @@ if (!malware_ok)
        /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
        file_name = string_sprintf("SCAN %s\n", eml_filename);
 
-       DEBUG(D_acl) debug_printf(
+       DEBUG(D_acl) debug_printf_indent(
            "Malware scan: issuing %s local-path scan [%s]\n",
            scanner_name, scanner_options);
 
@@ -1614,7 +1633,7 @@ if (!malware_ok)
       p = av_buffer + Ustrlen(av_buffer) - 1;
       if (*p == '\n') *p = '\0';
 
-      DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer);
+      DEBUG(D_acl) debug_printf_indent("Malware response: %s\n", av_buffer);
 
       while (isspace(*--p) && (p > av_buffer))
        *p = '\0';
@@ -1651,7 +1670,7 @@ if (!malware_ok)
            *p = '\0';
          }
        malware_name = string_copy(vname);
-       DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name);
+       DEBUG(D_acl) debug_printf_indent("Malware found, name \"%s\"\n", malware_name);
 
        }
       else if (Ustrcmp(result_tag, "ERROR") == 0)
@@ -1662,7 +1681,7 @@ if (!malware_ok)
        {
        /* Everything should be OK */
        malware_name = NULL;
-       DEBUG(D_acl) debug_printf("Malware not found\n");
+       DEBUG(D_acl) debug_printf_indent("Malware not found\n");
 
        }
       else
@@ -1687,8 +1706,10 @@ if (!malware_ok)
       const pcre *sockline_name_re;
 
       /* find scanner command line */
-      if ((sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
-                                         NULL, 0)))
+      if (  (sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
+                                         NULL, 0))
+        && *sockline_scanner
+        )
       {        /* check for no expansions apart from one %s */
        uschar * s = Ustrchr(sockline_scanner, '%');
        if (s++)
@@ -1698,6 +1719,8 @@ if (!malware_ok)
       }
       else
        sockline_scanner = sockline_scanner_default;
+      DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "cmdline: ",
+       string_printing(sockline_scanner));
 
       /* find scanner output trigger */
       sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
@@ -1712,9 +1735,9 @@ if (!malware_ok)
        return m_errlog_defer_3(scanent, NULL, errstr, sock);
 
       /* prepare scanner call - security depends on expansions check above */
-      commandline = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
-      commandline = string_sprintf( CS sockline_scanner, CS commandline);
-
+      commandline = string_sprintf( CS sockline_scanner, CS eml_filename);
+      DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "expanded: ",
+       string_printing(commandline));
 
       /* Pass the command string to the socket */
       if (m_sock_send(sock, commandline, Ustrlen(commandline), &errstr) < 0)
@@ -1733,12 +1756,16 @@ if (!malware_ok)
                US"buffer too small", sock);
       av_buffer[bread] = '\0';
       linebuffer = string_copy(av_buffer);
+      DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "answer: ",
+       string_printing(linebuffer));
 
       /* try trigger match */
       if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1))
        {
        if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
          malware_name = US "unknown";
+       DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "name: ",
+         string_printing(malware_name));
        }
       else /* no virus found */
        malware_name = NULL;
@@ -1768,7 +1795,7 @@ if (!malware_ok)
 
       malware_name = NULL;
 
-      DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name);
+      DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s scan\n", scanner_name);
 
       if ((retval = mksd_scan_packed(scanent, sock, eml_filename, tmo)) != OK)
        {
@@ -1810,7 +1837,7 @@ if (!malware_ok)
        int slen = Ustrlen(buf);
        if (slen >= 1)
          {
-         DEBUG(D_acl) debug_printf("got from avast: %s\n", buf);
+         DEBUG(D_acl) debug_printf_indent("got from avast: %s\n", buf);
          switch (avast_stage)
            {
            case AVA_HELO:
@@ -1836,8 +1863,7 @@ if (!malware_ok)
                }
              else
                {
-               scanrequest = string_sprintf("SCAN %s/scan/%s\n",
-                   spool_directory, message_id);
+               scanrequest = string_sprintf("SCAN %s\n", eml_dir);
                avast_stage = AVA_RSP;          /* just sent command */
                }
 
@@ -1909,8 +1935,53 @@ if (!malware_ok)
                          sock);
        default:        break;
        }
+      break;
       }
+
+    case M_FPROT6D: /* "f-prot6d" scanner type ----------------------------------- */
+      {
+      int bread;
+      uschar * e;
+      uschar * linebuffer;
+      uschar * scanrequest;
+      uschar av_buffer[1024];
+
+      if ((!fprot6d_re_virus && !(fprot6d_re_virus = m_pcre_compile(fprot6d_re_virus_str, &errstr)))
+        || (!fprot6d_re_error && !(fprot6d_re_error = m_pcre_compile(fprot6d_re_error_str, &errstr))))
+        return malware_errlog_defer(errstr);
+
+      scanrequest = string_sprintf("SCAN FILE %s\n", eml_filename);
+      DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n",
+        scanner_name, scanrequest);
+
+      if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0)
+        return m_errlog_defer(scanent, CUS callout_address, errstr);
+
+      bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL));
+
+      if (bread <= 0)
+        return m_errlog_defer_3(scanent, CUS callout_address,
+          string_sprintf("unable to read from socket (%s)", strerror(errno)),
+          sock);
+
+      if (bread == sizeof(av_buffer))
+        return m_errlog_defer_3(scanent, CUS callout_address,
+          US"buffer too small", sock);
+
+      av_buffer[bread] = '\0';
+      linebuffer = string_copy(av_buffer);
+
+      m_sock_send(sock, US"QUIT\n", 5, 0);
+
+      if ((e = m_pcre_exec(fprot6d_re_error, linebuffer)))
+        return m_errlog_defer_3(scanent, CUS callout_address,
+          string_sprintf("scanner reported error (%s)", e), sock);
+
+      if (!(malware_name = m_pcre_exec(fprot6d_re_virus, linebuffer)))
+        malware_name = NULL;
+
       break;
+      }  /* f-prot6d */
   }    /* scanner type switch */
 
   if (sock >= 0)
@@ -1921,7 +1992,7 @@ if (!malware_ok)
 /* match virus name against pattern (caseless ------->----------v) */
 if (malware_name && regex_match_and_setup(re, malware_name, 0, -1))
   {
-  DEBUG(D_acl) debug_printf(
+  DEBUG(D_acl) debug_printf_indent(
       "Matched regex to malware [%s] [%s]\n", malware_re, malware_name);
   return OK;
   }
@@ -1947,15 +2018,10 @@ Returns:      Exim message processing code (OK, FAIL, DEFER, ...)
 int
 malware(const uschar * malware_re, int timeout)
 {
-  uschar * scan_filename;
-  int ret;
+int ret = malware_internal(malware_re, NULL, timeout);
 
-  scan_filename = string_sprintf("%s/scan/%s/%s.eml",
-                   spool_directory, message_id, message_id);
-  ret = malware_internal(malware_re, scan_filename, timeout, FALSE);
-  if (ret == DEFER) av_failed = TRUE;
-
-  return ret;
+if (ret == DEFER) av_failed = TRUE;
+return ret;
 }
 
 
@@ -1977,32 +2043,35 @@ Returns:        Exim message processing code (OK, FAIL, DEFER, ...)
 int
 malware_in_file(uschar *eml_filename)
 {
-  uschar message_id_buf[64];
-  int ret;
-
-  /* spool_mbox() assumes various parameters exist, when creating
-  the relevant directory and the email within */
-  (void) string_format(message_id_buf, sizeof(message_id_buf),
-      "dummy-%d", vaguely_random_number(INT_MAX));
-  message_id = message_id_buf;
-  sender_address = US"malware-sender@example.net";
-  return_path = US"";
-  recipients_list = NULL;
-  receive_add_recipient(US"malware-victim@example.net", -1);
-  enable_dollar_recipients = TRUE;
-
-  ret = malware_internal(US"*", eml_filename, 0,  TRUE);
-
-  Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
-  spool_mbox_ok = 1;
-  /* don't set no_mbox_unspool; at present, there's no way for it to become
-  set, but if that changes, then it should apply to these tests too */
-  unspool_mbox();
-
-  /* silence static analysis tools */
-  message_id = NULL;
-
-  return ret;
+uschar message_id_buf[64];
+int ret;
+
+/* spool_mbox() assumes various parameters exist, when creating
+the relevant directory and the email within */
+
+(void) string_format(message_id_buf, sizeof(message_id_buf),
+    "dummy-%d", vaguely_random_number(INT_MAX));
+message_id = message_id_buf;
+sender_address = US"malware-sender@example.net";
+return_path = US"";
+recipients_list = NULL;
+receive_add_recipient(US"malware-victim@example.net", -1);
+enable_dollar_recipients = TRUE;
+
+ret = malware_internal(US"*", eml_filename, 0);
+
+Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
+spool_mbox_ok = 1;
+
+/* don't set no_mbox_unspool; at present, there's no way for it to become
+set, but if that changes, then it should apply to these tests too */
+
+unspool_mbox();
+
+/* silence static analysis tools */
+message_id = NULL;
+
+return ret;
 }
 
 
@@ -2023,6 +2092,10 @@ if (!ava_re_clean)
   ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE);
 if (!ava_re_virus)
   ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE);
+if (!fprot6d_re_error)
+  fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, FALSE, TRUE);
+if (!fprot6d_re_virus)
+  fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, FALSE, TRUE);
 }
 
 #endif /*WITH_CONTENT_SCAN*/