mirror of
https://github.com/inventree/InvenTree.git
synced 2026-05-09 03:03:41 +00:00
01bf600004
* Ensure the MeUserSerializer correctly marks fields as read-only
* fix behaviour
* Add note to plugin docs.
* Adjust logic for PluginListTable
* Add superuser scope to PluginInstall API endpoint
* Update unit test for API endpoint
* Explicitly set PLUGINS_INSTALL_DISABLED if PLUGINS_ENABLED = False
* Check for superuser permission in installer.py
* Additional user checks
* Sanitize package name to protect against OS command injection
* fix(security): use SandboxedEnvironment for PART_NAME_FORMAT rendering
- Switch jinja2.Environment to jinja2.sandbox.SandboxedEnvironment in
part/helpers.py to prevent SSTI via template tags in PART_NAME_FORMAT.
- Set pk=1 on the dummy Part instance in the validator to ensure
conditional expressions like {% if part.pk %} are properly evaluated
during validation, closing the sandbox bypass vector.
Fixes GHSA-84jh-x777-8pqq
* Disable some unit tests for backport
* Fix SSRF in remote image download
Add IP address validation to prevent Server-Side Request Forgery
when downloading images from remote URLs. The resolved IP is now
checked against private, loopback, link-local, and reserved ranges
before connecting.
Redirects are followed manually (up to 5 hops) with SSRF validation
at each step, preventing redirect-based bypass of URL format checks.
* Style fixes
* fix styles
* fix test
* Reintroduce unit tests
---------
Co-authored-by: Paul <morimori-dev@github.com>
Co-authored-by: tikket1 <chrisveres1@gmail.com>
Co-authored-by: Matthias Mair <code@mjmair.com>
168 lines
5.8 KiB
TypeScript
168 lines
5.8 KiB
TypeScript
import { expect, test } from './baseFixtures.js';
|
|
import { adminuser } from './defaults.js';
|
|
import { activateTableView, loadTab } from './helpers.js';
|
|
import { doCachedLogin } from './login.js';
|
|
import { setPluginState } from './settings.js';
|
|
|
|
// Test for the label editing interface
|
|
test('Printing - Label Editing', async ({ browser }) => {
|
|
const page = await doCachedLogin(browser, {
|
|
username: adminuser.username,
|
|
password: adminuser.password,
|
|
url: 'settings/admin/labels'
|
|
});
|
|
|
|
// Open a particular label template for editing
|
|
await page.getByRole('cell', { name: 'Sample build line label' }).click();
|
|
|
|
// Await expected entries
|
|
await page.getByRole('tab', { name: 'PDF Preview' }).waitFor();
|
|
await page.getByText('This is an example template').waitFor();
|
|
await page
|
|
.locator('div')
|
|
.filter({ hasText: /^BO\d+$/ })
|
|
.first()
|
|
.waitFor();
|
|
});
|
|
|
|
/*
|
|
* Test for label printing.
|
|
* Select a number of stock items from the table,
|
|
* and print labels against them
|
|
*/
|
|
test('Printing - Label Printing', async ({ browser }) => {
|
|
const page = await doCachedLogin(browser, { url: 'stock/location/index/' });
|
|
|
|
await page.waitForURL('**/web/stock/location/**');
|
|
|
|
await loadTab(page, 'Stock Items');
|
|
|
|
// Select some labels
|
|
await page.getByLabel('Select record 1', { exact: true }).click();
|
|
await page.getByLabel('Select record 2', { exact: true }).click();
|
|
await page.getByLabel('Select record 3', { exact: true }).click();
|
|
|
|
await page
|
|
.getByLabel('Stock Items')
|
|
.getByLabel('action-menu-printing-actions')
|
|
.click();
|
|
await page.getByLabel('action-menu-printing-actions-print-labels').click();
|
|
|
|
// Select plugin
|
|
await page.getByLabel('related-field-plugin').click();
|
|
await page.getByText('InvenTreeLabelMachine').last().click();
|
|
|
|
// Select label template
|
|
await page.getByLabel('related-field-template').click();
|
|
await page
|
|
.getByRole('option', { name: 'InvenTree Stock Item Label' })
|
|
.click();
|
|
|
|
await page.getByLabel('related-field-plugin').click();
|
|
await page.getByRole('option', { name: 'InvenTreeLabel provides' }).click();
|
|
|
|
// Submit the print form (second time should result in success)
|
|
await page.getByRole('button', { name: 'Print', exact: true }).isEnabled();
|
|
await page.getByRole('button', { name: 'Print', exact: true }).click();
|
|
|
|
await page.getByText('Process completed successfully').first().waitFor();
|
|
await page.context().close();
|
|
});
|
|
|
|
/*
|
|
* Test for report printing
|
|
* Navigate to a PurchaseOrder detail page,
|
|
* and print a report against it.
|
|
*/
|
|
test('Printing - Report Printing', async ({ browser }) => {
|
|
const page = await doCachedLogin(browser, { url: 'stock/location/index/' });
|
|
|
|
await page.waitForURL('**/web/stock/location/**');
|
|
|
|
// Navigate to a specific PurchaseOrder
|
|
await page.getByRole('tab', { name: 'Purchasing' }).click();
|
|
await loadTab(page, 'Purchase Orders');
|
|
await activateTableView(page);
|
|
|
|
await page.getByRole('cell', { name: 'PO0009' }).click();
|
|
|
|
// Select "print report"
|
|
await page.getByLabel('action-menu-printing-actions').click();
|
|
await page.getByLabel('action-menu-printing-actions-print-reports').click();
|
|
|
|
// Template should auto-fill (there is only one template available)
|
|
await page.getByText('Sample purchase order report').waitFor();
|
|
await page.getByRole('button', { name: 'Print', exact: true }).isEnabled();
|
|
await page.getByRole('button', { name: 'Print', exact: true }).click();
|
|
await page.getByText('Process completed successfully').first().waitFor();
|
|
|
|
await page.context().close();
|
|
});
|
|
|
|
test('Printing - Report Editing', async ({ browser }) => {
|
|
const page = await doCachedLogin(browser, {
|
|
username: 'admin',
|
|
password: 'inventree'
|
|
});
|
|
|
|
// activate the sample plugin for this test
|
|
await setPluginState({
|
|
plugin: 'sampleui',
|
|
state: true
|
|
});
|
|
|
|
// Navigate to the admin center
|
|
await page.getByRole('button', { name: 'admin' }).click();
|
|
await page.getByRole('menuitem', { name: 'Admin Center' }).click();
|
|
await loadTab(page, 'Label Templates');
|
|
await page
|
|
.getByRole('cell', { name: 'InvenTree Stock Item Label (' })
|
|
.click();
|
|
|
|
// Generate preview
|
|
await page.getByLabel('split-button-preview-options-action').click();
|
|
await page
|
|
.getByLabel('split-button-preview-options-item-preview-save', {
|
|
exact: true
|
|
})
|
|
.click();
|
|
|
|
await page.getByRole('button', { name: 'Save & Reload' }).click();
|
|
|
|
await page.getByText('The preview has been updated').waitFor();
|
|
|
|
// Test plugin provided editors
|
|
await page.getByRole('tab', { name: 'Sample Template Editor' }).click();
|
|
const textarea = page.locator('#sample-template-editor-textarea');
|
|
const textareaValue = await textarea.inputValue();
|
|
expect(textareaValue).toContain(
|
|
`<img class='qr' alt="{% trans 'QR Code' %}" src='{% qrcode qr_data %}'>`
|
|
);
|
|
textarea.fill(`${textareaValue}\nHello world`);
|
|
|
|
// Switch back and forth to see if the changed contents get correctly passed between the hooks
|
|
await page.getByRole('tab', { name: 'Code', exact: true }).click();
|
|
await page.getByRole('tab', { name: 'Sample Template Editor' }).click();
|
|
const newTextareaValue = await page
|
|
.locator('#sample-template-editor-textarea')
|
|
.inputValue();
|
|
expect(newTextareaValue).toMatch(/\nHello world$/);
|
|
|
|
// Test plugin provided previews
|
|
await page.getByRole('tab', { name: 'Sample Template Preview' }).click();
|
|
await page.getByRole('heading', { name: 'Hello world' }).waitFor();
|
|
const consoleLogPromise = page.waitForEvent('console');
|
|
await page
|
|
.getByLabel('split-button-preview-options', { exact: true })
|
|
.click();
|
|
const msg = (await consoleLogPromise).args();
|
|
expect(await msg[0].jsonValue()).toBe('updatePreview');
|
|
expect(await msg[1].jsonValue()).toBe(newTextareaValue);
|
|
|
|
// deactivate the sample plugin again after the test
|
|
await setPluginState({
|
|
plugin: 'sampleui',
|
|
state: false
|
|
});
|
|
});
|