From 9fe76765988e1c522e0926ddf7eea8b89cdf39c0 Mon Sep 17 00:00:00 2001 From: Ivan Robles Date: Mon, 18 May 2026 21:17:13 -0600 Subject: [PATCH 1/5] fix symlink detect and skip, path traversal. --- src/index.js | 10 ++++++++-- src/templateCopy.js | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index dce2bfa..20af6f1 100644 --- a/src/index.js +++ b/src/index.js @@ -14,7 +14,8 @@ import listMessage from './utils/listMessage'; const argsv = minimist(argv.slice(2)); const resolveAndInitialize = (template, appName) => { - const targetDirectory = path.join(process.cwd(), appName); + const safeName = appName === '.' ? '.' : path.basename(appName); + const targetDirectory = path.join(process.cwd(), safeName); const sourceDir = path.resolve( fileURLToPath(import.meta.url), '../../templates', @@ -106,17 +107,22 @@ const app = async (args) => { resolveAndInitialize(templateResponse.template, appNameResponse.appName); } catch (err) { console.error(colors.error(err.message)); + process.exit(1); } } else { try { resolveAndInitialize(templateArg, appNameArg); } catch (err) { console.error(colors.error(err.message)); + process.exit(1); } } return 0; }; -app(argsv); +app(argsv).catch((err) => { + console.error(err.message); + process.exit(1); +}); export default app; diff --git a/src/templateCopy.js b/src/templateCopy.js index b5118c3..e439bbf 100644 --- a/src/templateCopy.js +++ b/src/templateCopy.js @@ -20,6 +20,8 @@ const copyFilesAndDirectories = (source, destination) => { try { const stat = lstatSync(sourcePath); + if (stat.isSymbolicLink()) return; + if (stat.isDirectory()) { mkdirSync(destPath, { recursive: true }); copyFilesAndDirectories(sourcePath, destPath); From 6f39bcbbe007f9b1aa82dcefa01fcde2ef410457 Mon Sep 17 00:00:00 2001 From: Ivan Robles Date: Mon, 18 May 2026 21:20:37 -0600 Subject: [PATCH 2/5] add error handling at templateRenameName. --- __tests__/unit/index.unit.test.js | 4 ++-- __tests__/unit/initialize.unit.test.js | 12 ++++-------- __tests__/unit/templateRenameName.unit.test.js | 4 ++-- src/initialize.js | 6 ++---- src/templateRenameName.js | 2 +- 5 files changed, 11 insertions(+), 17 deletions(-) diff --git a/__tests__/unit/index.unit.test.js b/__tests__/unit/index.unit.test.js index 6561234..66235b6 100644 --- a/__tests__/unit/index.unit.test.js +++ b/__tests__/unit/index.unit.test.js @@ -52,7 +52,7 @@ describe('Passing arguments to main app', () => { }); test('Passing a invalid template', async () => { - const { stdout } = await execa`node index.js --with no-template ${appNameMock}`; - expect(stdout).toContain('Invalid Template'); + const { stderr } = await execa('node', ['index.js', '--with', 'no-template', appNameMock], { reject: false }); + expect(stderr).toContain('Invalid Template'); }); }); diff --git a/__tests__/unit/initialize.unit.test.js b/__tests__/unit/initialize.unit.test.js index 52afcf3..00e5891 100644 --- a/__tests__/unit/initialize.unit.test.js +++ b/__tests__/unit/initialize.unit.test.js @@ -14,18 +14,14 @@ describe('Initialize app', () => { const packageJson = readFileSync(`${genPath}/package.json`, 'utf8'); expect(packageJson).toContain(appNameMock); }); - test('Attempt directory already exist', () => { + test('Throws when target directory already exists', async () => { mkdirSync(genPath, { recursive: true }); copyFilesAndDirectories(sourcePath, genPath); - initialize(sourcePath, genPath, appNameMock); - - const packageJson = readFileSync(`${genPath}/package.json`, 'utf8'); - expect(packageJson).toContain('vanilla-js'); + await expect(initialize(sourcePath, genPath, appNameMock)).rejects.toThrow('Target directory already exist!'); }); - test('Invalid template path does not create directory', () => { + test('Throws on invalid template path', async () => { const invalidSource = '/non/existent/template'; - initialize(invalidSource, genPath, appNameMock); - + await expect(initialize(invalidSource, genPath, appNameMock)).rejects.toThrow('Invalid Template'); expect(existsSync(genPath)).toBe(false); }); }); diff --git a/__tests__/unit/templateRenameName.unit.test.js b/__tests__/unit/templateRenameName.unit.test.js index 926dcff..9279d3b 100644 --- a/__tests__/unit/templateRenameName.unit.test.js +++ b/__tests__/unit/templateRenameName.unit.test.js @@ -17,9 +17,9 @@ describe('App name in package.json', () => { expect(packageJson).toContain(appNameMock); }); - test('Handles missing package.json without throwing', () => { + test('Throws on missing package.json', () => { expect(() => { renamePackageJsonName('/non/existent/path', appNameMock); - }).not.toThrow(); + }).toThrow('Failed to update package.json name'); }); }); diff --git a/src/initialize.js b/src/initialize.js index 0a3ea3c..8c99153 100644 --- a/src/initialize.js +++ b/src/initialize.js @@ -10,13 +10,11 @@ async function initialize(sourcePath, destinationPath, name) { const isDestinationPath = existsSync(destinationPath); if (!isValidTemplate) { - console.log(colors.error('Invalid Template')); - return; + throw new Error('Invalid Template'); } if (isDestinationPath && !isCurrentDir) { - console.log(colors.error('Target directory already exist!')); - return; + throw new Error('Target directory already exist!'); } if (!isCurrentDir) { diff --git a/src/templateRenameName.js b/src/templateRenameName.js index d3b0058..7651f56 100644 --- a/src/templateRenameName.js +++ b/src/templateRenameName.js @@ -16,7 +16,7 @@ const renamePackageJsonName = (targetDir, nameApp) => { 'utf8', ); } catch (err) { - console.log(err.message); + throw new Error(`Failed to update package.json name: ${err.message}`); } }; From 80afe2beff9a45bbf80f81f1be236fca6a2a0095 Mon Sep 17 00:00:00 2001 From: Ivan Robles Date: Mon, 18 May 2026 21:21:17 -0600 Subject: [PATCH 3/5] add app name lenght validation. --- __tests__/unit/appName.unit.test.js | 2 ++ src/utils/appName.js | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/__tests__/unit/appName.unit.test.js b/__tests__/unit/appName.unit.test.js index b48ade7..e650805 100644 --- a/__tests__/unit/appName.unit.test.js +++ b/__tests__/unit/appName.unit.test.js @@ -24,5 +24,7 @@ describe('App name fuctions', () => { expect(validateAppName('my-app_app')).toContain('App name should not contain special characters except hyphen (-)'); expect(validateAppName('myapp!@(#&*(!@^$&*#&*@))')).toContain('App name should not contain special characters except hyphen (-)'); expect(validateAppName('my_app')).toContain('App name should not contain special characters except hyphen (-)'); + expect(validateAppName('a'.repeat(215))).toContain('App name must be 214 characters or fewer'); + expect(validateAppName('a'.repeat(214))).toBe(true); }); }); diff --git a/src/utils/appName.js b/src/utils/appName.js index 91482c1..4997d53 100644 --- a/src/utils/appName.js +++ b/src/utils/appName.js @@ -1,4 +1,10 @@ const formatAppName = (name) => name === '.' ? '.' : name.toLowerCase().split(/[\s|_]/).join('-'); -const validateAppName = (name) => name === '.' || !name.match(/[^a-zA-Z0-9-\s]/g) ? true : 'App name should not contain special characters except hyphen (-)'; + +const validateAppName = (name) => { + if (name === '.') return true; + if (name.length > 214) return 'App name must be 214 characters or fewer'; + if (name.match(/[^a-zA-Z0-9-\s]/g)) return 'App name should not contain special characters except hyphen (-)'; + return true; +}; export { formatAppName, validateAppName }; From 251f877070fa888dc5077883e3bbf277fa5c7b05 Mon Sep 17 00:00:00 2001 From: Ivan Robles Date: Mon, 18 May 2026 21:26:43 -0600 Subject: [PATCH 4/5] fix linter issues. --- src/templateRenameName.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/templateRenameName.js b/src/templateRenameName.js index 7651f56..609139e 100644 --- a/src/templateRenameName.js +++ b/src/templateRenameName.js @@ -16,7 +16,7 @@ const renamePackageJsonName = (targetDir, nameApp) => { 'utf8', ); } catch (err) { - throw new Error(`Failed to update package.json name: ${err.message}`); + throw new Error(`Failed to update package.json name: ${err.message}`, { cause: err }); } }; From eeb2ff90c1509bd31ecb9c2233196451b1ccb835 Mon Sep 17 00:00:00 2001 From: Ivan Robles Date: Mon, 18 May 2026 21:35:54 -0600 Subject: [PATCH 5/5] update package version. --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7fba259..55ea44b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "create-enchilada", - "version": "1.2.0", + "version": "1.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "create-enchilada", - "version": "1.2.0", + "version": "1.3.0", "license": "MIT", "dependencies": { "chalk": "^5.3.0", diff --git a/package.json b/package.json index 906679a..a006aa6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "create-enchilada", - "version": "1.2.0", + "version": "1.3.0", "type": "module", "description": "CLI scaffold tool to create web app projects from templates: React, Vanilla JS, Node/Express and more.", "main": "index.js",