Don't allow opening of files we know cannot be edited; closes #2286

This commit is contained in:
Dane Everitt 2020-08-30 10:25:48 -07:00
parent 906cfce81c
commit d87438621f
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
6 changed files with 65 additions and 65 deletions

View file

@ -14,14 +14,6 @@ class FileObjectTransformer extends BaseDaemonTransformer
*/
private $editable = [];
/**
* FileObjectTransformer constructor.
*/
public function __construct()
{
$this->editable = config('pterodactyl.files.editable', []);
}
/**
* Transform a file object response from the daemon into a standardized response.
*
@ -36,8 +28,7 @@ class FileObjectTransformer extends BaseDaemonTransformer
'size' => Arr::get($item, 'size'),
'is_file' => Arr::get($item, 'file', true),
'is_symlink' => Arr::get($item, 'symlink', false),
'is_editable' => in_array(Arr::get($item, 'mime', ''), $this->editable),
'mimetype' => Arr::get($item, 'mime'),
'mimetype' => Arr::get($item, 'mime', 'application/octet-stream'),
'created_at' => Carbon::parse(Arr::get($item, 'created', ''))->toIso8601String(),
'modified_at' => Carbon::parse(Arr::get($item, 'modified', ''))->toIso8601String(),
];

View file

@ -178,20 +178,6 @@ return [
*/
'files' => [
'max_edit_size' => env('PTERODACTYL_FILES_MAX_EDIT_SIZE', 1024 * 1024 * 4),
'editable' => [
'application/json',
'application/javascript',
'application/xml',
'application/xhtml+xml',
'inode/x-empty',
'text/xml',
'text/css',
'text/html',
'text/plain',
'text/x-perl',
'text/x-shellscript',
'text/x-python',
],
],
/*

View file

@ -8,11 +8,11 @@ export interface FileObject {
size: number;
isFile: boolean;
isSymlink: boolean;
isEditable: boolean;
mimetype: string;
createdAt: Date;
modifiedAt: Date;
isArchiveType: () => boolean;
isEditable: () => boolean;
}
export default async (uuid: string, directory?: string): Promise<FileObject[]> => {

View file

@ -19,7 +19,6 @@ export const rawDataToFileObject = (data: FractalResponseData): FileObject => ({
size: Number(data.attributes.size),
isFile: data.attributes.is_file,
isSymlink: data.attributes.is_symlink,
isEditable: data.attributes.is_editable,
mimetype: data.attributes.mimetype,
createdAt: new Date(data.attributes.created_at),
modifiedAt: new Date(data.attributes.modified_at),
@ -39,6 +38,19 @@ export const rawDataToFileObject = (data: FractalResponseData): FileObject => ({
'application/zip', // .zip
].indexOf(this.mimetype) >= 0;
},
isEditable: function () {
if (this.isArchiveType() || !this.isFile) return false;
const matches = [
'application/jar',
'application/octet-stream',
'inode/directory',
/^image\//,
];
return matches.every(m => !this.mimetype.match(m));
},
});
export const rawDataToServerBackup = ({ attributes }: FractalResponseData): ServerBackup => ({

View file

@ -16,7 +16,7 @@ const Row = styled.div`
${tw`flex bg-neutral-700 rounded-sm mb-px text-sm hover:text-neutral-100 cursor-pointer items-center no-underline hover:bg-neutral-600`};
`;
const FileObjectRow = ({ file }: { file: FileObject }) => {
const Clickable: React.FC<{ file: FileObject }> = memo(({ file, children }) => {
const directory = ServerContext.useStoreState(state => state.files.directory);
const history = useHistory();
@ -35,48 +35,59 @@ const FileObjectRow = ({ file }: { file: FileObject }) => {
};
return (
<Row
key={file.name}
onContextMenu={e => {
e.preventDefault();
window.dispatchEvent(new CustomEvent(`pterodactyl:files:ctx:${file.key}`, { detail: e.clientX }));
}}
>
<SelectFileCheckbox name={file.name}/>
file.isFile && !file.isEditable() ?
<div css={tw`flex flex-1 text-neutral-300 no-underline p-3 cursor-default`}>
{children}
</div>
:
<NavLink
to={`${match.url}/${file.isFile ? 'edit/' : ''}#${cleanDirectoryPath(`${directory}/${file.name}`)}`}
css={tw`flex flex-1 text-neutral-300 no-underline p-3`}
onClick={onRowClick}
>
<div css={tw`flex-none self-center text-neutral-400 mr-4 text-lg pl-3 ml-6`}>
{file.isFile ?
<FontAwesomeIcon icon={file.isSymlink ? faFileImport : file.isArchiveType() ? faFileArchive : faFileAlt}/>
:
<FontAwesomeIcon icon={faFolder}/>
}
</div>
<div css={tw`flex-1`}>
{file.name}
</div>
{file.isFile &&
<div css={tw`w-1/6 text-right mr-4`}>
{bytesToHuman(file.size)}
</div>
}
<div
css={tw`w-1/5 text-right mr-4`}
title={file.modifiedAt.toString()}
>
{Math.abs(differenceInHours(file.modifiedAt, new Date())) > 48 ?
format(file.modifiedAt, 'MMM do, yyyy h:mma')
:
formatDistanceToNow(file.modifiedAt, { addSuffix: true })
}
</div>
{children}
</NavLink>
<FileDropdownMenu file={file}/>
</Row>
);
};
}, isEqual);
export default memo(FileObjectRow, (prevProps, nextProps) => isEqual(prevProps.file, nextProps.file));
const FileObjectRow = ({ file }: { file: FileObject }) => (
<Row
key={file.name}
onContextMenu={e => {
e.preventDefault();
window.dispatchEvent(new CustomEvent(`pterodactyl:files:ctx:${file.key}`, { detail: e.clientX }));
}}
>
<SelectFileCheckbox name={file.name}/>
<Clickable file={file}>
<div css={tw`flex-none self-center text-neutral-400 mr-4 text-lg pl-3 ml-6`}>
{file.isFile ?
<FontAwesomeIcon icon={file.isSymlink ? faFileImport : file.isArchiveType() ? faFileArchive : faFileAlt}/>
:
<FontAwesomeIcon icon={faFolder}/>
}
</div>
<div css={tw`flex-1`}>
{file.name}
</div>
{file.isFile &&
<div css={tw`w-1/6 text-right mr-4`}>
{bytesToHuman(file.size)}
</div>
}
<div
css={tw`w-1/5 text-right mr-4`}
title={file.modifiedAt.toString()}
>
{Math.abs(differenceInHours(file.modifiedAt, new Date())) > 48 ?
format(file.modifiedAt, 'MMM do, yyyy h:mma')
:
formatDistanceToNow(file.modifiedAt, { addSuffix: true })
}
</div>
</Clickable>
<FileDropdownMenu file={file}/>
</Row>
);
export default memo(FileObjectRow, isEqual);

View file

@ -26,12 +26,12 @@ const generateDirectoryData = (name: string): FileObject => ({
mode: '0644',
size: 0,
isFile: false,
isEditable: false,
isSymlink: false,
mimetype: '',
createdAt: new Date(),
modifiedAt: new Date(),
isArchiveType: () => false,
isEditable: () => false,
});
export default () => {