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

Surpassing secrets interpolation warning in writeFile

      Straight to example:

      withCredentials([usernamePassword(
          credentialsId: 'PVS-credentials',
          usernameVariable: 'PVS_USER',
          passwordVariable: 'PVS_SERIAL'
      )]) {
          node('meta-job') {
              writeFile(
                  file: 'config.ini',
                  text: "${PVS_USER}\n${PVS_SERIAL}\n${JOB_NAME}-${BUILD_NUMBER}",
              )
          }
      }
      

      This causes:

      17:43:29  Warning: A secret was passed to "writeFile" using Groovy String interpolation, which is insecure.
      17:43:29  		 Affected argument(s) used the following variable(s): [PVS_SERIAL, PVS_USER]
      17:43:29  		 See https://jenkins.io/redirect/groovy-string-interpolation for details.
      

      Yes, writing credentials to files is bad, but unfortunately there are examples of software that operate like this, so... What do? Storing whole files as a secrets is not an option (they may contain additional dynamical data), and I would rather not dug into various templaters like Jinja or groovy's inhouse one.

          [JENKINS-67769] Surpassing secrets interpolation warning in writeFile

          Artalus S. added a comment - - edited

          It does not, but we find concatenation harder to read, especially on a multi-line file.

          We would also rather not resort to echo -e "..." > file , or sed -i ... file , since the substitution should be cross-platform in most cases.

          Artalus S. added a comment - - edited It does not, but we find concatenation harder to read, especially on a multi-line file. We would also rather not resort to  echo -e "..." > file , or sed -i ... file , since the substitution should be cross-platform in most cases.

          The warning was added to the Pipeline: Groovy plugin in PR #370 for JENKINS-63254. It can apparently be configured via the org.jenkinsci.plugins.workflow.cps.DSL.UNSAFE_GROOVY_INTERPOLATION system property. The possible values are "ignore", "warn" (default), and "error". These are documented in the Pipeline: Groovy changelog at version 2.85.

          Kalle Niemitalo added a comment - The warning was added to the Pipeline: Groovy plugin in PR #370 for JENKINS-63254 . It can apparently be configured via the org.jenkinsci.plugins.workflow.cps.DSL.UNSAFE_GROOVY_INTERPOLATION system property. The possible values are "ignore", "warn" (default), and "error". These are documented in the Pipeline: Groovy changelog at version 2.85 .

          It might be useful to edit https://jenkins.io/redirect/groovy-string-interpolation aka https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#string-interpolation to describe these two ways to suppress the warning. But I'm not sure how to explain when users should do that.

          Kalle Niemitalo added a comment - It might be useful to edit https://jenkins.io/redirect/groovy-string-interpolation aka https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#string-interpolation to describe these two ways to suppress the warning. But I'm not sure how to explain when users should do that.

          Does Jenkins save the text argument of writeFile to the console log or to any workflow/*.xml files?

          Kalle Niemitalo added a comment - Does Jenkins save the text argument of writeFile to the console log or to any workflow/*.xml files?

          Artalus S. added a comment -

          Thank you for info about the property! At least we can ignore it like we used to prior to updating, if it becomes too much of a burden for our users Though I'd rather prefer to disable it locally with comments, like CodeNarc linter does:

          // cannot do anything about it, PVS wants credentials in file
          // Disable-Warning UNSAFE_GROOVY_INTERPOLATION
          writeFile ...
          // Enable-Warning UNSAFE_GROOVY_INTERPOLATION

          Artalus S. added a comment - Thank you for info about the property! At least we can ignore it like we used to prior to updating, if it becomes too much of a burden for our users Though I'd rather prefer to disable it locally with comments, like CodeNarc linter does: // cannot do anything about it, PVS wants credentials in file // Disable-Warning UNSAFE_GROOVY_INTERPOLATION writeFile ... // Enable-Warning UNSAFE_GROOVY_INTERPOLATION

          Artalus S. added a comment -

          Does Jenkins save the text argument of writeFile to the console log or to any workflow/*.xml files?

          It does not write anything to console. Not sure about xmls, but can't see any reasons for it to do so.

          Artalus S. added a comment - Does Jenkins save the text argument of writeFile to the console log or to any workflow/*.xml files? It does not write anything to console. Not sure about xmls, but can't see any reasons for it to do so.

          Kalle Niemitalo added a comment - - edited

          From a quick test

          writeFile(file: 'JENKINS-67769 test', text: 'dummy')
          

          wrote workflow/16.xml with this content:

          <?xml version='1.1' encoding='UTF-8'?>
          <Tag plugin="workflow-support@813.vb_d7c3d2984a_0">
            <node class="cps.n.StepAtomNode" plugin="workflow-cps@2648.va9433432b33c">
              <parentIds>
                <string>15</string>
              </parentIds>
              <id>16</id>
              <descriptorId>org.jenkinsci.plugins.workflow.steps.WriteFileStep</descriptorId>
            </node>
            <actions>
              <cps.a.ArgumentsActionImpl plugin="workflow-cps@2648.va9433432b33c">
                <arguments>
                  <entry>
                    <string>file</string>
                    <string>JENKINS-67769 test</string>
                  </entry>
                  <entry>
                    <string>text</string>
                    <string>dummy</string>
                  </entry>
                </arguments>
                <sensitiveVariables/>
                <isUnmodifiedBySanitization>true</isUnmodifiedBySanitization>
              </cps.a.ArgumentsActionImpl>
              <wf.a.TimingAction plugin="workflow-api@1136.v7f5f1759dc16">
                <startTime>1644484564887</startTime>
              </wf.a.TimingAction>
              <s.a.LogStorageAction/>
            </actions>
          </Tag>
          

          So the values of both arguments were saved, even though the "Pipeline Steps" screen and the "Pipeline Console" screen (from Pipeline Graph View) display only the first one. But I did not test whether secrets are masked in this file.

          Kalle Niemitalo added a comment - - edited From a quick test writeFile(file: 'JENKINS-67769 test' , text: 'dummy' ) wrote workflow/16.xml with this content: <?xml version= '1.1' encoding= 'UTF-8' ?> <Tag plugin= "workflow-support@813.vb_d7c3d2984a_0" > <node class= "cps.n.StepAtomNode" plugin= "workflow-cps@2648.va9433432b33c" > <parentIds> <string> 15 </string> </parentIds> <id> 16 </id> <descriptorId> org.jenkinsci.plugins.workflow.steps.WriteFileStep </descriptorId> </node> <actions> <cps.a.ArgumentsActionImpl plugin= "workflow-cps@2648.va9433432b33c" > <arguments> <entry> <string> file </string> <string> JENKINS-67769 test </string> </entry> <entry> <string> text </string> <string> dummy </string> </entry> </arguments> <sensitiveVariables/> <isUnmodifiedBySanitization> true </isUnmodifiedBySanitization> </cps.a.ArgumentsActionImpl> <wf.a.TimingAction plugin= "workflow-api@1136.v7f5f1759dc16" > <startTime> 1644484564887 </startTime> </wf.a.TimingAction> <s.a.LogStorageAction/> </actions> </Tag> So the values of both arguments were saved, even though the "Pipeline Steps" screen and the "Pipeline Console" screen (from Pipeline Graph View ) display only the first one. But I did not test whether secrets are masked in this file.

          Artalus S. added a comment -

          In my case (written via concatenation), the xml is like this:

          <?xml version='1.1' encoding='UTF-8'?>
          <Tag plugin="workflow-support@3.8">
            <node class="cps.n.StepAtomNode" plugin="workflow-cps@2.94">
              <parentIds>
                <string>6</string>
              </parentIds>
              <id>7</id>
              <descriptorId>org.jenkinsci.plugins.workflow.steps.WriteFileStep</descriptorId>
            </node>
            <actions>
              <cps.a.ArgumentsActionImpl plugin="workflow-cps@2.94">
                <arguments>
                  <entry>
                    <string>file</string>
                    <string>config.ini</string>
                  </entry>
                  <entry>
                    <string>text</string>
                    <string>${PVS_USER}
          ${PVS_SERIAL}
          sandbox/tmp-secrets-15</string>
                  </entry>
                </arguments>
                <sensitiveVariables>
                  <string>PVS_SERIAL</string>
                  <string>PVS_USER</string>
                </sensitiveVariables>
                <isUnmodifiedBySanitization>false</isUnmodifiedBySanitization>
              </cps.a.ArgumentsActionImpl>
              <wf.a.TimingAction plugin="workflow-api@2.46">
                <startTime>1644478565578</startTime>
              </wf.a.TimingAction>
              <s.a.LogStorageAction/>
            </actions>

          , so the secrets are masked. Nevertheless, that is... an interesting observation.

          Artalus S. added a comment - In my case (written via concatenation), the xml is like this: <?xml version= '1.1' encoding= 'UTF-8' ?> <Tag plugin= "workflow-support@3.8" > <node class= "cps.n.StepAtomNode" plugin= "workflow-cps@2.94" > <parentIds> <string>6</string> </parentIds> <id>7</id> <descriptorId>org.jenkinsci.plugins.workflow.steps.WriteFileStep</descriptorId> </node> <actions> <cps.a.ArgumentsActionImpl plugin= "workflow-cps@2.94" > <arguments> <entry> <string>file</string> <string>config.ini</string> </entry> <entry> <string>text</string> <string>${PVS_USER} ${PVS_SERIAL} sandbox/tmp-secrets-15</string> </entry> </arguments> <sensitiveVariables> <string>PVS_SERIAL</string> <string>PVS_USER</string> </sensitiveVariables> <isUnmodifiedBySanitization> false </isUnmodifiedBySanitization> </cps.a.ArgumentsActionImpl> <wf.a.TimingAction plugin= "workflow-api@2.46" > <startTime>1644478565578</startTime> </wf.a.TimingAction> <s.a.LogStorageAction/> </actions> , so the secrets are masked. Nevertheless, that is... an interesting observation.

          ArgumentsActionImpl.replaceSensitiveVariables apparently masks the sensitive values from the workflow XML file. Oversized values are replaced with NotStoredReason.OVERSIZE_VALUE rather than truncated, so there is no risk that truncation of an oversized value could cause part of a sensitive variable to be left in.

          Kalle Niemitalo added a comment - ArgumentsActionImpl.replaceSensitiveVariables apparently masks the sensitive values from the workflow XML file. Oversized values are replaced with NotStoredReason.OVERSIZE_VALUE rather than truncated, so there is no risk that truncation of an oversized value could cause part of a sensitive variable to be left in.

          Jesse Glick added a comment -
          sh '(echo $PVS_USER; echo $PVS_SERIAL; echo $JOB_NAME-$BUILD_NUMBER) > config.ini'
          

          Jesse Glick added a comment - sh '(echo $PVS_USER; echo $PVS_SERIAL; echo $JOB_NAME-$BUILD_NUMBER) > config.ini'

            Unassigned Unassigned
            artalus Artalus S.
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: