Conversation
✅
|
| Descriptor | Linter | Files | Fixed | Errors | Max errors | Warnings | Elapsed time |
|---|---|---|---|---|---|---|---|
| ✅ ACTION | actionlint | 5 | 0 | 0 | 0.06s | ||
| ✅ JSON | jsonlint | 4 | 0 | 0 | 0.12s | ||
| ✅ JSON | prettier | 4 | 0 | 0 | 0.45s | ||
| ✅ JSON | v8r | 4 | 0 | 0 | 8.26s | ||
| ✅ MARKDOWN | markdownlint | 174 | 0 | 0 | 3.33s | ||
| ✅ MARKDOWN | markdown-table-formatter | 174 | 0 | 0 | 0.44s | ||
| ✅ REPOSITORY | betterleaks | yes | no | no | 0.98s | ||
| ✅ REPOSITORY | checkov | yes | no | no | 18.82s | ||
| ✅ REPOSITORY | git_diff | yes | no | no | 0.08s | ||
| ✅ REPOSITORY | secretlint | yes | no | no | 1.45s | ||
| ✅ REPOSITORY | trufflehog | yes | no | no | 4.3s | ||
| ✅ SPELL | codespell | 184 | 0 | 0 | 0.6s | ||
| lychee | 184 | 3 | 0 | 40.62s | |||
| ✅ YAML | prettier | 6 | 0 | 0 | 0.43s | ||
| ✅ YAML | v8r | 6 | 0 | 0 | 7.85s | ||
| ✅ YAML | yamllint | 6 | 0 | 0 | 0.55s |
Detailed Issues
⚠️ SPELL / lychee - 3 errors
📝 Summary
---------------------
🔍 Total.........1142
🔗 Unique.........866
✅ Successful....1043
⏳ Timeouts.........3
🔀 Redirected.......9
👻 Excluded........93
❓ Unknown..........0
🚫 Errors...........3
⛔ Unsupported......3
Errors in .claude/skills/security-advisory/SKILL.md
[400] https://cveawg.mitre.org/api/cve/ (at 16:18) | Rejected status code: 400 Bad Request
[404] https://raw.githubusercontent.com/osism/ (at 17:18) | Rejected status code: 404 Not Found
[400] https://registry.osism.tech/service/token (at 18:19) | Rejected status code: 400 Bad Request
Errors in docs/concepts/cluster-network.md
[TIMEOUT] https://stordis.com/basic-configuration-and-management-of-sonic-devices/ (at 196:3) | Request timed out
[TIMEOUT] https://stordis.com/bgp-unnumbered-in-enterprise-sonic/ (at 66:1) | Request timed out
[TIMEOUT] https://stordis.com/deploying-bgp-underlay-in-enterprise-sonic/ (at 197:3) | Request timed out
Hint: Followed 9 redirects. You might want to consider replacing redirecting URLs with the resolved URLs. Use verbose mode (`-v`/`-vv`) to see redirection details.
See detailed reports in MegaLinter artifacts
Your project could benefit from a custom flavor, which would allow you to run only the linters you need, and thus improve runtime performances. (Skip this info by defining FLAVOR_SUGGESTIONS: false)
- Documentation: Custom Flavors
- Command:
npx mega-linter-runner@10.1.0 --custom-flavor-setup --custom-flavor-linters ACTION_ACTIONLINT,JSON_JSONLINT,JSON_V8R,JSON_PRETTIER,MARKDOWN_MARKDOWNLINT,MARKDOWN_MARKDOWN_TABLE_FORMATTER,REPOSITORY_CHECKOV,REPOSITORY_GIT_DIFF,REPOSITORY_BETTERLEAKS,REPOSITORY_SECRETLINT,REPOSITORY_TRUFFLEHOG,SPELL_LYCHEE,SPELL_CODESPELL,YAML_PRETTIER,YAML_YAMLLINT,YAML_V8R

