Skip to content

Commit 78f6752

Browse files
stevezaufyockm
authored andcommitted
Sanitize file and folder names (#92)
* update env var in docs. Might help with #88 * Handle filenames/directories properly fixes #81 * add tests * linting error
1 parent 617c699 commit 78f6752

22 files changed

+294
-24
lines changed

package-lock.json

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"nconf": "^0.8.4",
4040
"node-storage": "0.0.7",
4141
"readline": "^1.3.0",
42+
"sanitize-filename": "^1.6.1",
4243
"superagent": "^3.5.2",
4344
"winston": "^2.3.0",
4445
"xregexp": "^3.1.1",

src/context/directory/handlers/clientGrants.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import path from 'path';
33
import { constants } from 'auth0-source-control-extension-tools';
44

55
import log from '../../../logger';
6-
import { getFiles, existsMustBeDir, loadJSON } from '../../../utils';
6+
import { getFiles, existsMustBeDir, loadJSON, sanitize } from '../../../utils';
77

88
function parse(context) {
99
const grantsFolder = path.join(context.filePath, constants.CLIENTS_GRANTS_DIRECTORY);
@@ -35,7 +35,7 @@ async function dump(context) {
3535
const found = clients.find(c => c.client_id === dumpGrant.client_id);
3636
if (found) dumpGrant.client_id = found.name;
3737

38-
const name = `${dumpGrant.client_id} (${dumpGrant.audience})`.replace(/[/\\?%*:|"<>]/g, '-');
38+
const name = sanitize(`${dumpGrant.client_id} (${dumpGrant.audience})`);
3939
const grantFile = path.join(grantsFolder, `${name}.json`);
4040
log.info(`Writing ${grantFile}`);
4141
fs.writeFileSync(grantFile, JSON.stringify(dumpGrant, null, 2));

src/context/directory/handlers/clients.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import path from 'path';
33
import { constants } from 'auth0-source-control-extension-tools';
44

55
import log from '../../../logger';
6-
import { getFiles, existsMustBeDir, loadJSON } from '../../../utils';
6+
import { getFiles, existsMustBeDir, loadJSON, sanitize } from '../../../utils';
77

88
function parse(context) {
99
const clientsFolder = path.join(context.filePath, constants.CLIENTS_DIRECTORY);
@@ -29,7 +29,7 @@ async function dump(context) {
2929
fs.ensureDirSync(clientsFolder);
3030

3131
clients.forEach((client) => {
32-
const clientFile = path.join(clientsFolder, `${client.name}.json`);
32+
const clientFile = path.join(clientsFolder, sanitize(`${client.name}.json`));
3333
log.info(`Writing ${clientFile}`);
3434
fs.writeFileSync(clientFile, JSON.stringify(client, null, 2));
3535
});

src/context/directory/handlers/connections.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import path from 'path';
33
import { constants } from 'auth0-source-control-extension-tools';
44

55
import log from '../../../logger';
6-
import { getFiles, existsMustBeDir, loadJSON } from '../../../utils';
6+
import { getFiles, existsMustBeDir, loadJSON, sanitize } from '../../../utils';
77

88
function parse(context) {
99
const connectionsFolder = path.join(context.filePath, constants.CONNECTIONS_DIRECTORY);
@@ -41,7 +41,7 @@ async function dump(context) {
4141
]
4242
};
4343

44-
const connectionFile = path.join(connectionsFolder, `${dumpedConnection.name}.json`);
44+
const connectionFile = path.join(connectionsFolder, sanitize(`${dumpedConnection.name}.json`));
4545
log.info(`Writing ${connectionFile}`);
4646
fs.writeFileSync(connectionFile, JSON.stringify(dumpedConnection, null, 2));
4747
});

src/context/directory/handlers/databases.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import fs from 'fs-extra';
33
import { constants, loadFile } from 'auth0-source-control-extension-tools';
44

55
import log from '../../../logger';
6-
import { isDirectory, existsMustBeDir, loadJSON, getFiles } from '../../../utils';
6+
import { isDirectory, existsMustBeDir, loadJSON, getFiles, sanitize } from '../../../utils';
77

88

99
function getDatabase(folder, mappings) {
@@ -72,7 +72,7 @@ async function dump(context) {
7272
fs.ensureDirSync(databasesFolder);
7373

7474
databases.forEach((database) => {
75-
const dbFolder = path.join(databasesFolder, database.name);
75+
const dbFolder = path.join(databasesFolder, sanitize(database.name));
7676
fs.ensureDirSync(dbFolder);
7777

7878
const formatted = {
@@ -90,10 +90,11 @@ async function dump(context) {
9090
...(database.options.customScripts && {
9191
customScripts: Object.entries(database.options.customScripts).reduce((scripts, [ name, script ]) => {
9292
// Dump custom script to file
93-
const scriptFile = path.join(dbFolder, `${name}.js`);
93+
const scriptName = sanitize(`${name}.js`);
94+
const scriptFile = path.join(dbFolder, scriptName);
9495
log.info(`Writing ${scriptFile}`);
9596
fs.writeFileSync(scriptFile, script);
96-
scripts[name] = `./${name}.js`;
97+
scripts[name] = `./${scriptName}`;
9798
return scripts;
9899
}, {})
99100
})

src/context/directory/handlers/resourceServers.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import path from 'path';
33
import { constants } from 'auth0-source-control-extension-tools';
44

55
import log from '../../../logger';
6-
import { getFiles, existsMustBeDir, loadJSON } from '../../../utils';
6+
import { getFiles, existsMustBeDir, loadJSON, sanitize } from '../../../utils';
77

88
function parse(context) {
99
const resourceServersFolder = path.join(context.filePath, constants.RESOURCE_SERVERS_DIRECTORY);
@@ -29,7 +29,7 @@ async function dump(context) {
2929
fs.ensureDirSync(resourceServersFolder);
3030

3131
resourceServers.forEach((resourceServer) => {
32-
const resourceServerFile = path.join(resourceServersFolder, `${resourceServer.name}.json`);
32+
const resourceServerFile = path.join(resourceServersFolder, sanitize(`${resourceServer.name}.json`));
3333
log.info(`Writing ${resourceServerFile}`);
3434
fs.writeFileSync(resourceServerFile, JSON.stringify(resourceServer, null, 2));
3535
});

src/context/directory/handlers/rules.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import path from 'path';
33
import { constants } from 'auth0-source-control-extension-tools';
44

55
import log from '../../../logger';
6-
import { getFiles, existsMustBeDir, loadJSON } from '../../../utils';
6+
import { getFiles, existsMustBeDir, loadJSON, sanitize } from '../../../utils';
77

88

99
function parse(context) {
@@ -36,14 +36,15 @@ async function dump(context) {
3636
fs.ensureDirSync(rulesFolder);
3737
rules.forEach((rule) => {
3838
// Dump script to file
39-
const ruleJS = path.join(rulesFolder, `${rule.name}.js`);
39+
const name = sanitize(rule.name);
40+
const ruleJS = path.join(rulesFolder, `${name}.js`);
4041
log.info(`Writing ${ruleJS}`);
4142
fs.writeFileSync(ruleJS, rule.script);
4243

4344
// Dump template metadata
44-
const ruleFile = path.join(rulesFolder, `${rule.name}.json`);
45+
const ruleFile = path.join(rulesFolder, `${name}.json`);
4546
log.info(`Writing ${ruleFile}`);
46-
fs.writeFileSync(ruleFile, JSON.stringify({ ...rule, script: `./${rule.name}.js` }, null, 2));
47+
fs.writeFileSync(ruleFile, JSON.stringify({ ...rule, script: `./${name}.js` }, null, 2));
4748
});
4849
}
4950

src/context/directory/handlers/rulesConfigs.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import fs from 'fs-extra';
22
import path from 'path';
33
import { constants } from 'auth0-source-control-extension-tools';
44

5-
import { getFiles, existsMustBeDir, loadJSON } from '../../../utils';
5+
import { getFiles, existsMustBeDir, loadJSON, sanitize } from '../../../utils';
66
import log from '../../../logger';
77

88
function parse(context) {
@@ -29,7 +29,7 @@ async function dump(context) {
2929
fs.ensureDirSync(ruleConfigsFolder);
3030

3131
rulesConfigs.forEach((rulesConfig) => {
32-
const ruleConfigFile = path.join(ruleConfigsFolder, `${rulesConfig.key}.json`);
32+
const ruleConfigFile = path.join(ruleConfigsFolder, sanitize(`${rulesConfig.key}.json`));
3333
log.info(`Writing ${ruleConfigFile}`);
3434
fs.writeFileSync(ruleConfigFile, JSON.stringify({
3535
value: '******',

src/context/yaml/handlers/databases.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import fs from 'fs-extra';
22
import path from 'path';
33

4+
import { sanitize } from '../../../utils';
45
import log from '../../../logger';
56

67

@@ -53,14 +54,16 @@ async function dump(context) {
5354
...(database.options.customScripts && {
5455
customScripts: Object.entries(database.options.customScripts).reduce((scripts, [ name, script ]) => {
5556
// Create Database folder
56-
const dbFolder = path.join(context.basePath, 'databases', database.name);
57+
const dbName = sanitize(database.name);
58+
const dbFolder = path.join(context.basePath, 'databases', sanitize(dbName));
5759
fs.ensureDirSync(dbFolder);
5860

5961
// Dump custom script to file
60-
const scriptFile = path.join(dbFolder, `${name}.js`);
62+
const scriptName = sanitize(name);
63+
const scriptFile = path.join(dbFolder, `${scriptName}.js`);
6164
log.info(`Writing ${scriptFile}`);
6265
fs.writeFileSync(scriptFile, script);
63-
scripts[name] = `./databases/${database.name}/${name}.js`;
66+
scripts[name] = `./databases/${dbName}/${scriptName}.js`;
6467
return scripts;
6568
}, {})
6669
})

src/context/yaml/handlers/rules.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import path from 'path';
22
import fs from 'fs-extra';
33

4+
import { sanitize } from '../../../utils';
45
import log from '../../../logger';
56

67
async function parse(context) {
@@ -28,10 +29,11 @@ async function dump(context) {
2829

2930
rules = rules.map((rule) => {
3031
// Dump rule to file
31-
const scriptFile = path.join(rulesFolder, `${rule.name}.js`);
32+
const scriptName = sanitize(`${rule.name}.js`);
33+
const scriptFile = path.join(rulesFolder, scriptName);
3234
log.info(`Writing ${scriptFile}`);
3335
fs.writeFileSync(scriptFile, rule.script);
34-
return { ...rule, script: `./rules/${rule.name}.js` };
36+
return { ...rule, script: `./rules/${scriptName}` };
3537
});
3638
}
3739

src/utils.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import fs from 'fs-extra';
22
import path from 'path';
3+
import sanitizeName from 'sanitize-filename';
34
import { loadFile } from 'auth0-source-control-extension-tools';
45

56
export function isDirectory(f) {
@@ -83,3 +84,8 @@ export function stripIdentifiers(auth0, assets) {
8384

8485
return updated;
8586
}
87+
88+
89+
export function sanitize(str) {
90+
return sanitizeName(str, { replacement: '-' });
91+
}

test/context/directory/clientGrants.test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,18 @@ describe('#directory context clientGrants', () => {
108108
const clientGrantsFolder = path.join(dir, constants.CLIENTS_GRANTS_DIRECTORY);
109109
expect(loadJSON(path.join(clientGrantsFolder, 'My M2M (https---test.myapp.com-api-v1).json'))).to.deep.equal(context.assets.clientGrants[0]);
110110
});
111+
112+
it('should dump client grants sanitized', async () => {
113+
const dir = path.join(testDataDir, 'directory', 'clientGrantsDump');
114+
cleanThenMkdir(dir);
115+
const context = new Context({ AUTH0_INPUT_FILE: dir }, mockMgmtClient());
116+
117+
context.assets.clientGrants = [
118+
{ audience: 'https://test.myapp.com/api/v1', client_id: 'My M2M', scope: [ 'update:account' ] }
119+
];
120+
121+
await handler.dump(context);
122+
const clientGrantsFolder = path.join(dir, constants.CLIENTS_GRANTS_DIRECTORY);
123+
expect(loadJSON(path.join(clientGrantsFolder, 'My M2M (https---test.myapp.com-api-v1).json'))).to.deep.equal(context.assets.clientGrants[0]);
124+
});
111125
});

test/context/directory/clients.test.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,20 @@ describe('#directory context clients', () => {
8585
expect(loadJSON(path.join(clientFolder, 'someClient.json'))).to.deep.equal(context.assets.clients[0]);
8686
expect(loadJSON(path.join(clientFolder, 'someClient2.json'))).to.deep.equal(context.assets.clients[1]);
8787
});
88+
89+
it('should dump clients sanitized', async () => {
90+
const dir = path.join(testDataDir, 'directory', 'clientsDump');
91+
cleanThenMkdir(dir);
92+
const context = new Context({ AUTH0_INPUT_FILE: dir }, mockMgmtClient());
93+
94+
context.assets.clients = [
95+
{ app_type: 'spa', name: 'someClient-test' },
96+
{ app_type: 'spa', name: 'someClient2/aa' }
97+
];
98+
99+
await handler.dump(context);
100+
const clientFolder = path.join(dir, constants.CLIENTS_DIRECTORY);
101+
expect(loadJSON(path.join(clientFolder, 'someClient-test.json'))).to.deep.equal(context.assets.clients[0]);
102+
expect(loadJSON(path.join(clientFolder, 'someClient2-aa.json'))).to.deep.equal(context.assets.clients[1]);
103+
});
88104
});

test/context/directory/connections.test.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,20 @@ describe('#directory context connections', () => {
9191
expect(loadJSON(path.join(clientFolder, 'myad-waad.json'))).to.deep.equal(context.assets.connections[0]);
9292
expect(loadJSON(path.join(clientFolder, 'facebook.json'))).to.deep.equal(context.assets.connections[1]);
9393
});
94+
95+
it('should dump connections sanitized', async () => {
96+
const dir = path.join(testDataDir, 'directory', 'connectionsDump');
97+
cleanThenMkdir(dir);
98+
const context = new Context({ AUTH0_INPUT_FILE: dir }, mockMgmtClient());
99+
100+
context.assets.connections = [
101+
{
102+
name: 'my/ad-waad', strategy: 'waad', var: 'something', enabled_clients: []
103+
}
104+
];
105+
106+
await handler.dump(context);
107+
const clientFolder = path.join(dir, constants.CONNECTIONS_DIRECTORY);
108+
expect(loadJSON(path.join(clientFolder, 'my-ad-waad.json'))).to.deep.equal(context.assets.connections[0]);
109+
});
94110
});

test/context/directory/databases.test.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,4 +250,40 @@ describe('#directory context databases', () => {
250250
expect(fs.readFileSync(path.join(scripsFolder, 'login.js'), 'utf8')).to.deep.equal(scriptValidate);
251251
expect(fs.readFileSync(path.join(scripsFolder, 'verify.js'), 'utf8')).to.deep.equal(scriptValidate);
252252
});
253+
254+
it('should dump custom databases sanitized', async () => {
255+
cleanThenMkdir(dbDumpDir);
256+
const context = new Context({ AUTH0_INPUT_FILE: dbDumpDir }, mockMgmtClient());
257+
258+
const scriptValidate = 'function login() { var env1 = "env2"; }';
259+
context.assets.databases = [
260+
{
261+
name: 'users/test',
262+
enabled_clients: [],
263+
options: {
264+
customScripts: {
265+
change_email: scriptValidate
266+
},
267+
enabledDatabaseCustomization: true
268+
},
269+
strategy: 'auth0'
270+
}
271+
];
272+
273+
await handler.dump(context);
274+
const scripsFolder = path.join(dbDumpDir, constants.DATABASE_CONNECTIONS_DIRECTORY, 'users-test');
275+
expect(loadJSON(path.join(scripsFolder, 'database.json'))).to.deep.equal({
276+
name: 'users/test',
277+
enabled_clients: [],
278+
options: {
279+
customScripts: {
280+
change_email: './change_email.js'
281+
},
282+
enabledDatabaseCustomization: true
283+
},
284+
strategy: 'auth0'
285+
});
286+
287+
expect(fs.readFileSync(path.join(scripsFolder, 'change_email.js'), 'utf8')).to.deep.equal(scriptValidate);
288+
});
253289
});

test/context/directory/resourceServers.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,26 @@ describe('#directory context resourceServers', () => {
8484
const resourceServersFolder = path.join(dir, constants.RESOURCE_SERVERS_DIRECTORY);
8585
expect(loadJSON(path.join(resourceServersFolder, 'my resource.json'))).to.deep.equal(context.assets.resourceServers[0]);
8686
});
87+
88+
it('should dump resource servers sanitized', async () => {
89+
const dir = path.join(testDataDir, 'directory', 'resourceServersDump');
90+
cleanThenMkdir(dir);
91+
const context = new Context({ AUTH0_INPUT_FILE: dir }, mockMgmtClient());
92+
93+
context.assets.resourceServers = [
94+
{
95+
identifier: 'http://myapi.com/api',
96+
name: 'my/test/ resource',
97+
scopes: [
98+
{ description: 'update account', name: 'update:account' },
99+
{ description: 'read account', name: 'read:account' },
100+
{ description: 'admin access', name: 'admin' }
101+
]
102+
}
103+
];
104+
105+
await handler.dump(context);
106+
const resourceServersFolder = path.join(dir, constants.RESOURCE_SERVERS_DIRECTORY);
107+
expect(loadJSON(path.join(resourceServersFolder, 'my-test- resource.json'))).to.deep.equal(context.assets.resourceServers[0]);
108+
});
87109
});

0 commit comments

Comments
 (0)