From f5c3642ae766fcfb4e817e8f5f82ea4d0fd43ec4 Mon Sep 17 00:00:00 2001
From: Rhys Arkins <rhys@keylocation.sg>
Date: Thu, 14 Dec 2017 06:19:24 +0100
Subject: [PATCH] fix: escape all forward slash with %2f (gitlab) (#1294)

Use a regex replace all instead of single replace, for cases where projects or branches have more than one forward slash.

Fixes #1293
---
 lib/platform/gitlab/index.js                  | 48 ++++++++++---------
 .../gitlab/__snapshots__/index.spec.js.snap   | 13 +++++
 test/platform/gitlab/index.spec.js            |  5 ++
 3 files changed, 43 insertions(+), 23 deletions(-)

diff --git a/lib/platform/gitlab/index.js b/lib/platform/gitlab/index.js
index 9df8e9b1ef..29d26faead 100644
--- a/lib/platform/gitlab/index.js
+++ b/lib/platform/gitlab/index.js
@@ -64,6 +64,10 @@ async function getRepos(token, endpoint) {
   }
 }
 
+function urlEscape(str) {
+  return str.replace(/\//g, '%2F');
+}
+
 // Initialize GitLab by getting base branch
 async function initRepo(repoName, token, endpoint) {
   logger.debug(`initRepo(${repoName})`);
@@ -78,7 +82,7 @@ async function initRepo(repoName, token, endpoint) {
   if (endpoint) {
     process.env.GITLAB_ENDPOINT = endpoint;
   }
-  config.repoName = repoName.replace('/', '%2F');
+  config.repoName = urlEscape(repoName);
   config.fileList = null;
   config.prList = null;
   try {
@@ -136,9 +140,9 @@ async function getFileList(branchName = config.baseBranch) {
 async function branchExists(branchName) {
   logger.debug(`Checking if branch exists: ${branchName}`);
   try {
-    const url = `projects/${
-      config.repoName
-    }/repository/branches/${branchName.replace('/', '%2F')}`;
+    const url = `projects/${config.repoName}/repository/branches/${urlEscape(
+      branchName
+    )}`;
     const res = await get(url);
     if (res.statusCode === 200) {
       logger.debug('Branch exists');
@@ -201,9 +205,9 @@ async function getBranchStatus(branchName, requiredStatusChecks) {
     return 'failed';
   }
   // First, get the branch to find the commit SHA
-  let url = `projects/${
-    config.repoName
-  }/repository/branches/${branchName.replace('/', '%2F')}`;
+  let url = `projects/${config.repoName}/repository/branches/${urlEscape(
+    branchName
+  )}`;
   let res = await get(url);
   const branchSha = res.body.commit.id;
   // Now, check the statuses for that commit
@@ -231,9 +235,9 @@ async function getBranchStatus(branchName, requiredStatusChecks) {
 
 async function getBranchStatusCheck(branchName, context) {
   // First, get the branch to find the commit SHA
-  let url = `projects/${
-    config.repoName
-  }/repository/branches/${branchName.replace('/', '%2F')}`;
+  let url = `projects/${config.repoName}/repository/branches/${urlEscape(
+    branchName
+  )}`;
   let res = await get(url);
   const branchSha = res.body.commit.id;
   // Now, check the statuses for that commit
@@ -256,9 +260,9 @@ async function setBranchStatus(
   targetUrl
 ) {
   // First, get the branch to find the commit SHA
-  let url = `projects/${
-    config.repoName
-  }/repository/branches/${branchName.replace('/', '%2F')}`;
+  let url = `projects/${config.repoName}/repository/branches/${urlEscape(
+    branchName
+  )}`;
   const res = await get(url);
   const branchSha = res.body.commit.id;
   // Now, check the statuses for that commit
@@ -276,10 +280,7 @@ async function setBranchStatus(
 
 async function deleteBranch(branchName) {
   await get.delete(
-    `projects/${config.repoName}/repository/branches/${branchName.replace(
-      '/',
-      '%2F'
-    )}`
+    `projects/${config.repoName}/repository/branches/${urlEscape(branchName)}`
   );
 }
 
@@ -290,7 +291,9 @@ function mergeBranch() {
 async function getBranchLastCommitTime(branchName) {
   try {
     const res = await get(
-      `projects/${config.repoName}/repository/commits?ref_name=${branchName}`
+      `projects/${config.repoName}/repository/commits?ref_name=${urlEscape(
+        branchName
+      )}`
     );
     return new Date(res.body[0].committed_date);
   } catch (err) {
@@ -409,7 +412,7 @@ async function getPr(iid) {
   // If not then we don't allow it to be rebased, in case someone's changes would be lost
   const branchUrl = `projects/${
     config.repoName
-  }/repository/branches/${pr.source_branch.replace('/', '%2F')}`;
+  }/repository/branches/${urlEscape(pr.source_branch)}`;
   const branch = (await get(branchUrl)).body;
   if (branch && branch.commit && branch.commit.author_email === config.email) {
     pr.canRebase = true;
@@ -444,10 +447,9 @@ async function mergePr(iid) {
 
 async function getFile(filePath, branchName) {
   try {
-    const url = `projects/${
-      config.repoName
-    }/repository/files/${filePath.replace(/\//g, '%2F')}?ref=${branchName ||
-      config.baseBranch}`;
+    const url = `projects/${config.repoName}/repository/files/${urlEscape(
+      filePath
+    )}?ref=${branchName || config.baseBranch}`;
     const res = await get(url);
     return Buffer.from(res.body.content, 'base64').toString();
   } catch (error) {
diff --git a/test/platform/gitlab/__snapshots__/index.spec.js.snap b/test/platform/gitlab/__snapshots__/index.spec.js.snap
index 7c760a240a..6b2300c701 100644
--- a/test/platform/gitlab/__snapshots__/index.spec.js.snap
+++ b/test/platform/gitlab/__snapshots__/index.spec.js.snap
@@ -204,6 +204,19 @@ Array [
 ]
 `;
 
+exports[`platform/gitlab initRepo should escape all forward slashes in project names 1`] = `
+Array [
+  Array [
+    "projects/some%2Frepo%2Fproject",
+  ],
+  Array [
+    "user",
+  ],
+]
+`;
+
+exports[`platform/gitlab initRepo should escape all forward slashes in project names 2`] = `Object {}`;
+
 exports[`platform/gitlab initRepo should initialise the config for the repo - 0 1`] = `
 Array [
   Array [
diff --git a/test/platform/gitlab/index.spec.js b/test/platform/gitlab/index.spec.js
index a4d4f74d48..ea823b0967 100644
--- a/test/platform/gitlab/index.spec.js
+++ b/test/platform/gitlab/index.spec.js
@@ -100,6 +100,11 @@ describe('platform/gitlab', () => {
         expect(process.env.GITLAB_ENDPOINT).toBe(endpoint);
       });
     });
+    it(`should escape all forward slashes in project names`, async () => {
+      const config = await initRepo('some/repo/project', 'some-token');
+      expect(get.mock.calls).toMatchSnapshot();
+      expect(config).toMatchSnapshot();
+    });
     it('should throw an error if no token is provided', async () => {
       let err;
       try {
-- 
GitLab