Show us your support by starring ⭐ the repository
| if not ossa.get("id"): | ||
| m = re.search(r"(OSSA-\d{4}-\d{3})", path) or re.search(r"(OSSA-\d{4}-\d{3})", text) | ||
| ossa["id"] = m.group(1) if m else None | ||
| ossa["products"] = [p["product"] for p in ossa.get("affected_products", []) if p.get("product")] |
There was a problem hiding this comment.
[P1] Product names are not normalised
ossa["products"] is taken verbatim from affected-products[].product, and every consumer
assumes it is the lowercase deliverable name. About 10% of the OSSA corpus disagrees, and it is
not a historical artifact:
OSSA-2026-035 2026-08-13 Octavia ← the second-newest advisory
OSSA-2026-004 2026-03-19 Glance
OSSA-2025-002 2025-11-04 Keystone
OSSA-2024-005 2024-12-03 Neutron
… 21 of 208 documents; OSSA-2023-002 even has "Cinder, Glance, Nova" in one field
The upstream paths are case-sensitive:
deliverables/epoxy/octavia.yaml → 200 deliverables/epoxy/Octavia.yaml → 404
kolla docker/octavia → 200 kolla docker/Octavia → 404
With Octavia the following all miss, every one of them silently because the fetches use
allow_404:
:605deliverables/<series>/Octavia.yaml→{"error": "no deliverable file"}for every series:749project_dir.lower() not in product_variants→ every patch file is dropped:893docker/Octavia→ 404, plus the misleading "probably a library" note at:897:901role matchr["name"] == product→ no roles → notag_variables:910,:913enable_Octavia[a-z0-9_]*→ case-sensitive regex, no match:919-921prod_var→^(Octavia[a-z0-9_]*_(image|tag)):→ noosism_image_parameters, no registry lookups:1031r["project"].split("/")[-1] in products→rel_reviewsempty
Only github_tag_date survives, because GitHub repo names are case-insensitive.
Net effect on a real run against OSSA-2026-035: the dossier is not empty, which is what makes it
dangerous — it is populated with confident negatives.
- Section 6 renders a per-release table whose "Latest released version" column reads
no deliverable fileon every row (:1181). This is the one visible tell, and nothing warns
or connects it to the cause. - Section 8 prints "No patch files for the affected products in any built release. Either the
fix is not yet shipped by OSISM, or OSISM relies on the upstream stable branch" (:1257) —
a plausible, wrong conclusion. - Section 9 lists the product with empty images/roles/tag variables and the note "no
docker/Octaviadirectory in openstack/kolla — probably a library that is installed into
several images" (:897). That is an actively false statement about a service project, and
section 9 is whatSKILL.md:71-76andstyle.md:153-172mandate for the override snippet. - Section 11 has a full set of rows, all with
-for versions and fix reviews and
Covered = **no**.
So it does fail safe on coverage — every ASK-THE-AUTHOR box fires — but every other input the
author needs is silently wrong, and SKILL.md:88-89 says to map versions to series only through
section 6.
Normalise once, at parse time, and keep the original for display:
ossa["products"] = [p["product"].strip().lower()
for p in ossa.get("affected_products", []) if p.get("product")]and lowercase p["product"] in affected_products too (both in parse_ossa and in
parse_ossa_fallback), or carry a product_key alongside the display name. While there:
product_variants at :730-732 should be built from the lowercased name so :749 compares like
with like.
Lowercasing alone is not sufficient for every value in the corpus. OSSA-2023-002 has
- product: Cinder, Glance, Nova in a single field, which lowercases to one bogus deliverable
name. Either split on , into separate products, or — simpler and honest — self.warn() when a
product value does not look like a single deliverable (re.fullmatch(r"[a-z0-9._-]+", key) fails)
and add a section-12 checkbox, so the author sees why the lookups came back empty.
| related = [e for e in patches if e.get("related_to_ossa")] | ||
| prs = sorted({f"#{p['number']}" for e in related for p in e.get("pull_requests", [])}) | ||
| fix_merged_upstream = any(r["status"] == "MERGED" for r in rel_reviews) | ||
| covered = bool(related) or fix_merged_upstream |
There was a problem hiding this comment.
[P1] covered over-claims from untrusted evidence
covered is the only gate in front of the ASK THE AUTHOR checkbox at :1355 and the Covered
column that style.md:94-100 drives the Affected Versions table from. Both disjuncts leak.
fix_merged_upstreamcomes fromrel_reviews(:1031), which filters only on branch +
project-basename +MERGED. Butcollect_reviewsdeliberately adds every change found via
message:<bugid>/topic:bug/<bugid>(:517-522) and marks themlisted_in_ossa: false
because they are unclassified. Any mergedRelated-Bug:follow-up or prerequisite on
stable/2024.1now reads as "the fix is merged there".bool(related)inherits the same pollution:related_to_ossa(:780) is also set by the
subject-slug prefix heuristic at:770-771(guarded only bylen(slug) >= 20) and the bare
bug-set intersection at:772-773.
Failure case: fix merged only on master, plus one merged Related-Bug cleanup on
stable/2024.1 → the dossier says Covered = yes for 2024.1 and no author question is raised.
The dossier is an authoring input rather than published output, so a human is still in the loop —
but the loop is exactly what this suppresses: SKILL.md:90-95 and style.md:96-97 tell the author
to take the coverage decision from this column and to pick the "Fix in OSISM Images" wording from
it, and the ASK-THE-AUTHOR checkbox is the only prompt to do otherwise. The realistic outcome is a
published advisory saying "Upstream fix via the stable/2024.1 branch" for a release that has none.
Because related also feeds the osism_prs column, a fuzzy filename match can likewise put a
wrong PR number into Remediation.
Suggest making this tri-state rather than boolean:
if strong_match: # listed_in_ossa + MERGED, or Change-Id / cherry-pick lineage
covered = "yes"
elif rel_reviews or related: # slug match, bug intersection, or unlisted reviews only
covered = "unverified"
else:
covered = "no"Treat unverified exactly like no at :1355 (emit the checkbox) and render it as
**unverified** in the section-11 table. listed_in_ossa and closes_bugs are already
collected; they just are not used here. Please also downgrade the two fuzzy paths at :770-773
to an unverified marker on the patch entry instead of related_to_ossa: True — fixing only
fix_merged_upstream leaves the gate open on a filename match.
Erring toward unverified costs one extra author question. Erring the current way deletes the
question.
| points.append("Reviews referencing the bugs but not listed in the OSSA: " + ", ".join(str(r["number"]) for r in extra) | ||
| + " — decide whether they belong to this advisory (follow-ups, OSSN material).") | ||
| for b in d.get("bugs", []): | ||
| if b.get("information_type") and "Private" in b["information_type"]: |
There was a problem hiding this comment.
[P1] The embargo gate cannot fire for the case it exists for
This gate cannot fire for the case it is meant to catch. A genuinely private bug is unreadable:
collect_bugs gets a 404, stores {"error": "not readable"} with no information_type
(:471-475), and b.get("information_type") is falsy here — so the only trace is a warn() that
lands in section 13 among transport noise, while section 12 (which SKILL.md:55-56 drives the
stop decision from) stays silent.
To be precise: a 404 does not prove the bug is private — it may be private, deleted, renumbered,
or Launchpad may simply be unavailable. That is the argument for a positive test rather than a
negative one. The rule in sources.md:41 is already stated positively ("information_type must be
Public Security before anything is published"); the code implements a negative test, and a
narrower one than the rule, since Launchpad's Embargoed and Proprietary types contain no
"Private".
The banner at :1109-1111 does not cover this — it only fires when the OSSA change is unmerged,
it is prose rather than a checklist item, and it says nothing when the change is merged while a
referenced bug is still unreadable.
Invert to a positive check that also covers the unreadable case:
for b in d.get("bugs", []):
if b.get("information_type") != "Public Security":
points.append(f"Bug #{b['id']} is not confirmed *Public Security* "
f"(information type: {b.get('information_type') or b.get('error') or 'unknown'}) "
"— STOP: do not publish until public status is positively established.")The blocker has to land in points, not just in self.warnings.
| if not files: | ||
| self.warn("the change does not touch an ossa/OSSA-*.{yaml,rst} file") | ||
| else: | ||
| path = sorted(files)[-1] |
There was a problem hiding this comment.
[P1] A batched OSSA change resolves to the wrong advisory
sorted(files)[-1] ignores self.data["input"]["ossa_id"], even when the user asked by id and
resolve() used that id to find the change — and resolve()'s candidate filter
(any(ossa_id in f for f in files), :294) happily matches a multi-file change.
Batched changes are routine in openstack/ossa, three times in 2026 alone:
7fefe7f 2026-07-28 Add OSSA-2026-030 and OSSA-2026-031 (CVE-2026-pending)
b646815 2026-07-23 Add OSSA-2026-027 (CVE-2026-pending) and OSSA-2026-028 (CVE-2026-54422)
688a44d 2026-06-03 OSSA-2026-017, OSSA-2026-018, OSSA-2026-019
Ask for OSSA-2026-030 and you get a dossier for OSSA-2026-031 — a different vulnerability,
different CVE, different bugs — written to <output-dir>/OSSA-2026-031/, because parse_ossa
takes the id from the document body. The only trace is that dossier section 1 still shows the
requested reference while section 2 shows the substituted id; nothing flags the mismatch.
wanted = self.data["input"]["ossa_id"]
path = next((f for f in sorted(files) if wanted and wanted in f), sorted(files)[-1])
if len(files) > 1:
self.warn(f"the change touches {len(files)} OSSA files ({', '.join(sorted(files))}); using {path}")Two rules rather than one, though, because the no-id case has no right answer. When the reference
was a bare change number or Gerrit URL, ossa_id is None (:316), the fallback still picks
sorted(files)[-1], and :358-360 then derives the id from whichever file won — so the run
silently commits to one of several advisories with nothing to check it against. Please:
- an OSSA id was supplied → select the matching file; hard error if none matches;
- no id supplied and the change touches several
ossa/OSSA-*files → hard error naming them and
asking for a specific id.
Guessing is defensible when there is a stated target to check against. Here there isn't one.
|
|
||
| def github_json(self, path: str, allow_404: bool = False): | ||
| """GitHub REST API via `gh api` when available, urllib otherwise.""" | ||
| if not self.use_github: |
There was a problem hiding this comment.
[P1] --no-github fails silently
--no-github is the fallback SKILL.md:57 actively recommends, but it fails silently: this early
return emits no warning, and nothing records the flag in self.data or self.warnings, so
neither dossier.md nor raw.json shows that GitHub data is unknown rather than absent.
Downstream:
- the tree listing is empty →
built_releases == [](:738-740) - →
osism_buildsis false for every row (:1062) → every per-releaseASK THE AUTHOR
checkbox at:1355is suppressed - section 8 renders "No patch files for the affected products in any built release…" (
:1257),
indistinguishable from a genuine negative github_tag_datereturns nothing, so release dates vanishstyle.md:86-89derives the whole Affected Versions release list from that now-empty
"Built OpenStack releases" line
The blanket checkbox at :1351 still fires, which mitigates but does not replace the per-release
questions.
Suggest: warn once in __init__ when use_github is false, set self.data["github_skipped"] = True,
render every GitHub-derived field in sections 8/11 as unknown (GitHub not queried) rather than
-/no, and drop c["osism_builds"] and from the guard at :1355 in that mode so the coverage
questions stay unconditional.
| | Date | The `date` of the OSSA document (not the day the page is written). With errata: `2026-01-15 (Errata: 2026-01-16)`. | | ||
| | CVE | Linked ids `[CVE-…](https://www.cve.org/CVERecord?id=CVE-…)`, comma separated. Four or more ids: plain ids in the cell, links only in References. Not assigned yet: `CVE pending (requested from MITRE)`. | | ||
| | Severity | CVSS base severity from the CVE record when it contains one (`High`, `Medium`, …). Otherwise OSISM's assessment; then the Summary must contain the sentence below. A qualifier is allowed: `High (multi-pool deployments only)`. | | ||
| | Affected Project | Project name without "OpenStack": `Keystone`, `Nova`, `Neutron`, `Designate`, `keystonemiddleware`. | |
There was a problem hiding this comment.
[P2] No rule for multi-product advisories
The property table row is Affected Project with a singular value (Keystone, Nova), the
template mirrors that (template.md:20), and the index component is OpenStack <Project>
(style.md:206-207). But OSSAs routinely cover several deliverables — 14 of the 208 documents in
openstack/ossa, and not only historical ones:
OSSA-2026-036 2026-08 aodh, watcher
OSSA-2024-003 2024-09 Ironic, Ironic-Python-Agent
OSSA-2024-001 2024-07 Cinder, Glance, Nova
OSSA-2023-003 2023 cinder, glance_store, nova, os-brick
collect.py handles N products throughout — products is a list, section 9 renders one block per
product, build_coverage matches any of them. The style and template files are the only place
where the arity drops to one, so the model is left to improvise the property table, the Affected
Versions table (one table per product? one combined?), the check commands and the index row. All
seven existing advisories are single-product, so there is no exemplar to imitate either.
Doesn't need much: a sentence on the property-table row (comma-separated, order of the OSSA), one
on whether Affected Versions gets a table per product or a Project column, and one on the index
component. Worth settling before the first multi-product advisory rather than during it.
| @@ -0,0 +1,232 @@ | |||
| # Style and content rules for OSISM security advisories | |||
|
|
|||
| Distilled from the advisories in `docs/appendix/security/`. When in doubt, imitate the two newest | |||
There was a problem hiding this comment.
[Minor] Exemplar selection has three authorities that can diverge
SKILL.md:62-64— "the two exemplar advisories named in dossier section 10"style.md:3-4— "imitate the two newest files there; they are the canonical examples"sources.md:139— "the two newest are the style reference"
These are not inherently incompatible — once collect.py:947 sorts by filename (finding 9),
section 10 does name the two newest advisories and all three agree. They are redundant
authorities that can diverge: two of them define the exemplars independently of the dossier and
neither says what "newest" means, so before finding 9 is fixed they can already point at different
files, and afterwards they are three statements to keep in sync. One authority is enough — point
style.md:3-4 and sources.md:139 at dossier section 10.
|
|
||
| | Property | Rule | | ||
| |:-----------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | ||
| | Date | The `date` of the OSSA document (not the day the page is written). With errata: `2026-01-15 (Errata: 2026-01-16)`. | |
There was a problem hiding this comment.
[Minor] The errata Date form has no derivation rule
The mandated form is 2026-01-15 (Errata: 2026-01-16), but nothing says where the errata date
comes from. errata_history is free text, newest first — OSSA-2026-035 has:
errata_history:
- 2026-08-17 - Errata 1
- 2026-08-13 - Original Versionand the dossier prints it as a joined string (:1120). So the model has to infer that the OSSA
date field is the original and that the errata date is the leading token of the first entry.
That happens to be right here, but it is an inference, not a rule. One sentence in style.md
("the date field is the original publication date; the errata date is the newest entry in
errata_history, dossier section 2") removes it.
| with urllib.request.urlopen(req, timeout=TIMEOUT) as resp: | ||
| return resp.read() | ||
| except urllib.error.HTTPError as err: | ||
| if err.code == 404 and allow_404: |
There was a problem hiding this comment.
[P2] A real 404 and a failed request are indistinguishable to every caller
fetch returns None for an allowed 404 and for a 4xx, a 5xx that survives the retry, a
timeout, a transport error, and (via fetch_json) unparseable JSON. Fourteen call sites pass
allow_404=True, and several of them turn that None into a positive statement. Two matter:
CVE state (:694-697). A failed lookup becomes {"state": "not published"}. That drives
section 7, the section-12 point "<CVE> is not published on cve.org yet", and — because metrics
stays empty — the point "No CVSS score available — the Severity is OSISM's own assessment and must
say so". style.md:24 and style.md:28-31 then make the author write OSISM's own severity plus
the sentence "Upstream has not published a CVSS score for this issue." So a transient
cveawg.mitre.org failure is published as a claim about upstream, and the severity in the property
table changes as a result.
Deliverable lookup (:604-609). A failed fetch becomes {"error": "no deliverable file"},
which is also the legitimate answer for a project that did not exist in that series. It feeds the
section-6 table that SKILL.md:88-89 designates the sole authority for mapping versions to
series, plus released state and the section-11 matrix. An opendev outage renders as "upstream has
no such deliverable" for every release at once.
A warning is emitted in the failure cases (:197, :201) and not for a real 404, so the
information exists — but only in section 13, and the callers can't see it. Please make the
distinction reachable, e.g. return a sentinel for an allowed 404 and let everything else raise or
return a typed error, so collect_cves can render state: "lookup failed" and collect_releases
can render error: "lookup failed" distinctly from "no deliverable file". The other twelve sites
are mostly benign (or "" / or {} defaults), but the same sentinel makes them auditable.
| entry["series"] = rid | ||
| break | ||
| if entry["series"] is None: | ||
| major = ver.split(".")[0] |
There was a problem hiding this comment.
[P2] An unreleased fixed version is assigned to a series by major-number guess
When the exact fixed version isn't in any series' release list, the fallback takes the first series
containing any version with the same major number, iterating releases[product] in ascending
release-id order (:600) — so it picks the oldest matching series, not an authoritative one.
For the independently-versioned deliverables that actually appear in OSSAs, a major spans many
series:
keystonemiddleware 10.x → caracal, dalmatian, epoxy, flamingo (11.x/12.x only in gazpacho)
os-brick 6.x → caracal … gazpacho (five series)
python-keystoneclient 5.x → caracal … gazpacho (five series)
So an unreleased keystonemiddleware 10.13.0 is attributed to 2024.1 (Caracal) when the real
answer is Flamingo or later. entry["series"] lands in the section-6 "Fixed versions named by the
OSSA" table and gates fixed_here in build_coverage (:1040), so the Affected Versions table
shows the fixed version against the wrong OpenStack release. The == pinned path just below
(:651-665) is worse: it uses the same guessed series to pick fixed_in from "later versions with
this major" and sets released: True on it.
The irony is worth naming: SKILL.md:88-89 tells the model "the major version of a project does
not equal its OpenStack release — never map from memory", and this is the collector doing exactly
that inference and presenting the result as fact.
Collect all candidate series rather than the first, and when the set isn't a singleton report the
mapping as unverified with a section-12 checkbox — the same tri-state shape as finding 2. The
authoritative signal is the deliverable's branches list (already parsed at :616), which maps a
series to its stable/* branch directly.
Add a Claude Code skill that drafts an OSISM security advisory from a single OSSA reference. A collector script gathers the OSSA document, Launchpad bugs, upstream fix reviews and releases, CVE records, the OSISM container-images-kolla patches with their pull requests, kolla image tag variables and the local advisory conventions into a research dossier; the skill then writes the advisory page and index row in the established format and verifies it. Assisted-by: Claude:claude-fable-5 Signed-off-by: Christian Berendt <berendt@osism.tech>
Address the review of the security-advisory skill. The common defect was that a lookup which failed, or evidence that was only a hint, ended up in the dossier as a definite statement. Collector: - Tell an allowed 404 (confirmed negative) and a failed request apart. CVE records, deliverable files, Launchpad bugs, kolla image listings and the registry now report "lookup failed" / "unknown" and add an open point instead of "not published", "no deliverable file" or "do not list it in the override". - Normalise product names to the deliverable name (Octavia -> octavia), split fields naming several products and flag names that are no deliverable. - Select the OSSA file of a batched change by the requested id; a missing id or a missing match is a hard error instead of a guess. - Make the coverage tri-state. Only a fix review listed in the OSSA (or a backport with its Change-Id) and a patch file carrying that Change-Id count as covered; file name and bug number matches and merged reviews that merely mention the bug are "unverified" and are asked like "no". - Check the need-to-know rule positively: every bug that is not confirmed Public Security, including an unreadable one, is a STOP point in section 12. - Record --no-github, render GitHub data as unknown and keep the coverage questions unconditional in that mode. - Follow renames back to the commit that added a patch file instead of citing the renumbering commit. - Replace the regex fallback with a YAML subset parser that agrees with PyYAML on all documents of openstack/ossa, and warn when PyYAML is missing. Notes, errata and errata history are no longer dropped. - Do not map an unreleased version to a series by its major version; report the candidates as unverified. - Choose exemplars by file name instead of mtime, fail when the checkout is not found, use a private temporary directory by default and derive the kolla reference branch from the series status. Documentation: state the publication rule once (style.md) and make it strict, drop the hardcoded release lists from style.md and template.md, add rules for advisories covering several projects, the errata date and inclusive upper bounds, and name dossier section 10 as the only authority for the exemplars. Narrow allowed-tools to the hosts and read-only endpoints the workflow uses and add the file tools. Add scripts/test_collect.py (standard library only) and run it in the Build workflow. Assisted-by: Claude:claude-fable-5-1 Signed-off-by: Christian Berendt <berendt@osism.tech>
OSSA-2026-038 was published with the override `glance_image: "registry.osism.tech/kolla/glance"` and had to be corrected in #1086: neither the parameter nor the image exists, it is `glance_api_image` with `kolla/glance-api`. The parameter names do not follow one pattern (`keystone_image`, but `glance_api_image` and `keystone_httpd_image` -> `httpd`); the only authority for them is all/002-images-kolla.yml in osism/defaults. The dossier listed the right names, but nothing checked the finished page against that file. - Add scripts/check_images.py. It compares every `*_image` / `*_tag` line in the YAML blocks of an advisory with the defaults file: the parameter must exist, the image name must match and use the rolling `kolla` namespace, and in a block titled environments/kolla/images.yml a tag override needs the image parameters it governs and vice versa. Exit status 2 means that the defaults file could not be loaded and nothing was verified. - Run it for all advisories in the Build workflow, so that hand-written advisories are covered as well. - Generate a ready-to-copy override snippet from osism/defaults in dossier section 9; images confirmed missing from the rolling registry are left out, failed lookups are marked. - SKILL.md, style.md, template.md and sources.md: copy the names from that snippet and never derive them by analogy with another service; run the check in the verify step. Assisted-by: Claude:claude-fable-5-1 Signed-off-by: Christian Berendt <berendt@osism.tech>
23d77b7 to
d5864b0
Compare
|
#1089 generated with the current state of this PR. |
Adds a project-level Claude Code skill (
.claude/skills/security-advisory/) that drafts an OSISMsecurity advisory from a single OSSA reference — a review.opendev.org change URL or number, an
OSSA id, or a security.openstack.org URL — for example
/security-advisory https://review.opendev.org/c/openstack/ossa/+/1002324.scripts/collect.py(stdlib only, PyYAML optional) resolves the OSSA change on Gerrit andcollects into a research dossier: the OSSA YAML, the Launchpad bugs (including whether they are
public), every upstream fix review per branch with status, upstream series status and released
versions from
openstack/releases(the series of a version is derived from the deliverables,never guessed), CVE records with CVSS, the OSISM patches in
container-images-kollamapped totheir pull requests and upstream reviews via the patch
Change-Id, CHANGELOG and PR search hits,kolla image names, kolla-ansible tag variables, the OSISM image parameters from
osism/defaultswith the tags available in the rolling registry, and the local advisory conventions and release
support status. It ends with a coverage matrix per OpenStack release and a list of open points.
SKILL.mdis the workflow: collect → read the conventions and the two newest advisories → readthe upstream diff → decide coverage (all releases OSISM builds images for; a release without a
fix is never guessed but clarified with the author) → write page and index row → verify (lint,
table format, links) → report with a proposed commit message. Nothing is committed without an
explicit yes.
reference/style.mddistils the conventions of the existing advisories (property table,status phrases, table variants, check commands, remediation with
*_tagand*_imageoverrides in the rolling
kollanamespace, reference order, commit message);reference/template.mdis the skeleton;reference/sources.mdlists the verified API commands.Tested against OSSA-2026-037 (new page, see #1075) and OSSA-2026-005 (existing page, update
mode). The files pass markdownlint, markdown-table-formatter and codespell; the script passes
ruff.
Assisted-by: Claude:claude-fable-5
🤖 Generated with Claude Code