Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIX] LibraryFormatter: Fix handling of paths containing special characters #622

Merged
merged 2 commits into from
Jul 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
31 changes: 17 additions & 14 deletions lib/types/library/LibraryFormatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,26 +270,29 @@ class LibraryFormatter extends AbstractUi5Formatter {
}

getNamespaceFromFsPath(fsPath) {
// Transform path to POSIX and remove any trailing slashes
const posixFsPath = fsPath.replace(/\\/g, "/").replace(/\/$/, "");
// Regex to ensure trailing slash
const rOptionalTrailingSlash = /\/?$/;

// Remove base path from fsPath
const posixBasePath = this.getSourceBasePath(true);
// Transform path to POSIX and ensure a trailing slash for correct comparison
const posixFsPath = fsPath.replace(/\\/g, "/").replace(rOptionalTrailingSlash, "/");
const posixBasePath = this.getSourceBasePath(true).replace(rOptionalTrailingSlash, "/");

// Can match /library/src as well as /library/src/some/namespace
const basePathPrefixRegExp = new RegExp(`^${posixBasePath}/?`);
if (posixBasePath === posixFsPath) {
// The given file system path does not contain a namespace path since it is equal to the source base path
// Therefore return an empty namespace
return "";
}

if (!basePathPrefixRegExp.test(posixFsPath)) {
if (posixBasePath === posixFsPath + "/") {
// The given file system path does not contain a namespace path
// It is equal to the source base path
// Therefore return an empty namespace
return "";
}
if (!posixFsPath.startsWith(posixBasePath)) {
throw new Error(`Given file system path ${posixFsPath} is not based on source base ` +
`path ${posixBasePath}.`);
}
const namespacePath = posixFsPath.replace(basePathPrefixRegExp, "");

// Remove base path from fsPath to get the namespace
let namespacePath = posixFsPath.replace(posixBasePath, "");

// Remove any leading and trailing slash
namespacePath = namespacePath.replace(/(?:^\/)|(?:\/$)/g, "");
return namespacePath;
}

Expand Down
18 changes: 16 additions & 2 deletions test/lib/types/library/LibraryFormatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -1454,11 +1454,25 @@ test("getNamespaceFromFsPath: fsPath is not based on base path", async (t) => {

const fsPath = "/some/different/path";
const err = t.throws(() => libraryFormatter.getNamespaceFromFsPath(fsPath));
t.deepEqual(err.message, `Given file system path /some/different/path is not based on source base ` +
`path /some/path.`,
t.deepEqual(err.message, `Given file system path /some/different/path/ is not based on source base ` +
`path /some/path/.`,
"Threw with correct error message");
});

test("getNamespaceFromFsPath: fsPath w/ regex metacharacters", async (t) => {
const myProject = clone(libraryETree);
myProject.resources.pathMappings = {
"/resources/": myProject.resources.configuration.paths.src
};

const libraryFormatter = new LibraryFormatter({project: myProject});
sinon.stub(libraryFormatter, "getSourceBasePath").returns("/some/(path");

const fsPath = "/some/(path/my/namespace";
const res = libraryFormatter.getNamespaceFromFsPath(fsPath);
t.deepEqual(res, "my/namespace", "Returned correct namespace");
});

test.serial("getPreloadExcludesFromDotLibrary: No excludes", async (t) => {
const log = {
verbose: sinon.stub()
Expand Down