Search in sources :

Example 1 with Step

use of org.jenkinsci.plugins.workflow.steps.Step in project workflow-cps-plugin by jenkinsci.

the class DSL method invokeStep.

/**
 * When {@link #invokeMethod(String, Object)} is calling a {@link StepDescriptor}
 */
protected Object invokeStep(StepDescriptor d, Object args) {
    final NamedArgsAndClosure ps = parseArgs(args, d);
    CpsThread thread = CpsThread.current();
    FlowNode an;
    // TODO: generalize the notion of Step taking over the FlowNode creation.
    boolean hack = d instanceof ParallelStep.DescriptorImpl || d instanceof LoadStep.DescriptorImpl;
    if (ps.body == null && !hack) {
        an = new StepAtomNode(exec, d, thread.head.get());
        // TODO: use CPS call stack to obtain the current call site source location. See JENKINS-23013
        thread.head.setNewHead(an);
    } else {
        an = new StepStartNode(exec, d, thread.head.get());
        thread.head.setNewHead(an);
    }
    final CpsStepContext context = new CpsStepContext(d, thread, handle, an, ps.body);
    Step s;
    boolean sync;
    ClassLoader originalLoader = Thread.currentThread().getContextClassLoader();
    try {
        d.checkContextAvailability(context);
        Thread.currentThread().setContextClassLoader(CpsVmExecutorService.ORIGINAL_CONTEXT_CLASS_LOADER.get());
        s = d.newInstance(ps.namedArgs);
        try {
            // No point storing empty arguments, and ParallelStep is a special case where we can't store its closure arguments
            if (ps.namedArgs != null && !(ps.namedArgs.isEmpty()) && isKeepStepArguments() && !(s instanceof ParallelStep)) {
                // Get the environment variables to find ones that might be credentials bindings
                Computer comp = context.get(Computer.class);
                EnvVars allEnv = new EnvVars(context.get(EnvVars.class));
                if (comp != null && allEnv != null) {
                    allEnv.entrySet().removeAll(comp.getEnvironment().entrySet());
                }
                an.addAction(new ArgumentsActionImpl(ps.namedArgs, allEnv));
            }
        } catch (Exception e) {
            // Avoid breaking execution because we can't store some sort of crazy Step argument
            LOGGER.log(Level.WARNING, "Error storing the arguments for step: " + d.getFunctionName(), e);
        }
        // Persist the node - block start and end nodes do their own persistence.
        CpsFlowExecution.maybeAutoPersistNode(an);
        StepExecution e = s.start(context);
        thread.setStep(e);
        sync = e.start();
    } catch (Exception e) {
        if (e instanceof MissingContextVariableException)
            reportMissingContextVariableException(context, (MissingContextVariableException) e);
        context.onFailure(e);
        s = null;
        sync = true;
    } finally {
        Thread.currentThread().setContextClassLoader(originalLoader);
    }
    if (sync) {
        assert context.bodyInvokers.isEmpty() : "If a step claims synchronous completion, it shouldn't invoke body";
        if (context.getOutcome() == null) {
            context.onFailure(new AssertionError("Step " + s + " claimed to have ended synchronously, but didn't set the result via StepContext.onSuccess/onFailure"));
        }
        thread.setStep(null);
        // we just move on accordingly
        if (an instanceof StepStartNode) {
            // no body invoked, so EndNode follows StartNode immediately.
            thread.head.setNewHead(new StepEndNode(exec, (StepStartNode) an, an));
        }
        thread.head.markIfFail(context.getOutcome());
        return context.replay();
    } else {
        // if it's in progress, suspend it until we get invoked later.
        // when it resumes, the CPS caller behaves as if this method returned with the resume value
        Continuable.suspend(new ThreadTaskImpl(context));
        // so the execution will never reach here.
        throw new AssertionError();
    }
}
Also used : StepStartNode(org.jenkinsci.plugins.workflow.cps.nodes.StepStartNode) LoadStep(org.jenkinsci.plugins.workflow.cps.steps.LoadStep) Step(org.jenkinsci.plugins.workflow.steps.Step) LoadStep(org.jenkinsci.plugins.workflow.cps.steps.LoadStep) ParallelStep(org.jenkinsci.plugins.workflow.cps.steps.ParallelStep) StepEndNode(org.jenkinsci.plugins.workflow.cps.nodes.StepEndNode) StepAtomNode(org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode) StepExecution(org.jenkinsci.plugins.workflow.steps.StepExecution) NoStaplerConstructorException(org.kohsuke.stapler.NoStaplerConstructorException) GroovyRuntimeException(groovy.lang.GroovyRuntimeException) IOException(java.io.IOException) MissingContextVariableException(org.jenkinsci.plugins.workflow.steps.MissingContextVariableException) ParallelStep(org.jenkinsci.plugins.workflow.cps.steps.ParallelStep) EnvVars(hudson.EnvVars) ArgumentsActionImpl(org.jenkinsci.plugins.workflow.cps.actions.ArgumentsActionImpl) Computer(hudson.model.Computer) MissingContextVariableException(org.jenkinsci.plugins.workflow.steps.MissingContextVariableException) FlowNode(org.jenkinsci.plugins.workflow.graph.FlowNode)

