Skip to content

install.sh pipes the admin view through echo -e, silently corrupting any backslash in it #229

Description

@Ryanawesome111

Summary

install.sh builds the installed admin view by piping the extension's admin.view source through bash's echo -e:

# scripts/commands/extensions/install.sh:1168 (main, beta-2026-06)
echo -e "$(<".blueprint/tmp/$admin_view")\n@endsection" >> "$AdminBladeConstructor"

echo -e expands backslash escapes, so any backslash in an extension's admin view is silently rewritten on the way to disk. Every other packaged file is copied verbatim; this one is not, and nothing warns the developer.

This is not theoretical — it took our panel down. A fully-qualified class name in our admin view, \Pterodactyl\BlueprintFramework\Extensions\<id>\Branding, put an ESC byte (0x1B) and a backspace (0x08) into the installed Blade, and /admin/extensions/<id> returned 500 on every request:

production.ERROR: syntax error, unexpected character 0x1B
(View: resources/views/admin/extensions/<id>/index.blade.php)

A fully-qualified class name is a completely ordinary thing to write in a Blade view, and the source file on disk is clean — the corrupt bytes only exist after install.

Minimal repro

Reproduces on any bash, including 3.2. \c is the worst of the three cases: it truncates the rest of the file and eats the appended @endsection, with no error at all.

$ printf '<p>@if($x)yes@endif</p>\n<p>{{ $path }}</p>\n<p>C:\\code\\thing</p>\n<p>tail of the view</p>\n' > view.blade.php
$ wc -c < view.blade.php
88

$ echo -e "$(<view.blade.php)\n@endsection" | cat -v
<p>@if($x)yes@endif</p>
<p>{{ $path }}</p>
<p>C:
$ echo -e "$(<view.blade.php)\n@endsection" | wc -c
48

40 bytes of the view are gone, @endsection was never appended, and the install reports success.

The other two:

  • \b → BS 0x08 (bash 3.2+)
  • \e / \E → ESC 0x1B (bash 4.2+) — this is what produced the 500 above
  • also \a \f \n \r \t \v \\ \0NNN \xHH \uHHHH \UHHHHHHHH
$ printf '<p>{{ \\Pterodactyl\\BlueprintFramework\\Extensions\\breeze\\Branding::x() }}</p>\n' > v.blade.php
$ echo -e "$(<v.blade.php)\n@endsection" | xxd | sed -n '4p'
00000030: 0872 6565 7a65 5c42 7261 6e64 696e 673a  .reeze\Branding:
#         ^^ 0x08, was 0x5C 0x62

Suggested fix

Append the bytes instead of echoing them. This preserves the file exactly and still handles a source file with no trailing newline:

cat ".blueprint/tmp/$admin_view" >> "$AdminBladeConstructor"
printf '\n@endsection\n' >> "$AdminBladeConstructor"

Verified against the repro above — 88 bytes in, 88 + \n@endsection\n out, backslashes intact.

printf '%s\n@endsection\n' "$(<file)" also works, but keeps the command substitution's trailing-newline stripping; cat avoids that too.

Notes

  • Present on main at scripts/commands/extensions/install.sh:1168, and in beta-2026-01 at blueprint/scripts/commands/extensions/install.sh:1192.
  • Our own workaround, until this is fixed upstream, is to forbid backslashes in the admin view outright and enforce it with a byte-level test over the packaged file set — the admin view gets no backslash at all, and anything needing a class name moves into admin.controller, which Blueprint copies verbatim. Happy to open a PR for the cat change if that's the direction you'd want.

Metadata

Metadata

Assignees

Labels

scope:backendIssues or pull requests related to Blueprint's backend.scope:cliIssues or pull requests related to the Blueprint CLI.scope:extensionsIssues or pull requests related to Blueprint extensions.

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions