Merge branch 'main' of https://github.com/aaronaxvig/firewallissues
This commit is contained in:
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"liveServer.settings.root": "/web"
|
||||||
|
}
|
||||||
@@ -10,7 +10,8 @@ Known and addressed issues for newly released versions are easily added using th
|
|||||||
- Copy the issue table's HTML from the webpage using devtools.
|
- Copy the issue table's HTML from the webpage using devtools.
|
||||||
- Fill out the process.html page's fields and paste in the table HTML.
|
- Fill out the process.html page's fields and paste in the table HTML.
|
||||||
- Download the Markdown file and put it into the correct folder.
|
- Download the Markdown file and put it into the correct folder.
|
||||||
- Fix up the products.json file manually or run `update_products_from_issues.py`.
|
- Run `npm run update:generated` to update `products.json`, append new stable
|
||||||
|
product-tree manifest slots, and regenerate rendered test fixtures.
|
||||||
- Submit a pull request.
|
- Submit a pull request.
|
||||||
|
|
||||||
There is intentionally no automated scaping of Palo Alto's website, to avoid abuse of server resources. Also releases are not that frequent. A crawler to grab some data from the Common Crawl dataset was started but never really finished.
|
There is intentionally no automated scaping of Palo Alto's website, to avoid abuse of server resources. Also releases are not that frequent. A crawler to grab some data from the Common Crawl dataset was started but never really finished.
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node --test",
|
"test": "node --test",
|
||||||
|
"update:products": "node scripts/update-products-from-issues.mjs",
|
||||||
|
"update:fixtures": "node scripts/generate-rendered-fixtures.mjs",
|
||||||
|
"update:generated": "node scripts/refresh-generated-data.mjs",
|
||||||
"vendor:marked": "mkdir -p web/vendor && cp node_modules/marked/lib/marked.esm.js web/vendor/marked.esm.js"
|
"vendor:marked": "mkdir -p web/vendor && cp node_modules/marked/lib/marked.esm.js web/vendor/marked.esm.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/**
|
/**
|
||||||
* Generates expected-parsed.json for each fixture directory that contains expected.md.
|
* Generates expected-parsed.json for each fixture directory that contains expected.md.
|
||||||
* Run manually: node test/generate-rendered-fixtures.mjs
|
* Run manually: node scripts/generate-rendered-fixtures.mjs
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { readFileSync, writeFileSync, readdirSync } from 'node:fs';
|
import { readFileSync, writeFileSync, readdirSync } from 'node:fs';
|
||||||
import { join } from 'node:path';
|
import { join, resolve } from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||||
import { JSDOM } from 'jsdom';
|
import { JSDOM } from 'jsdom';
|
||||||
|
|
||||||
const { window } = new JSDOM('<!doctype html><html><body></body></html>');
|
const { window } = new JSDOM('<!doctype html><html><body></body></html>');
|
||||||
@@ -20,14 +20,15 @@ globalThis.fetch = () => Promise.resolve({
|
|||||||
const { parseMarkdownIssues, markdownSummaryToHtml } = await import('../web/js/issues.js');
|
const { parseMarkdownIssues, markdownSummaryToHtml } = await import('../web/js/issues.js');
|
||||||
|
|
||||||
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||||
const fixturesDir = join(__dirname, '../test/fixtures');
|
const defaultFixturesDir = join(__dirname, '../test/fixtures');
|
||||||
|
|
||||||
const fixtures = readdirSync(fixturesDir).filter(name => {
|
export function generateRenderedFixtures(fixturesDir = defaultFixturesDir) {
|
||||||
|
const fixtures = readdirSync(fixturesDir).filter(name => {
|
||||||
const contents = readdirSync(join(fixturesDir, name)).map(d => d);
|
const contents = readdirSync(join(fixturesDir, name)).map(d => d);
|
||||||
return contents.includes('expected.md');
|
return contents.includes('expected.md');
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const fixtureId of fixtures) {
|
for (const fixtureId of fixtures) {
|
||||||
const fixturePath = join(fixturesDir, fixtureId);
|
const fixturePath = join(fixturesDir, fixtureId);
|
||||||
const markdownText = readFileSync(join(fixturePath, 'expected.md'), 'utf-8');
|
const markdownText = readFileSync(join(fixturePath, 'expected.md'), 'utf-8');
|
||||||
const parsed = parseMarkdownIssues(markdownText);
|
const parsed = parseMarkdownIssues(markdownText);
|
||||||
@@ -47,4 +48,10 @@ for (const fixtureId of fixtures) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
console.log(`Written ${fixtureId}/expected-parsed.json (${result.length} issues)`);
|
console.log(`Written ${fixtureId}/expected-parsed.json (${result.length} issues)`);
|
||||||
|
}
|
||||||
|
return { fixtures: fixtures.length };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
||||||
|
generateRenderedFixtures();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import { runUpdateProductsCli } from './update-products-from-issues.mjs';
|
||||||
|
import { generateRenderedFixtures } from './generate-rendered-fixtures.mjs';
|
||||||
|
|
||||||
|
await runUpdateProductsCli();
|
||||||
|
const fixtureResult = generateRenderedFixtures();
|
||||||
|
console.log(`Regenerated ${fixtureResult.fixtures} rendered fixture set(s).`);
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import { readdir, readFile, writeFile } from 'node:fs/promises';
|
||||||
|
import { dirname, join, relative, resolve } from 'node:path';
|
||||||
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||||
|
import { buildProductTree, enumerateTreePaths } from '../web/js/product-tree-model.js';
|
||||||
|
|
||||||
|
const ISSUE_TYPES = new Set(['addressed', 'known']);
|
||||||
|
const VERSION_FILENAME_RE = /^(?<version>[^.]+(?:\.[^.]+)*)\.md$/;
|
||||||
|
const HOTFIX_RE = /^(?<base>\d+(?:\.\d+)*)(?:-h(?<hotfix>\d+))?$/;
|
||||||
|
const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
||||||
|
|
||||||
|
async function walkFiles(root) {
|
||||||
|
const files = [];
|
||||||
|
async function visit(directory) {
|
||||||
|
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
||||||
|
const path = join(directory, entry.name);
|
||||||
|
if (entry.isDirectory()) await visit(path);
|
||||||
|
else if (entry.isFile()) files.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await visit(root);
|
||||||
|
return files.sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function collectIssueFiles(issuesDir) {
|
||||||
|
const records = [];
|
||||||
|
for (const filePath of await walkFiles(issuesDir)) {
|
||||||
|
const parts = relative(issuesDir, filePath).split(/[\\/]/);
|
||||||
|
if (parts.length !== 3) continue;
|
||||||
|
const [product, issueType, filename] = parts;
|
||||||
|
if (!ISSUE_TYPES.has(issueType)) continue;
|
||||||
|
const match = VERSION_FILENAME_RE.exec(filename);
|
||||||
|
if (!match) continue;
|
||||||
|
records.push({ product, issueType, version: match.groups.version, filename });
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearLeafIssueArrays(node) {
|
||||||
|
if (!node || typeof node !== 'object' || Array.isArray(node)) return;
|
||||||
|
if ('addressed' in node || 'known' in node) {
|
||||||
|
node.addressed = [];
|
||||||
|
node.known = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object.values(node).forEach(clearLeafIssueArrays);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPath(data, path) {
|
||||||
|
let current = data;
|
||||||
|
for (const key of path) {
|
||||||
|
if (!current || typeof current !== 'object' || !(key in current)) return null;
|
||||||
|
current = current[key];
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isIssueLeaf(node) {
|
||||||
|
return Boolean(node) && typeof node === 'object' && ('addressed' in node || 'known' in node);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pickTargetLeafPath(products, product, version) {
|
||||||
|
const parts = version.split('.');
|
||||||
|
const major = parts[0];
|
||||||
|
const minor = parts.length > 1 ? `${parts[0]}.${parts[1]}` : major;
|
||||||
|
for (const path of [[product, major, minor], [product, major], [product]]) {
|
||||||
|
if (isIssueLeaf(getPath(products, path))) return path;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseReleasePrefix(prefix) {
|
||||||
|
const match = HOTFIX_RE.exec(prefix);
|
||||||
|
if (!match) return { base: [], hotfix: -1 };
|
||||||
|
return {
|
||||||
|
base: match.groups.base.split('.').map(Number),
|
||||||
|
hotfix: match.groups.hotfix === undefined ? -1 : Number(match.groups.hotfix)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareNumberArrays(left, right) {
|
||||||
|
const length = Math.max(left.length, right.length);
|
||||||
|
for (let index = 0; index < length; index++) {
|
||||||
|
const difference = (left[index] ?? -1) - (right[index] ?? -1);
|
||||||
|
if (difference) return difference;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareIssueFilenames(left, right) {
|
||||||
|
const leftPrefix = left.replace(/\.[^.]+$/, '');
|
||||||
|
const rightPrefix = right.replace(/\.[^.]+$/, '');
|
||||||
|
const leftVersion = parseReleasePrefix(leftPrefix);
|
||||||
|
const rightVersion = parseReleasePrefix(rightPrefix);
|
||||||
|
return compareNumberArrays(leftVersion.base, rightVersion.base) ||
|
||||||
|
Number(leftVersion.hotfix >= 0) - Number(rightVersion.hotfix >= 0) ||
|
||||||
|
leftVersion.hotfix - rightVersion.hotfix ||
|
||||||
|
left.localeCompare(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function rebuildProducts(products, records) {
|
||||||
|
clearLeafIssueArrays(products);
|
||||||
|
const unmatched = [];
|
||||||
|
records.sort((left, right) =>
|
||||||
|
left.product.localeCompare(right.product) ||
|
||||||
|
left.issueType.localeCompare(right.issueType) ||
|
||||||
|
left.version.localeCompare(right.version)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const record of records) {
|
||||||
|
const path = pickTargetLeafPath(products, record.product, record.version);
|
||||||
|
if (!path) {
|
||||||
|
unmatched.push(record);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const leaf = getPath(products, path);
|
||||||
|
const files = Array.isArray(leaf[record.issueType]) ? leaf[record.issueType] : [];
|
||||||
|
leaf[record.issueType] = Array.from(new Set([...files, record.filename])).sort(compareIssueFilenames);
|
||||||
|
}
|
||||||
|
return { products, unmatched };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateManifest(manifest, currentPaths) {
|
||||||
|
if (!manifest || manifest.schema !== 1 || !Array.isArray(manifest.nodes)) {
|
||||||
|
throw new Error('Product-tree manifest must use schema 1 with a nodes array.');
|
||||||
|
}
|
||||||
|
const active = new Set();
|
||||||
|
manifest.nodes.forEach((path, slot) => {
|
||||||
|
if (path === null) return;
|
||||||
|
if (typeof path !== 'string' || !path) throw new Error(`Invalid manifest entry at slot ${slot}.`);
|
||||||
|
if (active.has(path)) throw new Error(`Duplicate manifest path: ${path}`);
|
||||||
|
active.add(path);
|
||||||
|
});
|
||||||
|
|
||||||
|
const current = new Set(currentPaths);
|
||||||
|
const appended = currentPaths.filter(path => !active.has(path));
|
||||||
|
manifest.nodes.push(...appended);
|
||||||
|
const missing = Array.from(active).filter(path => !current.has(path));
|
||||||
|
return { manifest, appended, missing };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateProductsFromIssues({ issuesDir, productsPath, manifestPath }) {
|
||||||
|
const [productsText, manifestText, records] = await Promise.all([
|
||||||
|
readFile(productsPath, 'utf8'),
|
||||||
|
readFile(manifestPath, 'utf8'),
|
||||||
|
collectIssueFiles(issuesDir)
|
||||||
|
]);
|
||||||
|
if (records.length === 0) {
|
||||||
|
throw new Error("No '<version>.md' issue files found; refusing to rewrite products.json");
|
||||||
|
}
|
||||||
|
const products = JSON.parse(productsText);
|
||||||
|
if (!products || typeof products !== 'object' || Array.isArray(products)) {
|
||||||
|
throw new Error('products.json must contain a top-level object.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const rebuilt = rebuildProducts(products, records);
|
||||||
|
const paths = enumerateTreePaths(buildProductTree(rebuilt.products));
|
||||||
|
const manifestUpdate = updateManifest(JSON.parse(manifestText), paths);
|
||||||
|
await writeFile(productsPath, `${JSON.stringify(rebuilt.products, null, 2)}\n`, 'utf8');
|
||||||
|
if (manifestUpdate.appended.length > 0) {
|
||||||
|
await writeFile(manifestPath, `${JSON.stringify(manifestUpdate.manifest, null, 2)}\n`, 'utf8');
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
records: records.length,
|
||||||
|
unmatched: rebuilt.unmatched,
|
||||||
|
appended: manifestUpdate.appended,
|
||||||
|
missing: manifestUpdate.missing
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseArgs(argv) {
|
||||||
|
const options = {
|
||||||
|
issuesDir: join(REPO_ROOT, 'web/data/issues'),
|
||||||
|
productsPath: join(REPO_ROOT, 'web/data/products.json'),
|
||||||
|
manifestPath: join(REPO_ROOT, 'web/data/product-tree-manifest.json')
|
||||||
|
};
|
||||||
|
const flags = new Map([
|
||||||
|
['--issues-dir', 'issuesDir'],
|
||||||
|
['--products-json', 'productsPath'],
|
||||||
|
['--manifest-json', 'manifestPath']
|
||||||
|
]);
|
||||||
|
for (let index = 0; index < argv.length; index += 2) {
|
||||||
|
const property = flags.get(argv[index]);
|
||||||
|
if (!property || argv[index + 1] === undefined) throw new Error(`Invalid argument: ${argv[index] ?? ''}`);
|
||||||
|
options[property] = resolve(argv[index + 1]);
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function runUpdateProductsCli(argv = process.argv.slice(2)) {
|
||||||
|
const options = parseArgs(argv);
|
||||||
|
const result = await updateProductsFromIssues(options);
|
||||||
|
console.log(`Updated ${options.productsPath} from ${result.records} issue files.`);
|
||||||
|
console.log(`Appended ${result.appended.length} manifest path(s).`);
|
||||||
|
if (result.missing.length) console.warn(`${result.missing.length} manifest path(s) are no longer rendered; slots were preserved.`);
|
||||||
|
if (result.unmatched.length) console.warn(`${result.unmatched.length} issue file(s) did not match a products.json leaf.`);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
||||||
|
await runUpdateProductsCli();
|
||||||
|
}
|
||||||
@@ -1,217 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""Update products.json entries from discovered issue data files."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import json
|
|
||||||
import re
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
|
|
||||||
ISSUE_TYPES = {"addressed", "known"}
|
|
||||||
VERSION_FILENAME_RE = re.compile(r"^(?P<version>[^.]+(?:\.[^.]+)*)\.md$")
|
|
||||||
HOTFIX_RE = re.compile(r"^(?P<base>\d+(?:\.\d+)*)(?:-h(?P<hotfix>\d+))?$")
|
|
||||||
|
|
||||||
|
|
||||||
def parse_args() -> argparse.Namespace:
|
|
||||||
"""Parse command line arguments for input and output paths."""
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description="Update products.json from issue files."
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--issues-dir",
|
|
||||||
default="web/data/issues",
|
|
||||||
help="Directory containing product issue files.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--products-json",
|
|
||||||
default="web/data/products.json",
|
|
||||||
help="Path to products.json.",
|
|
||||||
)
|
|
||||||
return parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
def collect_issue_files(issues_dir: Path) -> list[tuple[str, str, str, str]]:
|
|
||||||
"""Collect all issue files with '<version>.md' naming."""
|
|
||||||
|
|
||||||
records: list[tuple[str, str, str, str]] = []
|
|
||||||
|
|
||||||
for file_path in sorted(p for p in issues_dir.rglob("*") if p.is_file()):
|
|
||||||
relative = file_path.relative_to(issues_dir)
|
|
||||||
if len(relative.parts) != 3:
|
|
||||||
continue
|
|
||||||
|
|
||||||
product, issue_type, filename = relative.parts
|
|
||||||
if issue_type not in ISSUE_TYPES:
|
|
||||||
continue
|
|
||||||
|
|
||||||
version = parse_issue_filename(filename)
|
|
||||||
if version is None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
records.append((product, issue_type, version, filename))
|
|
||||||
|
|
||||||
return records
|
|
||||||
|
|
||||||
|
|
||||||
def parse_issue_filename(filename: str) -> str | None:
|
|
||||||
"""Parse '<version>.md' issue filenames and return version."""
|
|
||||||
|
|
||||||
match = VERSION_FILENAME_RE.match(filename)
|
|
||||||
if not match:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return match.group("version")
|
|
||||||
|
|
||||||
|
|
||||||
def clear_leaf_issue_arrays(node: Any) -> None:
|
|
||||||
"""Reset addressed/known arrays on every issue leaf in products.json."""
|
|
||||||
|
|
||||||
if isinstance(node, dict):
|
|
||||||
if "addressed" in node or "known" in node:
|
|
||||||
node["addressed"] = []
|
|
||||||
node["known"] = []
|
|
||||||
return
|
|
||||||
|
|
||||||
for value in node.values():
|
|
||||||
clear_leaf_issue_arrays(value)
|
|
||||||
|
|
||||||
|
|
||||||
def is_issue_leaf(node: Any) -> bool:
|
|
||||||
"""Return whether a node is a leaf containing issue arrays."""
|
|
||||||
|
|
||||||
return isinstance(node, dict) and ("addressed" in node or "known" in node)
|
|
||||||
|
|
||||||
|
|
||||||
def pick_target_leaf_path(products: dict[str, Any], product: str, version: str) -> list[str] | None:
|
|
||||||
"""Choose the best matching product tree path for a version."""
|
|
||||||
|
|
||||||
parts = version.split(".")
|
|
||||||
if not parts:
|
|
||||||
return None
|
|
||||||
|
|
||||||
major = parts[0]
|
|
||||||
minor = f"{parts[0]}.{parts[1]}" if len(parts) > 1 else parts[0]
|
|
||||||
|
|
||||||
candidates = [
|
|
||||||
[product, major, minor],
|
|
||||||
[product, major],
|
|
||||||
[product],
|
|
||||||
]
|
|
||||||
|
|
||||||
for path in candidates:
|
|
||||||
node = get_path(products, path)
|
|
||||||
if is_issue_leaf(node):
|
|
||||||
return path
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def get_path(data: Any, path: list[str]) -> Any:
|
|
||||||
"""Traverse nested dict keys and return the value at path, if present."""
|
|
||||||
|
|
||||||
current = data
|
|
||||||
for key in path:
|
|
||||||
if not isinstance(current, dict) or key not in current:
|
|
||||||
return None
|
|
||||||
current = current[key]
|
|
||||||
return current
|
|
||||||
|
|
||||||
|
|
||||||
def parse_release_prefix(prefix: str) -> tuple[tuple[int, ...], int]:
|
|
||||||
"""Extract numeric base version parts and optional hotfix number."""
|
|
||||||
|
|
||||||
match = HOTFIX_RE.match(prefix)
|
|
||||||
if not match:
|
|
||||||
return tuple(), -1
|
|
||||||
|
|
||||||
base_parts = tuple(int(part) for part in match.group("base").split("."))
|
|
||||||
hotfix_group = match.group("hotfix")
|
|
||||||
hotfix = int(hotfix_group) if hotfix_group is not None else -1
|
|
||||||
return base_parts, hotfix
|
|
||||||
|
|
||||||
|
|
||||||
def filename_sort_key(filename: str) -> tuple[tuple[int, ...], int, int, str]:
|
|
||||||
"""Build a sort key that orders base versions before hotfixes."""
|
|
||||||
|
|
||||||
prefix = filename.rsplit(".", 1)[0]
|
|
||||||
base_parts, hotfix = parse_release_prefix(prefix)
|
|
||||||
is_hotfix = 1 if hotfix >= 0 else 0
|
|
||||||
return base_parts, is_hotfix, hotfix, filename
|
|
||||||
|
|
||||||
|
|
||||||
def add_file_to_leaf(
|
|
||||||
products: dict[str, Any],
|
|
||||||
path: list[str],
|
|
||||||
issue_type: str,
|
|
||||||
filename: str,
|
|
||||||
) -> None:
|
|
||||||
"""Insert an issue filename into the target leaf with stable sorting."""
|
|
||||||
|
|
||||||
leaf = get_path(products, path)
|
|
||||||
if not isinstance(leaf, dict):
|
|
||||||
return
|
|
||||||
|
|
||||||
files = leaf.get(issue_type)
|
|
||||||
if not isinstance(files, list):
|
|
||||||
files = []
|
|
||||||
|
|
||||||
files.append(filename)
|
|
||||||
leaf[issue_type] = sorted(set(files), key=filename_sort_key)
|
|
||||||
|
|
||||||
|
|
||||||
def update_products(issues_dir: Path, products_path: Path) -> None:
|
|
||||||
"""Rebuild product issue file lists from crawled issue files."""
|
|
||||||
|
|
||||||
if not issues_dir.is_dir():
|
|
||||||
raise FileNotFoundError(f"Issues directory not found: {issues_dir}")
|
|
||||||
if not products_path.is_file():
|
|
||||||
raise FileNotFoundError(f"products.json not found: {products_path}")
|
|
||||||
|
|
||||||
with products_path.open("r", encoding="utf-8") as f:
|
|
||||||
products = json.load(f)
|
|
||||||
|
|
||||||
if not isinstance(products, dict):
|
|
||||||
raise ValueError("products.json must contain a top-level object")
|
|
||||||
|
|
||||||
records = collect_issue_files(issues_dir)
|
|
||||||
if not records:
|
|
||||||
raise ValueError(
|
|
||||||
"No '<version>.md' issue files found; refusing to rewrite products.json"
|
|
||||||
)
|
|
||||||
|
|
||||||
clear_leaf_issue_arrays(products)
|
|
||||||
|
|
||||||
for product, issue_type, version, filename in sorted(
|
|
||||||
records,
|
|
||||||
key=lambda r: (r[0], r[1], r[2]),
|
|
||||||
):
|
|
||||||
path = pick_target_leaf_path(products, product, version)
|
|
||||||
if path is None:
|
|
||||||
continue
|
|
||||||
add_file_to_leaf(products, path, issue_type, filename)
|
|
||||||
|
|
||||||
with products_path.open("w", encoding="utf-8") as f:
|
|
||||||
json.dump(products, f, indent=2)
|
|
||||||
f.write("\n")
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
"""Entrypoint for command-line usage."""
|
|
||||||
|
|
||||||
args = parse_args()
|
|
||||||
script_dir = Path(__file__).resolve().parent
|
|
||||||
repo_root = script_dir.parent
|
|
||||||
issues_dir = (repo_root / args.issues_dir).resolve()
|
|
||||||
products_path = (repo_root / args.products_json).resolve()
|
|
||||||
|
|
||||||
update_products(issues_dir=issues_dir, products_path=products_path)
|
|
||||||
print(f"Updated {products_path} from {issues_dir}")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
raise SystemExit(main())
|
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import test from 'node:test';
|
||||||
|
import { JSDOM } from 'jsdom';
|
||||||
|
|
||||||
|
const dom = new JSDOM('<!doctype html><html><body></body></html>', { url: 'https://example.test/' });
|
||||||
|
globalThis.window = dom.window;
|
||||||
|
globalThis.document = dom.window.document;
|
||||||
|
globalThis.Element = dom.window.Element;
|
||||||
|
globalThis.localStorage = dom.window.localStorage;
|
||||||
|
|
||||||
|
const { CheckboxTree } = await import('../web/vendor/checkbox-tree/checkbox-tree.js');
|
||||||
|
|
||||||
|
function createTree(options = {}) {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
document.body.replaceChildren(container);
|
||||||
|
const tree = new CheckboxTree(container, options);
|
||||||
|
tree.setData([
|
||||||
|
{
|
||||||
|
id: 'parent',
|
||||||
|
label: 'Parent',
|
||||||
|
metadata: { kind: 'parent' },
|
||||||
|
children: [
|
||||||
|
{ id: 'first', label: 'First', metadata: { kind: 'leaf' } },
|
||||||
|
{ id: 'second', label: 'Second', metadata: { kind: 'leaf' } }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
return { container, tree };
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkbox(container, id) {
|
||||||
|
return container.querySelector(`.checkbox-tree__checkbox[data-node-id="${id}"]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
test('selecting a parent selects all descendants and emits selected nodes', () => {
|
||||||
|
let change;
|
||||||
|
const { container, tree } = createTree({ onSelectionChange: event => { change = event; } });
|
||||||
|
|
||||||
|
const parent = checkbox(container, 'parent');
|
||||||
|
parent.checked = true;
|
||||||
|
parent.dispatchEvent(new window.Event('change', { bubbles: true }));
|
||||||
|
|
||||||
|
assert.deepEqual(tree.getSelectedIds(), ['parent', 'first', 'second']);
|
||||||
|
assert.deepEqual(change.selectedIds, ['parent', 'first', 'second']);
|
||||||
|
assert.equal(change.selectedNodes[1].metadata.kind, 'leaf');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('partial child selection makes the parent indeterminate', () => {
|
||||||
|
const { container, tree } = createTree();
|
||||||
|
const first = checkbox(container, 'first');
|
||||||
|
first.checked = true;
|
||||||
|
first.dispatchEvent(new window.Event('change', { bubbles: true }));
|
||||||
|
|
||||||
|
const parent = checkbox(container, 'parent');
|
||||||
|
assert.equal(parent.checked, false);
|
||||||
|
assert.equal(parent.indeterminate, true);
|
||||||
|
assert.deepEqual(tree.getSelectedIds(), ['first']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can initially select every selectable node', () => {
|
||||||
|
const { tree } = createTree({ initiallySelected: true });
|
||||||
|
assert.deepEqual(tree.getSelectedIds(), ['parent', 'first', 'second']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('persists selection and collapsed branches per configured key', () => {
|
||||||
|
localStorage.clear();
|
||||||
|
const first = createTree({ storageKey: 'example-tree' });
|
||||||
|
checkbox(first.container, 'first').click();
|
||||||
|
|
||||||
|
const toggle = first.container.querySelector('.checkbox-tree__toggle');
|
||||||
|
toggle.click();
|
||||||
|
assert.equal(toggle.getAttribute('aria-expanded'), 'true');
|
||||||
|
|
||||||
|
const second = createTree({ storageKey: 'example-tree' });
|
||||||
|
assert.deepEqual(second.tree.getSelectedIds(), ['first']);
|
||||||
|
assert.equal(second.container.querySelector('.checkbox-tree__toggle').getAttribute('aria-expanded'), 'true');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('supports independent instances and removes listeners on destroy', () => {
|
||||||
|
const firstContainer = document.createElement('div');
|
||||||
|
const secondContainer = document.createElement('div');
|
||||||
|
document.body.replaceChildren(firstContainer, secondContainer);
|
||||||
|
const first = new CheckboxTree(firstContainer);
|
||||||
|
const second = new CheckboxTree(secondContainer);
|
||||||
|
const data = [{ id: 'node', label: 'Node' }];
|
||||||
|
first.setData(data);
|
||||||
|
second.setData(data);
|
||||||
|
|
||||||
|
checkbox(firstContainer, 'node').click();
|
||||||
|
assert.deepEqual(first.getSelectedIds(), ['node']);
|
||||||
|
assert.deepEqual(second.getSelectedIds(), []);
|
||||||
|
|
||||||
|
first.destroy();
|
||||||
|
assert.equal(firstContainer.childElementCount, 0);
|
||||||
|
assert.equal(firstContainer.classList.contains('checkbox-tree'), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rejects duplicate node ids', () => {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
const tree = new CheckboxTree(container);
|
||||||
|
assert.throws(
|
||||||
|
() => tree.setData([{ id: 'same', label: 'One' }, { id: 'same', label: 'Two' }]),
|
||||||
|
/Duplicate CheckboxTree node id/
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "PAN-242777",
|
||||||
|
"summary": "Fixed an issue where users previously reported limitations due to session count caps when utilizing **Web Proxy** features on PA-5400 Series Firewalls. To address these performance complaints and support higher traffic volumes, we have increased the maximum session capacity on specific **PA-5400F** series platforms, leveraging available system memory. This update ensures greater capacity and stability for high-volume environments.The supported session limits are:| Platform | Max Sessions |\n| -------- | ------------ |\n| PA-5410 | 95K |\n| PA-5420 | 95K |\n| PA-5430 | 95K |\n| PA-5440 | 225K |\n| PA-5445 | 250K |\n| PA-5450 | 1.28M |",
|
||||||
|
"resolved": "",
|
||||||
|
"caveat": "",
|
||||||
|
"renderedHtml": "<p>Fixed an issue where users previously reported limitations due to session count caps when utilizing <strong>Web Proxy</strong> features on PA-5400 Series Firewalls. To address these performance complaints and support higher traffic volumes, we have increased the maximum session capacity on specific <strong>PA-5400F</strong> series platforms, leveraging available system memory. This update ensures greater capacity and stability for high-volume environments.The supported session limits are:| Platform | Max Sessions |\n| -------- | ------------ |\n| PA-5410 | 95K |\n| PA-5420 | 95K |\n| PA-5430 | 95K |\n| PA-5440 | 225K |\n| PA-5445 | 250K |\n| PA-5450 | 1.28M |</p>\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "PAN-291499",
|
||||||
|
"summary": "Fixed an issue where newly deployed firewalls were unable to connect to the Strata Logging Service (SLS) until after a reboot, license fetch, or management server restart.",
|
||||||
|
"resolved": "",
|
||||||
|
"caveat": "VM-Series firewalls on Amazon Web Services (AWS) environments only",
|
||||||
|
"renderedHtml": "<div><em>Caveat: VM-Series firewalls on Amazon Web Services (AWS) environments only</em></div><p>Fixed an issue where newly deployed firewalls were unable to connect to the Strata Logging Service (SLS) until after a reboot, license fetch, or management server restart.</p>\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "PAN-288726",
|
||||||
|
"summary": "Fixed an issue where the useridd process stopped responding due to a Security policy rule ID being set to 0, which caused the last configuration retrieval to fail.",
|
||||||
|
"resolved": "",
|
||||||
|
"caveat": "",
|
||||||
|
"renderedHtml": "<p>Fixed an issue where the useridd process stopped responding due to a Security policy rule ID being set to 0, which caused the last configuration retrieval to fail.</p>\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "PAN-287133",
|
||||||
|
"summary": "Fixed an issue on the Panorama web interface where assigning a policy rule to a group at the top or bottom of the list changed the order of other policy rules.",
|
||||||
|
"resolved": "",
|
||||||
|
"caveat": "",
|
||||||
|
"renderedHtml": "<p>Fixed an issue on the Panorama web interface where assigning a policy rule to a group at the top or bottom of the list changed the order of other policy rules.</p>\n"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
import { tmpdir } from 'node:os';
|
||||||
|
import test from 'node:test';
|
||||||
|
import { buildProductTree, enumerateTreePaths } from '../web/js/product-tree-model.js';
|
||||||
|
import { updateProductsFromIssues } from '../scripts/update-products-from-issues.mjs';
|
||||||
|
|
||||||
|
test('product updater rebuilds products and append-only manifest idempotently', async t => {
|
||||||
|
const root = await mkdtemp(join(tmpdir(), 'firewallissues-update-'));
|
||||||
|
t.after(() => rm(root, { recursive: true, force: true }));
|
||||||
|
const issuesDir = join(root, 'issues');
|
||||||
|
const productsPath = join(root, 'products.json');
|
||||||
|
const manifestPath = join(root, 'manifest.json');
|
||||||
|
await mkdir(join(issuesDir, 'Product', 'addressed'), { recursive: true });
|
||||||
|
await mkdir(join(issuesDir, 'Product', 'known'), { recursive: true });
|
||||||
|
await writeFile(join(issuesDir, 'Product', 'addressed', '1.2.3-h2.md'), 'addressed');
|
||||||
|
await writeFile(join(issuesDir, 'Product', 'addressed', '1.2.3.md'), 'addressed');
|
||||||
|
await writeFile(join(issuesDir, 'Product', 'known', '1.2.4.md'), 'known');
|
||||||
|
|
||||||
|
const initialProducts = {
|
||||||
|
Product: {
|
||||||
|
1: {
|
||||||
|
'1.2': {
|
||||||
|
addressed: ['obsolete.md'],
|
||||||
|
known: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const rootPath = JSON.stringify(['Product']);
|
||||||
|
await writeFile(productsPath, `${JSON.stringify(initialProducts, null, 2)}\n`);
|
||||||
|
await writeFile(manifestPath, `${JSON.stringify({ schema: 1, nodes: [rootPath, 'legacy-path', null] }, null, 2)}\n`);
|
||||||
|
|
||||||
|
const first = await updateProductsFromIssues({ issuesDir, productsPath, manifestPath });
|
||||||
|
assert.equal(first.records, 3);
|
||||||
|
assert.equal(first.unmatched.length, 0);
|
||||||
|
assert.ok(first.appended.length > 0);
|
||||||
|
assert.deepEqual(first.missing, ['legacy-path']);
|
||||||
|
|
||||||
|
const products = JSON.parse(await readFile(productsPath, 'utf8'));
|
||||||
|
assert.deepEqual(products.Product[1]['1.2'].addressed, ['1.2.3.md', '1.2.3-h2.md']);
|
||||||
|
assert.deepEqual(products.Product[1]['1.2'].known, ['1.2.4.md']);
|
||||||
|
|
||||||
|
const manifest = JSON.parse(await readFile(manifestPath, 'utf8'));
|
||||||
|
assert.deepEqual(manifest.nodes.slice(0, 3), [rootPath, 'legacy-path', null]);
|
||||||
|
const renderedPaths = enumerateTreePaths(buildProductTree(products));
|
||||||
|
assert.ok(renderedPaths.every(path => manifest.nodes.includes(path)));
|
||||||
|
|
||||||
|
const second = await updateProductsFromIssues({ issuesDir, productsPath, manifestPath });
|
||||||
|
assert.deepEqual(second.appended, []);
|
||||||
|
assert.deepEqual(JSON.parse(await readFile(manifestPath, 'utf8')), manifest);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('product updater refuses to rewrite when no issue files are found', async t => {
|
||||||
|
const root = await mkdtemp(join(tmpdir(), 'firewallissues-empty-update-'));
|
||||||
|
t.after(() => rm(root, { recursive: true, force: true }));
|
||||||
|
const issuesDir = join(root, 'issues');
|
||||||
|
const productsPath = join(root, 'products.json');
|
||||||
|
const manifestPath = join(root, 'manifest.json');
|
||||||
|
await mkdir(issuesDir);
|
||||||
|
await writeFile(productsPath, '{}\n');
|
||||||
|
await writeFile(manifestPath, '{"schema":1,"nodes":[]}\n');
|
||||||
|
|
||||||
|
await assert.rejects(
|
||||||
|
updateProductsFromIssues({ issuesDir, productsPath, manifestPath }),
|
||||||
|
/No '<version>\.md' issue files found/
|
||||||
|
);
|
||||||
|
assert.equal(await readFile(productsPath, 'utf8'), '{}\n');
|
||||||
|
});
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 10.2.10-h37
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address [CVE-2026-0273](https://security-stg.paloaltonetworks.com/CVE-2026-0273) and [CVE-2026-0272](https://security-stg.paloaltonetworks.com/CVE-2026-0272).
|
||||||
|
|
||||||
|
## PAN-325786
|
||||||
|
|
||||||
|
Fixed an issue where content updates with active traffic failed.
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 10.2.10-h39
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address the following CVEs:
|
||||||
|
|
||||||
|
- [CVE-2026-0283](https://security.paloaltonetworks.com/CVE-2026-0283)
|
||||||
|
- [CVE-2026-0287](https://security.paloaltonetworks.com/CVE-2026-0287)
|
||||||
|
- [CVE-2026-0279](https://security.paloaltonetworks.com/CVE-2026-0279)
|
||||||
|
- [CVE-2026-0282](https://security.paloaltonetworks.com/CVE-2026-0282)
|
||||||
|
- [CVE-2026-0288](https://security.paloaltonetworks.com/CVE-2026-0288)
|
||||||
|
- [CVE-2026-0286](https://security.paloaltonetworks.com/CVE-2026-0286)
|
||||||
|
- [CVE-2026-0285](https://security.paloaltonetworks.com/CVE-2026-0285)
|
||||||
|
- [CVE-2026-0280](https://security.paloaltonetworks.com/CVE-2026-0280)
|
||||||
|
- [CVE-2026-0284](https://security.paloaltonetworks.com/CVE-2026-0284)
|
||||||
|
- [CVE-2026-0281](https://security.paloaltonetworks.com/CVE-2026-0281)
|
||||||
|
|
||||||
|
## PAN-314630
|
||||||
|
|
||||||
|
Fixed an issue where the firewall repeatedly rebooted and entered maintenance mode, and a factory reset was required.
|
||||||
|
|
||||||
|
## PAN-300055
|
||||||
|
|
||||||
|
Fixed an issue where the firewall experienced high disk utilization in the /opt/pancfg/mgmt/content-preview directory due to older content data not being automatically removed when an error occurred during the process.
|
||||||
|
|
||||||
|
## PAN-298788
|
||||||
|
|
||||||
|
Fixed an issue where the /pancfg partition on the Azure Cloud NGFW reached 100% utilization, which caused commit failures.
|
||||||
|
|
||||||
|
## PAN-284073
|
||||||
|
|
||||||
|
Fixed an issue on the firewall that caused commits to fail and the web interface to become inaccessible.
|
||||||
|
|
||||||
|
## PAN-278611
|
||||||
|
|
||||||
|
Fixed an issue on Panorama where software images were not purged from the /opt/pancfg/mgmt/sw-images folder.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 10.2.13-h22
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address [CVE-2026-0273](https://security-stg.paloaltonetworks.com/CVE-2026-0273) and [CVE-2026-0272](https://security-stg.paloaltonetworks.com/CVE-2026-0272).
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 10.2.13-h23
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address the following CVEs:
|
||||||
|
|
||||||
|
- [CVE-2026-0283](https://security.paloaltonetworks.com/CVE-2026-0283)
|
||||||
|
- [CVE-2026-0287](https://security.paloaltonetworks.com/CVE-2026-0287)
|
||||||
|
- [CVE-2026-0279](https://security.paloaltonetworks.com/CVE-2026-0279)
|
||||||
|
- [CVE-2026-0282](https://security.paloaltonetworks.com/CVE-2026-0282)
|
||||||
|
- [CVE-2026-0288](https://security.paloaltonetworks.com/CVE-2026-0288)
|
||||||
|
- [CVE-2026-0286](https://security.paloaltonetworks.com/CVE-2026-0286)
|
||||||
|
- [CVE-2026-0285](https://security.paloaltonetworks.com/CVE-2026-0285)
|
||||||
|
- [CVE-2026-0280](https://security.paloaltonetworks.com/CVE-2026-0280)
|
||||||
|
- [CVE-2026-0284](https://security.paloaltonetworks.com/CVE-2026-0284)
|
||||||
|
- [CVE-2026-0281](https://security.paloaltonetworks.com/CVE-2026-0281)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 10.2.16-h8
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address [CVE-2026-0273](https://security-stg.paloaltonetworks.com/CVE-2026-0273) and [CVE-2026-0272](https://security-stg.paloaltonetworks.com/CVE-2026-0272).
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 10.2.16-h9
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address the following CVEs:
|
||||||
|
|
||||||
|
- [CVE-2026-0283](https://security.paloaltonetworks.com/CVE-2026-0283)
|
||||||
|
- [CVE-2026-0287](https://security.paloaltonetworks.com/CVE-2026-0287)
|
||||||
|
- [CVE-2026-0279](https://security.paloaltonetworks.com/CVE-2026-0279)
|
||||||
|
- [CVE-2026-0282](https://security.paloaltonetworks.com/CVE-2026-0282)
|
||||||
|
- [CVE-2026-0288](https://security.paloaltonetworks.com/CVE-2026-0288)
|
||||||
|
- [CVE-2026-0286](https://security.paloaltonetworks.com/CVE-2026-0286)
|
||||||
|
- [CVE-2026-0285](https://security.paloaltonetworks.com/CVE-2026-0285)
|
||||||
|
- [CVE-2026-0280](https://security.paloaltonetworks.com/CVE-2026-0280)
|
||||||
|
- [CVE-2026-0284](https://security.paloaltonetworks.com/CVE-2026-0284)
|
||||||
|
- [CVE-2026-0281](https://security.paloaltonetworks.com/CVE-2026-0281)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 10.2.18-h7
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address [CVE-2026-0273](https://security-stg.paloaltonetworks.com/CVE-2026-0273) and [CVE-2026-0272](https://security-stg.paloaltonetworks.com/CVE-2026-0272).
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 10.2.18-h8
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address the following CVEs:
|
||||||
|
|
||||||
|
- [CVE-2026-0283](https://security.paloaltonetworks.com/CVE-2026-0283)
|
||||||
|
- [CVE-2026-0287](https://security.paloaltonetworks.com/CVE-2026-0287)
|
||||||
|
- [CVE-2026-0279](https://security.paloaltonetworks.com/CVE-2026-0279)
|
||||||
|
- [CVE-2026-0282](https://security.paloaltonetworks.com/CVE-2026-0282)
|
||||||
|
- [CVE-2026-0288](https://security.paloaltonetworks.com/CVE-2026-0288)
|
||||||
|
- [CVE-2026-0286](https://security.paloaltonetworks.com/CVE-2026-0286)
|
||||||
|
- [CVE-2026-0285](https://security.paloaltonetworks.com/CVE-2026-0285)
|
||||||
|
- [CVE-2026-0280](https://security.paloaltonetworks.com/CVE-2026-0280)
|
||||||
|
- [CVE-2026-0284](https://security.paloaltonetworks.com/CVE-2026-0284)
|
||||||
|
- [CVE-2026-0281](https://security.paloaltonetworks.com/CVE-2026-0281)
|
||||||
|
|
||||||
|
## PAN-317466
|
||||||
|
|
||||||
|
Fixed an issue where SIP sessions stopped progressing after the firewall received fragmented packets, fragmented at header field.
|
||||||
|
|
||||||
|
## PAN-306555
|
||||||
|
|
||||||
|
Fixed an issue where the firewall stopped responding, which led to service outages.
|
||||||
|
|
||||||
|
## PAN-295803
|
||||||
|
|
||||||
|
Addressed a memory leak issue under sc3 and automatic commit recovery (ACR) code path.
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 10.2.7-h35
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address [CVE-2026-0273](https://security-stg.paloaltonetworks.com/CVE-2026-0273) and [CVE-2026-0272](https://security-stg.paloaltonetworks.com/CVE-2026-0272).
|
||||||
|
|
||||||
|
## PAN-293673
|
||||||
|
|
||||||
|
Fixed an issue where the firewall stopped all tasks due to an OOM condition caused by a scheduled log export using FTP to an external FTP server.
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 10.2.7-h36
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address the following CVEs:
|
||||||
|
|
||||||
|
- [CVE-2026-0283](https://security.paloaltonetworks.com/CVE-2026-0283)
|
||||||
|
- [CVE-2026-0287](https://security.paloaltonetworks.com/CVE-2026-0287)
|
||||||
|
- [CVE-2026-0279](https://security.paloaltonetworks.com/CVE-2026-0279)
|
||||||
|
- [CVE-2026-0282](https://security.paloaltonetworks.com/CVE-2026-0282)
|
||||||
|
- [CVE-2026-0288](https://security.paloaltonetworks.com/CVE-2026-0288)
|
||||||
|
- [CVE-2026-0286](https://security.paloaltonetworks.com/CVE-2026-0286)
|
||||||
|
- [CVE-2026-0285](https://security.paloaltonetworks.com/CVE-2026-0285)
|
||||||
|
- [CVE-2026-0280](https://security.paloaltonetworks.com/CVE-2026-0280)
|
||||||
|
- [CVE-2026-0284](https://security.paloaltonetworks.com/CVE-2026-0284)
|
||||||
|
- [CVE-2026-0281](https://security.paloaltonetworks.com/CVE-2026-0281)
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 11.1.10-h30
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address the following CVEs:
|
||||||
|
|
||||||
|
- [CVE-2026-0283](https://security.paloaltonetworks.com/CVE-2026-0283)
|
||||||
|
- [CVE-2026-0287](https://security.paloaltonetworks.com/CVE-2026-0287)
|
||||||
|
- [CVE-2026-0279](https://security.paloaltonetworks.com/CVE-2026-0279)
|
||||||
|
- [CVE-2026-0282](https://security.paloaltonetworks.com/CVE-2026-0282)
|
||||||
|
- [CVE-2026-0288](https://security.paloaltonetworks.com/CVE-2026-0288)
|
||||||
|
- [CVE-2026-0286](https://security.paloaltonetworks.com/CVE-2026-0286)
|
||||||
|
- [CVE-2026-0285](https://security.paloaltonetworks.com/CVE-2026-0285)
|
||||||
|
- [CVE-2026-0280](https://security.paloaltonetworks.com/CVE-2026-0280)
|
||||||
|
- [CVE-2026-0284](https://security.paloaltonetworks.com/CVE-2026-0284)
|
||||||
|
- [CVE-2026-0281](https://security.paloaltonetworks.com/CVE-2026-0281)
|
||||||
|
|
||||||
|
## PAN-326354
|
||||||
|
|
||||||
|
Fixed an issue where the sslmgr process stopped responding when attempting to display the OSCP host cache.
|
||||||
|
|
||||||
|
## PAN-324370
|
||||||
|
|
||||||
|
Fixed an issue where IDE traffic did not function as expected when both HTTP head insertion and DLP inspection were enabled.
|
||||||
|
|
||||||
|
## PAN-322281
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
Firewalls in HA configurations only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where the HA 2 interface did not come up on the passive firewall, which resulted in the firewall being unable to join the HA pair.
|
||||||
|
|
||||||
|
## PAN-321816
|
||||||
|
|
||||||
|
Fixed an issue where processes stopped responding unexpectedly.
|
||||||
|
|
||||||
|
## PAN-319504
|
||||||
|
|
||||||
|
Fixed an issue where telemetry data was not sent to the cloud due to the firewall being unable to resolve the destination server's FQDN even when a proxy server was configured. With this fix, the firewall properly sends telemetry data through the configured proxy server without requiring direct public DNS resolution for the telemetry server's FQDN.
|
||||||
|
|
||||||
|
## PAN-304360
|
||||||
|
|
||||||
|
Fixed an issue where the firewall did not redistribute its application routes to BGP peers. This occurred in multi-mesh deployments with the multi-cloud networking feature enabled.
|
||||||
|
|
||||||
|
## PAN-286386
|
||||||
|
|
||||||
|
Fixed an issue where GlobalProtect users were unable to connect.
|
||||||
|
|
||||||
|
## PAN-285327
|
||||||
|
|
||||||
|
Fixed an issue where a memory leak occurred when processing device and vsys tags.
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 11.1.13-h9
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address the following CVEs:
|
||||||
|
|
||||||
|
- [CVE-2026-0283](https://security.paloaltonetworks.com/CVE-2026-0283)
|
||||||
|
- [CVE-2026-0287](https://security.paloaltonetworks.com/CVE-2026-0287)
|
||||||
|
- [CVE-2026-0279](https://security.paloaltonetworks.com/CVE-2026-0279)
|
||||||
|
- [CVE-2026-0282](https://security.paloaltonetworks.com/CVE-2026-0282)
|
||||||
|
- [CVE-2026-0288](https://security.paloaltonetworks.com/CVE-2026-0288)
|
||||||
|
- [CVE-2026-0286](https://security.paloaltonetworks.com/CVE-2026-0286)
|
||||||
|
- [CVE-2026-0285](https://security.paloaltonetworks.com/CVE-2026-0285)
|
||||||
|
- [CVE-2026-0280](https://security.paloaltonetworks.com/CVE-2026-0280)
|
||||||
|
- [CVE-2026-0284](https://security.paloaltonetworks.com/CVE-2026-0284)
|
||||||
|
- [CVE-2026-0281](https://security.paloaltonetworks.com/CVE-2026-0281)
|
||||||
|
|
||||||
|
## PAN-326677
|
||||||
|
|
||||||
|
Fixed an issue where a selective push from Panorama to the firewall was successful even when applying rename operation failed in selective push, which resulted in configurations on the firewall being deleted. With this fix, the selective push will fail when applying rename operation fails.
|
||||||
|
|
||||||
|
## PAN-326354
|
||||||
|
|
||||||
|
Fixed an issue where the sslmgr process stopped responding when attempting to display the OSCP host cache.
|
||||||
|
|
||||||
|
## PAN-324370
|
||||||
|
|
||||||
|
Fixed an issue where IDE traffic did not function as expected when both HTTP head insertion and DLP inspection were enabled.
|
||||||
|
|
||||||
|
## PAN-321816
|
||||||
|
|
||||||
|
Fixed an issue where processes stopped responding unexpectedly.
|
||||||
|
|
||||||
|
## PAN-321699
|
||||||
|
|
||||||
|
Fixed an issue where device telemetry intermittently failed to send files, which resulted in critical alerts in system files.
|
||||||
|
|
||||||
|
## PAN-319504
|
||||||
|
|
||||||
|
Fixed an issue where telemetry data was not sent to the cloud due to the firewall being unable to resolve the destination server's FQDN even when a proxy server was configured. With this fix, the firewall properly sends telemetry data through the configured proxy server without requiring direct public DNS resolution for the telemetry server's FQDN.
|
||||||
|
|
||||||
|
## PAN-318106
|
||||||
|
|
||||||
|
Fixed an issue where SCM did not update device telemetry for the firewall after upgrading to an affected release.
|
||||||
|
|
||||||
|
## PAN-312442
|
||||||
|
|
||||||
|
Fixed an issue where 403 errors occurred when performing "show config effective-running" API query after downgrading to an affected PAN-OS release.
|
||||||
|
|
||||||
|
## PAN-308876
|
||||||
|
|
||||||
|
Fixed an issue where upgrades to managed firewalls from Panorama failed.
|
||||||
|
|
||||||
|
## PAN-304360
|
||||||
|
|
||||||
|
Fixed an issue where the firewall did not redistribute its application routes to BGP peers. This occurred in multi-mesh deployments with the multi-cloud networking feature enabled.
|
||||||
|
|
||||||
|
## PAN-297370
|
||||||
|
|
||||||
|
Fixed an issue where pushing a new object from Panorama to a Cloud NGFW Device Group unexpectedly removed existing Panorama-pushed policy rules, even though the **Push Preview** did not show any deletions, which led to traffic disruptions.
|
||||||
|
|
||||||
|
## PAN-286386
|
||||||
|
|
||||||
|
Fixed an issue where GlobalProtect users were unable to connect
|
||||||
|
|
||||||
|
## PAN-285327
|
||||||
|
|
||||||
|
Fixed an issue where a memory leak occurred when processing device and vsys tags.
|
||||||
@@ -0,0 +1,321 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 11.1.16
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address the following CVEs:
|
||||||
|
|
||||||
|
- [CVE-2026-0283](https://security.paloaltonetworks.com/CVE-2026-0283)
|
||||||
|
- [CVE-2026-0287](https://security.paloaltonetworks.com/CVE-2026-0287)
|
||||||
|
- [CVE-2026-0279](https://security.paloaltonetworks.com/CVE-2026-0279)
|
||||||
|
- [CVE-2026-0282](https://security.paloaltonetworks.com/CVE-2026-0282)
|
||||||
|
- [CVE-2026-0288](https://security.paloaltonetworks.com/CVE-2026-0288)
|
||||||
|
- [CVE-2026-0286](https://security.paloaltonetworks.com/CVE-2026-0286)
|
||||||
|
- [CVE-2026-0285](https://security.paloaltonetworks.com/CVE-2026-0285)
|
||||||
|
- [CVE-2026-0280](https://security.paloaltonetworks.com/CVE-2026-0280)
|
||||||
|
- [CVE-2026-0284](https://security.paloaltonetworks.com/CVE-2026-0284)
|
||||||
|
- [CVE-2026-0281](https://security.paloaltonetworks.com/CVE-2026-0281)
|
||||||
|
|
||||||
|
## PAN-327009
|
||||||
|
|
||||||
|
Fixed an issue where the all_task process stopped responding.
|
||||||
|
|
||||||
|
## PAN-326677
|
||||||
|
|
||||||
|
Fixed an issue where a selective push from Panorama to the firewall was successful even when applying rename operation failed in selective push, which resulted in configurations on the firewall being deleted. With this fix, the selective push will fail when applying rename operation fails.
|
||||||
|
|
||||||
|
## PAN-326354
|
||||||
|
|
||||||
|
Fixed an issue where the sslmgr process stopped responding when attempting to display the OSCP host cache.
|
||||||
|
|
||||||
|
## PAN-324370
|
||||||
|
|
||||||
|
Fixed an issue where IDE traffic did not function as expected when both HTTP head insertion and DLP inspection were enabled.
|
||||||
|
|
||||||
|
## PAN-323485
|
||||||
|
|
||||||
|
Fixed an issue where multicast radio RTP based traffic was dropped after an upgrade when the firewall performed Cloud Inline inspection, which led to an exceeded session queue for Cloud Threat Detection.
|
||||||
|
|
||||||
|
## PAN-321816
|
||||||
|
|
||||||
|
Fixed an issue where processes stopped responding unexpectedly.
|
||||||
|
|
||||||
|
## PAN-321699
|
||||||
|
|
||||||
|
Fixed an issue where device telemetry intermittently failed to send files, which resulted in critical alerts in system files.
|
||||||
|
|
||||||
|
## PAN-321527
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
PA-7500 firewalls in HA cluster configurations only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where, when one firewall suspended operations, the other firewall also suspended operations instead of initiating a failover, which resulted in a complete traffic outage.
|
||||||
|
|
||||||
|
## PAN-321516
|
||||||
|
|
||||||
|
Fixed an issue where the dataplane restarted due to a race condition in the dataplane cache infrastructure.
|
||||||
|
|
||||||
|
## PAN-321340
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
Firewalls in FIPS mode only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where GlobalProtect unexpectedly prompted for RADIUS authentication instead of client certificate authentication due to an OSCP validation error and subsequent CRL verification failure, which led to certificates being marked as invalid.
|
||||||
|
|
||||||
|
## PAN-321060
|
||||||
|
|
||||||
|
Fixed an issue where an ethernet interface remained in a down state after repeated automated enable/disable cycles and required manual intervention, which resulted in backup outages.
|
||||||
|
|
||||||
|
## PAN-320598
|
||||||
|
|
||||||
|
Fixed an issue where internal and external DNS names did not resolve when connected to a GlobalProtect gateway.
|
||||||
|
|
||||||
|
## PAN-320245
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
PA-7500 Series firewalls in vwire mode only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where Oracle application traffic was intermittently not processed even though connected devices sent the traffic, which led to service distruptions.
|
||||||
|
|
||||||
|
## PAN-319793
|
||||||
|
|
||||||
|
Fixed an issue where, after upgrading to PAN-OS 12.1.5, GlobalProtect Clientless VPN failed to access JavaScripts.
|
||||||
|
|
||||||
|
## PAN-319504
|
||||||
|
|
||||||
|
Fixed an issue where telemetry data was not sent to the cloud due to the firewall being unable to resolve the destination server's FQDN even when a proxy server was configured. With this fix, the firewall properly sends telemetry data through the configured proxy server without requiring direct public DNS resolution for the telemetry server's FQDN.
|
||||||
|
|
||||||
|
## PAN-319419
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
Firewalls in active/passive HA configurations only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where active firewalls were unable to send device telemetry data to CDL.
|
||||||
|
|
||||||
|
## PAN-319352
|
||||||
|
|
||||||
|
Fixed an issue where the firewall rebooted unexpectedly without any configuration or power changes.
|
||||||
|
|
||||||
|
## PAN-319343
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
Prisma Access Gateways only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where the global management plane stopped responding, which caused SSH and HTML disconnections, HIP database lookup failures, and significantly slower SCM commits. This occurred when egress IP allow listing was enabled in SCM and changes were made to EDLs.
|
||||||
|
|
||||||
|
## PAN-319288
|
||||||
|
|
||||||
|
Fixed an issue where a DPC in Slot 4 restarted repeatedly, which caused internal path monitoring failures and a failover event.
|
||||||
|
|
||||||
|
## PAN-319228
|
||||||
|
|
||||||
|
Fixed an issue where External Dynamic List (EDL) refresh and commit operations remained in a pending state, which prevented any subsequent operations from completing.
|
||||||
|
|
||||||
|
## PAN-318580
|
||||||
|
|
||||||
|
Fixed an issue where processes restarted and the firewall unexpectedly rebooted when you configured a Security policy rule with **Source Device > quarantine**.
|
||||||
|
|
||||||
|
## PAN-318382
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
Firewalls in HA configurations only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where the secondary firewall remained at an **Initial** state after an upgrade.
|
||||||
|
|
||||||
|
## PAN-318120
|
||||||
|
|
||||||
|
Fixed an issue where SSL traffic was silently dropped when traffic was processed by a Security policy with an Anti-Spyware profile that had Inline cloud Analysis enabled for SSL C2 Detector with an action other than allow or alert.
|
||||||
|
|
||||||
|
## PAN-318106
|
||||||
|
|
||||||
|
Fixed an issue where SCM did not update device telemetry for the firewall after upgrading to an affected release.
|
||||||
|
|
||||||
|
## PAN-317755
|
||||||
|
|
||||||
|
Fixed an issue on Panorama where selective push operations failed when plugin configurations included access-domain or log-collector references.
|
||||||
|
|
||||||
|
## PAN-317648
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
PA-5450 firewalls and PA-7000 Series firewalls with 100G NPCs only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where intermittent packet loss occurred when traversing the dataplane after upgrading the firewall. This occurred when a dataplane HA interface was configured in an environment where Slot 1 was unpopulated , which resulted in a wildcard entry being created within the QMAP table.
|
||||||
|
|
||||||
|
## PAN-317614
|
||||||
|
|
||||||
|
Fixed an issue where high throughput and increased packet rates caused high dataplane CPU usage.
|
||||||
|
|
||||||
|
## PAN-316435
|
||||||
|
|
||||||
|
Fixed an issue where the firewall restarted unexpectedly due to an OOM condition after upgrading to an affected release.
|
||||||
|
|
||||||
|
## PAN-316120
|
||||||
|
|
||||||
|
Fixed an issue where, after Advanced Routing was enabled, the firewall advertised routes to internal BGP neighbors with the original external BGP next-hop address.
|
||||||
|
|
||||||
|
## PAN-315337
|
||||||
|
|
||||||
|
Fixed an issue where GlobalProtect throughput was reduced after an upgrade.
|
||||||
|
|
||||||
|
## PAN-315326
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
PA-7500 firewalls only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where zone protection threshold values per dataplane were unexpectedly low.
|
||||||
|
|
||||||
|
## PAN-315314
|
||||||
|
|
||||||
|
Fixed an issue where, when a push operation from Panorama to the firewall failed, accounting logs stopped forwarding.
|
||||||
|
|
||||||
|
## PAN-315160
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
PA-7500 firewalls only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where internal path monitoring logs incorrectly reported internal path monitoring failures when they did not occur.
|
||||||
|
|
||||||
|
## PAN-314776
|
||||||
|
|
||||||
|
Fixed an issue where the configd process stopped responding after pushing configuration changes from Panorama to the firewall.
|
||||||
|
|
||||||
|
## PAN-314623
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
Firewalls in active/passive HA configurations only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where, after a failover, routing information within OSPF protocol was not correctly translated or propagated, which affected network path convergence and FRR capabilities.
|
||||||
|
|
||||||
|
## PAN-314512
|
||||||
|
|
||||||
|
Fixed an issue where the GlobalProtect portal became inaccessible when the dataplane was configured with a DHCP assigned IP address.
|
||||||
|
|
||||||
|
## PAN-314104
|
||||||
|
|
||||||
|
Fixed an issue where running BCM counter commands from the administrative shell did not consistently return output, and commands to modify queue sizes did not take effect.
|
||||||
|
|
||||||
|
## PAN-313787
|
||||||
|
|
||||||
|
Fixed an issue where some system log filters with the eventid operator for a BGP event did not work.
|
||||||
|
|
||||||
|
## PAN-313606
|
||||||
|
|
||||||
|
Fixed an issue where Panorama pushed commits took longer than expected to complete without displaying an error message when committing due to slow cloud-app compilation.
|
||||||
|
|
||||||
|
## PAN-313575
|
||||||
|
|
||||||
|
Fixed an issue where 10G connections on built-in RJ45 interfaces (ethernet1/1 through ethernet1/5) intermittently experienced interface flapping when connected to Cisco switchports.
|
||||||
|
|
||||||
|
## PAN-313523
|
||||||
|
|
||||||
|
Fixed an issue where generating a tech support file caused GlobalProtect users to be forcibly logged out.
|
||||||
|
|
||||||
|
## PAN-313443
|
||||||
|
|
||||||
|
Fixed an issue where firewalls acting as an accumulation proxy sent a server hello with an earlier TCP timestamp value than a preceding ACK packet, which prevented successful session establishment. This occurred when the client hello messages were split across multiple network segments.
|
||||||
|
|
||||||
|
To use this fix, run the CLI command debug dataplane set ssl-decrypt accumulate-client-hello ts-relay yes.
|
||||||
|
|
||||||
|
## PAN-313218
|
||||||
|
|
||||||
|
Added the following CLI commands to address QoS packet drops due to bursty traffic:
|
||||||
|
|
||||||
|
- debug dataplane set qos-setting qos-param qlimit 300
|
||||||
|
- debug dataplane set qos-setting qos-param red low 50 high 90
|
||||||
|
|
||||||
|
To utilize this fix, change the parameters, disable QoS, commit changes, enable QOS, and then re-commit changes.
|
||||||
|
|
||||||
|
## PAN-313036
|
||||||
|
|
||||||
|
Fixed an issue where the firewall dataplane continuously accumulated packets in the ctd_pkt_queue and packet buffers, which caused resource exhaustion and prematurely terminated sessions.
|
||||||
|
|
||||||
|
## PAN-312442
|
||||||
|
|
||||||
|
Fixed an issue where 403 errors occurred when performing "show config effective-running" API query after downgrading to an affected PAN-OS release.
|
||||||
|
|
||||||
|
## PAN-312330
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
Firewalls in active/passive HA configurations only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where the Clientless VPN applications failed to load due to the firewall dataplane incorrectly processing session information.
|
||||||
|
|
||||||
|
## PAN-312157
|
||||||
|
|
||||||
|
Fixed an issue where, during a commit, the firewall intermittently stopped sending SNMP messages, which caused interface counters to stop updating for brief periods of time.
|
||||||
|
|
||||||
|
## PAN-311658
|
||||||
|
|
||||||
|
Fixed an issue where the reportd process stopped responding, which caused the firewall to reboot.
|
||||||
|
|
||||||
|
## PAN-311419
|
||||||
|
|
||||||
|
Fixed an issue where the recommended filter for identifying traffic from unidentified users in traffic logs reported an incorrectly low number of results.
|
||||||
|
|
||||||
|
## PAN-310240
|
||||||
|
|
||||||
|
Fixed an issue where software packet buffers were completely utilized when performing a Data Loss Prevention longevity test.
|
||||||
|
|
||||||
|
## PAN-308876
|
||||||
|
|
||||||
|
Fixed an issue where upgrades to managed firewalls from Panorama failed.
|
||||||
|
|
||||||
|
## PAN-308775
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
Firewalls in active/passive configurations only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where NTP status intermittently showed as rejected on the active firewall, which prevented the firewalls from synchronizing time.
|
||||||
|
|
||||||
|
## PAN-308444
|
||||||
|
|
||||||
|
Fixed an issue where pushing multiple policy rules failed when the policy rules contained a large number of dynamic address object groups or user groups.
|
||||||
|
|
||||||
|
## PAN-307190
|
||||||
|
|
||||||
|
Fixed an issue where LED indicators on combo ports remained off even when the network link was active.
|
||||||
|
|
||||||
|
## PAN-304360
|
||||||
|
|
||||||
|
Fixed an issue where the firewall did not redistribute its application routes to BGP peers. This occurred in multi-mesh deployments with the multi-cloud networking feature enabled.
|
||||||
|
|
||||||
|
## PAN-295082
|
||||||
|
|
||||||
|
Fixed an issue on the Panorama web interface where you were unable to delete or change a logical router for tunnel, SD-WAN, VLAN, or loopback interfaces under a template.
|
||||||
|
|
||||||
|
## PAN-289460
|
||||||
|
|
||||||
|
Fixed an issue where the timestamp value in SNMPv3 trap headers was incorrect.
|
||||||
|
|
||||||
|
To use this fix, run the CLI command debug log-receiver enginetime-from-snmptime yes.
|
||||||
|
|
||||||
|
## PAN-286386
|
||||||
|
|
||||||
|
Fixed an issue where GlobalProtect users were unable to connect
|
||||||
|
|
||||||
|
## PAN-285327
|
||||||
|
|
||||||
|
Fixed an issue where a memory leak occurred when processing device and vsys tags.
|
||||||
|
|
||||||
|
## PAN-267067
|
||||||
|
|
||||||
|
Fixed an issue where VXLAN traffic failed and packet loss occurred in networks sensors after upgrading to an affected release.
|
||||||
|
|
||||||
|
## PAN-240066
|
||||||
|
|
||||||
|
Fixed a duplicate MAC address issue where an ethernet interface sent out Gratuitous ARP (GARP) messages for an IP address that was not configured on it.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 11.1.4-h34
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address [CVE-2026-0273](https://security-stg.paloaltonetworks.com/CVE-2026-0273) and [CVE-2026-0272](https://security-stg.paloaltonetworks.com/CVE-2026-0272).
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 11.1.4-h35
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address the following CVEs:
|
||||||
|
|
||||||
|
- [CVE-2026-0283](https://security.paloaltonetworks.com/CVE-2026-0283)
|
||||||
|
- [CVE-2026-0287](https://security.paloaltonetworks.com/CVE-2026-0287)
|
||||||
|
- [CVE-2026-0279](https://security.paloaltonetworks.com/CVE-2026-0279)
|
||||||
|
- [CVE-2026-0282](https://security.paloaltonetworks.com/CVE-2026-0282)
|
||||||
|
- [CVE-2026-0288](https://security.paloaltonetworks.com/CVE-2026-0288)
|
||||||
|
- [CVE-2026-0286](https://security.paloaltonetworks.com/CVE-2026-0286)
|
||||||
|
- [CVE-2026-0285](https://security.paloaltonetworks.com/CVE-2026-0285)
|
||||||
|
- [CVE-2026-0280](https://security.paloaltonetworks.com/CVE-2026-0280)
|
||||||
|
- [CVE-2026-0284](https://security.paloaltonetworks.com/CVE-2026-0284)
|
||||||
|
- [CVE-2026-0281](https://security.paloaltonetworks.com/CVE-2026-0281)
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 11.1.6-h35
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address the following CVEs:
|
||||||
|
|
||||||
|
- [CVE-2026-0283](https://security.paloaltonetworks.com/CVE-2026-0283)
|
||||||
|
- [CVE-2026-0287](https://security.paloaltonetworks.com/CVE-2026-0287)
|
||||||
|
- [CVE-2026-0279](https://security.paloaltonetworks.com/CVE-2026-0279)
|
||||||
|
- [CVE-2026-0282](https://security.paloaltonetworks.com/CVE-2026-0282)
|
||||||
|
- [CVE-2026-0288](https://security.paloaltonetworks.com/CVE-2026-0288)
|
||||||
|
- [CVE-2026-0286](https://security.paloaltonetworks.com/CVE-2026-0286)
|
||||||
|
- [CVE-2026-0285](https://security.paloaltonetworks.com/CVE-2026-0285)
|
||||||
|
- [CVE-2026-0280](https://security.paloaltonetworks.com/CVE-2026-0280)
|
||||||
|
- [CVE-2026-0284](https://security.paloaltonetworks.com/CVE-2026-0284)
|
||||||
|
- [CVE-2026-0281](https://security.paloaltonetworks.com/CVE-2026-0281)
|
||||||
|
|
||||||
|
## PAN-319504
|
||||||
|
|
||||||
|
Fixed an issue where telemetry data was not sent to the cloud due to the firewall being unable to resolve the destination server's FQDN even when a proxy server was configured. With this fix, the firewall properly sends telemetry data through the configured proxy server without requiring direct public DNS resolution for the telemetry server's FQDN.
|
||||||
|
|
||||||
|
## PAN-313218
|
||||||
|
|
||||||
|
Added the following CLI commands to address QoS packet drops due to bursty traffic:
|
||||||
|
|
||||||
|
- debug dataplane set qos-setting qos-param qlimit 300
|
||||||
|
- debug dataplane set qos-setting qos-param red low 50 high 90
|
||||||
|
|
||||||
|
To utilize this fix, change the parameters, disable QoS, commit changes, enable QOS, and then re-commit changes.
|
||||||
|
|
||||||
|
## PAN-304360
|
||||||
|
|
||||||
|
Fixed an issue where the firewall did not redistribute its application routes to BGP peers. This occurred in multi-mesh deployments with the multi-cloud networking feature enabled.
|
||||||
|
|
||||||
|
## PAN-285327
|
||||||
|
|
||||||
|
Fixed an issue where a memory leak occurred when processing device and vsys tags.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 11.1.7-h7
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address [CVE-2026-0273](https://security-stg.paloaltonetworks.com/CVE-2026-0273) and [CVE-2026-0272](https://security-stg.paloaltonetworks.com/CVE-2026-0272).
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 11.1.7-h8
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address the following CVEs:
|
||||||
|
|
||||||
|
- [CVE-2026-0283](https://security.paloaltonetworks.com/CVE-2026-0283)
|
||||||
|
- [CVE-2026-0287](https://security.paloaltonetworks.com/CVE-2026-0287)
|
||||||
|
- [CVE-2026-0279](https://security.paloaltonetworks.com/CVE-2026-0279)
|
||||||
|
- [CVE-2026-0282](https://security.paloaltonetworks.com/CVE-2026-0282)
|
||||||
|
- [CVE-2026-0288](https://security.paloaltonetworks.com/CVE-2026-0288)
|
||||||
|
- [CVE-2026-0286](https://security.paloaltonetworks.com/CVE-2026-0286)
|
||||||
|
- [CVE-2026-0285](https://security.paloaltonetworks.com/CVE-2026-0285)
|
||||||
|
- [CVE-2026-0280](https://security.paloaltonetworks.com/CVE-2026-0280)
|
||||||
|
- [CVE-2026-0284](https://security.paloaltonetworks.com/CVE-2026-0284)
|
||||||
|
- [CVE-2026-0281](https://security.paloaltonetworks.com/CVE-2026-0281)
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 11.2.10-h12
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address the following CVEs:
|
||||||
|
|
||||||
|
- [CVE-2026-0283](https://security.paloaltonetworks.com/CVE-2026-0283)
|
||||||
|
- [CVE-2026-0287](https://security.paloaltonetworks.com/CVE-2026-0287)
|
||||||
|
- [CVE-2026-0279](https://security.paloaltonetworks.com/CVE-2026-0279)
|
||||||
|
- [CVE-2026-0282](https://security.paloaltonetworks.com/CVE-2026-0282)
|
||||||
|
- [CVE-2026-0288](https://security.paloaltonetworks.com/CVE-2026-0288)
|
||||||
|
- [CVE-2026-0286](https://security.paloaltonetworks.com/CVE-2026-0286)
|
||||||
|
- [CVE-2026-0285](https://security.paloaltonetworks.com/CVE-2026-0285)
|
||||||
|
- [CVE-2026-0280](https://security.paloaltonetworks.com/CVE-2026-0280)
|
||||||
|
- [CVE-2026-0284](https://security.paloaltonetworks.com/CVE-2026-0284)
|
||||||
|
- [CVE-2026-0281](https://security.paloaltonetworks.com/CVE-2026-0281)
|
||||||
|
|
||||||
|
## PAN-328145
|
||||||
|
|
||||||
|
Fixed an issue where a firewall functioning as an Area Border Router did not correctly translate NSSA Type-7 LSAs to Type-5 LSAs when OSPF neighbors set the Nt bit in the NSSA Area, and routes were not advertised to upstream OSPF neighbors in the backbone area, which resulted in traffic being silently discarded.
|
||||||
|
|
||||||
|
## PAN-321699
|
||||||
|
|
||||||
|
Fixed an issue where device telemetry intermittently failed to send files, which resulted in critical alerts in system files.
|
||||||
|
|
||||||
|
## PAN-319228
|
||||||
|
|
||||||
|
Fixed an issue where External Dynamic List (EDL) refresh and commit operations remained in a pending state, which prevented any subsequent operations from completing.
|
||||||
|
|
||||||
|
## PAN-317466
|
||||||
|
|
||||||
|
Fixed an issue where SIP sessions stopped progressing after the firewall received fragmented packets, fragmented at header field.
|
||||||
|
|
||||||
|
## PAN-313700
|
||||||
|
|
||||||
|
Fixed an issue where an unexpected reboot occurred when Inline Cloud Analysis was enabled in an Anti-Spyware and Vulnerability profile.
|
||||||
|
|
||||||
|
## PAN-311658
|
||||||
|
|
||||||
|
Fixed an issue where the reportd process stopped responding, which caused the firewall to reboot.
|
||||||
|
|
||||||
|
## PAN-308775
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
Firewalls in active/passive configurations only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where NTP status intermittently showed as rejected on the active firewall, which prevented the firewalls from synchronizing time.
|
||||||
|
|
||||||
|
## PAN-308606
|
||||||
|
|
||||||
|
Fixed an issue where traffic was blocked due to a mismatch between the URL category specified in the Security policy rule and the URL filter profile when custom URL categories with the same FQDN were configured.
|
||||||
|
|
||||||
|
## PAN-305835
|
||||||
|
|
||||||
|
Fixed an issue where firewalls with Memory Integrity Checking Architecture enabled rebooted unexpectedly due to accessing an invalid memory address. This occurred because the forwarding data structure index exceeded its designed limit.
|
||||||
|
|
||||||
|
## PAN-291804
|
||||||
|
|
||||||
|
Fixed an issue on Panorama where deleting objects resulted in errors indicating references in Security policy rules.
|
||||||
|
|
||||||
|
## PAN-250445
|
||||||
|
|
||||||
|
Fixed an issue where DLP logs accumulated in the logrcvr cache when using DLP in mirror mode.
|
||||||
|
|
||||||
|
## PAN-246699
|
||||||
|
|
||||||
|
Fixed an issue on Panorama where **Rule Usage** and **Apps Seen** under Security policy rules stopped incrementing.
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 11.2.13
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address the following CVEs:
|
||||||
|
|
||||||
|
- [CVE-2026-0283](https://security.paloaltonetworks.com/CVE-2026-0283)
|
||||||
|
- [CVE-2026-0287](https://security.paloaltonetworks.com/CVE-2026-0287)
|
||||||
|
- [CVE-2026-0279](https://security.paloaltonetworks.com/CVE-2026-0279)
|
||||||
|
- [CVE-2026-0282](https://security.paloaltonetworks.com/CVE-2026-0282)
|
||||||
|
- [CVE-2026-0288](https://security.paloaltonetworks.com/CVE-2026-0288)
|
||||||
|
- [CVE-2026-0286](https://security.paloaltonetworks.com/CVE-2026-0286)
|
||||||
|
- [CVE-2026-0285](https://security.paloaltonetworks.com/CVE-2026-0285)
|
||||||
|
- [CVE-2026-0280](https://security.paloaltonetworks.com/CVE-2026-0280)
|
||||||
|
- [CVE-2026-0284](https://security.paloaltonetworks.com/CVE-2026-0284)
|
||||||
|
- [CVE-2026-0281](https://security.paloaltonetworks.com/CVE-2026-0281)
|
||||||
|
|
||||||
|
## PAN-328145
|
||||||
|
|
||||||
|
Fixed an issue where a firewall functioning as an Area Border Router did not correctly translate NSSA Type-7 LSAs to Type-5 LSAs when OSPF neighbors set the Nt bit in the NSSA Area, and routes were not advertised to upstream OSPF neighbors in the backbone area, which resulted in traffic being silently discarded.
|
||||||
|
|
||||||
|
## PAN-321699
|
||||||
|
|
||||||
|
Fixed an issue where device telemetry intermittently failed to send files, which resulted in critical alerts in system files.
|
||||||
|
|
||||||
|
## PAN-321150
|
||||||
|
|
||||||
|
Fixed an issue where the interface remained down after an upgrade.
|
||||||
|
|
||||||
|
## PAN-320598
|
||||||
|
|
||||||
|
Fixed an issue where internal and external DNS names did not resolve when connected to a GlobalProtect gateway.
|
||||||
|
|
||||||
|
## PAN-319798
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
Panorama virtual appliances in AWS environments only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where logging disks failed to mount or reported an unknown file system type.
|
||||||
|
|
||||||
|
## PAN-319793
|
||||||
|
|
||||||
|
Fixed an issue where, after upgrading to PAN-OS 12.1.5, GlobalProtect Clientless VPN failed to access JavaScripts.
|
||||||
|
|
||||||
|
## PAN-319266
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
Cloud IPS only
|
||||||
|
```
|
||||||
|
|
||||||
|
Increased scale limit for zone mappings.
|
||||||
|
|
||||||
|
## PAN-319228
|
||||||
|
|
||||||
|
Fixed an issue where External Dynamic List (EDL) refresh and commit operations remained in a pending state, which prevented any subsequent operations from completing.
|
||||||
|
|
||||||
|
## PAN-318120
|
||||||
|
|
||||||
|
Fixed an issue where SSL traffic was silently dropped when traffic was processed by a Security policy with an Anti-Spyware profile that had Inline cloud Analysis enabled for SSL C2 Detector with an action other than allow or alert.
|
||||||
|
|
||||||
|
## PAN-318106
|
||||||
|
|
||||||
|
Fixed an issue where SCM did not update device telemetry for the firewall after upgrading to an affected release.
|
||||||
|
|
||||||
|
## PAN-318030
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
VM-Series firewalls in Hyper-V only
|
||||||
|
```
|
||||||
|
|
||||||
|
) Fixed an issue where the throughput was reported to be twice as high as the actual traffic rate.
|
||||||
|
|
||||||
|
## PAN-317755
|
||||||
|
|
||||||
|
Fixed an issue on Panorama where selective push operations failed when plugin configurations included access-domain or log-collector references.
|
||||||
|
|
||||||
|
## PAN-317614
|
||||||
|
|
||||||
|
Fixed an issue where high throughput and increased packet rates caused high dataplane CPU usage.
|
||||||
|
|
||||||
|
## PAN-317466
|
||||||
|
|
||||||
|
Fixed an issue where SIP sessions stopped progressing after the firewall received fragmented packets, fragmented at header field.
|
||||||
|
|
||||||
|
## PAN-317215
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
VM-Series firewalls on ESXi with Intel E810 NICs using PCI passthrough
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where the brdagent process became unresponsive during data port initialization, which resulted in system instability, interface outages, HA split-brain conditions, and unexpected reboots during failover.
|
||||||
|
|
||||||
|
## PAN-315919
|
||||||
|
|
||||||
|
Fixed an issue where GlobalProtect pre-logon tunnel session was not cleared even after the user was logged in. With this fix, the session is cleared after the session timeout expires.
|
||||||
|
|
||||||
|
## PAN-315337
|
||||||
|
|
||||||
|
Fixed an issue where GlobalProtect throughput was reduced after an upgrade.
|
||||||
|
|
||||||
|
## PAN-315314
|
||||||
|
|
||||||
|
Fixed an issue where, when a push operation from Panorama to the firewall failed, accounting logs stopped forwarding.
|
||||||
|
|
||||||
|
## PAN-314512
|
||||||
|
|
||||||
|
Fixed an issue where the GlobalProtect portal became inaccessible when the dataplane was configured with a DHCP assigned IP address.
|
||||||
|
|
||||||
|
## PAN-314061
|
||||||
|
|
||||||
|
Fixed an issue where traffic was disrupted during IPSec rekey operations due to a 2 second delay in sending the DELETE message for the previous Security Association (SA) to the peer gateway after a new SA was negotiated.
|
||||||
|
|
||||||
|
## PAN-314020
|
||||||
|
|
||||||
|
Fixed an issue where the firewall did not decapsulate GENEVE packets when DNS Security retransmitted a DNS query after receiving a verdict from the cloud.
|
||||||
|
|
||||||
|
## PAN-313850
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
PA-1400 Series firewalls in HA configurations only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where a split-brain condition occurred and HA1/HA2 links went down while upgrading when the HA configuration used dataplane interfaces for HA1 and a combination of HSCI and Ethernet interfaces for HA2.
|
||||||
|
|
||||||
|
## PAN-313828
|
||||||
|
|
||||||
|
Fixed an issue where the firewall did not forward traffic due to memory issues on a forwarding component.
|
||||||
|
|
||||||
|
## PAN-312330
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
Firewalls in active/passive HA configurations only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where the Clientless VPN applications failed to load due to the firewall dataplane incorrectly processing session information.
|
||||||
|
|
||||||
|
## PAN-311658
|
||||||
|
|
||||||
|
Fixed an issue where the reportd process stopped responding, which caused the firewall to reboot.
|
||||||
|
|
||||||
|
## PAN-311285
|
||||||
|
|
||||||
|
Fixed an issue where a memory leak occurred related to the ospfd process, which caused RAM usage to continuously increase until the device stopped responding.
|
||||||
|
|
||||||
|
## PAN-311192
|
||||||
|
|
||||||
|
Fixed an issue where the device-telemetry collect-now process became unresponsive when the process was initiated multiple times with other processes running concurrently, which prevented subsequent telemetry collection.
|
||||||
|
|
||||||
|
## PAN-311040
|
||||||
|
|
||||||
|
Fixed an issue where the all_task process stopped responding and caused the firewall to reboot unexpectedly.
|
||||||
|
|
||||||
|
## PAN-310240
|
||||||
|
|
||||||
|
Fixed an issue where software packet buffers were completely utilized when performing a Data Loss Prevention longevity test.
|
||||||
|
|
||||||
|
## PAN-308775
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
Firewalls in active/passive configurations only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where NTP status intermittently showed as rejected on the active firewall, which prevented the firewalls from synchronizing time.
|
||||||
|
|
||||||
|
## PAN-307976
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
Firewalls in active/active HA configurations only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where tunnels failed to come up with the error message failed to find a socket for transmission.
|
||||||
|
|
||||||
|
## PAN-307618
|
||||||
|
|
||||||
|
Added a debug CLI command to address where remote networks for Prisma Access tenants randomly dropped monitoring packets from peer devices, which caused tunnels to be marked as down. This occurred when a CPU core suddenly experienced high utilization.
|
||||||
|
|
||||||
|
To utilize this fix, run debug dataplane set ssl-decrypt use-new-peek-window yes.
|
||||||
|
|
||||||
|
## PAN-307470
|
||||||
|
|
||||||
|
Fixed an issue where an External Dynamic List (EDL) fetch with an invalid certificate was skipped on newly provisioned GlobalProtect gateway instances.
|
||||||
|
|
||||||
|
## PAN-306356
|
||||||
|
|
||||||
|
Fixed an issue where the logrcvr process on a firewall stopped responding due to a document node being unexpectedly freed.
|
||||||
|
|
||||||
|
## PAN-300615
|
||||||
|
|
||||||
|
Fixed an issue where the pan_comm process stopped after multiple content versions were installed and the memory limits were reached.
|
||||||
|
|
||||||
|
## PAN-298960
|
||||||
|
|
||||||
|
Fixed an issue where the firewall continuously rebooted when the useridd process repeatedly restarted.
|
||||||
|
|
||||||
|
## PAN-296246
|
||||||
|
|
||||||
|
Fixed an issue where policy cache corruption led to unexpected policy rule behavior or operational instability. This occurred when an internal system process restarted while a commit was in progress or when a commit operation failed.
|
||||||
|
|
||||||
|
## PAN-295806
|
||||||
|
|
||||||
|
Fixed an issue where memory leaks on the configd process occurred due to a hash insert operation failing during connection management and SSL connections.
|
||||||
|
|
||||||
|
## PAN-294434
|
||||||
|
|
||||||
|
Fixed an issue where memory leaks occurred. These leaks were caused by two distinct scenarios: the failure to deallocate memory for a nodeset when a new nodeset was assigned to the same variable, and the failure to free a UUID hash table during error conditions.
|
||||||
|
|
||||||
|
## PAN-250445
|
||||||
|
|
||||||
|
Fixed an issue where DLP logs accumulated in the logrcvr cache when using DLP in mirror mode.
|
||||||
|
|
||||||
|
## PAN-246699
|
||||||
|
|
||||||
|
Fixed an issue on Panorama where **Rule Usage** and **Apps Seen** under Security policy rules stopped incrementing.
|
||||||
|
|
||||||
|
## PAN-234302
|
||||||
|
|
||||||
|
Fixed an issue where commit operations took longer than expected to complete due to EDL timeouts occurring on passive nodes when a service route was enabled.
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 11.2.4-h20
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address the following CVEs:
|
||||||
|
|
||||||
|
- [CVE-2026-0283](https://security.paloaltonetworks.com/CVE-2026-0283)
|
||||||
|
- [CVE-2026-0287](https://security.paloaltonetworks.com/CVE-2026-0287)
|
||||||
|
- [CVE-2026-0279](https://security.paloaltonetworks.com/CVE-2026-0279)
|
||||||
|
- [CVE-2026-0282](https://security.paloaltonetworks.com/CVE-2026-0282)
|
||||||
|
- [CVE-2026-0288](https://security.paloaltonetworks.com/CVE-2026-0288)
|
||||||
|
- [CVE-2026-0286](https://security.paloaltonetworks.com/CVE-2026-0286)
|
||||||
|
- [CVE-2026-0285](https://security.paloaltonetworks.com/CVE-2026-0285)
|
||||||
|
- [CVE-2026-0280](https://security.paloaltonetworks.com/CVE-2026-0280)
|
||||||
|
- [CVE-2026-0284](https://security.paloaltonetworks.com/CVE-2026-0284)
|
||||||
|
- [CVE-2026-0281](https://security.paloaltonetworks.com/CVE-2026-0281)
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 11.2.7-h17
|
||||||
|
---
|
||||||
|
|
||||||
|
## PAN-320598
|
||||||
|
|
||||||
|
Fixed an issue where internal and external DNS names did not resolve when connected to a GlobalProtect gateway.
|
||||||
|
|
||||||
|
## PAN-317755
|
||||||
|
|
||||||
|
Fixed an issue on Panorama where selective push operations failed when plugin configurations included access-domain or log-collector references.
|
||||||
|
|
||||||
|
## PAN-315337
|
||||||
|
|
||||||
|
Fixed an issue where GlobalProtect throughput was reduced after an upgrade.
|
||||||
|
|
||||||
|
## PAN-314319
|
||||||
|
|
||||||
|
Added a CLI command to enable and disable AHO software offload optimization.
|
||||||
|
|
||||||
|
## PAN-313606
|
||||||
|
|
||||||
|
Fixed an issue where Panorama pushed commits took longer than expected to complete without displaying an error message when committing due to slow cloud-app compilation.
|
||||||
|
|
||||||
|
## PAN-310263
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
VM-Series firewalls only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where enabling TLS1.3 in a decryption profile prevented access to websites.
|
||||||
|
|
||||||
|
## PAN-310240
|
||||||
|
|
||||||
|
Fixed an issue where software packet buffers were completely utilized when performing a Data Loss Prevention longevity test.
|
||||||
|
|
||||||
|
## PAN-307618
|
||||||
|
|
||||||
|
Added a debug CLI command to address where remote networks for Prisma Access tenants randomly dropped monitoring packets from peer devices, which caused tunnels to be marked as down. This occurred when a CPU core suddenly experienced high utilization.
|
||||||
|
|
||||||
|
To utilize this fix, run debug dataplane set ssl-decrypt use-new-peek-window yes.
|
||||||
|
|
||||||
|
## PAN-307470
|
||||||
|
|
||||||
|
Fixed an issue where an External Dynamic List (EDL) fetch with an invalid certificate was skipped on newly provisioned GlobalProtect gateway instances.
|
||||||
|
|
||||||
|
## PAN-266905
|
||||||
|
|
||||||
|
Fixed an issue where sessions ended with the message decrypt error in the logs for traffic that matched a **no-decrypt** policy.
|
||||||
|
|
||||||
|
## PAN-234302
|
||||||
|
|
||||||
|
Fixed an issue where commit operations took longer than expected to complete due to EDL timeouts occurring on passive nodes when a service route was enabled.
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 11.2.7-h18
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address the following CVEs:
|
||||||
|
|
||||||
|
- [CVE-2026-0283](https://security.paloaltonetworks.com/CVE-2026-0283)
|
||||||
|
- [CVE-2026-0287](https://security.paloaltonetworks.com/CVE-2026-0287)
|
||||||
|
- [CVE-2026-0279](https://security.paloaltonetworks.com/CVE-2026-0279)
|
||||||
|
- [CVE-2026-0282](https://security.paloaltonetworks.com/CVE-2026-0282)
|
||||||
|
- [CVE-2026-0288](https://security.paloaltonetworks.com/CVE-2026-0288)
|
||||||
|
- [CVE-2026-0286](https://security.paloaltonetworks.com/CVE-2026-0286)
|
||||||
|
- [CVE-2026-0285](https://security.paloaltonetworks.com/CVE-2026-0285)
|
||||||
|
- [CVE-2026-0280](https://security.paloaltonetworks.com/CVE-2026-0280)
|
||||||
|
- [CVE-2026-0284](https://security.paloaltonetworks.com/CVE-2026-0284)
|
||||||
|
- [CVE-2026-0281](https://security.paloaltonetworks.com/CVE-2026-0281)
|
||||||
|
|
||||||
|
## PAN-308775
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
Firewalls in active/passive configurations only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where NTP status intermittently showed as rejected on the active firewall, which prevented the firewalls from synchronizing time.
|
||||||
|
|
||||||
|
## PAN-308606
|
||||||
|
|
||||||
|
Fixed an issue where traffic was blocked due to a mismatch between the URL category specified in the Security policy rule and the URL filter profile when custom URL categories with the same FQDN were configured.
|
||||||
|
|
||||||
|
## PAN-295854
|
||||||
|
|
||||||
|
Fixed an issue where the firewall generated two URL logs for a single session.
|
||||||
|
|
||||||
|
## PAN-293707
|
||||||
|
|
||||||
|
Fixed an issue where the iotd process failed to install DPI Cloud server FQDN due to a configuration parsing failure, caused by the configuration XML memory buffer not being NULL terminated. This resulted in the accumulation of EAL logs and DLP forwarding being stopped.
|
||||||
|
|
||||||
|
## PAN-289895
|
||||||
|
|
||||||
|
Fixed an issue where, when SSL decryption was enabled, traffic matching a deny rule was incorrectly allowed until the SSL handshake was complete.
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 12.1.4-h7
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address [CVE-2026-0273](https://security-stg.paloaltonetworks.com/CVE-2026-0273) and [CVE-2026-0272](https://security-stg.paloaltonetworks.com/CVE-2026-0272).
|
||||||
|
|
||||||
|
## PAN-317755
|
||||||
|
|
||||||
|
Fixed an issue on Panorama where selective push operations failed when plugin configurations included access-domain or log-collector references.
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 12.1.4-h8
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address the following CVEs:
|
||||||
|
|
||||||
|
- [CVE-2026-0283](https://security.paloaltonetworks.com/CVE-2026-0283)
|
||||||
|
- [CVE-2026-0287](https://security.paloaltonetworks.com/CVE-2026-0287)
|
||||||
|
- [CVE-2026-0279](https://security.paloaltonetworks.com/CVE-2026-0279)
|
||||||
|
- [CVE-2026-0282](https://security.paloaltonetworks.com/CVE-2026-0282)
|
||||||
|
- [CVE-2026-0288](https://security.paloaltonetworks.com/CVE-2026-0288)
|
||||||
|
- [CVE-2026-0286](https://security.paloaltonetworks.com/CVE-2026-0286)
|
||||||
|
- [CVE-2026-0285](https://security.paloaltonetworks.com/CVE-2026-0285)
|
||||||
|
- [CVE-2026-0280](https://security.paloaltonetworks.com/CVE-2026-0280)
|
||||||
|
- [CVE-2026-0284](https://security.paloaltonetworks.com/CVE-2026-0284)
|
||||||
|
- [CVE-2026-0281](https://security.paloaltonetworks.com/CVE-2026-0281)
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 12.1.7-h1
|
||||||
|
---
|
||||||
|
|
||||||
|
## PAN-320598
|
||||||
|
|
||||||
|
Fixed an issue where internal and external DNS names did not resolve when connected to a GlobalProtect gateway.
|
||||||
|
|
||||||
|
## PAN-317755
|
||||||
|
|
||||||
|
Fixed an issue on Panorama where selective push operations failed when plugin configurations included access-domain or log-collector references.
|
||||||
|
|
||||||
|
## PAN-317133
|
||||||
|
|
||||||
|
Fixed an issue where you were unable to generate a ticket for the GlobalProtect portal.
|
||||||
|
|
||||||
|
## PAN-302150
|
||||||
|
|
||||||
|
```caveat
|
||||||
|
Panorama appliances only
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed an issue where you were unable to successfully configure log collector groups due to the master node settings not populating automatically.
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
type: Addressed
|
||||||
|
product: PAN-OS
|
||||||
|
version: 12.1.7-h2
|
||||||
|
---
|
||||||
|
|
||||||
|
## BLANK-000000
|
||||||
|
|
||||||
|
Fixes were made to address the following CVEs:
|
||||||
|
|
||||||
|
- [CVE-2026-0283](https://security.paloaltonetworks.com/CVE-2026-0283)
|
||||||
|
- [CVE-2026-0287](https://security.paloaltonetworks.com/CVE-2026-0287)
|
||||||
|
- [CVE-2026-0279](https://security.paloaltonetworks.com/CVE-2026-0279)
|
||||||
|
- [CVE-2026-0282](https://security.paloaltonetworks.com/CVE-2026-0282)
|
||||||
|
- [CVE-2026-0288](https://security.paloaltonetworks.com/CVE-2026-0288)
|
||||||
|
- [CVE-2026-0286](https://security.paloaltonetworks.com/CVE-2026-0286)
|
||||||
|
- [CVE-2026-0285](https://security.paloaltonetworks.com/CVE-2026-0285)
|
||||||
|
- [CVE-2026-0280](https://security.paloaltonetworks.com/CVE-2026-0280)
|
||||||
|
- [CVE-2026-0284](https://security.paloaltonetworks.com/CVE-2026-0284)
|
||||||
|
- [CVE-2026-0281](https://security.paloaltonetworks.com/CVE-2026-0281)
|
||||||
|
|
||||||
|
## PAN-324014
|
||||||
|
|
||||||
|
Fixed an issue where logging disks were reported with a byte size of zero in the system status even when they were properly mounted.
|
||||||
|
|
||||||
|
## PAN-323809
|
||||||
|
|
||||||
|
Fixed an issue where attempting to generate a ticket for the GlobalProtect portal caused Panorama to restart unexpectedly with the error message tpl is invalid.
|
||||||
|
|
||||||
|
## PAN-318619
|
||||||
|
|
||||||
|
Fixed an issue where Geneve ingress traffic did not use the correct public IP address for return traffic.
|
||||||
|
|
||||||
|
## PAN-317867
|
||||||
|
|
||||||
|
Fixed an issue where Panorama became inaccessible and a manual reboot was required to restore access. This occurred due rapid increase in memory usage on the reportd process, which led to OOM events.
|
||||||
|
|
||||||
|
## PAN-314512
|
||||||
|
|
||||||
|
Fixed an issue where the GlobalProtect portal became inaccessible when the dataplane was configured with a DHCP assigned IP address.
|
||||||
|
|
||||||
|
## PAN-313700
|
||||||
|
|
||||||
|
Fixed an issue where an unexpected reboot occurred when Inline Cloud Analysis was enabled in an Anti-Spyware and Vulnerability profile.
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,633 @@
|
|||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"nodes": [
|
||||||
|
"[\"GlobalProtect\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.3\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.3\"]>[\"GlobalProtect\",\"6\",\"6.3\",\"6.3.1.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.3\"]>[\"GlobalProtect\",\"6\",\"6.3\",\"6.3.2.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.3\"]>[\"GlobalProtect\",\"6\",\"6.3\",\"6.3.3-h1.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.3\"]>[\"GlobalProtect\",\"6\",\"6.3\",\"6.3.3-h11.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.3\"]>[\"GlobalProtect\",\"6\",\"6.3\",\"6.3.3-h12.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.3\"]>[\"GlobalProtect\",\"6\",\"6.3\",\"6.3.3-h2.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.3\"]>[\"GlobalProtect\",\"6\",\"6.3\",\"6.3.3-h3.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.3\"]>[\"GlobalProtect\",\"6\",\"6.3\",\"6.3.3-h4.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.3\"]>[\"GlobalProtect\",\"6\",\"6.3\",\"6.3.3-h6.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.3\"]>[\"GlobalProtect\",\"6\",\"6.3\",\"6.3.3-h7.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.3\"]>[\"GlobalProtect\",\"6\",\"6.3\",\"6.3.3-h8.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.3\"]>[\"GlobalProtect\",\"6\",\"6.3\",\"6.3.3-h9.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.3\"]>[\"GlobalProtect\",\"6\",\"6.3\",\"6.3.3.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]>[\"GlobalProtect\",\"6\",\"6.2\",\"6.2.1.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]>[\"GlobalProtect\",\"6\",\"6.2\",\"6.2.2.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]>[\"GlobalProtect\",\"6\",\"6.2\",\"6.2.3-c287.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]>[\"GlobalProtect\",\"6\",\"6.2\",\"6.2.3.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]>[\"GlobalProtect\",\"6\",\"6.2\",\"6.2.4.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]>[\"GlobalProtect\",\"6\",\"6.2\",\"6.2.5.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]>[\"GlobalProtect\",\"6\",\"6.2\",\"6.2.6-c857.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]>[\"GlobalProtect\",\"6\",\"6.2\",\"6.2.6.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]>[\"GlobalProtect\",\"6\",\"6.2\",\"6.2.7.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]>[\"GlobalProtect\",\"6\",\"6.2\",\"6.2.8-h1.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]>[\"GlobalProtect\",\"6\",\"6.2\",\"6.2.8-h11.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]>[\"GlobalProtect\",\"6\",\"6.2\",\"6.2.8-h2.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]>[\"GlobalProtect\",\"6\",\"6.2\",\"6.2.8-h3.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]>[\"GlobalProtect\",\"6\",\"6.2\",\"6.2.8-h4.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]>[\"GlobalProtect\",\"6\",\"6.2\",\"6.2.8-h5.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]>[\"GlobalProtect\",\"6\",\"6.2\",\"6.2.8-h6.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]>[\"GlobalProtect\",\"6\",\"6.2\",\"6.2.8-h7.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]>[\"GlobalProtect\",\"6\",\"6.2\",\"6.2.8-h9.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.2\"]>[\"GlobalProtect\",\"6\",\"6.2\",\"6.2.8.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.1\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.1\"]>[\"GlobalProtect\",\"6\",\"6.1\",\"6.1.1.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.1\"]>[\"GlobalProtect\",\"6\",\"6.1\",\"6.1.2.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.1\"]>[\"GlobalProtect\",\"6\",\"6.1\",\"6.1.3.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.1\"]>[\"GlobalProtect\",\"6\",\"6.1\",\"6.1.4.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.1\"]>[\"GlobalProtect\",\"6\",\"6.1\",\"6.1.5.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.1\"]>[\"GlobalProtect\",\"6\",\"6.1\",\"6.1.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.0\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.0\"]>[\"GlobalProtect\",\"6\",\"6.0\",\"6.0.1.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.0\"]>[\"GlobalProtect\",\"6\",\"6.0\",\"6.0.10-814.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.0\"]>[\"GlobalProtect\",\"6\",\"6.0\",\"6.0.10-c826.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.0\"]>[\"GlobalProtect\",\"6\",\"6.0\",\"6.0.10.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.0\"]>[\"GlobalProtect\",\"6\",\"6.0\",\"6.0.11.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.0\"]>[\"GlobalProtect\",\"6\",\"6.0\",\"6.0.3.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.0\"]>[\"GlobalProtect\",\"6\",\"6.0\",\"6.0.4-c26.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.0\"]>[\"GlobalProtect\",\"6\",\"6.0\",\"6.0.4.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.0\"]>[\"GlobalProtect\",\"6\",\"6.0\",\"6.0.5.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.0\"]>[\"GlobalProtect\",\"6\",\"6.0\",\"6.0.7.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.0\"]>[\"GlobalProtect\",\"6\",\"6.0\",\"6.0.8.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"6\"]>[\"GlobalProtect\",\"6\",\"6.0\"]>[\"GlobalProtect\",\"6\",\"6.0\",\"6.0.md\"]",
|
||||||
|
"[\"GlobalProtect\"]>[\"GlobalProtect\",\"5\"]",
|
||||||
|
"[\"GlobalProtect-Android\"]",
|
||||||
|
"[\"GlobalProtect-Android\"]>[\"GlobalProtect-Android\",\"6.0.1.md\"]",
|
||||||
|
"[\"GlobalProtect-Android\"]>[\"GlobalProtect-Android\",\"6.0.12.md\"]",
|
||||||
|
"[\"GlobalProtect-Android\"]>[\"GlobalProtect-Android\",\"6.0.2.md\"]",
|
||||||
|
"[\"GlobalProtect-Android\"]>[\"GlobalProtect-Android\",\"6.0.3.md\"]",
|
||||||
|
"[\"GlobalProtect-Android\"]>[\"GlobalProtect-Android\",\"6.0.4.md\"]",
|
||||||
|
"[\"GlobalProtect-Android\"]>[\"GlobalProtect-Android\",\"6.0.5.md\"]",
|
||||||
|
"[\"GlobalProtect-Android\"]>[\"GlobalProtect-Android\",\"6.1.0.md\"]",
|
||||||
|
"[\"GlobalProtect-Android\"]>[\"GlobalProtect-Android\",\"6.1.10.md\"]",
|
||||||
|
"[\"GlobalProtect-Android\"]>[\"GlobalProtect-Android\",\"6.1.11.md\"]",
|
||||||
|
"[\"GlobalProtect-Android\"]>[\"GlobalProtect-Android\",\"6.1.12.md\"]",
|
||||||
|
"[\"GlobalProtect-Android\"]>[\"GlobalProtect-Android\",\"6.1.4.md\"]",
|
||||||
|
"[\"GlobalProtect-Android\"]>[\"GlobalProtect-Android\",\"6.1.5.md\"]",
|
||||||
|
"[\"GlobalProtect-Android\"]>[\"GlobalProtect-Android\",\"6.1.6.md\"]",
|
||||||
|
"[\"GlobalProtect-Android\"]>[\"GlobalProtect-Android\",\"6.1.7.md\"]",
|
||||||
|
"[\"GlobalProtect-Android\"]>[\"GlobalProtect-Android\",\"6.1.9.md\"]",
|
||||||
|
"[\"GlobalProtect-Android\"]>[\"GlobalProtect-Android\",\"6.1.md\"]",
|
||||||
|
"[\"GlobalProtect-iOS\"]",
|
||||||
|
"[\"GlobalProtect-iOS\"]>[\"GlobalProtect-iOS\",\"6.0.12.md\"]",
|
||||||
|
"[\"GlobalProtect-iOS\"]>[\"GlobalProtect-iOS\",\"6.0.2.md\"]",
|
||||||
|
"[\"GlobalProtect-iOS\"]>[\"GlobalProtect-iOS\",\"6.0.4.md\"]",
|
||||||
|
"[\"GlobalProtect-iOS\"]>[\"GlobalProtect-iOS\",\"6.0.5.md\"]",
|
||||||
|
"[\"GlobalProtect-iOS\"]>[\"GlobalProtect-iOS\",\"6.0.6.md\"]",
|
||||||
|
"[\"GlobalProtect-iOS\"]>[\"GlobalProtect-iOS\",\"6.1.0.md\"]",
|
||||||
|
"[\"GlobalProtect-iOS\"]>[\"GlobalProtect-iOS\",\"6.1.10.md\"]",
|
||||||
|
"[\"GlobalProtect-iOS\"]>[\"GlobalProtect-iOS\",\"6.1.11.md\"]",
|
||||||
|
"[\"GlobalProtect-iOS\"]>[\"GlobalProtect-iOS\",\"6.1.4.md\"]",
|
||||||
|
"[\"GlobalProtect-iOS\"]>[\"GlobalProtect-iOS\",\"6.1.5.md\"]",
|
||||||
|
"[\"GlobalProtect-iOS\"]>[\"GlobalProtect-iOS\",\"6.1.6.md\"]",
|
||||||
|
"[\"GlobalProtect-iOS\"]>[\"GlobalProtect-iOS\",\"6.1.7.md\"]",
|
||||||
|
"[\"GlobalProtect-iOS\"]>[\"GlobalProtect-iOS\",\"6.1.9.md\"]",
|
||||||
|
"[\"GlobalProtect-iOS\"]>[\"GlobalProtect-iOS\",\"6.1.md\"]",
|
||||||
|
"[\"GlobalProtect-Linux\"]",
|
||||||
|
"[\"GlobalProtect-Linux\"]>[\"GlobalProtect-Linux\",\"6\"]",
|
||||||
|
"[\"GlobalProtect-Linux\"]>[\"GlobalProtect-Linux\",\"6\"]>[\"GlobalProtect-Linux\",\"6\",\"6.3\"]",
|
||||||
|
"[\"GlobalProtect-Linux\"]>[\"GlobalProtect-Linux\",\"6\"]>[\"GlobalProtect-Linux\",\"6\",\"6.3\"]>[\"GlobalProtect-Linux\",\"6\",\"6.3\",\"6.3.3-h1.md\"]",
|
||||||
|
"[\"GlobalProtect-Linux\"]>[\"GlobalProtect-Linux\",\"6\"]>[\"GlobalProtect-Linux\",\"6\",\"6.3\"]>[\"GlobalProtect-Linux\",\"6\",\"6.3\",\"6.3.3.md\"]",
|
||||||
|
"[\"GlobalProtect-Linux\"]>[\"GlobalProtect-Linux\",\"6\"]>[\"GlobalProtect-Linux\",\"6\",\"6.2\"]",
|
||||||
|
"[\"GlobalProtect-Linux\"]>[\"GlobalProtect-Linux\",\"6\"]>[\"GlobalProtect-Linux\",\"6\",\"6.2\"]>[\"GlobalProtect-Linux\",\"6\",\"6.2\",\"6.2.0.md\"]",
|
||||||
|
"[\"GlobalProtect-Linux\"]>[\"GlobalProtect-Linux\",\"6\"]>[\"GlobalProtect-Linux\",\"6\",\"6.2\"]>[\"GlobalProtect-Linux\",\"6\",\"6.2\",\"6.2.1.md\"]",
|
||||||
|
"[\"GlobalProtect-Linux\"]>[\"GlobalProtect-Linux\",\"6\"]>[\"GlobalProtect-Linux\",\"6\",\"6.2\"]>[\"GlobalProtect-Linux\",\"6\",\"6.2\",\"6.2.6.md\"]",
|
||||||
|
"[\"GlobalProtect-Linux\"]>[\"GlobalProtect-Linux\",\"6\"]>[\"GlobalProtect-Linux\",\"6\",\"6.2\"]>[\"GlobalProtect-Linux\",\"6\",\"6.2\",\"6.2.7.md\"]",
|
||||||
|
"[\"GlobalProtect-Linux\"]>[\"GlobalProtect-Linux\",\"6\"]>[\"GlobalProtect-Linux\",\"6\",\"6.2\"]>[\"GlobalProtect-Linux\",\"6\",\"6.2\",\"6.2.8.md\"]",
|
||||||
|
"[\"GlobalProtect-Linux\"]>[\"GlobalProtect-Linux\",\"6\"]>[\"GlobalProtect-Linux\",\"6\",\"6.2\"]>[\"GlobalProtect-Linux\",\"6\",\"6.2\",\"6.2.9.md\"]",
|
||||||
|
"[\"GlobalProtect-Linux\"]>[\"GlobalProtect-Linux\",\"6\"]>[\"GlobalProtect-Linux\",\"6\",\"6.1\"]",
|
||||||
|
"[\"GlobalProtect-Linux\"]>[\"GlobalProtect-Linux\",\"6\"]>[\"GlobalProtect-Linux\",\"6\",\"6.1\"]>[\"GlobalProtect-Linux\",\"6\",\"6.1\",\"6.1.1.md\"]",
|
||||||
|
"[\"GlobalProtect-Linux\"]>[\"GlobalProtect-Linux\",\"6\"]>[\"GlobalProtect-Linux\",\"6\",\"6.1\"]>[\"GlobalProtect-Linux\",\"6\",\"6.1\",\"6.1.3.md\"]",
|
||||||
|
"[\"GlobalProtect-Linux\"]>[\"GlobalProtect-Linux\",\"6\"]>[\"GlobalProtect-Linux\",\"6\",\"6.1\"]>[\"GlobalProtect-Linux\",\"6\",\"6.1\",\"6.1.4.md\"]",
|
||||||
|
"[\"GlobalProtect-Linux\"]>[\"GlobalProtect-Linux\",\"6\"]>[\"GlobalProtect-Linux\",\"6\",\"6.1\"]>[\"GlobalProtect-Linux\",\"6\",\"6.1\",\"6.1.5.md\"]",
|
||||||
|
"[\"GlobalProtect-Linux\"]>[\"GlobalProtect-Linux\",\"6\"]>[\"GlobalProtect-Linux\",\"6\",\"6.1\"]>[\"GlobalProtect-Linux\",\"6\",\"6.1\",\"6.1.md\"]",
|
||||||
|
"[\"PAN-OS\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"12\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"12\"]>[\"PAN-OS\",\"12\",\"12.1\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"12\"]>[\"PAN-OS\",\"12\",\"12.1\"]>[\"PAN-OS\",\"12\",\"12.1\",\"12.1.2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"12\"]>[\"PAN-OS\",\"12\",\"12.1\"]>[\"PAN-OS\",\"12\",\"12.1\",\"12.1.3-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"12\"]>[\"PAN-OS\",\"12\",\"12.1\"]>[\"PAN-OS\",\"12\",\"12.1\",\"12.1.3-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"12\"]>[\"PAN-OS\",\"12\",\"12.1\"]>[\"PAN-OS\",\"12\",\"12.1\",\"12.1.3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"12\"]>[\"PAN-OS\",\"12\",\"12.1\"]>[\"PAN-OS\",\"12\",\"12.1\",\"12.1.4-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"12\"]>[\"PAN-OS\",\"12\",\"12.1\"]>[\"PAN-OS\",\"12\",\"12.1\",\"12.1.4-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"12\"]>[\"PAN-OS\",\"12\",\"12.1\"]>[\"PAN-OS\",\"12\",\"12.1\",\"12.1.4-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"12\"]>[\"PAN-OS\",\"12\",\"12.1\"]>[\"PAN-OS\",\"12\",\"12.1\",\"12.1.4-h6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"12\"]>[\"PAN-OS\",\"12\",\"12.1\"]>[\"PAN-OS\",\"12\",\"12.1\",\"12.1.4-h7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"12\"]>[\"PAN-OS\",\"12\",\"12.1\"]>[\"PAN-OS\",\"12\",\"12.1\",\"12.1.4-h8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"12\"]>[\"PAN-OS\",\"12\",\"12.1\"]>[\"PAN-OS\",\"12\",\"12.1\",\"12.1.4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"12\"]>[\"PAN-OS\",\"12\",\"12.1\"]>[\"PAN-OS\",\"12\",\"12.1\",\"12.1.5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"12\"]>[\"PAN-OS\",\"12\",\"12.1\"]>[\"PAN-OS\",\"12\",\"12.1\",\"12.1.6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"12\"]>[\"PAN-OS\",\"12\",\"12.1\"]>[\"PAN-OS\",\"12\",\"12.1\",\"12.1.7-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"12\"]>[\"PAN-OS\",\"12\",\"12.1\"]>[\"PAN-OS\",\"12\",\"12.1\",\"12.1.7-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"12\"]>[\"PAN-OS\",\"12\",\"12.1\"]>[\"PAN-OS\",\"12\",\"12.1\",\"12.1.7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"12\"]>[\"PAN-OS\",\"12\",\"12.1\"]>[\"PAN-OS\",\"12\",\"12.1\",\"12.1.8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.0-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.0.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.1-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.10-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.10-h10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.10-h12.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.10-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.10-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.10-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.10-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.10-h6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.10-h7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.10-h8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.11.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.12.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.13.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.2-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.2-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.3-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.3-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.4-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.4-h10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.4-h11.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.4-h12.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.4-h14.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.4-h15.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.4-h17.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.4-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.4-h20.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.4-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.4-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.4-h6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.4-h7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.4-h8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.4-h9.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.5-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.7-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.7-h10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.7-h11.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.7-h12.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.7-h13.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.7-h14.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.7-h15.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.7-h17.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.7-h18.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.7-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.7-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.7-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.7-h7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.7-h8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.2\"]>[\"PAN-OS\",\"11\",\"11.2\",\"11.2.9.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.0-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.0-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.0-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.0-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.0.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.1-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.1-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.10-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.10-h10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.10-h12.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.10-h21.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.10-h25.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.10-h26.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.10-h27.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.10-h28.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.10-h30.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.10-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.10-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.10-h7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.10-h9.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.11.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.12.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.13-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.13-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.13-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.13-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.13-h6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.13-h7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.13-h8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.13-h9.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.13.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.14.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.15.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.16.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.2-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.2-h12.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.2-h14.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.2-h15.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.2-h16.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.2-h18.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.2-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.2-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.2-h9.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.3-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.3-h10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.3-h11.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.3-h13.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.3-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.3-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.3-h6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.4-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.4-h13.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.4-h15.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.4-h16.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.4-h17.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.4-h18.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.4-h25.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.4-h27.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.4-h32.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.4-h33.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.4-h34.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.4-h35.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.4-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.4-h7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.4-h9.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.5-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h14.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h17.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h19.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h20.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h21.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h22.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h23.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h25.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h29.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h32.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h33.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h34.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h35.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6-h7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.7-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.7-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.7-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.7-h6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.7-h7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.7-h8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.1\"]>[\"PAN-OS\",\"11\",\"11.1\",\"11.1.9.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.0-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.0-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.0-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.0-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.0.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.1-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.1-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.1-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.1-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.2-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.2-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.2-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.2-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.2-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.3-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.3-h10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.3-h12.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.3-h13.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.3-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.3-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.4-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.4-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.4-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.4-h6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.5-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.5-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.6-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"11\"]>[\"PAN-OS\",\"11\",\"11.0\"]>[\"PAN-OS\",\"11\",\"11.0\",\"11.0.6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.0-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.0-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.0-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.0-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.0.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.1-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.1-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.1-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.10-h10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.10-h14.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.10-h17.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.10-h18.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.10-h21.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.10-h30.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.10-h31.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.10-h36.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.10-h37.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.10-h39.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.10-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.10-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.10-h7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.10-h9.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.11-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.11-h10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.11-h12.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.11-h6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.11.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.12.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.13-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.13-h10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.13-h16.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.13-h18.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.13-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.13-h21.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.13-h22.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.13-h23.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.13-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.13-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.13-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.13-h7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.13.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.14-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.15.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.16-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.16-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.16-h6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.16-h7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.16-h8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.16-h9.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.16.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.18-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.18-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.18-h6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.18-h7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.18-h8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.18.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.2-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.2-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.2-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.2-h6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.3-h11.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.3-h12.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.3-h13.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.3-h14.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.3-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.3-h9.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.4-h10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.4-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.4-h32.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.4-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.5-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.5-h6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.6-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.6-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.7-h12.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.7-h16.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.7-h18.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.7-h19.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.7-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.7-h34.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.7-h35.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.7-h36.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.8-h13.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.8-h18.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.9-h11.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.9-h14.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.9-h16.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.9-h18.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.9-h9.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.2\"]>[\"PAN-OS\",\"10\",\"10.2\",\"10.2.9.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.0.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.10-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.10-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.11-h10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.11-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.11.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.12-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.13-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.13-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.13.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.14-h10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.14-h11.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.14-h13.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.14-h14.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.14-h16.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.14-h19.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.14-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.14-h20.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.14-h6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.14-h8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.14.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.3-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.4-h6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.5-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.5-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.6-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.6-h6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.6-h7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.6-h9.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.7-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.8-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.8-h7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.8-h8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.1\"]>[\"PAN-OS\",\"10\",\"10.1\",\"10.1.9-h6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.0\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.0\"]>[\"PAN-OS\",\"10\",\"10.0\",\"10.0.1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.0\"]>[\"PAN-OS\",\"10\",\"10.0\",\"10.0.10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.0\"]>[\"PAN-OS\",\"10\",\"10.0\",\"10.0.11-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.0\"]>[\"PAN-OS\",\"10\",\"10.0\",\"10.0.11-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.0\"]>[\"PAN-OS\",\"10\",\"10.0\",\"10.0.11-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.0\"]>[\"PAN-OS\",\"10\",\"10.0\",\"10.0.12-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.0\"]>[\"PAN-OS\",\"10\",\"10.0\",\"10.0.2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.0\"]>[\"PAN-OS\",\"10\",\"10.0\",\"10.0.3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.0\"]>[\"PAN-OS\",\"10\",\"10.0\",\"10.0.4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.0\"]>[\"PAN-OS\",\"10\",\"10.0\",\"10.0.5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.0\"]>[\"PAN-OS\",\"10\",\"10.0\",\"10.0.6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.0\"]>[\"PAN-OS\",\"10\",\"10.0\",\"10.0.7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.0\"]>[\"PAN-OS\",\"10\",\"10.0\",\"10.0.8-h10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.0\"]>[\"PAN-OS\",\"10\",\"10.0\",\"10.0.8-h11.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.0\"]>[\"PAN-OS\",\"10\",\"10.0\",\"10.0.8-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.0\"]>[\"PAN-OS\",\"10\",\"10.0\",\"10.0.8-h8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.0\"]>[\"PAN-OS\",\"10\",\"10.0\",\"10.0.8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"10\"]>[\"PAN-OS\",\"10\",\"10.0\"]>[\"PAN-OS\",\"10\",\"10.0\",\"10.0.9.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.0.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.11-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.11-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.11-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.11-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.11.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.12-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.12-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.12-h6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.12-h7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.12.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.13-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.13-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.13-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.13-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.13.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.14-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.14-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.14-h7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.14-h8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.14.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.15-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.15.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.16-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.16-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.16-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.16.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.17-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.17.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.18.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.19.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.2-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.3-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.1\"]>[\"PAN-OS\",\"9\",\"9.1\",\"9.1.9.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.0.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.11.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.12.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.13.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.14-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.14-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.14.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.15.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.16-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.16-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.16-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.16-h6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.16-h7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.16.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.17-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.17-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.17-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.17.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.2-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.3-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.3-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.5-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.9-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"9\"]>[\"PAN-OS\",\"9\",\"9.0\"]>[\"PAN-OS\",\"9\",\"9.0\",\"9.0.9.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.0.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.10.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.11.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.12.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.13.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.14-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.14.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.15-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.15.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.16.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.17.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.18.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.19.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.20-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.20.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.21-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.21-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.21-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.21.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.22.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.23-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.23.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.24-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.24-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.24.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.25-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.25-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.25-h3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.25.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.26-h1.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.26.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.3.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.4-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.6-h2.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.6.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.7.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.8-h5.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.8.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.9-h4.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.9.md\"]",
|
||||||
|
"[\"PAN-OS\"]>[\"PAN-OS\",\"8\"]>[\"PAN-OS\",\"8\",\"8.1\"]>[\"PAN-OS\",\"8\",\"8.1\",\"8.1.md\"]",
|
||||||
|
"[\"Prisma Access Agent\"]",
|
||||||
|
"[\"Prisma Access Agent\"]>[\"Prisma Access Agent\",\"26\"]",
|
||||||
|
"[\"Prisma Access Agent\"]>[\"Prisma Access Agent\",\"26\"]>[\"Prisma Access Agent\",\"26\",\"26.1-Linux.md\"]",
|
||||||
|
"[\"Prisma Access Agent\"]>[\"Prisma Access Agent\",\"26\"]>[\"Prisma Access Agent\",\"26\",\"26.1.1.md\"]",
|
||||||
|
"[\"Prisma Access Agent\"]>[\"Prisma Access Agent\",\"26\"]>[\"Prisma Access Agent\",\"26\",\"26.1.2.md\"]",
|
||||||
|
"[\"Prisma Access Agent\"]>[\"Prisma Access Agent\",\"25\"]",
|
||||||
|
"[\"Prisma Access Agent\"]>[\"Prisma Access Agent\",\"25\"]>[\"Prisma Access Agent\",\"25\",\"25.1.md\"]",
|
||||||
|
"[\"Prisma Access Agent\"]>[\"Prisma Access Agent\",\"25\"]>[\"Prisma Access Agent\",\"25\",\"25.3.0-iOS.md\"]",
|
||||||
|
"[\"Prisma Access Agent\"]>[\"Prisma Access Agent\",\"25\"]>[\"Prisma Access Agent\",\"25\",\"25.3.0.11-Android.md\"]",
|
||||||
|
"[\"Prisma Access Agent\"]>[\"Prisma Access Agent\",\"25\"]>[\"Prisma Access Agent\",\"25\",\"25.3.1.md\"]",
|
||||||
|
"[\"Prisma Access Agent\"]>[\"Prisma Access Agent\",\"25\"]>[\"Prisma Access Agent\",\"25\",\"25.3.md\"]",
|
||||||
|
"[\"Prisma Access Agent\"]>[\"Prisma Access Agent\",\"25\"]>[\"Prisma Access Agent\",\"25\",\"25.4.md\"]",
|
||||||
|
"[\"Prisma Access Agent\"]>[\"Prisma Access Agent\",\"25\"]>[\"Prisma Access Agent\",\"25\",\"25.6.1.md\"]",
|
||||||
|
"[\"Prisma Access Agent\"]>[\"Prisma Access Agent\",\"25\"]>[\"Prisma Access Agent\",\"25\",\"25.6.2.md\"]",
|
||||||
|
"[\"Prisma Access Agent\"]>[\"Prisma Access Agent\",\"25\"]>[\"Prisma Access Agent\",\"25\",\"25.6.md\"]",
|
||||||
|
"[\"Prisma Access Agent\"]>[\"Prisma Access Agent\",\"25\"]>[\"Prisma Access Agent\",\"25\",\"25.7-Linux.md\"]",
|
||||||
|
"[\"Prisma Access Agent\"]>[\"Prisma Access Agent\",\"25\"]>[\"Prisma Access Agent\",\"25\",\"25.7.1.md\"]",
|
||||||
|
"[\"Prisma Access Agent\"]>[\"Prisma Access Agent\",\"25\"]>[\"Prisma Access Agent\",\"25\",\"25.7.md\"]"
|
||||||
|
]
|
||||||
|
}
|
||||||
+32
-4
@@ -12,9 +12,14 @@
|
|||||||
"12.1.4-h3.md",
|
"12.1.4-h3.md",
|
||||||
"12.1.4-h5.md",
|
"12.1.4-h5.md",
|
||||||
"12.1.4-h6.md",
|
"12.1.4-h6.md",
|
||||||
|
"12.1.4-h7.md",
|
||||||
|
"12.1.4-h8.md",
|
||||||
"12.1.5.md",
|
"12.1.5.md",
|
||||||
"12.1.6.md",
|
"12.1.6.md",
|
||||||
"12.1.7.md"
|
"12.1.7.md",
|
||||||
|
"12.1.7-h1.md",
|
||||||
|
"12.1.7-h2.md",
|
||||||
|
"12.1.8.md"
|
||||||
],
|
],
|
||||||
"known": [
|
"known": [
|
||||||
"12.1.2.md",
|
"12.1.2.md",
|
||||||
@@ -53,6 +58,7 @@
|
|||||||
"11.2.4-h14.md",
|
"11.2.4-h14.md",
|
||||||
"11.2.4-h15.md",
|
"11.2.4-h15.md",
|
||||||
"11.2.4-h17.md",
|
"11.2.4-h17.md",
|
||||||
|
"11.2.4-h20.md",
|
||||||
"11.2.5.md",
|
"11.2.5.md",
|
||||||
"11.2.5-h2.md",
|
"11.2.5-h2.md",
|
||||||
"11.2.6.md",
|
"11.2.6.md",
|
||||||
@@ -69,6 +75,8 @@
|
|||||||
"11.2.7-h13.md",
|
"11.2.7-h13.md",
|
||||||
"11.2.7-h14.md",
|
"11.2.7-h14.md",
|
||||||
"11.2.7-h15.md",
|
"11.2.7-h15.md",
|
||||||
|
"11.2.7-h17.md",
|
||||||
|
"11.2.7-h18.md",
|
||||||
"11.2.8.md",
|
"11.2.8.md",
|
||||||
"11.2.9.md",
|
"11.2.9.md",
|
||||||
"11.2.10.md",
|
"11.2.10.md",
|
||||||
@@ -81,8 +89,10 @@
|
|||||||
"11.2.10-h7.md",
|
"11.2.10-h7.md",
|
||||||
"11.2.10-h8.md",
|
"11.2.10-h8.md",
|
||||||
"11.2.10-h10.md",
|
"11.2.10-h10.md",
|
||||||
|
"11.2.10-h12.md",
|
||||||
"11.2.11.md",
|
"11.2.11.md",
|
||||||
"11.2.12.md"
|
"11.2.12.md",
|
||||||
|
"11.2.13.md"
|
||||||
],
|
],
|
||||||
"known": [
|
"known": [
|
||||||
"11.2.0.md",
|
"11.2.0.md",
|
||||||
@@ -141,6 +151,8 @@
|
|||||||
"11.1.4-h27.md",
|
"11.1.4-h27.md",
|
||||||
"11.1.4-h32.md",
|
"11.1.4-h32.md",
|
||||||
"11.1.4-h33.md",
|
"11.1.4-h33.md",
|
||||||
|
"11.1.4-h34.md",
|
||||||
|
"11.1.4-h35.md",
|
||||||
"11.1.5.md",
|
"11.1.5.md",
|
||||||
"11.1.5-h1.md",
|
"11.1.5-h1.md",
|
||||||
"11.1.6.md",
|
"11.1.6.md",
|
||||||
@@ -163,11 +175,14 @@
|
|||||||
"11.1.6-h32.md",
|
"11.1.6-h32.md",
|
||||||
"11.1.6-h33.md",
|
"11.1.6-h33.md",
|
||||||
"11.1.6-h34.md",
|
"11.1.6-h34.md",
|
||||||
|
"11.1.6-h35.md",
|
||||||
"11.1.7.md",
|
"11.1.7.md",
|
||||||
"11.1.7-h1.md",
|
"11.1.7-h1.md",
|
||||||
"11.1.7-h2.md",
|
"11.1.7-h2.md",
|
||||||
"11.1.7-h4.md",
|
"11.1.7-h4.md",
|
||||||
"11.1.7-h6.md",
|
"11.1.7-h6.md",
|
||||||
|
"11.1.7-h7.md",
|
||||||
|
"11.1.7-h8.md",
|
||||||
"11.1.8.md",
|
"11.1.8.md",
|
||||||
"11.1.9.md",
|
"11.1.9.md",
|
||||||
"11.1.10.md",
|
"11.1.10.md",
|
||||||
@@ -183,6 +198,7 @@
|
|||||||
"11.1.10-h26.md",
|
"11.1.10-h26.md",
|
||||||
"11.1.10-h27.md",
|
"11.1.10-h27.md",
|
||||||
"11.1.10-h28.md",
|
"11.1.10-h28.md",
|
||||||
|
"11.1.10-h30.md",
|
||||||
"11.1.11.md",
|
"11.1.11.md",
|
||||||
"11.1.12.md",
|
"11.1.12.md",
|
||||||
"11.1.13.md",
|
"11.1.13.md",
|
||||||
@@ -193,8 +209,10 @@
|
|||||||
"11.1.13-h6.md",
|
"11.1.13-h6.md",
|
||||||
"11.1.13-h7.md",
|
"11.1.13-h7.md",
|
||||||
"11.1.13-h8.md",
|
"11.1.13-h8.md",
|
||||||
|
"11.1.13-h9.md",
|
||||||
"11.1.14.md",
|
"11.1.14.md",
|
||||||
"11.1.15.md"
|
"11.1.15.md",
|
||||||
|
"11.1.16.md"
|
||||||
],
|
],
|
||||||
"known": [
|
"known": [
|
||||||
"11.1.0.md",
|
"11.1.0.md",
|
||||||
@@ -300,6 +318,8 @@
|
|||||||
"10.2.7-h18.md",
|
"10.2.7-h18.md",
|
||||||
"10.2.7-h19.md",
|
"10.2.7-h19.md",
|
||||||
"10.2.7-h34.md",
|
"10.2.7-h34.md",
|
||||||
|
"10.2.7-h35.md",
|
||||||
|
"10.2.7-h36.md",
|
||||||
"10.2.8.md",
|
"10.2.8.md",
|
||||||
"10.2.8-h13.md",
|
"10.2.8-h13.md",
|
||||||
"10.2.8-h18.md",
|
"10.2.8-h18.md",
|
||||||
@@ -322,6 +342,8 @@
|
|||||||
"10.2.10-h30.md",
|
"10.2.10-h30.md",
|
||||||
"10.2.10-h31.md",
|
"10.2.10-h31.md",
|
||||||
"10.2.10-h36.md",
|
"10.2.10-h36.md",
|
||||||
|
"10.2.10-h37.md",
|
||||||
|
"10.2.10-h39.md",
|
||||||
"10.2.11.md",
|
"10.2.11.md",
|
||||||
"10.2.11-h1.md",
|
"10.2.11-h1.md",
|
||||||
"10.2.11-h6.md",
|
"10.2.11-h6.md",
|
||||||
@@ -339,6 +361,8 @@
|
|||||||
"10.2.13-h16.md",
|
"10.2.13-h16.md",
|
||||||
"10.2.13-h18.md",
|
"10.2.13-h18.md",
|
||||||
"10.2.13-h21.md",
|
"10.2.13-h21.md",
|
||||||
|
"10.2.13-h22.md",
|
||||||
|
"10.2.13-h23.md",
|
||||||
"10.2.14-h1.md",
|
"10.2.14-h1.md",
|
||||||
"10.2.15.md",
|
"10.2.15.md",
|
||||||
"10.2.16.md",
|
"10.2.16.md",
|
||||||
@@ -346,10 +370,14 @@
|
|||||||
"10.2.16-h4.md",
|
"10.2.16-h4.md",
|
||||||
"10.2.16-h6.md",
|
"10.2.16-h6.md",
|
||||||
"10.2.16-h7.md",
|
"10.2.16-h7.md",
|
||||||
|
"10.2.16-h8.md",
|
||||||
|
"10.2.16-h9.md",
|
||||||
"10.2.18.md",
|
"10.2.18.md",
|
||||||
"10.2.18-h1.md",
|
"10.2.18-h1.md",
|
||||||
"10.2.18-h5.md",
|
"10.2.18-h5.md",
|
||||||
"10.2.18-h6.md"
|
"10.2.18-h6.md",
|
||||||
|
"10.2.18-h7.md",
|
||||||
|
"10.2.18-h8.md"
|
||||||
],
|
],
|
||||||
"known": [
|
"known": [
|
||||||
"10.2.0.md",
|
"10.2.0.md",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
<link rel="icon" type="image/svg+xml" href="icons/favicon.svg">
|
<link rel="icon" type="image/svg+xml" href="icons/favicon.svg">
|
||||||
<link rel="apple-touch-icon" href="apple-touch-icon.png">
|
<link rel="apple-touch-icon" href="apple-touch-icon.png">
|
||||||
<link rel="apple-touch-icon-precomposed" href="apple-touch-icon-precomposed.png">
|
<link rel="apple-touch-icon-precomposed" href="apple-touch-icon-precomposed.png">
|
||||||
|
<link rel="stylesheet" href="vendor/checkbox-tree/checkbox-tree.css">
|
||||||
<link rel="stylesheet" href="styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
import { getSortedKeys } from './sort.js';
|
||||||
|
|
||||||
|
export function expandFilePrefixLevel(root) {
|
||||||
|
if (!root || typeof root !== 'object') {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
return transformNode(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformNode(node) {
|
||||||
|
if (!node || typeof node !== 'object' || Array.isArray(node)) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasIssueArrays = Object.prototype.hasOwnProperty.call(node, 'addressed') ||
|
||||||
|
Object.prototype.hasOwnProperty.call(node, 'known');
|
||||||
|
const childKeys = Object.keys(node).filter(key => key !== 'addressed' && key !== 'known');
|
||||||
|
const transformedChildren = Object.fromEntries(
|
||||||
|
childKeys.map(key => [key, transformNode(node[key])])
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasIssueArrays) {
|
||||||
|
return transformedChildren;
|
||||||
|
}
|
||||||
|
|
||||||
|
const addressed = Array.isArray(node.addressed) ? node.addressed : [];
|
||||||
|
const known = Array.isArray(node.known) ? node.known : [];
|
||||||
|
if (addressed.length + known.length === 0) {
|
||||||
|
return childKeys.length > 0 ? transformedChildren : { addressed: [], known: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupedByPrefix = {};
|
||||||
|
for (const [issueType, files] of [['addressed', addressed], ['known', known]]) {
|
||||||
|
for (const fileName of files) {
|
||||||
|
const prefix = getFilePrefix(fileName);
|
||||||
|
groupedByPrefix[prefix] ??= { addressed: [], known: [] };
|
||||||
|
groupedByPrefix[prefix][issueType].push(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const transformedIssueFiles = groupHotfixPrefixes(groupedByPrefix);
|
||||||
|
return childKeys.length === 0
|
||||||
|
? transformedIssueFiles
|
||||||
|
: mergeTreeNodes(transformedChildren, transformedIssueFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
function groupHotfixPrefixes(groupedByPrefix) {
|
||||||
|
const byFamily = {};
|
||||||
|
for (const prefix of Object.keys(groupedByPrefix)) {
|
||||||
|
const family = getReleaseFamily(prefix);
|
||||||
|
byFamily[family] ??= [];
|
||||||
|
byFamily[family].push(prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = {};
|
||||||
|
for (const [family, members] of Object.entries(byFamily)) {
|
||||||
|
const hasHotfixes = members.length > 1 || members.some(member => member !== family);
|
||||||
|
if (!hasHotfixes && members[0] === family) {
|
||||||
|
result[family] = groupedByPrefix[family];
|
||||||
|
} else {
|
||||||
|
result[family] = Object.fromEntries(
|
||||||
|
members.map(member => [member, groupedByPrefix[member]])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getReleaseFamily(prefix) {
|
||||||
|
return String(prefix || '').replace(/-h\d+$/i, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFilePrefix(fileName) {
|
||||||
|
const baseName = String(fileName || '');
|
||||||
|
const underscoreIndex = baseName.indexOf('_');
|
||||||
|
if (underscoreIndex <= 0) {
|
||||||
|
return baseName.replace(/\.json$/i, '') || 'unknown';
|
||||||
|
}
|
||||||
|
return baseName.slice(0, underscoreIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeTreeNodes(left, right) {
|
||||||
|
if (!isObjectNode(left)) return right;
|
||||||
|
if (!isObjectNode(right)) return left;
|
||||||
|
const merged = { ...left };
|
||||||
|
|
||||||
|
for (const [key, rightValue] of Object.entries(right)) {
|
||||||
|
const leftValue = merged[key];
|
||||||
|
if (key === 'addressed' || key === 'known') {
|
||||||
|
const leftFiles = Array.isArray(leftValue) ? leftValue : [];
|
||||||
|
const rightFiles = Array.isArray(rightValue) ? rightValue : [];
|
||||||
|
merged[key] = Array.from(new Set([...leftFiles, ...rightFiles]));
|
||||||
|
} else if (isObjectNode(leftValue) && isObjectNode(rightValue)) {
|
||||||
|
merged[key] = mergeTreeNodes(leftValue, rightValue);
|
||||||
|
} else {
|
||||||
|
merged[key] = rightValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isObjectNode(value) {
|
||||||
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toTreeNodes(value, path = []) {
|
||||||
|
return getSortedKeys(value)
|
||||||
|
.filter(key => key !== 'addressed' && key !== 'known')
|
||||||
|
.map(key => {
|
||||||
|
const childValue = value[key];
|
||||||
|
const childPath = [...path, key];
|
||||||
|
const isObject = childValue && typeof childValue === 'object';
|
||||||
|
const hasFiles = isObject && ('addressed' in childValue || 'known' in childValue);
|
||||||
|
const children = isObject ? toTreeNodes(childValue, childPath) : [];
|
||||||
|
return {
|
||||||
|
id: JSON.stringify(childPath),
|
||||||
|
label: key,
|
||||||
|
selectable: hasFiles || children.length > 0,
|
||||||
|
metadata: {
|
||||||
|
path: childPath,
|
||||||
|
addressed: hasFiles && Array.isArray(childValue.addressed) ? childValue.addressed : [],
|
||||||
|
known: hasFiles && Array.isArray(childValue.known) ? childValue.known : []
|
||||||
|
},
|
||||||
|
...(children.length > 0 ? { children } : {})
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function enumerateTreePaths(nodes) {
|
||||||
|
const paths = [];
|
||||||
|
function visit(children, parentPath = '') {
|
||||||
|
for (const node of children) {
|
||||||
|
const path = parentPath ? `${parentPath}>${node.id}` : node.id;
|
||||||
|
paths.push(path);
|
||||||
|
visit(node.children ?? [], path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visit(nodes);
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildProductTree(products) {
|
||||||
|
return toTreeNodes(expandFilePrefixLevel(products));
|
||||||
|
}
|
||||||
+3
-150
@@ -1,6 +1,7 @@
|
|||||||
import { loadIssuesForCheckedPaths } from './issues.js';
|
import { loadIssuesForCheckedPaths } from './issues.js';
|
||||||
import { getCheckedFileRefs, initializeTree, renderProductTree, restoreTreeState } from './tree.js';
|
import { getCheckedFileRefs, initializeTree, renderProductTree, restoreTreeState } from './tree.js';
|
||||||
import { applyIssueSearchFilter, getIssueTypeFilters, initializeUI } from './ui.js';
|
import { applyIssueSearchFilter, getIssueTypeFilters, initializeUI } from './ui.js';
|
||||||
|
import { expandFilePrefixLevel } from './product-tree-model.js';
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
initializeUI({ onSelectionChange: refreshIssuesForCurrentSelection });
|
initializeUI({ onSelectionChange: refreshIssuesForCurrentSelection });
|
||||||
@@ -21,159 +22,11 @@ function refreshIssuesForCurrentSelection() {
|
|||||||
function loadProductTree() {
|
function loadProductTree() {
|
||||||
fetch('data/products.json')
|
fetch('data/products.json')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(async data => {
|
||||||
productsData = expandFilePrefixLevel(data);
|
productsData = expandFilePrefixLevel(data);
|
||||||
renderProductTree(productsData);
|
await renderProductTree(productsData);
|
||||||
restoreTreeState();
|
restoreTreeState();
|
||||||
refreshIssuesForCurrentSelection();
|
refreshIssuesForCurrentSelection();
|
||||||
})
|
})
|
||||||
.catch(error => console.error('Error loading products:', error));
|
.catch(error => console.error('Error loading products:', error));
|
||||||
}
|
}
|
||||||
|
|
||||||
function expandFilePrefixLevel(root) {
|
|
||||||
if (!root || typeof root !== 'object') {
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
return transformNode(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
function transformNode(node) {
|
|
||||||
if (!node || typeof node !== 'object' || Array.isArray(node)) {
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasIssueArrays = Object.prototype.hasOwnProperty.call(node, 'addressed') ||
|
|
||||||
Object.prototype.hasOwnProperty.call(node, 'known');
|
|
||||||
const childKeys = Object.keys(node).filter(key => key !== 'addressed' && key !== 'known');
|
|
||||||
|
|
||||||
const transformedChildren = {};
|
|
||||||
childKeys.forEach(key => {
|
|
||||||
transformedChildren[key] = transformNode(node[key]);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!hasIssueArrays) {
|
|
||||||
return transformedChildren;
|
|
||||||
}
|
|
||||||
|
|
||||||
const addressed = Array.isArray(node.addressed) ? node.addressed : [];
|
|
||||||
const known = Array.isArray(node.known) ? node.known : [];
|
|
||||||
const allFiles = [...addressed, ...known];
|
|
||||||
|
|
||||||
if (allFiles.length === 0) {
|
|
||||||
if (childKeys.length > 0) {
|
|
||||||
return transformedChildren;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
addressed: [],
|
|
||||||
known: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const groupedByPrefix = {};
|
|
||||||
|
|
||||||
addressed.forEach(fileName => {
|
|
||||||
const prefix = getFilePrefix(fileName);
|
|
||||||
if (!groupedByPrefix[prefix]) {
|
|
||||||
groupedByPrefix[prefix] = { addressed: [], known: [] };
|
|
||||||
}
|
|
||||||
groupedByPrefix[prefix].addressed.push(fileName);
|
|
||||||
});
|
|
||||||
|
|
||||||
known.forEach(fileName => {
|
|
||||||
const prefix = getFilePrefix(fileName);
|
|
||||||
if (!groupedByPrefix[prefix]) {
|
|
||||||
groupedByPrefix[prefix] = { addressed: [], known: [] };
|
|
||||||
}
|
|
||||||
groupedByPrefix[prefix].known.push(fileName);
|
|
||||||
});
|
|
||||||
|
|
||||||
const transformedIssueFiles = groupHotfixPrefixes(groupedByPrefix);
|
|
||||||
if (childKeys.length === 0) {
|
|
||||||
return transformedIssueFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mergeTreeNodes(transformedChildren, transformedIssueFiles);
|
|
||||||
}
|
|
||||||
|
|
||||||
function groupHotfixPrefixes(groupedByPrefix) {
|
|
||||||
const byFamily = {};
|
|
||||||
|
|
||||||
Object.keys(groupedByPrefix).forEach(prefix => {
|
|
||||||
const family = getReleaseFamily(prefix);
|
|
||||||
if (!byFamily[family]) {
|
|
||||||
byFamily[family] = [];
|
|
||||||
}
|
|
||||||
byFamily[family].push(prefix);
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = {};
|
|
||||||
|
|
||||||
Object.keys(byFamily).forEach(family => {
|
|
||||||
const members = byFamily[family];
|
|
||||||
const hasHotfixes = members.length > 1 || members.some(member => member !== family);
|
|
||||||
|
|
||||||
if (!hasHotfixes && members[0] === family) {
|
|
||||||
result[family] = groupedByPrefix[family];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const branch = {};
|
|
||||||
members.forEach(member => {
|
|
||||||
branch[member] = groupedByPrefix[member];
|
|
||||||
});
|
|
||||||
result[family] = branch;
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getReleaseFamily(prefix) {
|
|
||||||
return String(prefix || '').replace(/-h\d+$/i, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFilePrefix(fileName) {
|
|
||||||
const baseName = String(fileName || '');
|
|
||||||
const underscoreIndex = baseName.indexOf('_');
|
|
||||||
if (underscoreIndex <= 0) {
|
|
||||||
return baseName.replace(/\.json$/i, '') || 'unknown';
|
|
||||||
}
|
|
||||||
return baseName.slice(0, underscoreIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
function mergeTreeNodes(left, right) {
|
|
||||||
if (!isObjectNode(left)) {
|
|
||||||
return right;
|
|
||||||
}
|
|
||||||
if (!isObjectNode(right)) {
|
|
||||||
return left;
|
|
||||||
}
|
|
||||||
|
|
||||||
const merged = { ...left };
|
|
||||||
|
|
||||||
Object.keys(right).forEach(key => {
|
|
||||||
const leftValue = merged[key];
|
|
||||||
const rightValue = right[key];
|
|
||||||
|
|
||||||
if (key === 'addressed' || key === 'known') {
|
|
||||||
const leftFiles = Array.isArray(leftValue) ? leftValue : [];
|
|
||||||
const rightFiles = Array.isArray(rightValue) ? rightValue : [];
|
|
||||||
merged[key] = Array.from(new Set([...leftFiles, ...rightFiles]));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isObjectNode(leftValue) && isObjectNode(rightValue)) {
|
|
||||||
merged[key] = mergeTreeNodes(leftValue, rightValue);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
merged[key] = rightValue;
|
|
||||||
});
|
|
||||||
|
|
||||||
return merged;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isObjectNode(value) {
|
|
||||||
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
||||||
}
|
|
||||||
|
|||||||
+52
-296
@@ -1,85 +1,58 @@
|
|||||||
import { getSortedKeys } from './sort.js';
|
import { CheckboxTree, loadManifest } from '../vendor/checkbox-tree/checkbox-tree.js';
|
||||||
|
import { toTreeNodes } from './product-tree-model.js';
|
||||||
|
|
||||||
const STORAGE_KEY = 'tree-state';
|
const STORAGE_KEY = 'tree-state-v2';
|
||||||
|
let productTree = null;
|
||||||
let treeContainer = null;
|
|
||||||
let onSelectionChangeHandler = null;
|
|
||||||
|
|
||||||
export function initializeTree(containerId, onSelectionChange) {
|
export function initializeTree(containerId, onSelectionChange) {
|
||||||
treeContainer = document.getElementById(containerId);
|
const container = document.getElementById(containerId);
|
||||||
onSelectionChangeHandler = onSelectionChange;
|
if (!container) {
|
||||||
}
|
throw new Error(`Tree container not found: ${containerId}`);
|
||||||
|
|
||||||
export function renderProductTree(productsData) {
|
|
||||||
if (!treeContainer) {
|
|
||||||
throw new Error('Tree is not initialized. Call initializeTree first.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
treeContainer.innerHTML = '';
|
productTree?.destroy();
|
||||||
|
productTree = new CheckboxTree(container, {
|
||||||
|
storageKey: STORAGE_KEY,
|
||||||
|
initiallyCollapsed: true,
|
||||||
|
stateEncoding: 'tiered',
|
||||||
|
onSelectionChange: event => {
|
||||||
|
onSelectionChange(event);
|
||||||
|
pushTreeState();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
getSortedKeys(productsData).forEach(product => {
|
container.addEventListener('click', event => {
|
||||||
const productNode = createTreeNode(product, productsData[product], [product]);
|
if (event.target.closest('.checkbox-tree__toggle')) {
|
||||||
treeContainer.appendChild(productNode);
|
pushTreeState();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window.addEventListener('popstate', () => {
|
||||||
|
productTree?.restoreStateFromLocation();
|
||||||
|
onSelectionChange({
|
||||||
|
selectedIds: productTree?.getSelectedIds() ?? [],
|
||||||
|
selectedNodes: productTree?.getSelectedNodes() ?? []
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function renderProductTree(productsData) {
|
||||||
|
const manifestSource = await fetch('data/product-tree-manifest.json').then(response => response.json());
|
||||||
|
requireTree().options.manifest = loadManifest(manifestSource);
|
||||||
|
requireTree().setData(toTreeNodes(productsData));
|
||||||
|
}
|
||||||
|
|
||||||
export function getCheckedPaths() {
|
export function getCheckedPaths() {
|
||||||
if (!treeContainer) {
|
return productTree ? productTree.getSelectedNodes().map(node => node.metadata.path) : [];
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const checkedBoxes = treeContainer.querySelectorAll('input[type="checkbox"]:checked');
|
|
||||||
|
|
||||||
return Array.from(checkedBoxes)
|
|
||||||
.map(cb => cb.dataset.path)
|
|
||||||
.filter(Boolean)
|
|
||||||
.map(pathText => {
|
|
||||||
try {
|
|
||||||
return JSON.parse(pathText);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Invalid checkbox path data:', pathText, error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(pathArray => Array.isArray(pathArray) && pathArray.length > 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCheckedFileRefs() {
|
export function getCheckedFileRefs() {
|
||||||
if (!treeContainer) {
|
|
||||||
return {
|
|
||||||
addressedFiles: [],
|
|
||||||
knownFiles: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const addressedFiles = [];
|
const addressedFiles = [];
|
||||||
const knownFiles = [];
|
const knownFiles = [];
|
||||||
const checkedBoxes = treeContainer.querySelectorAll('input[type="checkbox"]:checked[data-has-files="1"]');
|
|
||||||
|
|
||||||
checkedBoxes.forEach(cb => {
|
|
||||||
const pathText = cb.dataset.path;
|
|
||||||
if (!pathText) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let path;
|
|
||||||
try {
|
|
||||||
path = JSON.parse(pathText);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Invalid checkbox path data:', pathText, error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Array.isArray(path) || path.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
productTree?.getSelectedNodes().forEach(node => {
|
||||||
|
const { path, addressed = [], known = [] } = node.metadata;
|
||||||
const productKey = path[0];
|
const productKey = path[0];
|
||||||
|
|
||||||
const addressed = parseJsonArray(cb.dataset.addressed);
|
|
||||||
addressed.forEach(fileName => addressedFiles.push({ productKey, fileName }));
|
addressed.forEach(fileName => addressedFiles.push({ productKey, fileName }));
|
||||||
|
|
||||||
const known = parseJsonArray(cb.dataset.known);
|
|
||||||
known.forEach(fileName => knownFiles.push({ productKey, fileName }));
|
known.forEach(fileName => knownFiles.push({ productKey, fileName }));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -89,255 +62,38 @@ export function getCheckedFileRefs() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTreeNode(name, value, path) {
|
// State restoration now happens when setData renders the generic tree.
|
||||||
const container = document.createElement('div');
|
export function restoreTreeState() {}
|
||||||
container.className = 'tree-item';
|
|
||||||
|
|
||||||
const isObjectValue = value && typeof value === 'object';
|
function requireTree() {
|
||||||
const hasFiles = isObjectValue && ('addressed' in value || 'known' in value);
|
if (!productTree) {
|
||||||
const childKeys = isObjectValue
|
throw new Error('Tree is not initialized. Call initializeTree first.');
|
||||||
? getSortedKeys(value).filter(key => key !== 'addressed' && key !== 'known')
|
|
||||||
: [];
|
|
||||||
const hasChildren = childKeys.length > 0;
|
|
||||||
|
|
||||||
if (hasChildren) {
|
|
||||||
container.classList.add('parent');
|
|
||||||
|
|
||||||
const toggle = document.createElement('span');
|
|
||||||
toggle.className = 'tree-toggle';
|
|
||||||
toggle.textContent = '▼';
|
|
||||||
toggle.style.cursor = 'pointer';
|
|
||||||
|
|
||||||
const label = document.createElement('label');
|
|
||||||
const checkbox = createCheckbox(path, hasFiles ? value : undefined);
|
|
||||||
label.appendChild(checkbox);
|
|
||||||
label.appendChild(document.createTextNode(name));
|
|
||||||
|
|
||||||
const childrenContainer = document.createElement('div');
|
|
||||||
childrenContainer.className = 'tree-children';
|
|
||||||
|
|
||||||
childKeys.forEach(childKey => {
|
|
||||||
const childNode = createTreeNode(childKey, value[childKey], [...path, childKey]);
|
|
||||||
childrenContainer.appendChild(childNode);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (shouldStartCollapsed(value)) {
|
|
||||||
childrenContainer.classList.add('collapsed');
|
|
||||||
toggle.textContent = '▶';
|
|
||||||
}
|
}
|
||||||
|
return productTree;
|
||||||
toggle.addEventListener('click', event => {
|
|
||||||
event.stopPropagation();
|
|
||||||
childrenContainer.classList.toggle('collapsed');
|
|
||||||
toggle.textContent = childrenContainer.classList.contains('collapsed') ? '▶' : '▼';
|
|
||||||
saveTreeState();
|
|
||||||
});
|
|
||||||
|
|
||||||
container.appendChild(toggle);
|
|
||||||
container.appendChild(label);
|
|
||||||
container.appendChild(childrenContainer);
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
const label = document.createElement('label');
|
|
||||||
if (hasFiles) {
|
|
||||||
const checkbox = createCheckbox(path, value);
|
|
||||||
label.appendChild(checkbox);
|
|
||||||
}
|
|
||||||
|
|
||||||
label.appendChild(document.createTextNode(name));
|
|
||||||
container.appendChild(label);
|
|
||||||
return container;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldStartCollapsed(value) {
|
function pushTreeState() {
|
||||||
if (!value || typeof value !== 'object') {
|
if (!productTree?.options.manifest) {
|
||||||
return false;
|
return;
|
||||||
|
}
|
||||||
|
const url = new URL(window.location.href);
|
||||||
|
url.hash = productTree.serializeStateToFragment();
|
||||||
|
if (url.href !== window.location.href) {
|
||||||
|
history.pushState(null, '', url);
|
||||||
}
|
}
|
||||||
|
|
||||||
const childKeys = Object.keys(value).filter(key => key !== 'addressed' && key !== 'known');
|
|
||||||
return childKeys.length > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function dedupeFileRefs(fileRefs) {
|
function dedupeFileRefs(fileRefs) {
|
||||||
const seen = new Set();
|
const seen = new Set();
|
||||||
return fileRefs.filter(ref => {
|
return fileRefs.filter(({ productKey, fileName }) => {
|
||||||
const productKey = String(ref && ref.productKey ? ref.productKey : '');
|
|
||||||
const fileName = String(ref && ref.fileName ? ref.fileName : '');
|
|
||||||
if (!productKey || !fileName) {
|
if (!productKey || !fileName) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = `${productKey}::${fileName}`;
|
const key = `${productKey}::${fileName}`;
|
||||||
if (seen.has(key)) {
|
if (seen.has(key)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
seen.add(key);
|
seen.add(key);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createCheckbox(path, value) {
|
|
||||||
const checkbox = document.createElement('input');
|
|
||||||
checkbox.type = 'checkbox';
|
|
||||||
checkbox.value = path.join('/');
|
|
||||||
checkbox.dataset.path = JSON.stringify(path);
|
|
||||||
|
|
||||||
if (value && typeof value === 'object' && ('addressed' in value || 'known' in value)) {
|
|
||||||
checkbox.dataset.hasFiles = '1';
|
|
||||||
checkbox.dataset.addressed = JSON.stringify(Array.isArray(value.addressed) ? value.addressed : []);
|
|
||||||
checkbox.dataset.known = JSON.stringify(Array.isArray(value.known) ? value.known : []);
|
|
||||||
}
|
|
||||||
|
|
||||||
checkbox.addEventListener('change', event => {
|
|
||||||
handleCheckboxChange(event);
|
|
||||||
});
|
|
||||||
|
|
||||||
return checkbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseJsonArray(value) {
|
|
||||||
if (!value) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const parsed = JSON.parse(value);
|
|
||||||
return Array.isArray(parsed) ? parsed : [];
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Invalid checkbox file metadata:', value, error);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCheckboxChange(event) {
|
|
||||||
const checkbox = event.target;
|
|
||||||
const container = checkbox.closest('.tree-item');
|
|
||||||
const childrenContainer = container.querySelector('.tree-children');
|
|
||||||
|
|
||||||
if (childrenContainer) {
|
|
||||||
const childCheckboxes = childrenContainer.querySelectorAll('input[type="checkbox"]');
|
|
||||||
childCheckboxes.forEach(cb => {
|
|
||||||
cb.checked = checkbox.checked;
|
|
||||||
cb.indeterminate = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
checkbox.indeterminate = false;
|
|
||||||
updateParentCheckboxes(checkbox);
|
|
||||||
|
|
||||||
if (typeof onSelectionChangeHandler === 'function') {
|
|
||||||
onSelectionChangeHandler();
|
|
||||||
}
|
|
||||||
saveTreeState();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateParentCheckboxes(checkbox) {
|
|
||||||
let currentItem = checkbox.closest('.tree-item');
|
|
||||||
let parentItem = currentItem.parentElement.closest('.tree-item');
|
|
||||||
|
|
||||||
while (parentItem) {
|
|
||||||
const parentLabel = parentItem.querySelector(':scope > label');
|
|
||||||
if (!parentLabel) {
|
|
||||||
parentItem = parentItem.parentElement.closest('.tree-item');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentCheckbox = parentLabel.querySelector('input[type="checkbox"]');
|
|
||||||
if (!parentCheckbox) {
|
|
||||||
parentItem = parentItem.parentElement.closest('.tree-item');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const children = parentItem.querySelectorAll(':scope > .tree-children > .tree-item > label > input[type="checkbox"]');
|
|
||||||
const checkedCount = Array.from(children).filter(cb => cb.checked || cb.indeterminate).length;
|
|
||||||
|
|
||||||
if (checkedCount === 0) {
|
|
||||||
parentCheckbox.checked = false;
|
|
||||||
parentCheckbox.indeterminate = false;
|
|
||||||
} else if (checkedCount === children.length) {
|
|
||||||
parentCheckbox.checked = true;
|
|
||||||
parentCheckbox.indeterminate = false;
|
|
||||||
} else {
|
|
||||||
parentCheckbox.checked = false;
|
|
||||||
parentCheckbox.indeterminate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentItem = parentItem;
|
|
||||||
parentItem = currentItem.parentElement.closest('.tree-item');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveTreeState() {
|
|
||||||
if (!treeContainer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const checkedPaths = Array.from(
|
|
||||||
treeContainer.querySelectorAll('input[type="checkbox"]:checked[data-has-files="1"]')
|
|
||||||
).map(cb => cb.dataset.path).filter(Boolean);
|
|
||||||
|
|
||||||
const collapsedPaths = Array.from(
|
|
||||||
treeContainer.querySelectorAll('.tree-children.collapsed')
|
|
||||||
).map(children => {
|
|
||||||
const cb = children.parentElement.querySelector(':scope > label > input[type="checkbox"]');
|
|
||||||
return cb?.dataset.path ?? null;
|
|
||||||
}).filter(Boolean);
|
|
||||||
|
|
||||||
try {
|
|
||||||
localStorage.setItem(STORAGE_KEY, JSON.stringify({ checkedPaths, collapsedPaths }));
|
|
||||||
} catch {
|
|
||||||
// Storage unavailable or quota exceeded — silently ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function restoreTreeState() {
|
|
||||||
if (!treeContainer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let state;
|
|
||||||
try {
|
|
||||||
const raw = localStorage.getItem(STORAGE_KEY);
|
|
||||||
if (!raw) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
state = JSON.parse(raw);
|
|
||||||
} catch {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!state || typeof state !== 'object') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(state.checkedPaths) && state.checkedPaths.length > 0) {
|
|
||||||
const checkedSet = new Set(state.checkedPaths);
|
|
||||||
treeContainer.querySelectorAll('input[type="checkbox"][data-has-files="1"]').forEach(cb => {
|
|
||||||
cb.checked = checkedSet.has(cb.dataset.path);
|
|
||||||
});
|
|
||||||
treeContainer.querySelectorAll('input[type="checkbox"][data-has-files="1"]:checked').forEach(cb => {
|
|
||||||
updateParentCheckboxes(cb);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(state.collapsedPaths)) {
|
|
||||||
const collapsedSet = new Set(state.collapsedPaths);
|
|
||||||
treeContainer.querySelectorAll('.tree-children').forEach(childrenDiv => {
|
|
||||||
const cb = childrenDiv.parentElement.querySelector(':scope > label > input[type="checkbox"]');
|
|
||||||
if (!cb) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const toggle = childrenDiv.parentElement.querySelector(':scope > .tree-toggle');
|
|
||||||
const shouldBeCollapsed = collapsedSet.has(cb.dataset.path);
|
|
||||||
const isCollapsed = childrenDiv.classList.contains('collapsed');
|
|
||||||
if (shouldBeCollapsed !== isCollapsed) {
|
|
||||||
childrenDiv.classList.toggle('collapsed');
|
|
||||||
if (toggle) {
|
|
||||||
toggle.textContent = shouldBeCollapsed ? '▶' : '▼';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+2
-77
@@ -107,81 +107,6 @@ h2 {
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#product-tree {
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-item {
|
|
||||||
margin: 1px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-item > label {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 2px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Keep label text aligned whether or not a node has a toggle icon. */
|
|
||||||
.tree-item:not(.parent) > label {
|
|
||||||
margin-left: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-item.parent > label {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-toggle {
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 16px;
|
|
||||||
height: 1.4em;
|
|
||||||
margin-right: 2px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-children {
|
|
||||||
margin-left: 16px;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: max-height 0.3s ease, opacity 0.3s ease;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-children .tree-children {
|
|
||||||
margin-left: 33px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-children.collapsed {
|
|
||||||
max-height: 0;
|
|
||||||
opacity: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-item input[type="checkbox"] {
|
|
||||||
cursor: pointer;
|
|
||||||
margin-right: 4px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-item input[type="checkbox"]:indeterminate {
|
|
||||||
accent-color: #f39c12;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-item label {
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-item label:hover {
|
|
||||||
color: #2c3e50;
|
|
||||||
}
|
|
||||||
|
|
||||||
#issues {
|
#issues {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -298,8 +223,8 @@ li:hover {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tree-children {
|
.checkbox-tree {
|
||||||
margin-left: 12px;
|
--checkbox-tree-indent: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.issues-table {
|
.issues-table {
|
||||||
|
|||||||
Vendored
+137
@@ -0,0 +1,137 @@
|
|||||||
|
# Checkbox Tree
|
||||||
|
|
||||||
|
A dependency-free, instance-based checkbox tree for browser ES modules. It
|
||||||
|
supports cascading selection, indeterminate parent states, expandable branches,
|
||||||
|
multiple independent instances, optional local-storage persistence, and compact
|
||||||
|
shareable URL state backed by an append-only node manifest.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install @aaronaxvig/checkbox-tree
|
||||||
|
```
|
||||||
|
|
||||||
|
The package is not published yet. During local development, install it by path:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install ../checkbox-tree
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { CheckboxTree } from "@aaronaxvig/checkbox-tree";
|
||||||
|
import "@aaronaxvig/checkbox-tree/styles.css";
|
||||||
|
|
||||||
|
const tree = new CheckboxTree(document.querySelector("#example-tree"), {
|
||||||
|
initiallySelected: true,
|
||||||
|
storageKey: "example-tree-state",
|
||||||
|
onSelectionChange({ selectedIds, selectedNodes }) {
|
||||||
|
console.log(selectedIds, selectedNodes);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
tree.setData([
|
||||||
|
{
|
||||||
|
id: "fruit",
|
||||||
|
label: "Fruit",
|
||||||
|
children: [
|
||||||
|
{ id: "apple", label: "Apple", metadata: { color: "red" } },
|
||||||
|
{ id: "pear", label: "Pear", metadata: { color: "green" } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
|
||||||
|
For applications without a bundler, serve or copy the `src/` directory and use
|
||||||
|
relative URLs. The main module imports its state-codec modules alongside it:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<link rel="stylesheet" href="./checkbox-tree.css">
|
||||||
|
<script type="module">
|
||||||
|
import { CheckboxTree } from "./checkbox-tree.js";
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
Every node requires unique string `id` and `label` properties. Nodes may also
|
||||||
|
have `children`, application-owned `metadata`, and `selectable` or `disabled`
|
||||||
|
flags.
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
- `new CheckboxTree(container, options)` creates an independent instance.
|
||||||
|
- `setData(nodes)` validates and renders nodes, then restores saved state.
|
||||||
|
- `getSelectedIds()` returns selected node IDs.
|
||||||
|
- `getSelectedNodes()` returns selected source node objects.
|
||||||
|
- `setSelectedIds(ids, { notify })` replaces the selection.
|
||||||
|
- `restoreState()` restores selection and expansion state.
|
||||||
|
- `destroy()` removes listeners, markup, and the root CSS class.
|
||||||
|
- `serializeStateToFragment(manifest)` returns `#v=1&c=...&x=...` state.
|
||||||
|
- `restoreStateFromLocation(manifest, location)` safely applies URL state.
|
||||||
|
- `validateManifest(manifest)` reports unregistered and missing paths.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
- `initiallyCollapsed` defaults to `true`.
|
||||||
|
- `initiallySelected` defaults to `false`.
|
||||||
|
- `storageKey` defaults to `null`, which disables persistence.
|
||||||
|
- `onSelectionChange` receives `{ selectedIds, selectedNodes }`.
|
||||||
|
- `manifest` optionally supplies a parsed manifest and automatically restores
|
||||||
|
state from `window.location.hash` after `setData()`.
|
||||||
|
- `stateEncoding` selects `"adaptive"` (the default), `"dense"`, or `"tiered"`.
|
||||||
|
The website's configured value is the URL-format contract and is not embedded
|
||||||
|
in the fragment.
|
||||||
|
|
||||||
|
## Shareable URL state
|
||||||
|
|
||||||
|
Create a JSON manifest whose array positions are permanent slots. Canonical node
|
||||||
|
paths join ancestor IDs with `>`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "schema": 1, "nodes": ["fruit", "fruit>apple", "fruit>pear", null] }
|
||||||
|
```
|
||||||
|
|
||||||
|
Import `loadManifest`, pass its result as the `manifest` option, and call
|
||||||
|
`tree.serializeStateToFragment()` when creating a share link. Existing entries
|
||||||
|
must never be reordered. Append new paths, replace removed paths with `null`, and
|
||||||
|
edit a path in place for a logical rename or move. Node IDs therefore cannot
|
||||||
|
contain `>`.
|
||||||
|
|
||||||
|
The fragment adaptively encodes checked (`c`) and expanded (`x`) state using the
|
||||||
|
shortest of a dense bitset, sparse enabled slots, or sparse disabled slots.
|
||||||
|
Sparse slot numbers are delta-encoded variable-length integers. A coverage value
|
||||||
|
in the disabled-slot form ensures nodes appended later still default to unchecked
|
||||||
|
and collapsed. Malformed or unsupported state is ignored.
|
||||||
|
The helpers `encodeTreeState`, `decodeTreeState`, `applyTreeState`, and
|
||||||
|
`restoreStateFromLocation` are also exported for custom integrations.
|
||||||
|
Use `validateManifestEvolution(previous, next)` in a build check to reject
|
||||||
|
removed slots and tombstone reuse; path changes are reported for deliberate
|
||||||
|
rename/move review.
|
||||||
|
|
||||||
|
With `stateEncoding: "tiered"`, `c1` stores root checked state and each deeper
|
||||||
|
`cN` field stores nodes whose checked state differs from their parent. Expansion
|
||||||
|
does not inherit: every `xN` stores the actual expanded branches at that depth,
|
||||||
|
with collapsed as the baseline. Each tier field uses the adaptive codec. The
|
||||||
|
`n` field records manifest coverage so nodes appended later still default to
|
||||||
|
false. A non-selectable parent supplies a false checked baseline. With
|
||||||
|
`stateEncoding: "dense"`, `c` and `x` contain trimmed
|
||||||
|
least-significant-bit-first bitsets.
|
||||||
|
|
||||||
|
Styling is namespaced under `.checkbox-tree`. Override the custom properties
|
||||||
|
`--checkbox-tree-indent`, `--checkbox-tree-toggle-size`, and
|
||||||
|
`--checkbox-tree-hover-color` in the consuming application.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install
|
||||||
|
npm test
|
||||||
|
npm run pack:check
|
||||||
|
```
|
||||||
|
|
||||||
|
## Future demo ideas
|
||||||
|
|
||||||
|
- Add population metadata to the country, state, and county nodes and display the
|
||||||
|
total population represented by the current selection. Define the aggregation
|
||||||
|
rule carefully so checked ancestors and their checked descendants are not
|
||||||
|
counted twice; one option is to total only the most specific checked nodes.
|
||||||
+82
@@ -0,0 +1,82 @@
|
|||||||
|
.checkbox-tree {
|
||||||
|
--checkbox-tree-indent: 18px;
|
||||||
|
--checkbox-tree-toggle-size: 18px;
|
||||||
|
--checkbox-tree-hover-color: #2c3e50;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-tree__item {
|
||||||
|
margin: 1px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-tree__row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-tree__toggle,
|
||||||
|
.checkbox-tree__toggle-spacer {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex: 0 0 var(--checkbox-tree-toggle-size);
|
||||||
|
width: var(--checkbox-tree-toggle-size);
|
||||||
|
height: var(--checkbox-tree-toggle-size);
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-tree__toggle {
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-tree__toggle[aria-expanded="true"] {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-tree__toggle:focus-visible {
|
||||||
|
outline: 2px solid currentColor;
|
||||||
|
outline-offset: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-tree__label {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2px 0;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-tree__item:has(> .checkbox-tree__children) > .checkbox-tree__row > .checkbox-tree__label {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-tree__label:hover {
|
||||||
|
color: var(--checkbox-tree-hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-tree__checkbox {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
margin: 0 4px 0 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-tree__checkbox:indeterminate {
|
||||||
|
accent-color: #f39c12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-tree__children {
|
||||||
|
margin-left: var(--checkbox-tree-indent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-tree__children[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
+355
@@ -0,0 +1,355 @@
|
|||||||
|
// Dependency-free checkbox tree widget. State serialization lives in focused sibling modules.
|
||||||
|
import {
|
||||||
|
PATH_DELIMITER,
|
||||||
|
assertStateEncoding
|
||||||
|
} from './state-codec.js';
|
||||||
|
import {
|
||||||
|
restoreStateFromLocation,
|
||||||
|
serializeStateToFragment,
|
||||||
|
validateManifest
|
||||||
|
} from './tree-state.js';
|
||||||
|
|
||||||
|
export {
|
||||||
|
PATH_DELIMITER,
|
||||||
|
SERIALIZATION_VERSION,
|
||||||
|
base64UrlToBytes,
|
||||||
|
bytesToBase64Url,
|
||||||
|
decodeAdaptiveBitset,
|
||||||
|
encodeAdaptiveBitset,
|
||||||
|
getBit,
|
||||||
|
loadManifest,
|
||||||
|
setBit
|
||||||
|
} from './state-codec.js';
|
||||||
|
export {
|
||||||
|
applyTreeState,
|
||||||
|
decodeTreeState,
|
||||||
|
encodeTreeState,
|
||||||
|
restoreStateFromLocation,
|
||||||
|
serializeStateToFragment,
|
||||||
|
validateManifest,
|
||||||
|
validateManifestEvolution
|
||||||
|
} from './tree-state.js';
|
||||||
|
|
||||||
|
const DEFAULT_OPTIONS = {
|
||||||
|
initiallyCollapsed: true,
|
||||||
|
initiallySelected: false,
|
||||||
|
storageKey: null,
|
||||||
|
onSelectionChange: null,
|
||||||
|
manifest: null,
|
||||||
|
stateEncoding: 'adaptive'
|
||||||
|
};
|
||||||
|
|
||||||
|
export class CheckboxTree {
|
||||||
|
constructor(container, options = {}) {
|
||||||
|
if (!(container instanceof Element)) {
|
||||||
|
throw new TypeError('CheckboxTree requires a container element.');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.container = container;
|
||||||
|
this.options = { ...DEFAULT_OPTIONS, ...options };
|
||||||
|
assertStateEncoding(this.options.stateEncoding);
|
||||||
|
this.nodesById = new Map();
|
||||||
|
this.nodesByPath = new Map();
|
||||||
|
this.itemsById = new Map();
|
||||||
|
this.rootNodes = [];
|
||||||
|
this.handleChange = this.handleChange.bind(this);
|
||||||
|
this.handleClick = this.handleClick.bind(this);
|
||||||
|
|
||||||
|
this.container.classList.add('checkbox-tree');
|
||||||
|
this.container.addEventListener('change', this.handleChange);
|
||||||
|
this.container.addEventListener('click', this.handleClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
setData(nodes) {
|
||||||
|
if (!Array.isArray(nodes)) {
|
||||||
|
throw new TypeError('CheckboxTree data must be an array of nodes.');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.nodesById.clear();
|
||||||
|
this.nodesByPath.clear();
|
||||||
|
this.itemsById.clear();
|
||||||
|
this.rootNodes = nodes;
|
||||||
|
this.indexNodes(nodes);
|
||||||
|
this.render();
|
||||||
|
this.updateAllParentCheckboxes();
|
||||||
|
this.restoreState();
|
||||||
|
if (this.options.manifest) this.restoreStateFromLocation();
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectedIds() {
|
||||||
|
return this.getSelectedNodes().map(node => node.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectedNodes() {
|
||||||
|
return Array.from(this.container.querySelectorAll(
|
||||||
|
'.checkbox-tree__checkbox:checked[data-selectable="true"]'
|
||||||
|
)).map(checkbox => this.nodesById.get(checkbox.dataset.nodeId)).filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedIds(ids, { notify = false } = {}) {
|
||||||
|
const selectedIds = new Set(ids);
|
||||||
|
this.container.querySelectorAll('.checkbox-tree__checkbox').forEach(checkbox => {
|
||||||
|
checkbox.checked = checkbox.dataset.selectable === 'true' && selectedIds.has(checkbox.dataset.nodeId);
|
||||||
|
checkbox.indeterminate = false;
|
||||||
|
});
|
||||||
|
this.updateAllParentCheckboxes();
|
||||||
|
this.saveState();
|
||||||
|
|
||||||
|
if (notify) {
|
||||||
|
this.notifySelectionChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.container.removeEventListener('change', this.handleChange);
|
||||||
|
this.container.removeEventListener('click', this.handleClick);
|
||||||
|
this.container.classList.remove('checkbox-tree');
|
||||||
|
this.container.replaceChildren();
|
||||||
|
this.nodesById.clear();
|
||||||
|
this.nodesByPath.clear();
|
||||||
|
this.itemsById.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
indexNodes(nodes, parentPath = '') {
|
||||||
|
nodes.forEach(node => {
|
||||||
|
if (!node || typeof node.id !== 'string' || !node.id || typeof node.label !== 'string') {
|
||||||
|
throw new TypeError('Each CheckboxTree node requires non-empty string id and label properties.');
|
||||||
|
}
|
||||||
|
if (this.nodesById.has(node.id)) {
|
||||||
|
throw new Error(`Duplicate CheckboxTree node id: ${node.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.id.includes(PATH_DELIMITER)) {
|
||||||
|
throw new Error(`CheckboxTree node id cannot contain "${PATH_DELIMITER}": ${node.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.nodesById.set(node.id, node);
|
||||||
|
const path = parentPath ? `${parentPath}${PATH_DELIMITER}${node.id}` : node.id;
|
||||||
|
this.nodesByPath.set(path, node);
|
||||||
|
if (node.children !== undefined) {
|
||||||
|
if (!Array.isArray(node.children)) {
|
||||||
|
throw new TypeError(`CheckboxTree node children must be an array: ${node.id}`);
|
||||||
|
}
|
||||||
|
this.indexNodes(node.children, path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
this.rootNodes.forEach(node => fragment.appendChild(this.createNodeElement(node)));
|
||||||
|
this.container.replaceChildren(fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
createNodeElement(node) {
|
||||||
|
const item = document.createElement('div');
|
||||||
|
item.className = 'checkbox-tree__item';
|
||||||
|
item.dataset.nodeId = node.id;
|
||||||
|
this.itemsById.set(node.id, item);
|
||||||
|
|
||||||
|
const row = document.createElement('div');
|
||||||
|
row.className = 'checkbox-tree__row';
|
||||||
|
const hasChildren = Array.isArray(node.children) && node.children.length > 0;
|
||||||
|
|
||||||
|
if (hasChildren) {
|
||||||
|
const toggle = document.createElement('button');
|
||||||
|
toggle.type = 'button';
|
||||||
|
toggle.className = 'checkbox-tree__toggle';
|
||||||
|
toggle.dataset.action = 'toggle';
|
||||||
|
toggle.setAttribute('aria-label', `Expand ${node.label}`);
|
||||||
|
toggle.setAttribute('aria-expanded', 'false');
|
||||||
|
toggle.textContent = '▶';
|
||||||
|
row.appendChild(toggle);
|
||||||
|
} else {
|
||||||
|
const spacer = document.createElement('span');
|
||||||
|
spacer.className = 'checkbox-tree__toggle-spacer';
|
||||||
|
spacer.setAttribute('aria-hidden', 'true');
|
||||||
|
row.appendChild(spacer);
|
||||||
|
}
|
||||||
|
|
||||||
|
const label = document.createElement('label');
|
||||||
|
label.className = 'checkbox-tree__label';
|
||||||
|
if (node.selectable !== false) {
|
||||||
|
const checkbox = document.createElement('input');
|
||||||
|
checkbox.type = 'checkbox';
|
||||||
|
checkbox.className = 'checkbox-tree__checkbox';
|
||||||
|
checkbox.dataset.nodeId = node.id;
|
||||||
|
checkbox.dataset.selectable = 'true';
|
||||||
|
checkbox.disabled = node.disabled === true;
|
||||||
|
checkbox.checked = this.options.initiallySelected && !checkbox.disabled;
|
||||||
|
label.appendChild(checkbox);
|
||||||
|
}
|
||||||
|
label.appendChild(document.createTextNode(node.label));
|
||||||
|
row.appendChild(label);
|
||||||
|
item.appendChild(row);
|
||||||
|
|
||||||
|
if (hasChildren) {
|
||||||
|
const children = document.createElement('div');
|
||||||
|
children.className = 'checkbox-tree__children';
|
||||||
|
children.hidden = this.options.initiallyCollapsed;
|
||||||
|
node.children.forEach(child => children.appendChild(this.createNodeElement(child)));
|
||||||
|
item.appendChild(children);
|
||||||
|
|
||||||
|
if (!this.options.initiallyCollapsed) {
|
||||||
|
this.setExpanded(item, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClick(event) {
|
||||||
|
const toggle = event.target.closest('.checkbox-tree__toggle[data-action="toggle"]');
|
||||||
|
if (!toggle || !this.container.contains(toggle)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = toggle.closest('.checkbox-tree__item');
|
||||||
|
this.setExpanded(item, toggle.getAttribute('aria-expanded') !== 'true');
|
||||||
|
this.saveState();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChange(event) {
|
||||||
|
const checkbox = event.target.closest('.checkbox-tree__checkbox');
|
||||||
|
if (!checkbox || !this.container.contains(checkbox)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = checkbox.closest('.checkbox-tree__item');
|
||||||
|
const children = this.directChildrenContainer(item);
|
||||||
|
if (children) {
|
||||||
|
children.querySelectorAll('.checkbox-tree__checkbox:not(:disabled)').forEach(child => {
|
||||||
|
child.checked = checkbox.checked;
|
||||||
|
child.indeterminate = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
checkbox.indeterminate = false;
|
||||||
|
this.updateAncestorCheckboxes(item);
|
||||||
|
this.saveState();
|
||||||
|
this.notifySelectionChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
notifySelectionChange() {
|
||||||
|
if (typeof this.options.onSelectionChange === 'function') {
|
||||||
|
this.options.onSelectionChange({
|
||||||
|
selectedIds: this.getSelectedIds(),
|
||||||
|
selectedNodes: this.getSelectedNodes()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setExpanded(item, expanded) {
|
||||||
|
if (!item) return;
|
||||||
|
const toggle = item.querySelector(':scope > .checkbox-tree__row > .checkbox-tree__toggle');
|
||||||
|
const children = this.directChildrenContainer(item);
|
||||||
|
if (!toggle || !children) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
children.hidden = !expanded;
|
||||||
|
toggle.setAttribute('aria-expanded', String(expanded));
|
||||||
|
const label = this.nodesById.get(item.dataset.nodeId)?.label ?? 'branch';
|
||||||
|
toggle.setAttribute('aria-label', `${expanded ? 'Collapse' : 'Expand'} ${label}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
directChildrenContainer(item) {
|
||||||
|
return item.querySelector(':scope > .checkbox-tree__children');
|
||||||
|
}
|
||||||
|
|
||||||
|
directCheckbox(item) {
|
||||||
|
return item.querySelector(':scope > .checkbox-tree__row .checkbox-tree__checkbox');
|
||||||
|
}
|
||||||
|
|
||||||
|
itemForNode(id) {
|
||||||
|
return this.itemsById.get(id) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAncestorCheckboxes(item) {
|
||||||
|
let parentItem = item.parentElement?.closest('.checkbox-tree__item');
|
||||||
|
while (parentItem && this.container.contains(parentItem)) {
|
||||||
|
this.updateParentCheckbox(parentItem);
|
||||||
|
parentItem = parentItem.parentElement?.closest('.checkbox-tree__item');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAllParentCheckboxes() {
|
||||||
|
const parents = Array.from(this.container.querySelectorAll('.checkbox-tree__item'))
|
||||||
|
.filter(item => this.directChildrenContainer(item));
|
||||||
|
parents.reverse().forEach(item => this.updateParentCheckbox(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
updateParentCheckbox(item) {
|
||||||
|
const checkbox = this.directCheckbox(item);
|
||||||
|
const children = this.directChildrenContainer(item);
|
||||||
|
if (!checkbox || !children) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const childCheckboxes = Array.from(children.querySelectorAll(
|
||||||
|
':scope > .checkbox-tree__item > .checkbox-tree__row .checkbox-tree__checkbox'
|
||||||
|
)).filter(child => !child.disabled);
|
||||||
|
const hasSelection = childCheckboxes.some(child => child.checked || child.indeterminate);
|
||||||
|
const allSelected = childCheckboxes.length > 0 && childCheckboxes.every(child => child.checked);
|
||||||
|
checkbox.checked = allSelected;
|
||||||
|
checkbox.indeterminate = hasSelection && !allSelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveState() {
|
||||||
|
if (!this.options.storageKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const collapsedIds = Array.from(this.container.querySelectorAll('.checkbox-tree__children[hidden]'))
|
||||||
|
.map(children => children.parentElement.dataset.nodeId);
|
||||||
|
try {
|
||||||
|
localStorage.setItem(this.options.storageKey, JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
selectedIds: this.getSelectedIds(),
|
||||||
|
collapsedIds
|
||||||
|
}));
|
||||||
|
} catch {
|
||||||
|
// Persistence is optional; storage may be disabled or full.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreState() {
|
||||||
|
if (!this.options.storageKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let state;
|
||||||
|
try {
|
||||||
|
state = JSON.parse(localStorage.getItem(this.options.storageKey));
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!state || typeof state !== 'object') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(state.selectedIds)) {
|
||||||
|
this.setSelectedIds(state.selectedIds);
|
||||||
|
}
|
||||||
|
if (Array.isArray(state.collapsedIds)) {
|
||||||
|
const collapsedIds = new Set(state.collapsedIds);
|
||||||
|
this.container.querySelectorAll('.checkbox-tree__item').forEach(item => {
|
||||||
|
if (this.directChildrenContainer(item)) {
|
||||||
|
this.setExpanded(item, !collapsedIds.has(item.dataset.nodeId));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validateManifest(manifest = this.options.manifest) {
|
||||||
|
return validateManifest(manifest, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
serializeStateToFragment(manifest = this.options.manifest) {
|
||||||
|
return serializeStateToFragment(this, manifest);
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreStateFromLocation(manifest = this.options.manifest, location = window.location) {
|
||||||
|
return restoreStateFromLocation(this, manifest, location);
|
||||||
|
}
|
||||||
|
}
|
||||||
+206
@@ -0,0 +1,206 @@
|
|||||||
|
// Manifest parsing and binary codecs. This module has no DOM dependencies.
|
||||||
|
export const SERIALIZATION_VERSION = 1;
|
||||||
|
export const PATH_DELIMITER = '>';
|
||||||
|
const STATE_ENCODINGS = new Set(['adaptive', 'dense', 'tiered']);
|
||||||
|
|
||||||
|
export function loadManifest(source) {
|
||||||
|
let records;
|
||||||
|
let schema = 1;
|
||||||
|
|
||||||
|
if (typeof source === 'string') {
|
||||||
|
records = source.split(/\r?\n/)
|
||||||
|
.map(line => line.trim())
|
||||||
|
.filter(line => line && !line.startsWith('#'))
|
||||||
|
.map(line => line === '!deleted' ? null : line);
|
||||||
|
} else if (source && typeof source === 'object' && Array.isArray(source.nodes)) {
|
||||||
|
schema = source.schema;
|
||||||
|
records = source.nodes;
|
||||||
|
} else if (source && Array.isArray(source.slots)) {
|
||||||
|
return normalizeManifest(source.schema, source.slots.map(slot => slot?.deleted ? null : slot?.path));
|
||||||
|
} else {
|
||||||
|
throw new TypeError('CheckboxTree manifest must be text or an object with a nodes array.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizeManifest(schema, records);
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeManifest(schema, records) {
|
||||||
|
if (schema !== 1) {
|
||||||
|
throw new Error(`Unsupported CheckboxTree manifest schema: ${schema}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pathToSlot = new Map();
|
||||||
|
const slots = records.map((record, index) => {
|
||||||
|
if (record === null || record === '!deleted') {
|
||||||
|
return { path: null, deleted: true };
|
||||||
|
}
|
||||||
|
if (typeof record !== 'string' || !record || record.trim() !== record) {
|
||||||
|
throw new TypeError(`Invalid CheckboxTree manifest entry at slot ${index}.`);
|
||||||
|
}
|
||||||
|
if (pathToSlot.has(record)) {
|
||||||
|
throw new Error(`Duplicate CheckboxTree manifest path: ${record}`);
|
||||||
|
}
|
||||||
|
pathToSlot.set(record, index);
|
||||||
|
return { path: record, deleted: false };
|
||||||
|
});
|
||||||
|
|
||||||
|
return { schema: 1, slots, pathToSlot };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setBit(bytes, bitIndex, value) {
|
||||||
|
const byteIndex = Math.floor(bitIndex / 8);
|
||||||
|
const mask = 1 << (bitIndex % 8);
|
||||||
|
if (value) bytes[byteIndex] |= mask;
|
||||||
|
else bytes[byteIndex] &= ~mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBit(bytes, bitIndex) {
|
||||||
|
const byteIndex = Math.floor(bitIndex / 8);
|
||||||
|
return byteIndex < bytes.length && (bytes[byteIndex] & (1 << (bitIndex % 8))) !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bytesToBase64Url(bytes) {
|
||||||
|
let binary = '';
|
||||||
|
for (const byte of bytes) binary += String.fromCharCode(byte);
|
||||||
|
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function base64UrlToBytes(value, maximumLength = Infinity) {
|
||||||
|
if (typeof value !== 'string' || !/^[A-Za-z0-9_-]*$/.test(value) || value.length % 4 === 1) {
|
||||||
|
throw new Error('Invalid Base64URL value.');
|
||||||
|
}
|
||||||
|
if (Number.isFinite(maximumLength) && value.length > Math.ceil(maximumLength * 4 / 3) + 2) {
|
||||||
|
throw new Error('Encoded tree state exceeds the manifest size.');
|
||||||
|
}
|
||||||
|
const base64 = value.replace(/-/g, '+').replace(/_/g, '/');
|
||||||
|
const binary = atob(base64 + '='.repeat((4 - base64.length % 4) % 4));
|
||||||
|
if (binary.length > maximumLength) throw new Error('Encoded tree state exceeds the manifest size.');
|
||||||
|
return Uint8Array.from(binary, character => character.charCodeAt(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
function trimTrailingZeroBytes(bytes) {
|
||||||
|
let end = bytes.length;
|
||||||
|
while (end && bytes[end - 1] === 0) end--;
|
||||||
|
return bytes.slice(0, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeUnsignedVarint(value) {
|
||||||
|
const bytes = [];
|
||||||
|
do {
|
||||||
|
let byte = value & 0x7f;
|
||||||
|
value = Math.floor(value / 128);
|
||||||
|
if (value) byte |= 0x80;
|
||||||
|
bytes.push(byte);
|
||||||
|
} while (value);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeUnsignedVarint(bytes, offset) {
|
||||||
|
let value = 0;
|
||||||
|
let multiplier = 1;
|
||||||
|
for (let index = offset; index < bytes.length && index < offset + 8; index++) {
|
||||||
|
const byte = bytes[index];
|
||||||
|
value += (byte & 0x7f) * multiplier;
|
||||||
|
if ((byte & 0x80) === 0) return { value, offset: index + 1 };
|
||||||
|
multiplier *= 128;
|
||||||
|
if (!Number.isSafeInteger(value) || !Number.isSafeInteger(multiplier)) break;
|
||||||
|
}
|
||||||
|
throw new Error('Invalid adaptive tree-state integer.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeSparseIndexes(indexes) {
|
||||||
|
const bytes = [];
|
||||||
|
let previous = -1;
|
||||||
|
for (const index of indexes) {
|
||||||
|
bytes.push(...encodeUnsignedVarint(index - previous - 1));
|
||||||
|
previous = index;
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeSparseIndexes(bytes, offset, limit) {
|
||||||
|
const indexes = new Set();
|
||||||
|
let previous = -1;
|
||||||
|
while (offset < bytes.length) {
|
||||||
|
const decoded = decodeUnsignedVarint(bytes, offset);
|
||||||
|
const index = previous + decoded.value + 1;
|
||||||
|
if (index >= limit) throw new Error('Sparse tree state contains an out-of-range slot.');
|
||||||
|
indexes.add(index);
|
||||||
|
previous = index;
|
||||||
|
offset = decoded.offset;
|
||||||
|
}
|
||||||
|
return indexes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encodeAdaptiveBitset(values, slotCount) {
|
||||||
|
const enabled = [];
|
||||||
|
const disabled = [];
|
||||||
|
const denseBytes = new Uint8Array(Math.ceil(slotCount / 8));
|
||||||
|
for (const [slot, value] of values) {
|
||||||
|
(value ? enabled : disabled).push(slot);
|
||||||
|
setBit(denseBytes, slot, value);
|
||||||
|
}
|
||||||
|
enabled.sort((left, right) => left - right);
|
||||||
|
disabled.sort((left, right) => left - right);
|
||||||
|
if (enabled.length === 0) return new Uint8Array();
|
||||||
|
|
||||||
|
const candidates = [
|
||||||
|
Uint8Array.from([0, ...trimTrailingZeroBytes(denseBytes)]),
|
||||||
|
Uint8Array.from([1, ...encodeSparseIndexes(enabled)]),
|
||||||
|
Uint8Array.from([2, ...encodeUnsignedVarint(slotCount), ...encodeSparseIndexes(disabled)])
|
||||||
|
];
|
||||||
|
return candidates.reduce((shortest, candidate) =>
|
||||||
|
candidate.length < shortest.length ? candidate : shortest
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodeAdaptiveBitset(bytes, slotCount) {
|
||||||
|
if (bytes.length === 0) return () => false;
|
||||||
|
const mode = bytes[0];
|
||||||
|
if (mode === 0) {
|
||||||
|
const dense = bytes.slice(1);
|
||||||
|
if (dense.length > Math.ceil(slotCount / 8)) {
|
||||||
|
throw new Error('Dense tree state exceeds the manifest size.');
|
||||||
|
}
|
||||||
|
return slot => getBit(dense, slot);
|
||||||
|
}
|
||||||
|
if (mode === 1) {
|
||||||
|
const enabled = decodeSparseIndexes(bytes, 1, slotCount);
|
||||||
|
return slot => enabled.has(slot);
|
||||||
|
}
|
||||||
|
if (mode === 2) {
|
||||||
|
const coverage = decodeUnsignedVarint(bytes, 1);
|
||||||
|
if (coverage.value > slotCount) throw new Error('Tree-state coverage exceeds the manifest size.');
|
||||||
|
const disabled = decodeSparseIndexes(bytes, coverage.offset, coverage.value);
|
||||||
|
return slot => slot < coverage.value && !disabled.has(slot);
|
||||||
|
}
|
||||||
|
throw new Error('Unknown adaptive tree-state mode.');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function encodeDenseBitset(values, slotCount) {
|
||||||
|
const bytes = new Uint8Array(Math.ceil(slotCount / 8));
|
||||||
|
for (const [slot, value] of values) setBit(bytes, slot, value);
|
||||||
|
return trimTrailingZeroBytes(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodeDenseBitset(bytes, slotCount) {
|
||||||
|
if (bytes.length > Math.ceil(slotCount / 8)) {
|
||||||
|
throw new Error('Dense tree state exceeds the manifest size.');
|
||||||
|
}
|
||||||
|
return slot => getBit(bytes, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function maximumEncodedLength(slotCount) {
|
||||||
|
return Math.max(Math.ceil(slotCount / 8) + 1, slotCount * 2 + 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assertStateEncoding(encoding) {
|
||||||
|
if (!STATE_ENCODINGS.has(encoding)) {
|
||||||
|
throw new Error(`Unsupported CheckboxTree state encoding: ${encoding}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function asManifestSource(manifest) {
|
||||||
|
return manifest?.pathToSlot instanceof Map ? manifest : loadManifest(manifest);
|
||||||
|
}
|
||||||
+310
@@ -0,0 +1,310 @@
|
|||||||
|
// Adapts pure state codecs to CheckboxTree's rendered node model and URL format.
|
||||||
|
import {
|
||||||
|
PATH_DELIMITER,
|
||||||
|
SERIALIZATION_VERSION,
|
||||||
|
asManifestSource as asManifest,
|
||||||
|
assertStateEncoding,
|
||||||
|
base64UrlToBytes,
|
||||||
|
bytesToBase64Url,
|
||||||
|
decodeAdaptiveBitset,
|
||||||
|
decodeDenseBitset,
|
||||||
|
encodeAdaptiveBitset,
|
||||||
|
encodeDenseBitset,
|
||||||
|
maximumEncodedLength
|
||||||
|
} from './state-codec.js';
|
||||||
|
|
||||||
|
export function validateManifest(manifest, tree) {
|
||||||
|
const normalized = asManifest(manifest);
|
||||||
|
const treePaths = new Set(tree.nodesByPath.keys());
|
||||||
|
return {
|
||||||
|
valid: Array.from(treePaths).every(path => normalized.pathToSlot.has(path)),
|
||||||
|
unregisteredPaths: Array.from(treePaths).filter(path => !normalized.pathToSlot.has(path)),
|
||||||
|
missingPaths: normalized.slots
|
||||||
|
.filter(slot => !slot.deleted && !treePaths.has(slot.path))
|
||||||
|
.map(slot => slot.path)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validateManifestEvolution(previous, next) {
|
||||||
|
const oldManifest = asManifest(previous);
|
||||||
|
const newManifest = asManifest(next);
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
if (newManifest.slots.length < oldManifest.slots.length) {
|
||||||
|
errors.push('Manifest slots cannot be removed; use tombstones.');
|
||||||
|
}
|
||||||
|
oldManifest.slots.forEach((oldSlot, index) => {
|
||||||
|
const newSlot = newManifest.slots[index];
|
||||||
|
if (!newSlot) return;
|
||||||
|
if (oldSlot.deleted && !newSlot.deleted) {
|
||||||
|
errors.push(`Tombstoned slot ${index} cannot be reused.`);
|
||||||
|
}
|
||||||
|
if (!oldSlot.deleted && !newSlot.deleted && oldSlot.path !== newSlot.path) {
|
||||||
|
errors.push(`Slot ${index} changed path; confirm this is a logical rename or move.`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { valid: errors.length === 0, errors };
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectTreeValues(tree, manifest) {
|
||||||
|
const normalized = asManifest(manifest);
|
||||||
|
const checkedValues = new Map();
|
||||||
|
const expandedValues = new Map();
|
||||||
|
|
||||||
|
for (const [path, node] of tree.nodesByPath) {
|
||||||
|
const slot = normalized.pathToSlot.get(path);
|
||||||
|
if (slot === undefined) continue;
|
||||||
|
const item = tree.itemForNode(node.id);
|
||||||
|
const checkbox = item && tree.directCheckbox(item);
|
||||||
|
const toggle = item?.querySelector(':scope > .checkbox-tree__row > .checkbox-tree__toggle');
|
||||||
|
checkedValues.set(slot, Boolean(checkbox?.checked));
|
||||||
|
expandedValues.set(slot, toggle?.getAttribute('aria-expanded') === 'true');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { normalized, checkedValues, expandedValues };
|
||||||
|
}
|
||||||
|
|
||||||
|
function nodeState(tree, node) {
|
||||||
|
const item = tree.itemForNode(node.id);
|
||||||
|
const checkbox = item && tree.directCheckbox(item);
|
||||||
|
const toggle = item?.querySelector(':scope > .checkbox-tree__row > .checkbox-tree__toggle');
|
||||||
|
return {
|
||||||
|
checked: Boolean(checkbox?.checked),
|
||||||
|
hasChecked: Boolean(checkbox),
|
||||||
|
expanded: toggle?.getAttribute('aria-expanded') === 'true',
|
||||||
|
hasExpanded: Boolean(toggle)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeTieredState(tree, normalized) {
|
||||||
|
const checkedByDepth = new Map();
|
||||||
|
const expandedByDepth = new Map();
|
||||||
|
const statesByPath = new Map();
|
||||||
|
|
||||||
|
for (const [path, node] of tree.nodesByPath) {
|
||||||
|
const slot = normalized.pathToSlot.get(path);
|
||||||
|
if (slot === undefined) continue;
|
||||||
|
const depth = path.split(PATH_DELIMITER).length;
|
||||||
|
const parentPath = path.includes(PATH_DELIMITER)
|
||||||
|
? path.slice(0, path.lastIndexOf(PATH_DELIMITER))
|
||||||
|
: null;
|
||||||
|
const parent = statesByPath.get(parentPath) ?? {
|
||||||
|
checked: false,
|
||||||
|
hasChecked: false,
|
||||||
|
expanded: false,
|
||||||
|
hasExpanded: false
|
||||||
|
};
|
||||||
|
const current = nodeState(tree, node);
|
||||||
|
if (current.hasChecked) {
|
||||||
|
checkedByDepth.set(depth, checkedByDepth.get(depth) ?? new Map());
|
||||||
|
checkedByDepth.get(depth).set(slot, current.checked !== (parent.hasChecked ? parent.checked : false));
|
||||||
|
}
|
||||||
|
if (current.hasExpanded) {
|
||||||
|
expandedByDepth.set(depth, expandedByDepth.get(depth) ?? new Map());
|
||||||
|
expandedByDepth.get(depth).set(slot, current.expanded);
|
||||||
|
}
|
||||||
|
statesByPath.set(path, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fields = new Map([['n', String(normalized.slots.length)]]);
|
||||||
|
for (const [depth, values] of checkedByDepth) {
|
||||||
|
const encoded = bytesToBase64Url(encodeAdaptiveBitset(values, normalized.slots.length));
|
||||||
|
if (encoded) fields.set(`c${depth}`, encoded);
|
||||||
|
}
|
||||||
|
for (const [depth, values] of expandedByDepth) {
|
||||||
|
const encoded = bytesToBase64Url(encodeAdaptiveBitset(values, normalized.slots.length));
|
||||||
|
if (encoded) fields.set(`x${depth}`, encoded);
|
||||||
|
}
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encodeTreeState(tree, manifest, encoding = tree.options?.stateEncoding ?? 'adaptive') {
|
||||||
|
assertStateEncoding(encoding);
|
||||||
|
const { normalized, checkedValues, expandedValues } = collectTreeValues(tree, manifest);
|
||||||
|
|
||||||
|
if (encoding === 'tiered') {
|
||||||
|
return { version: SERIALIZATION_VERSION, encoding, fields: encodeTieredState(tree, normalized) };
|
||||||
|
}
|
||||||
|
|
||||||
|
const encode = encoding === 'dense' ? encodeDenseBitset : encodeAdaptiveBitset;
|
||||||
|
const checkedValue = bytesToBase64Url(encode(checkedValues, normalized.slots.length));
|
||||||
|
const expandedValue = bytesToBase64Url(encode(expandedValues, normalized.slots.length));
|
||||||
|
return {
|
||||||
|
version: SERIALIZATION_VERSION,
|
||||||
|
encoding,
|
||||||
|
checked: checkedValue || null,
|
||||||
|
expanded: expandedValue || null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeField(params, name, normalized, encoding) {
|
||||||
|
const maximumLength = encoding === 'dense'
|
||||||
|
? Math.ceil(normalized.slots.length / 8)
|
||||||
|
: maximumEncodedLength(normalized.slots.length);
|
||||||
|
const bytes = base64UrlToBytes(params.get(name) ?? '', maximumLength);
|
||||||
|
const decode = encoding === 'dense' ? decodeDenseBitset : decodeAdaptiveBitset;
|
||||||
|
decode(bytes, normalized.slots.length);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodeTreeState(fragment, manifest, encoding = 'adaptive') {
|
||||||
|
assertStateEncoding(encoding);
|
||||||
|
const normalized = asManifest(manifest);
|
||||||
|
const value = fragment.startsWith('#') ? fragment.slice(1) : fragment;
|
||||||
|
const params = new URLSearchParams(value);
|
||||||
|
if (params.get('v') !== String(SERIALIZATION_VERSION)) {
|
||||||
|
return { applied: false, version: params.get('v'), encoding, warnings: ['Missing or unsupported serialization version.'] };
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (encoding === 'tiered') {
|
||||||
|
if (params.has('c') || params.has('x')) {
|
||||||
|
throw new Error('Tiered tree state contains non-tiered fields.');
|
||||||
|
}
|
||||||
|
const maximumDepth = normalized.slots.reduce((depth, slot) =>
|
||||||
|
slot.deleted ? depth : Math.max(depth, slot.path.split(PATH_DELIMITER).length)
|
||||||
|
, 0);
|
||||||
|
for (const key of params.keys()) {
|
||||||
|
const match = /^([cx])(\d+)$/.exec(key);
|
||||||
|
if (match && (Number(match[2]) < 1 || Number(match[2]) > maximumDepth)) {
|
||||||
|
throw new Error(`Tree state contains an invalid tier: ${key}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const coverage = Number(params.get('n'));
|
||||||
|
if (!Number.isSafeInteger(coverage) || coverage < 0 || coverage > normalized.slots.length) {
|
||||||
|
throw new Error('Tiered tree state has invalid manifest coverage.');
|
||||||
|
}
|
||||||
|
const checkedTiers = new Map();
|
||||||
|
const expandedTiers = new Map();
|
||||||
|
for (let depth = 1; depth <= maximumDepth; depth++) {
|
||||||
|
checkedTiers.set(depth, decodeField(params, `c${depth}`, normalized, 'adaptive'));
|
||||||
|
expandedTiers.set(depth, decodeField(params, `x${depth}`, normalized, 'adaptive'));
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
applied: true,
|
||||||
|
version: SERIALIZATION_VERSION,
|
||||||
|
encoding,
|
||||||
|
coverage,
|
||||||
|
checkedTiers,
|
||||||
|
expandedTiers,
|
||||||
|
warnings: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key of params.keys()) {
|
||||||
|
if (key === 'n' || /^[cx]\d+$/.test(key)) {
|
||||||
|
throw new Error('Tree state contains tiered fields for a non-tiered encoding.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checked = decodeField(params, 'c', normalized, encoding);
|
||||||
|
const expanded = decodeField(params, 'x', normalized, encoding);
|
||||||
|
return {
|
||||||
|
applied: true,
|
||||||
|
version: SERIALIZATION_VERSION,
|
||||||
|
encoding,
|
||||||
|
checked,
|
||||||
|
expanded,
|
||||||
|
warnings: []
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return { applied: false, version: SERIALIZATION_VERSION, encoding, warnings: [error.message] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyTreeState(tree, manifest, state) {
|
||||||
|
if (!state?.applied) return false;
|
||||||
|
const normalized = asManifest(manifest);
|
||||||
|
if (state.encoding === 'tiered') return applyTieredTreeState(tree, normalized, state);
|
||||||
|
let isChecked;
|
||||||
|
let isExpanded;
|
||||||
|
try {
|
||||||
|
const decode = state.encoding === 'dense' ? decodeDenseBitset : decodeAdaptiveBitset;
|
||||||
|
isChecked = decode(state.checked, normalized.slots.length);
|
||||||
|
isExpanded = decode(state.expanded, normalized.slots.length);
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const [path, node] of tree.nodesByPath) {
|
||||||
|
const slot = normalized.pathToSlot.get(path);
|
||||||
|
if (slot === undefined) continue;
|
||||||
|
const item = tree.itemForNode(node.id);
|
||||||
|
const checkbox = item && tree.directCheckbox(item);
|
||||||
|
if (checkbox && !checkbox.disabled) checkbox.checked = isChecked(slot);
|
||||||
|
tree.setExpanded(item, isExpanded(slot));
|
||||||
|
}
|
||||||
|
tree.updateAllParentCheckboxes();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyTieredTreeState(tree, normalized, state) {
|
||||||
|
const resolvedByPath = new Map();
|
||||||
|
const checkedDecoders = new Map();
|
||||||
|
const expandedDecoders = new Map();
|
||||||
|
try {
|
||||||
|
for (const [depth, bytes] of state.checkedTiers) {
|
||||||
|
checkedDecoders.set(depth, decodeAdaptiveBitset(bytes, normalized.slots.length));
|
||||||
|
}
|
||||||
|
for (const [depth, bytes] of state.expandedTiers) {
|
||||||
|
expandedDecoders.set(depth, decodeAdaptiveBitset(bytes, normalized.slots.length));
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [path, node] of tree.nodesByPath) {
|
||||||
|
const slot = normalized.pathToSlot.get(path);
|
||||||
|
if (slot === undefined) continue;
|
||||||
|
const depth = path.split(PATH_DELIMITER).length;
|
||||||
|
const parentPath = path.includes(PATH_DELIMITER)
|
||||||
|
? path.slice(0, path.lastIndexOf(PATH_DELIMITER))
|
||||||
|
: null;
|
||||||
|
const parent = resolvedByPath.get(parentPath) ?? {
|
||||||
|
checked: false,
|
||||||
|
hasChecked: false,
|
||||||
|
expanded: false,
|
||||||
|
hasExpanded: false
|
||||||
|
};
|
||||||
|
const item = tree.itemForNode(node.id);
|
||||||
|
const checkbox = item && tree.directCheckbox(item);
|
||||||
|
const toggle = item?.querySelector(':scope > .checkbox-tree__row > .checkbox-tree__toggle');
|
||||||
|
const isCovered = slot < state.coverage;
|
||||||
|
const checked = checkbox && isCovered
|
||||||
|
? (parent.hasChecked ? parent.checked : false) !== checkedDecoders.get(depth)(slot)
|
||||||
|
: false;
|
||||||
|
const expanded = Boolean(toggle && isCovered && expandedDecoders.get(depth)(slot));
|
||||||
|
if (checkbox && !checkbox.disabled) checkbox.checked = checked;
|
||||||
|
tree.setExpanded(item, expanded);
|
||||||
|
resolvedByPath.set(path, {
|
||||||
|
checked,
|
||||||
|
hasChecked: Boolean(checkbox),
|
||||||
|
expanded,
|
||||||
|
hasExpanded: Boolean(toggle)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
tree.updateAllParentCheckboxes();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function serializeStateToFragment(tree, manifest, encoding = tree.options?.stateEncoding ?? 'adaptive') {
|
||||||
|
const state = encodeTreeState(tree, manifest, encoding);
|
||||||
|
const params = new URLSearchParams({ v: String(state.version) });
|
||||||
|
if (state.encoding === 'tiered') {
|
||||||
|
for (const [name, value] of state.fields) params.set(name, value);
|
||||||
|
} else {
|
||||||
|
if (state.checked) params.set('c', state.checked);
|
||||||
|
if (state.expanded) params.set('x', state.expanded);
|
||||||
|
}
|
||||||
|
return `#${params}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function restoreStateFromLocation(
|
||||||
|
tree,
|
||||||
|
manifest,
|
||||||
|
location = window.location,
|
||||||
|
encoding = tree.options?.stateEncoding ?? 'adaptive'
|
||||||
|
) {
|
||||||
|
const state = decodeTreeState(location.hash ?? '', manifest, encoding);
|
||||||
|
if (state.applied) applyTreeState(tree, manifest, state);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user