Example 2 with Step

use of org.jenkinsci.plugins.workflow.steps.Step in project workflow-cps-plugin by jenkinsci.

the class ArgumentsActionImplTest method testRecursiveSanitizationOfContent.

@Test
public void testRecursiveSanitizationOfContent() {
    EnvVars env = new EnvVars();
    String secretUsername = "secretuser";
    // assume secretuser is a bound credential
    env.put("USERVARIABLE", secretUsername);
    Set<String> sensitiveVariables = new HashSet<>();
    sensitiveVariables.add("USERVARIABLE");
    int maxLen = ArgumentsActionImpl.getMaxRetainedLength();
    ArgumentsActionImpl impl = new ArgumentsActionImpl(sensitiveVariables);
    String oversizedString = generateStringOfSize(maxLen + 10);
    // Simplest masking of secret and oversized value
    Assert.assertEquals("${USERVARIABLE}", impl.sanitizeObjectAndRecordMutation(secretUsername, env));
    Assert.assertFalse(impl.isUnmodifiedArguments());
    impl.isUnmodifiedBySanitization = true;
    Assert.assertEquals(ArgumentsAction.NotStoredReason.OVERSIZE_VALUE, impl.sanitizeObjectAndRecordMutation(oversizedString, env));
    Assert.assertFalse(impl.isUnmodifiedArguments());
    impl.isUnmodifiedBySanitization = true;
    // Test explosion of Step & UninstantiatedDescribable objects
    Step mystep = new EchoStep("I have a " + secretUsername);
    Map<String, ?> singleSanitization = (Map<String, Object>) (impl.sanitizeObjectAndRecordMutation(mystep, env));
    Assert.assertEquals(1, singleSanitization.size());
    Assert.assertEquals("I have a ${USERVARIABLE}", singleSanitization.get("message"));
    Assert.assertFalse(impl.isUnmodifiedArguments());
    impl.isUnmodifiedBySanitization = true;
    singleSanitization = ((UninstantiatedDescribable) (impl.sanitizeObjectAndRecordMutation(mystep.getDescriptor().uninstantiate(mystep), env))).getArguments();
    Assert.assertEquals(1, singleSanitization.size());
    Assert.assertEquals("I have a ${USERVARIABLE}", singleSanitization.get("message"));
    Assert.assertFalse(impl.isUnmodifiedArguments());
    impl.isUnmodifiedBySanitization = true;
    // Maps
    HashMap<String, Object> dangerous = new HashMap<>();
    dangerous.put("name", secretUsername);
    Object sanitized = impl.sanitizeMapAndRecordMutation(dangerous, env);
    Assert.assertNotEquals(sanitized, dangerous);
    assertThat(sanitized, instanceOf(Map.class));
    Map<String, Object> sanitizedMap = (Map<String, Object>) sanitized;
    Assert.assertEquals("${USERVARIABLE}", sanitizedMap.get("name"));
    Assert.assertFalse(impl.isUnmodifiedArguments());
    impl.isUnmodifiedBySanitization = true;
    // String is no longer dangerous
    Object identical = impl.sanitizeMapAndRecordMutation(dangerous, new EnvVars());
    Assert.assertEquals(identical, dangerous);
    Assert.assertTrue(impl.isUnmodifiedArguments());
    // Lists
    List unsanitizedList = Arrays.asList("cheese", null, secretUsername);
    Object sanitized2 = impl.sanitizeListAndRecordMutation(unsanitizedList, env);
    assertThat(sanitized2, instanceOf(List.class));
    List sanitizedList = (List) sanitized2;
    Assert.assertEquals(3, sanitizedList.size());
    Assert.assertFalse(impl.isUnmodifiedArguments());
    Assert.assertEquals("${USERVARIABLE}", sanitizedList.get(2));
    impl.isUnmodifiedBySanitization = true;
    Assert.assertEquals(unsanitizedList, impl.sanitizeObjectAndRecordMutation(unsanitizedList, new EnvVars()));
    Assert.assertEquals(unsanitizedList, impl.sanitizeListAndRecordMutation(unsanitizedList, new EnvVars()));
}
Also used : EchoStep(org.jenkinsci.plugins.workflow.steps.EchoStep) HashMap(java.util.HashMap) Step(org.jenkinsci.plugins.workflow.steps.Step) StateMetaStep(org.jenkinsci.plugins.workflow.testMetaStep.StateMetaStep) EchoStep(org.jenkinsci.plugins.workflow.steps.EchoStep) SemaphoreStep(org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep) BindingStep(org.jenkinsci.plugins.credentialsbinding.impl.BindingStep) EnvVars(hudson.EnvVars) List(java.util.List) ArrayList(java.util.ArrayList) Map(java.util.Map) HashMap(java.util.HashMap) HashSet(java.util.HashSet) Test(org.junit.Test)

