The form rendering for the SHOW_FORM step was inline in the dialog template.
Extract it into a presentation-only InstallFormRendererComponent next to the
dialog. The dialog now binds [fields], [formGroup], [resolveImagePath], and
[reviewMode] inputs.
[reviewMode] consolidates the previous dialog-side passwordVisible[key]=true
assignment into the renderer's own state — the dialog no longer needs to
track per-field password visibility.
Layout mirrors thingsboard-pe so changes propagate cleanly between branches.
- ${images.gallery(...)} accepts JS-style image objects
({src, alt?, caption?}) that may span multiple lines and contain
whitespace inside the gallery brackets. Captions go through a
whitelist sanitiser (b/strong/i/em/u/s/mark/small/sub/sup/br/code/
span with class+style only — style values containing
expression()/javascript:/url() are dropped) so authors can use
inline markup without exposing XSS.
- Promote escapeHtml / escapeHtmlAttr to exported helpers in
iot-hub-markdown.utils, add the new sanitizeInlineHtml. Drop the
duplicate copies (and the now-unused DocLinks helpers + buildDocLinkButton)
from shared/models/iot-hub/device-package.models.
- Move the PhotoSwipe wiring out of TbIotHubMarkdownComponent into a
reusable tbPhotoSwipeGallery shared directive, declared/exported
by SharedModule. The component now just renders the
data-attributed wrapper and the directive lazy-binds PhotoSwipe.
- Tighten the gallery layout: 200px-min auto-fill grid, 8px vertical
margins, hover border swapped to the shared #2a7dec accent token.
Two test layers covering the controller surface that the JVN PoC uses:
Java unit (Spring MockMvc):
RuleChainControllerTest#testScriptForbiddenForCustomer asserts a
customer JWT against POST /api/ruleChain/testScript returns 403,
locking in the existing @PreAuthorize('TENANT_ADMIN') guard.
Black-box (live docker-compose):
JsExecutorSandboxIsolationTest#testRuleChainScriptCannotReachHostProcess
posts the JVN exploit payload as a tenant admin and asserts the
response carries error='process is not defined'. End-to-end through
tb-node -> Kafka -> tb-js-executor with use_sandbox=true.
Registered the new org.thingsboard.server.msa.security package in
the connectivity TestNG suite so the black-box runner picks it up.
Added a thin TestRestClient.testRuleChainScript() helper.
Four test cases under describe('js-executor'):
- sandbox isolates args from host realm (JVN#16937365 — regression guard)
- sandbox passes string args through unchanged
- non-sandbox path does not isolate from host realm (documented contract)
- non-sandbox path passes string args through unchanged
Tests use Node's built-in node:test + node:assert (zero new devDependencies;
ts-node was already there). Two npm scripts:
test — spec output for local dev
test:ci — spec to stdout + Node's built-in junit reporter to
target/surefire-reports/TEST-js-executor.xml
Wired 'yarn test:ci' into the Maven 'test' phase via frontend-maven-plugin,
so 'mvn test -pl=msa/js-executor' produces JUnit XML that TeamCity's
Maven runner auto-discovers under the 'js-executor' suite name.
TEST_FAST.md picks up the same step.
tsconfig excludes test/ from the production pkg bundle.
The args array passed into the sandbox carried the host realm prototype
chain, so a script could reach the host Function constructor via
args.constructor.constructor and execute arbitrary code in the host
process (read files, run shell commands, dump env vars).
Construct args inside the sandbox context using vm.runInContext('[]'),
then populate with string primitives. The resulting array's prototype
chain belongs to the sandbox realm, so constructor traversal cannot
escape. Strings are primitives and safe to cross the realm boundary.
Affects use_sandbox=true path only. The use_sandbox=false path
(invokeFunction) is intentionally left as-is and explicitly marked as
dangerous-by-design — it compiles and runs user-supplied scripts in
the host realm via vm.compileFunction (parsingContext only isolates
parsing, not execution). It remains as a documented performance
trade-off for trusted, non-public clusters; a startup WARN is logged
when script.use_sandbox=false, and an operator-facing yaml comment
sits next to the setting in config/default.yml.
Reported by Hiroki Imai, LAC Co., Ltd.
- Wire photoswipe (5.4.4) into ui-ngx and load its CSS via angular.json.
- Replace the in-place ".tb-gallery-img-expanded" toggle in
TbIotHubMarkdownComponent.onReady with a PhotoSwipe lightbox bound
to each ".tb-gallery-images" container, picking up
".tb-gallery-image" anchors. Items are sized from the underlying
image's naturalWidth/Height; the lightbox slide uses contained
rendering with a max viewport sized 90vw / 78vh and a 0-opacity
backdrop.
- Style the gallery: 240px-min auto-fill grid with 12px gap, 115px
thumbnail strip with object-fit contained images, hover lift
(border + translate + shadow) and a fade-in caption tooltip
bottom-pinned over the thumbnail.
- Move solution-install-dialog.component.{ts,html,scss} from
home/components/solution/ into home/components/iot-hub/, drop the
now-empty solution folder.
- Register SolutionInstallDialogComponent in IotHubComponentsModule
(declaration + export) and remove its declaration / export /
import from HomeComponentsModule. HomeComponentsModule already
imports IotHubComponentsModule, so consumers continue to resolve
the component without changes.
- Update import paths in iot-hub-install-dialog,
iot-hub-update-dialog, iot-hub-item-detail-dialog (and the
modified home-components module) to the new
@home/components/iot-hub/solution-install-dialog.component path.
- Pick up incidental work-in-progress edits in the iot-hub markdown
utils / template / dialogs touched alongside this move.
- Add TbIotHubMarkdownComponent (home/components/iot-hub) wrapping
tb-markdown with applyDefaultMarkdownStyle=false, the IoT Hub item
link compile module, and forwarded lineNumbers /
fallbackToPlainMarkdown coerced-boolean inputs.
- Carry the readme/description typography (h1..h6, p, ul/ol, li, a,
img, code, code-wrapper / clipboard-btn, pre[language] with Prism
tokens and line-numbers, tables) so callers don't have to override
the default markdown styles. Tunes ported from the design:
list margins, 6px heading bottom padding, table widths and
padding, clipboard offsets, etc.
- Replace tb-markdown with tb-iot-hub-markdown in the item detail
dialog (description + readme + changelog), the install / update
dialog error blocks, and the solution install dialog. Drop the
inline .dlg-readme markdown overrides and the
.dlg-description ::ng-deep block, leaving only the dlg-readme
layout tweak and a padding:0 reset for .dlg-description.
- Solution install dialog drops the now-redundant
itemLinkCompileModules field + IotHubItemLinkModule import and the
.table-wrapper ::ng-deep override; the iot-hub-markdown component
carries those styles itself.
--no-bytecode is only required for the Windows target; running pkg as a
single multi-target invocation applied it to Linux too, dropping V8
bytecode from the Linux exe. Split into two pkg invocations so Linux
keeps bytecode and only the Windows build relaxes it.
- Selected card inverts the icon container: primary-coloured
background with the masked glyph painted white, on top of the
existing primary border + inset shadow.
- Default (idle, non-hovered) cards use a transparent border so the
outline appears only on hover or when selected — the 1px layout
space stays intact to avoid jitter.
Apply a tb-connectivity-cards-pair modifier to the card grid when
availableInstallMethods.length === 2 and switch its tracks to
repeat(auto-fill, minmax(300px, 1fr)) so the two cards spread to
sensible widths instead of squeezing into the 240px minimum.
- Add installMethodIcons map covering integrations (existing
assets/integration-icon files), direct-connect transports (new
assets/direct-connect-icon: http/mqtt/coap/lwm2m/snmp), and gateway
connectors (new assets/gateway-connect-icon: mqtt/modbus/opc-ua/
bacnet/ble/can/ftp/ocpp/odbc/request/rest/snmp/socket/xmpp).
Direct/gateway icons are exported from the design via the Figma
REST API. The PE-only illustration is also exported.
- Replace the row of stroked buttons in the device install
connectivity selector with a card grid that mirrors the design
(276x116, 6px radius, primary border on hover/select, mask-image
driven icon rendered in primary, primary-tinted 4% surface,
inner-shadowed 48x48 white icon container, "PE Only" pill).
- Restructure the wizard so that the connection method is the first
wizard step ("Connection method"). Three placeholder steps
(Prerequisites / Configuration / Provisioning) preview the rest of
the flow until a method is picked. On selection, placeholders are
swapped for the real install steps and the stepper auto-advances;
re-clicking the already selected card is a no-op.
- Replace the inline PE-only panel with a dedicated dialog
(TbPeConnectivityMethodPromptComponent) matching the design
(500x364 white card, 140 illustration, 24px title with primary
connector name, "Try Professional Edition" link to
https://thingsboard.io/installations/, Close, top-right X). PE-only
cards open this prompt instead of advancing the wizard.
* docs(iot-hub): spec for ${item-link:uuid} markdown component
Design for a new markdown placeholder that renders an item card
(thumbnail + name + creator) inline in IoT Hub readmes and install
instructions, opening the linked item in a new tab.
* docs(iot-hub): correct branch name in item-link spec
* docs(iot-hub): implementation plan for ${item-link:uuid} markdown component
Step-by-step task plan covering the placeholder utility, the link card
component, module wiring, three integration sites (item readme, device
install instructions, solution install instructions), translations, and
manual visual QA.
* feat(iot-hub): add item-link placeholder utility for markdown
* feat(iot-hub): add TbIotHubItemLinkCardComponent for markdown item links
* refactor(iot-hub): remove impossible-state guards in item-link card
* feat(iot-hub): wrap item-link card in IotHubItemLinkModule
* feat(iot-hub): add item-link-unavailable translation key
* feat(iot-hub): render ${item-link} cards in item readme
* feat(iot-hub): render ${item-link} cards in device install instructions
* feat(iot-hub): render ${item-link} cards in solution install instructions
* fix(iot-hub): match item-link card thumbnail to search popup (99x56, contain)
- Split the settings dialog into a resizable two-pane layout (left:
resources/HTML/CSS/JS tabs; right: live preview) using split.js,
with a fullscreen toggle that resets the tab animation duration to
avoid jank during expand.
- Split ContainerFunctionEditorCompleter into HTML- and Angular-mode
variants so the autocomplete suggests `container` only in HTML
mode (Angular mode has no container argument).
- Mark the widget with previewWidth/previewHeight 100% and
overflowVisible: true in its controllerScript typeParameters so
the basic config preview fills its slot.
ImageService.replaceBase64WithImageUrl rewrites base64 data URIs into
system-image URLs at save time. After install, the DB image is a
'tb-image;...' URL while the JSON file still carries a base64 data URI —
naive string compare always reported a diff and caused every system
bundle to be re-saved on every patch run.
SystemPatchApplier now creates widget types missing from the DB
instead of throwing, and merges new fqns into existing system bundles.
Bundle creation and inline widgetTypes in bundle JSONs are explicitly
rejected as out-of-scope for patch upgrades.
- Add a clearFilters output + hasActiveFilters() helper to
TbIotHubInstalledItemsTableComponent and render a primary
"Clear all filters" button in the empty state when search or
type filters are active. The parent installed-items page binds
the output to its clearAllFilters() and now also resets
textSearch / appliedTextSearch alongside the type filters.
- Restore the 24px bottom margin on the empty-state body paragraph
so the button has the same breathing room as in the browse
component empty state.
- Update the installed-items page filter panel: remove DASHBOARD,
add ALARM_RULE, and reorder to the canonical
DEVICE → SOLUTION_TEMPLATE → WIDGET → CALCULATED_FIELD →
ALARM_RULE → RULE_CHAIN order shared with home and search.
- Replace the plain text empty placeholder with the same illustration +
title + subtitle layout used by tb-iot-hub-empty-state in the browse
component (no-data illustration, 18/500 title, 14/0.54 body).
- Add a no-installed-items-text locale key ("Try adjusting your search
or filters.").
- Make the table wrapper a flex column and place the empty state and
loading spinner inside it as flex-1 children so both vertically
centre below the sticky header instead of pinning to the top.
- Add a dedicated alarm-rules hero SVG (560×276) and point the
ALARM_RULE PAGE_CONFIG to it instead of the stale dashboards image.
- Replace the calculated-fields and devices hero SVGs with the
updated illustrations exported from the design.
The Installed / IoT Hub toggle now carries icon templates that don't
collapse cleanly into a mat-select on xs viewports — keep the toggle
buttons at every breakpoint instead.
- Add a `template?: TemplateRef<any>` slot to ToggleHeaderOption +
ToggleOption so callers can render arbitrary content inside a
toggle (the existing string label / error icon path stays the
default).
- Use it in the dashboard widget-select header to render a tb-icon
next to each label: now_widgets for Installed, hub for IoT Hub.
- Style the top-level toggle with a 42px pill (8px radius, white-15
border, 40px buttons, 36px line-height, 500 weight) so the icon +
label sit comfortably and contrast against the primary toolbar.
- Apply the same DEVICE → SOLUTION_TEMPLATE → WIDGET → CALCULATED_FIELD →
ALARM_RULE → RULE_CHAIN ordering to the home search popup and the
search component, so all entry points group results consistently.
- Drop DASHBOARD entirely from the search component (route, installed
counts, type order) and filter it out of the home search popup
groups.
- Add ALARM_RULE to the search component (compact-card layout, route
alarm-rules, installed-count handling).
- Remove the "Add from IoT Hub" header action and addDashboardFromIotHub
handler from the dashboards page, plus the now-unused
IotHubActionsService / ItemType imports and constructor injection.