Skip to content

Commit

Permalink
🔒 Limited permissions for uploaded files to 0644 (#21841)
Browse files Browse the repository at this point in the history
Fixes
https://linear.app/ghost/issue/ENG-1010/uploaded-file-permission-security-improvement

- This commit ensures all files uploaded to Ghost via importer are set
with 0644 permissions to improve security.
- Uploaded files previously retained their original permissions, which
could leave them executable.
- This commit prevents files from being inadvertently executable.
  • Loading branch information
vershwal authored Dec 10, 2024
1 parent c38e83d commit 9476afb
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 0 deletions.
4 changes: 4 additions & 0 deletions ghost/core/core/server/data/importer/import-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,10 @@ class ImportManager {

try {
await extract(filePath, tmpDir);

// Set permissions for all extracted files
const files = glob.sync('**/*', {cwd: tmpDir, nodir: true});
await Promise.all(files.map(file => fs.chmod(path.join(tmpDir, file), 0o644)));
} catch (err) {
if (err.message.startsWith('ENAMETOOLONG:')) {
// The file was probably zipped with MacOS zip utility. Which doesn't correctly set UTF-8 encoding flag.
Expand Down
25 changes: 25 additions & 0 deletions ghost/core/test/unit/server/data/importer/import-manager.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const should = require('should');
const fs = require('fs-extra');
const path = require('path');
const glob = require('glob');
const importManager = require('../../../../../core/server/data/importer/import-manager');

describe('Import Manager', function () {
describe('extractZip', function () {
it('extracts zip file and sets correct file permissions', async function () {
const zipPath = path.join(__dirname, '/test.zip');
const extractedPath = await importManager.extractZip(zipPath);
try {
const files = glob.sync('**/*', {cwd: extractedPath, nodir: true});
files.forEach((file) => {
const filePath = path.join(extractedPath, file);
const stats = fs.statSync(filePath);
const fileMode = stats.mode & 0o777;
should.equal(fileMode, 0o644, `File ${file} should have 0644 permissions`);
});
} finally {
await fs.remove(extractedPath);
}
});
});
});
Binary file not shown.

0 comments on commit 9476afb

Please sign in to comment.