Example 3 with Step

use of org.jenkinsci.plugins.workflow.steps.Step in project workflow-cps-plugin by jenkinsci.

the class DSL method reportAmbiguousStepInvocation.

private void reportAmbiguousStepInvocation(CpsStepContext context, StepDescriptor d, @Nullable TaskListener listener) {
    if (listener != null) {
        List<String> ambiguousClassNames = StepDescriptor.all().stream().filter(sd -> sd.getFunctionName().equals(d.getFunctionName())).map(sd -> sd.clazz.getName()).collect(Collectors.toList());
        String message = String.format("Warning: Invoking ambiguous Pipeline Step ā€˜%1$sā€™ (%2$s). " + "ā€˜%1$sā€™ could refer to any of the following steps: %3$s. " + "You can invoke steps by class name instead to avoid ambiguity. " + "For example: steps.'%2$s'(...)", d.getFunctionName(), d.clazz.getName(), ambiguousClassNames);
        listener.getLogger().println(message);
        return;
    }
    LOGGER.log(Level.FINE, "Unable to report ambiguous step invocation for: " + d.getFunctionName());
}
Also used : Arrays(java.util.Arrays) StringUtils(org.apache.commons.lang.StringUtils) Step(org.jenkinsci.plugins.workflow.steps.Step) Closure(groovy.lang.Closure) Symbol(org.jenkinsci.Symbol) EnvironmentExpander(org.jenkinsci.plugins.workflow.steps.EnvironmentExpander) ReflectionCache(org.codehaus.groovy.reflection.ReflectionCache) NoStaplerConstructorException(org.kohsuke.stapler.NoStaplerConstructorException) Map(java.util.Map) NonNull(edu.umd.cs.findbugs.annotations.NonNull) GroovyObjectSupport(groovy.lang.GroovyObjectSupport) PersistenceContext(org.jenkinsci.plugins.workflow.cps.persistence.PersistenceContext) Restricted(org.kohsuke.accmod.Restricted) GroovyRuntimeException(groovy.lang.GroovyRuntimeException) Jenkins(jenkins.model.Jenkins) Set(java.util.Set) Logger(java.util.logging.Logger) Collectors(java.util.stream.Collectors) LoadStep(org.jenkinsci.plugins.workflow.cps.steps.LoadStep) ParallelStep(org.jenkinsci.plugins.workflow.cps.steps.ParallelStep) ClassDescriptor(org.kohsuke.stapler.ClassDescriptor) Run(hudson.model.Run) Serializable(java.io.Serializable) GString(groovy.lang.GString) CachedClass(org.codehaus.groovy.reflection.CachedClass) DescribableModel(org.jenkinsci.plugins.structs.describable.DescribableModel) StepStartNode(org.jenkinsci.plugins.workflow.cps.nodes.StepStartNode) List(java.util.List) Continuable(com.cloudbees.groovy.cps.Continuable) Annotation(java.lang.annotation.Annotation) Entry(java.util.Map.Entry) DescribableParameter(org.jenkinsci.plugins.structs.describable.DescribableParameter) SuppressFBWarnings(edu.umd.cs.findbugs.annotations.SuppressFBWarnings) FlowNode(org.jenkinsci.plugins.workflow.graph.FlowNode) FlowExecutionOwner(org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner) InterpolatedSecretsAction(org.jenkinsci.plugins.workflow.cps.view.InterpolatedSecretsAction) StepListener(org.jenkinsci.plugins.workflow.flow.StepListener) TreeSet(java.util.TreeSet) Computer(hudson.model.Computer) ArrayList(java.util.ArrayList) Level(java.util.logging.Level) GroovyObject(groovy.lang.GroovyObject) HashSet(java.util.HashSet) LinkedHashMap(java.util.LinkedHashMap) Queue(hudson.model.Queue) ArgumentsActionImpl(org.jenkinsci.plugins.workflow.cps.actions.ArgumentsActionImpl) UninstantiatedDescribable(org.jenkinsci.plugins.structs.describable.UninstantiatedDescribable) EnvVars(hudson.EnvVars) BodyExecutionCallback(org.jenkinsci.plugins.workflow.steps.BodyExecutionCallback) Index(org.jvnet.hudson.annotation_indexer.Index) Util(hudson.Util) PersistIn(org.jenkinsci.plugins.workflow.cps.persistence.PersistIn) AbortException(hudson.AbortException) TaskListener(hudson.model.TaskListener) SymbolLookup(org.jenkinsci.plugins.structs.SymbolLookup) Descriptor(hudson.model.Descriptor) StepExecution(org.jenkinsci.plugins.workflow.steps.StepExecution) NoExternalUse(org.kohsuke.accmod.restrictions.NoExternalUse) IOException(java.io.IOException) StepContext(org.jenkinsci.plugins.workflow.steps.StepContext) Outcome(com.cloudbees.groovy.cps.Outcome) TreeMap(java.util.TreeMap) Nullable(edu.umd.cs.findbugs.annotations.Nullable) Describable(hudson.model.Describable) StepDescriptor(org.jenkinsci.plugins.workflow.steps.StepDescriptor) StepAtomNode(org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode) StepEndNode(org.jenkinsci.plugins.workflow.cps.nodes.StepEndNode) CheckForNull(edu.umd.cs.findbugs.annotations.CheckForNull) Collections(java.util.Collections) ExtensionList(hudson.ExtensionList) ThreadTaskResult(org.jenkinsci.plugins.workflow.cps.ThreadTaskResult) GString(groovy.lang.GString)

