Skip to content

v1.1.2

Choose a tag to compare

@github-actions github-actions released this 16 Oct 18:31
· 17 commits to main since this release
e0d917d

[1.1.2] - 2026-10-16

Security

  • While a person's temporary password must still change (a new account an
    admin created, or a temporary password an admin set), their session can
    only read their own profile, change the password and use MFA. Every other
    API call from that session, creating an API token included, answers 403
    with password_change_required, their live streams are refused, and an
    open dashboard tab goes to the password change page. Before, only the web
    app asked for the change, and the API served that session in full,
    including creating API tokens.
  • Listing the Rosters a person leaves on demotion or deactivation now checks
    that the person belongs to your workspace, like the other People routes
    (404 otherwise). Before, it answered for a person of another workspace;
    one instance runs one workspace, so this only mattered if a second
    workspace row existed.
  • Model discovery (GET /models) now accepts connection settings (a base
    URL, a key variable, an API version, a region, a profile, a project or a
    location) only from admins; every other role, and their API tokens, gets
    403 before anything is read or called. Before, any signed-in user, a
    Viewer included, could name an environment variable and a URL, and
    OpsMender sent that variable's value to the URL as a bearer key, which
    could expose the token signing secret, the database URL or a provider key.
    A model's key setting must now be written like an environment variable
    name (letters, digits and underscores, not starting with a digit);
    discovery and the model save routes refuse anything else with 400
    before reading or storing it, without repeating it. Settings saved before
    this change are not rewritten. An unknown provider in discovery answers
    400 instead of a server error.
  • The generic command guard now also catches common runner names: terminal,
    console, tty, pty, shellexec, bash_tool, invoke_expression,
    subprocess, popen, os_system, sudo, su, docker, podman,
    nerdctl, crictl and systemctl, names that start with bash_,
    powershell_, pwsh_, sh_, terminal_, console_, sudo_, docker_
    or subprocess_, and names that end with _terminal, _console, _bash,
    _powershell, _pwsh, _subprocess or _cypher, in any case or separator
    style (Terminal, BashTool, bash-tool, PowerShellExecute). It also
    catches the runner tools of common MCP servers, such as kubectl_generic,
    call_kubectl, call_aws, execute_kubectl, ssh_execute,
    start_process, execute_code, browser_run_code_unsafe and
    explain_sql_statement (which runs the statement when it analyzes it); the
    MCP Skills page lists all of them and the bounded tools to classify in a
    Skill instead. Before, terminal,
    shellexec, bash_tool and the like were ordinary tools, so a Skill entry
    or glob that allowed one let it run at Tier 0, without approval at Tier 1,
    and in environment scans. Now they are blocked at Tier 0 and Tier 2 and in
    environment scans. At Tier 1 they ask for operator approval, and run once
    approved, even where the Skill blocks them
    : the guard is checked before a
    tool's own tier policy, so a T1 policy of blocked or advisory, or no
    entry for the tool, no longer refuses it. To block one at every tier, add it
    to the Skill's deny-list with deny: true. A narrowly scoped tool that a
    new prefix or suffix catches, such as docker_logs, console_url,
    get_console or aws_console, needs an entry with its exact name and
    allow_generic: true to run under its own tier policies again. Skill
    Studio's Generate from tools and AI assist now mark these names as generic
    and pre-set them to deny, as they already did for the older runner names.
  • OIDC sign-in and Teams actions now name the signature algorithms they
    accept. An identity provider's id token must be signed with an asymmetric
    algorithm that its discovery document lists (RS256 when it lists none), and
    a Teams action with RS256, the algorithm Bot Framework signs with. Before,
    both checks used whatever algorithm the token named (python-jose advisory
    GHSA-3qf3-8w2g-rqmx), and a token naming HS256 or none caused a server
    error instead of a refusal. An identity provider set to sign id tokens with
    HS256 (the client secret), or with an RSASSA-PSS algorithm (PS256 and up)
    that the token library cannot verify, is now refused with a clear message;
    set it to RS256 or ES256. Both checks also try only the published keys
    that can verify the token (only those with its key id when any key has
    it), so a key set that lists an EC key before the RSA key, or holds a key
    that cannot be used, no longer refuses a valid token.
  • The web app is now built with Next.js 16.4.1 (from 16.2.3) and tested with
    Vitest 4.1.11 (from 3.2.4), which carry the fixes for the published
    advisories against both, including three Next.js advisories from
    2026-10-14 that 16.4.0 does not fix. None reached a running OpsMender: it
    serves a static export with no Next.js server or image optimizer. The
    locked Mako moves to 1.4.3.
  • Every pull request and release now audits dependencies: the web app's
    runtime npm packages fail the check on a high or critical advisory, and
    the installed Python packages fail it on any known advisory (checked
    against OSV). Three advisories are ignored because they do not apply:
    GHSA-3qf3-8w2g-rqmx in python-jose (every token check names its
    algorithms), GHSA-wj6h-64fc-37mp in ecdsa (it affects signing, which
    OpsMender never does with ecdsa) and GHSA-84m7-p3x7-pcfv in the MCP SDK
    (it affects MCP servers; OpsMender only connects to them as a client).

Fixed

  • Assigning an incident to someone else, and bulk reassign or bulk
    acknowledge for someone else, now refuse the change when the incident's
    owner changed after the request was checked, naming the current owner if
    there is one (409, or that incident's failure in a bulk action); nothing
    is written. Before, an assignment that arrived a moment after someone took
    the incident replaced them without a note or an Inbox notice. An
    assignment that arrives as the incident is resolved or merged is refused
    the same way; before, it answered a server error or reported success.
  • Alert intake refuses a body whose objects and arrays nest more than 64
    levels deep with 400 and opens no incident; the token's delivery log keeps
    a short row naming the limit, never the body. Every other route refuses a
    JSON body nested more than 64 levels with 400 before the route acts on
    it, and the Teams, Discord, Slack and ticket webhooks, which read their own
    bodies, refuse one too deep to read with their usual 400. A token nested
    too deeply to read (a session, an MFA challenge, an SSO or bot sign-in
    state, a voice link or a Teams token) is refused like any bad token.
    Before, intake accepted and stored deeply nested bodies and failed with a
    server error on the deepest, and a deeply nested body or token sent to
    other routes, such as saving My Routing settings, failed with a server
    error, sometimes after the value was saved.
  • On SQLite, the standalone binary's local database, identical alerts that
    arrive at the same moment now open one incident and page once: the app takes
    incoming alerts one at a time inside its process. Before, each alert could
    open its own incident and page again. Run one app process per SQLite
    database; several processes need PostgreSQL.
  • Inbox quiet hours and My Routing quiet hours are now saved separately, and
    saving one page never changes the other. Before, both pages wrote one
    shared setting: saving the Inbox notification preferences, even just a
    category mute, turned off the quiet hours that hold back P1 pages, saving
    My Routing turned off Inbox quiet hours, and after any Inbox save, Save
    routing failed with "[object Object]". A save the server refuses as
    invalid now shows the reason, in My Routing and the other pages. Through
    the API, Inbox quiet hours are under routing.in_app.quiet_hours of
    /users/me/notification-preferences, and its quiet_hours is My
    Routing's only. Upgrading moves saved Inbox quiet hours to their new
    place; SQLite evaluation databases are not migrated, so set Inbox quiet
    hours again there.
  • Reloading the Reliability page, or following a link out of the app, while
    it is still loading no longer logs "Failed to load reliability data" in the
    browser console: the browser cancels the page's requests then, and a
    cancelled or aborted request is not a failure. A load that really fails is
    still logged, also after you move to another page of the app.
  • Saving My Routing through the API with only one of the Inbox settings
    under routing.in_app (a category mute, say) no longer drops the other
    (the Inbox quiet hours); the settings are merged with what is saved. The
    My Routing page never sends them, so this only affected API calls.

Changed

  • The People guide now says what a Viewer sees: in the web app the Dashboard,
    the incident list and each incident's summary, the On Call Schedule,
    Activity, their Inbox and their own profile; through the API also the lists
    of Escalation Chains, Rosters, teams, services, memories and more, but not
    incident timelines or comments, reports or analytics. A Viewer can't list or
    open AI sessions or read their chat history, but sees each session's tool
    calls in Activity and its proposed actions in the approval list, can read
    which memories a session used, and can follow a running session's live
    stream, chat included, if they know its id. What a Viewer can open is
    unchanged; tests now hold the sidebar, the page guard, these API answers and
    the session content a Viewer reads to it. Before, the Auth guide said
    Viewers could read sessions and the People guide didn't say what a Viewer
    could open.
  • The two quiet-hours settings now say which is which. The Inbox preferences
    page (Inbox, then Preferences; it was called Notification preferences) calls
    its setting Inbox quiet hours, says it only affects Inbox notices, and links
    admins and operators to My Routing. The quiet hours in My Routing say they
    hold pages and link back to the Inbox preferences page. Before, both pages
    just said "quiet hours", so setting one looked like setting the other.