Skip to content

Commit

Permalink
[INTERNAL] Ensure no RegExp is created from a project's namespace
Browse files Browse the repository at this point in the history
Since we don't validate namespaces yet, they may contain regular
expression metacharacters which can lead to unexpected behaviors when
using them in a dynamically created RegExp instance.
  • Loading branch information
RandomByte committed Jul 1, 2021
1 parent 2093562 commit 4cf6bd5
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 12 deletions.
9 changes: 6 additions & 3 deletions lib/builder/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ module.exports = {
const buildLogger = log.createTaskLogger("🛠 ", projectCount(tree));

function buildProject(project) {
const projectBasePath = `/resources/${project.metadata.namespace}`;
let depPromise;
let projectTasks = selectedTasks;

Expand Down Expand Up @@ -316,7 +317,7 @@ module.exports = {
virtualReaders: projectWriters,
getVirtualBasePathPrefix: function({project, virBasePath}) {
if (project.type === "application" && project.metadata.namespace) {
return "/resources/" + project.metadata.namespace;
return projectBasePath;
}
},
getProjectExcludes: function(project) {
Expand Down Expand Up @@ -382,8 +383,10 @@ module.exports = {
if (projectContext.isRootProject() && project.type === "application" &&
project.metadata.namespace) {
// Root-application projects only: Remove namespace prefix if given
resource.setPath(resource.getPath().replace(
new RegExp(`^/resources/${project.metadata.namespace}`), ""));
const resourcePath = resource.getPath();
if (resourcePath.startsWith(projectBasePath)) {
resource.setPath(resourcePath.replace(projectBasePath, ""));
}
}
return fsTarget.write(resource);
}));
Expand Down
9 changes: 4 additions & 5 deletions lib/processors/bundlers/manifestBundler.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,14 @@ module.exports = ({resources, options: {namespace, bundleName, propertiesExtensi
return archiveContent;
}).then((archiveContent) => new Promise((resolve) => {
const zip = new yazl.ZipFile();
const rBasePath = new RegExp(`^/resources/${namespace}/`);
const basePath = `/resources/${namespace}/`;
archiveContent.forEach((content, path) => {
if (!rBasePath.test(path)) {
log.verbose(`Not bundling resource with path ${path} since it is not based on path ` +
`/resources/${namespace}/`);
if (!path.startsWith(basePath)) {
log.verbose(`Not bundling resource with path ${path} since it is not based on path ${basePath}`);
return;
}
// Remove base path. Absolute paths are not allowed in ZIP files
const normalizedPath = path.replace(rBasePath, "");
const normalizedPath = path.replace(basePath, "");
zip.addBuffer(content, normalizedPath);
});
zip.end();
Expand Down
16 changes: 12 additions & 4 deletions lib/tasks/generateCachebusterInfo.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const resourceFactory = require("@ui5/fs").resourceFactory;
const crypto = require("crypto");
const resourceFactory = require("@ui5/fs").resourceFactory;
const log = require("@ui5/logger").getLogger("builder:tasks:generateCachebusterInfo");

async function signByTime(resource) {
return resource.getStatInfo().mtime.getTime();
Expand Down Expand Up @@ -41,15 +42,22 @@ function getSigner(type) {
* @returns {Promise<undefined>} Promise resolving with <code>undefined</code> once data has been written
*/
module.exports = function({workspace, dependencies, options: {namespace, signatureType}}) {
const basePath = `/resources/${namespace}/`;
return workspace.byGlob(`/resources/${namespace}/**/*`)
.then(async (resources) => {
const cachebusterInfo = {};
const regex = new RegExp(`^/resources/${namespace}/`);
const signer = getSigner(signatureType);

await Promise.all(resources.map(async (resource) => {
const normalizedPath = resource.getPath().replace(regex, "");
cachebusterInfo[normalizedPath] = await signer(resource);
let resourcePath = resource.getPath();
if (!resourcePath.startsWith(basePath)) {
log.verbose(
`Ignoring resource with path ${resourcePath} since it is not based on path ${basePath}`);
return;
}
// Remove base path. Absolute paths are not allowed in cachebuster info
resourcePath = resourcePath.replace(basePath, "");
cachebusterInfo[resourcePath] = await signer(resource);
}));
const cachebusterInfoResource = resourceFactory.createResource({
path: `/resources/${namespace}/sap-ui-cachebuster-info.json`,
Expand Down

0 comments on commit 4cf6bd5

Please sign in to comment.