Example 4 with Step

use of org.jenkinsci.plugins.workflow.steps.Step in project workflow-cps-plugin by jenkinsci.

the class DSL method invokeStep.

/**
 * When {@link #invokeMethod(String, Object)} is calling a {@link StepDescriptor}
 * @param d The {@link StepDescriptor} being invoked.
 * @param name The name used to invoke the step. May be {@link StepDescriptor#getFunctionName}, a symbol as in {@link StepDescriptor#metaStepsOf}, or {@code d.clazz.getName()}.
 * @param args The arguments passed to the step.
 */
protected Object invokeStep(StepDescriptor d, String name, Object args) {
    final NamedArgsAndClosure ps = parseArgs(args, d);
    CpsThread thread = CpsThread.current();
    FlowNode an;
    // TODO: generalize the notion of Step taking over the FlowNode creation.
    boolean hack = d instanceof ParallelStep.DescriptorImpl || d instanceof LoadStep.DescriptorImpl;
    if (ps.body == null && !hack) {
        if (!(d.getClass().getName().equals("org.jenkinsci.plugins.workflow.support.steps.StageStep$DescriptorImpl")) && d.takesImplicitBlockArgument()) {
            throw new IllegalStateException(String.format("%s step must be called with a body", name));
        } else {
            an = new StepAtomNode(exec, d, thread.head.get());
        }
    } else {
        an = new StepStartNode(exec, d, thread.head.get());
    }
    final CpsStepContext context = new CpsStepContext(d, thread, handle, an, ps.body);
    EnvVars allEnv = null;
    Set<String> sensitiveVariables = Collections.emptySet();
    try {
        allEnv = context.get(EnvVars.class);
        EnvironmentExpander envExpander = context.get(EnvironmentExpander.class);
        if (envExpander != null) {
            sensitiveVariables = new HashSet<>(envExpander.getSensitiveVariables());
        }
    } catch (IOException | InterruptedException e) {
        LOGGER.log(Level.WARNING, "Unable to retrieve environment variables", e);
    }
    // Ensure ArgumentsAction is attached before we notify even synchronous listeners:
    ArgumentsActionImpl argumentsAction = null;
    try {
        // No point storing empty arguments, and ParallelStep is a special case where we can't store its closure arguments
        if (ps.namedArgs != null && !(ps.namedArgs.isEmpty()) && isKeepStepArguments() && !(d instanceof ParallelStep.DescriptorImpl)) {
            // Get the environment variables to find ones that might be credentials bindings
            Computer comp = context.get(Computer.class);
            if (comp != null && allEnv != null) {
                allEnv.entrySet().removeAll(comp.getEnvironment().entrySet());
            }
            argumentsAction = new ArgumentsActionImpl(ps.namedArgs, allEnv, sensitiveVariables);
            an.addAction(argumentsAction);
        }
    } catch (Exception e) {
        // Avoid breaking execution because we can't store some sort of crazy Step argument
        LOGGER.log(Level.WARNING, "Error storing the arguments for step: " + d.getFunctionName(), e);
    }
    // TODO: use CPS call stack to obtain the current call site source location. See JENKINS-23013
    thread.head.setNewHead(an);
    Step s;
    boolean sync;
    ClassLoader originalLoader = Thread.currentThread().getContextClassLoader();
    try {
        TaskListener listener = context.get(TaskListener.class);
        logInterpolationWarnings(name, argumentsAction, ps.interpolatedStrings, allEnv, sensitiveVariables, listener);
        if (unreportedAmbiguousFunctions.remove(name)) {
            reportAmbiguousStepInvocation(context, d, listener);
        }
        d.checkContextAvailability(context);
        Thread.currentThread().setContextClassLoader(CpsVmExecutorService.ORIGINAL_CONTEXT_CLASS_LOADER.get());
        if (Util.isOverridden(StepDescriptor.class, d.getClass(), "newInstance", Map.class)) {
            s = d.newInstance(ps.namedArgs);
        } else {
            DescribableModel<? extends Step> stepModel = DescribableModel.of(d.clazz);
            s = stepModel.instantiate(ps.namedArgs, listener);
        }
        // Persist the node - block start and end nodes do their own persistence.
        CpsFlowExecution.maybeAutoPersistNode(an);
        // Call any registered StepListeners.
        for (StepListener sl : ExtensionList.lookup(StepListener.class)) {
            try {
                sl.notifyOfNewStep(s, context);
            } catch (Throwable e) {
                LOGGER.log(Level.WARNING, "failed to notify step listener before starting " + s.getDescriptor().getFunctionName(), e);
            }
        }
        if (!context.isCompleted()) {
            StepExecution e = s.start(context);
            thread.setStep(e);
            sync = e.start();
        } else {
            s = null;
            sync = true;
        }
    } catch (Exception e) {
        context.onFailure(e);
        s = null;
        sync = true;
    } finally {
        Thread.currentThread().setContextClassLoader(originalLoader);
    }
    if (sync) {
        assert context.withBodyInvokers(List::isEmpty) : "If a step claims synchronous completion, it shouldn't invoke body";
        if (context.getOutcome() == null) {
            context.onFailure(new AssertionError("Step " + s + " claimed to have ended synchronously, but didn't set the result via StepContext.onSuccess/onFailure"));
        }
        thread.setStep(null);
        // we just move on accordingly
        if (an instanceof StepStartNode) {
            // no body invoked, so EndNode follows StartNode immediately.
            thread.head.setNewHead(new StepEndNode(exec, (StepStartNode) an, an));
        }
        thread.head.markIfFail(context.getOutcome());
        return context.replay();
    } else {
        // if it's in progress, suspend it until we get invoked later.
        // when it resumes, the CPS caller behaves as if this method returned with the resume value
        Continuable.suspend(name, new ThreadTaskImpl(context));
        // so the execution will never reach here.
        throw new AssertionError();
    }
}
Also used : EnvironmentExpander(org.jenkinsci.plugins.workflow.steps.EnvironmentExpander) GString(groovy.lang.GString) Step(org.jenkinsci.plugins.workflow.steps.Step) LoadStep(org.jenkinsci.plugins.workflow.cps.steps.LoadStep) ParallelStep(org.jenkinsci.plugins.workflow.cps.steps.ParallelStep) StepAtomNode(org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode) StepExecution(org.jenkinsci.plugins.workflow.steps.StepExecution) ArgumentsActionImpl(org.jenkinsci.plugins.workflow.cps.actions.ArgumentsActionImpl) Computer(hudson.model.Computer) TaskListener(hudson.model.TaskListener) List(java.util.List) ArrayList(java.util.ArrayList) ExtensionList(hudson.ExtensionList) StepStartNode(org.jenkinsci.plugins.workflow.cps.nodes.StepStartNode) LoadStep(org.jenkinsci.plugins.workflow.cps.steps.LoadStep) IOException(java.io.IOException) StepEndNode(org.jenkinsci.plugins.workflow.cps.nodes.StepEndNode) NoStaplerConstructorException(org.kohsuke.stapler.NoStaplerConstructorException) GroovyRuntimeException(groovy.lang.GroovyRuntimeException) AbortException(hudson.AbortException) IOException(java.io.IOException) ParallelStep(org.jenkinsci.plugins.workflow.cps.steps.ParallelStep) EnvVars(hudson.EnvVars) StepListener(org.jenkinsci.plugins.workflow.flow.StepListener) FlowNode(org.jenkinsci.plugins.workflow.graph.FlowNode)

