Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: prevent print crash on bad deviceName #21982

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/api/web-contents.md
Expand Up @@ -1259,7 +1259,7 @@ Returns [`PrinterInfo[]`](structures/printer-info.md)
* `silent` Boolean (optional) - Don't ask user for print settings. Default is `false`.
* `printBackground` Boolean (optional) - Prints the background color and image of
the web page. Default is `false`.
* `deviceName` String (optional) - Set the printer device name to use. Default is `''`.
* `deviceName` String (optional) - Set the printer device name to use. Must be the system-defined name and not the 'friendly' name, e.g 'Brother_QL_820NWB' and not 'Brother QL-820NWB'.
* `color` Boolean (optional) - Set whether the printed web page will be in color or grayscale. Default is `true`.
* `margins` Object (optional)
* `marginType` String (optional) - Can be `default`, `none`, `printableArea`, or `custom`. If `custom` is chosen, you will also need to specify `top`, `bottom`, `left`, and `right`.
Expand Down
28 changes: 28 additions & 0 deletions shell/browser/api/atom_api_web_contents.cc
Expand Up @@ -121,6 +121,10 @@
#if BUILDFLAG(ENABLE_PRINTING)
#include "chrome/browser/printing/print_view_manager_basic.h"
#include "components/printing/common/print_messages.h"

#if defined(OS_WIN)
#include "printing/backend/win_helper.h"
#endif
#endif

#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
Expand Down Expand Up @@ -350,6 +354,26 @@ base::Optional<base::TimeDelta> GetCursorBlinkInterval() {
return base::nullopt;
}

#if BUILDFLAG(ENABLE_PRINTING)
// This will return false if no printer with the provided device_name can be
// found on the network. We need to check this because Chromium does not do
// sanity checking of device_name validity and so will crash on invalid names.
bool IsDeviceNameValid(const base::string16& device_name) {
#if defined(OS_MACOSX)
base::ScopedCFTypeRef<CFStringRef> new_printer_id(
base::SysUTF16ToCFStringRef(device_name));
PMPrinter new_printer = PMPrinterCreateFromPrinterID(new_printer_id.get());
bool printer_exists = new_printer != nullptr;
PMRelease(new_printer);
return printer_exists;
#elif defined(OS_WIN)
printing::ScopedPrinterHandle printer;
return printer.OpenPrinterWithName(device_name.c_str());
#endif
return true;
}
#endif

} // namespace

WebContents::WebContents(v8::Isolate* isolate,
Expand Down Expand Up @@ -1772,6 +1796,10 @@ void WebContents::Print(mate::Arguments* args) {
// Printer device name as opened by the OS.
base::string16 device_name;
options.Get("deviceName", &device_name);
if (!device_name.empty() && !IsDeviceNameValid(device_name)) {
args->ThrowError("webContents.print(): Invalid deviceName provided.");
return;
}
settings.SetStringKey(printing::kSettingDeviceName, device_name);

int scale_factor = 100;
Expand Down
17 changes: 15 additions & 2 deletions spec-main/api-web-contents-spec.ts
Expand Up @@ -104,22 +104,35 @@ describe('webContents module', () => {
})

ifdescribe(features.isPrintingEnabled())('webContents.print()', () => {
let w: BrowserWindow

beforeEach(() => {
w = new BrowserWindow({ show: false })
})

afterEach(closeAllWindows)

it('throws when invalid settings are passed', () => {
const w = new BrowserWindow({ show: false })
expect(() => {
// @ts-ignore this line is intentionally incorrect
w.webContents.print(true)
}).to.throw('webContents.print(): Invalid print settings specified.')
})

it('throws when an invalid callback is passed', () => {
expect(() => {
// @ts-ignore this line is intentionally incorrect
w.webContents.print({}, true)
}).to.throw('webContents.print(): Invalid optional callback provided.')
})

ifit(process.platform !== 'linux')('throws when an invalid deviceName is passed', () => {
expect(() => {
w.webContents.print({ deviceName: 'i-am-a-nonexistent-printer' }, () => {})
}).to.throw('webContents.print(): Invalid deviceName provided.')
})

it('does not crash', () => {
const w = new BrowserWindow({ show: false })
expect(() => {
w.webContents.print({ silent: true })
}).to.not.throw()
Expand Down