Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,18 @@ jobs:
{ printf -- "$front"; cat _app/CHANGELOG.md; } > source/_reference/changelog.md
{ printf -- "$front6"; cat _app6/CHANGELOG.md; } > source/v6/_reference/changelog.md

# The published security advisories of the app, for the Security advisories page
# (source/_security_assessment/security-assessment/advisories.md). Fetched on every build, so a
# new advisory reaches the site with the next build; a failed fetch fails the build rather than
# publishing a list that silently lost its entries.
- name: Bring in the security advisories
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
gh api 'repos/ansibleforms/ansibleforms/security-advisories?state=published&per_page=100' > source/_data/advisories.json
echo "$(jq length source/_data/advisories.json) published advisories"

- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ vendor/
_app/
_app6/
source/_data/help.yaml
source/_data/advisories.json

# A language other than English is assembled here before it is built (scripts/build-language.sh)
_build/
2 changes: 2 additions & 0 deletions i18n/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ just_the_docs:
name: Guía de usuario
integrate:
name: Integrar
security_assessment:
name: Seguridad
reference:
name: Referencia

Expand Down
7 changes: 7 additions & 0 deletions source/_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ collections:
integrate:
permalink: "/:path:output_ext"
output: true
security_assessment:
permalink: "/:path:output_ext"
output: true
reference:
permalink: "/:path:output_ext"
output: true
Expand Down Expand Up @@ -156,6 +159,10 @@ just_the_docs:
name: Integrate
icon: plug
nav_fold: true
security_assessment:
name: Security
icon: shield-halved
nav_fold: true
reference:
name: Reference
icon: book
Expand Down
79 changes: 79 additions & 0 deletions source/_security_assessment/security-assessment/advisories.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
layout: default
title: Security advisories
nav_order: 5
---

# Security advisories
{: .no_toc }

Every published vulnerability, its severity and its fix
{: .fs-6 .fw-300 }

1. TOC
{:toc}

---

## Published advisories

Read from the [Security tab of the repository](https://github.com/ansibleforms/ansibleforms/security/advisories) at every build of this site:

{% assign af_advisories = site.data.advisories | sort: "published_at" | reverse %}
{% if af_advisories.size > 0 %}
<table>
<thead>
<tr>
<th>Advisory</th>
<th>Severity</th>
<th>Weakness</th>
<th>Affected</th>
<th>Fixed in</th>
<th>Published</th>
</tr>
</thead>
<tbody>
{% for a in af_advisories %}
<tr>
<td>
<strong>{{ a.summary | escape }}</strong><br>
<a href="{{ a.html_url }}">{{ a.ghsa_id }}</a>{% if a.cve_id %}<br>{{ a.cve_id }}{% endif %}
</td>
<td>
{{ a.severity | capitalize }}
{%- assign af_cvss = a.cvss_severities.cvss_v3 | default: a.cvss -%}
{%- if af_cvss.score %}<br>CVSS {{ af_cvss.score }}{% endif %}
</td>
<td>
{%- for w in a.cwes %}<span title="{{ w.name | escape }}">{{ w.cwe_id }}</span>{% unless forloop.last %}<br>{% endunless %}{% endfor -%}
</td>
<td>{% for v in a.vulnerabilities %}<code>{{ v.vulnerable_version_range }}</code>{% unless forloop.last %}<br>{% endunless %}{% endfor %}</td>
<td>{% for v in a.vulnerabilities %}{{ v.patched_versions | default: "-" }}{% unless forloop.last %}<br>{% endunless %}{% endfor %}</td>
<td>{{ a.published_at | date: "%Y-%m-%d" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
No advisory has been published.
{% endif %}

Each advisory on GitHub gives the details: the impact, the conditions, the CVSS vector and the fix.

---

## What this means for your version

An advisory names the versions it affects and the first version that is fixed. The fix ships in the supported releases,
[7.x and 6.5.x](development.html#supported-versions), and older releases are not patched: upgrading is the fix.

Compare the version of an instance (**About** in the help menu, or `GET /api/v2/version`) with **Affected**.

---

## Being told of new advisories

GitHub notifies the watchers of the repository when an advisory is published:

* On [github.com/ansibleforms/ansibleforms](https://github.com/ansibleforms/ansibleforms), choose **Watch > Custom > Security alerts**.
* The [changelog](../changelog.html) lists every security fix under the release that ships it.
91 changes: 91 additions & 0 deletions source/_security_assessment/security-assessment/development.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
layout: default
title: Secure development
nav_order: 3
---

# Secure development
{: .no_toc }

How changes are checked and released, and what is supported
{: .fs-6 .fw-300 }

1. TOC
{:toc}

---

## The source code

AnsibleForms is open source, so everything on this page can be checked in the repository itself:

* The code, its history and its build pipelines are public on [github.com/ansibleforms/ansibleforms](https://github.com/ansibleforms/ansibleforms).
* The [license](https://github.com/ansibleforms/ansibleforms/blob/main/LICENSE) is GPL-3.0.
* The [changelog](../changelog.html) lists every change of every release, security fixes included.

---

## How a change is merged

Every change reaches `main` through a pull request, under a ruleset that nobody can bypass:

| Rule | What it means |
|---|---|
| Pull request required | Nothing is pushed to `main` directly |
| Required checks | The pull request merges only when every check below passes |
| No force push, no deletion | The history of `main` cannot be rewritten or removed |
| Linear history | Every pull request becomes one commit, so each change can be traced and reverted on its own |

The checks a pull request must pass:

| Check | What it does |
|---|---|
| Client, Server | The unit tests of the web interface and of the server |
| CodeQL | GitHub's static analysis of the JavaScript and TypeScript code, for injection, cross-site scripting and other weaknesses |
| Public schema is up to date | The database schema the code expects matches the one it ships |
| Title, Branch | The pull request follows the naming rules, so the changelog stays accurate |

Two more run on every pull request and report on it:

* **Database** tests the server on MySQL and MariaDB, and **Browser** drives the interface in a browser.
* **Dependency review** blocks a new dependency with a known high or critical vulnerability.

---

## Code analysis and dependencies

The code and what it depends on are checked again after they merge:

* **CodeQL** also analyses every push to `main` and to the release branches, and the whole code base every week:
a new CodeQL rule then also checks the code merged before it.
* **Dependabot** opens a pull request for each new version of a dependency: the npm packages of the server and the
interface, the Python packages of the runtime environment, and the GitHub Actions of the pipelines every week, and
the base images of the containers every day.
* The base images are pinned by digest (`node:26-trixie-slim@sha256:...`), so a build uses exactly the reviewed image.
Dependabot moves the digest when the image is rebuilt upstream.

---

## Releases and images

A release is built and published by GitHub Actions only, never from a developer's machine:

* A release is cut from `main` (or `release/6.x`), with a changelog built from its pull requests.
* The images are built by the [Publish workflow](https://github.com/ansibleforms/ansibleforms/blob/main/.github/workflows/publish.yml)
and pushed to the GitHub Container Registry, `ghcr.io/ansibleforms/ansibleforms`, with the runtime environment images
next to it. The workflow and its logs are public, so anyone can see what went into an image.
* The application and runtime environment images run as the unprivileged `node` user (uid 1000), never as root.

---

## Supported versions

Security fixes go into the newest release of each supported line:

| Version | Receives security fixes |
|---|---|
| 7.x | Yes |
| 6.5.x | Yes |
| Older than 6.5 | No: upgrading is the fix |

The [upgrade guide](../upgrading/) covers moving to a newer release, and [Upgrading to v7](../upgrade-7.html) the move from 6.5.
85 changes: 85 additions & 0 deletions source/_security_assessment/security-assessment/faq.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
layout: default
title: Assessment FAQ
nav_order: 6
---

# Assessment FAQ
{: .no_toc }

Short answers to the questions security questionnaires ask most
{: .fs-6 .fw-300 }

1. TOC
{:toc}

---

## Data and hosting

Where the data lives, and who can reach it:

| Question | Answer |
|---|---|
| Where is our data stored? | In the MySQL or MariaDB database you run, and in the persistent folder of your instance. AnsibleForms is self-hosted, with no cloud service behind it. |
| Does it send data to its makers? | No. There is no telemetry, no usage reporting and no update check. |
| What does it connect to? | Only the systems an administrator configures: see [Outbound connections](measures.html#outbound-connections). |
| Does it use AI or send data to a model provider? | Only with the [chat assistant](../chat/) on, which is off by default. It then sends form definitions and the values being filled in to the provider you configure. |
| Can it run without internet access? | Yes. The application needs no internet access; the playbooks need whatever their tasks reach. |

---

## Encryption

What is encrypted, and with which keys:

| Question | Answer |
|---|---|
| Is data encrypted in transit? | With [HTTPS](../security/https.html), served by the application or by your reverse proxy, and `wss://` between the application and its runtime environments. |
| Is data encrypted at rest? | Every stored secret is encrypted by the application with AES-256. Jobs and their output are stored as they are: use the database's own encryption at rest. See [Stored secrets](measures.html#stored-secrets). |
| Who holds the keys? | You do: the [`ENCRYPTION_SECRET`](../security/secrets.html) is set by you, kept out of backups, and can be rotated. |
| Can secrets stay in our vault? | Yes: [HashiCorp Vault and CyberArk](../secret-stores/) are read when a job needs them. |

---

## Access and accountability

Who can sign in, what they can do, and what is recorded:

| Question | Answer |
|---|---|
| Does it support single sign-on? | Yes: LDAP / Active Directory, OpenID Connect and Entra ID. See [Authentication](../authentication/). |
| Does it support multi-factor authentication? | Through single sign-on: your identity provider's MFA applies. Local accounts have none of their own. |
| How are permissions managed? | With roles mapped to groups, checked by the server on every request. See [Permissions](measures.html#permissions). |
| Is there an audit log? | Yes: every change and every refused attempt, kept 365 days by default and forwardable to syslog. See [Audit trail](measures.html#audit-trail). |
| Is there a default password? | The first admin account has a public default password, which must be changed at the first sign-in. See [The admin account](../security/hardening.html#the-admin-account). |

---

## Vulnerabilities and updates

How problems are found, fixed and announced:

| Question | Answer |
|---|---|
| Have vulnerabilities been found? | Yes. Every published one is listed in [Security advisories](advisories.html), with the version that fixed it. |
| How do we report one? | Privately, through GitHub: see [Reporting a vulnerability](reporting.html). |
| How is the code checked? | CodeQL static analysis and dependency review on every pull request, automated tests, and Dependabot updates. See [Secure development](development.html). |
| Which versions get security fixes? | 7.x and 6.5.x. See [Supported versions](development.html#supported-versions). |
| How do we hear of a new advisory? | Watch the repository's security alerts on GitHub. See [Being told of new advisories](advisories.html#being-told-of-new-advisories). |
| Has it been penetration tested? | No third-party penetration test report is published. The code is public, so your own team or tester can assess it, and findings are reported as above. |

---

## Images and supply chain

Where the images come from, and how to check them:

| Question | Answer |
|---|---|
| Where are the images published? | On the GitHub Container Registry, `ghcr.io/ansibleforms/ansibleforms`, built by public GitHub Actions workflows. See [Releases and images](development.html#releases-and-images). |
| Do the containers run as root? | No: as the unprivileged `node` user. |
| Are the images signed? | Not yet. Pull them by digest (`@sha256:...`) to be sure you run the image you reviewed. |
| Is there an SBOM? | None is published with the images yet. A scanner such as Trivy or Syft builds one from the image. |
| Are the images scanned for vulnerabilities? | Not in the release pipeline. Their base images are pinned by digest and kept current by Dependabot; scan the images in your own registry to apply your own policy. |
| What license is it under? | GPL-3.0, open source, without warranty. |
39 changes: 39 additions & 0 deletions source/_security_assessment/security-assessment/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
layout: default
title: Overview
nav_order: 1
permalink: /security-assessment/
---

# Security assessment
{: .no_toc }

For security reviews: measures, development and advisories
{: .fs-6 .fw-300 }

---

## In short

AnsibleForms runs playbooks and holds their credentials, so it is part of your trust chain:

* It is **self-hosted**: it runs on your infrastructure, keeps its data in your database, and sends nothing to its makers.
* It is **open source** (GPL-3.0): the code, its pipelines and every advisory are public.
* Every stored secret is **encrypted**, every change **audited**, and **roles** checked on every request.
* Vulnerabilities are **reported privately**, fixed in every supported release, and **published**.

---

## In this section

The pages answer the questions of a security review, from the design to the CVE list:

| Page | What it covers |
|---|---|
| [Security measures](measures.html) | Sign-in, permissions, stored secrets, the audit trail, the browser, the server, connections and containers |
| [Secure development](development.html) | How a change is reviewed and tested, code and dependency analysis, how images are built, supported versions |
| [Reporting a vulnerability](reporting.html) | How to report a problem privately, and what happens next |
| [Security advisories](advisories.html) | Every published vulnerability, with its CVE, severity and fixed version |
| [Assessment FAQ](faq.html) | Short answers to the questions security questionnaires ask most |

To secure an instance rather than assess it, see [Security](../security/) in the administrator's guide.
Loading
Loading