scaffolder-backend: switch nunjucks runner to use secure templater

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2021-11-19 18:32:49 +01:00
parent 0e016783b6
commit 7b5430fc82
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ScmIntegrations } from '@backstage/integration';
import {
TaskContext,
@@ -23,7 +24,6 @@ import {
WorkflowRunner,
} from './types';
import * as winston from 'winston';
import nunjucks from 'nunjucks';
import fs from 'fs-extra';
import path from 'path';
import { JsonObject, JsonValue } from '@backstage/types';
@@ -33,6 +33,7 @@ import { isTruthy } from './helper';
import { validate as validateJsonSchema } from 'jsonschema';
import { parseRepoUrl } from '../actions/builtin/publish/util';
import { TemplateActionRegistry } from '../actions';
import { SecureTemplater } from '../../lib/templating/SecureTemplater';
type NunjucksWorkflowRunnerOptions = {
workingDirectory: string;
@@ -84,36 +85,35 @@ const createStepLogger = ({
};
export class NunjucksWorkflowRunner implements WorkflowRunner {
private readonly nunjucks: nunjucks.Environment;
private readonly nunjucksOptions: nunjucks.ConfigureOptions = {
autoescape: false,
tags: {
variableStart: '${{',
variableEnd: '}}',
},
};
private readonly templater: SecureTemplater;
constructor(private readonly options: NunjucksWorkflowRunnerOptions) {
this.nunjucks = nunjucks.configure(this.nunjucksOptions);
const { integrations } = options;
// TODO(blam): let's work out how we can deprecate these.
// We shouldn't really need to be exposing these now we can deal with
// objects in the params block.
// Maybe we can expose a new RepoUrlPicker with secrets for V3 that provides an object already.
this.nunjucks.addFilter('parseRepoUrl', repoUrl => {
return parseRepoUrl(repoUrl, this.options.integrations);
});
this.nunjucks.addFilter('projectSlug', repoUrl => {
const { owner, repo } = parseRepoUrl(repoUrl, this.options.integrations);
return `${owner}/${repo}`;
this.templater = new SecureTemplater({
// TODO(blam): let's work out how we can deprecate this.
// We shouldn't really need to be exposing these now we can deal with
// objects in the params block.
// Maybe we can expose a new RepoUrlPicker with secrets for V3 that provides an object already.
parseRepoUrl(url: string) {
return parseRepoUrl(url, integrations);
},
});
}
private isSingleTemplateString(input: string) {
const { parser, nodes } = require('nunjucks');
const parsed = parser.parse(input, {}, this.nunjucksOptions);
const parsed = parser.parse(
input,
{},
{
autoescape: false,
tags: {
variableStart: '${{',
variableEnd: '}}',
},
},
);
return (
parsed.children.length === 1 &&
@@ -134,10 +134,7 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
);
// Run the templating
const templated = this.nunjucks.renderString(
wrappedDumped,
context,
);
const templated = this.templater.render(wrappedDumped, context);
// If there's an empty string returned, then it's undefined
if (templated === '') {
@@ -154,7 +151,7 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
}
// Fallback to default behaviour
const templated = this.nunjucks.renderString(value, context);
const templated = this.templater.render(value, context);
if (templated === '') {
return undefined;
@@ -184,6 +181,7 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
await task.emitLog(
`Starting up task with ${task.spec.steps.length} steps`,
);
await this.templater.initializeIfNeeded();
const context: TemplateContext = {
parameters: task.spec.parameters,