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

Support blocking untrusted operations #24

Merged
merged 17 commits into from
Sep 4, 2022
1 change: 1 addition & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ test -f "$TARGET/lib/pkgconfig/vips.pc" || (
patch -p1 <$SOURCE_DIR/build/patches/vips-1492-emscripten.patch
patch -p1 <$SOURCE_DIR/build/patches/vips-disable-nls.patch
patch -p1 <$SOURCE_DIR/build/patches/vips-libjxl-disable-concurrency.patch
patch -p1 <$SOURCE_DIR/build/patches/vips-operation-block-cache-invalidate.patch
[ -n "$ENABLE_MODULES" ] && patch -p1 <$SOURCE_DIR/build/patches/vips-dynamic-modules-emscripten.patch
#patch -p1 <$SOURCE_DIR/build/patches/vips-1492-profiler.patch
# Disable building C++ bindings, man pages, gettext po files, tools, and (fuzz-)tests
Expand Down
29 changes: 29 additions & 0 deletions build/patches/vips-operation-block-cache-invalidate.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: John Cupitt <jcupitt@gmail.com>
Date: Sun, 4 Sep 2022 16:35:00 +0100
Subject: [PATCH 1/1] check for blocked operations on cache lookup

We could return an operation from cache even after the operatuion had
been blocked. This was harmless, but could cause confusion.

see https://github.com/kleisauke/wasm-vips/pull/24

Upstream-Status: Accepted [https://github.com/libvips/libvips/commit/702ed8298f45d7ba342ebf5bae612d159e9cec6f]

diff --git a/libvips/iofuncs/cache.c b/libvips/iofuncs/cache.c
index 1111111..2222222 100644
--- a/libvips/iofuncs/cache.c
+++ b/libvips/iofuncs/cache.c
@@ -798,8 +798,10 @@ vips_cache_operation_lookup( VipsOperation *operation )
result = NULL;

if( (hit = g_hash_table_lookup( vips_cache_table, operation )) ) {
- if( hit->invalid ) {
- /* There but has been tagged for removal.
+ if( hit->invalid ||
+ (VIPS_OPERATION_GET_CLASS( hit->operation )->flags &
+ VIPS_OPERATION_BLOCKED) ) {
+ /* Has been tagged for removal, or has been blocked.
*/
vips_cache_remove( hit->operation );
hit = NULL;
32 changes: 32 additions & 0 deletions build/preamble_vips.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,38 @@ declare module Vips {
*/
function concurrency(concurrency?: number): void | number;

/**
* Set the block state on all untrusted operations.
* For example:
* ```js
* vips.blockUntrusted(true);
* ```
* Will block all untrusted operations from running. Use:
* ```bash
* $ vips -l | grep untrusted
* ```
* at the command-line to see which operations are marked as untrusted.
* @param state Set to `true` to block the operations, set to `false` to re-enable them.
*/
function blockUntrusted(state: boolean): void;

/**
* Set the block state on all operations in the libvips class hierarchy.
* For example:
* ```js
* vips.operationBlock('VipsForeignLoad', true);
* vips.operationBlock('VipsForeignLoadJpeg', false);
* ```
* Will block all load operations, except JPEG. Use:
* ```bash
* $ vips -l
* ```
* at the command-line to see the class hierarchy.
* @param name The name of the operation in the libvips class hierarchy.
* @param state Set to `true` to block the operation, set to `false` to re-enable it.
*/
function operationBlock(name: string, state: boolean): void;

/**
* Call this to shutdown libvips and the runtime of Emscripten.
* This is only needed on Node.js, as the thread pool of
Expand Down
32 changes: 32 additions & 0 deletions lib/vips.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions src/vips-emscripten.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,13 @@ EMSCRIPTEN_BINDINGS(my_module) {
function("config", optional_override([]() {
return vips::replace_all(VIPS_CONFIG, ", ", "\n");
}));
function("blockUntrusted", optional_override([](bool state) {
vips_block_untrusted_set(state ? 1 : 0);
}));
function("operationBlock",
optional_override([](const std::string &name, bool state) {
vips_operation_block_set(name.c_str(), state ? 1 : 0);
}));

// Helper for Node.js to shutdown libvips and the runtime of Emscripten
function("shutdown", optional_override([]() {
Expand Down
2 changes: 2 additions & 0 deletions test/unit/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const jpegFile = getPath('sample.jpg');
export const jxlFile = getPath('sample.jxl');
export const truncatedFile = getPath('truncated.jpg');
export const pngFile = getPath('sample.png');
export const vipsFile = getPath('sample.vips');
export const tifFile = getPath('sample.tif');
export const tif1File = getPath('1bit.tif');
export const tif2File = getPath('2bit.tif');
Expand All @@ -28,6 +29,7 @@ export const testFiles = [
jxlFile,
truncatedFile,
pngFile,
vipsFile,
tifFile,
tif1File,
tif2File,
Expand Down
Binary file added test/unit/images/sample.vips
Binary file not shown.
1 change: 1 addition & 0 deletions test/unit/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
</script>
<script src="lib/vips.js"></script>
<script type="module" src="test_arithmetic.js"></script>
<script type="module" src="test_block.js"></script>
<script type="module" src="test_colour.js"></script>
<script type="module" src="test_connection.js"></script>
<script type="module" src="test_conversion.js"></script>
Expand Down
57 changes: 57 additions & 0 deletions test/unit/test_block.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use strict';

import * as Helpers from './helpers.js';

describe('block', () => {
afterEach(function () {
// Not really necessary, but helps debugging ref leaks and ensures that the images are properly
// cleaned up after every test
cleanup();
});

it('blockUntrusted', function () {
// Needs VIPS file load support
if (!Helpers.have('vipsload')) {
return this.skip();
}

// By default, libvips doesn't block untrusted operations
expect(() => vips.Image.vipsload(Helpers.vipsFile)).to.not.throw();

// However, if the environment variable `VIPS_BLOCK_UNTRUSTED` is set,
// or `vips_block_untrusted_set( TRUE );` is called, any operations
// that are tagged as untrusted will be prevented from running
vips.blockUntrusted(true);

// For example, `vipsload` is an untrusted operation, and would throw
// an error when untrusted operations are blocked
expect(() => vips.Image.vipsload(Helpers.vipsFile)).to.throw(/operation is blocked/);

// Ensure no operations are blocked when the rest of the tests are run
vips.blockUntrusted(false);
});

it('operationBlock', function () {
// Needs JPEG and PNG file load support
if (!Helpers.have('jpegload') || !Helpers.have('pngload')) {
return this.skip();
}

vips.operationBlock('VipsForeignLoadJpeg', true);
expect(() => vips.Image.jpegload(Helpers.jpegFile)).to.throw(/operation is blocked/);

vips.operationBlock('VipsForeignLoad', false);
vips.operationBlock('VipsForeignLoadPng', true);
expect(() => vips.Image.jpegload(Helpers.jpegFile)).to.not.throw();
expect(() => vips.Image.pngload(Helpers.pngFile)).to.throw(/operation is blocked/);

vips.operationBlock('VipsForeignLoadJpeg', true);
expect(() => vips.Image.jpegload(Helpers.jpegFile)).to.throw(/operation is blocked/);

vips.operationBlock('VipsForeignLoadPng', false);
expect(() => vips.Image.pngload(Helpers.pngFile)).to.not.throw();

// Ensure no operations are blocked when the rest of the tests are run
vips.operationBlock('VipsForeignLoad', false);
});
});