Example 5 with Step

use of org.jenkinsci.plugins.workflow.steps.Step in project workflow-cps-plugin by jenkinsci.

the class ArgumentsActionImpl method sanitizeObjectAndRecordMutation.

/**
 * Recursively sanitize a single object by:
 *   - Exploding {@link Step}s and {@link UninstantiatedDescribable}s into their Maps to sanitize
 *   - Removing unsafe strings using {@link #replaceSensitiveVariables(String, EnvVars, Set)} and replace with the variable name
 *   - Removing oversized objects using {@link #isOversized(Object)} and replacing with {@link NotStoredReason#OVERSIZE_VALUE}
 *  While making an effort not to retain needless copies of objects and to re-use originals where possible
 *   (including the Step or UninstantiatedDescribable)
 */
@CheckForNull
@SuppressWarnings("unchecked")
Object sanitizeObjectAndRecordMutation(@CheckForNull Object o, @CheckForNull EnvVars vars) {
    // Package scoped so we can test it directly
    Object tempVal = o;
    DescribableModel m = null;
    if (tempVal instanceof Step) {
        // Ugly but functional used for legacy syntaxes with metasteps
        m = DescribableModel.of(tempVal.getClass());
        tempVal = ((Step) tempVal).getDescriptor().defineArguments((Step) tempVal);
    } else if (tempVal instanceof UninstantiatedDescribable) {
        tempVal = ((UninstantiatedDescribable) tempVal).toMap();
    } else if (tempVal instanceof Describable) {
        // Raw Describables may not be safe to store, so we should explode it
        try {
            m = DescribableModel.of(tempVal.getClass());
            tempVal = m.uninstantiate2(o).toMap();
        } catch (RuntimeException x) {
            // usually NoStaplerConstructorException, but could also be misc. UnsupportedOperationException
            LOGGER.log(Level.FINE, "failed to store " + tempVal, x);
            this.isUnmodifiedBySanitization = false;
            return NotStoredReason.UNSERIALIZABLE;
        }
    }
    if (isOversized(tempVal)) {
        this.isUnmodifiedBySanitization = false;
        return NotStoredReason.OVERSIZE_VALUE;
    }
    if (!isStorableType(tempVal)) {
        // If we're not a legal type to store, then don't.
        this.isUnmodifiedBySanitization = false;
        return NotStoredReason.UNSERIALIZABLE;
    }
    Object modded = tempVal;
    if (modded instanceof Map) {
        // Recursive sanitization, oh my!
        modded = sanitizeMapAndRecordMutation((Map) modded, vars, false);
    } else if (modded instanceof List) {
        modded = sanitizeListAndRecordMutation((List) modded, vars);
    } else if (modded != null && modded.getClass().isArray()) {
        Class componentType = modded.getClass().getComponentType();
        if (!componentType.isPrimitive()) {
            // Object arrays get recursively sanitized
            modded = sanitizeArrayAndRecordMutation((Object[]) modded, vars);
        } else {
            // Primitive arrays aren't a valid type here
            this.isUnmodifiedBySanitization = true;
            return NotStoredReason.UNSERIALIZABLE;
        }
    } else if (modded instanceof String && vars != null && !vars.isEmpty()) {
        String replaced = replaceSensitiveVariables((String) modded, vars, sensitiveVariables);
        if (!replaced.equals(modded)) {
            this.isUnmodifiedBySanitization = false;
        }
        return replaced;
    }
    if (modded != tempVal) {
        // Sanitization stripped out some values, so we need to record that and return modified version
        this.isUnmodifiedBySanitization = false;
        if (o instanceof Describable && !(o instanceof Step)) {
            // Return an UninstantiatedDescribable for the input Describable with masking applied to arguments
            // We're skipping steps because for those we want to return the raw arguments anyway...
            UninstantiatedDescribable rawUd = m.uninstantiate2(o);
            return new UninstantiatedDescribable(rawUd.getSymbol(), rawUd.getKlass(), (Map<String, ?>) modded);
        } else if (o instanceof UninstantiatedDescribable) {
            // Need to retain the symbol.
            UninstantiatedDescribable ud = (UninstantiatedDescribable) o;
            return new UninstantiatedDescribable(ud.getSymbol(), ud.getKlass(), (Map<String, ?>) modded);
        } else {
            return modded;
        }
    } else if (o instanceof Describable && tempVal instanceof Map) {
        // Handle oddball cases where Describable is passed in directly and we need to uninstantiate.
        UninstantiatedDescribable rawUd = m.uninstantiate2(o);
        return rawUd;
    } else {
        // Any mutation was just from exploding step/uninstantiated describable, and we can just use the original
        return o;
    }
}
Also used : UninstantiatedDescribable(org.jenkinsci.plugins.structs.describable.UninstantiatedDescribable) UninstantiatedDescribable(org.jenkinsci.plugins.structs.describable.UninstantiatedDescribable) Describable(hudson.model.Describable) DescribableModel(org.jenkinsci.plugins.structs.describable.DescribableModel) ArrayList(java.util.ArrayList) List(java.util.List) Step(org.jenkinsci.plugins.workflow.steps.Step) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map) CheckForNull(edu.umd.cs.findbugs.annotations.CheckForNull)

Aggregations

Step (org.jenkinsci.plugins.workflow.steps.Step)10 IOException (java.io.IOException)5 List (java.util.List)5 EnvVars (hudson.EnvVars)4 ArrayList (java.util.ArrayList)4 Map (java.util.Map)4 UninstantiatedDescribable (org.jenkinsci.plugins.structs.describable.UninstantiatedDescribable)4 StepDescriptor (org.jenkinsci.plugins.workflow.steps.StepDescriptor)4 NoStaplerConstructorException (org.kohsuke.stapler.NoStaplerConstructorException)4 GroovyRuntimeException (groovy.lang.GroovyRuntimeException)3 ExtensionList (hudson.ExtensionList)3 Computer (hudson.model.Computer)3 Describable (hudson.model.Describable)3 ArgumentsActionImpl (org.jenkinsci.plugins.workflow.cps.actions.ArgumentsActionImpl)3 StepAtomNode (org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode)3 StepEndNode (org.jenkinsci.plugins.workflow.cps.nodes.StepEndNode)3 StepStartNode (org.jenkinsci.plugins.workflow.cps.nodes.StepStartNode)3 LoadStep (org.jenkinsci.plugins.workflow.cps.steps.LoadStep)3 ParallelStep (org.jenkinsci.plugins.workflow.cps.steps.ParallelStep)3 FlowNode (org.jenkinsci.plugins.workflow.graph.FlowNode)3