Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-48522

Classpath exception in Email Extension Plugin preSend script.

    • Icon: Bug Bug
    • Resolution: Not A Defect
    • Icon: Major Major
    • None
    • Jenkins 2.89.1
      Email Extension Plugin 2.61
      Script Security Plugin 1.38
      Groovy Plugin 2.0

      I had defined a working email extension in one of my templates that fired off emails in certain cases.  This extension defined the following groovy pre-send script:

      def getReplacementIndicatorFile(subproject) {
           if (subproject == null) {
                build.getWorkspace().child('build').child('replaceInfo.txt')
           } else {
                build.getWorkspace().child(subproject).child('build').child('replaceInfo.txt')

                }
      }
      if (this.hasProperty("RAWSUB")) {
          def replaceInfoFile = getReplacementIndicatorFile("${RAWSUB}")  
          logger.println("replaceInfoFile=${replaceInfoFile}")
          if (replaceInfoFile.exists()) {
              msg.text = replaceInfoFile.readToString()
          } else {
              cancel = true

              }
      } else {
         cancel = true
      }

       

      This script now fails with the following error:

       

      Pre-send script tried to access secured objects: classpath entry file:/ is a class directory, which are not allowed.
      ERROR: Could not send email as a part of the post-build publishers.
      org.jenkinsci.plugins.scriptsecurity.scripts.UnapprovedClasspathException: classpath entry file:/ is a class directory, which are not allowed.
              at org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval.using(ScriptApproval.java:570)
              at hudson.plugins.emailext.ExtendedEmailPublisher.expandClasspath(ExtendedEmailPublisher.java:678)
              at hudson.plugins.emailext.ExtendedEmailPublisher.executeScript(ExtendedEmailPublisher.java:620)
              at hudson.plugins.emailext.ExtendedEmailPublisher.executePresendScript(ExtendedEmailPublisher.java:573)
              at hudson.plugins.emailext.ExtendedEmailPublisher.sendMail(ExtendedEmailPublisher.java:456)
              at hudson.plugins.emailext.ExtendedEmailPublisher._perform(ExtendedEmailPublisher.java:441)
              at hudson.plugins.emailext.ExtendedEmailPublisher.perform(ExtendedEmailPublisher.java:349)
              at hudson.tasks.BuildStepMonitor$1.perform(BuildStepMonitor.java:20)
              at hudson.model.AbstractBuild$AbstractBuildExecution.perform(AbstractBuild.java:744)
              at hudson.model.AbstractBuild$AbstractBuildExecution.performAllBuildSteps(AbstractBuild.java:690)
              at hudson.model.Build$BuildExecution.cleanUp(Build.java:196)
              at hudson.model.Run.execute(Run.java:1771)
              at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:43)
              at hudson.model.ResourceController.execute(ResourceController.java:97)
              at hudson.model.Executor.run(Executor.java:421)
      Finished: SUCCESS

       

      Since my script says nothing about the classpath, I wonder what I would need to do to set up a classpath for my script that does not produce this error.  Why is file:/ the default classpath?

      Also, nothing is available in the script approval page that I can approve to get around this.

      This issue was not present prior to the upgrade to the above versions.

       

          [JENKINS-48522] Classpath exception in Email Extension Plugin preSend script.

          Steve Cohen added a comment -

          Also the "Additional Groovy Classpath" help page is missing.  If this contained information that would help me understand the problem, it's not available to me.

           

          Steve Cohen added a comment - Also the "Additional Groovy Classpath" help page is missing.  If this contained information that would help me understand the problem, it's not available to me.  

          Steve Cohen added a comment -

          Rolling Script Security Plugin back to v1.26 "solves" the problem.

           

          Steve Cohen added a comment - Rolling Script Security Plugin back to v1.26 "solves" the problem.  

          Steve Cohen added a comment -

          Actually, I can't just roll back Script Security to 1.26.  Doing that UNINSTALLS Email Ext.  I have roll back Script Security to 1.26 AND Email Ext to 2.57.

          Steve Cohen added a comment - Actually, I can't just roll back Script Security to 1.26.  Doing that UNINSTALLS Email Ext.  I have roll back Script Security to 1.26 AND Email Ext to 2.57.

          Steve Cohen added a comment - - edited

          I need to know what it is in my script that induces Jenkins to assume that a file:/ URL is present and needs to be added to my classpath.  Nothing I've done in my script explicitly does anything of the kind.  If working with File objects in the script, as my script does, is the problem, then user needs to be told so explicitly in documentation.  Documentation on what is and what is not allowed in a preSend script of this plugin is astonishingly thin (iow, no such documentation exists). Or maybe that is not the issue at all.

          But relying on such poor diagnostic messages without documentation is unacceptable.  As is having a perfectly valid script now fail due to an upgrade.

          Steve Cohen added a comment - - edited I need to know what it is in my script that induces Jenkins to assume that a file:/ URL is present and needs to be added to my classpath.  Nothing I've done in my script explicitly does anything of the kind.  If working with File objects in the script, as my script does, is the problem, then user needs to be told so explicitly in documentation.  Documentation on what is and what is not allowed in a preSend script of this plugin is astonishingly thin (iow, no such documentation exists). Or maybe that is not the issue at all. But relying on such poor diagnostic messages without documentation is unacceptable.  As is having a perfectly valid script now fail due to an upgrade.

          Steve Cohen added a comment -

          Something happened and now, for the first time, my script is offered up for approval on the Script Approvals page.  I approve it, but this still makes no difference and the script is rejected with the same stack trace.

          I have been looking at the code, and it doesn't appear likely that the problem is with my script's access to File objects.  It's something else, a more fundamental problem.  Aside from turning off security globally, there appears to be nothing I can do to get this script to work with the new plugins.  If these upgrades for the purpose of improving security drive users with their hair on fire to remove all security, is this not self-defeating?

           

          Steve Cohen added a comment - Something happened and now, for the first time, my script is offered up for approval on the Script Approvals page.  I approve it, but this still makes no difference and the script is rejected with the same stack trace. I have been looking at the code, and it doesn't appear likely that the problem is with my script's access to File objects.  It's something else, a more fundamental problem.  Aside from turning off security globally, there appears to be nothing I can do to get this script to work with the new plugins.  If these upgrades for the purpose of improving security drive users with their hair on fire to remove all security, is this not self-defeating?  

          Andrew Bayer added a comment -

          Feels like this is something weird in email-ext, perhaps. Not sure who the maintainer is these days.

          Andrew Bayer added a comment - Feels like this is something weird in email-ext, perhaps. Not sure who the maintainer is these days.

          Steve Cohen added a comment -

          Andrew, I assume then that you are the Script Security Plugin guy, am I right?  The issue was originally assigned to https://issues.jenkins-ci.org/secure/ViewProfile.jspa?name=davidvanlaatum who I am assuming is, or was, the Email Ext guy.  He is listed as the maintainer on the plugin page.  Can you and he discuss this?

          Steve Cohen added a comment - Andrew, I assume then that you are the Script Security Plugin guy, am I right?  The issue was originally assigned to https://issues.jenkins-ci.org/secure/ViewProfile.jspa?name=davidvanlaatum who I am assuming is, or was, the Email Ext guy.  He is listed as the maintainer on the plugin page.  Can you and he discuss this?

          Andrew Bayer added a comment -

          Well, this definitely looks to be email-ext - it seems to be deciding that file:/ is one of the classpath entries, and Script Security is, correctly, looking at that funny and rejecting it. I'm reassigning this to davidvanlaatum

          Andrew Bayer added a comment - Well, this definitely looks to be email-ext - it seems to be deciding that file:/ is one of the classpath entries, and Script Security is, correctly, looking at that funny and rejecting it. I'm reassigning this to davidvanlaatum

          Steve Cohen added a comment -

          Your diagnosis seems reasonable to me.

          Steve Cohen added a comment - Your diagnosis seems reasonable to me.

          Andrew Bayer added a comment -

          fwiw, looks like a GroovyScriptPath instance is where that URL is coming from. That seems to have been initialized here. My rough guess is that "/" is getting passed into new GroovyScriptPath(String) from the ext_mailer_default_classpath field in the global configuration of email-ext, which results in GroovyScriptPath#asUrl() returning new File("/").toURI().toURL().

          So...check your global Jenkins configuration to see if there's something weird in the default classpath setting there.

          Andrew Bayer added a comment - fwiw, looks like a GroovyScriptPath instance is where that URL is coming from. That seems to have been initialized here . My rough guess is that "/" is getting passed into new GroovyScriptPath(String) from the ext_mailer_default_classpath field in the global configuration of email-ext, which results in GroovyScriptPath#asUrl() returning new File("/").toURI().toURL() . So...check your global Jenkins configuration to see if there's something weird in the default classpath setting there.

          Steve Cohen added a comment -

          Thanks, Andrew, for digging further.  Where in global settings is the default classpath set?  I don't find such a setting. I only find a place to add Additional Classpath entries.

          Steve Cohen added a comment - Thanks, Andrew, for digging further.  Where in global settings is the default classpath set?  I don't find such a setting. I only find a place to add Additional Classpath entries.

          Steve Cohen added a comment -

          ~/jenkins/data$ cat hudson.plugins.emailext.ExtendedEmailPublisher.xml
          <?xml version='1.0' encoding='UTF-8'?>
          <hudson.plugins.emailext.ExtendedEmailPublisherDescriptor plugin="email-ext@2.57">
            <useSsl>false</useSsl>
            <charset>UTF-8</charset>
            <defaultContentType>text/plain</defaultContentType>
            <defaultSubject>$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!</defaultSubject>
            <defaultBody>$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS:

          Check console output at $BUILD_URL to view the results.</defaultBody>
            <defaultPresendScript></defaultPresendScript>
            <defaultPostsendScript></defaultPostsendScript>
            <defaultClasspath/>

           

          That's my default classpath, nothing there.  Looks like email ext is assuming this means '/'?

          Steve Cohen added a comment - ~/jenkins/data$ cat hudson.plugins.emailext.ExtendedEmailPublisher.xml <?xml version='1.0' encoding='UTF-8'?> <hudson.plugins.emailext.ExtendedEmailPublisherDescriptor plugin="email-ext@2.57">   <useSsl>false</useSsl>   <charset>UTF-8</charset>   <defaultContentType>text/plain</defaultContentType>   <defaultSubject>$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!</defaultSubject>   <defaultBody>$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS: Check console output at $BUILD_URL to view the results.</defaultBody>   <defaultPresendScript></defaultPresendScript>   <defaultPostsendScript></defaultPostsendScript>   <defaultClasspath/>   That's my default classpath, nothing there.  Looks like email ext is assuming this means '/'?

          Steve Cohen added a comment -

          As a further experiment, I changed the script to read just this:

          cancel = true

          It failed with exactly the same stack trace.  I think this rules out anything wrong with my script.  Somehow the email ext plugin is getting a bum default classpath from somewhere.  The question is where?  My previous comment indicates that my "default classpath" is, as far as I can tell, blank.  Where can I find out what this value is?

          Looking at the System Information page is see that System property java.class.path is the absolute pathname of jenkins.war.  Looks perfectly standard to me.

          Steve Cohen added a comment - As a further experiment, I changed the script to read just this: cancel = true It failed with exactly the same stack trace.  I think this rules out anything wrong with my script.  Somehow the email ext plugin is getting a bum default classpath from somewhere.  The question is where?  My previous comment indicates that my "default classpath" is, as far as I can tell, blank.  Where can I find out what this value is? Looking at the System Information page is see that System property java.class.path is the absolute pathname of jenkins.war.  Looks perfectly standard to me.

          defaultClasspath is it and yes that is empty.

          its labeled Additional groovy classpath in global settings

          David van Laatum added a comment - defaultClasspath is it and yes that is empty. its labeled Additional groovy classpath in global settings

          I just attempted to reproduce and was unable to 

          David van Laatum added a comment - I just attempted to reproduce and was unable to 

          Steve Cohen added a comment -

          My Jenkins instance is launched with this command on Red Hat Enterprise Linux Server release 6.9 (Santiago)

          java -DHUDSON_HOME=/vtone/3rd-party/jenkins/jenkins/data -jar /vtone/3rd-party/jenkins/jenkins/bin/jenkins.war --logfile=/vtone/3rd-party/jenkins/jenkins/logs/jenkins.log --httpPort=14354 --prefix=/jenkins --handlerCountMax=500 --handlerCountMaxIdle=100

          The significant bits as far as this issue is concerned are that it was launched with a java -jar command with the jar being jenkins.war.  Jenkins.war is a symbolic link pointing a specific version of Jenkins.war:

          $ ll /vtone/3rd-party/jenkins/jenkins/bin/jenkins.war
          lrwxrwxrwx 1 jenkins jenkins 18 Dec 12 11:27 /vtone/3rd-party/jenkins/jenkins/bin/jenkins.war -> jenkins-2.89.1.war

          java is

          $ java -version
          java version "1.8.0_131"
          Java(TM) SE Runtime Environment (build 1.8.0_131-b33)
          Java HotSpot(TM) 64-Bit Server VM (build 25.131-b33, mixed mode)

          Perhaps you can find some differences with your setup that might explain your inability to replicate?

           

          Steve Cohen added a comment - My Jenkins instance is launched with this command on Red Hat Enterprise Linux Server release 6.9 (Santiago) java -DHUDSON_HOME=/vtone/3rd-party/jenkins/jenkins/data -jar /vtone/3rd-party/jenkins/jenkins/bin/jenkins.war --logfile=/vtone/3rd-party/jenkins/jenkins/logs/jenkins.log --httpPort=14354 --prefix=/jenkins --handlerCountMax=500 --handlerCountMaxIdle=100 The significant bits as far as this issue is concerned are that it was launched with a java -jar command with the jar being jenkins.war.  Jenkins.war is a symbolic link pointing a specific version of Jenkins.war: $ ll /vtone/3rd-party/jenkins/jenkins/bin/jenkins.war lrwxrwxrwx 1 jenkins jenkins 18 Dec 12 11:27 /vtone/3rd-party/jenkins/jenkins/bin/jenkins.war -> jenkins-2.89.1.war java is $ java -version java version "1.8.0_131" Java(TM) SE Runtime Environment (build 1.8.0_131-b33) Java HotSpot(TM) 64-Bit Server VM (build 25.131-b33, mixed mode) Perhaps you can find some differences with your setup that might explain your inability to replicate?  

          Steve Cohen added a comment -

          My log is littered with entries like this.  They seem to occur whenever I modify the template that holds the script that is failing, which thereby causes Jenkins to rewrite all the job configs that use the template:

          Dec 14, 2017 9:06:41 PM org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval configuring
          WARNING: Classpath file:/ is a class directory, which are not allowed. Ignored in configuration, use will be rejected

           

          Steve Cohen added a comment - My log is littered with entries like this.  They seem to occur whenever I modify the template that holds the script that is failing, which thereby causes Jenkins to rewrite all the job configs that use the template: Dec 14, 2017 9:06:41 PM org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval configuring WARNING: Classpath file:/ is a class directory, which are not allowed. Ignored in configuration, use will be rejected  

          Steve Cohen added a comment -

          At the point of actually running the job that produces the error, the log has:

          Dec 14, 2017 8:58:13 PM hudson.model.Run execute
          INFO: Platdev/Projects/jenkinsutil #722 main build action completed: SUCCESS
          Dec 14, 2017 8:58:14 PM org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval using
          WARNING: Classpath file:/ (550a0a3d3d812f9cf4cbf15b4a8afd4d9a56b0bd) is a class directory, which are not allowed.
          Dec 14, 2017 8:58:14 PM hudson.plugins.emailext.ExtendedEmailPublisher sendMail
          WARNING: Could not send email.

          followed by the stack trace shown above.

          Clearly something is munging the classpath from its legitimate value
          /vtone/3rd-party/jenkins/jenkins/bin/jenkins.war
          to

          file:/

          probably in the URL conversion process but I can't quite see where.

          I can't help but think that the fact that this file is actually a symbolic link might somehow be involved.

           

           

          Steve Cohen added a comment - At the point of actually running the job that produces the error, the log has: Dec 14, 2017 8:58:13 PM hudson.model.Run execute INFO: Platdev/Projects/jenkinsutil #722 main build action completed: SUCCESS Dec 14, 2017 8:58:14 PM org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval using WARNING: Classpath file:/ (550a0a3d3d812f9cf4cbf15b4a8afd4d9a56b0bd) is a class directory, which are not allowed. Dec 14, 2017 8:58:14 PM hudson.plugins.emailext.ExtendedEmailPublisher sendMail WARNING: Could not send email. followed by the stack trace shown above. Clearly something is munging the classpath from its legitimate value /vtone/3rd-party/jenkins/jenkins/bin/jenkins.war to file:/ probably in the URL conversion process but I can't quite see where. I can't help but think that the fact that this file is actually a symbolic link might somehow be involved.    

          Daniel Beck added a comment -

          Please provide the global Jenkins config.xml directly in JENKINS_HOME (after purging secrets), and the job's config.xml.

          Daniel Beck added a comment - Please provide the global Jenkins config.xml directly in JENKINS_HOME (after purging secrets), and the job's config.xml.

          Steve Cohen added a comment -

          There's no config.xml in JENKINS_HOME. I presume you mean the top level one in JENKINS_HOME/data. I am sending that along with the one that contains the email-ext stuff. This is a template used by many jobs.

          Steve Cohen added a comment - There's no config.xml in JENKINS_HOME. I presume you mean the top level one in JENKINS_HOME/data. I am sending that along with the one that contains the email-ext stuff. This is a template used by many jobs.

          Daniel Beck added a comment -

          As the screen shot shows, you configure one 'additional class path' entry, and it's the empty string.

          Don't do that.

          Click the red 'X' to remove it.

          Daniel Beck added a comment - As the screen shot shows, you configure one 'additional class path' entry, and it's the empty string. Don't do that. Click the red 'X' to remove it.

          Daniel Beck added a comment -

          User error

          Daniel Beck added a comment - User error

          Steve Cohen added a comment -

          Thank you, Daniel!
          Your find was the answer.
          That is not particularly obvious from the screen shot. It looks as though there are no entries in the additional classpath, rather than one empty entry. I suppose it would be an improvement to the plugin to treat an empty entry not as something to be added to the classpath. I might never have clicked the add button, had the question mark link been working, which I mentioned above. Looking at github it looks like that issue is a link looking for the helpfile in the wrong location.

          Anyway, thank you for your help.

          Steve Cohen added a comment - Thank you, Daniel! Your find was the answer. That is not particularly obvious from the screen shot. It looks as though there are no entries in the additional classpath, rather than one empty entry. I suppose it would be an improvement to the plugin to treat an empty entry not as something to be added to the classpath. I might never have clicked the add button, had the question mark link been working, which I mentioned above. Looking at github it looks like that issue is a link looking for the helpfile in the wrong location. Anyway, thank you for your help.

          Steve Cohen added a comment - - edited

          While it's true that adding a blank entry is a "User Error", it's also true that an empty string is not a valid file system path.  It would be better to refuse such entries than to convert them to "file:/" since the error message in no way points to the root cause.  If the current working directory is wanted on the classpath (don't see why it ever would be wanted in Jenkins), this should be indicated by

          '.'.

          Suggested improvement:
          ExtendedEmailPublisher.transformToClasspathEntries()

              private void transformToClasspathEntries(List<GroovyScriptPath> input, ExtendedEmailPublisherContext context, List<ClasspathEntry> output) {
                  for (GroovyScriptPath path : input) {
                      if (path.getPath().trim().isEmpty() {
                          continue;
                      } 
                      ...
                  }
              }
          

          An alternative improvement would be for the UI to simply not save empty classpath entries.

          Steve Cohen added a comment - - edited While it's true that adding a blank entry is a "User Error", it's also true that an empty string is not a valid file system path.  It would be better to refuse such entries than to convert them to "file:/" since the error message in no way points to the root cause.  If the current working directory is wanted on the classpath (don't see why it ever would be wanted in Jenkins), this should be indicated by '.'. Suggested improvement: ExtendedEmailPublisher.transformToClasspathEntries() private void transformToClasspathEntries(List<GroovyScriptPath> input, ExtendedEmailPublisherContext context, List<ClasspathEntry> output) { for (GroovyScriptPath path : input) { if (path.getPath().trim().isEmpty() { continue; } ... } } An alternative improvement would be for the UI to simply not save empty classpath entries.

            davidvanlaatum David van Laatum
            sc1478 Steve Cohen
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: