From c84426d58d093474b86e9861019c79d8b1c503a1 Mon Sep 17 00:00:00 2001 From: Jeff Musgrave <1578618+JeffMusgrave@users.noreply.github.com> Date: Wed, 21 Oct 2026 21:11:25 -0400 Subject: [PATCH 01/15] Correct native Cargo consumer compilation and generated lint scopes Port the governed replacement compilation correction from PR #9 to current main. Respect compiler-generated test and standard-macro lint scopes without allowing written source suppressions; retain source deny/forbid as compiler errors. Real Cargo regression tests, source-attribute rejection and the scoped source check pass. This is a local tooling milestone, not a versioned or published release; semantic Rust coverage and native acceptance remain open. --- docs/design/cargo-facts.md | 27 +++++++--- internal/quality/cargo_test.go | 26 ++++++++++ internal/rustfacts/compilation.go | 8 +-- .../rustfacts/compilation_patches_test.go | 49 ++++++++++++++++++ internal/rustfacts/compilation_test.go | 50 ++++++++++--------- internal/rustfacts/discovery.go | 5 ++ internal/rustfacts/lints_test.go | 8 +-- 7 files changed, 137 insertions(+), 36 deletions(-) create mode 100644 internal/rustfacts/compilation_patches_test.go diff --git a/docs/design/cargo-facts.md b/docs/design/cargo-facts.md index 7e64dd03..5be2f507 100644 --- a/docs/design/cargo-facts.md +++ b/docs/design/cargo-facts.md @@ -290,11 +290,26 @@ invitation for the check to resolve dependencies. Checks select the manifest's ordinary Cargo packages, default features and all enabled targets, using the governed native host explicitly. They do not enable -every feature or establish coverage of other target configurations. Compiler -warnings, Clippy's default lint group and unused-code lints use `--force-warn` at -the invocation boundary. Source `allow`, `expect`, `deny` and `forbid` attributes -cannot hide these diagnostics or turn policy lint collection into a compiler -failure. Code Polishy treats the resulting typed findings as blocking errors, +every feature or establish coverage of other target configurations. + +Compiler selection maps explicitly governed replacement packages to their consumers, +using the same ownership boundary as locked resolution. Discovery retains the +replacement's own metadata for source analysis and also discovers its consumer. +Selecting only replacement source still checks the consumer; selecting both +does not compile it twice. Replacement development targets and their independent +locks are not part of the consumer build. Source analysis remains selected for +the replacement, while compiler evidence covers only dependencies active in the +consumer's default feature and host configuration. + +Compiler warnings, Clippy's default lint group and unused-code lints use `--warn` at +the invocation boundary. This retains the compiler-generated test-entry and +standard-library macro lint scopes. Forcing those scopes reports valid binary +entry points as unused in test builds and deliberate standard-library expansion +control flow as unreachable user code. Written `allow` and `expect` attributes +remain blocking findings under the independent parsed source policy, including +conditional forms. Written `deny` and `forbid` retain compiler failure semantics; +collection does not lower source errors to warnings. Code Polishy treats the +resulting typed findings as blocking errors, subject to its existing exact exception policy. Failed compilation always adds a project failure, even when the compiler also reports typed source diagnostics; an excepted lint cannot make a failed build or operational invocation pass. @@ -307,7 +322,7 @@ The advertised `tail-call-track-caller` warning requires unstable explicit tail calls in this release and is not selectable on the supported stable toolchain. Its exclusion is verified against the real compiler, not silently ignored at runtime. -Native dead-code coverage uses rustc's forced unused-item/import/macro diagnostics +Native dead-code coverage uses rustc's unused-item/import/macro diagnostics for the selected Cargo configuration. Public library APIs remain legitimate entry points; a private item used only by a disabled feature is unused in the checked configuration. This is compiler reachability, not a claim that every diff --git a/internal/quality/cargo_test.go b/internal/quality/cargo_test.go index 635c6a44..3342c151 100644 --- a/internal/quality/cargo_test.go +++ b/internal/quality/cargo_test.go @@ -82,6 +82,32 @@ func TestCargoAndGoCheckPlanMatchesObservedCommandOrder(t *testing.T) { } } +func TestCargoReplacementCheckKeepsGoCommandsInsideThePlan(t *testing.T) { + t.Parallel() + repo := cargoQualityRepository(t) + repo.Config.SupplyChain.DependencyOverridePolicies = []policy.DependencyOverridePolicy{{Ecosystem: "cargo", Path: "Cargo.toml", Field: "patch.crates-io"}} + writeQualityFile(t, repo.Root, "Cargo.toml", "[package]\nname='app'\nversion='1.0.0'\nedition='2024'\n[workspace]\nexclude=['vendor/library']\n[dependencies]\nlibrary='=1.0.0'\n[patch.crates-io]\nlibrary={path='vendor/library'}\n") + writeQualityFile(t, repo.Root, "Cargo.lock", "version=4\n[[package]]\nname='app'\nversion='1.0.0'\ndependencies=['library']\n[[package]]\nname='library'\nversion='1.0.0'\n") + writeQualityFile(t, repo.Root, "src/lib.rs", "pub fn app() {\n library::call();\n}\n") + writeQualityFile(t, repo.Root, "vendor/library/Cargo.toml", "[package]\nname='library'\nversion='1.0.0'\nedition='2024'\n") + writeQualityFile(t, repo.Root, "vendor/library/src/lib.rs", "pub fn call() {}\n") + writeQualityFile(t, repo.Root, "go.mod", "module example.test/mixed\n\ngo 1.26.6\n") + writeQualityFile(t, repo.Root, "main.go", "package main\n\nfunc main() {}\n") + selection := repository.Selection{Files: []string{"main.go", "src/lib.rs", "vendor/library/src/lib.rs"}} + planned := CheckCommands(repo, selection, "check") + boundary := &cargoRecordingRunner{OSRunner: runner.OSRunner{}} + if findings := Check(t.Context(), repo, selection, boundary, "check"); len(findings) != 0 { + t.Fatalf("replacement prevented the mixed-language check: %+v", findings) + } + want := []string{} + for _, command := range planned { + want = append(want, command.Name) + } + if !reflect.DeepEqual(boundary.names, want) { + t.Fatalf("executed commands = %v, planned = %v", boundary.names, want) + } +} + func TestCargoNativeDeadCodePreservesPublicAPIAndChecksSelectedFeatures(t *testing.T) { t.Parallel() repo := cargoQualityRepository(t) diff --git a/internal/rustfacts/compilation.go b/internal/rustfacts/compilation.go index e3e4fa41..ab76e8ee 100644 --- a/internal/rustfacts/compilation.go +++ b/internal/rustfacts/compilation.go @@ -26,7 +26,9 @@ func PlanCompilation(repo repository.Repository, discovery DiscoveryPlan, allFil if len(discovery.Commands) == 0 { return plan } - for _, manifest := range discovery.Manifests { + manifests, _, findings := cargoResolutionRoots(repo, discovery.Manifests) + plan.Findings = append(plan.Findings, findings...) + for _, manifest := range manifests { files := compilationSources(repo, manifest, allFiles) native, err := repo.NativeUnit(files, "lint", "typecheck", "dead-code") if err != nil { @@ -58,9 +60,9 @@ func compilationSources(repo repository.Repository, manifest string, allFiles [] } func compilationCommand(repo repository.Repository, manifest string) policy.Command { - command := Command(repo, manifestCommandName("policy-clippy", manifest), "cargo-clippy", "clippy", "--frozen", "--all-targets", "--no-deps", "--message-format=json", "--target", HostTarget(), "--manifest-path", manifest, "--", "--force-warn", "clippy::all", "--force-warn", "unused") + command := Command(repo, manifestCommandName("policy-clippy", manifest), "cargo-clippy", "clippy", "--frozen", "--all-targets", "--no-deps", "--message-format=json", "--target", HostTarget(), "--manifest-path", manifest, "--", "--warn", "clippy::all", "--warn", "unused") for _, lint := range strings.Fields(compilerWarningLints) { - command.Argv = append(command.Argv, "--force-warn", lint) + command.Argv = append(command.Argv, "--warn", lint) } command.Environment = append(command.Environment, "PATH", "LIB", "INCLUDE", "LIBPATH", "SDKROOT", "DEVELOPER_DIR") command.EnvironmentOverrides = append(command.EnvironmentOverrides, "CARGO="+repo.RustTool("cargo"), "CLIPPY_CONF_DIR="+filepath.Join(repo.PolicyRoot, "tools")) diff --git a/internal/rustfacts/compilation_patches_test.go b/internal/rustfacts/compilation_patches_test.go new file mode 100644 index 00000000..b5608b05 --- /dev/null +++ b/internal/rustfacts/compilation_patches_test.go @@ -0,0 +1,49 @@ +package rustfacts + +import ( + "bytes" + "os" + "path/filepath" + "slices" + "testing" + + "github.com/riteofstring/code-polishy/internal/policy" + "github.com/riteofstring/code-polishy/internal/runner" +) + +func TestRustCompilationUsesReplacementConsumerAndPreservesDependencyTargets(t *testing.T) { + t.Parallel() + for _, selected := range []string{"Cargo.toml", "vendor/patch-source/src/lib.rs", "vendor/leaf/src/lib.rs"} { + t.Run(selected, func(t *testing.T) { + repo := cargoPatchRepository(t) + manifest := string(readRustFile(t, repo, "Cargo.toml")) + manifest = string(bytes.ReplaceAll([]byte(manifest), []byte("default=[]"), []byte("default=['desktop']"))) + write(t, repo.Root, "Cargo.toml", manifest) + write(t, repo.Root, "src/lib.rs", "pub fn value() -> u32 { patch_source::value() }\n") + write(t, repo.Root, "vendor/patch-source/src/lib.rs", "pub fn value() -> u32 { patch_leaf::value() }\n") + write(t, repo.Root, "vendor/patch-source/build.rs", "fn main() {}\n") + write(t, repo.Root, "vendor/patch-source/tests/unconsumed.rs", "compile_error!(\"dependency development targets must not compile\");\n") + write(t, repo.Root, "vendor/leaf/src/lib.rs", "pub fn value() -> u32 { 42 }\n") + before := readRustFile(t, repo, "Cargo.lock") + plan, discovery := compilationDiscovery(t, repo, selected) + if findings := Compile(t.Context(), repo, plan, discovery, runner.OSRunner{}); len(findings) != 0 { + t.Fatalf("consumer compilation failed: %+v", findings) + } + if !bytes.Equal(before, readRustFile(t, repo, "Cargo.lock")) { + t.Fatal("compilation changed the consumer lock") + } + for _, path := range []string{"vendor/patch-source/Cargo.lock", "vendor/leaf/Cargo.lock"} { + if _, err := os.Stat(filepath.Join(repo.Root, filepath.FromSlash(path))); !os.IsNotExist(err) { + t.Fatalf("compilation created a replacement lock at %s: %v", path, err) + } + } + write(t, repo.Root, "vendor/leaf/src/lib.rs", "pub fn value() -> u32 { \"wrong\" }\n") + findings := Compile(t.Context(), repo, plan, discovery, runner.OSRunner{}) + if !slices.ContainsFunc(findings, func(f policy.Finding) bool { + return f.Check == "quality.rustCompiler" && f.Subject == "E0308" && f.Path == "vendor/leaf/src/lib.rs" + }) { + t.Fatalf("consumer failed to check the replacement source: %+v", findings) + } + }) + } +} diff --git a/internal/rustfacts/compilation_test.go b/internal/rustfacts/compilation_test.go index 89aaca25..3f28c911 100644 --- a/internal/rustfacts/compilation_test.go +++ b/internal/rustfacts/compilation_test.go @@ -51,32 +51,36 @@ func TestRustCompilationReportsOriginalTypeAndLintLocations(t *testing.T) { } } -func TestRustCompilationCollectsLintsDespiteSourceLevels(t *testing.T) { +func TestRustCompilationRespectsCompilerGeneratedLintScopes(t *testing.T) { t.Parallel() - for name, test := range map[string]struct{ source, subject string }{ - "allow clippy": {"#[allow(clippy::needless_bool)]\npub fn answer(value: bool) -> bool { if value { true } else { false } }\n", "clippy::needless_bool"}, - "expect clippy": {"#[expect(clippy::needless_bool)]\npub fn answer(value: bool) -> bool { if value { true } else { false } }\n", "clippy::needless_bool"}, - "deny clippy": {"#[deny(clippy::needless_bool)]\npub fn answer(value: bool) -> bool { if value { true } else { false } }\n", "clippy::needless_bool"}, - "forbid clippy": {"#[forbid(clippy::needless_bool)]\npub fn answer(value: bool) -> bool { if value { true } else { false } }\n", "clippy::needless_bool"}, - "allow unused": {"#[allow(dead_code)]\nfn unused() {}\npub fn answer() {}\n", "dead_code"}, - "expect unused": {"#[expect(dead_code)]\nfn unused() {}\npub fn answer() {}\n", "dead_code"}, - "allow warning": {"#[allow(unreachable_code)]\npub fn answer() { return; println!(\"unreachable\"); }\n", "unreachable_code"}, - } { - t.Run(name, func(t *testing.T) { - repo := compilationRepository(t, test.source) + repo := compilationRepository(t, "pub fn answer() -> u32 { 42 }\n") + write(t, repo.Root, "src/main.rs", `fn main() { show_answer(); } +fn show_answer() { println!("{}", app::answer()); } +#[test] +fn pinned_future() { + let future = std::pin::pin!(async { app::answer() }); + assert_eq!(std::mem::size_of_val(&future), std::mem::size_of::()); +} +`) + plan, discovery := compilationDiscovery(t, repo, "src/main.rs") + if findings := Compile(t.Context(), repo, plan, discovery, runner.OSRunner{}); len(findings) != 0 { + t.Fatalf("compiler-generated test entry and standard macro produced false lints: %+v", findings) + } +} + +func TestRustCompilationPreservesDeniedSourceLintsAsFailures(t *testing.T) { + t.Parallel() + for _, level := range []string{"deny", "forbid"} { + t.Run(level, func(t *testing.T) { + repo := compilationRepository(t, "#["+level+"(clippy::needless_bool)]\npub fn answer(value: bool) -> bool { if value { true } else { false } }\n") plan, discovery := compilationDiscovery(t, repo, "src/lib.rs") - result, output, err := runner.RunCaptured(t.Context(), runner.OSRunner{}, repo.Root, plan.Commands[0], runner.CaptureStructured) - if err != nil || result.ExitStatus != 0 { - t.Fatalf("lint collection failed compilation: %+v, %v; %s", result, err, output.Stderr) - } - report, err := ParseCompilation(repo, "Cargo.toml", discovery.Workspaces[0], output.Stdout) - if err != nil || !report.Success { - t.Fatalf("lint report lacks successful compilation evidence: %+v, %v", report, err) - } - if !slices.ContainsFunc(report.Findings, func(finding policy.Finding) bool { - return finding.Subject == test.subject && finding.Path == "src/lib.rs" && finding.Line > 0 + findings := Compile(t.Context(), repo, plan, discovery, runner.OSRunner{}) + if !slices.ContainsFunc(findings, func(finding policy.Finding) bool { + return finding.Subject == "clippy::needless_bool" && finding.Path == "src/lib.rs" && finding.Line > 0 + }) || !slices.ContainsFunc(findings, func(finding policy.Finding) bool { + return finding.Subject == "compilation" }) { - t.Fatalf("source levels hid the governed lint: %+v", report.Findings) + t.Fatalf("source error was lowered or lost: %+v", findings) } }) } diff --git a/internal/rustfacts/discovery.go b/internal/rustfacts/discovery.go index b7e66a40..a2006620 100644 --- a/internal/rustfacts/discovery.go +++ b/internal/rustfacts/discovery.go @@ -29,6 +29,11 @@ func PlanDiscovery(repo repository.Repository, selected, allFiles []string) Disc if len(manifests) == 0 { return plan } + consumers, _, findings := cargoResolutionRoots(repo, manifests) + plan.Findings = append(plan.Findings, findings...) + manifests = append(manifests, consumers...) + slices.Sort(manifests) + manifests = slices.Compact(manifests) if err := configurationProblem(repo); err != nil { plan.Findings = append(plan.Findings, projectFinding(manifests[0], "configuration", err.Error())) return plan diff --git a/internal/rustfacts/lints_test.go b/internal/rustfacts/lints_test.go index 72810d12..42c5c920 100644 --- a/internal/rustfacts/lints_test.go +++ b/internal/rustfacts/lints_test.go @@ -15,11 +15,11 @@ func TestRustLintCollectionCoversCompilerWarningsWithoutLoweringErrors(t *testin if err != nil { t.Fatalf("compiler lint interface: %v; %s", err, output.Stderr) } - forced := map[string]bool{} + requested := map[string]bool{} arguments := compilationCommand(repo, "Cargo.toml").Argv for index, argument := range arguments[:len(arguments)-1] { - if argument == "--force-warn" { - forced[arguments[index+1]] = true + if argument == "--warn" { + requested[arguments[index+1]] = true } } warnings, errors := 0, 0 @@ -33,7 +33,7 @@ func TestRustLintCollectionCoversCompilerWarningsWithoutLoweringErrors(t *testin } else { errors++ } - if forced[fields[0]] != (fields[1] == "warn") { + if requested[fields[0]] != (fields[1] == "warn") { t.Errorf("policy lint collection changes the compiler's %s contract for %s", fields[1], fields[0]) } } From 3c71610abd422a3d1776297c21ed7ae42c2b5c46 Mon Sep 17 00:00:00 2001 From: Jeff Musgrave <1578618+JeffMusgrave@users.noreply.github.com> Date: Wed, 21 Oct 2026 21:50:24 -0400 Subject: [PATCH 02/15] Remove distrusted eMudhra roots from the scanner trust bundle Trivy 0.74.0 is built with Go 1.26.6, which lacks the GO-2026-6809 public-key denylist. The source image's bundle trusts emSign ECC Root CA - G3, which cross-signed the issuer of the compromised eMudhra TLS CAs. Ship that bundle without the three roots Mozilla removed on 2026-10-20, keeping every other certificate byte-identical. CVE-2026-111943 stays unassessed because undisclosed cross-certificates remain possible without Go's denylist. Co-Authored-By: Claude Opus 5.5 --- artifact-security/scanner-ca-certificates.crt | 3018 +++++++++++++++++ artifact-security/scanner.Dockerfile | 2 +- docs/artifact-security.md | 5 +- docs/design/trivy-scanner-assessments.md | 70 +- docs/source-provenance.md | 4 +- .../testdata/emsign-ecc-root-ca-g3.pem | 16 + .../testdata/emsign-root-tls-ca-g3-cross.pem | 21 + .../testdata/ghcr-io-chain.pem | 114 + .../testdata/pkg-containers-chain.pem | 93 + internal/artifactsecurity/trust_test.go | 99 + 10 files changed, 3437 insertions(+), 5 deletions(-) create mode 100644 artifact-security/scanner-ca-certificates.crt create mode 100644 internal/artifactsecurity/testdata/emsign-ecc-root-ca-g3.pem create mode 100644 internal/artifactsecurity/testdata/emsign-root-tls-ca-g3-cross.pem create mode 100644 internal/artifactsecurity/testdata/ghcr-io-chain.pem create mode 100644 internal/artifactsecurity/testdata/pkg-containers-chain.pem create mode 100644 internal/artifactsecurity/trust_test.go diff --git a/artifact-security/scanner-ca-certificates.crt b/artifact-security/scanner-ca-certificates.crt new file mode 100644 index 00000000..e9f91335 --- /dev/null +++ b/artifact-security/scanner-ca-certificates.crt @@ -0,0 +1,3018 @@ +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE +AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw +CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ +BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND +VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb +qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY +HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo +G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA +lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr +IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ +0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH +k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 +4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO +m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa +cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl +uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI +KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls +ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG +AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT +VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG +CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA +cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA +QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA +7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA +cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA +QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA +czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu +aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt +aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud +DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF +BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp +D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU +JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m +AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD +vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms +tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH +7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA +h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF +d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H +pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx +CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ +WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ +BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG +Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/ +yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf +BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz +WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF +tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z +374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC +IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL +mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7 +wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS +MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2 +ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet +UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H +YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3 +LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1 +RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM +LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf +77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N +JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm +fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp +6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp +1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B +9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok +RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv +uu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQsw +CQYDVQQGEwJFUzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgw +FgYDVQRhDA9WQVRFUy1RMjgyNjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1S +Q00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4MTIyMDA5MzczM1oXDTQzMTIyMDA5 +MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQtUkNNMQ4wDAYDVQQL +DAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNBQyBS +QUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LH +sbI6GA60XYyzZl2hNPk2LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oK +Um8BA06Oi6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqGSM49BAMDA2kAMGYCMQCu +SuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoDzBOQn5IC +MQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJy +v+c= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNV +BAUTCUc2MzI4NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlk +YWQgZGUgQ2VydGlmaWNhY2lvbjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNV +BAMTGUFORiBTZWN1cmUgU2VydmVyIFJvb3QgQ0EwHhcNMTkwOTA0MTAwMDM4WhcN +MzkwODMwMTAwMDM4WjCBhDESMBAGA1UEBRMJRzYzMjg3NTEwMQswCQYDVQQGEwJF +UzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uMRQwEgYD +VQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3VyZSBTZXJ2ZXIgUm9v +dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvmZFSVgpCj +cqQZAZ2cC4Ffc0m6p6zzBE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9q +yGFOtibBTI3/TO80sh9l2Ll49a2pcbnvT1gdpd50IJeh7WhM3pIXS7yr/2WanvtH +2Vdy8wmhrnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcvB2VSAKduyK9o7PQUlrZX +H1bDOZ8rbeTzPvY1ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXsezx76W0OL +zc2oD2rKDF65nkeP8Nm2CgtYZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyR +p1RMVwnVdmPF6GUe7m1qzwmd+nxPrWAI/VaZDxUse6mAq4xhj0oHdkLePfTdsiQz +W7i1o0TJrH93PB0j7IKppuLIBkwC/qxcmZkLLxCKpvR/1Yd0DVlJRfbwcVw5Kda/ +SiOL9V8BY9KHcyi1Swr1+KuCLH5zJTIdC2MKF4EA/7Z2Xue0sUDKIbvVgFHlSFJn +LNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe8TZBAQIvfXOn3kLMTOmJDVb3 +n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVOHj1tyRRM4y5B +u8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxj +o1+TypOYCK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC +AgEATh65isagmD9uw2nAalxJUqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L +9IBdeeUXZ701guLyPI59WzbLWoAAKfLOKyzxj6ptBZNscsdW699QIyjlRRA96Gej +rw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/RogsKQOLHDtdD+4E5UGUcjohybK +pFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFsSBXXiJCZBMXM5gf0 +vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eurU+AagNjq +OknkJjCb5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ +/zo1PqVUSlJZS2Db7v54EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ9 +2zg/LFis6ELhDtjTO0wugumDLmsx2d1Hhk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI ++PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGyg77FGr8H6lnco4g175x2 +MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3r5+qPeoo +tt7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE +BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w +MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC +SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 +ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv +UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX +4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 +KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ +gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb +rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ +51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F +be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe +KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F +v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn +fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 +jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz +ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL +e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 +jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz +WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V +SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j +pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX +X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok +fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R +K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU +ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU +LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT +LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK +gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ +W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg +1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K +8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r +2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me +z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR +8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj +mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz +7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6 ++XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI +0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm +UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2 +LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS +k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl +7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm +btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl +urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+ +fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63 +n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE +76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H +9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT +4PsJYGw= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl +ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr +ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr +BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM +YyRIHN8wfdVoOw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi +9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk +M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB +MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw +CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW +1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE +AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG +EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM +FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC +REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp +Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM +VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ +SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ +4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L +cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi +eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG +A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 +DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j +vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP +DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc +maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D +lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv +KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICFTCCAZugAwIBAgIQPZg7pmY9kGP3fiZXOATvADAKBggqhkjOPQQDAzBMMS4w +LAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgRUNDIFRMUyAyMDIxMQ0w +CwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTI2MjNaFw00MTA0 +MTcwOTI2MjJaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBDQSBF +Q0MgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMHYwEAYHKoZI +zj0CAQYFK4EEACIDYgAEloZYKDcKZ9Cg3iQZGeHkBQcfl+3oZIK59sRxUM6KDP/X +tXa7oWyTbIOiaG6l2b4siJVBzV3dscqDY4PMwL502eCdpO5KTlbgmClBk1IQ1SQ4 +AjJn8ZQSb+/Xxd4u/RmAo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR2 +KCXWfeBmmnoJsmo7jjPXNtNPojAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMD +aAAwZQIwW5kp85wxtolrbNa9d+F851F+uDrNozZffPc8dz7kUK2o59JZDCaOMDtu +CCrCp1rIAjEAmeMM56PDr9NJLkaCI2ZdyQAUEv049OGYa3cpetskz2VAv9LcjBHo +9H1/IISpQuQo +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFZDCCA0ygAwIBAgIQU9XP5hmTC/srBRLYwiqipDANBgkqhkiG9w0BAQwFADBM +MS4wLAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgUlNBIFRMUyAyMDIx +MQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTIxMTBaFw00 +MTA0MTcwOTIxMDlaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBD +QSBSU0EgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtoAOxHm9BYx9sKOdTSJNy/BBl01Z +4NH+VoyX8te9j2y3I49f1cTYQcvyAh5x5en2XssIKl4w8i1mx4QbZFc4nXUtVsYv +Ye+W/CBGvevUez8/fEc4BKkbqlLfEzfTFRVOvV98r61jx3ncCHvVoOX3W3WsgFWZ +kmGbzSoXfduP9LVq6hdKZChmFSlsAvFr1bqjM9xaZ6cF4r9lthawEO3NUDPJcFDs +GY6wx/J0W2tExn2WuZgIWWbeKQGb9Cpt0xU6kGpn8bRrZtkh68rZYnxGEFzedUln +nkL5/nWpo63/dgpnQOPF943HhZpZnmKaau1Fh5hnstVKPNe0OwANwI8f4UDErmwh +3El+fsqyjW22v5MvoVw+j8rtgI5Y4dtXz4U2OLJxpAmMkokIiEjxQGMYsluMWuPD +0xeqqxmjLBvk1cbiZnrXghmmOxYsL3GHX0WelXOTwkKBIROW1527k2gV+p2kHYzy +geBYBr3JtuP2iV2J+axEoctr+hbxx1A9JNr3w+SH1VbxT5Aw+kUJWdo0zuATHAR8 +ANSbhqRAvNncTFd+rrcztl524WWLZt+NyteYr842mIycg5kDcPOvdO3GDjbnvezB +c6eUWsuSZIKmAMFwoW4sKeFYV+xafJlrJaSQOoD0IJ2azsct+bJLKZWD6TWNp0lI +pw9MGZHQ9b8Q4HECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +dEmZ0f+0emhFdcN+tNzMzjkz2ggwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +DAUAA4ICAQAjQ1MkYlxt/T7Cz1UAbMVWiLkO3TriJQ2VSpfKgInuKs1l+NsW4AmS +4BjHeJi78+xCUvuppILXTdiK/ORO/auQxDh1MoSf/7OwKwIzNsAQkG8dnK/haZPs +o0UvFJ/1TCplQ3IM98P4lYsU84UgYt1UU90s3BiVaU+DR3BAM1h3Egyi61IxHkzJ +qM7F78PRreBrAwA0JrRUITWXAdxfG/F851X6LWh3e9NpzNMOa7pNdkTWwhWaJuyw +xfW70Xp0wmzNxbVe9kzmWy2B27O3Opee7c9GslA9hGCZcbUztVdF5kJHdWoOsAgM +rr3e97sPWD2PAzHoPYJQyi9eDF20l74gNAf0xBLh7tew2VktafcxBPTy+av5EzH4 +AXcOPUIjJsyacmdRIXrMPIWo6iFqO9taPKU0nprALN+AnCng33eU0aKAQv9qTFsR +0PXNor6uzFFcw9VUewyu1rkGd4Di7wcaaMxZUa1+XGdrudviB0JbuAEFWDlN5LuY +o7Ey7Nmj1m+UI/87tyll5gfp77YZ6ufCOB0yiJA8EytuzO+rdwY0d4RPcuSBhPm5 +dDTedk+SKlOxJTnbPP/lPqYO5Wue/9vsL3SD3460s6neFE3/MaNFcyT6lSnMEpcE +oji2jbDwN/zIIX8/syQbPYtuzE2wFg2WHYMfRsCbvUOZ58SWLs5fyQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1 +MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1UdDgQWBBRlzeurNR4APn7VdMAc +tHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4wgZswgZgGBFUd +IAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j +b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABC +AG8AbgBhAG4AbwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAw +ADEANzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9m +iWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL4QjbEwj4KKE1soCzC1HA01aajTNF +Sa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDbLIpgD7dvlAceHabJ +hfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1ilI45P +Vf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZE +EAEeiGaPcjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV +1aUsIC+nmCjuRfzxuIgALI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2t +CsvMo2ebKHTEm9caPARYpoKdrcd7b/+Alun4jWq9GJAd/0kakFI3ky88Al2CdgtR +5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH9IBk9W6VULgRfhVwOEqw +f9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpfNIbnYrX9 +ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNK +GbqEZycPvEJdvSRUDewdcAZfpLz6IHxV +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIQVW9l47TZkGobCdFsPsBsIDANBgkqhkiG9w0BAQsFADBU +MQswCQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRI +T1JJVFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0ExMB4XDTE5MTIxOTAz +MTYxN1oXDTQ0MTIxMjAzMTYxN1owVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJF +SUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2Jh +bCBSb290IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPFmCL3Z +xRVhy4QEQaVpN3cdwbB7+sN3SJATcmTRuHyQNZ0YeYjjlwE8R4HyDqKYDZ4/N+AZ +spDyRhySsTphzvq3Rp4Dhtczbu33RYx2N95ulpH3134rhxfVizXuhJFyV9xgw8O5 +58dnJCNPYwpj9mZ9S1WnP3hkSWkSl+BMDdMJoDIwOvqfwPKcxRIqLhy1BDPapDgR +at7GGPZHOiJBhyL8xIkoVNiMpTAK+BcWyqw3/XmnkRd4OJmtWO2y3syJfQOcs4ll +5+M7sSKGjwZteAf9kRJ/sGsciQ35uMt0WwfCyPQ10WRjeulumijWML3mG90Vr4Tq +nMfK9Q7q8l0ph49pczm+LiRvRSGsxdRpJQaDrXpIhRMsDQa4bHlW/KNnMoH1V6XK +V0Jp6VwkYe/iMBhORJhVb3rCk9gZtt58R4oRTklH2yiUAguUSiz5EtBP6DF+bHq/ +pj+bOT0CFqMYs2esWz8sgytnOYFcuX6U1WTdno9uruh8W7TXakdI136z1C2OVnZO +z2nxbkRs1CTqjSShGL+9V/6pmTW12xB3uD1IutbB5/EjPtffhZ0nPNRAvQoMvfXn +jSXWgXSHRtQpdaJCbPdzied9v3pKH9MiyRVVz99vfFXQpIsHETdfg6YmV6YBW37+ +WGgHqel62bno/1Afq8K0wM7o6v0PvY1NuLxxAgMBAAGjQjBAMB0GA1UdDgQWBBTF +7+3M2I0hxkjk49cULqcWk+WYATAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAUoKsITQfI/Ki2Pm4rzc2IInRNwPWaZ+4 +YRC6ojGYWUfo0Q0lHhVBDOAqVdVXUsv45Mdpox1NcQJeXyFFYEhcCY5JEMEE3Kli +awLwQ8hOnThJdMkycFRtwUf8jrQ2ntScvd0g1lPJGKm1Vrl2i5VnZu69mP6u775u ++2D2/VnGKhs/I0qUJDAnyIm860Qkmss9vk/Ves6OF8tiwdneHg56/0OGNFK8YT88 +X7vZdrRTvJez/opMEi4r89fO4aL/3Xtw+zuhTaRjAv04l5U/BXCga99igUOLtFkN +SoxUnMW7gZ/NfaXvCyUeOiDbHPwfmGcCCtRzRBPbUYQaVQNW4AB+dAb/OMRyHdOo +P2gxXdMJxy6MW2Pg6Nwe0uxhHvLe5e/2mXZgLR6UcnHGCyoyx5JO1UbXHfmpGQrI ++pXObSOYqgs4rZpWDW+N8TEAiMEXnM0ZNjX+VVOg4DwzX5Ze4jLp3zO7Bkqp2IRz +znfSxqxx4VyjHQy7Ct9f4qNx2No3WqB4K/TUfet27fJhcKVlmtOJNBir+3I+17Q9 +eVzYH6Eze9mCUAyTF6ps3MKCuwJXNq+YJyo5UOGwifUll35HaBC07HPKs5fRJNz2 +YqAo07WjuGS3iGJCz51TzZm+ZGiPTx4SSPfSKcOYKMryMguTjClPPGAyzQWWYezy +r/6zcCwupvI= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQsw +CQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJ +VFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgy +MVoXDTQ0MTIxMjAzMTgyMVowVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJ +TkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2JhbCBS +b290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABJ3LgJGNU2e1uVCxA/jlSR9B +IgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK+ ++kpRuDCK/eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0OBBYEFNJK +sVF/BvDRgh9Obl+rg/xI1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA +94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8gUXOQwKhbYdDFUDn9hf7B +43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr +6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV +L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 +1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx +MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ +QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB +arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr +Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi +FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS +P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN +9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz +uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h +9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t +OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo ++fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 +KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 +DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us +H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ +I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 +5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h +3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz +Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y +ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E +N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 +tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX +0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c +/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X +KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY +zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS +O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D +34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP +K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv +Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj +QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS +IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 +HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa +O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv +033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u +dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE +kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 +3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD +u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq +4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy +MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe +NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH +PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I +x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe +QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR +yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO +QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 +H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ +QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD +i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs +nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 +rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI +hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf +GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb +lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka ++elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal +TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i +nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 +gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr +G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os +zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x +L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx +MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP +T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 +sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL +TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 +/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp +7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz +EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt +hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP +a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot +aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg +TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV +PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv +cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL +tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd +BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT +ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL +jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS +ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy +P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 +xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d +Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN +5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe +/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z +AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ +5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT +IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw +MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy +ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N +T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR +FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J +cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW +BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm +fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv +GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQsw +CQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlu +bHkgUm9vdCBFMTAeFw0yMTA0MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJ +BgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlubHkxGjAYBgNVBAMTEUNlcnRhaW5s +eSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4fxzf7flHh4axpMCK ++IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9YBk2 +QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4 +hevIIgcwCgYIKoZIzj0EAwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozm +ut6Dacpps6kFtZaSF4fC0urQe87YQVt8rgIwRt7qy12a7DLCZRawTDBcMPPaTnOG +BtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAw +PTELMAkGA1UEBhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2Vy +dGFpbmx5IFJvb3QgUjEwHhcNMjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9 +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0 +YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANA2 +1B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O5MQT +vqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbed +aFySpvXl8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b0 +1C7jcvk2xusVtyWMOvwlDbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5 +r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGIXsXwClTNSaa/ApzSRKft43jvRl5tcdF5 +cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkNKPl6I7ENPT2a/Z2B7yyQ +wHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQAjeZjOVJ +6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA +2CnbrlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyH +Wyf5QBGenDPBt+U1VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMR +eiFPCyEQtkA6qyI6BJyLm4SGcprSp6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB +/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTgqj8ljZ9EXME66C6u +d0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAszHQNTVfSVcOQr +PbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d +8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi +1wrykXprOQ4vMMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrd +rRT90+7iIgXr0PK3aBLXWopBGsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9di +taY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+gjwN/KUD+nsa2UUeYNrEjvn8K8l7 +lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgHJBu6haEaBQmAupVj +yTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7fpYn +Kx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLy +yCwzk5Iwx06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5n +wXARPbv0+Em34yaXOp/SX3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6 +OV+KmalBWQewLK8= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAw +WjELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAw +MiA0ODE0NjMwODEwMDAzNjEZMBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0x +MzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjdaMFoxCzAJBgNVBAYTAkZSMRIwEAYD +VQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYzMDgxMDAwMzYxGTAX +BgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sO +ty3tRQgXstmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9M +CiBtnyN6tMbaLOQdLNyzKNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPu +I9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8JXrJhFwLrN1CTivngqIkicuQstDuI7pm +TLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16XdG+RCYyKfHx9WzMfgIh +C59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq4NYKpkDf +ePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3Yz +IoejwpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWT +Co/1VTp2lc5ZmIoJlXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1k +JWumIWmbat10TWuXekG9qxf5kBdIjzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5 +hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp//TBt2dzhauH8XwIDAQABo4IB +GjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of +1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczov +L3d3d3cuY2VydGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilo +dHRwOi8vY3JsLmNlcnRpZ25hLmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYr +aHR0cDovL2NybC5kaGlteW90aXMuY29tL2NlcnRpZ25hcm9vdGNhLmNybDANBgkq +hkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOItOoldaDgvUSILSo3L +6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxPTGRG +HVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH6 +0BGM+RFq7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncB +lA2c5uk5jR+mUYyZDDl34bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdi +o2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1 +gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS6Cvu5zHbugRqh5jnxV/v +faci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaYtlu3zM63 +Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayh +jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw +3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQsw +CQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScw +JQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMT +EENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2MDcyNDU0WhcNNDMwMzI2MDcyNDU0 +WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBT +LkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAX +BgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATE +KI6rGFtqvm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7Tm +Fy8as10CW4kjPMIRBSqniBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68Kj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI0GZnQkdjrzife81r1HfS+8 +EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjADVS2m5hjEfO/J +UG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0QoSZ/6vn +nvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM +MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D +ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU +cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 +WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg +Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw +IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH +UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM +TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU +BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM +kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x +AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y +sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL +I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 +J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY +VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB +gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu +QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG +A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz +OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ +VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3 +b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA +DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn +0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB +OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE +fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E +Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m +o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i +sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW +OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez +Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS +adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n +3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ +F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf +CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29 +XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm +djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/ +WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb +AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq +P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko +b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj +XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P +5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi +DrW5viSP +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6 +MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEu +MScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNV +BAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwHhcNMTgwMzE2MTIxMDEzWhcNNDMw +MzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEg +U3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMlzhyC93yZ +n0EGze2jusDbCSzBfN8pfktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/q +p1x4EaTByIVcJdPTsuclzxFUl6s1wB52HO8AU5853BSlLCIls3Jy/I2z5T4IHhQq +NwuIPMqw9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2fJmItdUDmj0VDT06qKhF +8JVOJVkdzZhpu9PMMsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGtg/BKEiJ3 +HAVz4hlxQsDsdUaakFjgao4rpUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGa +mqi4NboMOvJEGyCI98Ul1z3G4z5D3Yf+xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi +7VdNIuJGmj8PkTQkfVXjjJU30xrwCSss0smNtA0Aq2cpKNgB9RkEth2+dv5yXMSF +ytKAQd8FqKPVhJBPC/PgP5sZ0jeJP/J7UhyM9uH3PAeXjA6iWYEMspA90+NZRu0P +qafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSYnjYJdmZm/Bo/6khUHL4wvYBQ +v3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHKHRzQ+8S1h9E6 +Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1 +vALTn04uSNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQAD +ggIBAEii1QALLtA/vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4 +WxmB82M+w85bj/UvXgF2Ez8sALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvo +zMrnadyHncI013nR03e4qllY/p0m+jiGPp2Kh2RX5Rc64vmNueMzeMGQ2Ljdt4NR +5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M1ISHgCq8CYyqOhNf6DR5UMEQ +GfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3EQxiJ2fAyQOaA4kZf +5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2fqSLLiMmq +0Uc9NneoWWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7D +P78v3DSk+yshzWePS/Tj6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTM +qJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmTOPQD8rv7gmsHINFSH5pkAnuYZttcTVoP +0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZckbxJF0WddCajJFdr60qZf +E2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQsw +CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS +VVNUIEJSIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5 +NDQ1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG +A1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB +BAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE1HaTJddZO0Flax7mNCq7dPYS +zuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJGT11NIXe7WB9xwy0 +QVK5buXuQqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHOREKv/ +VbNafAkl1bK6CKBrqx9tMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g +PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2JyX3Jvb3Rf +Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l +dC9DTj1ELVRSVVNUJTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 +c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO +PQQDAwNpADBmAjEAlJAtE/rhY/hhY+ithXhUkZy4kzg+GkHaQBZTQgjKL47xPoFW +wKrY7RjEsK70PvomAjEA8yjixtsrmfu3Ubgko6SUeho/5jbiA1czijDLgsfWFBHV +dWNbFJWcHwHP2NVypw87 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFqTCCA5GgAwIBAgIQczswBEhb2U14LnNLyaHcZjANBgkqhkiG9w0BAQ0FADBI +MQswCQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlE +LVRSVVNUIEJSIFJvb3QgQ0EgMiAyMDIzMB4XDTIzMDUwOTA4NTYzMVoXDTM4MDUw +OTA4NTYzMFowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEi +MCAGA1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDIgMjAyMzCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAK7/CVmRgApKaOYkP7in5Mg6CjoWzckjYaCTcfKr +i3OPoGdlYNJUa2NRb0kz4HIHE304zQaSBylSa053bATTlfrdTIzZXcFhfUvnKLNE +gXtRr90zsWh81k5M/itoucpmacTsXld/9w3HnDY25QdgrMBM6ghs7wZ8T1soegj8 +k12b9py0i4a6Ibn08OhZWiihNIQaJZG2tY/vsvmA+vk9PBFy2OMvhnbFeSzBqZCT +Rphny4NqoFAjpzv2gTng7fC5v2Xx2Mt6++9zA84A9H3X4F07ZrjcjrqDy4d2A/wl +2ecjbwb9Z/Pg/4S8R7+1FhhGaRTMBffb00msa8yr5LULQyReS2tNZ9/WtT5PeB+U +cSTq3nD88ZP+npNa5JRal1QMNXtfbO4AHyTsA7oC9Xb0n9Sa7YUsOCIvx9gvdhFP +/Wxc6PWOJ4d/GUohR5AdeY0cW/jPSoXk7bNbjb7EZChdQcRurDhaTyN0dKkSw/bS +uREVMweR2Ds3OmMwBtHFIjYoYiMQ4EbMl6zWK11kJNXuHA7e+whadSr2Y23OC0K+ +0bpwHJwh5Q8xaRfX/Aq03u2AnMuStIv13lmiWAmlY0cL4UEyNEHZmrHZqLAbWt4N +DfTisl01gLmB1IRpkQLLddCNxbU9CZEJjxShFHR5PtbJFR2kWVki3PaKRT08EtY+ +XTIvAgMBAAGjgY4wgYswDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUZ5Dw1t61 +GNVGKX5cq/ieCLxklRAwDgYDVR0PAQH/BAQDAgEGMEkGA1UdHwRCMEAwPqA8oDqG +OGh0dHA6Ly9jcmwuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3RfYnJfcm9vdF9jYV8y +XzIwMjMuY3JsMA0GCSqGSIb3DQEBDQUAA4ICAQA097N3U9swFrktpSHxQCF16+tI +FoE9c+CeJyrrd6kTpGoKWloUMz1oH4Guaf2Mn2VsNELZLdB/eBaxOqwjMa1ef67n +riv6uvw8l5VAk1/DLQOj7aRvU9f6QA4w9QAgLABMjDu0ox+2v5Eyq6+SmNMW5tTR +VFxDWy6u71cqqLRvpO8NVhTaIasgdp4D/Ca4nj8+AybmTNudX0KEPUUDAxxZiMrc +LmEkWqTqJwtzEr5SswrPMhfiHocaFpVIbVrg0M8JkiZmkdijYQ6qgYF/6FKC0ULn +4B0Y+qSFNueG4A3rvNTJ1jxD8V1Jbn6Bm2m1iWKPiFLY1/4nwSPFyysCu7Ff/vtD +hQNGvl3GyiEm/9cCnnRK3PgTFbGBVzbLZVzRHTF36SXDw7IyN9XxmAnkbWOACKsG +koHU6XCPpz+y7YaMgmo1yEJagtFSGkUPFaUA8JR7ZSdXOUPPfH/mvTWze/EZTN46 +ls/pdu4D58JDUjxqgejBWoC9EV2Ta/vH5mQ/u2kc6d0li690yVRAysuTEwrt+2aS +Ecr1wPrYg1UDfNPFIkZ1cGt5SAYqgpq/5usWDiJFAbzdNpQ0qTUmiteXue4Icr80 +knCDgKs4qllo3UCkGJCy89UDyibK79XH4I9TjvAA46jtn/mtd+ArY0+ew+43u3gJ +hJ65bvspmZDogNOfJA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQsw +CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS +VVNUIEVWIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5 +NTk1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG +A1UEAxMZRC1UUlVTVCBFViBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB +BAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL46bSj8WeeHsxiamJrSc8ZRCC +/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3VlSSowZ/Rk99Yad9rD +wpdhQntJraOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFH8QARY3 +OqQo5FD4pPfsazK2/umLMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g +PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2V2X3Jvb3Rf +Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l +dC9DTj1ELVRSVVNUJTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 +c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO +PQQDAwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CA +y/m0sRtW9XLS/BnRAjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJb +gfM0agPnIjhQW+0ZT0MW +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFqTCCA5GgAwIBAgIQaSYJfoBLTKCnjHhiU19abzANBgkqhkiG9w0BAQ0FADBI +MQswCQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlE +LVRSVVNUIEVWIFJvb3QgQ0EgMiAyMDIzMB4XDTIzMDUwOTA5MTAzM1oXDTM4MDUw +OTA5MTAzMlowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEi +MCAGA1UEAxMZRC1UUlVTVCBFViBSb290IENBIDIgMjAyMzCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBANiOo4mAC7JXUtypU0w3uX9jFxPvp1sjW2l1sJkK +F8GLxNuo4MwxusLyzV3pt/gdr2rElYfXR8mV2IIEUD2BCP/kPbOx1sWy/YgJ25yE +7CUXFId/MHibaljJtnMoPDT3mfd/06b4HEV8rSyMlD/YZxBTfiLNTiVR8CUkNRFe +EMbsh2aJgWi6zCudR3Mfvc2RpHJqnKIbGKBv7FD0fUDCqDDPvXPIEysQEx6Lmqg6 +lHPTGGkKSv/BAQP/eX+1SH977ugpbzZMlWGG2Pmic4ruri+W7mjNPU0oQvlFKzIb +RlUWaqZLKfm7lVa/Rh3sHZMdwGWyH6FDrlaeoLGPaxK3YG14C8qKXO0elg6DpkiV +jTujIcSuWMYAsoS0I6SWhjW42J7YrDRJmGOVxcttSEfi8i4YHtAxq9107PncjLgc +jmgjutDzUNzPZY9zOjLHfP7KgiJPvo5iR2blzYfi6NUPGJ/lBHJLRjwQ8kTCZFZx +TnXonMkmdMV9WdEKWw9t/p51HBjGGjp82A0EzM23RWV6sY+4roRIPrN6TagD4uJ+ +ARZZaBhDM7DS3LAaQzXupdqpRlyuhoFBAUp0JuyfBr/CBTdkdXgpaP3F9ev+R/nk +hbDhezGdpn9yo7nELC7MmVcOIQxFAZRl62UJxmMiCzNJkkg8/M3OsD6Onov4/knF +NXJHAgMBAAGjgY4wgYswDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUqvyREBuH +kV8Wub9PS5FeAByxMoAwDgYDVR0PAQH/BAQDAgEGMEkGA1UdHwRCMEAwPqA8oDqG +OGh0dHA6Ly9jcmwuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3RfZXZfcm9vdF9jYV8y +XzIwMjMuY3JsMA0GCSqGSIb3DQEBDQUAA4ICAQCTy6UfmRHsmg1fLBWTxj++EI14 +QvBukEdHjqOSMo1wj/Zbjb6JzkcBahsgIIlbyIIQbODnmaprxiqgYzWRaoUlrRc4 +pZt+UPJ26oUFKidBK7GB0aL2QHWpDsvxVUjY7NHss+jOFKE17MJeNRqrphYBBo7q +3C+jisosketSjl8MmxfPy3MHGcRqwnNU73xDUmPBEcrCRbH0O1P1aa4846XerOhU +t7KR/aypH/KH5BfGSah82ApB9PI+53c0BFLd6IHyTS9URZ0V4U/M5d40VxDJI3IX +cI1QcB9WbMy5/zpaT2N6w25lBx2Eof+pDGOJbbJAiDnXH3dotfyc1dZnaVuodNv8 +ifYbMvekJKZ2t0dT741Jj6m2g1qllpBFYfXeA08mD6iL8AOWsKwV0HFaanuU5nCT +2vFp4LJiTZ6P/4mdm13NRemUAiKN4DV/6PEEeXFsVIP4M7kFMhtYVRFP0OUnR3Hs +7dpn1mKmS00PaaLJvOwiS5THaJQXfuKOKD62xur1NGyfN4gHONuGcfrNlUhDbqNP +gofXNJhuS5N5YHVpD/Aa1VP6IQzCP+k/HxiMkl14p3ZnGbuy6n/pcAlWVqOwDAst +Nl7F6cTVg8uGF5csbBNvh1qvSaYd2804BC5f4ko1Di1L+KIkBI3Y4WNeApI02phh +XBxvWHZks/wCuPWdCg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha +ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM +HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 +UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 +tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R +ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM +lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp +/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G +A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy +MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl +cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js +L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL +BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni +acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K +zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 +PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y +Johw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw +NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV +BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn +ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 +3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z +qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR +p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 +HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw +ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea +HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw +Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh +c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E +RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt +dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku +Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp +3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF +CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na +xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX +KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp +Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2 +MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ +bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS +7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp +0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS +B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49 +BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ +LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4 +DXZDjC5Ty3zfDBeWUA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBN +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMT +HERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN +NDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs +IEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS87IE+ +ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG0 +2C+JFvuUAT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgp +wgscONyfMXdcvyej/Cestyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZM +pG2T6T867jp8nVid9E6P/DsjyG244gXazOvswzH016cpVIDPRFtMbzCe88zdH5RD +nU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnVDdXifBBiqmvwPXbzP6Po +sMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9qTXeXAaDx +Zre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cd +Lvvyz6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvX +KyY//SovcfXWJL5/MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNe +XoVPzthwiHvOAbWWl9fNff2C+MIkwcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPL +tgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4EFgQUUTMc7TZArxfTJc1paPKv +TiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN +AQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw +GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7H +PNtQOa27PShNlnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLF +O4uJ+DQtpBflF+aZfTCIITfNMBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQ +REtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/u4cnYiWB39yhL/btp/96j1EuMPik +AdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9GOUrYU9DzLjtxpdRv +/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh47a+ +p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilw +MUc/dNAUFvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WF +qUITVuwhd4GTWgzqltlJyqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCK +ovfepEWFJqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+ +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE +BhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ +IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0 +MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNV +BAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w +HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJj +Dp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBj +TnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u +KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEj +qcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpm +MUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12 +ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloP +zgsMR6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3Gk +L30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeC +jGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLwwoC8N9VKejveSswoA +HQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeFd3mqgnkC +AwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg +p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZm +DRd9FBUb1Ov9H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5 +COmSdI31R9KrO9b7eGZONn356ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ry +L3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPf +JWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQHtZa37dG/OaG+svg +IHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBDF8Io +2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV +09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ +XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrq +T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe +MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo +27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w +Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw +TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl +qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH +szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8 +Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk +MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 +wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p +aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN +VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb +C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe +QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy +h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4 +7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J +ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef +MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/ +Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT +6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ +0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm +2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb +bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout736G +jOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL2 +4CejQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7 +VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/Jdm +ZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi +QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR +HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D +9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8 +p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYD +VQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2Jh +bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgw +MTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0g +UjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wWTAT +BgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkWymOx +uYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNV +HQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/ ++wpu+74zyTyjhNUwCgYIKoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147 +bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 +MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 +RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT +gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm +KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd +QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ +XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o +LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU +RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp +jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK +6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX +mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs +Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH +WD9f +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg +MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh +bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx +MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET +MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI +xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k +ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD +aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw +LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw +1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX +k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2 +SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h +bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n +WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY +rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce +MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu +bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN +nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt +Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61 +55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj +vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf +cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz +oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp +nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs +pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v +JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R +8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4 +5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYx +CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQD +ExNHbG9iYWxTaWduIFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAw +MDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2Ex +HDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkBjtjq +R+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGdd +yXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ +7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cVi4RguYv/Uo7njLwcAjA8 ++RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+CAezNIm8BZ/3Hobui3A= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA +MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD +VQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy +MDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYt +c2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08EsCVeJ +OaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQG +vGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud +316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo +0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSE +y132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWF +zXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE ++cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCN +I/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzs +x2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqa +ByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC +4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4 +7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg +JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti +2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIk +pnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRF +FRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLt +rWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qqJZ4d16GLuc1CLgSk +ZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwyeqiv5 +u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP +4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6 +N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3 +vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQsw +CQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2Vh +cmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9v +dCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoXDTQ1MDIxMzExMDEwOVowbDELMAkG +A1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj +aCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJvb3Qg +Q0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7 +KKrxcm1lAEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9Y +STHMmE5gEYd103KUkE+bECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQD +AgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAircJRQO9gcS3ujwLEXQNw +SaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/QwCZ61IygN +nxS2PFOiTAZpffpskcYqSUXm7LcT4Tps +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBs +MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0Eg +Um9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUzOFoXDTQ1MDIxMzEwNTUzN1owbDEL +MAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl +YXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNBIFJv +b3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569l +mwVnlskNJLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE +4VGC/6zStGndLuwRo0Xua2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uv +a9of08WRiFukiZLRgeaMOVig1mlDqa2YUlhu2wr7a89o+uOkXjpFc5gH6l8Cct4M +pbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K5FrZx40d/JiZ+yykgmvw +Kh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEvdmn8kN3b +LW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcY +AuUR0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqB +AGMUuTNe3QvboEUHGjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYq +E613TBoYm5EPWNgGVMWX+Ko/IIqmhaZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHr +W2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQCPxrvrNQKlr9qEgYRtaQQJKQ +CoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAU +X15QvWiWkKQUEapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3 +f5Z2EMVGpdAgS1D0NTsY9FVqQRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxaja +H6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxDQpSbIPDRzbLrLFPCU3hKTwSUQZqP +JzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcRj88YxeMn/ibvBZ3P +zzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5vZSt +jBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0 +/L5H9MG0qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pT +BGIBnfHAT+7hOtSLIBD6Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79 +aPib8qXPMThcFarmlwDB31qlpzmq6YR/PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YW +xw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnnkf3/W9b3raYvAwtt41dU +63ZTGI0RmLo= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN +BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl +bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv +b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ +BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj +YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5 +MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0 +dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg +QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa +jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi +C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep +lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof +TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix +DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k +IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT +N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v +dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG +A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh +ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx +QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA +4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0 +AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10 +4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C +ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV +9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD +gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6 +Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq +NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko +LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd +ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I +XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI +M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot +9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V +Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea +j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh +X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ +l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf +bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4 +pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK +e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0 +vm9qp/UsQu0yrbYhnr68 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBP +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xGzAZBgNVBAMMEkhpUEtJIFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRa +Fw0zNzEyMzExNTU5NTlaME8xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3 +YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQS0kgUm9vdCBDQSAtIEcx +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9B5/UnMyDHPkvRN0o9Qw +qNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh8Ge6zCFovkRTv4354twv +Vcg3Px+kwJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux55199QmQ5eiY29yTw1S+6 +lZgRZq2XNdZ1AYDgr/SEYYwNHl98h5ZeQa/rh+r4XfEuiAU+TCK72h8q3VJGZDnz +Qs7ZngyzsHeXZJzA9KMuH5UHsBffMNsAGJZMoYFL3QRtU6M9/Aes1MU3guvklQgZ +KILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfdhSi8MEyr48KxRURHH+CK +FgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXTT3OUM3ECoWqj1jOXTyFj +HluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK9p/7qxj3ccC2HTHsOyDr +y+K49a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8b3ti6RZsR1pl8w4Rm0bZ +/W3c1pzAtH2lsN0/Vm+h+fbkEkj9Bn8SV7apI09bA8PgcSojt/ewsTu8mL3WmKgM +a/aOEmem8rJY5AIJEzypuxC00jBF8ez3ABHfZfjcK0NVvxaXxA/VLGGEqnKG/uY6 +fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQU8ncX+l6o/vY9cdVouslGDDjYr7AwDgYDVR0PAQH/BAQDAgGGMA0GCSqG +SIb3DQEBCwUAA4ICAQBQUfB13HAE4/+qddRxosuej6ip0691x1TPOhwEmSKsxBHi +7zNKpiMdDg1H2DfHb680f0+BazVP6XKlMeJ45/dOlBhbQH3PayFUhuaVevvGyuqc +SE5XCV0vrPSltJczWNWseanMX/mF+lLFjfiRFOs6DRfQUsJ748JzjkZ4Bjgs6Fza +ZsT0pPBWGTMpWmWSBUdGSquEwx4noR8RkpkndZMPvDY7l1ePJlsMu5wP1G4wB9Tc +XzZoZjmDlicmisjEOf6aIW/Vcobpf2Lll07QJNBAsNB1CI69aO4I1258EHBGG3zg +iLKecoaZAeO/n0kZtCW+VmWuF2PlHt/o/0elv+EmBYTksMCv5wiZqAxeJoBF1Pho +L5aPruJKHJwWDBNvOIf2u8g0X5IDUXlwpt/L9ZlNec1OvFefQ05rLisY+GpzjLrF +Ne85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wr +kkVbbiVghUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+ +vhV4nYWBSipX3tUZQ9rbyltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQU +YDksswBVLuT1sw5XxJFBAJw/6KXf6vb/yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQEL +BQAwbzELMAkGA1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJ +SG9uZyBLb25nMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25n +a29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2MDMwMjI5NDZaFw00MjA2MDMwMjI5 +NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtvbmcxEjAQBgNVBAcT +CUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMXSG9u +Z2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCziNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFO +dem1p+/l6TWZ5Mwc50tfjTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mI +VoBc+L0sPOFMV4i707mV78vH9toxdCim5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV +9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOesL4jpNrcyCse2m5FHomY +2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj0mRiikKY +vLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+Tt +bNe/JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZb +x39ri1UbSsUgYT2uy1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+ +l2oBlKN8W4UdKjk60FSh0Tlxnf0h+bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YK +TE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsGxVd7GYYKecsAyVKvQv83j+Gj +Hno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwIDAQABo2MwYTAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e +i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEw +DQYJKoZIhvcNAQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG +7BJ8dNVI0lkUmcDrudHr9EgwW62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCk +MpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWldy8joRTnU+kLBEUx3XZL7av9YROXr +gZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov+BS5gLNdTaqX4fnk +GMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDceqFS +3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJm +Ozj/2ZQw9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+ +l6mc1X5VTMbeRRAc6uk7nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6c +JfTzPV4e0hz5sy229zdcxsshTrD3mUcYhcErulWuBurQB7Lcq9CClnXO0lD+mefP +L5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB60PZ2Pierc+xYw5F9KBa +LJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fqdBb9HxEG +mpv0 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw +CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg +R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 +MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT +ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW ++1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9 +ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI +zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW +tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 +/q4AaOeMSQ+2b1tbFfLn +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu +VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw +MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw +JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT +3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU ++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp +S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 +bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi +T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL +vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK +Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK +dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT +c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv +l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N +iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD +ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt +LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 +nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 ++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK +W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT +AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq +l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG +4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ +mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A +7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu +VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN +MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 +MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 +ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy +RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS +bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF +/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R +3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw +EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy +9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V +GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ +2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV +WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD +W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN +AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV +DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 +TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G +lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW +mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df +WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 ++bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ +tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA +GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv +8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 +MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 +ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD +VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq +scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO +xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H +LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX +uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD +yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ +JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q +rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN +BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L +hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB +QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ +HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu +Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg +QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB +BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA +A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb +laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 +awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo +JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw +LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT +VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk +LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb +UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ +QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ +naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls +QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G +CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y +OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx +FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp +Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP +kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc +cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U +fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 +N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC +xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 ++rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM +Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG +SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h +mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk +ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c +2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t +HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYD +VQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw +MTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4MjMxNjA0WjBlMQswCQYDVQQGEwJV +UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNy +b3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZR +ogPZnZH6thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYb +hGBKia/teQ87zvH2RPUBeMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3 +FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zV +L8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUB +iudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBl +MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw +NAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIwNzE4MjMwMDIzWjBlMQswCQYDVQQG +EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1N +aWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZ +Nt9GkMml7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0 +ZdDMbRnMlfl7rEqUrQ7eS0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1 +HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw71VdyvD/IybLeS2v4I2wDwAW9lcfNcztm +gGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+dkC0zVJhUXAoP8XFWvLJ +jEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49FyGcohJUc +aDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaG +YaRSMLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6 +W6IYZVcSn2i51BVrlMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4K +UGsTuqwPN1q3ErWQgR5WrlcihtnJ0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH ++FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJClTUFLkqqNfs+avNJVgyeY+Q +W5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZC +LgLNFgVZJ8og6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OC +gMNPOsduET/m4xaRhPtthH80dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6 +tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk+ONVFT24bcMKpBLBaYVu32TxU5nh +SnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex/2kskZGT4d9Mozd2 +TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDyAmH3 +pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGR +xpl/j8nWZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiApp +GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9 +dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hN +AHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZB +RA+GsCyRxj3qrg+E +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEM +BQAwaTELMAkGA1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRG +T1JNIENvcnAuMTIwMAYDVQQDDClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xNzA4MTgwODU4NDJaFw0zNzA4MTgyMzU5NTlaMGkx +CzAJBgNVBAYTAktSMSYwJAYDVQQKDB1OQVZFUiBCVVNJTkVTUyBQTEFURk9STSBD +b3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJvb3QgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC21PGTXLVA +iQqrDZBbUGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM62RHcQDaH +38dq6SZeWYp34+hInDEW+j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lE +HoSTGEq0n+USZGnQJoViAbbJAh2+g1G7XNr4rRVqmfeSVPc0W+m/6imBEtRTkZaz +kVrd/pBzKPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2aacp+yPOiNgSnABIqKYP +szuSjXEOdMWLyEz59JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4Yb8Obtoq +vC8MC3GmsxY/nOb5zJ9TNeIDoKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHf +nZ3zVHbOUzoBppJB7ASjjw2i1QnK1sua8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaG +YQ5fG8Ir4ozVu53BA0K6lNpfqbDKzE0K70dpAy8i+/Eozr9dUGWokG2zdLAIx6yo +0es+nPxdGoMuK8u180SdOqcXYZaicdNwlhVNt0xz7hlcxVs+Qf6sdWA7G2POAN3a +CJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejyYhbLgGvtPe31HzClrkvJE+2K +AQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNVHQ4EFgQU0p+I +36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB +Af8wDQYJKoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoN +qo0hV4/GPnrK21HUUrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatj +cu3cvuzHV+YwIHHW1xDBE1UBjCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm ++LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4G3bxhYTeodoS76TiEJd6eN4MUZeoIUCL +hr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0bBwcupTgE34h5prCy8VCZLQe +lHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRwTMKeuB330DTHD8z7 +p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgvOtB5CTh8 +piKCk5XQA76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLR +LBT/DShycpWbXgnbiUSYqqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX +5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oGI/hGoiLtk/bdmuYqh7GYVPEi92tF4+KO +dh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmgkpzNNIaRkPpkUZ3+/uul +9XXeifdy +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG +EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 +MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl +cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR +dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB +pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM +b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz +IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT +lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz +AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 +VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG +ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 +BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG +AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M +U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh +bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C ++C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F +uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 +XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICNTCCAbqgAwIBAgIQI/nD1jWvjyhLH/BU6n6XnTAKBggqhkjOPQQDAzBLMQsw +CQYDVQQGEwJDSDEZMBcGA1UECgwQT0lTVEUgRm91bmRhdGlvbjEhMB8GA1UEAwwY +T0lTVEUgU2VydmVyIFJvb3QgRUNDIEcxMB4XDTIzMDUzMTE0NDIyOFoXDTQ4MDUy +NDE0NDIyN1owSzELMAkGA1UEBhMCQ0gxGTAXBgNVBAoMEE9JU1RFIEZvdW5kYXRp +b24xITAfBgNVBAMMGE9JU1RFIFNlcnZlciBSb290IEVDQyBHMTB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABBcv+hK8rBjzCvRE1nZCnrPoH7d5qVi2+GXROiFPqOujvqQy +cvO2Ackr/XeFblPdreqqLiWStukhEaivtUwL85Zgmjvn6hp4LrQ95SjeHIC6XG4N +2xml4z+cKrhAS93mT6NjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBQ3 +TYhlz/w9itWj8UnATgwQb0K0nDAdBgNVHQ4EFgQUN02IZc/8PYrVo/FJwE4MEG9C +tJwwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2kAMGYCMQCpKjAd0MKfkFFR +QD6VVCHNFmb3U2wIFjnQEnx/Yxvf4zgAOdktUyBFCxxgZzFDJe0CMQCSia7pXGKD +YmH5LVerVrkR3SW+ak5KGoJr3M/TvEqzPNcum9v4KGm8ay3sMaE641c= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIQVaXZZ5Qoxu0M+ifdWwFNGDANBgkqhkiG9w0BAQwFADBL +MQswCQYDVQQGEwJDSDEZMBcGA1UECgwQT0lTVEUgRm91bmRhdGlvbjEhMB8GA1UE +AwwYT0lTVEUgU2VydmVyIFJvb3QgUlNBIEcxMB4XDTIzMDUzMTE0MzcxNloXDTQ4 +MDUyNDE0MzcxNVowSzELMAkGA1UEBhMCQ0gxGTAXBgNVBAoMEE9JU1RFIEZvdW5k +YXRpb24xITAfBgNVBAMMGE9JU1RFIFNlcnZlciBSb290IFJTQSBHMTCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAKqu9KuCz/vlNwvn1ZatkOhLKdxVYOPM +vLO8LZK55KN68YG0nnJyQ98/qwsmtO57Gmn7KNByXEptaZnwYx4M0rH/1ow00O7b +rEi56rAUjtgHqSSY3ekJvqgiG1k50SeH3BzN+Puz6+mTeO0Pzjd8JnduodgsIUzk +ik/HEzxux9UTl7Ko2yRpg1bTacuCErudG/L4NPKYKyqOBGf244ehHa1uzjZ0Dl4z +O8vbUZeUapU8zhhabkvG/AePLhq5SvdkNCncpo1Q4Y2LS+VIG24ugBA/5J8bZT8R +tOpXaZ+0AOuFJJkk9SGdl6r7NH8CaxWQrbueWhl/pIzY+m0o/DjH40ytas7ZTpOS +jswMZ78LS5bOZmdTaMsXEY5Z96ycG7mOaES3GK/m5Q9l3JUJsJMStR8+lKXHiHUh +sd4JJCpM4rzsTGdHwimIuQq6+cF0zowYJmXa92/GjHtoXAvuY8BeS/FOzJ8vD+Ho +mnqT8eDI278n5mUpezbgMxVz8p1rhAhoKzYHKyfMeNhqhw5HdPSqoBNdZH702xSu ++zrkL8Fl47l6QGzwBrd7KJvX4V84c5Ss2XCTLdyEr0YconosP4EmQufU2MVshGYR +i3drVByjtdgQ8K4p92cIiBdcuJd5z+orKu5YM+Vt6SmqZQENghPsJQtdLEByFSnT +kCz3GkPVavBpAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU +8snBDw1jALvsRQ5KH7WxszbNDo0wHQYDVR0OBBYEFPLJwQ8NYwC77EUOSh+1sbM2 +zQ6NMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQwFAAOCAgEANGd5sjrG5T33 +I3K5Ce+SrScfoE4KsvXaFwyihdJ+klH9FWXXXGtkFu6KRcoMQzZENdl//nk6HOjG +5D1rd9QhEOP28yBOqb6J8xycqd+8MDoX0TJD0KqKchxRKEzdNsjkLWd9kYccnbz8 +qyiWXmFcuCIzGEgWUOrKL+mlSdx/PKQZvDatkuK59EvV6wit53j+F8Bdh3foZ3dP +AGav9LEDOr4SfEE15fSmG0eLy3n31r8Xbk5l8PjaV8GUgeV6Vg27Rn9vkf195hfk +gSe7BYhW3SCl95gtkRlpMV+bMPKZrXJAlszYd2abtNUOshD+FKrDgHGdPY3ofRRs +YWSGRqbXVMW215AWRqWFyp464+YTFrYVI8ypKVL9AMb2kI5Wj4kI3Zaq5tNqqYY1 +9tVFeEJKRvwDyF7YZvZFZSS0vod7VSCd9521Kvy5YhnLbDuv0204bKt7ph6N/Ome +/msVuduCmsuY33OhkKCgxeDoAaijFJzIwZqsFVAzje18KotzlUBDJvyBpCpfOZC3 +J8tRd/iWkx7P8nd9H0aTolkelUTFLXVksNb54Dxp6gS1HAviRkRNQzuXSXERvSS2 +wq1yVAb+axj5d9spLFKebXd7Yv0PTY6YMjAwcRLWJTXjn/hvnLXrahut6hDTlhZy +BiElxky8j3C7DOReIoMt0r7+hVu05L0= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt +MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg +Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i +YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x +CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG +b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3 +HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx +WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX +1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk +u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P +99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r +M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB +BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh +cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5 +gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO +ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf +aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQsw +CQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91 +bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwg +Um9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRaFw00MjA1MDkwOTU4MzNaMG0xCzAJ +BgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBGb3Vu +ZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2JhbCBS +b290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4ni +eUqjFqdrVCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4W +p2OQ0jnUsYd4XxiWD1AbNTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7T +rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV +57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg +Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 +MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV +wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe +rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 +68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh +4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp +UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o +abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc +3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G +KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt +hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO +Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt +zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD +ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 +cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN +qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 +YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv +b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 +8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k +NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj +ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp +q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt +nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 +MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR +/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu +FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR +U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c +ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR +FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k +A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw +eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl +sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp +VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q +A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ +ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD +ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI +FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv +oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg +u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP +0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf +3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl +8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ +DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN +PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ +ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx +NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv +bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA +VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku +WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX +5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ +ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg +h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV +BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE +CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMB4XDTE3MDUzMTE4MTQzN1oXDTQy +MDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4G +A1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQD +DC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvq +M0fNTPl9fb69LT3w23jhhqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssuf +OePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7wcXHswxzpY6IXFJ3vG2fThVUCAtZJycxa +4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTOZw+oz12WGQvE43LrrdF9 +HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+B6KjBSYR +aZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcA +b9ZhCBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQ +Gp8hLH94t2S42Oim9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQV +PWKchjgGAGYS5Fl2WlPAApiiECtoRHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMO +pgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+SlmJuwgUHfbSguPvuUCYHBBXtSu +UDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48+qvWBkofZ6aY +MBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV +HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa4 +9QaAJadz20ZpqJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBW +s47LCp1Jjr+kxJG7ZhcFUZh1++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5 +Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nxY/hoLVUE0fKNsKTPvDxeH3jnpaAg +cLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2GguDKBAdRUNf/ktUM +79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDzOFSz +/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXt +ll9ldDz7CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEm +Kf7GUmG6sXP/wwyc5WxqlD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKK +QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ +w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi +S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07 +mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz +WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0 +b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS +b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI +7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg +CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud +EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD +VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T +kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+ +gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE +BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK +DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTcz +OTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv +bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2R +xFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX +qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcC +C52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ3 +6YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh +/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrF +YD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93E +JNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVc +US4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8 +ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2snYxnP/Okm ++Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi +M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4G +A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGV +cpNxJK1ok1iOMq8bs3AD/CUrdIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBc +Hadm47GUBwwyOabqG7B52B2ccETjit3E+ZUfijhDPwGFpUenPUayvOUiaPd7nNgs +PgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAslu1OJD7OAUN5F7kR/ +q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjqerQ0 +cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jr +a6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I +H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/Y +K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu +nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf +oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY +Ic2wBlX7Jz9TkHCpBB5XJ7k= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQsw +CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxT +U0wuY29tIFRMUyBFQ0MgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2 +MDgxOTE2MzM0N1owTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jwb3Jh +dGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgRUNDIFJvb3QgQ0EgMjAyMjB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABEUpNXP6wrgjzhR9qLFNoFs27iosU8NgCTWyJGYm +acCzldZdkkAZDsalE3D07xJRKF3nzL35PIXBz5SQySvOkkJYWWf9lCcQZIxPBLFN +SeR7T5v15wj4A4j3p8OSSxlUgaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSME +GDAWgBSJjy+j6CugFFR781a4Jl9nOAuc0DAdBgNVHQ4EFgQUiY8vo+groBRUe/NW +uCZfZzgLnNAwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2gAMGUCMFXjIlbp +15IkWE8elDIPDAI2wv2sdDJO4fscgIijzPvX6yv/N33w7deedWo1dlJF4AIxAMeN +b0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5Zn6g6g== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFiTCCA3GgAwIBAgIQb77arXO9CEDii02+1PdbkTANBgkqhkiG9w0BAQsFADBO +MQswCQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQD +DBxTU0wuY29tIFRMUyBSU0EgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzQyMloX +DTQ2MDgxOTE2MzQyMVowTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jw +b3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgUlNBIFJvb3QgQ0EgMjAyMjCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANCkCXJPQIgSYT41I57u9nTP +L3tYPc48DRAokC+X94xI2KDYJbFMsBFMF3NQ0CJKY7uB0ylu1bUJPiYYf7ISf5OY +t6/wNr/y7hienDtSxUcZXXTzZGbVXcdotL8bHAajvI9AI7YexoS9UcQbOcGV0ins +S657Lb85/bRi3pZ7QcacoOAGcvvwB5cJOYF0r/c0WRFXCsJbwST0MXMwgsadugL3 +PnxEX4MN8/HdIGkWCVDi1FW24IBydm5MR7d1VVm0U3TZlMZBrViKMWYPHqIbKUBO +L9975hYsLfy/7PO0+r4Y9ptJ1O4Fbtk085zx7AGL0SDGD6C1vBdOSHtRwvzpXGk3 +R2azaPgVKPC506QVzFpPulJwoxJF3ca6TvvC0PeoUidtbnm1jPx7jMEWTO6Af77w +dr5BUxIzrlo4QqvXDz5BjXYHMtWrifZOZ9mxQnUjbvPNQrL8VfVThxc7wDNY8VLS ++YCk8OjwO4s4zKTGkH8PnP2L0aPP2oOnaclQNtVcBdIKQXTbYxE3waWglksejBYS +d66UNHsef8JmAOSqg+qKkK3ONkRN0VHpvB/zagX9wHQfJRlAUW7qglFA35u5CCoG +AtUjHBPW6dvbxrB6y3snm/vg1UYk7RBLY0ulBY+6uB0rpvqR4pJSvezrZ5dtmi2f +gTIFZzL7SAg/2SW4BCUvAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j +BBgwFoAU+y437uOEeicuzRk1sTN8/9REQrkwHQYDVR0OBBYEFPsuN+7jhHonLs0Z +NbEzfP/UREK5MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAjYlt +hEUY8U+zoO9opMAdrDC8Z2awms22qyIZZtM7QbUQnRC6cm4pJCAcAZli05bg4vsM +QtfhWsSWTVTNj8pDU/0quOr4ZcoBwq1gaAafORpR2eCNJvkLTqVTJXojpBzOCBvf +R4iyrT7gJ4eLSYwfqUdYe5byiB0YrrPRpgqU+tvT5TgKa3kSM/tKWTcWQA673vWJ +DPFs0/dRa1419dvAJuoSc06pkZCmF8NsLzjUo3KUQyxi4U5cMj29TH0ZR6LDSeeW +P4+a0zvkEdiLA9z2tmBVGKaBUfPhqBVq6+AL8BQx1rmMRTqoENjwuSfr98t67wVy +lrXEj5ZzxOhWc5y8aVFjvO9nHEMaX3cZHxj4HCUp+UmZKbaSPaKDN7EgkaibMOlq +bLQjk2UEqxHzDh1TJElTHaE/nUiSEeJ9DU/1172iWD54nR4fK/4huxoTtrEoZP2w +AgDHbICivRZQIA9ygV/MlP+7mea6kMvq+cYMwq7FGc4zoWtcu358NFcXrfA/rs3q +r5nsLFR+jM4uElZI7xc7P0peYNLcdDa8pUNjyw9bowJWCZ4kLOGGgYz+qxcs+sji +Mho6/4UIyYOf8kpIEFR3N+2ivEC+5BB09+Rbu7nzifmPQdjH5FCQNYA+HLhNkNPU +98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL +BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6 +ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw +NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L +cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg +Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN +QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT +3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw +3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6 +3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5 +BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN +XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF +AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw +8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG +nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP +oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy +d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg +LvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQsw +CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T +ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcN +MjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEYMBYG +A1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBT +ZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAR2+pmpbiDt+dd34wc7qNs9Xzjoq1WmVk/WSOrsfy2qw7LFeeyZYX8QeccC +WvkEN/U0NSt3zn8gj1KjAIns1aeibVvjS5KToID1AZTc8GgHHs3u/iVStSBDHBv+ +6xnOQ6OjQjBAMB0GA1UdDgQWBBTRItpMWfFLXyY4qp3W7usNw/upYTAOBgNVHQ8B +Af8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNnADBkAjAn7qRa +qCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RHlAFWovgzJQxC36oCMB3q +4S6ILuH5px0CMk7yn2xVdOOurvulGu7t0vzCAxHrRVxgED1cf5kDW21USAGKcw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBf +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD +Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw +HhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEY +MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1Ymxp +YyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCTvtU2UnXYASOgHEdCSe5jtrch/cSV1UgrJnwUUxDa +ef0rty2k1Cz66jLdScK5vQ9IPXtamFSvnl0xdE8H/FAh3aTPaE8bEmNtJZlMKpnz +SDBh+oF8HqcIStw+KxwfGExxqjWMrfhu6DtK2eWUAtaJhBOqbchPM8xQljeSM9xf +iOefVNlI8JhD1mb9nxc4Q8UBUQvX4yMPFF1bFOdLvt30yNoDN9HWOaEhUTCDsG3X +ME6WW5HwcCSrv0WBZEMNvSE6Lzzpng3LILVCJ8zab5vuZDCQOc2TZYEhMbUjUDM3 +IuM47fgxMMxF/mL50V0yeUKH32rMVhlATc6qu/m1dkmU8Sf4kaWD5QazYw6A3OAS +VYCmO2a0OYctyPDQ0RTp5A1NDvZdV3LFOxxHVp3i1fuBYYzMTYCQNFu31xR13NgE +SJ/AwSiItOkcyqex8Va3e0lMWeUgFaiEAin6OJRpmkkGj80feRQXEgyDet4fsZfu ++Zd4KKTIRJLpfSYFplhym3kT2BFfrsU4YjRosoYwjviQYZ4ybPUHNs2iTG7sijbt +8uaZFURww3y8nDnAtOFr94MlI1fZEoDlSfB1D++N6xybVCi0ITz8fAr/73trdf+L +HaAZBav6+CuBQug4urv7qv094PPK306Xlynt8xhW6aWWrL3DkJiy4Pmi1KZHQ3xt +zwIDAQABo0IwQDAdBgNVHQ4EFgQUVnNYZJX5khqwEioEYnmhQBWIIUkwDgYDVR0P +AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAC9c +mTz8Bl6MlC5w6tIyMY208FHVvArzZJ8HXtXBc2hkeqK5Duj5XYUtqDdFqij0lgVQ +YKlJfp/imTYpE0RHap1VIDzYm/EDMrraQKFz6oOht0SmDpkBm+S8f74TlH7Kph52 +gDY9hAaLMyZlbcp+nv4fjFg4exqDsQ+8FxG75gbMY/qB8oFM2gsQa6H61SilzwZA +Fv97fRheORKkU55+MkIQpiGRqRxOF3yEvJ+M0ejf5lG5Nkc/kLnHvALcWxxPDkjB +JYOcCj+esQMzEhonrPcibCTRAUH4WAP+JWgiH5paPHxsnnVI84HxZmduTILA7rpX +DhjvLpr3Etiga+kFpaHpaPi8TD8SHkXoUsCjvxInebnMMTzD9joiFgOgyY9mpFui +TdaBJQbpdqQACj7LzTWb4OE4y2BThihCQRxEV+ioratF4yUQvNs+ZUH7G6aXD+u5 +dHn5HrwdVw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65 +LvKRRFHQV80MNNVIIb/bE/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp +0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmmJ1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAY +QqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIUZNtaDCBO6Ncpd8hQJ6JaJ90t8sswDQYJKoZIhvcNAQEM +BQAwUTELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28u +LCBMdGQuMR0wGwYDVQQDExRTZWN1cmVTaWduIFJvb3QgQ0ExNDAeFw0yMDA0MDgw +NzA2MTlaFw00NTA0MDgwNzA2MTlaMFExCzAJBgNVBAYTAkpQMSMwIQYDVQQKExpD +eWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2VjdXJlU2lnbiBS +b290IENBMTQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDF0nqh1oq/ +FjHQmNE6lPxauG4iwWL3pwon71D2LrGeaBLwbCRjOfHw3xDG3rdSINVSW0KZnvOg +vlIfX8xnbacuUKLBl422+JX1sLrcneC+y9/3OPJH9aaakpUqYllQC6KxNedlsmGy +6pJxaeQp8E+BgQQ8sqVb1MWoWWd7VRxJq3qdwudzTe/NCcLEVxLbAQ4jeQkHO6Lo +/IrPj8BGJJw4J+CDnRugv3gVEOuGTgpa/d/aLIJ+7sr2KeH6caH3iGicnPCNvg9J +kdjqOvn90Ghx2+m1K06Ckm9mH+Dw3EzsytHqunQG+bOEkJTRX45zGRBdAuVwpcAQ +0BB8b8VYSbSwbprafZX1zNoCr7gsfXmPvkPx+SgojQlD+Ajda8iLLCSxjVIHvXib +y8posqTdDEx5YMaZ0ZPxMBoH064iwurO8YQJzOAUbn8/ftKChazcqRZOhaBgy/ac +18izju3Gm5h1DVXoX+WViwKkrkMpKBGk5hIwAUt1ax5mnXkvpXYvHUC0bcl9eQjs +0Wq2XSqypWa9a4X0dFbD9ed1Uigspf9mR6XU/v6eVL9lfgHWMI+lNpyiUBzuOIAB +SMbHdPTGrMNASRZhdCyvjG817XsYAFs2PJxQDcqSMxDxJklt33UkN4Ii1+iW/RVL +ApY+B3KVfqs9TC7XyvDf4Fg/LS8EmjijAQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUBpOjCl4oaTeqYR3r6/wtbyPk +86AwDQYJKoZIhvcNAQEMBQADggIBAJaAcgkGfpzMkwQWu6A6jZJOtxEaCnFxEM0E +rX+lRVAQZk5KQaID2RFPeje5S+LGjzJmdSX7684/AykmjbgWHfYfM25I5uj4V7Ib +ed87hwriZLoAymzvftAj63iP/2SbNDefNWWipAA9EiOWWF3KY4fGoweITedpdopT +zfFP7ELyk+OZpDc8h7hi2/DsHzc/N19DzFGdtfCXwreFamgLRB7lUe6TzktuhsHS +DCRZNhqfLJGP4xjblJUK7ZGqDpncllPjYYPGFrojutzdfhrGe0K22VoF3Jpf1d+4 +2kd92jjbrDnVHmtsKheMYc2xbXIBw8MgAGJoFjHVdqqGuw6qnsb58Nn4DSEC5MUo +FlkRudlpcyqSeLiSV5sI8jrlL5WwWLdrIBRtFO8KvH7YVdiI2i/6GaX7i+B/OfVy +K4XELKzvGUWSTLNhB9xNH27SgRNcmvMSZ4PPmz+Ln52kuaiWA3rF7iDeM9ovnhp6 +dB7h7sxaOgTdsxoEqBRjrLdHEoOabPXm6RUVkRqEGQ6UROcSjiVbgGcZ3GOTEAtl +Lor6CZpO2oYofaphNdgOpygau1LgePhsumywbrmHXumZNTfxPWQrqaA0k89jL9WB +365jJ6UeTo3cKXhZ+PmhIIynJkBugnLNeLLIjzwec+fBH7/PzqUqm9tEZDKgu39c +JRNItX+S +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICIzCCAamgAwIBAgIUFhXHw9hJp75pDIqI7fBw+d23PocwCgYIKoZIzj0EAwMw +UTELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBM +dGQuMR0wGwYDVQQDExRTZWN1cmVTaWduIFJvb3QgQ0ExNTAeFw0yMDA0MDgwODMy +NTZaFw00NTA0MDgwODMyNTZaMFExCzAJBgNVBAYTAkpQMSMwIQYDVQQKExpDeWJl +cnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2VjdXJlU2lnbiBSb290 +IENBMTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQLUHSNZDKZmbPSYAi4Io5GdCx4 +wCtELW1fHcmuS1Iggz24FG1Th2CeX2yF2wYUleDHKP+dX+Sq8bOLbe1PL0vJSpSR +ZHX+AezB2Ot6lHhWGENfa4HL9rzatAy2KZMIaY+jQjBAMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTrQciu/NWeUUj1vYv0hyCTQSvT +9DAKBggqhkjOPQQDAwNoADBlAjEA2S6Jfl5OpBEHvVnCB96rMjhTKkZEBhd6zlHp +4P9mLQlO4E/0BdGF9jVg3PVys0Z9AjBEmEYagoUeYWmJSwdLZrWeqrqgHkHZAXQ6 +bkU6iYAZezKYVWOr62Nuk22rGwlgMU4= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYT +AkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYD +VQQDEyJTZWN1cml0eSBDb21tdW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYx +NjA1MTUyOFoXDTM4MDExODA1MTUyOFowYTELMAkGA1UEBhMCSlAxJTAjBgNVBAoT +HFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKzApBgNVBAMTIlNlY3VyaXR5 +IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPpR27KkBLIE+Cnnfdl +dB9sELLo5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8EkdtXP9JTxpK +ULGjQjBAMB0GA1UdDgQWBBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu +9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3LsnNdo4gIxwwCMQDAqy0O +be0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX +DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy +dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj +YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV +OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr +zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM +VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ +hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO +ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw +awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs +OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF +coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc +okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 +t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy +1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ +SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFkzCCA3ugAwIBAgIUQ/oMX04bgBhE79G0TzUfRPSA7cswDQYJKoZIhvcNAQEL +BQAwUTELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzErMCkGA1UE +AxMiU3dpc3NTaWduIFJTQSBUTFMgUm9vdCBDQSAyMDIyIC0gMTAeFw0yMjA2MDgx +MTA4MjJaFw00NzA2MDgxMTA4MjJaMFExCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxT +d2lzc1NpZ24gQUcxKzApBgNVBAMTIlN3aXNzU2lnbiBSU0EgVExTIFJvb3QgQ0Eg +MjAyMiAtIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDLKmjiC8NX +vDVjvHClO/OMPE5Xlm7DTjak9gLKHqquuN6orx122ro10JFwB9+zBvKK8i5VUXu7 +LCTLf5ImgKO0lPaCoaTo+nUdWfMHamFk4saMla+ju45vVs9xzF6BYQ1t8qsCLqSX +5XH8irCRIFucdFJtrhUnWXjyCcplDn/L9Ovn3KlMd/YrFgSVrpxxpT8q2kFC5zyE +EPThPYxr4iuRR1VPuFa+Rd4iUU1OKNlfGUEGjw5NBuBwQCMBauTLE5tzrE0USJIt +/m2n+IdreXXhvhCxqohAWVTXz8TQm0SzOGlkjIHRI36qOTw7D59Ke4LKa2/KIj4x +0LDQKhySio/YGZxH5D4MucLNvkEM+KRHBdvBFzA4OmnczcNpI/2aDwLOEGrOyvi5 +KaM2iYauC8BPY7kGWUleDsFpswrzd34unYyzJ5jSmY0lpx+Gs6ZUcDj8fV3oT4MM +0ZPlEuRU2j7yrTrePjxF8CgPBrnh25d7mUWe3f6VWQQvdT/TromZhqwUtKiE+shd +OxtYk8EXlFXIC+OCeYSf8wCENO7cMdWP8vpPlkwGqnj73mSiI80fPsWMvDdUDrta +clXvyFu1cvh43zcgTFeRc5JzrBh3Q4IgaezprClG5QtO+DdziZaKHG29777YtvTK +wP1H8K4LWCDFyB02rpeNUIMmJCn3nTsPBQIDAQABo2MwYTAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBRvjmKLk0Ow4UD2p8P98Q+4 +DxU4pTAdBgNVHQ4EFgQUb45ii5NDsOFA9qfD/fEPuA8VOKUwDQYJKoZIhvcNAQEL +BQADggIBAKwsKUF9+lz1GpUYvyypiqkkVHX1uECry6gkUSsYP2OprphWKwVDIqO3 +10aewCoSPY6WlkDfDDOLazeROpW7OSltwAJsipQLBwJNGD77+3v1dj2b9l4wBlgz +Hqp41eZUBDqyggmNzhYzWUUo8aWjlw5DI/0LIICQ/+Mmz7hkkeUFjxOgdg3XNwwQ +iJb0Pr6VvfHDffCjw3lHC1ySFWPtUnWK50Zpy1FVCypM9fJkT6lc/2cyjlUtMoIc +gC9qkfjLvH4YoiaoLqNTKIftV+Vlek4ASltOU8liNr3CjlvrzG4ngRhZi0Rjn9UM +ZfQpZX+RLOV/fuiJz48gy20HQhFRJjKKLjpHE7iNvUcNCfAWpO2Whi4Z2L6MOuhF +LhG6rlrnub+xzI/goP+4s9GFe3lmozm1O2bYQL7Pt2eLSMkZJVX8vY3PXtpOpvJp +zv1/THfQwUY1mFwjmwJFQ5Ra3bxHrSL+ul4vkSkphnsh3m5kt8sNjzdbowhq6/Td +Ao9QAwKxuDdollDruF/UKIqlIgyKhPBZLtU30WHlQnNYKoH3dtvi4k0NX/a3vgW0 +rk4N3hY9A4GzJl5LuEsAz/+MF7psYC0nhzck5npgL7XTgwSqT0N1osGDsieYK7EO +gLrAhV5Cud+xYJHT6xh+cHiudoO+cVrQkOPKwRYlZ0rwtnu64ZzZ +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd +AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC +FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi +1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq +jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ +wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ +WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy +NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC +uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw +IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 +g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP +BSeOE6Fuwg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN +8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ +RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 +hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 +ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM +EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 +A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy +WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ +1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 +6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT +91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p +TpPDpFQUWw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx +GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp +bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w +KwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0 +BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRpZmlrYXNpIC0gU3Vy +dW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYDVQQG +EwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXll +IEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklU +QUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT +TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kg +LSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7 +a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySr +LqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INr +N3wcwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2X +YacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/ +iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4f +AJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1+DrtUgxH +V8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPf +IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4 +lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c +8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf +lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIQQAE0jMIAAAAAAAAAATzyxjANBgkqhkiG9w0BAQwFADBQ +MQswCQYDVQQGEwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290 +IENBMRswGQYDVQQDExJUV0NBIENZQkVSIFJvb3QgQ0EwHhcNMjIxMTIyMDY1NDI5 +WhcNNDcxMTIyMTU1OTU5WjBQMQswCQYDVQQGEwJUVzESMBAGA1UEChMJVEFJV0FO +LUNBMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJUV0NBIENZQkVSIFJvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDG+Moe2Qkgfh1sTs6P +40czRJzHyWmqOlt47nDSkvgEs1JSHWdyKKHfi12VCv7qze33Kc7wb3+szT3vsxxF +avcokPFhV8UMxKNQXd7UtcsZyoC5dc4pztKFIuwCY8xEMCDa6pFbVuYdHNWdZsc/ +34bKS1PE2Y2yHer43CdTo0fhYcx9tbD47nORxc5zb87uEB8aBs/pJ2DFTxnk684i +JkXXYJndzk834H/nY62wuFm40AZoNWDTNq5xQwTxaWV4fPMf88oon1oglWa0zbfu +j3ikRRjpJi+NmykosaS3Om251Bw4ckVYsV7r8Cibt4LK/c/WMw+f+5eesRycnupf +Xtuq3VTpMCEobY5583WSjCb+3MX2w7DfRFlDo7YDKPYIMKoNM+HvnKkHIuNZW0CP +2oi3aQiotyMuRAlZN1vH4xfyIutuOVLF3lSnmMlLIJXcRolftBL5hSmO68gnFSDA +S9TMfAxsNAwmmyYxpjyn9tnQS6Jk/zuZQXLB4HCX8SS7K8R0IrGsayIyJNN4KsDA +oS/xUgXJP+92ZuJF2A09rZXIx4kmyA+upwMu+8Ff+iDhcK2wZSA3M2Cw1a/XDBzC +kHDXShi8fgGwsOsVHkQGzaRP6AzRwyAQ4VRlnrZR0Bp2a0JaWHY06rc3Ga4udfmW +5cFZ95RXKSWNOkyrTZpB0F8mAwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSdhWEUfMFib5do5E83QOGt4A1WNzAd +BgNVHQ4EFgQUnYVhFHzBYm+XaORPN0DhreANVjcwDQYJKoZIhvcNAQEMBQADggIB +AGSPesRiDrWIzLjHhg6hShbNcAu3p4ULs3a2D6f/CIsLJc+o1IN1KriWiLb73y0t +tGlTITVX1olNc79pj3CjYcya2x6a4CD4bLubIp1dhDGaLIrdaqHXKGnK/nZVekZn +68xDiBaiA9a5F/gZbG0jAn/xX9AKKSM70aoK7akXJlQKTcKlTfjF/biBzysseKNn +TKkHmvPfXvt89YnNdJdhEGoHK4Fa0o635yDRIG4kqIQnoVesqlVYL9zZyvpoBJ7t +RCT5dEA7IzOrg1oYJkK2bVS1FmAwbLGg+LhBoF1JSdJlBTrq/p1hvIbZv97Tujqx +f36SNI7JAG7cmL3c7IAFrQI932XtCwP39xaEBDG6k5TY8hL4iuO/Qq+n1M0RFxbI +Qh0UqEL20kCGoE8jypZFVmAGzbdVAaYBlGX+bgUJurSkquLvWL69J1bY73NxW0Qz +8ppy6rBePm6pUlvscG21h483XjyMnM7k8M4MZ0HMzvaAq07MTFb1wWFZk7Q+ptq4 +NxKfKjLji7gh7MMrZQzvIt6IKTtM1/r+t+FHvpw+PoP7UV31aPcuIYXcv/Fa4nzX +xeSDwWrruoBa3lwtcHb4yOWHh8qgnaHlIhInD0Q9HWzq1MKLL295q39QpsQZp6F6 +t5b5wR9iWqJDB0BeJsas7a5wFsWqynKKTbDPAYsDP27X +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx +EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT +VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 +NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT +B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF +10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz +0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh +MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH +zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc +46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 +yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi +laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP +oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA +BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE +qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm +4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL +1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF +H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo +RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ +nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh +15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW +6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW +nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j +wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz +aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy +KwbQBM0= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES +MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU +V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz +WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO +LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE +AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH +K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX +RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z +rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx +3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq +hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC +MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls +XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D +lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn +aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ +YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICQjCCAcmgAwIBAgIQNjqWjMlcsljN0AFdxeVXADAKBggqhkjOPQQDAzBjMQsw +CQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBH +bWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBFQ0MgUm9vdCAyMDIw +MB4XDTIwMDgyNTA3NDgyMFoXDTQ1MDgyNTIzNTk1OVowYzELMAkGA1UEBhMCREUx +JzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkgR21iSDErMCkGA1UE +AwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgRUNDIFJvb3QgMjAyMDB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABM6//leov9Wq9xCazbzREaK9Z0LMkOsVGJDZos0MKiXrPk/O +tdKPD/M12kOLAoC+b1EkHQ9rK8qfwm9QMuU3ILYg/4gND21Ju9sGpIeQkpT0CdDP +f8iAC8GXs7s1J8nCG6NCMEAwHQYDVR0OBBYEFONyzG6VmUex5rNhTNHLq+O6zd6f +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cA +MGQCMHVSi7ekEE+uShCLsoRbQuHmKjYC2qBuGT8lv9pZMo7k+5Dck2TOrbRBR2Di +z6fLHgIwN0GMZt9Ba9aDAEH9L1r3ULRn0SyocddDypwnJJGDSA3PzfdUga/sf+Rn +27iQ7t0l +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFszCCA5ugAwIBAgIQIZxULej27HF3+k7ow3BXlzANBgkqhkiG9w0BAQwFADBj +MQswCQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0 +eSBHbWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBSU0EgUm9vdCAy +MDIzMB4XDTIzMDMyODEyMTY0NVoXDTQ4MDMyNzIzNTk1OVowYzELMAkGA1UEBhMC +REUxJzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkgR21iSDErMCkG +A1UEAwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgUlNBIFJvb3QgMjAyMzCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAO01oYGA88tKaVvC+1GDrib94W7zgRJ9 +cUD/h3VCKSHtgVIs3xLBGYSJwb3FKNXVS2xE1kzbB5ZKVXrKNoIENqil/Cf2SfHV +cp6R+SPWcHu79ZvB7JPPGeplfohwoHP89v+1VmLhc2o0mD6CuKyVU/QBoCcHcqMA +U6DksquDOFczJZSfvkgdmOGjup5czQRxUX11eKvzWarE4GC+j4NSuHUaQTXtvPM6 +Y+mpFEXX5lLRbtLevOP1Czvm4MS9Q2QTps70mDdsipWol8hHD/BeEIvnHRz+sTug +BTNoBUGCwQMrAcjnj02r6LX2zWtEtefdi+zqJbQAIldNsLGyMcEWzv/9FIS3R/qy +8XDe24tsNlikfLMR0cN3f1+2JeANxdKz+bi4d9s3cXFH42AYTyS2dTd4uaNir73J +co4vzLuu2+QVUhkHM/tqty1LkCiCc/4YizWN26cEar7qwU02OxY2kTLvtkCJkUPg +8qKrBC7m8kwOFjQgrIfBLX7JZkcXFBGk8/ehJImr2BrIoVyxo/eMbcgByU/J7MT8 +rFEz0ciD0cmfHdRHNCk+y7AO+oMLKFjlKdw/fKifybYKu6boRhYPluV75Gp6SG12 +mAWl3G0eQh5C2hrgUve1g8Aae3g1LDj1H/1Joy7SWWO/gLCMk3PLNaaZlSJhZQNg ++y+TS/qanIA7AgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtqeX +gj10hZv3PJ+TmpV5dVKMbUcwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS2 +p5eCPXSFm/c8n5OalXl1UoxtRzANBgkqhkiG9w0BAQwFAAOCAgEAqMxhpr51nhVQ +pGv7qHBFfLp+sVr8WyP6Cnf4mHGCDG3gXkaqk/QeoMPhk9tLrbKmXauw1GLLXrtm +9S3ul0A8Yute1hTWjOKWi0FpkzXmuZlrYrShF2Y0pmtjxrlO8iLpWA1WQdH6DErw +M807u20hOq6OcrXDSvvpfeWxm4bu4uB9tPcy/SKE8YXJN3nptT+/XOR0so8RYgDd +GGah2XsjX/GO1WfoVNpbOms2b/mBsTNHM3dA+VKq3dSDz4V4mZqTuXNnQkYRIer+ +CqkbGmVps4+uFrb2S1ayLfmlyOw7YqPta9BO1UAJpB+Y1zqlklkg5LB9zVtzaL1t +xKITDmcZuI1CfmwMmm6gJC3VRRvcxAIU/oVbZZfKTpBQCHpCNfnqwmbU+AGuHrS+ +w6jv/naaoqYfRvaE7fzbzsQCzndILIyy7MMAo+wsVRjBfhnu4S/yrYObnqsZ38aK +L4x35bcF7DvB7L6Gs4a8wPfc5+pbrrLMtTWGS9DiP7bY+A4A7l3j941Y/8+LN+lj +X273CXE2whJdV/LItM3z7gLfEdxquVeEHVlNjM7IDiPCtyaaEBRx/pOyiriA8A4Q +ntOoUAw3gi/q4Iqd4Sw5/7W0cwDk90imc6y/st53BIe0o82bNSQ3+pCTE4FCxpgm +dTdmQRCsu/WU48IxK63nI1bMNSWSs1A= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQx +CzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UE +AwwQVGVsaWEgUm9vdCBDQSB2MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1 +NTRaMEQxCzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZ +MBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2MjCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBALLQPwe84nvQa5n44ndp586dpAO8gm2h/oFlH0wnrI4AuhZ76zBq +AMCzdGh+sq/H1WKzej9Qyow2RCRj0jbpDIX2Q3bVTKFgcmfiKDOlyzG4OiIjNLh9 +vVYiQJ3q9HsDrWj8soFPmNB06o3lfc1jw6P23pLCWBnglrvFxKk9pXSW/q/5iaq9 +lRdU2HhE8Qx3FZLgmEKnpNaqIJLNwaCzlrI6hEKNfdWV5Nbb6WLEWLN5xYzTNTOD +n3WhUidhOPFZPY5Q4L15POdslv5e2QJltI5c0BE0312/UqeBAMN/mUWZFdUXyApT +7GPzmX3MaRKGwhfwAZ6/hLzRUssbkmbOpFPlob/E2wnW5olWK8jjfN7j/4nlNW4o +6GwLI1GpJQXrSPjdscr6bAhR77cYbETKJuFzxokGgeWKrLDiKca5JLNrRBH0pUPC +TEPlcDaMtjNXepUugqD0XBCzYYP2AgWGLnwtbNwDRm41k9V6lS/eINhbfpSQBGq6 +WT0EBXWdN6IOLj3rwaRSg/7Qa9RmjtzG6RJOHSpXqhC8fF6CfaamyfItufUXJ63R +DolUK5X6wK0dmBR4M0KGCqlztft0DbcbMBnEWg4cJ7faGND/isgFuvGqHKI3t+ZI +pEYslOqodmJHixBTB0hXbOKSTbauBcvcwUpej6w9GU7C7WB1K9vBykLVAgMBAAGj +YzBhMB8GA1UdIwQYMBaAFHKs5DN5qkWH9v2sHZ7Wxy+G2CQ5MB0GA1UdDgQWBBRy +rOQzeapFh/b9rB2e1scvhtgkOTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAoDtZpwmUPjaE0n4vOaWWl/oRrfxn83EJ +8rKJhGdEr7nv7ZbsnGTbMjBvZ5qsfl+yqwE2foH65IRe0qw24GtixX1LDoJt0nZi +0f6X+J8wfBj5tFJ3gh1229MdqfDBmgC9bXXYfef6xzijnHDoRnkDry5023X4blMM +A8iZGok1GTzTyVR8qPAs5m4HeW9q4ebqkYJpCh3DflminmtGFZhb069GHWLIzoBS +SRE/yQQSwxN8PzuKlts8oB4KtItUsiRnDe+Cy748fdHif64W1lZYudogsYMVoe+K +TTJvQS8TUoKU1xrBeKJR3Stwbbca+few4GeXVtt8YVMJAygCQMez2P2ccGrGKMOF +6eLtGpOg3kuYooQ+BXcBlj37tCAPnHICehIv1aO6UXivKitEZU61/Qrowc15h2Er +3oBXRb9n8ZuRXqWk7FlIEA04x7D6w0RtBPV4UBySllva9bguulvP5fBqnUsvWHMt +Ty3EHD70sz+rFQ47GUGKpMFXEmZxTPpT41frYpUJnlTd0cI8Vzy9OK2YZLe4A5pT +VmBds9hCG1xLEooc6+t9xnppxyd/pPiL8uSUZodL6ZQHCRJ5irLrdATczvREWeAW +ysUsWNc8e89ihmpQfTU2Zqf7N+cox9jQraVplI/owd8k+BsHMYeB2F326CjYSlKA +rBPuUBQemMc= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFpTCCA42gAwIBAgIUZPYOZXdhaqs7tOqFhLuxibhxkw8wDQYJKoZIhvcNAQEM +BQAwWjELMAkGA1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dp +ZXMsIEluYy4xJDAiBgNVBAMMG1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHMzAe +Fw0yMTA1MjAwMjEwMTlaFw00NjA1MTkwMjEwMTlaMFoxCzAJBgNVBAYTAkNOMSUw +IwYDVQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQDDBtU +cnVzdEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDAMYJhkuSUGwoqZdC+BqmHO1ES6nBBruL7dOoKjbmzTNyPtxNS +T1QY4SxzlZHFZjtqz6xjbYdT8PfxObegQ2OwxANdV6nnRM7EoYNl9lA+sX4WuDqK +AtCWHwDNBSHvBm3dIZwZQ0WhxeiAysKtQGIXBsaqvPPW5vxQfmZCHzyLpnl5hkA1 +nyDvP+uLRx+PjsXUjrYsyUQE49RDdT/VP68czH5GX6zfZBCK70bwkPAPLfSIC7Ep +qq+FqklYqL9joDiR5rPmd2jE+SoZhLsO4fWvieylL1AgdB4SQXMeJNnKziyhWTXA +yB1GJ2Faj/lN03J5Zh6fFZAhLf3ti1ZwA0pJPn9pMRJpxx5cynoTi+jm9WAPzJMs +hH/x/Gr8m0ed262IPfN2dTPXS6TIi/n1Q1hPy8gDVI+lhXgEGvNz8teHHUGf59gX +zhqcD0r83ERoVGjiQTz+LISGNzzNPy+i2+f3VANfWdP3kXjHi3dqFuVJhZBFcnAv +kV34PmVACxmZySYgWmjBNb9Pp1Hx2BErW+Canig7CjoKH8GB5S7wprlppYiU5msT +f9FkPz2ccEblooV7WIQn3MSAPmeamseaMQ4w7OYXQJXZRe0Blqq/DPNL0WP3E1jA +uPP6Z92bfW1K/zJMtSU7/xxnD4UiWQWRkUF3gdCFTIcQcf+eQxuulXUtgQIDAQAB +o2MwYTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEDk5PIj7zjKsK5Xf/Ih +MBY027ySMB0GA1UdDgQWBBRA5OTyI+84yrCuV3/yITAWNNu8kjAOBgNVHQ8BAf8E +BAMCAQYwDQYJKoZIhvcNAQEMBQADggIBACY7UeFNOPMyGLS0XuFlXsSUT9SnYaP4 +wM8zAQLpw6o1D/GUE3d3NZ4tVlFEbuHGLige/9rsR82XRBf34EzC4Xx8MnpmyFq2 +XFNFV1pF1AWZLy4jVe5jaN/TG3inEpQGAHUNcoTpLrxaatXeL1nHo+zSh2bbt1S1 +JKv0Q3jbSwTEb93mPmY+KfJLaHEih6D4sTNjduMNhXJEIlU/HHzp/LgV6FL6qj6j +ITk1dImmasI5+njPtqzn59ZW/yOSLlALqbUHM/Q4X6RJpstlcHboCoWASzY9M/eV +VHUl2qzEc4Jl6VL1XP04lQJqaTDFHApXB64ipCz5xUG3uOyfT0gA+QEEVcys+TIx +xHWVBqB/0Y0n3bOppHKH/lmLmnp0Ft0WpWIp6zqW3IunaFnT63eROfjXy9mPX1on +AX1daBli2MjN9LdyR75bl87yraKZk62Uy5P2EgmVtqvXO9A/EcswFi55gORngS1d +7XB4tmBZrOFdRWOPyN9yaFvqHbgB8X7754qz41SgOAngPN5C8sLtLpvzHzW2Ntjj +gKGLzZlkD8Kqq7HK9W+eQ42EVJmzbsASZthwEPEGNTNDqJwuuhQxzhB/HIbjj9LV ++Hfsm6vxL2PZQl/gZ4FkkfGXL/xuJvYz+NO1+MRiqzFRJQJ6+N1rZdVtTTDIZbpo +FGWsJwt0ivKH +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICVTCCAdygAwIBAgIUTyNkuI6XY57GU4HBdk7LKnQV1tcwCgYIKoZIzj0EAwMw +WjELMAkGA1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dpZXMs +IEluYy4xJDAiBgNVBAMMG1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHNDAeFw0y +MTA1MjAwMjEwMjJaFw00NjA1MTkwMjEwMjJaMFoxCzAJBgNVBAYTAkNOMSUwIwYD +VQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQDDBtUcnVz +dEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATx +s8045CVD5d4ZCbuBeaIVXxVjAd7Cq92zphtnS4CDr5nLrBfbK5bKfFJV4hrhPVbw +LxYI+hW8m7tH5j/uqOFMjPXTNvk4XatwmkcN4oFBButJ+bAp3TPsUKV/eSm4IJij +YzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUpbtKl86zK3+kMd6Xg1mD +pm9xy94wHQYDVR0OBBYEFKW7SpfOsyt/pDHel4NZg6ZvccveMA4GA1UdDwEB/wQE +AwIBBjAKBggqhkjOPQQDAwNnADBkAjBe8usGzEkxn0AAbbd+NvBNEU/zy4k6LHiR +UKNbwMp1JvK/kF0LgoxgKJ/GcJpo5PECMFxYDlZ2z1jD1xCMuo6u47xkdUfFVZDj +/bpV6wfEU6s3qe4hsiFbYI89MvHVI5TWWA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICMTCCAbegAwIBAgIUNnThTXxlE8msg1UloD5Sfi9QaMcwCgYIKoZIzj0EAwMw +WDELMAkGA1UEBhMCQ04xJTAjBgNVBAoTHFRydXN0QXNpYSBUZWNobm9sb2dpZXMs +IEluYy4xIjAgBgNVBAMTGVRydXN0QXNpYSBUTFMgRUNDIFJvb3QgQ0EwHhcNMjQw +NTE1MDU0MTU2WhcNNDQwNTE1MDU0MTU1WjBYMQswCQYDVQQGEwJDTjElMCMGA1UE +ChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywgSW5jLjEiMCAGA1UEAxMZVHJ1c3RB +c2lhIFRMUyBFQ0MgUm9vdCBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLh/pVs/ +AT598IhtrimY4ZtcU5nb9wj/1WrgjstEpvDBjL1P1M7UiFPoXlfXTr4sP/MSpwDp +guMqWzJ8S5sUKZ74LYO1644xST0mYekdcouJtgq7nDM1D9rs3qlKH8kzsaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQULIVTu7FDzTLqnqOH/qKYqKaT6RAw +DgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gAMGUCMFRH18MtYYZI9HlaVQ01 +L18N9mdsd0AaRuf4aFtOJx24mH1/k78ITcTaRTChD15KeAIxAKORh/IRM4PDwYqR +OkwrULG9IpRdNYlzg8WbGf60oenUoWa2AaU2+dhoYSi3dOGiMQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFgDCCA2igAwIBAgIUHBjYz+VTPyI1RlNUJDxsR9FcSpwwDQYJKoZIhvcNAQEM +BQAwWDELMAkGA1UEBhMCQ04xJTAjBgNVBAoTHFRydXN0QXNpYSBUZWNobm9sb2dp +ZXMsIEluYy4xIjAgBgNVBAMTGVRydXN0QXNpYSBUTFMgUlNBIFJvb3QgQ0EwHhcN +MjQwNTE1MDU0MTU3WhcNNDQwNTE1MDU0MTU2WjBYMQswCQYDVQQGEwJDTjElMCMG +A1UEChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywgSW5jLjEiMCAGA1UEAxMZVHJ1 +c3RBc2lhIFRMUyBSU0EgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC +AgoCggIBAMMWuBtqpERz5dZO9LnPWwvB0ZqB9WOwj0PBuwhaGnrhB3YmH49pVr7+ +NmDQDIPNlOrnxS1cLwUWAp4KqC/lYCZUlviYQB2srp10Zy9U+5RjmOMmSoPGlbYJ +Q1DNDX3eRA5gEk9bNb2/mThtfWza4mhzH/kxpRkQcwUqwzIZheo0qt1CHjCNP561 +HmHVb70AcnKtEj+qpklz8oYVlQwQX1Fkzv93uMltrOXVmPGZLmzjyUT5tUMnCE32 +ft5EebuyjBza00tsLtbDeLdM1aTk2tyKjg7/D8OmYCYozza/+lcK7Fs/6TAWe8Tb +xNRkoDD75f0dcZLdKY9BWN4ArTr9PXwaqLEX8E40eFgl1oUh63kd0Nyrz2I8sMeX +i9bQn9P+PN7F4/w6g3CEIR0JwqH8uyghZVNgepBtljhb//HXeltt08lwSUq6HTrQ +UNoyIBnkiz/r1RYmNzz7dZ6wB3C4FGB33PYPXFIKvF1tjVEK2sUYyJtt3LCDs3+j +TnhMmCWr8n4uIF6CFabW2I+s5c0yhsj55NqJ4js+k8UTav/H9xj8Z7XvGCxUq0DT +bE3txci3OE9kxJRMT6DNrqXGJyV1J23G2pyOsAWZ1SgRxSHUuPzHlqtKZFlhaxP8 +S8ySpg+kUb8OWJDZgoM5pl+z+m6Ss80zDoWo8SnTq1mt1tve1CuBAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLgHkXlcBvRG/XtZylomkadFK/hT +MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQwFAAOCAgEAIZtqBSBdGBanEqT3 +Rz/NyjuujsCCztxIJXgXbODgcMTWltnZ9r96nBO7U5WS/8+S4PPFJzVXqDuiGev4 +iqME3mmL5Dw8veWv0BIb5Ylrc5tvJQJLkIKvQMKtuppgJFqBTQUYo+IzeXoLH5Pt +7DlK9RME7I10nYEKqG/odv6LTytpEoYKNDbdgptvT+Bz3Ul/KD7JO6NXBNiT2Twp +2xIQaOHEibgGIOcberyxk2GaGUARtWqFVwHxtlotJnMnlvm5P1vQiJ3koP26TpUJ +g3933FEFlJ0gcXax7PqJtZwuhfG5WyRasQmr2soaB82G39tp27RIGAAtvKLEiUUj +pQ7hRGU+isFqMB3iYPg6qocJQrmBktwliJiJ8Xw18WLK7nn4GS/+X/jbh87qqA8M +pugLoDzga5SYnH+tBuYc6kIQX+ImFTw3OffXvO645e8D7r0i+yiGNFjEWn9hongP +XvPKnbwbPKfILfanIhHKA9jnZwqKDss1jjQ52MjqjZ9k4DewbNfFj8GQYSbbJIwe +SsCI3zWQzj8C9GRh3sfIB5XeMhg6j6JCQCTl1jNdfK7vsU1P1FeQNWrcrgSXSYk0 +ly4wBOeY99sLAZDBHwo/+ML+TvrbmnNzFrwFuHnYWa8G5z9nODmxfKuU4CkUpijy +323imttUQ/hHWKNddBWcwauwxzQ= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQEL +BQAwYTELMAkGA1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUg +Q2VydGlmaWNhdGlvbiBFbGVjdHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJv +b3QgQ0EwHhcNMTkwNDI2MDg1NzU2WhcNNDQwNDI2MDg1NzU2WjBhMQswCQYDVQQG +EwJUTjE3MDUGA1UECgwuQWdlbmNlIE5hdGlvbmFsZSBkZSBDZXJ0aWZpY2F0aW9u +IEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1c3QgUm9vdCBDQTCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA61YguBUtB9YOCfvdZ +n56eY+hz2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb60ksPqxd +2JQDoOw05TDENX37Jk0bbjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgF +VwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZ +GoXRlJfXyqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAdgjH8KcwAWJeRTIAAHDOF +li/LQcKLEITDCSSJH7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViWVSHbhlnU +r8a83YFuB9tgYv7sEG7aaAH0gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2 +eY8fTpkdso8MDhz/yV3A/ZAQprE38806JG60hZC/gLkMjNWb1sjxVj8agIl6qeIb +MlEsPvLfe/ZdeikZjuXIvTZxi11Mwh0/rViizz1wTaZQmCXcI/m4WEEIcb9PuISg +jwBUFfyRbVinljvrS5YnzWuioYasDXxU5mZMZl+QviGaAkYt5IPCgLnPSz7ofzwB +7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwSVXAkPcvCFDVDXSdOvsC9qnyW +5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI04Y+oXNZtPdE +ITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m0 +90QhMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+z +xiD2BkewhpMl0425yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYu +QEkHDVneixCwSQXi/5E/S7fdAo74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4 +FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUcoqgRYYdZ2vyJ/0Adqp2RT8JeNnYA/u8EH +22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8pjkcFwRJpadbGNjHh/PqAulxP +xOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgGiPEZtdmYu65xxBzn +dFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MBkRArHtG5 +Xc0yGYuPjCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7b +nV2UqL1g52KAdoGDDIzMMEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQ +CvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9zZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZH +u/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3rAZ3r2OvEhJn7wAzMMujj +d9qDRIueVSjAi1jTkD5OGwDxFa2DK5o= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBH +MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBF +eHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMx +MDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNV +BAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrsiWog +D4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvS +sPGP2KxFRv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aop +O2z6+I9tTcg1367r3CTueUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dk +sHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR59mzLC52LqGj3n5qiAno8geK+LLNEOfi +c0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH0mK1lTnj8/FtDw5lhIpj +VMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KRel7sFsLz +KuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/ +TuDvB0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41G +sx2VYVdWf6/wFlthWG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs +1+lvK9JKBZP8nm9rZ/+I8U6laUpSNwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQD +fwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS3H5aBZ8eNJr34RQwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBADaN +l8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR +ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQ +VBcZEhrxH9cMaVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5 +c6sq1WnIeJEmMX3ixzDx/BR4dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp +4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb+7lsq+KePRXBOy5nAliRn+/4Qh8s +t2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOWF3sGPjLtx7dCvHaj +2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwiGpWO +vpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2C +xR9GUeOcGMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmx +cmtpzyKEC2IPrNkZAJSidjzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbM +fjKaiJUINlK73nZfdklJrX+9ZSCyycErdhh2n1ax +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9 +MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBH +bG9iYWwgRzIgUm9vdDAeFw0xNjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0x +CzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEbMBkGA1UEAwwSVUNBIEds +b2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxeYr +b3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmToni9 +kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzm +VHqUwCoV8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/R +VogvGjqNO7uCEeBHANBSh6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDc +C/Vkw85DvG1xudLeJ1uK6NjGruFZfc8oLTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIj +tm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/R+zvWr9LesGtOxdQXGLY +D0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBeKW4bHAyv +j5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6Dl +NaBa4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6 +iIis7nCs+dwp4wwcOxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznP +O6Q0ibd5Ei9Hxeepl2n8pndntd978XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIHEjMz15DD/pQwIX4wV +ZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo5sOASD0Ee/oj +L3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 +1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl +1qnN3e92mI0ADs0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oU +b3n09tDh05S60FdRvScFDcH9yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LV +PtateJLbXDzz2K36uGt/xDYotgIVilQsnLAXc47QN6MUPJiVAAwpBVueSUmxX8fj +y88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHojhJi6IjMtX9Gl8Cb +EGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZkbxqg +DMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI ++Vg7RE+xygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGy +YiGqhkCyLmTTX8jjfhFnRR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bX +UB+K+wb1whnw0A== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV +BAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04g +Uk9PVCBDQSBHMjAeFw0xNzAyMDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJ +BgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJ +R04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDF +dRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05N0Iw +vlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZ +uIt4ImfkabBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhp +n+Sc8CnTXPnGFiWeI8MgwT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKs +cpc/I1mbySKEwQdPzH/iV8oScLumZfNpdWO9lfsbl83kqK/20U6o2YpxJM02PbyW +xPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91QqhngLjYl/rNUssuHLoPj1P +rCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732jcZZroiF +DsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fx +DTvf95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgy +LcsUDFDYg2WD7rlcz8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6C +eWRgKRM+o/1Pcmqr4tTluCRVLERLiohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSCIS1mxteg4BXrzkwJ +d8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOBywaK8SJJ6ejq +kX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC +b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQl +qiCA2ClV9+BB/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0 +OJD7uNGzcgbJceaBxXntC6Z58hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+c +NywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5BiKDUyUM/FHE5r7iOZULJK2v0ZXk +ltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklWatKcsWMy5WHgUyIO +pwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tUSxfj +03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZk +PuXaTH4MNMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE +1LlSVHJ7liXMvGnjSG4N0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MX +QRBdJ3NghVdJIgc= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV +BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk +LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv +b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ +BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg +THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v +IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv +xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H +Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB +eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo +jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ ++efcMQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICzzCCAjGgAwIBAgINAOhvGHvWOWuYSkmYCjAKBggqhkjOPQQDBDB1MQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xFzAVBgNVBGEMDlZBVEhVLTIzNTg0NDk3MSIwIAYDVQQDDBllLVN6aWdubyBU +TFMgUm9vdCBDQSAyMDIzMB4XDTIzMDcxNzE0MDAwMFoXDTM4MDcxNzE0MDAwMFow +dTELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRYwFAYDVQQKDA1NaWNy +b3NlYyBMdGQuMRcwFQYDVQRhDA5WQVRIVS0yMzU4NDQ5NzEiMCAGA1UEAwwZZS1T +emlnbm8gVExTIFJvb3QgQ0EgMjAyMzCBmzAQBgcqhkjOPQIBBgUrgQQAIwOBhgAE +AGgP36J8PKp0iGEKjcJMpQEiFNT3YHdCnAo4YKGMZz6zY+n6kbCLS+Y53wLCMAFS +AL/fjO1ZrTJlqwlZULUZwmgcAOAFX9pQJhzDrAQixTpN7+lXWDajwRlTEArRzT/v +SzUaQ49CE0y5LBqcvjC2xN7cS53kpDzLLtmt3999Cd8ukv+ho2MwYTAPBgNVHRMB +Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUWYQCYlpGePVd3I8K +ECgj3NXW+0UwHwYDVR0jBBgwFoAUWYQCYlpGePVd3I8KECgj3NXW+0UwCgYIKoZI +zj0EAwQDgYsAMIGHAkIBLdqu9S54tma4n7Zwf2Z0z+yOfP7AAXmazlIC58PRDHpt +y7Ve7hekm9sEdu4pKeiv+62sUvTXK9Z3hBC9xdIoaDQCQTV2WnXzkoYI9bIeCvZl +C9p2x1L/Cx6AcCIwwzPbGO2E14vs7dOoY4G1VnxHx1YwlGhza9IuqbnZLBwpvQy6 +uWWL +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw +IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL +SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH +SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh +ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X +DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1 +TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ +fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA +sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU +WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS +nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH +dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip +NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC +AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF +MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB +uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl +PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP +JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/ +gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2 +j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6 +5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB +o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS +/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z +Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE +W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D +hNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYD +VQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBU +ZWNobm9sb2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBH +MTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgxODMwMDBaMGcxCzAJBgNVBAYTAklO +MRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVkaHJhIFRlY2hub2xv +Z2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQz +f2N4aLTNLnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO +8oG0x5ZOrRkVUkr+PHB1cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aq +d7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHWDV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhM +tTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ6DqS0hdW5TUaQBw+jSzt +Od9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrHhQIDAQAB +o0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQD +AgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31x +PaOfG1vR2vjTnGs2vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjM +wiI/aTvFthUvozXGaCocV685743QNcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6d +GNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8EI6/6nAYH +6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby +RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx +iN66zB+Afko= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMw +RzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAY +BgNVBAMTEXZUcnVzIEVDQyBSb290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDcz +MTA3MjY0NFowRzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28u +LEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBSb290IENBMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9JpRCux3NCNtzslt188+cToL0 +v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A2MMrMudwpremIFUd +e4BdS49nTPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHbH88wDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIw +V53dVvHH4+m4SVBrm2nDb+zDfSXkV5UTQJtS0zvzQBm8JsctBp61ezaf9SXUY2sA +AjEA6dPGnlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQLYgmRWAD5Tfs0aNoJrSEG +GJTO +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQEL +BQAwQzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4x +FjAUBgNVBAMTDXZUcnVzIFJvb3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMx +MDcyNDA1WjBDMQswCQYDVQQGEwJDTjEcMBoGA1UEChMTaVRydXNDaGluYSBDby4s +THRkLjEWMBQGA1UEAxMNdlRydXMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB6ksDXhA/kFocizuwZotsSKYc +IrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoRuBLpDLvPbmyAhykU +AyyNJJrIZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6WTEN+VRS+ +GrPSbcKvdmaVayqwlHeFXgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z9 +8Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KAYPxMvDVTAWqXcoKv8R1w6Jz1717CbMdH +flqUhSZNO7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70kLJrxLT5ZOrpGgrIDajt +J8nU57O5q4IikCc9Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2AXPKBlim +0zvc+gRGE1WKyURHuFE5Gi7oNOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZN +pGvu/9ROutW04o5IWgAZCfEF2c6Rsffr6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQ +UqqzApVg+QxMaPnu1RcN+HFXtSXkKe5lXa/R7jwXC1pDxaWG6iSe4gUH3DRCEpHW +OXSuTEGC2/KmSNGzm/MzqvOmwMVO9fSddmPmAsYiS8GVP1BkLFTltvA8Kc9XAgMB +AAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYgscasGrz2iTAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAKbqSSaet +8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWd +nxEt/Hlk3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1j +bhd47F18iMjrjld22VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvM +Kar5CKXiNxTKsbhm7xqC5PD48acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIiv +TDUHKgLKeBRtRytAVunLKmChZwOgzoy8sHJnxDHO2zTlJQNgJXtxmOTAGytfdELS +S8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVTsMo554WgicEFOwE30z9J4nfr +I8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7IdMakLXogqzu4sEb9 +b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9mK5S4fNB +UvupLnKWnyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1P +Ti07NEPhmg4NpGaXutIcSkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929ven +sBxXVsFy6K2ir40zSbofitzmdHxghm+Hl3s= +-----END CERTIFICATE----- + diff --git a/artifact-security/scanner.Dockerfile b/artifact-security/scanner.Dockerfile index a35b70fd..0a72e926 100644 --- a/artifact-security/scanner.Dockerfile +++ b/artifact-security/scanner.Dockerfile @@ -1,7 +1,7 @@ FROM docker.io/aquasec/trivy@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969 AS source FROM scratch COPY --from=source /usr/local/bin/trivy /usr/local/bin/trivy -COPY --from=source /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt +COPY scanner-ca-certificates.crt /etc/ssl/certs/ca-certificates.crt ENV GODEBUG=http2client=0 USER 65532:65532 ENTRYPOINT ["/usr/local/bin/trivy"] diff --git a/docs/artifact-security.md b/docs/artifact-security.md index e943bfa3..ee1b4931 100644 --- a/docs/artifact-security.md +++ b/docs/artifact-security.md @@ -8,8 +8,9 @@ OpenVEX, SBOM, report, or hardening scripts. The policy release pins an official Trivy image by digest and verifies that digest, the expected Trivy version, the minimal scanner Dockerfile hash, and the -scanner runtime's OpenVEX hash before use. It copies only the Trivy binary and -CA bundle into a non-root scratch image. +scanner runtime's OpenVEX hash before use. It copies only the Trivy binary from +that image into a non-root scratch image, with a release-owned CA bundle that +omits roots distrusted after the source image was built. The scanner image disables Go's HTTP/2 client. Its database download uses HTTPS over HTTP/1.1; target scanning remains offline. The release-owned diff --git a/docs/design/trivy-scanner-assessments.md b/docs/design/trivy-scanner-assessments.md index 953efa6f..673e0d86 100644 --- a/docs/design/trivy-scanner-assessments.md +++ b/docs/design/trivy-scanner-assessments.md @@ -10,7 +10,8 @@ Go 1.26.6. Its source revision is `e1fd17a0ea4a8cf24bc4b4dd7e2cfbf4bb31b994`. The image index is pinned to `docker.io/aquasec/trivy@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969`. The registry manifest and image labels bind that version and source revision; -Code Polishy copies only its executable and CA bundle into the scanner image. +Code Polishy copies only its executable into the scanner image, alongside the +release-owned trust bundle described below. [Trivy 0.74.0](https://github.com/aquasecurity/trivy/releases/tag/v0.74.0) meets the 30-day admission minimum. Its updated Go runtime and dependencies remove @@ -67,6 +68,73 @@ and dispositions are in `artifact-security/scanner.openvex.json`. | [CVE-2026-78669](https://pkg.go.dev/vuln/GO-2026-6611) | `stdlib@v1.26.6` | Repeated HTTP/2 window changes exhaust CPU in a client or server. The image disables the standard HTTP/2 client before Trivy starts, and no server mode is enabled. This assessment depends on that mitigation. | | [CVE-2026-97031](https://pkg.go.dev/vuln/GO-2026-6607) | `stdlib@v1.26.6` | `decodeInnerClientHello` processes malicious ECH extensions on a TLS server. The scanner's TLS use is client-side database download; it accepts no TLS clients. | +## Scanner trust bundle and CVE-2026-111943 + +Reviewed by Claude Opus 5.5 on October 22, 2026 (UTC). Reassess when Trivy +0.76.1, built with Go 1.27.3, reaches the admission minimum at +2026-11-20T11:18:34Z, or whenever eMudhra, Mozilla, or Go change the distrust +set. + +[GO-2026-6809](https://pkg.go.dev/vuln/GO-2026-6809) affects +`crypto/x509.Certificate.Verify` before Go 1.26.10 and 1.27.3. The +[upstream fix](https://github.com/golang/go/commit/aa64342a9e13affffea3e989d67e574475af5a78) +rejects any chain containing one of eight public keys from the compromised +eMudhra issuing CAs `EM DV/OV TLS CA - G3B`, `EM DV/EV TLS CA - G3C`, and +`EM OV CS CA - G3B/G3C`. Trivy 0.74.0 is built with Go 1.26.6, and its database +download is the scanner's only TLS client. A peer holding a compromised key and +an interception position on the database hosts could therefore serve a forged +database that suppresses blocking findings. + +The source image's bundle (119 roots, SHA-256 +`b8d837841b88bfaa1a0fa827cbca8e2576418dd47c9fc4bb7f1f9d89c83111b9`) trusts +`emSign ECC Root CA - G3`. That root +[cross-signed `emSign Root TLS CA - G3`](https://crt.sh/?id=14658191778) +(certificate SHA-256 +`3094025792039a68af6e553686410bdab7c0d4a36b9084cba64202e35d6ebbdd`), which +issued the compromised TLS CAs. Go 1.27.0 without the fix verifies all four +compromised TLS CAs to that root. The compromised CAs' other issuer, +`AAA Certificate Services`, is absent from the bundle. + +`artifact-security/scanner-ca-certificates.crt` is that source bundle without +the three eMudhra roots that the +[Mozilla bundle dated 2026-10-20](https://curl.se/ca/cacert.pem) removed: + +| Root | SPKI SHA-256 | +| ------------------------- | ------------------------------------------------------------------ | +| `emSign ECC Root CA - G3` | `8d417db2dd8bf5e3084d1e3f196d583849d81bdd4c00c70b9d39369e96b8c782` | +| `emSign Root CA - C1` | `b7408b4d2be0238ba37004dd34e276c6019bd2f24c9db7d4980f5f6c359a4bcc` | +| `emSign ECC Root CA - C3` | `eabc185c4e82d942b1a5978ba3c0181487d6b3b9974e5c49f72f6d0bd9637150` | + +The remaining 116 certificates keep their original bytes and order; the +filtered bundle differs from the source only by deleted lines. Its SHA-256 is +`faf05e99823ab205a6dd9416042ae3750f5a9e36de03d01d2cb4811097616bcc`. The bundle +adds no root and changes no other trust decision. It is frozen with the source +image, as the copied bundle already was. Replace it with the new image's bundle +when the scanner pin changes, then reapply only distrust that bundle lacks. + +The database hosts do not depend on the removed roots. `ghcr.io` chains through +Sectigo Public Server Authentication Root R46 to USERTrust RSA, and +`pkg-containers.githubusercontent.com` chains through Let's Encrypt to ISRG +Root X1. On October 22 the rebuilt scanner downloaded +`ghcr.io/aquasecurity/trivy-db:2` through this bundle. Tests in +`internal/artifactsecurity/trust_test.go` check that the bundle omits the three +roots and leaves the cross-certificate without a trusted issuer, while a fixture +copy of the removed root still verifies it. They also verify captured chains +for both database hosts. + +The finding remains affected and has no OpenVEX statement. Removing these roots +closes every path to the compromised keys found in Certificate Transparency, +but Certificate Transparency need not contain every CA certificate. An +undisclosed certificate from another bundled root to an eMudhra CA would reopen +the path. Go's public-key denylist does not depend on disclosure. No root-pool +restriction proves the same property: limiting the pool to the current database +roots would still trust Sectigo, which cross-signed these CAs from +`AAA Certificate Services`, and would break downloads after an ordinary host +certificate change. Rebuilding Trivy 0.74.0 with Go 1.26.10 would replace the +official binary with a locally built scanner and add a networked source build. +The aged Trivy release remains the resolution; this bundle reduces exposure +until then. + OpenVEX reconciliation still requires every statement to match an observed advisory and exact package PURL. Accepted findings remain in the scanner report. New or unmatched findings, changed findings, and unused statements remain diff --git a/docs/source-provenance.md b/docs/source-provenance.md index 97fb5320..255aae27 100644 --- a/docs/source-provenance.md +++ b/docs/source-provenance.md @@ -58,7 +58,9 @@ version before staging a release. - Trivy is copied from an exact official image digest into the minimal scanner image. `artifact-security/scanner-policy.json` records that external source and its runtime configuration; the release manifest covers the repository-owned - Dockerfile and `artifact-security/scanner.openvex.json`, which records reviewed + Dockerfile, the scanner's trust bundle + `artifact-security/scanner-ca-certificates.crt`, and + `artifact-security/scanner.openvex.json`, which records reviewed vulnerability applicability. - Portable Linux releases use one digest-pinned Ubuntu base only for the OCI transport. The image installs Git and CA certificates, copies an already diff --git a/internal/artifactsecurity/testdata/emsign-ecc-root-ca-g3.pem b/internal/artifactsecurity/testdata/emsign-ecc-root-ca-g3.pem new file mode 100644 index 00000000..fd18c69d --- /dev/null +++ b/internal/artifactsecurity/testdata/emsign-ecc-root-ca-g3.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG +EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo +bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g +RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ +TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s +b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0 +WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS +fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB +zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq +hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB +CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD ++JbNR6iC8hZVdyR+EhCVBCyj +-----END CERTIFICATE----- + diff --git a/internal/artifactsecurity/testdata/emsign-root-tls-ca-g3-cross.pem b/internal/artifactsecurity/testdata/emsign-root-tls-ca-g3-cross.pem new file mode 100644 index 00000000..e12600d6 --- /dev/null +++ b/internal/artifactsecurity/testdata/emsign-root-tls-ca-g3-cross.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDdDCCAvmgAwIBAgIQAJYaPvsTWVdu0EAFtGhKWzAKBggqhkjOPQQDAzBrMQsw +CQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhy +YSBUZWNobm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290 +IENBIC0gRzMwHhcNMjQwNzExMTU0MzM2WhcNMzkwNzA5MTgzMDAwWjBWMQswCQYD +VQQGEwJJTjElMCMGA1UECgwcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEg +MB4GA1UEAwwXZW1TaWduIFJvb3QgVExTIENBIC0gRzMwdjAQBgcqhkjOPQIBBgUr +gQQAIgNiAARfD9w/q1cWxL6D9bMGH6CQ4u+gAV6GN9s+PwQx9Bl4hC+0iU8QOk8m +WO/RwXQy10DCSxT8vSIJClHKp+Ao4qJKdYeV0SwZ7LMQziyLfxIUOY6Grja/sQFw +LKYf8EdDXAyjggF1MIIBcTAfBgNVHSMEGDAWgBR8XQKEE9TMipuBzhccLikenEhj +QjAdBgNVHQ4EFgQU4TqrIXzx401UrtRyt/2LWUIgkFQwHQYDVR0lBBYwFAYIKwYB +BQUHAwEGCCsGAQUFBwMCMD0GA1UdIAQ2MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIB +FhxodHRwOi8vcmVwb3NpdG9yeS5lbXNpZ24uY29tMHgGCCsGAQUFBwEBBGwwajAi +BggrBgEFBQcwAYYWaHR0cDovL29jc3AuZW1zaWduLmNvbTBEBggrBgEFBQcwAoY4 +aHR0cDovL3JlcG9zaXRvcnkuZW1zaWduLmNvbS9jZXJ0cy9lbVNpZ25FQ0NSb290 +Q0FHMy5jcnQwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5lbXNpZ24uY29t +P1Jvb3RDQUczLmNybDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIB +ATAKBggqhkjOPQQDAwNpADBmAjEAt9CIpRGp46QHQYDUBnLKw+vvK0/h9vmilEAg +TroUl/anH4x2rXHqiCtBTBfgKDegAjEA8WpG1LTZdAIL0FSYqxXeREaeTSxEofM0 +1jJH3jGvCXC79LU9UPJ2o3TU+UIj2gMV +-----END CERTIFICATE----- diff --git a/internal/artifactsecurity/testdata/ghcr-io-chain.pem b/internal/artifactsecurity/testdata/ghcr-io-chain.pem new file mode 100644 index 00000000..b3735f1a --- /dev/null +++ b/internal/artifactsecurity/testdata/ghcr-io-chain.pem @@ -0,0 +1,114 @@ +-----BEGIN CERTIFICATE----- +MIIG+TCCBWGgAwIBAgIQHzFhlotyxL7wcAFGgXSc0zANBgkqhkiG9w0BAQsFADBg +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQD +Ey5TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gQ0EgRFYgUjM2 +MB4XDTI2MTAwODAwMDAwMFoXDTI3MDEwNTIzNTk1OVowFDESMBAGA1UEAwwJKi5n +aGNyLmlvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv8Uw1tX2wiav +iYkkfDbv1SoVZm2bnePRrU36SwqbAwEqBLm51UCAbp026jnigItGrM33XSSKb3pG +HR7ukdNc13HTD6QMzTtgo2kEODUrm2mrj/g1VionZe/RffAQBd3ATVxN/3noCXym +jjI/p1qGiFqCvSjxtF9mx2wRYa37sXV6LN+sPmExJdcW0heOUFFN7qVCGyNBaiRa +rwhCPopvZECDd3m3mSlBIMbdbAS+isz+lQdhJObg50OxfLq2T+u+0HZdKohAJglz +tjuzlJE84X2xv0YppORacFLYmcwT5oa/DDkwPTJJSCvABODq0CVFlA1eDbDl+UcN +G/BbzcJVFtQmeowgSMbTdmNQw8mDdIOjrEXbRZOWSb2G0uOgPZ6eSGHBettI7lHh +UcsnFxtlRyXwDkEuf45R/3/7O3jmpJ0e9Av5ljYAN46hMelNlTe2yhlVBmhGy9G7 +O1PCoFbd/lKARpEnOw2QU3WSnhLpqvuglMrDotKwphxUVcCxWTXHNdpHDa7qe7ys +ZreUJ6AkeAogfV71VYE3CedCxkFN1VBio7TdZ6etc1tqng89/FjcfbiaT+ILIUXX +Dm3zHFUGGAtpQWTBYrczZIBQM+miDI9X4ALXG5dxRFmVKHc4tIzdKIgcISSUrZsn ++foyzQMbt3eZNg+dU42+P7l+D3w70JcCAwEAAaOCAnkwggJ1MB8GA1UdIwQYMBaA +FGjAEhYYDq/O9oemMlejRlFdywcnMB0GA1UdDgQWBBSXRKWhXbZTJDkoz5EoBBSy +ATFGzDAOBgNVHQ8BAf8EBAMCBaAwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggr +BgEFBQcDATBJBgNVHSAEQjBAMDQGCysGAQQBsjEBAgIHMCUwIwYIKwYBBQUHAgEW +F2h0dHBzOi8vc2VjdGlnby5jb20vQ1BTMAgGBmeBDAECATCBhAYIKwYBBQUHAQEE +eDB2ME8GCCsGAQUFBzAChkNodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29Q +dWJsaWNTZXJ2ZXJBdXRoZW50aWNhdGlvbkNBRFZSMzYuY3J0MCMGCCsGAQUFBzAB +hhdodHRwOi8vb2NzcC5zZWN0aWdvLmNvbTAdBgNVHREEFjAUggkqLmdoY3IuaW+C +B2doY3IuaW8wggENBgorBgEEAdZ5AgQCBIH+BIH7APkAdgAAGl0aHC2TdbZIVXj4 +L3Ghrm7vOX0pfIrjFXvK3uGgHgAAAaEZAVumAAAEAwBHMEUCIQCfUjJPp5aTQJvc +Ukd05athNk13vlRyu2afm82YNYqlCwIgfX8c3pm15BNHgWQmHw6FShnTCjzENyfb +pDwAY32cB6IAfwBZbmwzhpSyWXKiVsig6N2QSnboCD3ahzsBCDgoFDzuWQAAAaEZ +AVvKAAgAAAUACTljIQQDAEgwRgIhALinOqKOXJJEWeZXcKVmCcYgkC+4vYvWaxRz +goh7S/awAiEA5Oplg/uslmBRpu+AajZuveQqHgHBku6W09kgb2QHTBswDQYJKoZI +hvcNAQELBQADggGBAGtkKRzg2OkEBsO1dnuptQaPogbAUrwgtdH1LSyQed1AMd7t +kTk4eVk3/zvS3N8oyvwrCdNO9z8hjxLG9s093TcUyzbqaWmnh3w/3wSLlwWAocsD +YKyhWTga+RCAWs8WdpPcrWiLoG+N7t6bpDgt19MQSS/l8HDBKD4e5+zS1IarKBNx +A1nViJbMVhwEu9b9mJpY2nNyLcUj/HO4sSKriai6jFEmdG+U2LiFIhgRYQo2QQfh +a1iYIG1CIpA8POfqn9gnntj/smQ8COTdEdJ7axbI8lO4iJiwp5kp5ubdx/L/IxMc +/GEbXp/E+WhR5ygBwWGoelJonH5U6zA4W6vqUp3m+TlthCsxPvGSgVeht1+E4wp4 +cVdFKw/6V5ksrO9k6KR+8VoK0dpjcqSWUBNOMlkdwRg1CtGwm20xa+2JJlVCy/lN +tlFcMnGpjtsN4A79Y1DoqFQV7fidAYgUMVuaWdA37QVDfQH7UAFoJx51o7Euadf3 +Gidq8iXNeyVbK8Rz9g== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGTDCCBDSgAwIBAgIQOXpmzCdWNi4NqofKbqvjsTANBgkqhkiG9w0BAQwFADBf +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD +Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw +HhcNMjEwMzIyMDAwMDAwWhcNMzYwMzIxMjM1OTU5WjBgMQswCQYDVQQGEwJHQjEY +MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQDEy5TZWN0aWdvIFB1Ymxp +YyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gQ0EgRFYgUjM2MIIBojANBgkqhkiG9w0B +AQEFAAOCAY8AMIIBigKCAYEAljZf2HIz7+SPUPQCQObZYcrxLTHYdf1ZtMRe7Yeq +RPSwygz16qJ9cAWtWNTcuICc++p8Dct7zNGxCpqmEtqifO7NvuB5dEVexXn9RFFH +12Hm+NtPRQgXIFjx6MSJcNWuVO3XGE57L1mHlcQYj+g4hny90aFh2SCZCDEVkAja +EMMfYPKuCjHuuF+bzHFb/9gV8P9+ekcHENF2nR1efGWSKwnfG5RawlkaQDpRtZTm +M64TIsv/r7cyFO4nSjs1jLdXYdz5q3a4L0NoabZfbdxVb+CUEHfB0bpulZQtH1Rv +38e/lIdP7OTTIlZh6OYL6NhxP8So0/sht/4J9mqIGxRFc0/pC8suja+wcIUna0HB +pXKfXTKpzgis+zmXDL06ASJf5E4A2/m+Hp6b84sfPAwQ766rI65mh50S0Di9E3Pn +2WcaJc+PILsBmYpgtmgWTR9eV9otfKRUBfzHUHcVgarub/XluEpRlTtZudU5xbFN +xx/DgMrXLUAPaI60fZ6wA+PTAgMBAAGjggGBMIIBfTAfBgNVHSMEGDAWgBRWc1hk +lfmSGrASKgRieaFAFYghSTAdBgNVHQ4EFgQUaMASFhgOr872h6YyV6NGUV3LBycw +DgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYI +KwYBBQUHAwEGCCsGAQUFBwMCMBsGA1UdIAQUMBIwBgYEVR0gADAIBgZngQwBAgEw +VAYDVR0fBE0wSzBJoEegRYZDaHR0cDovL2NybC5zZWN0aWdvLmNvbS9TZWN0aWdv +UHVibGljU2VydmVyQXV0aGVudGljYXRpb25Sb290UjQ2LmNybDCBhAYIKwYBBQUH +AQEEeDB2ME8GCCsGAQUFBzAChkNodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3Rp +Z29QdWJsaWNTZXJ2ZXJBdXRoZW50aWNhdGlvblJvb3RSNDYucDdjMCMGCCsGAQUF +BzABhhdodHRwOi8vb2NzcC5zZWN0aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEA +YtOC9Fy+TqECFw40IospI92kLGgoSZGPOSQXMBqmsGWZUQ7rux7cj1du6d9rD6C8 +ze1B2eQjkrGkIL/OF1s7vSmgYVafsRoZd/IHUrkoQvX8FZwUsmPu7amgBfaY3g+d +q1x0jNGKb6I6Bzdl6LgMD9qxp+3i7GQOnd9J8LFSietY6Z4jUBzVoOoz8iAU84OF +h2HhAuiPw1ai0VnY38RTI+8kepGWVfGxfBWzwH9uIjeooIeaosVFvE8cmYUB4TSH +5dUyD0jHct2+8ceKEtIoFU/FfHq/mDaVnvcDCZXtIgitdMFQdMZaVehmObyhRdDD +4NQCs0gaI9AAgFj4L9QtkARzhQLNyRf87Kln+YU0lgCGr9HLg3rGO8q+Y4ppLsOd +unQZ6ZxPNGIfOApbPVf5hCe58EZwiWdHIMn9lPP6+F404y8NNugbQixBber+x536 +WrZhFZLjEkhp7fFXf9r32rNPfb74X/U90Bdy4lzp3+X1ukh1BuMxA/EEhDoTOS3l +7ABvc7BYSQubQ2490OcdkIzUh3ZwDrakMVrbaTxUM2p24N6dB+ns2zptWCva6jzW +r8IWKIMxzxLPv5Kt3ePKcUdvkBU/smqujSczTzzSjIoR5QqQA6lN1ZRSnuHIWCvh +JEltkYnTAH41QJ6SAWO66GrrUESwN/cgZzL4JLEqz1Y= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGlTCCBH2gAwIBAgIRANJ/u8HeNZ5SFq1hSVhgmcQwDQYJKoZIhvcNAQEMBQAw +gYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtK +ZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMS4wLAYD +VQQDEyVVU0VSVHJ1c3QgUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTIx +MDMyMjAwMDAwMFoXDTM4MDExODIzNTk1OVowXzELMAkGA1UEBhMCR0IxGDAWBgNV +BAoTD1NlY3RpZ28gTGltaXRlZDE2MDQGA1UEAxMtU2VjdGlnbyBQdWJsaWMgU2Vy +dmVyIEF1dGhlbnRpY2F0aW9uIFJvb3QgUjQ2MIICIjANBgkqhkiG9w0BAQEFAAOC +Ag8AMIICCgKCAgEAk77VNlJ12AEjoBxHQknuY7a3If3EldVIKyZ8FFMQ2nn9K7ct +pNQs+uoy3UnCub0PSD17WphUr55dMXRPB/xQId2kz2hPGxJjbSWZTCqZ80gwYfqB +fB6nCErcPiscHxhMcao1jK34bug7StnllALWiYQTqm3ITzPMUJY3kjPcX4jnn1TZ +SPCYQ9Zm/Z8XOEPFAVEL1+MjDxRdWxTnS77d9MjaAzfR1jmhIVEwg7Bt1zBOlluR +8HAkq79FgWRDDb0hOi886Z4NyyC1QifM2m+b7mQwkDnNk2WBITG1I1AzNyLjOO34 +MTDMRf5i+dFdMnlCh99qzFYZQE3Oqrv5tXZJlPEn+JGlg+UGs2MOgNzgElWApjtm +tDmHLcjw0NEU6eQNTQ72XVdyxTscR1ad4tX7gWGMzE2AkDRbt9cUddzYBEifwMEo +iLTpHMqnsfFWt3tJTFnlIBWohAIp+jiUaZpJBo/NH3kUFxIMg3reH7GX7vmXeCik +yESS6X0mBaZYcpt5E9gRX67FOGI0aLKGMI74kGGeMmz1BzbNokxu7Io27fLmmRVE +cMN8vJw5wLTha/eDJSNX2RKA5UnwdQ/vjescm1QotCE8/HwK/+97a3X/ix2gGQWr ++vgrgULoOLq7+6r9PeDzyt9Ol5cp7fMYVumllqy9w5CYsuD5otSmR0N8bc8CAwEA +AaOCASAwggEcMB8GA1UdIwQYMBaAFFN5v1qqK0rPVIDh2JvAnfKyA2bLMB0GA1Ud +DgQWBBRWc1hklfmSGrASKgRieaFAFYghSTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwEQYDVR0g +BAowCDAGBgRVHSAAMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwudXNlcnRy +dXN0LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDA1 +BggrBgEFBQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVz +dC5jb20wDQYJKoZIhvcNAQEMBQADggIBADpvBIlq7bMU0cFDT/9P9+BsgCkRgQs0 +S6Bf7vJSlWMHwby0VGvxCS0hrbi0K2BINZbEbsVsgpQq04431yyoVn3Hldorgq24 +RldRDOOipEZDTFB9wC9HYt1thHF00XeG2C8KC1plwoEzKAIhPvefI/C3cT0CfTXJ +uFjUbKIgSwjNjw6YHtLgoy/hd5+JLUlLco/gzFX/qWbT7tEquOMYpsNKWZj8TLqP +q6zMiG4Na6feEZte6YPXGrMWlTWN341vDedc+yxQqSug79HJUQcOZs7KyDWztmae +QxsPE49UV/8XwrfZtZaYyrs4FpD94Z4Q8dzXGL8+qEJjxgcza7W6PROaClubavd1 +VKPm8+aCW77u7SxpR2TFGL6kPdxsKyFijpcunR5V79sUyROfNdzjrAcFWZXK8sbb +9FlnwuVG677JLv+ZVTX5AxLvW5OB4zt5uS+zB62wJ/Wv+jXGAttSAcJec4iFgCWH +Rvdi/jJoSzRLa3nEzx6pFIzclSCnh0u1xCeLcUBypSiPga8W+6PkuoyQq8U9qs9E +oxG5NvrvlyshwUS9yvcZRGw7Ljlx4jJH/BhIPR8kIBCQj1vna9TziZOrw1Of8hDU +bHKFG9Pm8Dp2vbjz/2JH39qvxshPKVllGfq+5klPm7yZRUYTiCMAbqwNdL/nsqF2 +Rnnyp58XRStJ +-----END CERTIFICATE----- diff --git a/internal/artifactsecurity/testdata/pkg-containers-chain.pem b/internal/artifactsecurity/testdata/pkg-containers-chain.pem new file mode 100644 index 00000000..acb588c4 --- /dev/null +++ b/internal/artifactsecurity/testdata/pkg-containers-chain.pem @@ -0,0 +1,93 @@ +-----BEGIN CERTIFICATE----- +MIIFRzCCBC+gAwIBAgISBqZeXTJ5ulZKTedOtdZEeNu4MA0GCSqGSIb3DQEBCwUA +MDMxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQD +EwNZUjEwHhcNMjYwOTMwMjM0NDQ2WhcNMjYxMjI5MjM0NDQ1WjAWMRQwEgYDVQQD +DAsqLmdpdGh1Yi5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ5v +cCX3iMOsvE/MS2wW4FhU49mznQlZk3JWoZZUvS7eBEJmS7WgL0GmZfRbYvpi6Jj3 +tEu3XMsAwshw3VD3sGBVF715P9KBbzOcsprV+tHdBE6W0aIBeXh0br211DAAKLe6 +wvUl8gsnNAX9w2REPbFRlS3OLKcHKTCoNe9vNA1H4wqvMZGCCxiSfWGV6aECYlCJ +7agyjfaMZDcgRmh9etkha1MuMdgqmhZyN4dK68pjXgiUlg1PWXGTfMnUmtpI6SKL +Cy/WTxn4EwEHp3RnuH2FoROqNaT/PSB9fgJRderXiLZCfAXUevdqHBd2QHBHPtXY +szda6avR4UI346zmU0kCAwEAAaOCAnAwggJsMA4GA1UdDwEB/wQEAwIFoDATBgNV +HSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQkv28B7ZOO +2TtRvMz6OPOmEXTesDAfBgNVHSMEGDAWgBQfLzW+RhSCzUCxrnksVXj699Ro+zAz +BggrBgEFBQcBAQQnMCUwIwYIKwYBBQUHMAKGF2h0dHA6Ly95cjEuaS5sZW5jci5v +cmcvMGsGA1UdEQRkMGKCDCouZ2l0aHViLmNvbYILKi5naXRodWIuaW+CFyouZ2l0 +aHVidXNlcmNvbnRlbnQuY29tggpnaXRodWIuY29tgglnaXRodWIuaW+CFWdpdGh1 +YnVzZXJjb250ZW50LmNvbTATBgNVHSAEDDAKMAgGBmeBDAECATAuBgNVHR8EJzAl +MCOgIaAfhh1odHRwOi8veXIxLmMubGVuY3Iub3JnLzU3LmNybDCCAQ4GCisGAQQB +1nkCBAIEgf8EgfwA+gB3AMIxfldFGaNF7n843rKQQevHwiFaIr9/1bWtdprZDlLN +AAABoPTqY6sAAAQDAEgwRgIhAMkx3rfvs2Wbp+C7HuP66KjnAVOd0TBISqhlmG3i +XI2FAiEAuG5fbduLC6iNj0VA0Ff0Du0I7X5Ep3hCgCi3Y1ypbOkAfwCigQAYc04X +bh1H4JVA84G6VGaXzWOoQ1BxbrgJTtrxDQAAAaD06mcWAAgAAAUAGSPmdQQDAEgw +RgIhAI4wrGrWnEK0FZWenv0x8oYGsRwpBi8IYncGGUPR4ZWBAiEAtaGtzp9OCY1L +N0kvT44JFOS5QtmcaIl5gH1oZ8EJE/4wDQYJKoZIhvcNAQELBQADggEBADQyL2gT +QJZjQHdkPckmvH5qVpjxK4iQxo3VDY1cahEf8i0ADZwbFmSVY1QGUfLVMixcEtdi +rqik27F0dABcV4GVkJ5aBNO2SEwud5Hzqpr/Ehph/AqDOw3NUGGGQPPOmwK+M95s +1597qbGApjjIScgTjYZfxA+33JAj9zBm0frBnw9OZI3xrQ0h4GDuZmccfDkeFHVy +Vos/hNCc9Meim92ll7LdoHf3CvGu8VW7jAy+Rf7xtFD4qCTkObzcBi9b6vpsz3RB +m+oyGf/6lQ1YeU0gP07unocAryl3bfGtYTTLbOUg0MNiQ99Mv1Tl4Fuhx/6t/kUs +8As2fnldKCrZN8w= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIE2zCCAsOgAwIBAgIRAKICU/FfJpHAXcHOE7m8yk4wDQYJKoZIhvcNAQELBQAw +LjELMAkGA1UEBhMCVVMxDTALBgNVBAoTBElTUkcxEDAOBgNVBAMTB1Jvb3QgWVIw +HhcNMjUwOTAzMDAwMDAwWhcNMjgwOTAyMjM1OTU5WjAzMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNTGV0J3MgRW5jcnlwdDEMMAoGA1UEAxMDWVIxMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoVi8X2xCYgMXvJxNPKp/oF13UMgmPABB07VC +LNDtoXmt9luEZNJSBV10VyT1Pz6LD8Zq1d2gc43WNl1AdRrj4sEnazbOiz0nPpmG +Bp2hui49oZtDIY6wdKeZAi5BbNU20CH6RSBBMLSQ9cXrH8dxdv4PAJ45ssGML68U +SE3BsjC2a6cAN9L5CgXVIQi5tfNiTPoFZZ3S0OlXqLmmtdV95udWAb5b6e/F49Di +CsH0Y00Ag72BVIb1hzynmKe+X0mERBTtsb3BwmpV9ipeBjMLoR/D9cHxHQCWoi5l +TmXwY015J5rGelz1nZjJuxc2kioaX29XJBnhMkP531rSdG5uMwIDAQABo4HuMIHr +MA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDATASBgNVHRMBAf8E +CDAGAQH/AgEAMB0GA1UdDgQWBBQfLzW+RhSCzUCxrnksVXj699Ro+zAfBgNVHSME +GDAWgBTe51tg0CJtQCh9Pw0B/qS1UrRRlDAyBggrBgEFBQcBAQQmMCQwIgYIKwYB +BQUHMAKGFmh0dHA6Ly95ci5pLmxlbmNyLm9yZy8wEwYDVR0gBAwwCjAIBgZngQwB +AgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3lyLmMubGVuY3Iub3JnLzANBgkq +hkiG9w0BAQsFAAOCAgEA0+zvMq3kHig1ddTmmm+RibTr9/RpX7k4buanMMRqbV/y +IvP82zAHN3mvaw+cASuVsdpd0ikjhr4hnhJQLQOzOp2ccKrsdGOAgo0vddeISFAq +EWEV4lmUM3vFF796up+bSgmJ1u6RupDCMxDgF8M3eLvGuj6L0lu3zkQ0KuQLnKxL +tB0oQqn1Idg5CuuGpMvQzk29Pa3D/qHurc0EIM9SxukQuJqq63lxsYyRQFU8yMBO +hq1w5LbfaWNRrz1uklOfI/pYkAb2E2MTZrAMQkBIE2S8Jt1F8gRc96o/xOsrgvSk +a84AisX6xq1lz1Z7jGvrnXc4TMcjxZTjiTaihcYI1JIXZiLtEMSCa5l3cu8YWd6z +dLRQlqRdclVjuQfNHawRJ6GWlkK0QJosivTKwdBw3KxEtzGo8yMHERbsy57gP1UX +HOMcmZYQC0gtyR3SxfenIM/MxC3Ia2Ypab/kQ/CTnlIn2KQ5JUC6NYrGCbhFN9bp +5lKJStEwCUnLpntcrXk5XVDCNv/5RyWpRThkGOV7GetKkQ0qAY8hCzWK6oqnAhDZ +cjlYVdWfqOw3DIOX6EDNBgAqHarRVxyF9QZdOaXSyPJ0ueD2BYJEBgaCGQ8rAaU/ +Qc123V5LTXDZW4CcsPBDyhy4v+c8hClAyw/IkJlfBqxB9D+/wvIMHgECZ4ptP6o= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF9DCCA9ygAwIBAgIRAPJLbRf52a18scn+p4eCaZ8wDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjYwNTEzMDAwMDAw +WhcNMzIwOTAyMjM1OTU5WjAuMQswCQYDVQQGEwJVUzENMAsGA1UEChMESVNSRzEQ +MA4GA1UEAxMHUm9vdCBZUjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +ANvGJnN78CTJdWL3+eGfsLN5TrNBJs+VH9hRXqRbwxu9sGNiB0BD1fcOxbSUQCJI +M1xE13Db+5Cw1w0s0EBYsvuIP/6joF0w8cuImbgR1OGgYbSQ4OpzI+DG8SGuTlcE +873OCS+kh3srlo6vl43M5OJg4Aeo1sfHp6kTJDoIiFBNJAY+OKfX/FUvYKuhjT+n +o49lmqmupSBI5PkBQiqrEGtWU5uxU/cQWHGu8jSjFBznZqvbNPLMXMLFxCb3WTfr +JBXXjqvWG+v4bjzxjjeAtOlU7qarRDvNOyAuQYLln904M+faKx8hnLCpJ15ZqaEg +cNlY+9MMWcC5yvL2A2j3l9+2buggZX+dOE91zYmIdawTvSZuVvlbRrAlLxIB6pwM +BjneXCjYQ8+3BCCjssbSNpZU3hTcBDdhfAlEDlYr6pEatnMdmDT5BqnKC92bd0Eh +M1fbLHioLccLCuievT8ZkPhZrq7Mii7gNXAcUEAR8+lzYal+9zTg7C5DALyVOeG/ +CqfRAMn1KSHCR0NSA6P8tn/mGRlnCct5rtVCLnVySVpU6H1qGg3DgTOuskf8eahT +MiYbI5ezPJmO5ertalskQ1utp74+eDy92PI4ftHKTbq9IWhH4YZKh3WnJEIt+oQv +lYZbY8tpEroKrFB6PFGzrJIDRyts4HqvuH52RFj2zv/BAgMBAAGjgeswgegwDgYD +VR0PAQH/BAQDAgEGMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFN7nW2DQIm1AKH0/DQH+pLVStFGUMB8GA1UdIwQYMBaAFHm0 +WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAoYW +aHR0cDovL3gxLmkubGVuY3Iub3JnLzATBgNVHSAEDDAKMAgGBmeBDAECATAnBgNV +HR8EIDAeMBygGqAYhhZodHRwOi8veDEuYy5sZW5jci5vcmcvMA0GCSqGSIb3DQEB +CwUAA4ICAQA8spSI95KKfn2W6GMmDpHBJSPaLbsS3W93cijJCRCYAc1fsJgL1FIL +7C0C9ecPOdcwB2fi0Dk2p94j9iTJCxmt5CFSKLRWwnXT2MMSXexVxqoVB79BdWPx +VXETkVme/qYSAuKVHh5Ps+5BixgmwS1JkjSAc+MfrUbNssVEEnH0aEiAh+rotXAV +JSP/Ye7LJPEwD9DWG72vVWbhAcuOf5OLjz57Ctk7MgQHynZ7+PlHJtajroCaIbtC +r6tcZZaAwUQm+jQyeWdV+2hv9deOYFmKeQyjjcSrN5Nadrw+L9DZJLbA1HqeNvLh +BgqpP0fvJq2N6EtD574N6eMI7uMsJTnji2UDz9el5XLSv9fqJMuDQtYVb2oTNoKp +oUqhxPVC0aq4eG5MESaIdn8b5ZGSSeAJLMHXljEdlNza+ncfkviXk1POLnnFdvx8 +/gk6M374WbLWFXw8N141B/Rl/tINGfl1TxOIiqtiMYkL02RSGb1kq34BL9NPP27z +RGMuHGnzS3hFIrRTfKxrzUZ9RzQWzEG3K6fJ3r2nqSltkeytis9DIBoFY9VmVyjL +M71DMi+y1+TRSJVClEMwvA4yL++7q9XZx5r5wBRWB4kQTKH5qyoZnDw7iiuh1lID +yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA== +-----END CERTIFICATE----- diff --git a/internal/artifactsecurity/trust_test.go b/internal/artifactsecurity/trust_test.go new file mode 100644 index 00000000..cc929cf8 --- /dev/null +++ b/internal/artifactsecurity/trust_test.go @@ -0,0 +1,99 @@ +package artifactsecurity + +import ( + "crypto/sha256" + "crypto/x509" + "encoding/hex" + "encoding/pem" + "errors" + "os" + "path/filepath" + "testing" + "time" +) + +var scannerTrustVerificationTime = time.Date(2026, time.October, 22, 2, 0, 0, 0, time.UTC) + +func TestScannerTrustBundleOmitsDistrustedEMudhraRoots(t *testing.T) { + distrusted := map[string]string{ + "8d417db2dd8bf5e3084d1e3f196d583849d81bdd4c00c70b9d39369e96b8c782": "emSign ECC Root CA - G3", + "b7408b4d2be0238ba37004dd34e276c6019bd2f24c9db7d4980f5f6c359a4bcc": "emSign Root CA - C1", + "eabc185c4e82d942b1a5978ba3c0181487d6b3b9974e5c49f72f6d0bd9637150": "emSign ECC Root CA - C3", + } + for _, certificate := range readCertificates(t, scannerTrustBundlePath(t)) { + digest := sha256.Sum256(certificate.RawSubjectPublicKeyInfo) + if name, found := distrusted[hex.EncodeToString(digest[:])]; found { + t.Fatalf("scanner trust bundle still trusts %s", name) + } + } +} + +func TestScannerTrustBundleRejectsTheEMudhraTLSCrossCertificate(t *testing.T) { + cross := readCertificates(t, filepath.Join("testdata", "emsign-root-tls-ca-g3-cross.pem"))[0] + shipped := scannerTrustPool(t) + _, err := cross.Verify(x509.VerifyOptions{Roots: shipped, CurrentTime: scannerTrustVerificationTime, KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}}) + var unknown x509.UnknownAuthorityError + if !errors.As(err, &unknown) { + t.Fatalf("emSign Root TLS CA - G3 must have no trusted issuer in the scanner bundle, got %v", err) + } + withDistrustedRoot := scannerTrustPool(t) + withDistrustedRoot.AddCert(readCertificates(t, filepath.Join("testdata", "emsign-ecc-root-ca-g3.pem"))[0]) + if _, err := cross.Verify(x509.VerifyOptions{Roots: withDistrustedRoot, CurrentTime: scannerTrustVerificationTime, KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}}); err != nil { + t.Fatalf("cross-certificate control must verify through emSign ECC Root CA - G3: %v", err) + } +} + +func TestScannerTrustBundleAuthenticatesDatabaseHosts(t *testing.T) { + shipped := scannerTrustPool(t) + for host, fixture := range map[string]string{ + "ghcr.io": "ghcr-io-chain.pem", + "pkg-containers.githubusercontent.com": "pkg-containers-chain.pem", + } { + chain := readCertificates(t, filepath.Join("testdata", fixture)) + intermediates := x509.NewCertPool() + for _, certificate := range chain[1:] { + intermediates.AddCert(certificate) + } + if _, err := chain[0].Verify(x509.VerifyOptions{DNSName: host, Roots: shipped, Intermediates: intermediates, CurrentTime: scannerTrustVerificationTime}); err != nil { + t.Fatalf("scanner trust bundle rejects %s: %v", host, err) + } + } +} + +func scannerTrustBundlePath(t *testing.T) string { + t.Helper() + policyRoot, err := filepath.Abs(filepath.Join("..", "..")) + if err != nil { + t.Fatal(err) + } + return filepath.Join(policyRoot, "artifact-security", "scanner-ca-certificates.crt") +} + +func scannerTrustPool(t *testing.T) *x509.CertPool { + t.Helper() + pool := x509.NewCertPool() + for _, certificate := range readCertificates(t, scannerTrustBundlePath(t)) { + pool.AddCert(certificate) + } + return pool +} + +func readCertificates(t *testing.T, path string) []*x509.Certificate { + t.Helper() + payload, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + var certificates []*x509.Certificate + for block, rest := pem.Decode(payload); block != nil; block, rest = pem.Decode(rest) { + certificate, err := x509.ParseCertificate(block.Bytes) + if err != nil { + t.Fatalf("parse %s: %v", path, err) + } + certificates = append(certificates, certificate) + } + if len(certificates) == 0 { + t.Fatalf("%s contains no certificates", path) + } + return certificates +} From b0532865acab98c1cc338ba1f038f8233b62a9c6 Mon Sep 17 00:00:00 2001 From: Jeff Musgrave <1578618+JeffMusgrave@users.noreply.github.com> Date: Wed, 21 Oct 2026 21:52:02 -0400 Subject: [PATCH 03/15] Lint governed Cargo replacements and restore forced lint collection Run pinned cargo check with clippy-driver as RUSTC_WRAPPER so path replacements outside the workspace receive Clippy, while Cargo's lint cap keeps registry and Git dependencies silent. Restore --force-warn so source and macro-emitted allow/expect attributes cannot hide governed lints, and document compiler-generated test-harness and pin! diagnostics as unresolved. Co-Authored-By: Claude Opus 5.5 --- docs/design/cargo-facts.md | 46 ++++++++------ internal/rustfacts/compilation.go | 17 ++++-- .../rustfacts/compilation_patches_test.go | 60 ++++++++++++++++--- internal/rustfacts/compilation_test.go | 53 ++++++++-------- internal/rustfacts/lints_test.go | 10 ++-- 5 files changed, 125 insertions(+), 61 deletions(-) diff --git a/docs/design/cargo-facts.md b/docs/design/cargo-facts.md index 5be2f507..f526faf5 100644 --- a/docs/design/cargo-facts.md +++ b/docs/design/cargo-facts.md @@ -281,8 +281,15 @@ handwritten lock or disabled source check cannot replace that boundary. ## Compiler checks -Compilation uses the matching pinned cargo-clippy executable directly, with an -absolute Cargo path. This bypasses Cargo's ambient external-subcommand lookup. +Compilation runs the pinned Cargo's `check` with the matching pinned +clippy-driver as `RUSTC_WRAPPER` and the policy lint arguments in +`CLIPPY_ARGS`, the driver interface cargo-clippy itself uses. Cargo-clippy is +not used: it installs the driver only as the workspace wrapper, so path +replacements outside the workspace would compile with plain rustc and escape +Clippy. The wrapper reaches every unit. Cargo still caps lints for registry and +Git packages, and the driver disables Clippy for those capped units, so +non-local code stays silent. The pinned driver is selected explicitly; there is +no ambient external-subcommand lookup. The common runner captures Cargo's JSON events with bounded output and process tree cancellation. Cargo must retain its reviewed lock and available dependency cache through `--frozen`; a missing lock is a preparation failure, never an @@ -299,20 +306,25 @@ Selecting only replacement source still checks the consumer; selecting both does not compile it twice. Replacement development targets and their independent locks are not part of the consumer build. Source analysis remains selected for the replacement, while compiler evidence covers only dependencies active in the -consumer's default feature and host configuration. - -Compiler warnings, Clippy's default lint group and unused-code lints use `--warn` at -the invocation boundary. This retains the compiler-generated test-entry and -standard-library macro lint scopes. Forcing those scopes reports valid binary -entry points as unused in test builds and deliberate standard-library expansion -control flow as unreachable user code. Written `allow` and `expect` attributes -remain blocking findings under the independent parsed source policy, including -conditional forms. Written `deny` and `forbid` retain compiler failure semantics; -collection does not lower source errors to warnings. Code Polishy treats the -resulting typed findings as blocking errors, -subject to its existing exact exception policy. Failed compilation always adds -a project failure, even when the compiler also reports typed source diagnostics; -an excepted lint cannot make a failed build or operational invocation pass. +consumer's default feature and host configuration. Within that configuration, +replacement libraries receive the same Clippy, compiler +warning and unused-code collection as the consumer. + +Compiler warnings, Clippy's default lint group and unused-code lints use +`--force-warn` at the invocation boundary. Source `allow`, `expect`, `deny` and +`forbid` attributes, including attributes emitted by macro expansion, cannot +hide these diagnostics or turn policy lint collection into a compiler failure. +Forcing also overrides lint scopes the compiler generates itself. The generated +test harness reports a binary's `main` and its callees as unused, and some +standard-library macro expansions, such as `std::pin::pin!` of an `async` +block, report unreachable code at the call site. These diagnostics are false +and remain unresolved; no exception or source-parser rule hides them. A +complete proof that macro-expanded suppression is otherwise ineffective is also +unavailable, so native admission stays blocked on both. Code Polishy treats +the resulting typed findings as blocking errors, subject to its existing exact +exception policy. Failed compilation always adds a project failure, even when +the compiler also reports typed source diagnostics; an excepted lint cannot make a failed build or +operational invocation pass. Rust does not support forcing the aggregate `warnings` group. The release owns the concrete warning-by-default lint inventory for its pinned compiler, checked against that compiler's advertised lint interface during toolchain tests. Rust @@ -322,7 +334,7 @@ The advertised `tail-call-track-caller` warning requires unstable explicit tail calls in this release and is not selectable on the supported stable toolchain. Its exclusion is verified against the real compiler, not silently ignored at runtime. -Native dead-code coverage uses rustc's unused-item/import/macro diagnostics +Native dead-code coverage uses rustc's forced unused-item/import/macro diagnostics for the selected Cargo configuration. Public library APIs remain legitimate entry points; a private item used only by a disabled feature is unused in the checked configuration. This is compiler reachability, not a claim that every diff --git a/internal/rustfacts/compilation.go b/internal/rustfacts/compilation.go index ab76e8ee..3bda8a4a 100644 --- a/internal/rustfacts/compilation.go +++ b/internal/rustfacts/compilation.go @@ -60,15 +60,22 @@ func compilationSources(repo repository.Repository, manifest string, allFiles [] } func compilationCommand(repo repository.Repository, manifest string) policy.Command { - command := Command(repo, manifestCommandName("policy-clippy", manifest), "cargo-clippy", "clippy", "--frozen", "--all-targets", "--no-deps", "--message-format=json", "--target", HostTarget(), "--manifest-path", manifest, "--", "--warn", "clippy::all", "--warn", "unused") - for _, lint := range strings.Fields(compilerWarningLints) { - command.Argv = append(command.Argv, "--warn", lint) - } + command := Command(repo, manifestCommandName("policy-clippy", manifest), "cargo", "check", "--frozen", "--all-targets", "--message-format=json", "--target", HostTarget(), "--manifest-path", manifest) command.Environment = append(command.Environment, "PATH", "LIB", "INCLUDE", "LIBPATH", "SDKROOT", "DEVELOPER_DIR") - command.EnvironmentOverrides = append(command.EnvironmentOverrides, "CARGO="+repo.RustTool("cargo"), "CLIPPY_CONF_DIR="+filepath.Join(repo.PolicyRoot, "tools")) + command.EnvironmentOverrides = append(command.EnvironmentOverrides, "RUSTC_WRAPPER="+repo.RustTool("clippy-driver"), "CLIPPY_ARGS="+strings.Join(compilationLintArguments(), clippyArgumentSeparator)+clippyArgumentSeparator, "CLIPPY_CONF_DIR="+filepath.Join(repo.PolicyRoot, "tools")) return command } +func compilationLintArguments() []string { + arguments := []string{"--force-warn", "clippy::all", "--force-warn", "unused"} + for _, lint := range strings.Fields(compilerWarningLints) { + arguments = append(arguments, "--force-warn", lint) + } + return arguments +} + +const clippyArgumentSeparator = "__CLIPPY_HACKERY__" + func HostTarget() string { architecture := map[string]string{"amd64": "x86_64", "arm64": "aarch64"}[runtime.GOARCH] platform := map[string]string{"linux": "unknown-linux-gnu", "darwin": "apple-darwin", "windows": "pc-windows-msvc"}[runtime.GOOS] diff --git a/internal/rustfacts/compilation_patches_test.go b/internal/rustfacts/compilation_patches_test.go index b5608b05..e45a86af 100644 --- a/internal/rustfacts/compilation_patches_test.go +++ b/internal/rustfacts/compilation_patches_test.go @@ -3,11 +3,13 @@ package rustfacts import ( "bytes" "os" + "os/exec" "path/filepath" "slices" "testing" "github.com/riteofstring/code-polishy/internal/policy" + "github.com/riteofstring/code-polishy/internal/repository" "github.com/riteofstring/code-polishy/internal/runner" ) @@ -15,14 +17,8 @@ func TestRustCompilationUsesReplacementConsumerAndPreservesDependencyTargets(t * t.Parallel() for _, selected := range []string{"Cargo.toml", "vendor/patch-source/src/lib.rs", "vendor/leaf/src/lib.rs"} { t.Run(selected, func(t *testing.T) { - repo := cargoPatchRepository(t) - manifest := string(readRustFile(t, repo, "Cargo.toml")) - manifest = string(bytes.ReplaceAll([]byte(manifest), []byte("default=[]"), []byte("default=['desktop']"))) - write(t, repo.Root, "Cargo.toml", manifest) - write(t, repo.Root, "src/lib.rs", "pub fn value() -> u32 { patch_source::value() }\n") + repo := consumedReplacementRepository(t) write(t, repo.Root, "vendor/patch-source/src/lib.rs", "pub fn value() -> u32 { patch_leaf::value() }\n") - write(t, repo.Root, "vendor/patch-source/build.rs", "fn main() {}\n") - write(t, repo.Root, "vendor/patch-source/tests/unconsumed.rs", "compile_error!(\"dependency development targets must not compile\");\n") write(t, repo.Root, "vendor/leaf/src/lib.rs", "pub fn value() -> u32 { 42 }\n") before := readRustFile(t, repo, "Cargo.lock") plan, discovery := compilationDiscovery(t, repo, selected) @@ -47,3 +43,53 @@ func TestRustCompilationUsesReplacementConsumerAndPreservesDependencyTargets(t * }) } } + +func TestRustCompilationLintsGovernedReplacementsAndCapsNonLocalDependencies(t *testing.T) { + t.Parallel() + repo := consumedReplacementRepository(t) + remote := t.TempDir() + write(t, remote, "Cargo.toml", "[package]\nname='remote'\nversion='1.0.0'\nedition='2021'\n") + write(t, remote, "src/lib.rs", "pub fn value() -> u32 { if needless(true) { 1 } else { 2 } }\nfn needless(value: bool) -> bool { if value { true } else { false } }\nfn remote_unused() {}\n") + for _, arguments := range [][]string{{"init", "-q"}, {"add", "."}, {"-c", "user.name=fixture", "-c", "user.email=fixture@example.test", "commit", "-qm", "fixture"}} { + if output, err := exec.Command("git", append([]string{"-C", remote}, arguments...)...).CombinedOutput(); err != nil { + t.Fatalf("git fixture: %v; %s", err, output) + } + } + manifest := string(readRustFile(t, repo, "Cargo.toml")) + write(t, repo.Root, "Cargo.toml", string(bytes.Replace([]byte(manifest), []byte("[patch.crates-io]"), []byte("remote={git='file://"+filepath.ToSlash(remote)+"'}\n[patch.crates-io]"), 1))) + write(t, repo.Root, "src/lib.rs", "pub fn value() -> u32 { patch_source::value() + remote::value() }\n") + write(t, repo.Root, "vendor/patch-source/src/lib.rs", "pub fn value() -> u32 { if needless(true) { patch_leaf::value() } else { 2 } }\nfn needless(value: bool) -> bool { if value { true } else { false } }\n#[allow(dead_code)]\nfn source_unused() {}\n") + write(t, repo.Root, "vendor/leaf/src/lib.rs", "macro_rules! quiet { ($i:item) => { #[allow(dead_code, clippy::needless_bool)] $i }; }\nquiet! { fn leaf_needless(value: bool) -> bool { if value { true } else { false } } }\npub fn value() -> u32 { 42 }\n") + lock := exec.Command(repo.RustTool("cargo"), "generate-lockfile", "--manifest-path", filepath.Join(repo.Root, "Cargo.toml")) + lock.Env = append(os.Environ(), "CARGO_HOME="+CargoHome(repo), "RUSTC="+repo.RustTool("rustc")) + if output, err := lock.CombinedOutput(); err != nil { + t.Fatalf("fixture lock: %v; %s", err, output) + } + plan, discovery := compilationDiscovery(t, repo, "vendor/patch-source/src/lib.rs") + reported := [][3]string{} + for _, finding := range Compile(t.Context(), repo, plan, discovery, runner.OSRunner{}) { + reported = append(reported, [3]string{finding.Check, finding.Subject, finding.Path}) + } + slices.SortFunc(reported, func(left, right [3]string) int { return slices.Compare(left[:], right[:]) }) + reported = slices.Compact(reported) + want := [][3]string{ + {"quality.clippy", "clippy::needless_bool", "vendor/leaf/src/lib.rs"}, + {"quality.clippy", "clippy::needless_bool", "vendor/patch-source/src/lib.rs"}, + {"quality.rustCompiler", "dead_code", "vendor/leaf/src/lib.rs"}, + {"quality.rustCompiler", "dead_code", "vendor/patch-source/src/lib.rs"}, + } + if !slices.Equal(reported, want) { + t.Fatalf("replacement lint findings = %v, want %v", reported, want) + } +} + +func consumedReplacementRepository(t *testing.T) repository.Repository { + t.Helper() + repo := cargoPatchRepository(t) + manifest := string(readRustFile(t, repo, "Cargo.toml")) + write(t, repo.Root, "Cargo.toml", string(bytes.ReplaceAll([]byte(manifest), []byte("default=[]"), []byte("default=['desktop']")))) + write(t, repo.Root, "src/lib.rs", "pub fn value() -> u32 { patch_source::value() }\n") + write(t, repo.Root, "vendor/patch-source/build.rs", "fn main() {}\n") + write(t, repo.Root, "vendor/patch-source/tests/unconsumed.rs", "compile_error!(\"dependency development targets must not compile\");\n") + return repo +} diff --git a/internal/rustfacts/compilation_test.go b/internal/rustfacts/compilation_test.go index 3f28c911..93ceeb03 100644 --- a/internal/rustfacts/compilation_test.go +++ b/internal/rustfacts/compilation_test.go @@ -51,36 +51,35 @@ func TestRustCompilationReportsOriginalTypeAndLintLocations(t *testing.T) { } } -func TestRustCompilationRespectsCompilerGeneratedLintScopes(t *testing.T) { +func TestRustCompilationCollectsLintsDespiteSourceLevels(t *testing.T) { t.Parallel() - repo := compilationRepository(t, "pub fn answer() -> u32 { 42 }\n") - write(t, repo.Root, "src/main.rs", `fn main() { show_answer(); } -fn show_answer() { println!("{}", app::answer()); } -#[test] -fn pinned_future() { - let future = std::pin::pin!(async { app::answer() }); - assert_eq!(std::mem::size_of_val(&future), std::mem::size_of::()); -} -`) - plan, discovery := compilationDiscovery(t, repo, "src/main.rs") - if findings := Compile(t.Context(), repo, plan, discovery, runner.OSRunner{}); len(findings) != 0 { - t.Fatalf("compiler-generated test entry and standard macro produced false lints: %+v", findings) - } -} - -func TestRustCompilationPreservesDeniedSourceLintsAsFailures(t *testing.T) { - t.Parallel() - for _, level := range []string{"deny", "forbid"} { - t.Run(level, func(t *testing.T) { - repo := compilationRepository(t, "#["+level+"(clippy::needless_bool)]\npub fn answer(value: bool) -> bool { if value { true } else { false } }\n") + for name, test := range map[string]struct{ source, subject string }{ + "allow clippy": {"#[allow(clippy::needless_bool)]\npub fn answer(value: bool) -> bool { if value { true } else { false } }\n", "clippy::needless_bool"}, + "expect clippy": {"#[expect(clippy::needless_bool)]\npub fn answer(value: bool) -> bool { if value { true } else { false } }\n", "clippy::needless_bool"}, + "deny clippy": {"#[deny(clippy::needless_bool)]\npub fn answer(value: bool) -> bool { if value { true } else { false } }\n", "clippy::needless_bool"}, + "forbid clippy": {"#[forbid(clippy::needless_bool)]\npub fn answer(value: bool) -> bool { if value { true } else { false } }\n", "clippy::needless_bool"}, + "allow unused": {"#[allow(dead_code)]\nfn unused() {}\npub fn answer() {}\n", "dead_code"}, + "expect unused": {"#[expect(dead_code)]\nfn unused() {}\npub fn answer() {}\n", "dead_code"}, + "allow warning": {"#[allow(unreachable_code)]\npub fn answer() { return; println!(\"unreachable\"); }\n", "unreachable_code"}, + "macro clippy": {"macro_rules! quiet { ($i:item) => { #[allow(dead_code, clippy::needless_bool)] $i }; }\nquiet! { fn unused(value: bool) -> bool { if value { true } else { false } } }\npub fn answer() {}\n", "clippy::needless_bool"}, + "macro unused": {"macro_rules! quiet { ($i:item) => { #[allow(dead_code, clippy::needless_bool)] $i }; }\nquiet! { fn unused(value: bool) -> bool { if value { true } else { false } } }\npub fn answer() {}\n", "dead_code"}, + "meta unused": {"macro_rules! with { ($m:meta, $i:item) => { #[$m] $i }; }\nwith!(allow(dead_code), fn unused() {});\npub fn answer() {}\n", "dead_code"}, + } { + t.Run(name, func(t *testing.T) { + repo := compilationRepository(t, test.source) plan, discovery := compilationDiscovery(t, repo, "src/lib.rs") - findings := Compile(t.Context(), repo, plan, discovery, runner.OSRunner{}) - if !slices.ContainsFunc(findings, func(finding policy.Finding) bool { - return finding.Subject == "clippy::needless_bool" && finding.Path == "src/lib.rs" && finding.Line > 0 - }) || !slices.ContainsFunc(findings, func(finding policy.Finding) bool { - return finding.Subject == "compilation" + result, output, err := runner.RunCaptured(t.Context(), runner.OSRunner{}, repo.Root, plan.Commands[0], runner.CaptureStructured) + if err != nil || result.ExitStatus != 0 { + t.Fatalf("lint collection failed compilation: %+v, %v; %s", result, err, output.Stderr) + } + report, err := ParseCompilation(repo, "Cargo.toml", discovery.Workspaces[0], output.Stdout) + if err != nil || !report.Success { + t.Fatalf("lint report lacks successful compilation evidence: %+v, %v", report, err) + } + if !slices.ContainsFunc(report.Findings, func(finding policy.Finding) bool { + return finding.Subject == test.subject && finding.Path == "src/lib.rs" && finding.Line > 0 }) { - t.Fatalf("source error was lowered or lost: %+v", findings) + t.Fatalf("source levels hid the governed lint: %+v", report.Findings) } }) } diff --git a/internal/rustfacts/lints_test.go b/internal/rustfacts/lints_test.go index 42c5c920..78eb6634 100644 --- a/internal/rustfacts/lints_test.go +++ b/internal/rustfacts/lints_test.go @@ -15,11 +15,11 @@ func TestRustLintCollectionCoversCompilerWarningsWithoutLoweringErrors(t *testin if err != nil { t.Fatalf("compiler lint interface: %v; %s", err, output.Stderr) } - requested := map[string]bool{} - arguments := compilationCommand(repo, "Cargo.toml").Argv + forced := map[string]bool{} + arguments := compilationLintArguments() for index, argument := range arguments[:len(arguments)-1] { - if argument == "--warn" { - requested[arguments[index+1]] = true + if argument == "--force-warn" { + forced[arguments[index+1]] = true } } warnings, errors := 0, 0 @@ -33,7 +33,7 @@ func TestRustLintCollectionCoversCompilerWarningsWithoutLoweringErrors(t *testin } else { errors++ } - if requested[fields[0]] != (fields[1] == "warn") { + if forced[fields[0]] != (fields[1] == "warn") { t.Errorf("policy lint collection changes the compiler's %s contract for %s", fields[1], fields[0]) } } From efae918c8773b06987e805d0f711e644b8d0c0a4 Mon Sep 17 00:00:00 2001 From: Jeff Musgrave <1578618+JeffMusgrave@users.noreply.github.com> Date: Wed, 21 Oct 2026 22:06:36 -0400 Subject: [PATCH 04/15] Omit forced warnings located entirely in external macro bodies Forced lint levels bypass rustc's external-macro suppression, so std::pin::pin! of an async block reported library-internal unreachable code at the user's call site. Omit a warning only when every span is macro-expanded text outside the repository; macro input, local macro bodies, repository causes and errors remain findings. Record why the binary test-harness dead-code diagnostics remain unresolved. Co-Authored-By: Claude Opus 5.5 --- docs/design/cargo-facts.md | 36 +++++++++++++++------- internal/rustfacts/compilation_test.go | 25 +++++++++++++++ internal/rustfacts/compiler_diagnostics.go | 23 ++++++++++++++ internal/rustfacts/compiler_output_test.go | 31 +++++++++++++++++++ 4 files changed, 104 insertions(+), 11 deletions(-) diff --git a/docs/design/cargo-facts.md b/docs/design/cargo-facts.md index f526faf5..73b7f8f7 100644 --- a/docs/design/cargo-facts.md +++ b/docs/design/cargo-facts.md @@ -314,17 +314,31 @@ Compiler warnings, Clippy's default lint group and unused-code lints use `--force-warn` at the invocation boundary. Source `allow`, `expect`, `deny` and `forbid` attributes, including attributes emitted by macro expansion, cannot hide these diagnostics or turn policy lint collection into a compiler failure. -Forcing also overrides lint scopes the compiler generates itself. The generated -test harness reports a binary's `main` and its callees as unused, and some -standard-library macro expansions, such as `std::pin::pin!` of an `async` -block, report unreachable code at the call site. These diagnostics are false -and remain unresolved; no exception or source-parser rule hides them. A -complete proof that macro-expanded suppression is otherwise ineffective is also -unavailable, so native admission stays blocked on both. Code Polishy treats -the resulting typed findings as blocking errors, subject to its existing exact -exception policy. Failed compilation always adds a project failure, even when -the compiler also reports typed source diagnostics; an excepted lint cannot make a failed build or -operational invocation pass. +Forcing also bypasses rustc's suppression of lints in code generated by +external macros. A warning is therefore omitted when every one of its spans, +at its own location, is macro-expanded text outside the repository. For +example, `std::pin::pin!` of an `async` block reports unreachable code whose +expression and cause both lie in the standard library's macro body. A warning +remains a finding when any span is repository text, including macro input +tokens, a local macro body or the user statement that makes later expanded code +unreachable. Errors are never omitted. + +Forcing also overrides the `allow(dead_code)` the test harness adds to a +binary's `main`. That attribute normally seeds dead-code reachability, so the +binary test build reports `main` and everything reachable only from it as +unused. These diagnostics are false and remain unresolved; no exception or +source-parser rule hides them. Cargo's compiler messages carry the same target +identity for the binary and its test build, and rustc reports these items +exactly as it reports a truly unused `cfg(test)` item. Distinguishing them +would require the emitting unit's test mode and either reachability provenance +or the item inventory of the ordinary build. A complete proof that +macro-expanded suppression is otherwise ineffective is also unavailable, so +native admission stays blocked on both. + +Code Polishy treats the resulting typed findings as blocking errors, subject to +its existing exact exception policy. Failed compilation always adds a project +failure, even when the compiler also reports typed source diagnostics; an +excepted lint cannot make a failed build or operational invocation pass. Rust does not support forcing the aggregate `warnings` group. The release owns the concrete warning-by-default lint inventory for its pinned compiler, checked against that compiler's advertised lint interface during toolchain tests. Rust diff --git a/internal/rustfacts/compilation_test.go b/internal/rustfacts/compilation_test.go index 93ceeb03..e86da7ba 100644 --- a/internal/rustfacts/compilation_test.go +++ b/internal/rustfacts/compilation_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "errors" + "fmt" "os" "path/filepath" "slices" @@ -85,6 +86,30 @@ func TestRustCompilationCollectsLintsDespiteSourceLevels(t *testing.T) { } } +func TestRustCompilationOmitsLibraryExpansionWarningsAndKeepsRepositoryCode(t *testing.T) { + t.Parallel() + repo := compilationRepository(t, `macro_rules! stop { () => {{ return 0; 1 }} } +pub fn local_macro() -> u32 { stop!() } +pub fn after_todo() -> u32 { todo!(); 1 } +fn really_unused() {} +#[cfg(test)] +fn test_helper_unused() {} +pub fn pinned() -> usize { let future = std::pin::pin!(async { 1u32 }); std::mem::size_of_val(&future) } +pub fn pinned_input() -> usize { let future = std::pin::pin!(async { return 1u32; 2u32 }); std::mem::size_of_val(&future) } +`) + plan, discovery := compilationDiscovery(t, repo, "src/lib.rs") + reported := []string{} + for _, finding := range Compile(t.Context(), repo, plan, discovery, runner.OSRunner{}) { + reported = append(reported, fmt.Sprintf("%s %s:%d", finding.Subject, finding.Path, finding.Line)) + } + slices.Sort(reported) + reported = slices.Compact(reported) + want := []string{"dead_code src/lib.rs:4", "dead_code src/lib.rs:6", "unreachable_code src/lib.rs:1", "unreachable_code src/lib.rs:3", "unreachable_code src/lib.rs:8"} + if !slices.Equal(reported, want) { + t.Fatalf("findings = %v, want %v", reported, want) + } +} + func TestRustCompilationFailureCannotBecomeAnExceptableLint(t *testing.T) { t.Parallel() for name, test := range map[string]struct{ source, build, subject string }{ diff --git a/internal/rustfacts/compiler_diagnostics.go b/internal/rustfacts/compiler_diagnostics.go index b245fa72..5f84999b 100644 --- a/internal/rustfacts/compiler_diagnostics.go +++ b/internal/rustfacts/compiler_diagnostics.go @@ -24,6 +24,9 @@ func compilerFindings(repo repository.Repository, manifest string, workspace Wor default: return nil, errors.New("cargo compiler diagnostic has an unsupported severity") } + if diagnostic.Level == "warning" && externalExpansion(repo, workspace, diagnostic.Spans) { + return nil, nil + } code, check := "rustc", "quality.rustCompiler" if diagnostic.Code != nil && diagnostic.Code.Code != "" { code = diagnostic.Code.Code @@ -74,3 +77,23 @@ func compilerLocation(repo repository.Repository, workspace Workspace, span comp } return policy.FindingLocation{}, false } + +func externalExpansion(repo repository.Repository, workspace Workspace, spans []compilerSpan) bool { + primary := false + for _, span := range spans { + path := span.File + if !filepath.IsAbs(path) { + path = filepath.Join(repo.Root, filepath.FromSlash(workspace.Root), path) + } + if span.Expansion == nil || withinRepository(repo, path) { + return false + } + primary = primary || span.Primary + } + return primary +} + +func withinRepository(repo repository.Repository, path string) bool { + physical, err := filepath.EvalSymlinks(repo.Root) + return withinDirectory(repo.Root, path) || err == nil && withinDirectory(physical, path) +} diff --git a/internal/rustfacts/compiler_output_test.go b/internal/rustfacts/compiler_output_test.go index 894685eb..ab7b86e8 100644 --- a/internal/rustfacts/compiler_output_test.go +++ b/internal/rustfacts/compiler_output_test.go @@ -60,6 +60,37 @@ func TestCargoCompilerDiagnosticsRetainMacroAndUnavailableLocations(t *testing.T } } +func TestCargoCompilerWarningsOmitOnlyExpansionsOutsideTheRepository(t *testing.T) { + t.Parallel() + repo, workspace, _ := compilerOutputFixture(t) + target := workspace.Packages[0].Targets[0] + target.Path = filepath.Join(repo.Root, target.Path) + library := compilerSpan{File: "/rustc/toolchain/library/core/src/pin.rs", Line: 9, Column: 9, Primary: true, Expansion: &compilerExpansion{Span: compilerSpan{File: "src/lib.rs", Line: 1, Column: 8}}} + repository := compilerSpan{File: "src/lib.rs", Line: 2, Column: 3, Primary: true} + cause := compilerSpan{File: "src/lib.rs", Line: 1, Column: 18} + libraryCause := compilerSpan{File: library.File, Line: 2, Column: 9, Expansion: library.Expansion} + unexpanded := compilerSpan{File: "/unavailable/dependency.rs", Line: 5, Column: 3, Primary: true} + for name, test := range map[string]struct { + level string + spans []compilerSpan + kept bool + }{ + "library expansion": {"warning", []compilerSpan{library, libraryCause}, false}, + "repository cause": {"warning", []compilerSpan{library, cause}, true}, + "library and repository": {"warning", []compilerSpan{library, repository}, true}, + "outside without expansion": {"warning", []compilerSpan{unexpanded}, true}, + "library expansion error": {"error", []compilerSpan{library}, true}, + } { + t.Run(name, func(t *testing.T) { + event := compilerEvent{Reason: "compiler-message", PackageID: workspace.Packages[0].ID, Target: target, Message: compilerDiagnostic{Message: "seeded diagnostic", Level: test.level, Code: &compilerCode{Code: "unreachable_code"}, Spans: test.spans}} + findings, err := compilerFindings(repo, "Cargo.toml", workspace, event) + if err != nil || (len(findings) == 1) != test.kept { + t.Fatalf("findings = %+v, %v; kept = %v", findings, err, test.kept) + } + }) + } +} + func TestCargoCompilerOutputRetainsDiagnosticsBeforeTruncation(t *testing.T) { t.Parallel() repo, workspace, _ := compilerOutputFixture(t) From b9e0d7cb506af2003336c822d84a4cf3def2d2bf Mon Sep 17 00:00:00 2001 From: Jeff Musgrave <1578618+JeffMusgrave@users.noreply.github.com> Date: Wed, 21 Oct 2026 22:40:51 -0400 Subject: [PATCH 05/15] Carry a Go 1.26.10 build of Trivy 0.74.0 as the artifact scanner The official Trivy 0.74.0 image is built with Go 1.26.6, whose certificate verifier accepts the compromised eMudhra intermediates (GO-2026-6809) during the scanner's database download. The policy-tool installers now build the same official module, pinned by its checksum-database sum, with the pinned Go 1.26.10 and the official build settings, and verify the result's module and toolchain identity. Releases carry the host architecture's Linux binary and its license, and release installation refuses a mismatched version. The scanner runtime copies the carried binary through a named build context instead of pulling the Trivy image, and refuses a Docker platform the release does not carry. Scanner policy moves to version 3 and reports to version 2 without the source-image field. The standard-library OpenVEX statements no longer match any finding and are removed. Co-Authored-By: Claude Opus 5.5 --- .code-polishy.json | 4 + CHANGELOG.md | 10 ++ artifact-security/scanner-policy.json | 3 +- artifact-security/scanner.Dockerfile | 3 +- artifact-security/scanner.openvex.json | 45 +------- docs/artifact-security.md | 12 +- docs/design/carried-trivy-scanner.md | 43 ++++++++ docs/design/trivy-scanner-assessments.md | 103 +++++++----------- docs/security/dependency-assessments.md | 18 ++- docs/source-provenance.md | 8 +- internal/artifactsecurity/archive_test.go | 5 +- .../artifactsecurity/artifactsecurity_test.go | 81 ++++++++++++-- internal/artifactsecurity/commands.go | 2 - internal/artifactsecurity/run.go | 34 ++---- internal/artifactsecurity/scan.go | 5 +- internal/artifactsecurity/types.go | 13 +-- scripts/build-release.ps1 | 2 +- scripts/install.sh | 7 ++ scripts/test-install.sh | 18 ++- tools/install-go-tools.sh | 47 ++++++++ tools/install-policy-tools.ps1 | 46 ++++++++ tools/trivy_module_sum.txt | 1 + 22 files changed, 330 insertions(+), 180 deletions(-) create mode 100644 docs/design/carried-trivy-scanner.md create mode 100644 tools/trivy_module_sum.txt diff --git a/.code-polishy.json b/.code-polishy.json index f3ca07c4..8319ac54 100644 --- a/.code-polishy.json +++ b/.code-polishy.json @@ -63,6 +63,10 @@ "path": "docs/design/trivy-scanner-assessments.md", "module": "artifactsecurity" }, + { + "path": "docs/design/carried-trivy-scanner.md", + "sourcePaths": ["tools/install-go-tools.sh"] + }, { "path": "docs/design/optional-rust-tools.md", "sourcePaths": [ diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b624c66..6153682d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## Unreleased + +- Build the artifact scanner from the official Trivy 0.74.0 Go module with the + pinned Go 1.26.10 and carry it in each release, so the scanner's database + download rejects compromised certificate intermediates covered by + GO-2026-6809. The runtime no longer pulls the Trivy image. Scanner policy is + version 3 and scanner reports are version 2, without the `sourceImage` field. +- Give the scanner a release-owned trust bundle that omits the eMudhra roots + Mozilla distrusted on 2026-10-20. + ## 0.30.0 - 2026-10-20 - Build and ship Go 1.26.10 to reject compromised certificate intermediates diff --git a/artifact-security/scanner-policy.json b/artifact-security/scanner-policy.json index 3ee24426..4021659d 100644 --- a/artifact-security/scanner-policy.json +++ b/artifact-security/scanner-policy.json @@ -1,7 +1,6 @@ { - "version": 2, + "version": 3, "trivyVersion": "0.74.0", - "sourceImage": "docker.io/aquasec/trivy@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969", "databaseRepository": "ghcr.io/aquasecurity/trivy-db:2", "databaseSchemaVersion": 2, "databaseMaximumAgeHours": 24, diff --git a/artifact-security/scanner.Dockerfile b/artifact-security/scanner.Dockerfile index 0a72e926..0b452e73 100644 --- a/artifact-security/scanner.Dockerfile +++ b/artifact-security/scanner.Dockerfile @@ -1,6 +1,5 @@ -FROM docker.io/aquasec/trivy@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969 AS source FROM scratch -COPY --from=source /usr/local/bin/trivy /usr/local/bin/trivy +COPY --from=trivy trivy /usr/local/bin/trivy COPY scanner-ca-certificates.crt /etc/ssl/certs/ca-certificates.crt ENV GODEBUG=http2client=0 USER 65532:65532 diff --git a/artifact-security/scanner.openvex.json b/artifact-security/scanner.openvex.json index b73c0f7d..ad71ef83 100644 --- a/artifact-security/scanner.openvex.json +++ b/artifact-security/scanner.openvex.json @@ -1,9 +1,9 @@ { "@context": "https://openvex.dev/ns/v0.2.0", "@id": "https://github.com/riteofstring/code-polishy/blob/main/artifact-security/scanner.openvex.json", - "author": "Codex", - "timestamp": "2026-10-17T19:40:46Z", - "version": 2, + "author": "Claude Opus 5.5", + "timestamp": "2026-10-22T02:31:29Z", + "version": 3, "statements": [ { "vulnerability": { @@ -56,45 +56,6 @@ "status": "not_affected", "justification": "vulnerable_code_not_in_execute_path", "impact_statement": "The vulnerable operation is xDS server routing of an HTTP/2 request without authority or Host. Code Polishy invokes only version, database download, and standalone offline image-archive scans; it never starts a gRPC or xDS server." - }, - { - "vulnerability": { - "name": "CVE-2026-78667" - }, - "products": [ - { - "@id": "pkg:golang/stdlib@v1.26.6" - } - ], - "status": "not_affected", - "justification": "vulnerable_code_not_in_execute_path", - "impact_statement": "The affected net/http file-serving handlers parse attacker-controlled Range request headers. The scanner starts no HTTP server and uses no file-serving transport; its only networked operation is an outgoing HTTPS database download." - }, - { - "vulnerability": { - "name": "CVE-2026-78669" - }, - "products": [ - { - "@id": "pkg:golang/stdlib@v1.26.6" - } - ], - "status": "not_affected", - "justification": "inline_mitigations_already_exist", - "impact_statement": "The minimal scanner image sets GODEBUG=http2client=0. Trivy clones the standard net/http transport for database downloads, so Go does not negotiate HTTP/2 or process HTTP/2 SETTINGS frames. Archive scans run with --network none and no server mode is enabled." - }, - { - "vulnerability": { - "name": "CVE-2026-97031" - }, - "products": [ - { - "@id": "pkg:golang/stdlib@v1.26.6" - } - ], - "status": "not_affected", - "justification": "vulnerable_code_not_in_execute_path", - "impact_statement": "The affected crypto/tls decodeInnerClientHello operation processes ECH ClientHello extensions on a TLS server. Code Polishy never starts a TLS server in the scanner: database download is an HTTPS client, and archive scans run without network access." } ] } diff --git a/docs/artifact-security.md b/docs/artifact-security.md index ee1b4931..b0e96500 100644 --- a/docs/artifact-security.md +++ b/docs/artifact-security.md @@ -6,11 +6,13 @@ OpenVEX, SBOM, report, or hardening scripts. ## Execution contract -The policy release pins an official Trivy image by digest and verifies that -digest, the expected Trivy version, the minimal scanner Dockerfile hash, and the -scanner runtime's OpenVEX hash before use. It copies only the Trivy binary from -that image into a non-root scratch image, with a release-owned CA bundle that -omits roots distrusted after the source image was built. +Each policy release carries Trivy built from its exact official Go module with +the release's pinned Go toolchain, for the host architecture's Linux platform. +The release manifest covers that binary, the minimal scanner Dockerfile, the +scanner's OpenVEX, and a release-owned CA bundle that omits distrusted roots. +The runtime copies only the binary and bundle into a non-root scratch image and +pulls no scanner image. A Docker server whose platform the release does not +carry fails before the scanner is built. The scanner image disables Go's HTTP/2 client. Its database download uses HTTPS over HTTP/1.1; target scanning remains offline. The release-owned diff --git a/docs/design/carried-trivy-scanner.md b/docs/design/carried-trivy-scanner.md new file mode 100644 index 00000000..bdad660f --- /dev/null +++ b/docs/design/carried-trivy-scanner.md @@ -0,0 +1,43 @@ +# Carried Trivy Scanner + +The scanner is Trivy 0.74.0, built by Code Polishy from the official Go module +`github.com/aquasecurity/trivy@v0.74.0` with the pinned Go 1.26.10. The module +proxy records that version at 2026-08-14T10:24:58Z from tag `v0.74.0`, commit +`e1fd17a0ea4a8cf24bc4b4dd7e2cfbf4bb31b994`, and the checksum database records +its content as `h1:B2/h1Aehsq/zykg0jxpWUlOX7e3UXpbxjZZs/0vNlC4=`, pinned in +`tools/trivy_module_sum.txt`. [Trivy 0.74.0](https://github.com/aquasecurity/trivy/releases/tag/v0.74.0) +meets the 30-day admission minimum, and its `go.mod` has no replacements. + +`tools/install-go-tools.sh` and `tools/install-policy-tools.ps1` run +`go install github.com/aquasecurity/trivy/cmd/trivy@v0.74.0` in a private module +and build cache, ignoring user Go configuration and private-module exceptions. +They fetch only from `proxy.golang.org`, verify against `sum.golang.org`, and +copy the official build's settings: `CGO_ENABLED=0`, `GOEXPERIMENT=jsonv2`, and +`-s -w -X github.com/aquasecurity/trivy/pkg/version/app.ver=0.74.0`, adding +`-trimpath`. The result is accepted only when its build information names +`go1.26.10-X:jsonv2` and exactly that module version and sum. The pinned module +content includes Trivy's `go.sum`, so the module graph is frozen. The +installer keeps the binary and Trivy's Apache-2.0 `LICENSE` in +`.tools/trivy/linux-` for the host architecture; Windows releases carry +`linux-amd64`. Release installation refuses a carried binary whose module +version differs from `tools/trivy-version.txt`, and the release manifest covers +its bytes, so the runtime trusts the installed release rather than rechecking +them. + +The resulting graph has the same 375 dependency versions and checksums as the +official binary in +`docker.io/aquasec/trivy@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969`. +Every one was published to the module proxy by 2026-08-13; the newest is +`golang.org/x/mod@v0.40.0`. A rebuild from the populated module cache with no +network proxy or checksum database produced the same binary +(`60a36e8a86456cbbc20a097f14552354001047a1a65b93ca1c0a2e84c9220860` for +`linux/arm64`). That rebuild reused the build cache, so it shows identical +inputs and link output rather than a cold reproduction. + +At runtime the scanner image copies the carried binary for the Docker server's +platform through a named build context. Docker pulls no scanner image, and the +installed release is only read. A release without a binary for that platform +fails before building. + +The scanner's vulnerability findings, trust bundle, and their review dates are +in [Trivy Scanner Assessments](trivy-scanner-assessments.md). diff --git a/docs/design/trivy-scanner-assessments.md b/docs/design/trivy-scanner-assessments.md index 673e0d86..cac7274e 100644 --- a/docs/design/trivy-scanner-assessments.md +++ b/docs/design/trivy-scanner-assessments.md @@ -1,31 +1,19 @@ # Trivy Scanner Assessments -Reviewed by Codex on October 17, 2026. Reassess by November 13, 2026, or whenever -the scanner image, invocation, transport, or advisory evidence changes. +Runtime, admission, and findings reviewed by Claude Opus 5.5 on October 22, 2026 +(UTC); the operation analysis below was first reviewed by Codex on October 17. +Reassess when Trivy 0.76.1 reaches the admission minimum at +2026-11-20T11:18:34Z, when the Go pin changes, or whenever the scanner +invocation, transport, or advisory evidence changes. ## Runtime and admission -The scanner uses official Trivy 0.74.0, published August 14, 2026, and built with -Go 1.26.6. Its source revision is -`e1fd17a0ea4a8cf24bc4b4dd7e2cfbf4bb31b994`. The image index is pinned to -`docker.io/aquasec/trivy@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969`. -The registry manifest and image labels bind that version and source revision; -Code Polishy copies only its executable into the scanner image, alongside the -release-owned trust bundle described below. - -[Trivy 0.74.0](https://github.com/aquasecurity/trivy/releases/tag/v0.74.0) meets -the 30-day admission minimum. Its updated Go runtime and dependencies remove -the older XML, ASN.1, URL, DNS, Go module, go-git, ORAS, SSH, and text findings. -In particular, offline image analysis parses package metadata through Trivy's -Java and NuGet XML parsers, so local archives are not a sound reason to dismiss -the older XML decoder vulnerability. - -Trivy 0.75.0 and 0.76.0 were published October 1 and October 14, respectively. -Neither is old enough at this review. The remaining findings have the specific -absent execution paths or transport mitigation below, so there is no need to -admit a fresh scanner. Review the official 0.76.0 release when it reaches the -minimum age on November 13; refresh the scan and remove assessments whose exact -package versions or findings no longer occur. +The scanner is Trivy 0.74.0, built from its official Go module with the pinned +Go 1.26.10 and carried in each release; see +[Carried Trivy Scanner](carried-trivy-scanner.md) for its provenance, build, +and runtime identity. Trivy 0.75.0 and 0.76.0 were published October 1 and +October 14 and are built with Go releases that lack the GO-2026-6809 fix. Trivy +0.76.1 is not yet aged. ## Enabled operations and inputs @@ -39,7 +27,7 @@ transport. It does not use ORAS extraction or a gRPC service. The scanner Dockerfile sets `GODEBUG=http2client=0`. Trivy's [`NewTransport`](https://github.com/aquasecurity/trivy/blob/e1fd17a0ea4a8cf24bc4b4dd7e2cfbf4bb31b994/pkg/x/http/transport.go) clones `http.DefaultTransport`; Go's -[`onceSetNextProtoDefaults`](https://github.com/golang/go/blob/go1.26.6/src/net/http/transport.go) +[`onceSetNextProtoDefaults`](https://github.com/golang/go/blob/go1.26.10/src/net/http/transport.go) honors that setting and does not configure the bundled HTTP/2 client. Database downloads therefore use HTTPS over HTTP/1.1. This removes an unnecessary protocol from the scanner's network surface without changing its database @@ -55,8 +43,10 @@ for declaring a reachable denial of service unaffected. ## Exact remaining findings -The October 17 database reports seven HIGH findings. Their exact package PURLs -and dispositions are in `artifact-security/scanner.openvex.json`. +The October 22 database reports four HIGH findings in the carried scanner. Its +Go 1.26.10 standard library removes the earlier `stdlib@v1.26.6` findings, +including GO-2026-6809. The exact package PURLs and dispositions are in +`artifact-security/scanner.openvex.json`. | Advisory | Package version | Applicable API and disposition | | -------------------------------------------------------------------------------------------------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -64,38 +54,34 @@ and dispositions are in `artifact-security/scanner.openvex.json`. | [CVE-2026-78669](https://pkg.go.dev/vuln/GO-2026-6611) | `golang.org/x/net@v0.58.0` | The affected HTTP/2 SETTINGS handling requires a live HTTP/2 transport. Database download uses the standard library transport, and archive scanning starts no gRPC or containerd connection. The standalone `x/net/http2` transport is not used by these operations. | | [CVE-2026-84304](https://github.com/grpc/grpc-go/security/advisories/GHSA-vp52-pcj8-j9qc) | `google.golang.org/grpc@v1.82.1` | Fragmented HTTP/2 DATA frames exhaust a live gRPC transport. Neither the local archive scanner nor the HTTP database client creates a gRPC connection. | | [CVE-2026-84445](https://github.com/grpc/grpc-go/security/advisories/GHSA-2v4p-qf9q-27wj) | `google.golang.org/grpc@v1.82.1` | Missing authority/Host headers panic xDS server routing. No gRPC or xDS server is started. | -| [CVE-2026-78667](https://pkg.go.dev/vuln/GO-2026-6609) | `stdlib@v1.26.6` | `ServeContent`, `ServeFile`, and file-serving handlers parse malicious Range headers. The scanner serves no HTTP requests and installs no file-serving client transport. | -| [CVE-2026-78669](https://pkg.go.dev/vuln/GO-2026-6611) | `stdlib@v1.26.6` | Repeated HTTP/2 window changes exhaust CPU in a client or server. The image disables the standard HTTP/2 client before Trivy starts, and no server mode is enabled. This assessment depends on that mitigation. | -| [CVE-2026-97031](https://pkg.go.dev/vuln/GO-2026-6607) | `stdlib@v1.26.6` | `decodeInnerClientHello` processes malicious ECH extensions on a TLS server. The scanner's TLS use is client-side database download; it accepts no TLS clients. | -## Scanner trust bundle and CVE-2026-111943 +The pinned `govulncheck` in binary mode reports the same eleven module +advisories for the carried and official binaries: HTTP/2 server and transport +issues in `golang.org/x/net`, gRPC server and xDS issues, and +`golang.org/x/crypto` SSH channel and OpenPGP issues. The source build changes +none of them. Advisories below the scanner's HIGH threshold are outside this +release gate. -Reviewed by Claude Opus 5.5 on October 22, 2026 (UTC). Reassess when Trivy -0.76.1, built with Go 1.27.3, reaches the admission minimum at -2026-11-20T11:18:34Z, or whenever eMudhra, Mozilla, or Go change the distrust -set. +## Scanner trust bundle [GO-2026-6809](https://pkg.go.dev/vuln/GO-2026-6809) affects `crypto/x509.Certificate.Verify` before Go 1.26.10 and 1.27.3. The [upstream fix](https://github.com/golang/go/commit/aa64342a9e13affffea3e989d67e574475af5a78) rejects any chain containing one of eight public keys from the compromised -eMudhra issuing CAs `EM DV/OV TLS CA - G3B`, `EM DV/EV TLS CA - G3C`, and -`EM OV CS CA - G3B/G3C`. Trivy 0.74.0 is built with Go 1.26.6, and its database -download is the scanner's only TLS client. A peer holding a compromised key and -an interception position on the database hosts could therefore serve a forged -database that suppresses blocking findings. - -The source image's bundle (119 roots, SHA-256 -`b8d837841b88bfaa1a0fa827cbca8e2576418dd47c9fc4bb7f1f9d89c83111b9`) trusts -`emSign ECC Root CA - G3`. That root +eMudhra issuing CAs. The carried scanner includes that fix. Its trust bundle +also removes the roots Mozilla distrusted, so the scanner does not depend on +the denylist alone. + +The official image's bundle trusts `emSign ECC Root CA - G3`, which [cross-signed `emSign Root TLS CA - G3`](https://crt.sh/?id=14658191778) (certificate SHA-256 -`3094025792039a68af6e553686410bdab7c0d4a36b9084cba64202e35d6ebbdd`), which -issued the compromised TLS CAs. Go 1.27.0 without the fix verifies all four -compromised TLS CAs to that root. The compromised CAs' other issuer, -`AAA Certificate Services`, is absent from the bundle. +`3094025792039a68af6e553686410bdab7c0d4a36b9084cba64202e35d6ebbdd`), the +issuer of the compromised TLS CAs. Unpatched Go 1.27.0 verifies all four +compromised TLS CAs to that root. -`artifact-security/scanner-ca-certificates.crt` is that source bundle without +`artifact-security/scanner-ca-certificates.crt` is the scanner's only trust +store. It is the official Trivy 0.74.0 image's bundle (119 roots, SHA-256 +`b8d837841b88bfaa1a0fa827cbca8e2576418dd47c9fc4bb7f1f9d89c83111b9`) without the three eMudhra roots that the [Mozilla bundle dated 2026-10-20](https://curl.se/ca/cacert.pem) removed: @@ -108,33 +94,20 @@ the three eMudhra roots that the The remaining 116 certificates keep their original bytes and order; the filtered bundle differs from the source only by deleted lines. Its SHA-256 is `faf05e99823ab205a6dd9416042ae3750f5a9e36de03d01d2cb4811097616bcc`. The bundle -adds no root and changes no other trust decision. It is frozen with the source -image, as the copied bundle already was. Replace it with the new image's bundle -when the scanner pin changes, then reapply only distrust that bundle lacks. +adds no root and changes no other trust decision. Refresh it from a current +Mozilla-derived bundle when the scanner pin changes, keeping only distrust that +bundle already applies. The database hosts do not depend on the removed roots. `ghcr.io` chains through Sectigo Public Server Authentication Root R46 to USERTrust RSA, and `pkg-containers.githubusercontent.com` chains through Let's Encrypt to ISRG -Root X1. On October 22 the rebuilt scanner downloaded +Root X1. On October 22 the carried scanner downloaded `ghcr.io/aquasecurity/trivy-db:2` through this bundle. Tests in `internal/artifactsecurity/trust_test.go` check that the bundle omits the three roots and leaves the cross-certificate without a trusted issuer, while a fixture copy of the removed root still verifies it. They also verify captured chains for both database hosts. -The finding remains affected and has no OpenVEX statement. Removing these roots -closes every path to the compromised keys found in Certificate Transparency, -but Certificate Transparency need not contain every CA certificate. An -undisclosed certificate from another bundled root to an eMudhra CA would reopen -the path. Go's public-key denylist does not depend on disclosure. No root-pool -restriction proves the same property: limiting the pool to the current database -roots would still trust Sectigo, which cross-signed these CAs from -`AAA Certificate Services`, and would break downloads after an ordinary host -certificate change. Rebuilding Trivy 0.74.0 with Go 1.26.10 would replace the -official binary with a locally built scanner and add a networked source build. -The aged Trivy release remains the resolution; this bundle reduces exposure -until then. - OpenVEX reconciliation still requires every statement to match an observed advisory and exact package PURL. Accepted findings remain in the scanner report. New or unmatched findings, changed findings, and unused statements remain diff --git a/docs/security/dependency-assessments.md b/docs/security/dependency-assessments.md index aafea358..ecf171c5 100644 --- a/docs/security/dependency-assessments.md +++ b/docs/security/dependency-assessments.md @@ -1,7 +1,8 @@ # Dependency vulnerability assessments Brace expansion and URI processing resolved with aged fixes on 2026-10-15; other JavaScript and Go module findings -reviewed on 2026-10-08; Go toolchain admission reviewed on 2026-10-21. +reviewed on 2026-10-08; Go toolchain admission reviewed on 2026-10-21; carried Trivy +build reviewed on 2026-10-22. Assessment owner: Codex. The vulnerability assessments cover `tools/javascript/pnpm-lock.yaml` and @@ -96,6 +97,21 @@ pins. Reassess immediately if upstream provenance changes or a regression or compromise is reported. Do not extend it to another toolchain version or weaken vulnerability scanning. +## Carried Trivy scanner + +The artifact scanner is now built from the aged official Trivy 0.74.0 module +with the Go 1.26.10 admitted above, instead of copying Trivy's Go 1.26.6 +binary. The scanner's database download used that binary's vulnerable +certificate verifier; its trust and the cost of waiting are assessed in +[Trivy Scanner Assessments](../design/trivy-scanner-assessments.md). This +admits no new dependency version. The build resolved the 375 module versions +and checksums that Trivy's checksummed `go.sum` already fixed and the official +binary already contained. The module proxy dates all of them on or before +2026-08-13, beyond the 30-day minimum. The `tools/trivy-version.txt` release-age +artifact and `tools/trivy_module_sum.txt` pin the same release. The Go toolchain +admission's expiry and removal conditions apply unchanged. Assessed by Claude Opus +5.5. + ## Brace expansion Both JavaScript graphs now resolve `brace-expansion` 1.1.21 and 5.0.12. diff --git a/docs/source-provenance.md b/docs/source-provenance.md index 255aae27..11f2ca9f 100644 --- a/docs/source-provenance.md +++ b/docs/source-provenance.md @@ -55,9 +55,11 @@ version before staging a release. the carried runtime. Their exact pins and checksum inventories ship with the release, while the online supply-chain gate resolves their release age from fixed upstream metadata services; neither comes from a target environment. -- Trivy is copied from an exact official image digest into the minimal scanner - image. `artifact-security/scanner-policy.json` records that external source - and its runtime configuration; the release manifest covers the repository-owned +- Trivy is built from the official `github.com/aquasecurity/trivy` module at the + pinned version and checksum in `tools/trivy_module_sum.txt`, with the pinned + Go toolchain, and carried with its license under `.tools/trivy`. + `artifact-security/scanner-policy.json` records its runtime configuration; + the release manifest covers the carried binary, the repository-owned Dockerfile, the scanner's trust bundle `artifact-security/scanner-ca-certificates.crt`, and `artifact-security/scanner.openvex.json`, which records reviewed diff --git a/internal/artifactsecurity/archive_test.go b/internal/artifactsecurity/archive_test.go index 94bf2fdd..8cc69ec3 100644 --- a/internal/artifactsecurity/archive_test.go +++ b/internal/artifactsecurity/archive_test.go @@ -73,10 +73,7 @@ func TestDockerArchiveRejectsMissingAmbiguousAndUnboundedIdentity(t *testing.T) func TestRunRejectsScannerConfigurationMismatch(t *testing.T) { root := t.TempDir() - policyRoot, err := filepath.Abs(filepath.Join("..", "..")) - if err != nil { - t.Fatal(err) - } + policyRoot := carriedScannerPolicyRoot(t) repo := repository.Repository{ Root: root, PolicyRoot: policyRoot, Config: policy.Config{SupplyChain: policy.SupplyChain{ArtifactSecurity: policy.ArtifactSecurity{ diff --git a/internal/artifactsecurity/artifactsecurity_test.go b/internal/artifactsecurity/artifactsecurity_test.go index 39f9dbde..265fd9c1 100644 --- a/internal/artifactsecurity/artifactsecurity_test.go +++ b/internal/artifactsecurity/artifactsecurity_test.go @@ -132,8 +132,8 @@ func TestWriteArtifactsPublishesHashedNormalizedEvidence(t *testing.T) { now := time.Now().UTC() scanned := scanOutput{ report: artifactReport{ - Version: 1, Target: "agent", Mode: "archive", Reference: "registry.example/agent:9.9.9", - ImageID: "sha256:" + strings.Repeat("d", 64), ScannerVersion: "0.72.0", SourceImage: "scanner@example", + Version: 2, Target: "agent", Mode: "archive", Reference: "registry.example/agent:9.9.9", + ImageID: "sha256:" + strings.Repeat("d", 64), ScannerVersion: "0.72.0", ArchiveSHA256: strings.Repeat("a", 64), DatabaseSHA256: strings.Repeat("b", 64), SBOMSHA256: strings.Repeat("c", 64), StartedAt: now.Add(-time.Second), CompletedAt: now, }, @@ -221,7 +221,7 @@ func readPublishedArtifactReport(t *testing.T, directory string) publishedArtifa func requirePublishedArtifactReport(t *testing.T, report publishedArtifactReport) { t.Helper() - if report.Version != 1 || report.Target != "agent" || report.Mode != "archive" || + if report.Version != 2 || report.Target != "agent" || report.Mode != "archive" || report.Reference != "registry.example/agent:9.9.9" || report.ImageID != "sha256:"+strings.Repeat("d", 64) || report.ScannerVersion != "0.72.0" || report.ArchiveSHA256 != strings.Repeat("a", 64) || report.DatabaseSHA256 != strings.Repeat("b", 64) || report.SBOMSHA256 != strings.Repeat("c", 64) || @@ -243,10 +243,7 @@ func requireMarkdownArtifactIdentity(t *testing.T, directory, reference, imageID func TestRunPublishesEvidenceThroughTheScannerBoundary(t *testing.T) { root := t.TempDir() - policyRoot, err := filepath.Abs(filepath.Join("..", "..")) - if err != nil { - t.Fatal(err) - } + policyRoot := carriedScannerPolicyRoot(t) archive := filepath.Join(root, "agent.tar") if err := os.WriteFile(archive, []byte("target image"), 0o600); err != nil { t.Fatal(err) @@ -329,6 +326,61 @@ func TestRunFailsClosedBeforeArtifactMutation(t *testing.T) { } } +func TestRunRefusesAReleaseWithoutTheCarriedScannerForTheDockerPlatform(t *testing.T) { + root := t.TempDir() + policyRoot := carriedScannerPolicyRoot(t) + if err := os.Remove(filepath.Join(policyRoot, ".tools", "trivy", "linux-amd64", "trivy")); err != nil { + t.Fatal(err) + } + repo := repository.Repository{ + Root: root, PolicyRoot: policyRoot, + Config: policy.Config{SupplyChain: policy.SupplyChain{ArtifactSecurity: policy.ArtifactSecurity{ + Targets: []policy.ArtifactTarget{{Name: "agent", Mode: "archive", Archive: "agent.tar"}}, + }}}, + } + result := Run(t.Context(), repo, newScannerBoundary(t, policyRoot)) + if len(result.Findings) != 1 || result.Findings[0].Subject != "scanner:scanner-runtime" || + !strings.Contains(result.Findings[0].Message, "carries no Trivy scanner for linux/amd64") { + t.Fatalf("missing carried scanner = %+v", result) + } +} + +func carriedScannerPolicyRoot(t *testing.T) string { + t.Helper() + source, err := filepath.Abs(filepath.Join("..", "..")) + if err != nil { + t.Fatal(err) + } + root := t.TempDir() + entries, err := os.ReadDir(filepath.Join(source, "artifact-security")) + if err != nil { + t.Fatal(err) + } + copied := []string{filepath.Join("tools", "trivy-version.txt")} + for _, entry := range entries { + copied = append(copied, filepath.Join("artifact-security", entry.Name())) + } + for _, relative := range copied { + payload, readErr := os.ReadFile(filepath.Join(source, relative)) + if readErr != nil { + t.Fatal(readErr) + } + writeTestFile(t, filepath.Join(root, relative), payload) + } + writeTestFile(t, filepath.Join(root, ".tools", "trivy", "linux-amd64", "trivy"), []byte("carried scanner")) + return root +} + +func writeTestFile(t *testing.T, path string, payload []byte) { + t.Helper() + if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(path, payload, 0o600); err != nil { + t.Fatal(err) + } +} + func TestPlannedArtifactAuthorizationIsExactAtTheSecurityBoundary(t *testing.T) { repo := repository.Repository{Config: policy.Config{SupplyChain: policy.SupplyChain{ArtifactSecurity: policy.ArtifactSecurity{Targets: []policy.ArtifactTarget{ {Name: "archive", Mode: "archive"}, @@ -485,13 +537,18 @@ func (boundary *scannerBoundary) imageCommand(arguments []string) (runner.Output return runner.Output{}, fmt.Errorf("missing image operation") } switch arguments[0] { - case "pull", "build", "rm": + case "build": + if label, _ := flagValue(arguments, "--label"); label != "com.code-polishy.security.role=scanner" { + return runner.Output{}, nil + } + if carried, _ := flagValue(arguments, "--build-context"); !strings.HasPrefix(carried, "trivy=") || + !strings.HasSuffix(filepath.ToSlash(carried), "/.tools/trivy/linux-amd64") { + return runner.Output{}, fmt.Errorf("scanner build omitted the carried Trivy for its platform: %v", arguments) + } + return runner.Output{}, nil + case "rm": return runner.Output{}, nil case "inspect": - format, _ := flagValue(arguments, "--format") - if strings.Contains(format, "RepoDigests") { - return runner.Output{Stdout: []byte(`["` + arguments[len(arguments)-1] + `"]` + "\n")}, nil - } return runner.Output{Stdout: []byte("sha256:" + strings.Repeat("b", 64) + "\n")}, nil case "save": path, ok := flagValue(arguments, "--output") diff --git a/internal/artifactsecurity/commands.go b/internal/artifactsecurity/commands.go index ae66b493..91483f80 100644 --- a/internal/artifactsecurity/commands.go +++ b/internal/artifactsecurity/commands.go @@ -17,8 +17,6 @@ func PlannedCommands(repo repository.Repository) []policy.Command { commands := []policy.Command{ plannedDockerCommand("preflight"), plannedDockerCommand("platform"), - plannedDockerCommand("pull-source"), - plannedDockerCommand("verify-source"), plannedDockerCommand("build-scanner"), } commands = appendScannerCommands(commands, "scanner-version") diff --git a/internal/artifactsecurity/run.go b/internal/artifactsecurity/run.go index 7930c7e9..4b1bb781 100644 --- a/internal/artifactsecurity/run.go +++ b/internal/artifactsecurity/run.go @@ -163,7 +163,7 @@ func artifactOperationalFinding(target, subject string, err error) policy.Findin func prepareScannerRuntime(ctx context.Context, repo repository.Repository, configured scannerPolicy, commandRunner runner.Runner) (*scannerRuntime, error) { client := dockerClient{root: repo.Root, executor: commandRunner} - platform, err := prepareScannerSource(ctx, client, configured.SourceImage) + platform, err := prepareScannerPlatform(ctx, client) if err != nil { return nil, err } @@ -178,21 +178,11 @@ func prepareScannerRuntime(ctx context.Context, repo repository.Repository, conf return state, nil } -func prepareScannerSource(ctx context.Context, client dockerClient, sourceImage string) (string, error) { +func prepareScannerPlatform(ctx context.Context, client dockerClient) (string, error) { if _, err := client.run(ctx, governedDockerCommandPrefix+"preflight", "version", "--format", "{{.Server.Version}}"); err != nil { return "", fmt.Errorf("docker server preflight: %w", err) } - platform, err := dockerPlatform(ctx, client, governedDockerCommandPrefix+"platform") - if err != nil { - return "", err - } - if _, err := client.run(ctx, governedDockerCommandPrefix+"pull-source", "image", "pull", "--platform", platform, sourceImage); err != nil { - return "", fmt.Errorf("pull pinned Trivy source image: %w", err) - } - if err := verifyPinnedSourceImage(ctx, client, governedDockerCommandPrefix+"verify-source", sourceImage); err != nil { - return "", err - } - return platform, nil + return dockerPlatform(ctx, client, governedDockerCommandPrefix+"platform") } func newScannerRuntime(client dockerClient, configured scannerPolicy, platform string) (*scannerRuntime, error) { @@ -227,9 +217,13 @@ func newScannerRuntime(client dockerClient, configured scannerPolicy, platform s func (state *scannerRuntime) buildAndVerify(ctx context.Context, policyRoot string) error { dockerfile := filepath.Join(policyRoot, filepath.FromSlash(scannerDockerfilePath)) contextRoot := filepath.Dir(dockerfile) + carried := filepath.Join(policyRoot, ".tools", "trivy", strings.ReplaceAll(state.platform, "/", "-")) + if info, err := os.Lstat(filepath.Join(carried, "trivy")); err != nil || !info.Mode().IsRegular() { + return fmt.Errorf("this Code Polishy release carries no Trivy scanner for %s", state.platform) + } _, err := state.client.run(ctx, governedDockerCommandPrefix+"build-scanner", "image", "build", "--network", "none", "--pull=false", "--provenance=false", - "--platform", state.platform, "--file", dockerfile, + "--platform", state.platform, "--file", dockerfile, "--build-context", "trivy="+carried, "--label", "com.code-polishy.security.role=scanner", "--tag", state.image, contextRoot, ) if err != nil { @@ -260,18 +254,6 @@ func dockerPlatform(ctx context.Context, client dockerClient, name string) (stri } } -func verifyPinnedSourceImage(ctx context.Context, client dockerClient, name, image string) error { - result, err := client.run(ctx, name, "image", "inspect", "--format", "{{json .RepoDigests}}", image) - if err != nil { - return err - } - _, digest, _ := strings.Cut(image, "@") - if !strings.Contains(result.stdout, "@"+digest) { - return errors.New("pulled Trivy source image did not retain the configured digest") - } - return nil -} - func currentUserID() string { current, err := user.Current() if err != nil { diff --git a/internal/artifactsecurity/scan.go b/internal/artifactsecurity/scan.go index 3e0b5d2b..4eca16d8 100644 --- a/internal/artifactsecurity/scan.go +++ b/internal/artifactsecurity/scan.go @@ -207,9 +207,8 @@ func buildScanOutput( } return scanOutput{ report: artifactReport{ - Version: 1, Target: target.Name, Mode: artifact.mode, Reference: artifact.reference, ImageID: artifact.imageID, - ScannerVersion: state.policy.TrivyVersion, - SourceImage: state.policy.SourceImage, DatabaseSHA256: database.SHA256, + Version: 2, Target: target.Name, Mode: artifact.mode, Reference: artifact.reference, ImageID: artifact.imageID, + ScannerVersion: state.policy.TrivyVersion, DatabaseSHA256: database.SHA256, ArchiveSHA256: archiveHash, SBOMSHA256: bytesSHA256(sbom), StartedAt: started, CompletedAt: time.Now().UTC(), Findings: blocking, AcceptedFindings: accepted, }, diff --git a/internal/artifactsecurity/types.go b/internal/artifactsecurity/types.go index 38f424f2..920308a3 100644 --- a/internal/artifactsecurity/types.go +++ b/internal/artifactsecurity/types.go @@ -41,7 +41,6 @@ type Result struct { type scannerPolicy struct { Version int `json:"version"` TrivyVersion string `json:"trivyVersion"` - SourceImage string `json:"sourceImage"` DatabaseRepository string `json:"databaseRepository"` DatabaseSchemaVersion int `json:"databaseSchemaVersion"` DatabaseMaximumAgeHours int `json:"databaseMaximumAgeHours"` @@ -77,7 +76,6 @@ type artifactReport struct { Reference string `json:"reference"` ImageID string `json:"imageId"` ScannerVersion string `json:"scannerVersion"` - SourceImage string `json:"sourceImage"` DatabaseSHA256 string `json:"databaseSha256"` ArchiveSHA256 string `json:"archiveSha256"` SBOMSHA256 string `json:"sbomSha256"` @@ -144,19 +142,12 @@ func (configured scannerPolicy) validateFields(evidence scannerPolicyEvidence) e } func (configured scannerPolicy) validateProvenanceFields(evidence scannerPolicyEvidence) error { - if configured.Version != 2 { - return errors.New("artifact scanner policy version must be 2") + if configured.Version != 3 { + return errors.New("artifact scanner policy version must be 3") } if evidence.versionText != configured.TrivyVersion { return errors.New("artifact scanner policy and version pin do not agree") } - const imagePrefix = "docker.io/aquasec/trivy@sha256:" - if !strings.HasPrefix(configured.SourceImage, imagePrefix) { - return errors.New("artifact scanner source image must use the exact official Trivy digest") - } - if !exactDigest.MatchString(strings.TrimPrefix(configured.SourceImage, imagePrefix)) { - return errors.New("artifact scanner source image digest must contain 64 lowercase hexadecimal characters") - } if configured.DatabaseRepository != fmt.Sprintf("ghcr.io/aquasecurity/trivy-db:%d", configured.DatabaseSchemaVersion) { return errors.New("artifact scanner database policy is invalid") } diff --git a/scripts/build-release.ps1 b/scripts/build-release.ps1 index 1d7348c1..545eaffd 100644 --- a/scripts/build-release.ps1 +++ b/scripts/build-release.ps1 @@ -128,7 +128,7 @@ try { 'tools/trivy-version.txt','tools/javascript_bundle_inventory.txt','tools/javascript_runtime_binaries.txt', 'tools/javascript_runtime_checksums.txt','tools/windows_tool_checksums.txt', '.tools/go/windows-amd64','.tools/shellcheck/windows-x86_64','.tools/javascript/windows-x64','.tools/python/windows-x64', - '.tools/rust/windows-x64' + '.tools/rust/windows-x64','.tools/trivy/linux-amd64' )) { Copy-ReleasePath $Relative } Assert-ReleasePythonCarrier $Stage diff --git a/scripts/install.sh b/scripts/install.sh index 6ced6997..7abfd4ac 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -332,6 +332,7 @@ policy_tools=( ".tools/bin/osv-scanner" ".tools/bin/ruff" ".tools/bin/ty" + ".tools/trivy/linux-${go_arch_tag}" ) carried_tools=( @@ -353,6 +354,7 @@ carried_tools=( "rustdoc:tools/rust-version.txt" "cargo:tools/rust-version.txt" "cargo-nextest:tools/cargo-nextest-version.txt" + "trivy:tools/trivy-version.txt" ) carried_markers=( @@ -406,6 +408,11 @@ probed_version() { "${policy_root}/.tools/bin/$1" | awk '$1 == "mod" { sub(/^v/, "", $3); print $3; exit }' ;; + trivy) + javascript_sealed_run "${policy_root}/${go_tool_dir}/go/bin/go" version -m \ + "${policy_root}/.tools/trivy/linux-${go_arch_tag}/trivy" | + awk '$1 == "mod" && $2 == "github.com/aquasecurity/trivy" { sub(/^v/, "", $3); print $3; exit }' + ;; osv-scanner) javascript_sealed_run "${policy_root}/.tools/bin/osv-scanner" --version | awk '/^osv-scanner version:/ { print $3 }' diff --git a/scripts/test-install.sh b/scripts/test-install.sh index 22b6d0b7..6155a309 100755 --- a/scripts/test-install.sh +++ b/scripts/test-install.sh @@ -351,6 +351,7 @@ if [[ "\${1:-}" == "version" && "\${2:-}" == "-m" ]]; then staticcheck) printf '\tmod\thonnef.co/go/tools\tv0.7.0\th1:disposable\n' ;; deadcode) printf '\tmod\tgolang.org/x/tools\tv0.50.0\th1:disposable\n' ;; govulncheck) printf '\tmod\tgolang.org/x/vuln\tv1.3.0\th1:disposable\n' ;; + trivy) printf '\tmod\tgithub.com/aquasecurity/trivy\tv%s\th1:disposable\n' "\$(cat "\${3%/*}/fixture-version")" ;; *) echo "unexpected stub go module probe: \$*" >&2 exit 1 @@ -405,6 +406,9 @@ EOF write_stub_tool "${source_root}/.tools/bin/staticcheck" write_stub_tool "${source_root}/.tools/bin/deadcode" write_stub_tool "${source_root}/.tools/bin/govulncheck" + write_stub_tool "${source_root}/.tools/trivy/linux-${go_platform_tag##*-}/trivy" + printf 'fixture license\n' >"${source_root}/.tools/trivy/linux-${go_platform_tag##*-}/LICENSE" + printf '0.72.0\n' >"${source_root}/.tools/trivy/linux-${go_platform_tag##*-}/fixture-version" "${real_git}" -C "${source_root}" init --quiet "${real_git}" -C "${source_root}" config user.email "install-test@example.invalid" @@ -579,7 +583,8 @@ for required in bin/code-polishy bin/code-polishy-launcher VERSION LICENSE READM ".tools/go/${go_platform_tag}/go/bin/gofmt" \ ".tools/shellcheck/${shellcheck_platform_tag}/shellcheck" \ ".tools/rust/${platform_tag}/bin/rustc" ".tools/rust/${platform_tag}/bin/cargo-clippy" \ - .tools/bin/staticcheck .tools/bin/deadcode .tools/bin/govulncheck .tools/bin/osv-scanner .tools/bin/ruff .tools/bin/ty .tools/bin/cargo-nextest; do + .tools/bin/staticcheck .tools/bin/deadcode .tools/bin/govulncheck .tools/bin/osv-scanner .tools/bin/ruff .tools/bin/ty .tools/bin/cargo-nextest \ + ".tools/trivy/linux-${go_platform_tag##*-}/trivy" ".tools/trivy/linux-${go_platform_tag##*-}/LICENSE"; do [[ -e "${release}/${required}" ]] || fail "the release is missing ${required}" done [[ ! -e "${release}/scripts/automation" ]] || @@ -712,6 +717,17 @@ mv "${fixture_root}/held-osv-scanner" "${source_root}/.tools/bin/osv-scanner" [[ "$(installed_release_count)" == "1" ]] || fail "a checkout missing a pinned tool changed what is installed" +carried_trivy="${source_root}/.tools/trivy/linux-${go_platform_tag##*-}" +printf '0.72.1\n' >"${carried_trivy}/fixture-version" +if install_release >"${fixture_root}/trivy-version.log" 2>&1; then + fail "a checkout carrying the wrong Trivy installed a release" +fi +grep -q "trivy.*0.72.1" "${fixture_root}/trivy-version.log" || + fail "the wrong carried Trivy was refused without naming it" +printf '0.72.0\n' >"${carried_trivy}/fixture-version" +[[ "$(installed_release_count)" == "1" ]] || + fail "a checkout carrying the wrong Trivy changed what is installed" + for rust_tool in rustc rustdoc cargo; do write_version_tool "${source_root}/.tools/rust/${platform_tag}/bin/${rust_tool}" --version "${rust_tool} 1.97.2 (fixture)" if install_release >"${fixture_root}/rust-version.log" 2>&1; then diff --git a/tools/install-go-tools.sh b/tools/install-go-tools.sh index 2f4d3716..541f40a9 100755 --- a/tools/install-go-tools.sh +++ b/tools/install-go-tools.sh @@ -5,13 +5,60 @@ repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" staticcheck_version="$(tr -d '[:space:]' <"${repo_root}/tools/staticcheck-version.txt")" govulncheck_version="$(tr -d '[:space:]' <"${repo_root}/tools/govulncheck-version.txt")" deadcode_version="$(tr -d '[:space:]' <"${repo_root}/tools/deadcode-version.txt")" +trivy_version="$(tr -d '[:space:]' <"${repo_root}/tools/trivy-version.txt")" +trivy_module_sum="$(tr -d '[:space:]' <"${repo_root}/tools/trivy_module_sum.txt")" +go_version="$(tr -d '[:space:]' <"${repo_root}/scripts/go_version.txt")" mkdir -p "${repo_root}/.tools/bin" export GOBIN="${repo_root}/.tools/bin" "${repo_root}/scripts/go.sh" install "honnef.co/go/tools/cmd/staticcheck@${staticcheck_version}" "${repo_root}/scripts/go.sh" install "golang.org/x/vuln/cmd/govulncheck@${govulncheck_version}" "${repo_root}/scripts/go.sh" install "golang.org/x/tools/cmd/deadcode@${deadcode_version}" +unset GOBIN + +case "$(uname -m)" in + arm64|aarch64) trivy_arch="arm64" ;; + x86_64|amd64) trivy_arch="amd64" ;; + *) echo "Unsupported architecture for the carried Trivy scanner: $(uname -m)" >&2; exit 1 ;; +esac +trivy_dir="${repo_root}/.tools/trivy/linux-${trivy_arch}" +trivy_expected="go${go_version#go}-X:jsonv2 github.com/aquasecurity/trivy v${trivy_version} ${trivy_module_sum}" + +trivy_identity() { + "${repo_root}/scripts/go.sh" version -m "$1" 2>/dev/null | + awk 'NR == 1 { identity = $NF } $1 == "mod" { identity = identity " " $2 " " $3 " " $4 } END { print identity }' +} + +if [[ -f "${trivy_dir}/trivy" && -f "${trivy_dir}/LICENSE" && "$(trivy_identity "${trivy_dir}/trivy")" == "${trivy_expected}" ]]; then + echo "Trivy ${trivy_version} for linux/${trivy_arch} is already built at ${trivy_dir}." +else + trivy_scratch="$(mktemp -d "${TMPDIR:-/tmp}/code-polishy-trivy.XXXXXX")" + trap 'rm -rf "${trivy_scratch}"' EXIT + echo "Building Trivy ${trivy_version} for linux/${trivy_arch} from its Go module..." + env -u GONOSUMDB -u GONOSUMCHECK -u GOPRIVATE -u GONOPROXY -u GOINSECURE -u GOWORK \ + GOENV=off GOPATH="${trivy_scratch}/gopath" GOMODCACHE="${trivy_scratch}/gopath/pkg/mod" GOCACHE="${trivy_scratch}/gocache" \ + GOPROXY=https://proxy.golang.org GOSUMDB=sum.golang.org GOFLAGS=-modcacherw \ + GOOS=linux GOARCH="${trivy_arch}" CGO_ENABLED=0 GOEXPERIMENT=jsonv2 \ + "${repo_root}/scripts/go.sh" install -trimpath \ + -ldflags="-s -w -extldflags '-static' -X github.com/aquasecurity/trivy/pkg/version/app.ver=${trivy_version}" \ + "github.com/aquasecurity/trivy/cmd/trivy@v${trivy_version}" + built="${trivy_scratch}/gopath/bin/linux_${trivy_arch}/trivy" + [[ -f "${built}" ]] || built="${trivy_scratch}/gopath/bin/trivy" + if [[ "$(trivy_identity "${built}")" != "${trivy_expected}" ]]; then + echo "The built Trivy does not match ${trivy_expected}." >&2 + exit 1 + fi + mkdir -p "${trivy_scratch}/stage" + cp "${built}" "${trivy_scratch}/stage/trivy" + cp "${trivy_scratch}/gopath/pkg/mod/github.com/aquasecurity/trivy@v${trivy_version}/LICENSE" "${trivy_scratch}/stage/LICENSE" + chmod 0755 "${trivy_scratch}/stage/trivy" + chmod 0644 "${trivy_scratch}/stage/LICENSE" + mkdir -p "$(dirname "${trivy_dir}")" + rm -rf "${trivy_dir}" + mv "${trivy_scratch}/stage" "${trivy_dir}" +fi "${repo_root}/.tools/bin/staticcheck" -version "${repo_root}/.tools/bin/govulncheck" -version "${repo_root}/scripts/go.sh" version -m "${repo_root}/.tools/bin/deadcode" +echo "trivy $(trivy_identity "${trivy_dir}/trivy")" diff --git a/tools/install-policy-tools.ps1 b/tools/install-policy-tools.ps1 index e30382a9..227aa967 100644 --- a/tools/install-policy-tools.ps1 +++ b/tools/install-policy-tools.ps1 @@ -102,6 +102,51 @@ try { & $Go install "golang.org/x/vuln/cmd/govulncheck@$(Read-Pin 'tools/govulncheck-version.txt')" } finally { $env:GOBIN = $SavedGoBin } + $TrivyVersion = Read-Pin 'tools/trivy-version.txt' + $TrivyExpected = "go$GoVersion-X:jsonv2 github.com/aquasecurity/trivy v$TrivyVersion $(Read-Pin 'tools/trivy_module_sum.txt')" + $TrivyRoot = Join-Path $PolicyRoot '.tools/trivy/linux-amd64' + function Get-TrivyIdentity([string]$Path) { + if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { return '' } + $Lines = @(& $Go version -m $Path 2>$null) + if ($LASTEXITCODE -ne 0 -or $Lines.Count -eq 0) { return '' } + $Identity = @(($Lines[0].Trim() -split '\s+')[-1]) + foreach ($Line in $Lines) { + $Fields = @($Line.Trim() -split '\s+') + if ($Fields[0] -ceq 'mod') { $Identity += $Fields[1..3] } + } + return $Identity -join ' ' + } + if ((Get-TrivyIdentity (Join-Path $TrivyRoot 'trivy')) -cne $TrivyExpected -or + -not (Test-Path -LiteralPath (Join-Path $TrivyRoot 'LICENSE') -PathType Leaf)) { + $TrivyScratch = Join-Path $Scratch 'trivy' + $TrivyEnvironment = [ordered]@{ + GOENV = 'off'; GOPATH = (Join-Path $TrivyScratch 'gopath'); GOMODCACHE = (Join-Path $TrivyScratch 'gopath/pkg/mod') + GOCACHE = (Join-Path $TrivyScratch 'gocache'); GOPROXY = 'https://proxy.golang.org'; GOSUMDB = 'sum.golang.org' + GOFLAGS = '-modcacherw'; GOOS = 'linux'; GOARCH = 'amd64'; CGO_ENABLED = '0'; GOEXPERIMENT = 'jsonv2' + GONOSUMDB = $null; GONOSUMCHECK = $null; GOPRIVATE = $null; GONOPROXY = $null; GOINSECURE = $null; GOWORK = $null; GOBIN = $null + } + $SavedTrivyEnvironment = @{} + foreach ($Name in $TrivyEnvironment.Keys) { + $SavedTrivyEnvironment[$Name] = [Environment]::GetEnvironmentVariable($Name) + [Environment]::SetEnvironmentVariable($Name, $TrivyEnvironment[$Name]) + } + try { + & $Go install -trimpath "-ldflags=-s -w -extldflags '-static' -X github.com/aquasecurity/trivy/pkg/version/app.ver=$TrivyVersion" "github.com/aquasecurity/trivy/cmd/trivy@v$TrivyVersion" + if ($LASTEXITCODE -ne 0) { throw 'Trivy source build failed.' } + } finally { + foreach ($Name in $SavedTrivyEnvironment.Keys) { + [Environment]::SetEnvironmentVariable($Name, $SavedTrivyEnvironment[$Name]) + } + } + $BuiltTrivy = Join-Path $TrivyScratch 'gopath/bin/linux_amd64/trivy' + if ((Get-TrivyIdentity $BuiltTrivy) -cne $TrivyExpected) { throw "The built Trivy does not match $TrivyExpected." } + $TrivyStage = Join-Path $TrivyScratch 'stage' + New-Item -ItemType Directory -Path $TrivyStage | Out-Null + Copy-Item -LiteralPath $BuiltTrivy -Destination (Join-Path $TrivyStage 'trivy') + Copy-Item -LiteralPath (Join-Path $TrivyScratch "gopath/pkg/mod/github.com/aquasecurity/trivy@v$TrivyVersion/LICENSE") -Destination (Join-Path $TrivyStage 'LICENSE') + Replace-Directory $TrivyStage $TrivyRoot + } + $NodeVersion = Read-Pin 'tools/node-version.txt' $NodeAsset = "node-v$NodeVersion-win-x64.zip" $NodeArchive = Get-Verified $NodeAsset "https://nodejs.org/dist/v$NodeVersion/$NodeAsset" @@ -502,6 +547,7 @@ with zipfile.ZipFile(wheel) as archive: & (Join-Path $Bin 'staticcheck.exe') -version & $Go version -m (Join-Path $Bin 'deadcode.exe') & (Join-Path $Bin 'govulncheck.exe') -version + Write-Host "trivy $(Get-TrivyIdentity (Join-Path $TrivyRoot 'trivy'))" & (Join-Path $Bin 'osv-scanner.exe') --version & (Join-Path $Bin 'ruff.exe') --version & (Join-Path $Bin 'ty.exe') --version diff --git a/tools/trivy_module_sum.txt b/tools/trivy_module_sum.txt new file mode 100644 index 00000000..34068585 --- /dev/null +++ b/tools/trivy_module_sum.txt @@ -0,0 +1 @@ +h1:B2/h1Aehsq/zykg0jxpWUlOX7e3UXpbxjZZs/0vNlC4= From b73e1f69f72b49b4506fc745da71349bcc8cbe4e Mon Sep 17 00:00:00 2001 From: Jeff Musgrave <1578618+JeffMusgrave@users.noreply.github.com> Date: Wed, 21 Oct 2026 23:16:27 -0400 Subject: [PATCH 06/15] Inventory the carried Trivy and its module notices in releases Release manifest version 9 records the carried Trivy version, and version 8 manifests remain readable with their exact smaller tool inventory. The release SBOM lists Trivy with every Go module its build information names and refuses a carried binary without build information or a module without a license notice. The tool installers copy each linked module's license, copying, notice, and patent files from the checksum-verified module cache. Record the scanner's Go advisories below the HIGH gate against their exact preconditions and the three enabled Trivy operations, including the HTTP/2 configurers, the containerd daemon-only path, and the March 2026 Trivy release compromise. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 3 + cmd/code-polishy-launcher/main_test.go | 2 +- cmd/code-polishy/main_test.go | 2 +- cmd/code-polishy/upgrade_meta_test.go | 2 +- docs/design/carried-trivy-scanner.md | 23 +++- docs/design/trivy-scanner-assessments.md | 54 +++++++- docs/installation.md | 9 +- docs/policy-engine-architecture.md | 2 +- docs/source-provenance.md | 3 +- internal/release/capability_proof.go | 8 +- internal/release/launcher_manifest.go | 29 +++- internal/release/launcher_manifest_test.go | 24 +++- internal/release/manifest_writer.go | 10 +- internal/release/manifest_writer_test.go | 2 +- internal/release/publication_test.go | 21 +++ internal/release/release.go | 9 +- internal/release/release_test.go | 7 +- internal/release/sbom.go | 6 + internal/release/trivy_sbom.go | 83 +++++++++++ internal/release/trivy_sbom_test.go | 153 +++++++++++++++++++++ scripts/test-install.sh | 9 +- tools/install-go-tools.sh | 32 ++++- tools/install-policy-tools.ps1 | 22 ++- 23 files changed, 470 insertions(+), 45 deletions(-) create mode 100644 internal/release/trivy_sbom.go create mode 100644 internal/release/trivy_sbom_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 6153682d..dc9904cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ version 3 and scanner reports are version 2, without the `sourceImage` field. - Give the scanner a release-owned trust bundle that omits the eMudhra roots Mozilla distrusted on 2026-10-20. +- Record the carried Trivy in release manifest version 9 and in the release + SBOM with every Go module linked into it, and carry each module's license + and notice files. ## 0.30.0 - 2026-10-20 diff --git a/cmd/code-polishy-launcher/main_test.go b/cmd/code-polishy-launcher/main_test.go index 048d69ec..952a90e6 100644 --- a/cmd/code-polishy-launcher/main_test.go +++ b/cmd/code-polishy-launcher/main_test.go @@ -52,7 +52,7 @@ func (installed store) install(t *testing.T, revision, engine string) release.Lo Tools: release.Tools{ Go: "1.26.6", Govulncheck: "1.3.0", Node: "24.18.0", OSVScanner: "2.4.0", PNPM: "11.13.0", Packaging: "26.3", Python: "3.12.13+20260728", Ruff: "0.16.0", Shellcheck: "0.11.0", Staticcheck: "0.7.0", Deadcode: "0.50.0", - Ty: "0.0.65", Vulture: "2.16", Rust: "1.97.1", Nextest: "0.9.138", + Ty: "0.0.65", Vulture: "2.16", Rust: "1.97.1", Nextest: "0.9.138", Trivy: "0.74.0", }, Entries: []release.Entry{ {Path: bundleLink, Symlink: "../.pnpm/tool"}, diff --git a/cmd/code-polishy/main_test.go b/cmd/code-polishy/main_test.go index 08d4d7f2..ea7464f7 100644 --- a/cmd/code-polishy/main_test.go +++ b/cmd/code-polishy/main_test.go @@ -771,7 +771,7 @@ func installedRelease(t *testing.T, revision string) string { Tools: release.Tools{ Go: "1.26.6", Govulncheck: "1.3.0", Node: "24.18.0", OSVScanner: "2.4.0", PNPM: "11.13.0", Packaging: "26.3", Python: "3.12.13+20260728", Ruff: "0.16.0", Shellcheck: "0.11.0", Staticcheck: "0.7.0", Deadcode: "0.50.0", - Ty: "0.0.65", Vulture: "2.16", Rust: "1.97.1", Nextest: "0.9.138", + Ty: "0.0.65", Vulture: "2.16", Rust: "1.97.1", Nextest: "0.9.138", Trivy: "0.74.0", }, CapabilityCatalogSHA256: hex.EncodeToString(catalogDigest[:]), Entries: []release.Entry{ diff --git a/cmd/code-polishy/upgrade_meta_test.go b/cmd/code-polishy/upgrade_meta_test.go index eaa343fb..9055c046 100644 --- a/cmd/code-polishy/upgrade_meta_test.go +++ b/cmd/code-polishy/upgrade_meta_test.go @@ -76,7 +76,7 @@ func historicalUpgradeCLIRelease(t *testing.T, root string) release.Lock { t.Fatal(err) } manifest.ManifestVersion = 6 - manifest.Tools.Rust, manifest.Tools.Nextest, manifest.Tools.Deadcode = "", "", "" + manifest.Tools.Rust, manifest.Tools.Nextest, manifest.Tools.Deadcode, manifest.Tools.Trivy = "", "", "", "" manifest.ReleaseDigest = manifest.Identity() data, err := json.Marshal(manifest) if err != nil { diff --git a/docs/design/carried-trivy-scanner.md b/docs/design/carried-trivy-scanner.md index bdad660f..d9a039bc 100644 --- a/docs/design/carried-trivy-scanner.md +++ b/docs/design/carried-trivy-scanner.md @@ -17,12 +17,23 @@ copy the official build's settings: `CGO_ENABLED=0`, `GOEXPERIMENT=jsonv2`, and `-trimpath`. The result is accepted only when its build information names `go1.26.10-X:jsonv2` and exactly that module version and sum. The pinned module content includes Trivy's `go.sum`, so the module graph is frozen. The -installer keeps the binary and Trivy's Apache-2.0 `LICENSE` in -`.tools/trivy/linux-` for the host architecture; Windows releases carry -`linux-amd64`. Release installation refuses a carried binary whose module -version differs from `tools/trivy-version.txt`, and the release manifest covers -its bytes, so the runtime trusts the installed release rather than rechecking -them. +installer keeps the binary in `.tools/trivy/linux-` for the host +architecture; Windows releases carry `linux-amd64`. Release installation +refuses a carried binary whose module version differs from +`tools/trivy-version.txt`, and the release manifest covers its bytes, so the +runtime trusts the installed release rather than rechecking them. + +Distributing the binary distributes every module linked into it. For each +module the binary's build information names, including Trivy itself, the +installer copies the license, copying, notice, and patent files at that +module's root in the checksum-verified module cache into +`licenses/@/`. A module without a license or +copying file, or a replaced module, fails the build. Release manifest version +9 records Trivy's version as a tool. The release SBOM reads the carried +binary's build information, lists Trivy with every linked module as its +dependency, and records each module's notice paths. A release whose carried +binary lacks build information or a module's license notice cannot produce an +SBOM. The resulting graph has the same 375 dependency versions and checksums as the official binary in diff --git a/docs/design/trivy-scanner-assessments.md b/docs/design/trivy-scanner-assessments.md index cac7274e..ccf921a8 100644 --- a/docs/design/trivy-scanner-assessments.md +++ b/docs/design/trivy-scanner-assessments.md @@ -55,12 +55,54 @@ including GO-2026-6809. The exact package PURLs and dispositions are in | [CVE-2026-84304](https://github.com/grpc/grpc-go/security/advisories/GHSA-vp52-pcj8-j9qc) | `google.golang.org/grpc@v1.82.1` | Fragmented HTTP/2 DATA frames exhaust a live gRPC transport. Neither the local archive scanner nor the HTTP database client creates a gRPC connection. | | [CVE-2026-84445](https://github.com/grpc/grpc-go/security/advisories/GHSA-2v4p-qf9q-27wj) | `google.golang.org/grpc@v1.82.1` | Missing authority/Host headers panic xDS server routing. No gRPC or xDS server is started. | -The pinned `govulncheck` in binary mode reports the same eleven module -advisories for the carried and official binaries: HTTP/2 server and transport -issues in `golang.org/x/net`, gRPC server and xDS issues, and -`golang.org/x/crypto` SSH channel and OpenPGP issues. The source build changes -none of them. Advisories below the scanner's HIGH threshold are outside this -release gate. +## Go module advisories below the scanner gate + +The pinned `govulncheck` reports further Go advisories for the carried binary. +In binary mode it finds eleven whose symbols are present and six more whose +modules are present; source mode over Trivy's exact module reports +whole-program reachability through command registration and package +initializers. Neither result establishes which code runs in Code Polishy's +invocations, so each advisory below is assessed against its precondition and +the three enabled operations: + +- `trivy version`, with no network. +- `trivy image --download-db-only`, which returns after the database download + in [`initDB`](https://github.com/aquasecurity/trivy/blob/e1fd17a0ea4a8cf24bc4b4dd7e2cfbf4bb31b994/pkg/commands/artifact/run.go). The download clones + `http.DefaultTransport` in [`NewTransport`](https://github.com/aquasecurity/trivy/blob/e1fd17a0ea4a8cf24bc4b4dd7e2cfbf4bb31b994/pkg/x/http/transport.go). + That transport sets `ForceAttemptHTTP2`, but Go 1.26.10's + `onceSetNextProtoDefaults` and ALPN selection honor `GODEBUG=http2client=0`, + so the bundled standard-library HTTP/2 client is neither configured nor + offered. Trivy itself imports no `golang.org/x/net/http2`. Of the linked + modules, only the Azure SDK configures `x/net/http2` at startup, on its own + private client; Google authentication and API transports and the Kubernetes + client configure it when their clients are built. No linked code configures + `http.DefaultTransport`. The registry credential helpers in + [`authOptions`](https://github.com/aquasecurity/trivy/blob/e1fd17a0ea4a8cf24bc4b4dd7e2cfbf4bb31b994/pkg/remote/remote.go) match only Google, Azure, and AWS + registry domains, so `ghcr.io` builds none of those clients. +- `trivy image --input` with `--scanners vuln,secret` and + `--image-config-scanners misconfig,secret`, in a container without network. + [`ScanImage`](https://github.com/aquasecurity/trivy/blob/e1fd17a0ea4a8cf24bc4b4dd7e2cfbf4bb31b994/pkg/commands/artifact/run.go) selects + `archiveStandaloneScanService`, and + [`NewArchiveImage`](https://github.com/aquasecurity/trivy/blob/e1fd17a0ea4a8cf24bc4b4dd7e2cfbf4bb31b994/pkg/fanal/image/archive.go) opens the archive with + `go-containerregistry` Docker-archive and OCI-layout readers. No SBOM source + is configured, so remote SBOM and Rekor lookups do not run. + +| Advisory | Module | Precondition | Disposition | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [GO-2026-6603](https://pkg.go.dev/vuln/GO-2026-6603), [GO-2026-6612](https://pkg.go.dev/vuln/GO-2026-6612), [GO-2026-6617](https://pkg.go.dev/vuln/GO-2026-6617) | `golang.org/x/net@v0.58.0` | An `x/net/http2` server receiving requests. | Not exposed: no operation starts an HTTP server. | +| [GO-2026-6610](https://pkg.go.dev/vuln/GO-2026-6610) | `golang.org/x/net@v0.58.0` | An `x/net/http2` transport response forwarded to an HTTP/1 client. | Not exposed: the only `x/net/http2` transport configured in these operations is the Azure SDK's unused private client, and nothing is proxied. The standard-library copy is fixed in Go 1.26.9. | +| [GO-2026-6611](https://pkg.go.dev/vuln/GO-2026-6611), [GO-2026-6348](https://pkg.go.dev/vuln/GO-2026-6348), [GO-2026-6443](https://pkg.go.dev/vuln/GO-2026-6443), [GO-2026-6791](https://pkg.go.dev/vuln/GO-2026-6791) | `x/net`, `grpc`, `containerd` | As above. | Reported HIGH by the scanner; assessed in OpenVEX. | +| [GO-2026-6441](https://pkg.go.dev/vuln/GO-2026-6441) | `google.golang.org/grpc@v1.82.1` | A gRPC xDS server applying RBAC HTTP filters. | Not exposed: no gRPC server is started. | +| [GO-2026-6354](https://pkg.go.dev/vuln/GO-2026-6354), [GO-2026-6355](https://pkg.go.dev/vuln/GO-2026-6355) | `golang.org/x/crypto@v0.55.0` | An SSH connection with a malicious peer. | Not exposed: the importers are Git, Terraform-module, BuildKit, and signature code that image scanning does not call. The download is HTTPS and scans have no network. | +| [GO-2026-5932](https://pkg.go.dev/vuln/GO-2026-5932) | `golang.org/x/crypto@v0.55.0` | Use of the unmaintained OpenPGP packages. | Not exposed: only Rekor PGP and Helm entry types import them, through Rekor SBOM attestation lookup, which requires `--sbom-sources rekor`. No fixed version exists. | +| [GO-2026-6444](https://pkg.go.dev/vuln/GO-2026-6444) | `github.com/containerd/containerd/v2@v2.3.3` | A CRI server handling `ExecSync`. | Not exposed: no CRI package is linked into the binary. | +| [GO-2026-6597](https://pkg.go.dev/vuln/GO-2026-6597), [GO-2026-6796](https://pkg.go.dev/vuln/GO-2026-6796) | `github.com/containerd/containerd/v2@v2.3.3` | containerd image-pull handler traversal or layer unpack. | Not exposed: of the linked Linux packages, only Trivy's `pkg/fanal/image/daemon` imports containerd, and only the daemon image-source table in `NewContainerImage` calls it; `--input` uses `go-containerregistry`'s `tarball` and `layout` readers instead. These records list no vulnerable symbols, so govulncheck also counts linked containerd package initializers. | +| [GO-2026-6505](https://pkg.go.dev/vuln/GO-2026-6505) | `go.opentelemetry.io/otel/exporters/otlp/otlptrace` | Constructing an OTLP trace exporter. | Not exposed: the importers are containerd, Docker CLI, BuildKit, OPA, and Kubernetes server tracing, none of which configures an exporter in these operations. | +| [GO-2026-4919](https://pkg.go.dev/vuln/GO-2026-4919) | `github.com/aquasecurity/trivy@v0.74.0` | Use of the malicious v0.69.4 release, or of v0.69.5 and v0.69.6 images, published with stolen release credentials in March 2026. | Not affected: the Go record's range is open-ended, but [GHSA-69fq-xp46-6x23](https://github.com/aquasecurity/trivy/security/advisories/GHSA-69fq-xp46-6x23) limits the Go package to `= 0.69.4` and states that the malicious code was never committed to `main`. GitHub's comparison shows release commit `1885610c` is not an ancestor of `e1fd17a0` (merge base `20458b83`). The carried scanner is built from the checksum-verified module source and runs no upstream release workflow or artifact. | + +These dispositions depend on the invocations, network modes, and severity +gate above. Adding an operation, scanner, SBOM source, credential helper, or +server mode invalidates them. ## Scanner trust bundle diff --git a/docs/installation.md b/docs/installation.md index 85076285..1bdce318 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -320,8 +320,8 @@ One release identity has one self-contained policy root per supported host: rustfmt, Clippy, the host standard library and Nextest `0.9.138`; - every other pinned tool the engine runs: the Go toolchain, ShellCheck, staticcheck, deadcode, govulncheck, OSV-Scanner, Ruff, PyPA `packaging` `26.3`, Vulture - `2.16`, `ty`, and the carried CPython `3.12.13+20260728` runtime from - python-build-standalone. A + `2.16`, `ty`, the carried CPython `3.12.13+20260728` runtime from + python-build-standalone, and the Linux Trivy artifact scanner. A target installs no policy tooling, and none of these is ever taken from an ambient `PATH`, a host installation, or an environment override, so a check decides the same thing on every machine that has the matching host release; @@ -421,6 +421,11 @@ libraries reside in the carried Python runtime with their exact dependency manifest, lock, and distribution metadata. Changing either executable pin changes the cross-host release identity. +Manifest version 9 adds `tools.trivy` for the carried artifact scanner, a Linux +build of Trivy `0.74.0` for the host architecture with the license and notice +files of every Go module linked into it. The release SBOM lists those modules +from the binary's build information. + `code-polishy release-manifest verify --root ` recomputes the core entry evidence. Optional Rust tool integrity is required before Rust execution. A core release that was truncated, changed after diff --git a/docs/policy-engine-architecture.md b/docs/policy-engine-architecture.md index 9f73ff5a..99e14784 100644 --- a/docs/policy-engine-architecture.md +++ b/docs/policy-engine-architecture.md @@ -392,7 +392,7 @@ separately. The engine reads config and source from the target while resolving policy-owned tools from the release beside it. A release carries every pinned tool the engine runs — the sealed Node runtime and JavaScript bundle, the Go toolchain, ShellCheck, staticcheck, deadcode, govulncheck, OSV-Scanner, Ruff, Vulture, -`ty`, and carried CPython — and none of them is resolved from an ambient `PATH`, +`ty`, carried CPython, and the Linux Trivy artifact scanner — and none of them is resolved from an ambient `PATH`, a host installation, or an environment override, so what a check decides does not depend on the machine it ran on. The release manifest records the exact version of each one, the installer probes every local tool against its diff --git a/docs/source-provenance.md b/docs/source-provenance.md index 11f2ca9f..eec75c24 100644 --- a/docs/source-provenance.md +++ b/docs/source-provenance.md @@ -57,7 +57,8 @@ version before staging a release. fixed upstream metadata services; neither comes from a target environment. - Trivy is built from the official `github.com/aquasecurity/trivy` module at the pinned version and checksum in `tools/trivy_module_sum.txt`, with the pinned - Go toolchain, and carried with its license under `.tools/trivy`. + Go toolchain, and carried under `.tools/trivy` with the license and notice + files of every module linked into it. `artifact-security/scanner-policy.json` records its runtime configuration; the release manifest covers the carried binary, the repository-owned Dockerfile, the scanner's trust bundle diff --git a/internal/release/capability_proof.go b/internal/release/capability_proof.go index 0f5005c3..f9e67729 100644 --- a/internal/release/capability_proof.go +++ b/internal/release/capability_proof.go @@ -61,8 +61,14 @@ func authenticateCapabilityProof(proof *capabilityCatalogProof, expected Lock) ( return AuthenticatedCapabilityCatalog{Release: expected, SHA256: digest, Catalog: catalog, proof: proof}, nil } +func recordsLaterManifestTools(manifest Manifest) bool { + return manifest.ManifestVersion < 7 && (manifest.Tools.Rust != "" || manifest.Tools.Nextest != "") || + manifest.ManifestVersion < 8 && manifest.Tools.Deadcode != "" || + manifest.ManifestVersion < 9 && manifest.Tools.Trivy != "" +} + func validateCapabilityProofIdentity(manifest Manifest, expected Lock) error { - if manifest.ManifestVersion < 7 && (manifest.Tools.Rust != "" || manifest.Tools.Nextest != "") || manifest.ManifestVersion < 8 && manifest.Tools.Deadcode != "" { + if recordsLaterManifestTools(manifest) { return fmt.Errorf("capability evidence records tools absent from its historical manifest schema") } if !revisionPattern.MatchString(manifest.SourceRevision) { diff --git a/internal/release/launcher_manifest.go b/internal/release/launcher_manifest.go index 0e6f68b6..ca3d61c3 100644 --- a/internal/release/launcher_manifest.go +++ b/internal/release/launcher_manifest.go @@ -72,6 +72,11 @@ type manifestToolsV7 struct { Nextest string `json:"cargo-nextest"` } +type manifestToolsV8 struct { + manifestToolsV7 + Deadcode string `json:"deadcode"` +} + func ReadLauncherManifest(releaseDir string) (Manifest, bool, error) { return readManifest(releaseDir, parseLauncherManifest) } @@ -117,15 +122,25 @@ func parseLauncherManifest(data []byte, source string) (Manifest, error) { return manifest, nil } +func parseNativeManifestTools(data []byte, source string, version int) (Tools, error) { + wire := manifestToolsV8{} + var err error + if version >= 8 { + err = decodeExactly(data, source+" tools", &wire) + } else { + err = decodeExactly(data, source+" tools", &wire.manifestToolsV7) + } + if err != nil { + return Tools{}, err + } + result := wire.manifestToolsV4.tools() + result.Packaging, result.Rust, result.Nextest, result.Deadcode = wire.Packaging, wire.Rust, wire.Nextest, wire.Deadcode + return result, nil +} + func parseHistoricalManifestTools(data []byte, source string, version int) (Tools, error) { if version >= 7 { - wire := manifestToolsV7{} - if err := decodeExactly(data, source+" tools", &wire); err != nil { - return Tools{}, err - } - result := wire.manifestToolsV4.tools() - result.Packaging, result.Rust, result.Nextest = wire.Packaging, wire.Rust, wire.Nextest - return result, nil + return parseNativeManifestTools(data, source, version) } if version >= 5 { wire := manifestToolsV5{} diff --git a/internal/release/launcher_manifest_test.go b/internal/release/launcher_manifest_test.go index f0506f16..172dcf77 100644 --- a/internal/release/launcher_manifest_test.go +++ b/internal/release/launcher_manifest_test.go @@ -43,6 +43,19 @@ func TestLauncherManifestRejectsDeadcodeInHistoricalSchemas(t *testing.T) { } } +func TestLauncherManifestReadsVersion8AndRejectsItsLaterTrivyField(t *testing.T) { + t.Parallel() + data, manifest := historicalManifest(t, 8) + parsed, err := parseLauncherManifest(data, ManifestFilename) + if err != nil || parsed.Tools.Deadcode != manifest.Tools.Deadcode || parsed.Tools.Trivy != "" { + t.Fatalf("version 8 manifest = %+v, error = %v", parsed.Tools, err) + } + changed := strings.Replace(string(data), `"tools":{`, `"tools":{"trivy":"0.74.0",`, 1) + if _, err := parseLauncherManifest([]byte(changed), ManifestFilename); err == nil { + t.Fatal("version 8 accepted the later trivy tool field") + } +} + func TestLauncherManifestRejectsUnsupportedSchemas(t *testing.T) { t.Parallel() _, manifest := exampleRelease(t) @@ -74,7 +87,10 @@ func historicalManifest(t *testing.T, version int) ([]byte, Manifest) { if version < 6 { manifest.CapabilityCatalogSHA256 = "" } - manifest.Tools.Deadcode = "" + manifest.Tools.Trivy = "" + if version < 8 { + manifest.Tools.Deadcode = "" + } if version < 7 { manifest.Tools.Rust = "" manifest.Tools.Nextest = "" @@ -131,8 +147,12 @@ func historicalManifestTools(t *testing.T, manifest Manifest) json.RawMessage { Ty: manifest.Tools.Ty, Python: manifest.Tools.Python, Vulture: manifest.Tools.Vulture, } versionFive := manifestToolsV5{manifestToolsV4: versionFour, Packaging: manifest.Tools.Packaging} + versionSeven := manifestToolsV7{manifestToolsV5: versionFive, Rust: manifest.Tools.Rust, Nextest: manifest.Tools.Nextest} + if manifest.ManifestVersion >= 8 { + return render(t, manifestToolsV8{manifestToolsV7: versionSeven, Deadcode: manifest.Tools.Deadcode}) + } if manifest.ManifestVersion >= 7 { - return render(t, manifestToolsV7{manifestToolsV5: versionFive, Rust: manifest.Tools.Rust, Nextest: manifest.Tools.Nextest}) + return render(t, versionSeven) } if manifest.ManifestVersion >= 5 { return render(t, versionFive) diff --git a/internal/release/manifest_writer.go b/internal/release/manifest_writer.go index ab0dd2ec..78fe92c5 100644 --- a/internal/release/manifest_writer.go +++ b/internal/release/manifest_writer.go @@ -18,13 +18,13 @@ import ( ) var releasePinPaths = struct { - version, goVersion, govulncheck, node, osv, pnpm, packaging, python, ruff, shellcheck, staticcheck, ty, vulture, rust, nextest, deadcode string + version, goVersion, govulncheck, node, osv, pnpm, packaging, python, ruff, shellcheck, staticcheck, ty, vulture, rust, nextest, deadcode, trivy string }{ "VERSION", "scripts/go_version.txt", "tools/govulncheck-version.txt", "tools/node-version.txt", "tools/osv-scanner-version.txt", "tools/pnpm-version.txt", "tools/packaging-version.txt", "tools/python-version.txt", "tools/ruff-version.txt", "tools/shellcheck-version.txt", "tools/staticcheck-version.txt", "tools/ty-version.txt", "tools/vulture-version.txt", - "tools/rust-version.txt", "tools/cargo-nextest-version.txt", "tools/deadcode-version.txt", + "tools/rust-version.txt", "tools/cargo-nextest-version.txt", "tools/deadcode-version.txt", "tools/trivy-version.txt", } func WriteManifest(releaseDir, sourceRevision string) (Manifest, error) { @@ -65,8 +65,8 @@ func composeReleaseManifest(root, sourceRevision string) (Manifest, error) { if err != nil { return Manifest{}, err } - pins := make([]string, 15) - for index, path := range []string{releasePinPaths.goVersion, releasePinPaths.govulncheck, releasePinPaths.node, releasePinPaths.osv, releasePinPaths.pnpm, releasePinPaths.packaging, releasePinPaths.python, releasePinPaths.ruff, releasePinPaths.shellcheck, releasePinPaths.staticcheck, releasePinPaths.ty, releasePinPaths.vulture, releasePinPaths.rust, releasePinPaths.nextest, releasePinPaths.deadcode} { + pins := make([]string, 16) + for index, path := range []string{releasePinPaths.goVersion, releasePinPaths.govulncheck, releasePinPaths.node, releasePinPaths.osv, releasePinPaths.pnpm, releasePinPaths.packaging, releasePinPaths.python, releasePinPaths.ruff, releasePinPaths.shellcheck, releasePinPaths.staticcheck, releasePinPaths.ty, releasePinPaths.vulture, releasePinPaths.rust, releasePinPaths.nextest, releasePinPaths.deadcode, releasePinPaths.trivy} { pins[index], err = readReleasePin(root, path, true) if err != nil { return Manifest{}, err @@ -120,7 +120,7 @@ func readReleasePin(root, relative string, trimV bool) (string, error) { func newReleaseManifest(version, sourceRevision, host string, pins []string, entries []Entry) Manifest { manifest := Manifest{ManifestVersion: ManifestVersion, CodePolishyVersion: version, SourceRevision: sourceRevision, - Host: host, Features: []string{"javascript-bundle"}, Tools: Tools{Go: pins[0], Govulncheck: pins[1], Node: pins[2], OSVScanner: pins[3], PNPM: pins[4], Packaging: pins[5], Python: pins[6], Ruff: pins[7], Shellcheck: pins[8], Staticcheck: pins[9], Ty: pins[10], Vulture: pins[11], Rust: pins[12], Nextest: pins[13], Deadcode: pins[14]}, Entries: entries, EntryCount: len(entries)} + Host: host, Features: []string{"javascript-bundle"}, Tools: Tools{Go: pins[0], Govulncheck: pins[1], Node: pins[2], OSVScanner: pins[3], PNPM: pins[4], Packaging: pins[5], Python: pins[6], Ruff: pins[7], Shellcheck: pins[8], Staticcheck: pins[9], Ty: pins[10], Vulture: pins[11], Rust: pins[12], Nextest: pins[13], Deadcode: pins[14], Trivy: pins[15]}, Entries: entries, EntryCount: len(entries)} for _, entry := range entries { if entry.Path == CapabilityCatalogPath && entry.Symlink == "" { manifest.CapabilityCatalogSHA256 = entry.SHA256 diff --git a/internal/release/manifest_writer_test.go b/internal/release/manifest_writer_test.go index 1df152e8..0bf9bb30 100644 --- a/internal/release/manifest_writer_test.go +++ b/internal/release/manifest_writer_test.go @@ -48,7 +48,7 @@ func writableReleaseStage(t *testing.T) string { "tools/python-version.txt": "3.12.13+20260728\n", "tools/ruff-version.txt": "0.16.0\n", "tools/shellcheck-version.txt": "0.11.0\n", "tools/staticcheck-version.txt": "v0.7.0\n", "tools/deadcode-version.txt": "v0.50.0\n", "tools/ty-version.txt": "0.0.65\n", "tools/vulture-version.txt": "2.16\n", - "tools/rust-version.txt": "1.97.1\n", "tools/cargo-nextest-version.txt": "0.9.138\n", + "tools/rust-version.txt": "1.97.1\n", "tools/cargo-nextest-version.txt": "0.9.138\n", "tools/trivy-version.txt": "0.74.0\n", BinaryPath: "engine", LauncherBinaryPath: "launcher", } for relative, content := range files { diff --git a/internal/release/publication_test.go b/internal/release/publication_test.go index 43cfca22..c78832e0 100644 --- a/internal/release/publication_test.go +++ b/internal/release/publication_test.go @@ -293,6 +293,26 @@ func TestPublicationIndexRequiresEveryHostFromOneSource(t *testing.T) { } } +func moveForeignTrivyFixture(t *testing.T, root string, manifest *Manifest, host string) { + t.Helper() + before, err := trivyReleaseDirectory(manifest.Host) + if err != nil { + t.Fatal(err) + } + after, err := trivyReleaseDirectory(host) + if err != nil || before == after { + return + } + if err := os.Rename(filepath.Join(root, filepath.FromSlash(before)), filepath.Join(root, filepath.FromSlash(after))); err != nil { + t.Fatal(err) + } + for index := range manifest.Entries { + if rest, found := strings.CutPrefix(manifest.Entries[index].Path, before+"/"); found { + manifest.Entries[index].Path = after + "/" + rest + } + } +} + func foreignRelease(t *testing.T, host string) (string, Manifest) { t.Helper() root, manifest := installedRelease(t, map[string]string{BinaryPath: "engine", LauncherBinaryPath: "launcher"}, nil) @@ -333,6 +353,7 @@ func foreignRelease(t *testing.T, host string) (string, Manifest) { manifest.Entries[index].Path = after + "/" + rest } } + moveForeignTrivyFixture(t, root, &manifest, host) } manifest.Host = host slices.SortFunc(manifest.Entries, func(first, second Entry) int { return strings.Compare(first.Path, second.Path) }) diff --git a/internal/release/release.go b/internal/release/release.go index b789a13d..acf09a6b 100644 --- a/internal/release/release.go +++ b/internal/release/release.go @@ -27,7 +27,7 @@ const ( MaximumLockBytes = 32 << 10 LockVersion = 2 - ManifestVersion = 8 + ManifestVersion = 9 releasesDirectory = "releases" ) @@ -111,6 +111,7 @@ type Tools struct { Shellcheck string `json:"shellcheck"` Staticcheck string `json:"staticcheck"` Deadcode string `json:"deadcode,omitempty"` + Trivy string `json:"trivy,omitempty"` Ty string `json:"ty"` Vulture string `json:"vulture"` } @@ -320,6 +321,9 @@ func (manifest Manifest) Identity() string { if manifest.ManifestVersion >= 8 { fmt.Fprintf(identity, "tool.deadcode=%s\n", manifest.Tools.Deadcode) } + if manifest.ManifestVersion >= 9 { + fmt.Fprintf(identity, "tool.trivy=%s\n", manifest.Tools.Trivy) + } digest := sha256.Sum256([]byte(identity.String())) return hex.EncodeToString(digest[:]) } @@ -487,6 +491,9 @@ func validateManifestToolPins(manifest Manifest, source string) error { if manifest.ManifestVersion >= 8 { pins = append(pins, struct{ tool, version string }{"deadcode", manifest.Tools.Deadcode}) } + if manifest.ManifestVersion >= 9 { + pins = append(pins, struct{ tool, version string }{"Trivy", manifest.Tools.Trivy}) + } for _, pin := range pins { if !versionPattern.MatchString(pin.version) { return fmt.Errorf("%s records the unusable %s version %q", source, pin.tool, pin.version) diff --git a/internal/release/release_test.go b/internal/release/release_test.go index 8aa76c3c..8593159e 100644 --- a/internal/release/release_test.go +++ b/internal/release/release_test.go @@ -51,6 +51,11 @@ func installedRelease(t *testing.T, files, links map[string]string) (string, Man files[".tools/python/fixture/lib/python3.12/site-packages/packaging-26.3.dist-info/METADATA"] = "Metadata-Version: 2.4\nName: packaging\nVersion: 26.3\n\n" files[".tools/python/fixture/lib/python3.12/site-packages/vulture-2.16.dist-info/METADATA"] = "Metadata-Version: 2.4\nName: vulture\nVersion: 2.16\nRequires-Dist: packaging>=25\n\n" addRustReleaseInventoryFixture(t, files, host) + if trivy, err := trivyReleaseDirectory(host); err == nil { + if _, present := files[trivy+"/trivy"]; !present { + addTrivyReleaseFixture(t, files, host) + } + } paths := append(slices.Collect(maps.Keys(files)), slices.Collect(maps.Keys(links))...) slices.Sort(paths) entries := []Entry{} @@ -78,7 +83,7 @@ func installedRelease(t *testing.T, files, links map[string]string) (string, Man Tools: Tools{ Go: "1.26.6", Govulncheck: "1.3.0", Node: "24.18.0", OSVScanner: "2.4.0", PNPM: "11.13.0", Packaging: "26.3", Python: "3.12.13+20260728", Ruff: "0.16.0", Shellcheck: "0.11.0", Staticcheck: "0.7.0", Deadcode: "0.50.0", - Ty: "0.0.65", Vulture: "2.16", Rust: "1.97.1", Nextest: "0.9.138", + Ty: "0.0.65", Vulture: "2.16", Rust: "1.97.1", Nextest: "0.9.138", Trivy: "0.74.0", }, ContentDigest: EntriesDigest(entries), EntryCount: len(entries), Entries: entries, CapabilityCatalogSHA256: capabilityContentSHA256([]byte(files[CapabilityCatalogPath])), diff --git a/internal/release/sbom.go b/internal/release/sbom.go index 5d1da0c8..c3b29c37 100644 --- a/internal/release/sbom.go +++ b/internal/release/sbom.go @@ -59,6 +59,9 @@ func renderReleaseSBOM(manifest Manifest, releaseRoot, archiveName, archiveDiges if err := inventory.addGo(releaseRoot, manifest, rootRef, toolRefs); err != nil { return nil, err } + if err := inventory.addTrivyNotices(releaseRoot, manifest); err != nil { + return nil, err + } components, dependencies, err := inventory.finalize(rootRef) if err != nil { return nil, err @@ -97,6 +100,9 @@ func releaseSBOMTools(manifest Manifest) map[string]cdx.Component { "ty": manifest.Tools.Ty, "rust": manifest.Tools.Rust, "cargo-nextest": manifest.Tools.Nextest, } + if manifest.Tools.Trivy != "" { + versions["trivy"] = manifest.Tools.Trivy + } result := make(map[string]cdx.Component, len(versions)) for name, version := range versions { ref := "pkg:generic/" + name + "@" + version diff --git a/internal/release/trivy_sbom.go b/internal/release/trivy_sbom.go new file mode 100644 index 00000000..3be33853 --- /dev/null +++ b/internal/release/trivy_sbom.go @@ -0,0 +1,83 @@ +package release + +import ( + "debug/buildinfo" + "fmt" + "path" + "path/filepath" + "regexp" + "strings" + + cdx "github.com/CycloneDX/cyclonedx-go" + "golang.org/x/mod/module" +) + +var trivyLicenseNotice = regexp.MustCompile(`(?i)^(licen[cs]e|copying)`) + +func trivyReleaseDirectory(host string) (string, error) { + switch { + case strings.HasSuffix(host, "-arm64"): + return ".tools/trivy/linux-arm64", nil + case strings.HasSuffix(host, "-x64"): + return ".tools/trivy/linux-amd64", nil + default: + return "", fmt.Errorf("carried Trivy has no Linux platform for release host %q", host) + } +} + +func (inventory *releaseSBOMInventory) addTrivyNotices(root string, manifest Manifest) error { + if manifest.Tools.Trivy == "" { + return nil + } + directory, err := trivyReleaseDirectory(manifest.Host) + if err != nil { + return err + } + binary := directory + "/trivy" + if !manifestRecordsCapabilityFile(manifest, binary, "") { + return fmt.Errorf("carried Trivy is absent from the release manifest: %s", binary) + } + info, err := buildinfo.ReadFile(filepath.Join(root, filepath.FromSlash(binary))) + if err != nil { + return fmt.Errorf("read carried Trivy build information: %w", err) + } + for _, shipped := range shippedGoModules(info) { + resolved := shipped + if shipped.Replace != nil { + resolved = shipped.Replace + } + if err := inventory.addTrivyModuleNotices(manifest, directory, resolved.Path, resolved.Version); err != nil { + return err + } + } + return nil +} + +func (inventory *releaseSBOMInventory) addTrivyModuleNotices(manifest Manifest, directory, modulePath, version string) error { + escaped, err := module.EscapePath(modulePath) + if err != nil { + return fmt.Errorf("carried Trivy module %s has an invalid path: %w", modulePath, err) + } + prefix := directory + "/licenses/" + escaped + "@" + version + "/" + var properties []cdx.Property + licensed := false + for _, entry := range manifest.Entries { + relative, found := strings.CutPrefix(entry.Path, prefix) + if !found || entry.Symlink != "" || entry.SHA256 == "" || strings.Contains(relative, "/") { + continue + } + licensed = licensed || trivyLicenseNotice.MatchString(path.Base(relative)) + properties = append(properties, cdx.Property{Name: "code-polishy:notice-path", Value: entry.Path}) + } + if !licensed { + return fmt.Errorf("carried Trivy module %s@%s has no license notice under %s", modulePath, version, prefix) + } + ref := "pkg:golang/" + modulePath + "@" + version + component, found := inventory.components[ref] + if !found { + return fmt.Errorf("carried Trivy module %s@%s is absent from the SBOM inventory", modulePath, version) + } + component.Properties = &properties + inventory.components[ref] = component + return nil +} diff --git a/internal/release/trivy_sbom_test.go b/internal/release/trivy_sbom_test.go new file mode 100644 index 00000000..40a7c87d --- /dev/null +++ b/internal/release/trivy_sbom_test.go @@ -0,0 +1,153 @@ +package release + +import ( + "bytes" + "debug/buildinfo" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "sync" + "testing" + + cdx "github.com/CycloneDX/cyclonedx-go" + "golang.org/x/mod/module" +) + +var trivyFixtureBinary = sync.OnceValues(func() ([]byte, error) { + directory, err := os.MkdirTemp("", "code-polishy-trivy-fixture-") + if err != nil { + return nil, err + } + defer os.RemoveAll(directory) + binary := filepath.Join(directory, "trivy") + build := exec.Command("go", "build", "-trimpath", "-o", binary, "github.com/riteofstring/code-polishy/cmd/code-polishy-launcher") + if output, err := build.CombinedOutput(); err != nil { + return nil, fmt.Errorf("build the fixture Go binary: %w: %s", err, output) + } + return os.ReadFile(binary) +}) + +func addTrivyReleaseFixture(t *testing.T, files map[string]string, host string) { + t.Helper() + binary, err := trivyFixtureBinary() + if err != nil { + t.Fatal(err) + } + directory, err := trivyReleaseDirectory(host) + if err != nil { + t.Fatal(err) + } + files[directory+"/trivy"] = string(binary) + for _, shipped := range trivyFixtureModules(t) { + escaped, err := module.EscapePath(shipped.Path) + if err != nil { + t.Fatal(err) + } + files[directory+"/licenses/"+escaped+"@"+shipped.Version+"/LICENSE"] = "upstream license\n" + } +} + +func trivyFixtureModules(t *testing.T) []module.Version { + t.Helper() + binary, err := trivyFixtureBinary() + if err != nil { + t.Fatal(err) + } + info, err := buildinfo.Read(bytes.NewReader(binary)) + if err != nil { + t.Fatal(err) + } + var modules []module.Version + for _, shipped := range shippedGoModules(info) { + resolved := shipped + if shipped.Replace != nil { + resolved = shipped.Replace + } + modules = append(modules, module.Version{Path: resolved.Path, Version: resolved.Version}) + } + if len(modules) == 0 { + t.Fatal("the fixture binary records no shipped modules") + } + return modules +} + +func TestReleaseSBOMRecordsCarriedTrivyModulesAndTheirNotices(t *testing.T) { + root, manifest := installedRelease(t, map[string]string{BinaryPath: "fixture"}, nil) + data, err := renderReleaseSBOM(manifest, root, "release.zip", exampleDigest) + if err != nil { + t.Fatal(err) + } + document := &cdx.BOM{} + if err := cdx.NewBOMDecoder(bytes.NewReader(data), cdx.BOMFileFormatJSON).Decode(document); err != nil { + t.Fatal(err) + } + components := map[string]cdx.Component{} + for _, component := range *document.Components { + components[component.BOMRef] = component + } + if _, found := components["pkg:generic/trivy@0.74.0"]; !found { + t.Fatal("the SBOM omits the carried Trivy scanner") + } + dependencies := map[string]bool{} + for _, dependency := range *document.Dependencies { + if dependency.Ref == "pkg:generic/trivy@0.74.0" && dependency.Dependencies != nil { + for _, target := range *dependency.Dependencies { + dependencies[target] = true + } + } + } + directory, err := trivyReleaseDirectory(manifest.Host) + if err != nil { + t.Fatal(err) + } + for _, shipped := range trivyFixtureModules(t) { + ref := "pkg:golang/" + shipped.Path + "@" + shipped.Version + component, found := components[ref] + if !found || !dependencies[ref] { + t.Fatalf("the SBOM does not attribute %s to the carried Trivy", ref) + } + if component.Properties == nil || len(*component.Properties) != 1 || + !strings.HasPrefix((*component.Properties)[0].Value, directory+"/licenses/") { + t.Fatalf("%s has no carried notice: %+v", ref, component.Properties) + } + } +} + +func TestReleaseSBOMRefusesCarriedTrivyWithoutBuildInformationOrNotices(t *testing.T) { + host, err := Host() + if err != nil { + t.Fatal(err) + } + directory, err := trivyReleaseDirectory(host) + if err != nil { + t.Fatal(err) + } + modules := trivyFixtureModules(t) + escaped, err := module.EscapePath(modules[0].Path) + if err != nil { + t.Fatal(err) + } + for _, test := range []struct { + name, path, content, problem string + }{ + {"stub binary", directory + "/trivy", "not a Go binary", "build information"}, + {"missing license", directory + "/licenses/" + escaped + "@" + modules[0].Version + "/LICENSE", "", "has no license notice"}, + } { + t.Run(test.name, func(t *testing.T) { + files := map[string]string{BinaryPath: "fixture"} + addTrivyReleaseFixture(t, files, host) + if test.content == "" { + delete(files, test.path) + files[strings.TrimSuffix(test.path, "LICENSE")+"README.md"] = "not a license\n" + } else { + files[test.path] = test.content + } + root, manifest := installedRelease(t, files, nil) + if _, err := renderReleaseSBOM(manifest, root, "release.zip", exampleDigest); err == nil || !strings.Contains(err.Error(), test.problem) { + t.Fatalf("SBOM error = %v, want %q", err, test.problem) + } + }) + } +} diff --git a/scripts/test-install.sh b/scripts/test-install.sh index 6155a309..dc58f73b 100755 --- a/scripts/test-install.sh +++ b/scripts/test-install.sh @@ -407,7 +407,9 @@ EOF write_stub_tool "${source_root}/.tools/bin/deadcode" write_stub_tool "${source_root}/.tools/bin/govulncheck" write_stub_tool "${source_root}/.tools/trivy/linux-${go_platform_tag##*-}/trivy" - printf 'fixture license\n' >"${source_root}/.tools/trivy/linux-${go_platform_tag##*-}/LICENSE" + mkdir -p "${source_root}/.tools/trivy/linux-${go_platform_tag##*-}/licenses/github.com/aquasecurity/trivy@v0.72.0" + printf 'fixture license\n' \ + >"${source_root}/.tools/trivy/linux-${go_platform_tag##*-}/licenses/github.com/aquasecurity/trivy@v0.72.0/LICENSE" printf '0.72.0\n' >"${source_root}/.tools/trivy/linux-${go_platform_tag##*-}/fixture-version" "${real_git}" -C "${source_root}" init --quiet @@ -542,7 +544,7 @@ expected_revision="$("${real_git}" -C "${source_root}" rev-parse HEAD)" for carried in go:1.26.9 node:24.18.0 pnpm:11.13.0 packaging:26.3 shellcheck:0.11.0 \ staticcheck:0.7.0 deadcode:0.50.0 govulncheck:1.3.0 osv-scanner:2.4.0 python:3.12.13+20260728 \ - ruff:0.16.0 ty:0.0.65 vulture:2.16 rust:1.97.1 cargo-nextest:0.9.138; do + ruff:0.16.0 ty:0.0.65 vulture:2.16 rust:1.97.1 cargo-nextest:0.9.138 trivy:0.72.0; do [[ "$(manifest_field "${manifest}" "${carried%%:*}")" == "${carried##*:}" ]] || fail "the release does not record which ${carried%%:*} it carries" done @@ -584,7 +586,8 @@ for required in bin/code-polishy bin/code-polishy-launcher VERSION LICENSE READM ".tools/shellcheck/${shellcheck_platform_tag}/shellcheck" \ ".tools/rust/${platform_tag}/bin/rustc" ".tools/rust/${platform_tag}/bin/cargo-clippy" \ .tools/bin/staticcheck .tools/bin/deadcode .tools/bin/govulncheck .tools/bin/osv-scanner .tools/bin/ruff .tools/bin/ty .tools/bin/cargo-nextest \ - ".tools/trivy/linux-${go_platform_tag##*-}/trivy" ".tools/trivy/linux-${go_platform_tag##*-}/LICENSE"; do + ".tools/trivy/linux-${go_platform_tag##*-}/trivy" \ + ".tools/trivy/linux-${go_platform_tag##*-}/licenses/github.com/aquasecurity/trivy@v0.72.0/LICENSE"; do [[ -e "${release}/${required}" ]] || fail "the release is missing ${required}" done [[ ! -e "${release}/scripts/automation" ]] || diff --git a/tools/install-go-tools.sh b/tools/install-go-tools.sh index 541f40a9..7cd6933e 100755 --- a/tools/install-go-tools.sh +++ b/tools/install-go-tools.sh @@ -29,7 +29,34 @@ trivy_identity() { awk 'NR == 1 { identity = $NF } $1 == "mod" { identity = identity " " $2 " " $3 " " $4 } END { print identity }' } -if [[ -f "${trivy_dir}/trivy" && -f "${trivy_dir}/LICENSE" && "$(trivy_identity "${trivy_dir}/trivy")" == "${trivy_expected}" ]]; then +trivy_notices() { + local binary="$1" module_cache="$2" destination="$3" module version escaped notice licensed + if "${repo_root}/scripts/go.sh" version -m "${binary}" | awk '$1 == "=>" { found = 1 } END { exit !found }'; then + echo "The built Trivy replaces a module, so its notices cannot be attributed." >&2 + return 1 + fi + while read -r module version; do + escaped="$(printf '%s\n' "${module}" | awk '{ out = ""; for (i = 1; i <= length($0); i++) { c = substr($0, i, 1); out = out (c ~ /[A-Z]/ ? "!" tolower(c) : c) } print out }')" + mkdir -p "${destination}/${escaped}@${version}" + licensed=false + for notice in "${module_cache}/${escaped}@${version}"/*; do + [[ -f "${notice}" && ! -L "${notice}" ]] || continue + case "$(basename "${notice}")" in + [Ll][Ii][Cc][Ee][Nn][CcSs][Ee]* | [Cc][Oo][Pp][Yy][Ii][Nn][Gg]*) licensed=true ;; + [Nn][Oo][Tt][Ii][Cc][Ee]* | [Pp][Aa][Tt][Ee][Nn][Tt][Ss]*) ;; + *) continue ;; + esac + cp "${notice}" "${destination}/${escaped}@${version}/" + chmod 0644 "${destination}/${escaped}@${version}/$(basename "${notice}")" + done + if [[ "${licensed}" != true ]]; then + echo "Trivy module ${module}@${version} carries no license file." >&2 + return 1 + fi + done < <("${repo_root}/scripts/go.sh" version -m "${binary}" | awk '$1 == "dep" || $1 == "mod" { print $2, $3 }') +} + +if [[ -f "${trivy_dir}/trivy" && -d "${trivy_dir}/licenses" && "$(trivy_identity "${trivy_dir}/trivy")" == "${trivy_expected}" ]]; then echo "Trivy ${trivy_version} for linux/${trivy_arch} is already built at ${trivy_dir}." else trivy_scratch="$(mktemp -d "${TMPDIR:-/tmp}/code-polishy-trivy.XXXXXX")" @@ -50,9 +77,8 @@ else fi mkdir -p "${trivy_scratch}/stage" cp "${built}" "${trivy_scratch}/stage/trivy" - cp "${trivy_scratch}/gopath/pkg/mod/github.com/aquasecurity/trivy@v${trivy_version}/LICENSE" "${trivy_scratch}/stage/LICENSE" chmod 0755 "${trivy_scratch}/stage/trivy" - chmod 0644 "${trivy_scratch}/stage/LICENSE" + trivy_notices "${built}" "${trivy_scratch}/gopath/pkg/mod" "${trivy_scratch}/stage/licenses" mkdir -p "$(dirname "${trivy_dir}")" rm -rf "${trivy_dir}" mv "${trivy_scratch}/stage" "${trivy_dir}" diff --git a/tools/install-policy-tools.ps1 b/tools/install-policy-tools.ps1 index 227aa967..da0dac46 100644 --- a/tools/install-policy-tools.ps1 +++ b/tools/install-policy-tools.ps1 @@ -116,8 +116,26 @@ try { } return $Identity -join ' ' } + function Copy-TrivyNotices([string]$Binary, [string]$ModuleCache, [string]$Destination) { + $Lines = @(& $Go version -m $Binary) + if ($LASTEXITCODE -ne 0) { throw 'Trivy build information is unreadable.' } + foreach ($Line in $Lines) { + $Fields = @($Line.Trim() -split '\s+') + if ($Fields[0] -ceq '=>') { throw 'The built Trivy replaces a module, so its notices cannot be attributed.' } + if ($Fields[0] -cne 'dep' -and $Fields[0] -cne 'mod') { continue } + $Escaped = [regex]::Replace($Fields[1], '[A-Z]', { param($Match) '!' + $Match.Value.ToLowerInvariant() }) + $Source = Join-Path $ModuleCache "$Escaped@$($Fields[2])" + $Target = Join-Path $Destination "$Escaped@$($Fields[2])" + New-Item -ItemType Directory -Force -Path $Target | Out-Null + $Notices = @(Get-ChildItem -LiteralPath $Source -File | Where-Object { $_.Name -match '^(?i)(licen[cs]e|copying|notice|patents)' }) + if (@($Notices | Where-Object { $_.Name -match '^(?i)(licen[cs]e|copying)' }).Count -eq 0) { + throw "Trivy module $($Fields[1])@$($Fields[2]) carries no license file." + } + foreach ($Notice in $Notices) { Copy-Item -LiteralPath $Notice.FullName -Destination (Join-Path $Target $Notice.Name) } + } + } if ((Get-TrivyIdentity (Join-Path $TrivyRoot 'trivy')) -cne $TrivyExpected -or - -not (Test-Path -LiteralPath (Join-Path $TrivyRoot 'LICENSE') -PathType Leaf)) { + -not (Test-Path -LiteralPath (Join-Path $TrivyRoot 'licenses') -PathType Container)) { $TrivyScratch = Join-Path $Scratch 'trivy' $TrivyEnvironment = [ordered]@{ GOENV = 'off'; GOPATH = (Join-Path $TrivyScratch 'gopath'); GOMODCACHE = (Join-Path $TrivyScratch 'gopath/pkg/mod') @@ -143,7 +161,7 @@ try { $TrivyStage = Join-Path $TrivyScratch 'stage' New-Item -ItemType Directory -Path $TrivyStage | Out-Null Copy-Item -LiteralPath $BuiltTrivy -Destination (Join-Path $TrivyStage 'trivy') - Copy-Item -LiteralPath (Join-Path $TrivyScratch "gopath/pkg/mod/github.com/aquasecurity/trivy@v$TrivyVersion/LICENSE") -Destination (Join-Path $TrivyStage 'LICENSE') + Copy-TrivyNotices $BuiltTrivy (Join-Path $TrivyScratch 'gopath/pkg/mod') (Join-Path $TrivyStage 'licenses') Replace-Directory $TrivyStage $TrivyRoot } From 1c38a4271326ade650410ad6cbeba3d18fd00cc7 Mon Sep 17 00:00:00 2001 From: Jeff Musgrave <1578618+JeffMusgrave@users.noreply.github.com> Date: Wed, 21 Oct 2026 23:48:12 -0400 Subject: [PATCH 07/15] Carry nested Trivy module notices and validate the exact scanner build Root-only notice copying omitted licenses of forked code compiled inside modules, such as klauspost/compress's xxhash, OPA's AWS provider, and Kubernetes' forked Go and govalidator sources. Both installers now copy every license, copying, notice, and patent file in each linked module with its relative path, reading build information once and failing on missing, malformed, or empty evidence. The release SBOM records nested notices and still requires a root license per module. Release installation and the Windows release build now require the carried binary's exact Go 1.26.10 jsonv2 toolchain, Trivy module, version, and checksum, and its module notices, instead of the module version alone. Name the linked OpenTelemetry SDK and gRPC exporter importers in the GO-2026-6505 assessment. Co-Authored-By: Claude Opus 5.5 --- docs/design/carried-trivy-scanner.md | 31 ++++++++++------- docs/design/trivy-scanner-assessments.md | 24 ++++++------- internal/release/trivy_sbom.go | 7 ++-- internal/release/trivy_sbom_test.go | 36 +++++++++++++++---- scripts/build-release.ps1 | 18 ++++++++++ scripts/install.sh | 21 +++++++---- scripts/test-install.sh | 32 ++++++++++++----- tools/install-go-tools.sh | 44 +++++++++++++++++------- tools/install-policy-tools.ps1 | 35 ++++++++++++++----- 9 files changed, 178 insertions(+), 70 deletions(-) diff --git a/docs/design/carried-trivy-scanner.md b/docs/design/carried-trivy-scanner.md index d9a039bc..53c220dd 100644 --- a/docs/design/carried-trivy-scanner.md +++ b/docs/design/carried-trivy-scanner.md @@ -18,21 +18,28 @@ copy the official build's settings: `CGO_ENABLED=0`, `GOEXPERIMENT=jsonv2`, and `go1.26.10-X:jsonv2` and exactly that module version and sum. The pinned module content includes Trivy's `go.sum`, so the module graph is frozen. The installer keeps the binary in `.tools/trivy/linux-` for the host -architecture; Windows releases carry `linux-amd64`. Release installation -refuses a carried binary whose module version differs from -`tools/trivy-version.txt`, and the release manifest covers its bytes, so the -runtime trusts the installed release rather than rechecking them. +architecture; Windows releases carry `linux-amd64`. Release installation on +Linux and macOS, and the Windows release build, refuse a carried binary whose +build information does not name exactly `go1.26.10-X:jsonv2`, Trivy's module, +version, and checksum, or that has no module notices. The release manifest +covers its bytes, so the runtime trusts the installed release rather than +rechecking them. Distributing the binary distributes every module linked into it. For each module the binary's build information names, including Trivy itself, the -installer copies the license, copying, notice, and patent files at that -module's root in the checksum-verified module cache into -`licenses/@/`. A module without a license or -copying file, or a replaced module, fails the build. Release manifest version -9 records Trivy's version as a tool. The release SBOM reads the carried -binary's build information, lists Trivy with every linked module as its -dependency, and records each module's notice paths. A release whose carried -binary lacks build information or a module's license notice cannot produce an +installer copies every license, copying, notice, and patent file anywhere in +that module's checksum-verified module cache directory into +`licenses/@/`, keeping its relative path. Forked +or vendored code inside a module, such as `zstd/internal/xxhash` in +`klauspost/compress` or `third_party/forked/golang` in Kubernetes modules, +keeps its own attribution. Files under test data are included rather than +pruned by package reachability. Unreadable or empty build information, a module +missing from the cache or without a root license or copying file, or a replaced +module fails the build. Release manifest version 9 records Trivy's version as +a tool. The release SBOM reads the carried binary's build information, lists +Trivy with every linked module as its dependency, and records each module's +root and nested notice paths. A release whose carried binary lacks build +information, or whose module lacks a root license notice, cannot produce an SBOM. The resulting graph has the same 375 dependency versions and checksums as the diff --git a/docs/design/trivy-scanner-assessments.md b/docs/design/trivy-scanner-assessments.md index ccf921a8..a07c2ace 100644 --- a/docs/design/trivy-scanner-assessments.md +++ b/docs/design/trivy-scanner-assessments.md @@ -87,18 +87,18 @@ the three enabled operations: `go-containerregistry` Docker-archive and OCI-layout readers. No SBOM source is configured, so remote SBOM and Rekor lookups do not run. -| Advisory | Module | Precondition | Disposition | -| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [GO-2026-6603](https://pkg.go.dev/vuln/GO-2026-6603), [GO-2026-6612](https://pkg.go.dev/vuln/GO-2026-6612), [GO-2026-6617](https://pkg.go.dev/vuln/GO-2026-6617) | `golang.org/x/net@v0.58.0` | An `x/net/http2` server receiving requests. | Not exposed: no operation starts an HTTP server. | -| [GO-2026-6610](https://pkg.go.dev/vuln/GO-2026-6610) | `golang.org/x/net@v0.58.0` | An `x/net/http2` transport response forwarded to an HTTP/1 client. | Not exposed: the only `x/net/http2` transport configured in these operations is the Azure SDK's unused private client, and nothing is proxied. The standard-library copy is fixed in Go 1.26.9. | -| [GO-2026-6611](https://pkg.go.dev/vuln/GO-2026-6611), [GO-2026-6348](https://pkg.go.dev/vuln/GO-2026-6348), [GO-2026-6443](https://pkg.go.dev/vuln/GO-2026-6443), [GO-2026-6791](https://pkg.go.dev/vuln/GO-2026-6791) | `x/net`, `grpc`, `containerd` | As above. | Reported HIGH by the scanner; assessed in OpenVEX. | -| [GO-2026-6441](https://pkg.go.dev/vuln/GO-2026-6441) | `google.golang.org/grpc@v1.82.1` | A gRPC xDS server applying RBAC HTTP filters. | Not exposed: no gRPC server is started. | -| [GO-2026-6354](https://pkg.go.dev/vuln/GO-2026-6354), [GO-2026-6355](https://pkg.go.dev/vuln/GO-2026-6355) | `golang.org/x/crypto@v0.55.0` | An SSH connection with a malicious peer. | Not exposed: the importers are Git, Terraform-module, BuildKit, and signature code that image scanning does not call. The download is HTTPS and scans have no network. | -| [GO-2026-5932](https://pkg.go.dev/vuln/GO-2026-5932) | `golang.org/x/crypto@v0.55.0` | Use of the unmaintained OpenPGP packages. | Not exposed: only Rekor PGP and Helm entry types import them, through Rekor SBOM attestation lookup, which requires `--sbom-sources rekor`. No fixed version exists. | -| [GO-2026-6444](https://pkg.go.dev/vuln/GO-2026-6444) | `github.com/containerd/containerd/v2@v2.3.3` | A CRI server handling `ExecSync`. | Not exposed: no CRI package is linked into the binary. | -| [GO-2026-6597](https://pkg.go.dev/vuln/GO-2026-6597), [GO-2026-6796](https://pkg.go.dev/vuln/GO-2026-6796) | `github.com/containerd/containerd/v2@v2.3.3` | containerd image-pull handler traversal or layer unpack. | Not exposed: of the linked Linux packages, only Trivy's `pkg/fanal/image/daemon` imports containerd, and only the daemon image-source table in `NewContainerImage` calls it; `--input` uses `go-containerregistry`'s `tarball` and `layout` readers instead. These records list no vulnerable symbols, so govulncheck also counts linked containerd package initializers. | -| [GO-2026-6505](https://pkg.go.dev/vuln/GO-2026-6505) | `go.opentelemetry.io/otel/exporters/otlp/otlptrace` | Constructing an OTLP trace exporter. | Not exposed: the importers are containerd, Docker CLI, BuildKit, OPA, and Kubernetes server tracing, none of which configures an exporter in these operations. | -| [GO-2026-4919](https://pkg.go.dev/vuln/GO-2026-4919) | `github.com/aquasecurity/trivy@v0.74.0` | Use of the malicious v0.69.4 release, or of v0.69.5 and v0.69.6 images, published with stolen release credentials in March 2026. | Not affected: the Go record's range is open-ended, but [GHSA-69fq-xp46-6x23](https://github.com/aquasecurity/trivy/security/advisories/GHSA-69fq-xp46-6x23) limits the Go package to `= 0.69.4` and states that the malicious code was never committed to `main`. GitHub's comparison shows release commit `1885610c` is not an ancestor of `e1fd17a0` (merge base `20458b83`). The carried scanner is built from the checksum-verified module source and runs no upstream release workflow or artifact. | +| Advisory | Module | Precondition | Disposition | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [GO-2026-6603](https://pkg.go.dev/vuln/GO-2026-6603), [GO-2026-6612](https://pkg.go.dev/vuln/GO-2026-6612), [GO-2026-6617](https://pkg.go.dev/vuln/GO-2026-6617) | `golang.org/x/net@v0.58.0` | An `x/net/http2` server receiving requests. | Not exposed: no operation starts an HTTP server. | +| [GO-2026-6610](https://pkg.go.dev/vuln/GO-2026-6610) | `golang.org/x/net@v0.58.0` | An `x/net/http2` transport response forwarded to an HTTP/1 client. | Not exposed: the only `x/net/http2` transport configured in these operations is the Azure SDK's unused private client, and nothing is proxied. The standard-library copy is fixed in Go 1.26.9. | +| [GO-2026-6611](https://pkg.go.dev/vuln/GO-2026-6611), [GO-2026-6348](https://pkg.go.dev/vuln/GO-2026-6348), [GO-2026-6443](https://pkg.go.dev/vuln/GO-2026-6443), [GO-2026-6791](https://pkg.go.dev/vuln/GO-2026-6791) | `x/net`, `grpc`, `containerd` | As above. | Reported HIGH by the scanner; assessed in OpenVEX. | +| [GO-2026-6441](https://pkg.go.dev/vuln/GO-2026-6441) | `google.golang.org/grpc@v1.82.1` | A gRPC xDS server applying RBAC HTTP filters. | Not exposed: no gRPC server is started. | +| [GO-2026-6354](https://pkg.go.dev/vuln/GO-2026-6354), [GO-2026-6355](https://pkg.go.dev/vuln/GO-2026-6355) | `golang.org/x/crypto@v0.55.0` | An SSH connection with a malicious peer. | Not exposed: the importers are Git, Terraform-module, BuildKit, and signature code that image scanning does not call. The download is HTTPS and scans have no network. | +| [GO-2026-5932](https://pkg.go.dev/vuln/GO-2026-5932) | `golang.org/x/crypto@v0.55.0` | Use of the unmaintained OpenPGP packages. | Not exposed: only Rekor PGP and Helm entry types import them, through Rekor SBOM attestation lookup, which requires `--sbom-sources rekor`. No fixed version exists. | +| [GO-2026-6444](https://pkg.go.dev/vuln/GO-2026-6444) | `github.com/containerd/containerd/v2@v2.3.3` | A CRI server handling `ExecSync`. | Not exposed: no CRI package is linked into the binary. | +| [GO-2026-6597](https://pkg.go.dev/vuln/GO-2026-6597), [GO-2026-6796](https://pkg.go.dev/vuln/GO-2026-6796) | `github.com/containerd/containerd/v2@v2.3.3` | containerd image-pull handler traversal or layer unpack. | Not exposed: of the linked Linux packages, only Trivy's `pkg/fanal/image/daemon` imports containerd, and only the daemon image-source table in `NewContainerImage` calls it; `--input` uses `go-containerregistry`'s `tarball` and `layout` readers instead. These records list no vulnerable symbols, so govulncheck also counts linked containerd package initializers. | +| [GO-2026-6505](https://pkg.go.dev/vuln/GO-2026-6505) | `go.opentelemetry.io/otel/exporters/otlp/otlptrace`, with linked `otlptracegrpc` and `go.opentelemetry.io/otel/sdk` | Constructing an OTLP trace exporter. | Not exposed: in the linked Linux build only `github.com/docker/cli/cli/command` imports `otlptracegrpc` and `otel/sdk/trace`, and only Trivy's daemon image source imports that package. The `otel/sdk` metric packages are imported only through Google Cloud Storage under `hashicorp/go-getter`, used for VEX repository, Terraform module, and plugin downloads that these operations do not perform. | +| [GO-2026-4919](https://pkg.go.dev/vuln/GO-2026-4919) | `github.com/aquasecurity/trivy@v0.74.0` | Use of the malicious v0.69.4 release, or of v0.69.5 and v0.69.6 images, published with stolen release credentials in March 2026. | Not affected: the Go record's range is open-ended, but [GHSA-69fq-xp46-6x23](https://github.com/aquasecurity/trivy/security/advisories/GHSA-69fq-xp46-6x23) limits the Go package to `= 0.69.4` and states that the malicious code was never committed to `main`. GitHub's comparison shows release commit `1885610c` is not an ancestor of `e1fd17a0` (merge base `20458b83`). The carried scanner is built from the checksum-verified module source and runs no upstream release workflow or artifact. | These dispositions depend on the invocations, network modes, and severity gate above. Adding an operation, scanner, SBOM source, credential helper, or diff --git a/internal/release/trivy_sbom.go b/internal/release/trivy_sbom.go index 3be33853..54f20928 100644 --- a/internal/release/trivy_sbom.go +++ b/internal/release/trivy_sbom.go @@ -3,7 +3,6 @@ package release import ( "debug/buildinfo" "fmt" - "path" "path/filepath" "regexp" "strings" @@ -63,14 +62,14 @@ func (inventory *releaseSBOMInventory) addTrivyModuleNotices(manifest Manifest, licensed := false for _, entry := range manifest.Entries { relative, found := strings.CutPrefix(entry.Path, prefix) - if !found || entry.Symlink != "" || entry.SHA256 == "" || strings.Contains(relative, "/") { + if !found || entry.Symlink != "" || entry.SHA256 == "" { continue } - licensed = licensed || trivyLicenseNotice.MatchString(path.Base(relative)) + licensed = licensed || !strings.Contains(relative, "/") && trivyLicenseNotice.MatchString(relative) properties = append(properties, cdx.Property{Name: "code-polishy:notice-path", Value: entry.Path}) } if !licensed { - return fmt.Errorf("carried Trivy module %s@%s has no license notice under %s", modulePath, version, prefix) + return fmt.Errorf("carried Trivy module %s@%s has no root license notice under %s", modulePath, version, prefix) } ref := "pkg:golang/" + modulePath + "@" + version component, found := inventory.components[ref] diff --git a/internal/release/trivy_sbom_test.go b/internal/release/trivy_sbom_test.go index 40a7c87d..59463ba2 100644 --- a/internal/release/trivy_sbom_test.go +++ b/internal/release/trivy_sbom_test.go @@ -7,6 +7,7 @@ import ( "os" "os/exec" "path/filepath" + "slices" "strings" "sync" "testing" @@ -40,15 +41,23 @@ func addTrivyReleaseFixture(t *testing.T, files map[string]string, host string) t.Fatal(err) } files[directory+"/trivy"] = string(binary) - for _, shipped := range trivyFixtureModules(t) { + for index, shipped := range trivyFixtureModules(t) { escaped, err := module.EscapePath(shipped.Path) if err != nil { t.Fatal(err) } files[directory+"/licenses/"+escaped+"@"+shipped.Version+"/LICENSE"] = "upstream license\n" + if index == 0 { + files[directory+"/"+trivyFixtureNestedNotice(t, escaped, shipped.Version)] = "forked component license\n" + } } } +func trivyFixtureNestedNotice(t *testing.T, escaped, version string) string { + t.Helper() + return "licenses/" + escaped + "@" + version + "/internal/third_party/forked/LICENSE.txt" +} + func trivyFixtureModules(t *testing.T) []module.Version { t.Helper() binary, err := trivyFixtureBinary() @@ -102,15 +111,30 @@ func TestReleaseSBOMRecordsCarriedTrivyModulesAndTheirNotices(t *testing.T) { if err != nil { t.Fatal(err) } - for _, shipped := range trivyFixtureModules(t) { + for index, shipped := range trivyFixtureModules(t) { ref := "pkg:golang/" + shipped.Path + "@" + shipped.Version component, found := components[ref] if !found || !dependencies[ref] { t.Fatalf("the SBOM does not attribute %s to the carried Trivy", ref) } - if component.Properties == nil || len(*component.Properties) != 1 || - !strings.HasPrefix((*component.Properties)[0].Value, directory+"/licenses/") { - t.Fatalf("%s has no carried notice: %+v", ref, component.Properties) + escaped, err := module.EscapePath(shipped.Path) + if err != nil { + t.Fatal(err) + } + wanted := []string{directory + "/licenses/" + escaped + "@" + shipped.Version + "/LICENSE"} + if index == 0 { + wanted = append(wanted, directory+"/"+trivyFixtureNestedNotice(t, escaped, shipped.Version)) + } + var recorded []string + if component.Properties != nil { + for _, property := range *component.Properties { + recorded = append(recorded, property.Value) + } + } + slices.Sort(wanted) + slices.Sort(recorded) + if !slices.Equal(recorded, wanted) { + t.Fatalf("%s notices = %v, want %v", ref, recorded, wanted) } } } @@ -133,7 +157,7 @@ func TestReleaseSBOMRefusesCarriedTrivyWithoutBuildInformationOrNotices(t *testi name, path, content, problem string }{ {"stub binary", directory + "/trivy", "not a Go binary", "build information"}, - {"missing license", directory + "/licenses/" + escaped + "@" + modules[0].Version + "/LICENSE", "", "has no license notice"}, + {"nested license only", directory + "/licenses/" + escaped + "@" + modules[0].Version + "/LICENSE", "", "has no root license notice"}, } { t.Run(test.name, func(t *testing.T) { files := map[string]string{BinaryPath: "fixture"} diff --git a/scripts/build-release.ps1 b/scripts/build-release.ps1 index 545eaffd..ab739092 100644 --- a/scripts/build-release.ps1 +++ b/scripts/build-release.ps1 @@ -132,6 +132,24 @@ try { )) { Copy-ReleasePath $Relative } Assert-ReleasePythonCarrier $Stage + $TrivyRoot = Join-Path $Stage '.tools/trivy/linux-amd64' + $TrivyExpected = 'go' + (Get-Content -Raw -LiteralPath (Join-Path $Stage 'scripts/go_version.txt')).Trim().TrimStart('g', 'o') + + '-X:jsonv2 github.com/aquasecurity/trivy v' + (Get-Content -Raw -LiteralPath (Join-Path $Stage 'tools/trivy-version.txt')).Trim() + + ' ' + (Get-Content -Raw -LiteralPath (Join-Path $PolicyRoot 'tools/trivy_module_sum.txt')).Trim() + $TrivyBuildInfo = @(& $Go version -m (Join-Path $TrivyRoot 'trivy')) + $TrivyIdentity = '' + if ($LASTEXITCODE -eq 0 -and $TrivyBuildInfo.Count -gt 0) { + $TrivyIdentity = @(($TrivyBuildInfo[0].Trim() -split '\s+')[-1]) + foreach ($Line in $TrivyBuildInfo) { + $Fields = @($Line.Trim() -split '\s+') + if ($Fields[0] -ceq 'mod') { $TrivyIdentity += $Fields[1..3] } + } + $TrivyIdentity = $TrivyIdentity -join ' ' + } + if ($TrivyIdentity -cne $TrivyExpected -or -not (Test-Path -LiteralPath (Join-Path $TrivyRoot 'licenses') -PathType Container)) { + throw "The carried Trivy reports '$TrivyIdentity'; the pins require '$TrivyExpected' with module notices." + } + $StageBin = Join-Path $Stage '.tools/bin' New-Item -ItemType Directory -Force -Path $StageBin | Out-Null foreach ($Tool in @('staticcheck.exe','deadcode.exe','govulncheck.exe','osv-scanner.exe','ruff.exe','ty.exe','cargo-nextest.exe')) { diff --git a/scripts/install.sh b/scripts/install.sh index 7abfd4ac..fdabfcf5 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -354,7 +354,6 @@ carried_tools=( "rustdoc:tools/rust-version.txt" "cargo:tools/rust-version.txt" "cargo-nextest:tools/cargo-nextest-version.txt" - "trivy:tools/trivy-version.txt" ) carried_markers=( @@ -408,11 +407,6 @@ probed_version() { "${policy_root}/.tools/bin/$1" | awk '$1 == "mod" { sub(/^v/, "", $3); print $3; exit }' ;; - trivy) - javascript_sealed_run "${policy_root}/${go_tool_dir}/go/bin/go" version -m \ - "${policy_root}/.tools/trivy/linux-${go_arch_tag}/trivy" | - awk '$1 == "mod" && $2 == "github.com/aquasecurity/trivy" { sub(/^v/, "", $3); print $3; exit }' - ;; osv-scanner) javascript_sealed_run "${policy_root}/.tools/bin/osv-scanner" --version | awk '/^osv-scanner version:/ { print $3 }' @@ -638,6 +632,21 @@ for carried in "${carried_markers[@]}"; do fi done +trivy_carrier=".tools/trivy/linux-${go_arch_tag}" +trivy_expected="go$(pinned_identity scripts/go_version.txt)-X:jsonv2 github.com/aquasecurity/trivy" +trivy_expected+=" v$(pinned_identity tools/trivy-version.txt) $(pinned_identity tools/trivy_module_sum.txt)" +if ! trivy_build_info="$(javascript_sealed_run "${policy_root}/${go_tool_dir}/go/bin/go" version -m \ + "${policy_root}/${trivy_carrier}/trivy")"; then + trivy_build_info="" +fi +trivy_reported="$(awk 'NR == 1 { identity = $NF } $1 == "mod" { identity = identity " " $2 " " $3 " " $4 } END { print identity }' \ + <<<"${trivy_build_info}")" +if [[ "${trivy_reported}" != "${trivy_expected}" || ! -d "${policy_root}/${trivy_carrier}/licenses" ]]; then + echo "The Trivy carrier at ${trivy_carrier} reports ${trivy_reported:-no build identity}; the pins require ${trivy_expected} with module notices." >&2 + echo "Run ./tools/install-policy-tools.sh before installing a release." >&2 + exit 1 +fi + if ! vulture_metadata_is_exact; then echo "The Vulture carrier at ${python_tool_dir} has missing or stale Vulture metadata." >&2 echo "Run ./tools/install-policy-tools.sh before installing a release." >&2 diff --git a/scripts/test-install.sh b/scripts/test-install.sh index dc58f73b..42a6de74 100755 --- a/scripts/test-install.sh +++ b/scripts/test-install.sh @@ -221,6 +221,7 @@ EOF printf '%s\n' 'vulture-2.16-py3-none-any.whl 6e0f1c312cef1c87856957e5c2ca9608834a7c794c2180477f30bf0e4cc58eee' \ >"${source_root}/tools/vulture_wheel_checksums.txt" printf '0.72.0\n' >"${source_root}/tools/trivy-version.txt" + printf 'h1:disposable\n' >"${source_root}/tools/trivy_module_sum.txt" write_file "${source_root}/tools/ty.toml" <<'EOF' [rules] EOF @@ -351,7 +352,10 @@ if [[ "\${1:-}" == "version" && "\${2:-}" == "-m" ]]; then staticcheck) printf '\tmod\thonnef.co/go/tools\tv0.7.0\th1:disposable\n' ;; deadcode) printf '\tmod\tgolang.org/x/tools\tv0.50.0\th1:disposable\n' ;; govulncheck) printf '\tmod\tgolang.org/x/vuln\tv1.3.0\th1:disposable\n' ;; - trivy) printf '\tmod\tgithub.com/aquasecurity/trivy\tv%s\th1:disposable\n' "\$(cat "\${3%/*}/fixture-version")" ;; + trivy) + printf '%s: %s\n\tpath\tgithub.com/aquasecurity/trivy/cmd/trivy\n' "\$3" "\$(cat "\${3%/*}/fixture-go")" + printf '\tmod\tgithub.com/aquasecurity/trivy\tv%s\t%s\n' "\$(cat "\${3%/*}/fixture-version")" "\$(cat "\${3%/*}/fixture-sum")" + ;; *) echo "unexpected stub go module probe: \$*" >&2 exit 1 @@ -411,6 +415,8 @@ EOF printf 'fixture license\n' \ >"${source_root}/.tools/trivy/linux-${go_platform_tag##*-}/licenses/github.com/aquasecurity/trivy@v0.72.0/LICENSE" printf '0.72.0\n' >"${source_root}/.tools/trivy/linux-${go_platform_tag##*-}/fixture-version" + printf 'go1.26.9-X:jsonv2\n' >"${source_root}/.tools/trivy/linux-${go_platform_tag##*-}/fixture-go" + printf 'h1:disposable\n' >"${source_root}/.tools/trivy/linux-${go_platform_tag##*-}/fixture-sum" "${real_git}" -C "${source_root}" init --quiet "${real_git}" -C "${source_root}" config user.email "install-test@example.invalid" @@ -721,15 +727,25 @@ mv "${fixture_root}/held-osv-scanner" "${source_root}/.tools/bin/osv-scanner" fail "a checkout missing a pinned tool changed what is installed" carried_trivy="${source_root}/.tools/trivy/linux-${go_platform_tag##*-}" -printf '0.72.1\n' >"${carried_trivy}/fixture-version" -if install_release >"${fixture_root}/trivy-version.log" 2>&1; then - fail "a checkout carrying the wrong Trivy installed a release" +for wrong in "fixture-version:0.72.1" "fixture-go:go1.26.9" "fixture-sum:h1:other"; do + cp "${carried_trivy}/${wrong%%:*}" "${fixture_root}/held-trivy-evidence" + printf '%s\n' "${wrong#*:}" >"${carried_trivy}/${wrong%%:*}" + if install_release >"${fixture_root}/trivy-identity.log" 2>&1; then + fail "a checkout carrying Trivy with the wrong ${wrong%%:*} installed a release" + fi + grep -q "Trivy carrier.*reports.*${wrong#*:}" "${fixture_root}/trivy-identity.log" || + fail "the carried Trivy with the wrong ${wrong%%:*} was refused without naming it" + mv "${fixture_root}/held-trivy-evidence" "${carried_trivy}/${wrong%%:*}" +done +mv "${carried_trivy}/licenses" "${fixture_root}/held-trivy-licenses" +if install_release >"${fixture_root}/trivy-notices.log" 2>&1; then + fail "a checkout carrying Trivy without module notices installed a release" fi -grep -q "trivy.*0.72.1" "${fixture_root}/trivy-version.log" || - fail "the wrong carried Trivy was refused without naming it" -printf '0.72.0\n' >"${carried_trivy}/fixture-version" +grep -q "with module notices" "${fixture_root}/trivy-notices.log" || + fail "the carried Trivy without module notices was refused for the wrong reason" +mv "${fixture_root}/held-trivy-licenses" "${carried_trivy}/licenses" [[ "$(installed_release_count)" == "1" ]] || - fail "a checkout carrying the wrong Trivy changed what is installed" + fail "a checkout carrying an inexact Trivy changed what is installed" for rust_tool in rustc rustdoc cargo; do write_version_tool "${source_root}/.tools/rust/${platform_tag}/bin/${rust_tool}" --version "${rust_tool} 1.97.2 (fixture)" diff --git a/tools/install-go-tools.sh b/tools/install-go-tools.sh index 7cd6933e..2adac557 100755 --- a/tools/install-go-tools.sh +++ b/tools/install-go-tools.sh @@ -30,30 +30,48 @@ trivy_identity() { } trivy_notices() { - local binary="$1" module_cache="$2" destination="$3" module version escaped notice licensed - if "${repo_root}/scripts/go.sh" version -m "${binary}" | awk '$1 == "=>" { found = 1 } END { exit !found }'; then + local binary="$1" module_cache="$2" destination="$3" build_info modules module version escaped source notices notice relative licensed + if ! build_info="$("${repo_root}/scripts/go.sh" version -m "${binary}")"; then + echo "The built Trivy has no readable build information." >&2 + return 1 + fi + if awk '$1 == "=>" { found = 1 } END { exit !found }' <<<"${build_info}"; then echo "The built Trivy replaces a module, so its notices cannot be attributed." >&2 return 1 fi + modules="$(awk '$1 == "dep" || $1 == "mod" { print $2, $3 }' <<<"${build_info}")" + if ! awk '$1 == "mod" && $2 == "github.com/aquasecurity/trivy" { found = 1 } END { exit !found }' <<<"${build_info}"; then + echo "The built Trivy build information names no Trivy module." >&2 + return 1 + fi while read -r module version; do + if [[ -z "${module}" || -z "${version}" ]]; then + echo "The built Trivy build information has a malformed module record." >&2 + return 1 + fi escaped="$(printf '%s\n' "${module}" | awk '{ out = ""; for (i = 1; i <= length($0); i++) { c = substr($0, i, 1); out = out (c ~ /[A-Z]/ ? "!" tolower(c) : c) } print out }')" - mkdir -p "${destination}/${escaped}@${version}" + source="${module_cache}/${escaped}@${version}" + if [[ ! -d "${source}" ]] || ! notices="$(find "${source}" -type f \( -iname 'licen[cs]e*' -o -iname 'copying*' -o -iname 'notice*' -o -iname 'patents*' \) -print)"; then + echo "Trivy module ${module}@${version} has no readable source in the module cache." >&2 + return 1 + fi licensed=false - for notice in "${module_cache}/${escaped}@${version}"/*; do - [[ -f "${notice}" && ! -L "${notice}" ]] || continue - case "$(basename "${notice}")" in + while IFS= read -r notice; do + [[ -n "${notice}" ]] || continue + relative="${notice#"${source}/"}" + case "${relative}" in + */*) ;; [Ll][Ii][Cc][Ee][Nn][CcSs][Ee]* | [Cc][Oo][Pp][Yy][Ii][Nn][Gg]*) licensed=true ;; - [Nn][Oo][Tt][Ii][Cc][Ee]* | [Pp][Aa][Tt][Ee][Nn][Tt][Ss]*) ;; - *) continue ;; esac - cp "${notice}" "${destination}/${escaped}@${version}/" - chmod 0644 "${destination}/${escaped}@${version}/$(basename "${notice}")" - done + mkdir -p "$(dirname "${destination}/${escaped}@${version}/${relative}")" + cp "${notice}" "${destination}/${escaped}@${version}/${relative}" + chmod 0644 "${destination}/${escaped}@${version}/${relative}" + done <<<"${notices}" if [[ "${licensed}" != true ]]; then - echo "Trivy module ${module}@${version} carries no license file." >&2 + echo "Trivy module ${module}@${version} carries no license file at its root." >&2 return 1 fi - done < <("${repo_root}/scripts/go.sh" version -m "${binary}" | awk '$1 == "dep" || $1 == "mod" { print $2, $3 }') + done <<<"${modules}" } if [[ -f "${trivy_dir}/trivy" && -d "${trivy_dir}/licenses" && "$(trivy_identity "${trivy_dir}/trivy")" == "${trivy_expected}" ]]; then diff --git a/tools/install-policy-tools.ps1 b/tools/install-policy-tools.ps1 index da0dac46..c438883f 100644 --- a/tools/install-policy-tools.ps1 +++ b/tools/install-policy-tools.ps1 @@ -118,22 +118,39 @@ try { } function Copy-TrivyNotices([string]$Binary, [string]$ModuleCache, [string]$Destination) { $Lines = @(& $Go version -m $Binary) - if ($LASTEXITCODE -ne 0) { throw 'Trivy build information is unreadable.' } + if ($LASTEXITCODE -ne 0 -or $Lines.Count -eq 0) { throw 'The built Trivy has no readable build information.' } + $Modules = @() foreach ($Line in $Lines) { $Fields = @($Line.Trim() -split '\s+') if ($Fields[0] -ceq '=>') { throw 'The built Trivy replaces a module, so its notices cannot be attributed.' } if ($Fields[0] -cne 'dep' -and $Fields[0] -cne 'mod') { continue } - $Escaped = [regex]::Replace($Fields[1], '[A-Z]', { param($Match) '!' + $Match.Value.ToLowerInvariant() }) - $Source = Join-Path $ModuleCache "$Escaped@$($Fields[2])" - $Target = Join-Path $Destination "$Escaped@$($Fields[2])" - New-Item -ItemType Directory -Force -Path $Target | Out-Null - $Notices = @(Get-ChildItem -LiteralPath $Source -File | Where-Object { $_.Name -match '^(?i)(licen[cs]e|copying|notice|patents)' }) - if (@($Notices | Where-Object { $_.Name -match '^(?i)(licen[cs]e|copying)' }).Count -eq 0) { - throw "Trivy module $($Fields[1])@$($Fields[2]) carries no license file." + if ($Fields.Count -lt 3 -or -not $Fields[1] -or -not $Fields[2]) { throw 'The built Trivy build information has a malformed module record.' } + $Modules += , @($Fields[0], $Fields[1], $Fields[2]) + } + if (@($Modules | Where-Object { $_[0] -ceq 'mod' -and $_[1] -ceq 'github.com/aquasecurity/trivy' }).Count -ne 1) { + throw 'The built Trivy build information names no Trivy module.' + } + foreach ($Module in $Modules) { + $Escaped = [regex]::Replace($Module[1], '[A-Z]', { param($Match) '!' + $Match.Value.ToLowerInvariant() }) + $Source = Join-Path $ModuleCache "$Escaped@$($Module[2])" + if (-not (Test-Path -LiteralPath $Source -PathType Container)) { throw "Trivy module $($Module[1])@$($Module[2]) has no readable source in the module cache." } + $SourceRoot = (Resolve-Path -LiteralPath $Source).Path.TrimEnd('\', '/') + $Target = Join-Path $Destination "$Escaped@$($Module[2])" + $Licensed = $false + $Notices = @(Get-ChildItem -LiteralPath $SourceRoot -Recurse -File | Where-Object { + $_.Name -match '^(?i)(licen[cs]e|copying|notice|patents)' -and -not ($_.Attributes -band [IO.FileAttributes]::ReparsePoint) + }) + foreach ($Notice in $Notices) { + $Relative = $Notice.FullName.Substring($SourceRoot.Length + 1) + if ($Relative -notmatch '[\\/]' -and $Notice.Name -match '^(?i)(licen[cs]e|copying)') { $Licensed = $true } + $Copy = Join-Path $Target $Relative + New-Item -ItemType Directory -Force -Path (Split-Path -Parent $Copy) | Out-Null + Copy-Item -LiteralPath $Notice.FullName -Destination $Copy } - foreach ($Notice in $Notices) { Copy-Item -LiteralPath $Notice.FullName -Destination (Join-Path $Target $Notice.Name) } + if (-not $Licensed) { throw "Trivy module $($Module[1])@$($Module[2]) carries no license file at its root." } } } + if ((Get-TrivyIdentity (Join-Path $TrivyRoot 'trivy')) -cne $TrivyExpected -or -not (Test-Path -LiteralPath (Join-Path $TrivyRoot 'licenses') -PathType Container)) { $TrivyScratch = Join-Path $Scratch 'trivy' From 7d0151b01970409eeda2db6722519c1af1502d72 Mon Sep 17 00:00:00 2001 From: Jeff Musgrave <1578618+JeffMusgrave@users.noreply.github.com> Date: Thu, 22 Oct 2026 00:03:26 -0400 Subject: [PATCH 08/15] Admit only complete Trivy notice layouts and exact Linux builds Prefix-only names missed compiled modules' notices such as gocsaf's LICENSES directory and 3rdpartylicenses.md, sergi/go-diff's APACHE-LICENSE-2.0, and shibumi/go-pathspec's GO-LICENSE. The copiers now take every file under a LICENSES directory and every name containing license, licence, copying, notice, or patents, except source, configuration, and test-data formats, which also cannot satisfy a module's root license. The copiers record code-polishy-trivy-notices-v2 only after a complete copy. The installer's reuse check, release installation, the Windows release build, and the release SBOM refuse a missing or earlier layout, so carriers copied under earlier rules are rebuilt rather than reused. Carrier identity now also requires CGO_ENABLED=0, the expected GOARCH, GOOS=linux, and -trimpath=true, so a same-module build for another platform is refused. The install contract exercises the copier's naming forms and failures and the stale-layout and wrong-architecture refusals. Co-Authored-By: Claude Opus 5.5 --- .code-polishy.json | 2 + docs/design/carried-trivy-scanner.md | 61 ++++++++++------ internal/release/trivy_sbom.go | 33 ++++++++- internal/release/trivy_sbom_test.go | 69 +++++++++--------- scripts/build-release.ps1 | 22 ++++-- scripts/install.sh | 16 ++++- scripts/test-install.sh | 103 +++++++++++++++++++++++++-- tools/install-go-tools.sh | 59 ++++++++++----- tools/install-policy-tools.ps1 | 58 ++++++++++----- 9 files changed, 317 insertions(+), 106 deletions(-) diff --git a/.code-polishy.json b/.code-polishy.json index 8319ac54..d814b571 100644 --- a/.code-polishy.json +++ b/.code-polishy.json @@ -1058,6 +1058,8 @@ "templates/**", "tools/*-version.txt", "tools/install-p*", + "tools/install-go-tools.sh", + "tools/trivy_module_sum.txt", "tools/javascript**", "tools/packaging_wheel_checksums.txt", "scripts/test-install.sh" diff --git a/docs/design/carried-trivy-scanner.md b/docs/design/carried-trivy-scanner.md index 53c220dd..3de5ccce 100644 --- a/docs/design/carried-trivy-scanner.md +++ b/docs/design/carried-trivy-scanner.md @@ -14,33 +14,50 @@ and build cache, ignoring user Go configuration and private-module exceptions. They fetch only from `proxy.golang.org`, verify against `sum.golang.org`, and copy the official build's settings: `CGO_ENABLED=0`, `GOEXPERIMENT=jsonv2`, and `-s -w -X github.com/aquasecurity/trivy/pkg/version/app.ver=0.74.0`, adding -`-trimpath`. The result is accepted only when its build information names -`go1.26.10-X:jsonv2` and exactly that module version and sum. The pinned module +`-trimpath`. The result must match the carrier identity below. The pinned module content includes Trivy's `go.sum`, so the module graph is frozen. The installer keeps the binary in `.tools/trivy/linux-` for the host -architecture; Windows releases carry `linux-amd64`. Release installation on -Linux and macOS, and the Windows release build, refuse a carried binary whose -build information does not name exactly `go1.26.10-X:jsonv2`, Trivy's module, -version, and checksum, or that has no module notices. The release manifest -covers its bytes, so the runtime trusts the installed release rather than -rechecking them. +architecture; Windows releases carry `linux-amd64`. + +A carrier is admitted only when the binary's build information records exactly +`go1.26.10-X:jsonv2`, Trivy's module, version, and checksum, and the build +settings `CGO_ENABLED=0`, the expected `GOARCH`, `GOOS=linux`, and +`-trimpath=true`, so a same-module build for another platform or with other +settings is refused. The tool installer's reuse check, release installation on +Linux and macOS, and the Windows release build apply that identity. The release +manifest covers the admitted bytes, so the runtime trusts the installed release +rather than rechecking them. Distributing the binary distributes every module linked into it. For each module the binary's build information names, including Trivy itself, the -installer copies every license, copying, notice, and patent file anywhere in -that module's checksum-verified module cache directory into -`licenses/@/`, keeping its relative path. Forked -or vendored code inside a module, such as `zstd/internal/xxhash` in -`klauspost/compress` or `third_party/forked/golang` in Kubernetes modules, -keeps its own attribution. Files under test data are included rather than -pruned by package reachability. Unreadable or empty build information, a module -missing from the cache or without a root license or copying file, or a replaced -module fails the build. Release manifest version 9 records Trivy's version as -a tool. The release SBOM reads the carried binary's build information, lists -Trivy with every linked module as its dependency, and records each module's -root and nested notice paths. A release whose carried binary lacks build -information, or whose module lacks a root license notice, cannot produce an -SBOM. +installer copies, with its relative path, every regular file under a +`LICENSES` directory and every file whose name contains `license`, `licence`, +`copying`, `notice`, or `patents`. Name matching also covers forms such as +`APACHE-LICENSE-2.0`, `GO-LICENSE`, `3rdpartylicenses.md`, and +`*.json.license`, and nested forks such as `zstd/internal/xxhash` or +`third_party/forked/golang` keep their own attribution. Source, configuration, +and test-data formats such as `.go`, `.c`, `.sh`, `.rego`, YAML, JSON, and XML +are not notices and are not copied outside a `LICENSES` directory, even when +their names mention a license. Each module still needs a non-source license or +copying file at its root. Unreadable or empty build information, a module +missing from the cache, a replaced module, or a module without that root +license fails the build. + +The installer writes `notices-layout` containing +`code-polishy-trivy-notices-v2` only after every module's notices are copied. +The marker names the notice rules above, not a digest; it distinguishes a +complete carrier from one copied under earlier rules or interrupted, which the +identity alone cannot. A missing or different marker makes the installer +rebuild the carrier, and release installation, the Windows release build, and +SBOM composition refuse it. There is no compatibility path for earlier +layouts; changing the copy rules requires a new marker value. + +Release manifest version 9 records Trivy's version as a tool. The release SBOM +reads the carried binary's build information, lists Trivy with every linked +module as its dependency, and records each module's root and nested notice +paths. A release whose carried binary lacks build information, whose module +lacks a root license notice, or whose notices lack the current layout marker +cannot produce an SBOM. The resulting graph has the same 375 dependency versions and checksums as the official binary in diff --git a/internal/release/trivy_sbom.go b/internal/release/trivy_sbom.go index 54f20928..ae6124af 100644 --- a/internal/release/trivy_sbom.go +++ b/internal/release/trivy_sbom.go @@ -3,6 +3,7 @@ package release import ( "debug/buildinfo" "fmt" + "os" "path/filepath" "regexp" "strings" @@ -11,7 +12,12 @@ import ( "golang.org/x/mod/module" ) -var trivyLicenseNotice = regexp.MustCompile(`(?i)^(licen[cs]e|copying)`) +const trivyNoticeLayout = "code-polishy-trivy-notices-v2" + +var ( + trivyLicenseNotice = regexp.MustCompile(`(?i)licen[cs]e|copying`) + trivySourceFile = regexp.MustCompile(`(?i)\.(go|s|c|h|cc|cpp|proto|py|js|ts|sh|rego|ya?ml|json|xml|toml|mod|sum|pom|gemspec|jar|golden)$`) +) func trivyReleaseDirectory(host string) (string, error) { switch { @@ -36,6 +42,9 @@ func (inventory *releaseSBOMInventory) addTrivyNotices(root string, manifest Man if !manifestRecordsCapabilityFile(manifest, binary, "") { return fmt.Errorf("carried Trivy is absent from the release manifest: %s", binary) } + if err := requireTrivyNoticeLayout(root, manifest, directory); err != nil { + return err + } info, err := buildinfo.ReadFile(filepath.Join(root, filepath.FromSlash(binary))) if err != nil { return fmt.Errorf("read carried Trivy build information: %w", err) @@ -65,7 +74,7 @@ func (inventory *releaseSBOMInventory) addTrivyModuleNotices(manifest Manifest, if !found || entry.Symlink != "" || entry.SHA256 == "" { continue } - licensed = licensed || !strings.Contains(relative, "/") && trivyLicenseNotice.MatchString(relative) + licensed = licensed || !strings.Contains(relative, "/") && trivyLicenseNotice.MatchString(relative) && !trivySourceFile.MatchString(relative) properties = append(properties, cdx.Property{Name: "code-polishy:notice-path", Value: entry.Path}) } if !licensed { @@ -80,3 +89,23 @@ func (inventory *releaseSBOMInventory) addTrivyModuleNotices(manifest Manifest, inventory.components[ref] = component return nil } + +func requireTrivyNoticeLayout(root string, manifest Manifest, directory string) error { + marker := directory + "/notices-layout" + if !manifestRecordsCapabilityFile(manifest, marker, "") { + return fmt.Errorf("carried Trivy notices are incomplete: %s is absent from the release manifest", marker) + } + sealed, err := os.OpenRoot(root) + if err != nil { + return err + } + defer sealed.Close() + data, err := readCapabilityFile(sealed, marker, 256) + if err != nil { + return err + } + if strings.TrimSpace(string(data)) != trivyNoticeLayout { + return fmt.Errorf("carried Trivy notices use layout %q, not %s", strings.TrimSpace(string(data)), trivyNoticeLayout) + } + return nil +} diff --git a/internal/release/trivy_sbom_test.go b/internal/release/trivy_sbom_test.go index 59463ba2..ffb18902 100644 --- a/internal/release/trivy_sbom_test.go +++ b/internal/release/trivy_sbom_test.go @@ -41,21 +41,31 @@ func addTrivyReleaseFixture(t *testing.T, files map[string]string, host string) t.Fatal(err) } files[directory+"/trivy"] = string(binary) + files[directory+"/notices-layout"] = trivyNoticeLayout + "\n" for index, shipped := range trivyFixtureModules(t) { - escaped, err := module.EscapePath(shipped.Path) - if err != nil { - t.Fatal(err) - } - files[directory+"/licenses/"+escaped+"@"+shipped.Version+"/LICENSE"] = "upstream license\n" - if index == 0 { - files[directory+"/"+trivyFixtureNestedNotice(t, escaped, shipped.Version)] = "forked component license\n" + for _, notice := range trivyFixtureNotices(t, index, shipped) { + files[directory+"/"+notice] = "upstream notice\n" } } } -func trivyFixtureNestedNotice(t *testing.T, escaped, version string) string { +func trivyFixtureNotices(t *testing.T, index int, shipped module.Version) []string { t.Helper() - return "licenses/" + escaped + "@" + version + "/internal/third_party/forked/LICENSE.txt" + escaped, err := module.EscapePath(shipped.Path) + if err != nil { + t.Fatal(err) + } + prefix := "licenses/" + escaped + "@" + shipped.Version + "/" + switch index { + case 0: + return []string{prefix + "LICENSE", prefix + "internal/third_party/forked/LICENSE.txt", prefix + "zstd/internal/xxhash/NOTICE.txt"} + case 1: + return []string{prefix + "APACHE-LICENSE-2.0", prefix + "LICENSES/BSD-3-Clause.txt", prefix + "3rdpartylicenses.md"} + case 2: + return []string{prefix + "GO-LICENSE", prefix + "schema/cvss-v3.1.json.license"} + default: + return []string{prefix + "LICENSE"} + } } func trivyFixtureModules(t *testing.T) []module.Version { @@ -76,8 +86,8 @@ func trivyFixtureModules(t *testing.T) []module.Version { } modules = append(modules, module.Version{Path: resolved.Path, Version: resolved.Version}) } - if len(modules) == 0 { - t.Fatal("the fixture binary records no shipped modules") + if len(modules) < 3 { + t.Fatalf("the fixture binary records only %d shipped modules", len(modules)) } return modules } @@ -117,13 +127,9 @@ func TestReleaseSBOMRecordsCarriedTrivyModulesAndTheirNotices(t *testing.T) { if !found || !dependencies[ref] { t.Fatalf("the SBOM does not attribute %s to the carried Trivy", ref) } - escaped, err := module.EscapePath(shipped.Path) - if err != nil { - t.Fatal(err) - } - wanted := []string{directory + "/licenses/" + escaped + "@" + shipped.Version + "/LICENSE"} - if index == 0 { - wanted = append(wanted, directory+"/"+trivyFixtureNestedNotice(t, escaped, shipped.Version)) + var wanted []string + for _, notice := range trivyFixtureNotices(t, index, shipped) { + wanted = append(wanted, directory+"/"+notice) } var recorded []string if component.Properties != nil { @@ -148,26 +154,25 @@ func TestReleaseSBOMRefusesCarriedTrivyWithoutBuildInformationOrNotices(t *testi if err != nil { t.Fatal(err) } - modules := trivyFixtureModules(t) - escaped, err := module.EscapePath(modules[0].Path) - if err != nil { - t.Fatal(err) - } + first := directory + "/" + trivyFixtureNotices(t, 0, trivyFixtureModules(t)[0])[0] for _, test := range []struct { - name, path, content, problem string + name string + change func(map[string]string) + problem string }{ - {"stub binary", directory + "/trivy", "not a Go binary", "build information"}, - {"nested license only", directory + "/licenses/" + escaped + "@" + modules[0].Version + "/LICENSE", "", "has no root license notice"}, + {"stub binary", func(files map[string]string) { files[directory+"/trivy"] = "not a Go binary" }, "build information"}, + {"nested license only", func(files map[string]string) { delete(files, first) }, "has no root license notice"}, + {"root Go source named for a license", func(files map[string]string) { + delete(files, first) + files[strings.TrimSuffix(first, "LICENSE")+"license.go"] = "package license\n" + }, "has no root license notice"}, + {"missing notice layout", func(files map[string]string) { delete(files, directory+"/notices-layout") }, "notices are incomplete"}, + {"earlier notice layout", func(files map[string]string) { files[directory+"/notices-layout"] = "code-polishy-trivy-notices-v1\n" }, "use layout"}, } { t.Run(test.name, func(t *testing.T) { files := map[string]string{BinaryPath: "fixture"} addTrivyReleaseFixture(t, files, host) - if test.content == "" { - delete(files, test.path) - files[strings.TrimSuffix(test.path, "LICENSE")+"README.md"] = "not a license\n" - } else { - files[test.path] = test.content - } + test.change(files) root, manifest := installedRelease(t, files, nil) if _, err := renderReleaseSBOM(manifest, root, "release.zip", exampleDigest); err == nil || !strings.Contains(err.Error(), test.problem) { t.Fatalf("SBOM error = %v, want %q", err, test.problem) diff --git a/scripts/build-release.ps1 b/scripts/build-release.ps1 index ab739092..7e6e0b27 100644 --- a/scripts/build-release.ps1 +++ b/scripts/build-release.ps1 @@ -135,19 +135,29 @@ try { $TrivyRoot = Join-Path $Stage '.tools/trivy/linux-amd64' $TrivyExpected = 'go' + (Get-Content -Raw -LiteralPath (Join-Path $Stage 'scripts/go_version.txt')).Trim().TrimStart('g', 'o') + '-X:jsonv2 github.com/aquasecurity/trivy v' + (Get-Content -Raw -LiteralPath (Join-Path $Stage 'tools/trivy-version.txt')).Trim() + - ' ' + (Get-Content -Raw -LiteralPath (Join-Path $PolicyRoot 'tools/trivy_module_sum.txt')).Trim() + ' ' + (Get-Content -Raw -LiteralPath (Join-Path $PolicyRoot 'tools/trivy_module_sum.txt')).Trim() + + ' CGO_ENABLED=0 GOARCH=amd64 GOOS=linux -trimpath=true' $TrivyBuildInfo = @(& $Go version -m (Join-Path $TrivyRoot 'trivy')) $TrivyIdentity = '' if ($LASTEXITCODE -eq 0 -and $TrivyBuildInfo.Count -gt 0) { - $TrivyIdentity = @(($TrivyBuildInfo[0].Trim() -split '\s+')[-1]) + $TrivyModule = '' + $TrivySettings = @{} foreach ($Line in $TrivyBuildInfo) { $Fields = @($Line.Trim() -split '\s+') - if ($Fields[0] -ceq 'mod') { $TrivyIdentity += $Fields[1..3] } + if ($Fields[0] -ceq 'mod') { $TrivyModule = $Fields[1..3] -join ' ' } + if ($Fields[0] -ceq 'build' -and $Fields.Count -ge 2 -and $Fields[1] -cmatch '^(CGO_ENABLED|GOARCH|GOOS|-trimpath)=') { + $TrivySettings[$Fields[1].Split('=')[0]] = $Fields[1] + } } - $TrivyIdentity = $TrivyIdentity -join ' ' + $TrivyIdentity = (@(($TrivyBuildInfo[0].Trim() -split '\s+')[-1], $TrivyModule) + + @('CGO_ENABLED', 'GOARCH', 'GOOS', '-trimpath' | ForEach-Object { $TrivySettings[$_] })) -join ' ' } - if ($TrivyIdentity -cne $TrivyExpected -or -not (Test-Path -LiteralPath (Join-Path $TrivyRoot 'licenses') -PathType Container)) { - throw "The carried Trivy reports '$TrivyIdentity'; the pins require '$TrivyExpected' with module notices." + if ($TrivyIdentity -cne $TrivyExpected) { throw "The carried Trivy reports '$TrivyIdentity'; the pins require '$TrivyExpected'." } + $TrivyLayout = Join-Path $TrivyRoot 'notices-layout' + if (-not (Test-Path -LiteralPath (Join-Path $TrivyRoot 'licenses') -PathType Container) -or + -not (Test-Path -LiteralPath $TrivyLayout -PathType Leaf) -or + (Get-Content -Raw -LiteralPath $TrivyLayout).Trim() -cne 'code-polishy-trivy-notices-v2') { + throw 'The carried Trivy lacks complete code-polishy-trivy-notices-v2 module notices.' } $StageBin = Join-Path $Stage '.tools/bin' diff --git a/scripts/install.sh b/scripts/install.sh index fdabfcf5..af549aaf 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -635,14 +635,24 @@ done trivy_carrier=".tools/trivy/linux-${go_arch_tag}" trivy_expected="go$(pinned_identity scripts/go_version.txt)-X:jsonv2 github.com/aquasecurity/trivy" trivy_expected+=" v$(pinned_identity tools/trivy-version.txt) $(pinned_identity tools/trivy_module_sum.txt)" +trivy_expected+=" CGO_ENABLED=0 GOARCH=${go_arch_tag} GOOS=linux -trimpath=true" if ! trivy_build_info="$(javascript_sealed_run "${policy_root}/${go_tool_dir}/go/bin/go" version -m \ "${policy_root}/${trivy_carrier}/trivy")"; then trivy_build_info="" fi -trivy_reported="$(awk 'NR == 1 { identity = $NF } $1 == "mod" { identity = identity " " $2 " " $3 " " $4 } END { print identity }' \ +trivy_reported="$(awk 'NR == 1 { go = $NF } + $1 == "mod" { module = $2 " " $3 " " $4 } + $1 == "build" && $2 ~ /^(CGO_ENABLED|GOARCH|GOOS|-trimpath)=/ { split($2, setting, "="); value[setting[1]] = $2 } + END { print go " " module " " value["CGO_ENABLED"] " " value["GOARCH"] " " value["GOOS"] " " value["-trimpath"] }' \ <<<"${trivy_build_info}")" -if [[ "${trivy_reported}" != "${trivy_expected}" || ! -d "${policy_root}/${trivy_carrier}/licenses" ]]; then - echo "The Trivy carrier at ${trivy_carrier} reports ${trivy_reported:-no build identity}; the pins require ${trivy_expected} with module notices." >&2 +if [[ "${trivy_reported}" != "${trivy_expected}" ]]; then + echo "The Trivy carrier at ${trivy_carrier} reports ${trivy_reported}; the pins require ${trivy_expected}." >&2 + echo "Run ./tools/install-policy-tools.sh before installing a release." >&2 + exit 1 +fi +if [[ ! -d "${policy_root}/${trivy_carrier}/licenses" || + "$(cat "${policy_root}/${trivy_carrier}/notices-layout" 2>/dev/null || true)" != "code-polishy-trivy-notices-v2" ]]; then + echo "The Trivy carrier at ${trivy_carrier} lacks complete code-polishy-trivy-notices-v2 module notices." >&2 echo "Run ./tools/install-policy-tools.sh before installing a release." >&2 exit 1 fi diff --git a/scripts/test-install.sh b/scripts/test-install.sh index 42a6de74..71ab8df1 100755 --- a/scripts/test-install.sh +++ b/scripts/test-install.sh @@ -355,6 +355,7 @@ if [[ "\${1:-}" == "version" && "\${2:-}" == "-m" ]]; then trivy) printf '%s: %s\n\tpath\tgithub.com/aquasecurity/trivy/cmd/trivy\n' "\$3" "\$(cat "\${3%/*}/fixture-go")" printf '\tmod\tgithub.com/aquasecurity/trivy\tv%s\t%s\n' "\$(cat "\${3%/*}/fixture-version")" "\$(cat "\${3%/*}/fixture-sum")" + cat "\${3%/*}/fixture-build" ;; *) echo "unexpected stub go module probe: \$*" >&2 @@ -417,6 +418,9 @@ EOF printf '0.72.0\n' >"${source_root}/.tools/trivy/linux-${go_platform_tag##*-}/fixture-version" printf 'go1.26.9-X:jsonv2\n' >"${source_root}/.tools/trivy/linux-${go_platform_tag##*-}/fixture-go" printf 'h1:disposable\n' >"${source_root}/.tools/trivy/linux-${go_platform_tag##*-}/fixture-sum" + printf '\tbuild\t%s\n' CGO_ENABLED=0 "GOARCH=${go_platform_tag##*-}" GOOS=linux -trimpath=true \ + >"${source_root}/.tools/trivy/linux-${go_platform_tag##*-}/fixture-build" + printf 'code-polishy-trivy-notices-v2\n' >"${source_root}/.tools/trivy/linux-${go_platform_tag##*-}/notices-layout" "${real_git}" -C "${source_root}" init --quiet "${real_git}" -C "${source_root}" config user.email "install-test@example.invalid" @@ -726,6 +730,79 @@ mv "${fixture_root}/held-osv-scanner" "${source_root}/.tools/bin/osv-scanner" [[ "$(installed_release_count)" == "1" ]] || fail "a checkout missing a pinned tool changed what is installed" +notice_root="${fixture_root}/trivy-notices" +mkdir -p "${notice_root}/repo/scripts" +write_file "${notice_root}/repo/scripts/go.sh" <<'EOF' +#!/usr/bin/env bash +[[ -f "${TRIVY_FIXTURE_BUILD_INFO}" ]] || exit 1 +cat "${TRIVY_FIXTURE_BUILD_INFO}" +EOF +chmod +x "${notice_root}/repo/scripts/go.sh" +sed -n '/^trivy_notice_layout=/p; /^trivy_notice_candidates()/,/^}/p; /^trivy_notices()/,/^}/p' \ + "${policy_root}/tools/install-go-tools.sh" >"${notice_root}/functions.sh" +grep -q '^trivy_notices()' "${notice_root}/functions.sh" || fail "the Trivy notice copier was not found" +notice_cache="${notice_root}/cache" +write_notice_source() { + mkdir -p "$(dirname "${notice_cache}/$1")" + printf '%s\n' "$1" >"${notice_cache}/$1" +} +for source_file in \ + github.com/aquasecurity/trivy@v0.74.0/LICENSE github.com/aquasecurity/trivy@v0.74.0/NOTICE \ + github.com/aquasecurity/trivy@v0.74.0/pkg/licensing/license.go \ + github.com/aquasecurity/trivy@v0.74.0/pkg/x/testdata/valid-license-id.json \ + 'example.com/!forked@v1.0.0/APACHE-LICENSE-2.0' 'example.com/!forked@v1.0.0/LICENSES/BSD-3-Clause.txt' \ + 'example.com/!forked@v1.0.0/LICENSES/MIT.txt' 'example.com/!forked@v1.0.0/3rdpartylicenses.md' \ + 'example.com/!forked@v1.0.0/zstd/internal/xxhash/LICENSE.txt' 'example.com/!forked@v1.0.0/cvss3enums.go' \ + example.com/pathspec@v1.3.0/GO-LICENSE example.com/pathspec@v1.3.0/schema/cvss-v3.1.json.license \ + example.com/sourceonly@v1.0.0/license.go example.com/sourceonly@v1.0.0/third_party/LICENSE; do + write_notice_source "${source_file}" +done +write_notice_build_info() { + { + printf 'trivy: go1.26.10-X:jsonv2\n\tmod\tgithub.com/aquasecurity/trivy\tv0.74.0\th1:x\n' + printf '\tdep\texample.com/Forked\tv1.0.0\th1:y\n\tdep\texample.com/pathspec\tv1.3.0\th1:z\n' + [[ "$1" != source-only ]] || printf '\tdep\texample.com/sourceonly\tv1.0.0\th1:w\n' + [[ "$1" != missing ]] || printf '\tdep\texample.com/absent\tv1.0.0\th1:v\n' + } >"${notice_root}/build-info" +} +copy_trivy_notices() { + rm -rf "${notice_root}/carrier" + mkdir -p "${notice_root}/carrier" + repo_root="${notice_root}/repo" TRIVY_FIXTURE_BUILD_INFO="$1" bash -c \ + 'set -euo pipefail; source "$1"; trivy_notices trivy "$2" "$3"' _ \ + "${notice_root}/functions.sh" "${notice_cache}" "${notice_root}/carrier" >"${notice_root}/copy.log" 2>&1 +} +write_notice_build_info complete +copy_trivy_notices "${notice_root}/build-info" || + fail "the Trivy notice copier rejected complete notices: $(cat "${notice_root}/copy.log")" +expected_notices="$(printf '%s\n' \ + 'example.com/!forked@v1.0.0/3rdpartylicenses.md' 'example.com/!forked@v1.0.0/APACHE-LICENSE-2.0' \ + 'example.com/!forked@v1.0.0/LICENSES/BSD-3-Clause.txt' 'example.com/!forked@v1.0.0/LICENSES/MIT.txt' \ + 'example.com/!forked@v1.0.0/zstd/internal/xxhash/LICENSE.txt' example.com/pathspec@v1.3.0/GO-LICENSE \ + example.com/pathspec@v1.3.0/schema/cvss-v3.1.json.license github.com/aquasecurity/trivy@v0.74.0/LICENSE \ + github.com/aquasecurity/trivy@v0.74.0/NOTICE | LC_ALL=C sort)" +copied_notices="$(cd "${notice_root}/carrier/licenses" && find . -type f | sed 's#^\./##' | LC_ALL=C sort)" +[[ "${copied_notices}" == "${expected_notices}" ]] || + fail "the Trivy notice copier copied: ${copied_notices}" +cmp -s "${notice_cache}/example.com/!forked@v1.0.0/LICENSES/MIT.txt" \ + "${notice_root}/carrier/licenses/example.com/!forked@v1.0.0/LICENSES/MIT.txt" || + fail "the Trivy notice copier changed a notice" +[[ "$(cat "${notice_root}/carrier/notices-layout")" == code-polishy-trivy-notices-v2 ]] || + fail "complete Trivy notices did not record their layout" +for failure in "source-only:example.com/sourceonly@v1.0.0 carries no license file at its root" \ + "missing:example.com/absent@v1.0.0 has no readable source" "unreadable:no readable build information"; do + write_notice_build_info "${failure%%:*}" + build_info="${notice_root}/build-info" + [[ "${failure%%:*}" != unreadable ]] || build_info="${notice_root}/absent-build-info" + if copy_trivy_notices "${build_info}"; then + fail "the Trivy notice copier accepted ${failure%%:*} evidence" + fi + grep -qF "${failure#*:}" "${notice_root}/copy.log" || + fail "the Trivy notice copier refused ${failure%%:*} evidence without naming it: $(cat "${notice_root}/copy.log")" + [[ ! -e "${notice_root}/carrier/notices-layout" ]] || + fail "the Trivy notice copier recorded a layout for ${failure%%:*} evidence" +done + carried_trivy="${source_root}/.tools/trivy/linux-${go_platform_tag##*-}" for wrong in "fixture-version:0.72.1" "fixture-go:go1.26.9" "fixture-sum:h1:other"; do cp "${carried_trivy}/${wrong%%:*}" "${fixture_root}/held-trivy-evidence" @@ -737,13 +814,25 @@ for wrong in "fixture-version:0.72.1" "fixture-go:go1.26.9" "fixture-sum:h1:othe fail "the carried Trivy with the wrong ${wrong%%:*} was refused without naming it" mv "${fixture_root}/held-trivy-evidence" "${carried_trivy}/${wrong%%:*}" done -mv "${carried_trivy}/licenses" "${fixture_root}/held-trivy-licenses" -if install_release >"${fixture_root}/trivy-notices.log" 2>&1; then - fail "a checkout carrying Trivy without module notices installed a release" -fi -grep -q "with module notices" "${fixture_root}/trivy-notices.log" || - fail "the carried Trivy without module notices was refused for the wrong reason" -mv "${fixture_root}/held-trivy-licenses" "${carried_trivy}/licenses" +cp "${carried_trivy}/fixture-build" "${fixture_root}/held-trivy-build" +sed 's/GOARCH=.*/GOARCH=s390x/' "${fixture_root}/held-trivy-build" >"${carried_trivy}/fixture-build" +if install_release >"${fixture_root}/trivy-arch.log" 2>&1; then + fail "a checkout carrying Trivy for another architecture installed a release" +fi +grep -q "Trivy carrier.*reports.*GOARCH=s390x" "${fixture_root}/trivy-arch.log" || + fail "the carried Trivy for another architecture was refused without naming it" +mv "${fixture_root}/held-trivy-build" "${carried_trivy}/fixture-build" +for stale in missing code-polishy-trivy-notices-v1; do + mv "${carried_trivy}/notices-layout" "${fixture_root}/held-trivy-layout" + [[ "${stale}" == missing ]] || printf '%s\n' "${stale}" >"${carried_trivy}/notices-layout" + if install_release >"${fixture_root}/trivy-notices.log" 2>&1; then + fail "a checkout carrying Trivy with ${stale} notice layout installed a release" + fi + grep -q "lacks complete code-polishy-trivy-notices-v2 module notices" "${fixture_root}/trivy-notices.log" || + fail "the carried Trivy with ${stale} notice layout was refused for the wrong reason" + rm -f "${carried_trivy}/notices-layout" + mv "${fixture_root}/held-trivy-layout" "${carried_trivy}/notices-layout" +done [[ "$(installed_release_count)" == "1" ]] || fail "a checkout carrying an inexact Trivy changed what is installed" diff --git a/tools/install-go-tools.sh b/tools/install-go-tools.sh index 2adac557..ae3235b6 100755 --- a/tools/install-go-tools.sh +++ b/tools/install-go-tools.sh @@ -23,14 +23,36 @@ case "$(uname -m)" in esac trivy_dir="${repo_root}/.tools/trivy/linux-${trivy_arch}" trivy_expected="go${go_version#go}-X:jsonv2 github.com/aquasecurity/trivy v${trivy_version} ${trivy_module_sum}" +trivy_expected+=" CGO_ENABLED=0 GOARCH=${trivy_arch} GOOS=linux -trimpath=true" +trivy_notice_layout="code-polishy-trivy-notices-v2" trivy_identity() { "${repo_root}/scripts/go.sh" version -m "$1" 2>/dev/null | - awk 'NR == 1 { identity = $NF } $1 == "mod" { identity = identity " " $2 " " $3 " " $4 } END { print identity }' + awk 'NR == 1 { go = $NF } + $1 == "mod" { module = $2 " " $3 " " $4 } + $1 == "build" && $2 ~ /^(CGO_ENABLED|GOARCH|GOOS|-trimpath)=/ { split($2, setting, "="); value[setting[1]] = $2 } + END { print go " " module " " value["CGO_ENABLED"] " " value["GOARCH"] " " value["GOOS"] " " value["-trimpath"] }' +} + +trivy_notice_candidates() { + awk -v root="$1/" '{ + relative = substr($0, length(root) + 1) + count = split(tolower(relative), part, "/") + name = part[count] + extension = "" + if (match(name, /\.[^.]*$/)) extension = substr(name, RSTART) + licensed_directory = 0 + for (index_ = 1; index_ < count; index_++) if (part[index_] == "licenses") licensed_directory = 1 + source = extension ~ /^\.(go|s|c|h|cc|cpp|proto|py|js|ts|sh|rego|ya?ml|json|xml|toml|mod|sum|pom|gemspec|jar|golden)$/ + if (licensed_directory || (name ~ /licen[cs]e|copying|notice|patents/ && !source)) { + root_license = count == 1 && name ~ /licen[cs]e|copying/ && !source + print (root_license ? "root" : "notice") "\t" relative + } + }' } trivy_notices() { - local binary="$1" module_cache="$2" destination="$3" build_info modules module version escaped source notices notice relative licensed + local binary="$1" module_cache="$2" carrier="$3" build_info modules module version escaped source files candidates kind relative licensed if ! build_info="$("${repo_root}/scripts/go.sh" version -m "${binary}")"; then echo "The built Trivy has no readable build information." >&2 return 1 @@ -39,11 +61,11 @@ trivy_notices() { echo "The built Trivy replaces a module, so its notices cannot be attributed." >&2 return 1 fi - modules="$(awk '$1 == "dep" || $1 == "mod" { print $2, $3 }' <<<"${build_info}")" if ! awk '$1 == "mod" && $2 == "github.com/aquasecurity/trivy" { found = 1 } END { exit !found }' <<<"${build_info}"; then echo "The built Trivy build information names no Trivy module." >&2 return 1 fi + modules="$(awk '$1 == "dep" || $1 == "mod" { print $2, $3 }' <<<"${build_info}")" while read -r module version; do if [[ -z "${module}" || -z "${version}" ]]; then echo "The built Trivy build information has a malformed module record." >&2 @@ -51,30 +73,33 @@ trivy_notices() { fi escaped="$(printf '%s\n' "${module}" | awk '{ out = ""; for (i = 1; i <= length($0); i++) { c = substr($0, i, 1); out = out (c ~ /[A-Z]/ ? "!" tolower(c) : c) } print out }')" source="${module_cache}/${escaped}@${version}" - if [[ ! -d "${source}" ]] || ! notices="$(find "${source}" -type f \( -iname 'licen[cs]e*' -o -iname 'copying*' -o -iname 'notice*' -o -iname 'patents*' \) -print)"; then + if [[ ! -d "${source}" ]] || ! files="$(find "${source}" -type f -print)"; then echo "Trivy module ${module}@${version} has no readable source in the module cache." >&2 return 1 fi + candidates="$(trivy_notice_candidates "${source}" <<<"${files}")" licensed=false - while IFS= read -r notice; do - [[ -n "${notice}" ]] || continue - relative="${notice#"${source}/"}" - case "${relative}" in - */*) ;; - [Ll][Ii][Cc][Ee][Nn][CcSs][Ee]* | [Cc][Oo][Pp][Yy][Ii][Nn][Gg]*) licensed=true ;; - esac - mkdir -p "$(dirname "${destination}/${escaped}@${version}/${relative}")" - cp "${notice}" "${destination}/${escaped}@${version}/${relative}" - chmod 0644 "${destination}/${escaped}@${version}/${relative}" - done <<<"${notices}" + while IFS=$'\t' read -r kind relative; do + [[ -n "${relative}" ]] || continue + [[ "${kind}" != root ]] || licensed=true + mkdir -p "$(dirname "${carrier}/licenses/${escaped}@${version}/${relative}")" + cp "${source}/${relative}" "${carrier}/licenses/${escaped}@${version}/${relative}" + chmod 0644 "${carrier}/licenses/${escaped}@${version}/${relative}" + done <<<"${candidates}" if [[ "${licensed}" != true ]]; then echo "Trivy module ${module}@${version} carries no license file at its root." >&2 return 1 fi done <<<"${modules}" + printf '%s\n' "${trivy_notice_layout}" >"${carrier}/notices-layout" +} + +trivy_carrier_complete() { + [[ -f "$1/trivy" && -d "$1/licenses" && "$(trivy_identity "$1/trivy")" == "${trivy_expected}" && + "$(cat "$1/notices-layout" 2>/dev/null || true)" == "${trivy_notice_layout}" ]] } -if [[ -f "${trivy_dir}/trivy" && -d "${trivy_dir}/licenses" && "$(trivy_identity "${trivy_dir}/trivy")" == "${trivy_expected}" ]]; then +if trivy_carrier_complete "${trivy_dir}"; then echo "Trivy ${trivy_version} for linux/${trivy_arch} is already built at ${trivy_dir}." else trivy_scratch="$(mktemp -d "${TMPDIR:-/tmp}/code-polishy-trivy.XXXXXX")" @@ -96,7 +121,7 @@ else mkdir -p "${trivy_scratch}/stage" cp "${built}" "${trivy_scratch}/stage/trivy" chmod 0755 "${trivy_scratch}/stage/trivy" - trivy_notices "${built}" "${trivy_scratch}/gopath/pkg/mod" "${trivy_scratch}/stage/licenses" + trivy_notices "${built}" "${trivy_scratch}/gopath/pkg/mod" "${trivy_scratch}/stage" mkdir -p "$(dirname "${trivy_dir}")" rm -rf "${trivy_dir}" mv "${trivy_scratch}/stage" "${trivy_dir}" diff --git a/tools/install-policy-tools.ps1 b/tools/install-policy-tools.ps1 index c438883f..b60dd52a 100644 --- a/tools/install-policy-tools.ps1 +++ b/tools/install-policy-tools.ps1 @@ -103,20 +103,42 @@ try { } finally { $env:GOBIN = $SavedGoBin } $TrivyVersion = Read-Pin 'tools/trivy-version.txt' - $TrivyExpected = "go$GoVersion-X:jsonv2 github.com/aquasecurity/trivy v$TrivyVersion $(Read-Pin 'tools/trivy_module_sum.txt')" + $TrivyExpected = "go$GoVersion-X:jsonv2 github.com/aquasecurity/trivy v$TrivyVersion $(Read-Pin 'tools/trivy_module_sum.txt')" + + ' CGO_ENABLED=0 GOARCH=amd64 GOOS=linux -trimpath=true' + $TrivyNoticeLayout = 'code-polishy-trivy-notices-v2' $TrivyRoot = Join-Path $PolicyRoot '.tools/trivy/linux-amd64' function Get-TrivyIdentity([string]$Path) { if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { return '' } $Lines = @(& $Go version -m $Path 2>$null) if ($LASTEXITCODE -ne 0 -or $Lines.Count -eq 0) { return '' } - $Identity = @(($Lines[0].Trim() -split '\s+')[-1]) + $GoIdentity = ($Lines[0].Trim() -split '\s+')[-1] + $ModuleIdentity = '' + $Settings = @{} foreach ($Line in $Lines) { $Fields = @($Line.Trim() -split '\s+') - if ($Fields[0] -ceq 'mod') { $Identity += $Fields[1..3] } + if ($Fields[0] -ceq 'mod') { $ModuleIdentity = $Fields[1..3] -join ' ' } + if ($Fields[0] -ceq 'build' -and $Fields.Count -ge 2 -and $Fields[1] -cmatch '^(CGO_ENABLED|GOARCH|GOOS|-trimpath)=') { + $Settings[$Fields[1].Split('=')[0]] = $Fields[1] + } + } + return (@($GoIdentity, $ModuleIdentity) + @('CGO_ENABLED', 'GOARCH', 'GOOS', '-trimpath' | ForEach-Object { $Settings[$_] })) -join ' ' + } + function Get-TrivyNoticeCandidates([string]$SourceRoot) { + foreach ($File in @(Get-ChildItem -LiteralPath $SourceRoot -Recurse -File | Where-Object { -not ($_.Attributes -band [IO.FileAttributes]::ReparsePoint) })) { + $Relative = $File.FullName.Substring($SourceRoot.Length + 1) + $Parts = @($Relative.ToLowerInvariant() -split '[\\/]') + $Name = $Parts[-1] + $Source = [IO.Path]::GetExtension($Name) -match '^\.(go|s|c|h|cc|cpp|proto|py|js|ts|sh|rego|ya?ml|json|xml|toml|mod|sum|pom|gemspec|jar|golden)$' + $LicensedDirectory = $Parts.Count -gt 1 -and @($Parts[0..($Parts.Count - 2)] | Where-Object { $_ -ceq 'licenses' }).Count -gt 0 + if ($LicensedDirectory -or ($Name -match 'licen[cs]e|copying|notice|patents' -and -not $Source)) { + [pscustomobject]@{ + File = $File; Relative = $Relative + RootLicense = $Parts.Count -eq 1 -and $Name -match 'licen[cs]e|copying' -and -not $Source + } + } } - return $Identity -join ' ' } - function Copy-TrivyNotices([string]$Binary, [string]$ModuleCache, [string]$Destination) { + function Copy-TrivyNotices([string]$Binary, [string]$ModuleCache, [string]$Carrier) { $Lines = @(& $Go version -m $Binary) if ($LASTEXITCODE -ne 0 -or $Lines.Count -eq 0) { throw 'The built Trivy has no readable build information.' } $Modules = @() @@ -135,24 +157,26 @@ try { $Source = Join-Path $ModuleCache "$Escaped@$($Module[2])" if (-not (Test-Path -LiteralPath $Source -PathType Container)) { throw "Trivy module $($Module[1])@$($Module[2]) has no readable source in the module cache." } $SourceRoot = (Resolve-Path -LiteralPath $Source).Path.TrimEnd('\', '/') - $Target = Join-Path $Destination "$Escaped@$($Module[2])" + $Target = Join-Path $Carrier "licenses/$Escaped@$($Module[2])" $Licensed = $false - $Notices = @(Get-ChildItem -LiteralPath $SourceRoot -Recurse -File | Where-Object { - $_.Name -match '^(?i)(licen[cs]e|copying|notice|patents)' -and -not ($_.Attributes -band [IO.FileAttributes]::ReparsePoint) - }) - foreach ($Notice in $Notices) { - $Relative = $Notice.FullName.Substring($SourceRoot.Length + 1) - if ($Relative -notmatch '[\\/]' -and $Notice.Name -match '^(?i)(licen[cs]e|copying)') { $Licensed = $true } - $Copy = Join-Path $Target $Relative + foreach ($Candidate in @(Get-TrivyNoticeCandidates $SourceRoot)) { + if ($Candidate.RootLicense) { $Licensed = $true } + $Copy = Join-Path $Target $Candidate.Relative New-Item -ItemType Directory -Force -Path (Split-Path -Parent $Copy) | Out-Null - Copy-Item -LiteralPath $Notice.FullName -Destination $Copy + Copy-Item -LiteralPath $Candidate.File.FullName -Destination $Copy } if (-not $Licensed) { throw "Trivy module $($Module[1])@$($Module[2]) carries no license file at its root." } } + Set-Content -LiteralPath (Join-Path $Carrier 'notices-layout') -Value $TrivyNoticeLayout + } + function Test-TrivyCarrier([string]$Carrier) { + $Layout = Join-Path $Carrier 'notices-layout' + return (Get-TrivyIdentity (Join-Path $Carrier 'trivy')) -ceq $TrivyExpected -and + (Test-Path -LiteralPath (Join-Path $Carrier 'licenses') -PathType Container) -and + (Test-Path -LiteralPath $Layout -PathType Leaf) -and (Get-Content -Raw -LiteralPath $Layout).Trim() -ceq $TrivyNoticeLayout } - if ((Get-TrivyIdentity (Join-Path $TrivyRoot 'trivy')) -cne $TrivyExpected -or - -not (Test-Path -LiteralPath (Join-Path $TrivyRoot 'licenses') -PathType Container)) { + if (-not (Test-TrivyCarrier $TrivyRoot)) { $TrivyScratch = Join-Path $Scratch 'trivy' $TrivyEnvironment = [ordered]@{ GOENV = 'off'; GOPATH = (Join-Path $TrivyScratch 'gopath'); GOMODCACHE = (Join-Path $TrivyScratch 'gopath/pkg/mod') @@ -178,7 +202,7 @@ try { $TrivyStage = Join-Path $TrivyScratch 'stage' New-Item -ItemType Directory -Path $TrivyStage | Out-Null Copy-Item -LiteralPath $BuiltTrivy -Destination (Join-Path $TrivyStage 'trivy') - Copy-TrivyNotices $BuiltTrivy (Join-Path $TrivyScratch 'gopath/pkg/mod') (Join-Path $TrivyStage 'licenses') + Copy-TrivyNotices $BuiltTrivy (Join-Path $TrivyScratch 'gopath/pkg/mod') $TrivyStage Replace-Directory $TrivyStage $TrivyRoot } From f1edf563a275a2315dc51b6870376200988764a7 Mon Sep 17 00:00:00 2001 From: Jeff Musgrave <1578618+JeffMusgrave@users.noreply.github.com> Date: Thu, 22 Oct 2026 00:18:13 -0400 Subject: [PATCH 09/15] Report pnpm license listing failures in the bundle check When the policy-owned store lacked a package index, pnpm licenses list returned an error document and the flattener failed with "packages is not iterable", hiding the cause. Fail with pnpm's error code and message, and reject any license entry that is not a package list. Co-Authored-By: Claude Opus 5.5 --- scripts/check-javascript-bundle.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/check-javascript-bundle.sh b/scripts/check-javascript-bundle.sh index 0d48eb4b..bbb6b6b7 100755 --- a/scripts/check-javascript-bundle.sh +++ b/scripts/check-javascript-bundle.sh @@ -46,11 +46,21 @@ license_flattener="${temporary_dir}/flatten-licenses.mjs" cat >"${license_flattener}" <<'EOF' let raw = ""; process.stdin.on("data", (chunk) => (raw += chunk)).on("end", () => { + const report = JSON.parse(raw); + if (report.error) { + process.stderr.write(`pnpm could not list the bundle licenses: ${report.error.code}: ${report.error.message}\n`); + process.exit(1); + } const rows = []; - for (const [license, packages] of Object.entries(JSON.parse(raw))) + for (const [license, packages] of Object.entries(report)) { + if (!Array.isArray(packages)) { + process.stderr.write(`pnpm reported an unreadable license list for ${license}\n`); + process.exit(1); + } for (const entry of packages) for (const version of entry.versions) rows.push(`${entry.name}@${version}\t${license}`); + } process.stdout.write(rows.sort().join("\n") + "\n"); }); EOF From 916fbec5f3c78672a6a5c5e13fa837f26ff91560 Mon Sep 17 00:00:00 2001 From: Jeff Musgrave <1578618+JeffMusgrave@users.noreply.github.com> Date: Thu, 22 Oct 2026 00:38:48 -0400 Subject: [PATCH 10/15] Prepare Code Polishy 0.30.1 scanner and Cargo fixes Record the verified cold arm64 scanner reproduction and the exact release scope. Formatting and installer admission passed. Native release CI and publication remain pending; no tag or consumer lock is changed. --- CHANGELOG.md | 9 ++++++++- VERSION | 2 +- docs/design/carried-trivy-scanner.md | 8 ++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc9904cd..6217bca9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,10 @@ # Changelog -## Unreleased +## 0.30.1 - 2026-10-22 +- Compile and lint governed Cargo replacement packages through their consuming + project, retain forced lint diagnostics, and omit warnings located entirely + inside external macro implementations while preserving caller diagnostics. - Build the artifact scanner from the official Trivy 0.74.0 Go module with the pinned Go 1.26.10 and carry it in each release, so the scanner's database download rejects compromised certificate intermediates covered by @@ -12,6 +15,10 @@ - Record the carried Trivy in release manifest version 9 and in the release SBOM with every Go module linked into it, and carry each module's license and notice files. +- Require the scanner's exact target, compiler, module checksum and build + settings, and reject incomplete cached notice layouts during installation. +- Report the original pnpm failure when JavaScript license listing fails, + instead of treating its error response as a package inventory. ## 0.30.0 - 2026-10-20 diff --git a/VERSION b/VERSION index c25c8e5b..1a44cad7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.30.0 +0.30.1 diff --git a/docs/design/carried-trivy-scanner.md b/docs/design/carried-trivy-scanner.md index 3de5ccce..5c471317 100644 --- a/docs/design/carried-trivy-scanner.md +++ b/docs/design/carried-trivy-scanner.md @@ -69,6 +69,14 @@ network proxy or checksum database produced the same binary `linux/arm64`). That rebuild reused the build cache, so it shows identical inputs and link output rather than a cold reproduction. +On 2026-10-22 the real macOS arm64 installer also rebuilt the carrier with +fresh private module and build caches, downloading 381 modules and producing +the same binary SHA-256. Its completed carrier contains 376 module notice +directories and 772 notice files totaling 4,927,370 bytes. Installer reuse, +release-install admission and offline supply-chain verification passed for +that carrier. This proves a cold reproduction on that host; the amd64 and +Windows build paths still require their native release verification. + At runtime the scanner image copies the carried binary for the Docker server's platform through a named build context. Docker pulls no scanner image, and the installed release is only read. A release without a binary for that platform From 8bc27c1314885af946d268e22f7ea89cc927aad7 Mon Sep 17 00:00:00 2001 From: Jeff Musgrave <1578618+JeffMusgrave@users.noreply.github.com> Date: Thu, 22 Oct 2026 00:49:55 -0400 Subject: [PATCH 11/15] Expose the carried scanner guide through the documentation catalog The release gate found the new permanent guide missing from docs/catalog.json. Register the topic without changing the document contract. The focused documentation suite and formatting pass. --- docs/catalog.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/catalog.json b/docs/catalog.json index f9190952..e85eaa3f 100644 --- a/docs/catalog.json +++ b/docs/catalog.json @@ -81,6 +81,14 @@ "aliases": ["trivy-assessments"], "public": true }, + { + "id": "carried-trivy-scanner", + "path": "docs/design/carried-trivy-scanner.md", + "title": "Carried Trivy Scanner", + "summary": "Build and admit the pinned scanner with its exact identity, complete module notices, and release SBOM.", + "aliases": ["trivy-build"], + "public": true + }, { "id": "behavior-review", "path": "docs/policies/behavior-review.md", From d787ae3f4ef1e855f1dc5ef0b146c6faaa335d1b Mon Sep 17 00:00:00 2001 From: Jeff Musgrave <1578618+JeffMusgrave@users.noreply.github.com> Date: Thu, 22 Oct 2026 20:45:02 -0400 Subject: [PATCH 12/15] Refresh expiring dependency assessments with aged TOML fix --- .code-polishy.json | 46 ++++--------------------- CHANGELOG.md | 5 ++- docs/security/dependency-assessments.md | 41 ++++++++++++++-------- providers/javascript/pnpm-lock.yaml | 8 ++--- tools/javascript/pnpm-lock.yaml | 8 ++--- tools/javascript_bundle_inventory.txt | 2 +- 6 files changed, 47 insertions(+), 63 deletions(-) diff --git a/.code-polishy.json b/.code-polishy.json index d814b571..4751ea94 100644 --- a/.code-polishy.json +++ b/.code-polishy.json @@ -34,7 +34,9 @@ }, "quality": { "allowComments": false, - "lineBudget": { "production": 100000 } + "lineBudget": { + "production": 100000 + } }, "documentation": { "design": [ @@ -2000,25 +2002,8 @@ "impact": "Excessive-depth payloads cannot reach the recursive braces walkers; rejected input produces failed or incomplete analysis.", "evidence": "https://github.com/riteofstring/code-polishy/blob/main/docs/security/dependency-assessments.md", "owner": "Codex", - "reviewed": "2026-10-08", - "expires": "2026-10-22" - }, - { - "id": "tooling-toml-parser-unreachable", - "ecosystem": "pnpm", - "advisory": "GHSA-r4xh-jqrq-34v2", - "package": "smol-toml", - "affectedVersion": "1.7.1", - "scope": "tools/javascript/pnpm-lock.yaml", - "severity": "moderate", - "status": "not-affected", - "basis": "unreachable", - "reason": "Knip uses policy-owned configuration and all plugins are disabled. Manifests use JSON or YAML; disabled script-referenced plugin configs become entry paths without invoking the TOML loader.", - "impact": "No repository-controlled TOML reaches smol-toml.parse or its quadratic key parser.", - "evidence": "https://github.com/riteofstring/code-polishy/blob/main/docs/security/dependency-assessments.md", - "owner": "Codex", - "reviewed": "2026-10-08", - "expires": "2026-10-22" + "reviewed": "2026-10-23", + "expires": "2026-11-06" }, { "id": "provider-glob-depth-unreachable", @@ -2034,25 +2019,8 @@ "impact": "Excessive-depth payloads cannot reach the recursive braces walkers; rejected input produces failed or incomplete analysis.", "evidence": "https://github.com/riteofstring/code-polishy/blob/main/docs/security/dependency-assessments.md", "owner": "Codex", - "reviewed": "2026-10-08", - "expires": "2026-10-22" - }, - { - "id": "provider-toml-parser-unreachable", - "ecosystem": "pnpm", - "advisory": "GHSA-r4xh-jqrq-34v2", - "package": "smol-toml", - "affectedVersion": "1.7.1", - "scope": "providers/javascript/pnpm-lock.yaml", - "severity": "moderate", - "status": "not-affected", - "basis": "unreachable", - "reason": "Knip uses policy-owned configuration and all plugins are disabled. Manifests use JSON or YAML; disabled script-referenced plugin configs become entry paths without invoking the TOML loader.", - "impact": "No repository-controlled TOML reaches smol-toml.parse or its quadratic key parser.", - "evidence": "https://github.com/riteofstring/code-polishy/blob/main/docs/security/dependency-assessments.md", - "owner": "Codex", - "reviewed": "2026-10-08", - "expires": "2026-10-22" + "reviewed": "2026-10-23", + "expires": "2026-11-06" }, { "id": "provider-source-map-unreachable", diff --git a/CHANGELOG.md b/CHANGELOG.md index 6217bca9..a67e9f90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 0.30.1 - 2026-10-22 +## 0.30.1 - 2026-10-23 - Compile and lint governed Cargo replacement packages through their consuming project, retain forced lint diagnostics, and omit warnings located entirely @@ -19,6 +19,9 @@ settings, and reject incomplete cached notice layouts during installation. - Report the original pnpm failure when JavaScript license listing fails, instead of treating its error response as a package inventory. +- Resolve the aged smol-toml 1.9.0 security fix in both JavaScript graphs and + remove its temporary vulnerability assessments. Reassess the unchanged + braces depth guard while no official fixed release is available. ## 0.30.0 - 2026-10-20 diff --git a/docs/security/dependency-assessments.md b/docs/security/dependency-assessments.md index ecf171c5..b0f57b6e 100644 --- a/docs/security/dependency-assessments.md +++ b/docs/security/dependency-assessments.md @@ -1,8 +1,9 @@ # Dependency vulnerability assessments -Brace expansion and URI processing resolved with aged fixes on 2026-10-15; other JavaScript and Go module findings -reviewed on 2026-10-08; Go toolchain admission reviewed on 2026-10-21; carried Trivy -build reviewed on 2026-10-22. +Brace expansion and URI processing resolved with aged fixes on 2026-10-15; +TOML parsing resolved with an aged fix and the braces depth guard reassessed on +2026-10-23. Other JavaScript and Go module findings were reviewed on 2026-10-08; +Go toolchain admission on 2026-10-21; carried Trivy build on 2026-10-22. Assessment owner: Codex. The vulnerability assessments cover `tools/javascript/pnpm-lock.yaml` and @@ -242,11 +243,19 @@ produces a native operational failure or provider incomplete coverage, never a passing empty result. Regressions exercise real source globs, workspace globs, package scripts, ordinary nested alternatives, and escaped literal paths. -Retain exact high-severity `not-affected` assessments on the basis that the -exploit prerequisite is now unreachable. They expire on 2026-10-22. Check for an -official fix by that date and reassess before renewal; adopt an aged fix when -available. Reassess immediately if Knip, fast-glob, braces, the shared guard, -or any expansion caller changes. See the +The 2026-10-23 review reconfirmed that the advisory has no patched version and +the [npm registry](https://registry.npmjs.org/braces) still publishes 3.0.3 as +latest. Inspection of the installed Knip 5.55.1 call sites and both analyzer +bindings confirms that the same three guarded methods own expansion. Neither +those packages nor the guard changes in this release. Existing native and +packaged-provider regressions cover deeply nested workspace, script and source +patterns, ordinary alternatives, and escaped literal paths. + +Retain the two exact high-severity `not-affected` assessments because the +exploit prerequisite remains unreachable. Their next review is due by +2026-11-06. Check for an official fix by that date and reassess before renewal; +adopt an aged fix when available. Reassess immediately if Knip, fast-glob, +braces, the shared guard, or any expansion caller changes. See the [boundary design](../design/javascript-glob-inputs.md) for the binding scope. ## TOML parsing @@ -266,12 +275,16 @@ package manifests as JSON. Both explicitly disable every Knip plugin. script-referenced configuration for a disabled plugin becomes an entry path, not a call to its configuration loader. TOML parsing receives no target input. -Retain exact moderate-severity `not-affected` assessments in both locks until -2026-10-22. Update to official 1.9.0 on or after 17:15:35 UTC that day, 30 days -after its [npm publication](https://registry.npmjs.org/smol-toml), and remove the -assessments. With no enabled parse call, waiting avoids admitting fresh parser -code without exposing the analyzer to this advisory. Reassess if configuration -loading or plugin activation changes. +Both locks now resolve official 1.9.0 and the two moderate-severity assessments +are removed. Its [npm publication](https://registry.npmjs.org/smol-toml) was +2026-09-22 at 17:15:34.756 UTC, so the 2026-10-23 update meets the 30-day minimum. +Registry-integrity-verified archives confirm that the package still has no +runtime dependencies or install lifecycle scripts. The parser replaces the +quadratic key search; no other dependency or manifest changes in either graph. +The newer 1.9.1 only corrects numeric exponent parsing in the shipped runtime; +it is not yet aged, and this analyzer does not expose the TOML parse path. +No early admission is needed. Reassess if configuration loading or plugin +activation changes. ## Source maps diff --git a/providers/javascript/pnpm-lock.yaml b/providers/javascript/pnpm-lock.yaml index 0262e78f..50187f7b 100644 --- a/providers/javascript/pnpm-lock.yaml +++ b/providers/javascript/pnpm-lock.yaml @@ -1110,8 +1110,8 @@ packages: resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} engines: {node: '>= 0.4'} - smol-toml@1.7.1: - resolution: {integrity: sha512-PPlsspAZ4jbMBu5DMFhfUGDQLu/vrL4SyBROVS37x8ynnVmFIs1VPBz1Co8Xks3TvpIaZXmU85y4DrQ+UyVFoQ==} + smol-toml@1.9.0: + resolution: {integrity: sha512-hpd+HLON7HdZXqYchMM/+LaTTbdK0AU3NngIJ4KVyWbY9bfQqdL9cD+4yf6dUoU2Ap4VsU0JkQi6FxAI1B2mXQ==} engines: {node: '>= 18'} source-map-js@1.2.1: @@ -2373,7 +2373,7 @@ snapshots: minimist: 1.2.8 picocolors: 1.1.1 picomatch: 4.0.5 - smol-toml: 1.7.1 + smol-toml: 1.9.0 strip-json-comments: 5.0.1 typescript: 6.0.3 zod: 3.25.76 @@ -2634,7 +2634,7 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 - smol-toml@1.7.1: {} + smol-toml@1.9.0: {} source-map-js@1.2.1: {} diff --git a/tools/javascript/pnpm-lock.yaml b/tools/javascript/pnpm-lock.yaml index 2c73cde4..f4038219 100644 --- a/tools/javascript/pnpm-lock.yaml +++ b/tools/javascript/pnpm-lock.yaml @@ -893,8 +893,8 @@ packages: resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} engines: {node: '>= 0.4'} - smol-toml@1.7.1: - resolution: {integrity: sha512-PPlsspAZ4jbMBu5DMFhfUGDQLu/vrL4SyBROVS37x8ynnVmFIs1VPBz1Co8Xks3TvpIaZXmU85y4DrQ+UyVFoQ==} + smol-toml@1.9.0: + resolution: {integrity: sha512-hpd+HLON7HdZXqYchMM/+LaTTbdK0AU3NngIJ4KVyWbY9bfQqdL9cD+4yf6dUoU2Ap4VsU0JkQi6FxAI1B2mXQ==} engines: {node: '>= 18'} stop-iteration-iterator@1.1.0: @@ -1825,7 +1825,7 @@ snapshots: minimist: 1.2.8 picocolors: 1.1.1 picomatch: 4.0.5 - smol-toml: 1.7.1 + smol-toml: 1.9.0 strip-json-comments: 5.0.1 typescript: 6.0.3 zod: 3.25.76 @@ -2050,7 +2050,7 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 - smol-toml@1.7.1: {} + smol-toml@1.9.0: {} stop-iteration-iterator@1.1.0: dependencies: diff --git a/tools/javascript_bundle_inventory.txt b/tools/javascript_bundle_inventory.txt index 65cd33fc..467e0879 100644 --- a/tools/javascript_bundle_inventory.txt +++ b/tools/javascript_bundle_inventory.txt @@ -218,7 +218,7 @@ side-channel-list@1.0.1 MIT side-channel-map@1.0.1 MIT side-channel-weakmap@1.0.2 MIT side-channel@1.1.1 MIT -smol-toml@1.7.1 BSD-3-Clause +smol-toml@1.9.0 BSD-3-Clause stop-iteration-iterator@1.1.0 MIT string.prototype.includes@2.0.1 MIT string.prototype.trim@1.2.11 MIT From a109b4d18eaf0a8964e3813e2e56d695958f7c4c Mon Sep 17 00:00:00 2001 From: Jeff Musgrave <1578618+JeffMusgrave@users.noreply.github.com> Date: Thu, 22 Oct 2026 23:02:39 -0400 Subject: [PATCH 13/15] Exclude dependency-cache macro bodies from project warnings A real Git dependency fixture reproduced a forced unreachable-code warning wholly inside Cargo home. Classify that cache as external under both repository root spellings, retain caller spans and all errors, and document local path dependency lint scope. The Rust facts suite fails on the old filter and passes with the correction; final verification remains CI-owned. --- docs/design/cargo-facts.md | 11 ++++++++--- internal/rustfacts/compilation_patches_test.go | 9 +++++++-- internal/rustfacts/compiler_diagnostics.go | 14 ++++++++++---- internal/rustfacts/compiler_output_test.go | 8 ++++++++ 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/docs/design/cargo-facts.md b/docs/design/cargo-facts.md index 73b7f8f7..990a1b47 100644 --- a/docs/design/cargo-facts.md +++ b/docs/design/cargo-facts.md @@ -288,8 +288,10 @@ not used: it installs the driver only as the workspace wrapper, so path replacements outside the workspace would compile with plain rustc and escape Clippy. The wrapper reaches every unit. Cargo still caps lints for registry and Git packages, and the driver disables Clippy for those capped units, so -non-local code stays silent. The pinned driver is selected explicitly; there is -no ambient external-subcommand lookup. +non-local code stays silent. Local path dependencies receive the same forced +lints even when they are outside the consumer's workspace; their diagnostics +can therefore refer to source beyond the selected files. The pinned driver is +selected explicitly; there is no ambient external-subcommand lookup. The common runner captures Cargo's JSON events with bounded output and process tree cancellation. Cargo must retain its reviewed lock and available dependency cache through `--frozen`; a missing lock is a preparation failure, never an @@ -316,7 +318,10 @@ Compiler warnings, Clippy's default lint group and unused-code lints use hide these diagnostics or turn policy lint collection into a compiler failure. Forcing also bypasses rustc's suppression of lints in code generated by external macros. A warning is therefore omitted when every one of its spans, -at its own location, is macro-expanded text outside the repository. For +at its own location, is macro-expanded text outside project source. The governed +Cargo home contains downloaded registry and Git sources, not project source, +even though it lives under the repository. Both the caller's root spelling and +the physical root spelling retain this distinction. For example, `std::pin::pin!` of an `async` block reports unreachable code whose expression and cause both lie in the standard library's macro body. A warning remains a finding when any span is repository text, including macro input diff --git a/internal/rustfacts/compilation_patches_test.go b/internal/rustfacts/compilation_patches_test.go index e45a86af..7b183496 100644 --- a/internal/rustfacts/compilation_patches_test.go +++ b/internal/rustfacts/compilation_patches_test.go @@ -49,7 +49,12 @@ func TestRustCompilationLintsGovernedReplacementsAndCapsNonLocalDependencies(t * repo := consumedReplacementRepository(t) remote := t.TempDir() write(t, remote, "Cargo.toml", "[package]\nname='remote'\nversion='1.0.0'\nedition='2021'\n") - write(t, remote, "src/lib.rs", "pub fn value() -> u32 { if needless(true) { 1 } else { 2 } }\nfn needless(value: bool) -> bool { if value { true } else { false } }\nfn remote_unused() {}\n") + write(t, remote, "src/lib.rs", `pub fn value() -> u32 { if needless(true) { 1 } else { 2 } } +fn needless(value: bool) -> bool { if value { true } else { false } } +fn remote_unused() {} +#[macro_export] +macro_rules! remote_stop { () => {{ return 0u32; 1u32 }}; } +`) for _, arguments := range [][]string{{"init", "-q"}, {"add", "."}, {"-c", "user.name=fixture", "-c", "user.email=fixture@example.test", "commit", "-qm", "fixture"}} { if output, err := exec.Command("git", append([]string{"-C", remote}, arguments...)...).CombinedOutput(); err != nil { t.Fatalf("git fixture: %v; %s", err, output) @@ -57,7 +62,7 @@ func TestRustCompilationLintsGovernedReplacementsAndCapsNonLocalDependencies(t * } manifest := string(readRustFile(t, repo, "Cargo.toml")) write(t, repo.Root, "Cargo.toml", string(bytes.Replace([]byte(manifest), []byte("[patch.crates-io]"), []byte("remote={git='file://"+filepath.ToSlash(remote)+"'}\n[patch.crates-io]"), 1))) - write(t, repo.Root, "src/lib.rs", "pub fn value() -> u32 { patch_source::value() + remote::value() }\n") + write(t, repo.Root, "src/lib.rs", "pub fn value() -> u32 { patch_source::value() + remote::value() }\npub fn remote_macro() -> u32 { remote::remote_stop!() }\n") write(t, repo.Root, "vendor/patch-source/src/lib.rs", "pub fn value() -> u32 { if needless(true) { patch_leaf::value() } else { 2 } }\nfn needless(value: bool) -> bool { if value { true } else { false } }\n#[allow(dead_code)]\nfn source_unused() {}\n") write(t, repo.Root, "vendor/leaf/src/lib.rs", "macro_rules! quiet { ($i:item) => { #[allow(dead_code, clippy::needless_bool)] $i }; }\nquiet! { fn leaf_needless(value: bool) -> bool { if value { true } else { false } } }\npub fn value() -> u32 { 42 }\n") lock := exec.Command(repo.RustTool("cargo"), "generate-lockfile", "--manifest-path", filepath.Join(repo.Root, "Cargo.toml")) diff --git a/internal/rustfacts/compiler_diagnostics.go b/internal/rustfacts/compiler_diagnostics.go index 5f84999b..c470b6ff 100644 --- a/internal/rustfacts/compiler_diagnostics.go +++ b/internal/rustfacts/compiler_diagnostics.go @@ -85,7 +85,7 @@ func externalExpansion(repo repository.Repository, workspace Workspace, spans [] if !filepath.IsAbs(path) { path = filepath.Join(repo.Root, filepath.FromSlash(workspace.Root), path) } - if span.Expansion == nil || withinRepository(repo, path) { + if span.Expansion == nil || repositorySource(repo, path) { return false } primary = primary || span.Primary @@ -93,7 +93,13 @@ func externalExpansion(repo repository.Repository, workspace Workspace, spans [] return primary } -func withinRepository(repo repository.Repository, path string) bool { - physical, err := filepath.EvalSymlinks(repo.Root) - return withinDirectory(repo.Root, path) || err == nil && withinDirectory(physical, path) +func repositorySource(repo repository.Repository, path string) bool { + physical, _ := filepath.EvalSymlinks(repo.Root) + for _, root := range []string{repo.Root, physical} { + if root != "" && withinDirectory(root, path) { + repo.Root = root + return !withinDirectory(CargoHome(repo), path) + } + } + return false } diff --git a/internal/rustfacts/compiler_output_test.go b/internal/rustfacts/compiler_output_test.go index ab7b86e8..0498c1cb 100644 --- a/internal/rustfacts/compiler_output_test.go +++ b/internal/rustfacts/compiler_output_test.go @@ -70,6 +70,10 @@ func TestCargoCompilerWarningsOmitOnlyExpansionsOutsideTheRepository(t *testing. cause := compilerSpan{File: "src/lib.rs", Line: 1, Column: 18} libraryCause := compilerSpan{File: library.File, Line: 2, Column: 9, Expansion: library.Expansion} unexpanded := compilerSpan{File: "/unavailable/dependency.rs", Line: 5, Column: 3, Primary: true} + cached := library + cached.File = filepath.Join(CargoHome(repo), "registry", "src", "source-id", "dependency-1.0.0", "src", "lib.rs") + nearCache := library + nearCache.File = filepath.Join(CargoHome(repo)+"-local", "src", "lib.rs") for name, test := range map[string]struct { level string spans []compilerSpan @@ -80,6 +84,10 @@ func TestCargoCompilerWarningsOmitOnlyExpansionsOutsideTheRepository(t *testing. "library and repository": {"warning", []compilerSpan{library, repository}, true}, "outside without expansion": {"warning", []compilerSpan{unexpanded}, true}, "library expansion error": {"error", []compilerSpan{library}, true}, + "cached registry expansion": {"warning", []compilerSpan{cached}, false}, + "cached caller input": {"warning", []compilerSpan{cached, repository}, true}, + "cached expansion error": {"error", []compilerSpan{cached}, true}, + "similar local directory": {"warning", []compilerSpan{nearCache}, true}, } { t.Run(name, func(t *testing.T) { event := compilerEvent{Reason: "compiler-message", PackageID: workspace.Packages[0].ID, Target: target, Message: compilerDiagnostic{Message: "seeded diagnostic", Level: test.level, Code: &compilerCode{Code: "unreachable_code"}, Spans: test.spans}} From 8af44e5cb02ce21d18ed0635c8584ecc9d82695c Mon Sep 17 00:00:00 2001 From: Jeff Musgrave <1578618+JeffMusgrave@users.noreply.github.com> Date: Fri, 23 Oct 2026 11:48:12 -0400 Subject: [PATCH 14/15] Remove assessments for withdrawn braces advisory --- .code-polishy.json | 34 ------------------------- docs/security/dependency-assessments.md | 24 ++++++++--------- 2 files changed, 11 insertions(+), 47 deletions(-) diff --git a/.code-polishy.json b/.code-polishy.json index b610a74e..571879c4 100644 --- a/.code-polishy.json +++ b/.code-polishy.json @@ -2022,40 +2022,6 @@ } ], "vulnerabilityAssessments": [ - { - "id": "tooling-glob-depth-unreachable", - "ecosystem": "pnpm", - "advisory": "GHSA-vfj7-8cjw-p6xm", - "package": "braces", - "affectedVersion": "3.0.3", - "scope": "tools/javascript/pnpm-lock.yaml", - "severity": "high", - "status": "not-affected", - "basis": "unreachable", - "reason": "The shared glob boundary checks source imports and Knip fast-glob patterns and ignores before expansion, rejecting brace or parenthesis nesting beyond 64 levels.", - "impact": "Excessive-depth payloads cannot reach the recursive braces walkers; rejected input produces failed or incomplete analysis.", - "evidence": "https://github.com/riteofstring/code-polishy/blob/main/docs/security/dependency-assessments.md", - "owner": "Codex", - "reviewed": "2026-10-23", - "expires": "2026-11-06" - }, - { - "id": "provider-glob-depth-unreachable", - "ecosystem": "pnpm", - "advisory": "GHSA-vfj7-8cjw-p6xm", - "package": "braces", - "affectedVersion": "3.0.3", - "scope": "providers/javascript/pnpm-lock.yaml", - "severity": "high", - "status": "not-affected", - "basis": "unreachable", - "reason": "The shared glob boundary checks source imports and Knip fast-glob patterns and ignores before expansion, rejecting brace or parenthesis nesting beyond 64 levels.", - "impact": "Excessive-depth payloads cannot reach the recursive braces walkers; rejected input produces failed or incomplete analysis.", - "evidence": "https://github.com/riteofstring/code-polishy/blob/main/docs/security/dependency-assessments.md", - "owner": "Codex", - "reviewed": "2026-10-23", - "expires": "2026-11-06" - }, { "id": "provider-source-map-unreachable", "ecosystem": "pnpm", diff --git a/docs/security/dependency-assessments.md b/docs/security/dependency-assessments.md index db6ba0db..7102efae 100644 --- a/docs/security/dependency-assessments.md +++ b/docs/security/dependency-assessments.md @@ -197,18 +197,18 @@ Both satisfy the age minimum; module checksums authenticate the downloads. ## Guarded glob expansion +GitHub withdrew [GHSA-vfj7-8cjw-p6xm](https://github.com/advisories/GHSA-vfj7-8cjw-p6xm) -affects `braces` 3.0.3 in both graphs. Its iterative parser accepts deeply nested -brace and parenthesis nodes that overflow recursive compilation or expansion. -The ordinary pattern-length limit does not stop the attack. On 2026-10-23 UTC, -the GitHub advisory still lists no patched version and the npm registry still -lists 3.0.3 as the latest release. +on 2026-10-23 at 12:53:02 UTC, stating that it is not practically exploitable. +The tooling and provider assessments for `braces` 3.0.3 are removed because +current vulnerability scans no longer report this advisory. Dependency versions +and the existing bounded glob-input behavior remain unchanged. The provider's `resolveGlob` in [`imports.mjs`](../../providers/javascript/imports.mjs) accepts source-selected `import.meta.glob` patterns. Both analyzers also use Knip 5.55.1, whose workspace discovery and package-script entries reach `fast-glob` 3.3.3. Its task generator -calls `micromatch` 4.0.8 brace expansion, which invokes the affected `braces` API. +calls `micromatch` 4.0.8 brace expansion, which invokes the `braces` API. These are reachable input paths; disabled plugins alone do not protect them. [`glob-inputs.mjs`](../../tools/javascript/glob-inputs.mjs) now rejects more than @@ -222,19 +222,17 @@ Knip's `util/map-workspaces.js`, `util/glob.js`, and `util/glob-core.js` confirm these are its expansion entry points, including entries extracted from scripts even when plugins are disabled. Methods are restored during cleanup. -The advisory's excessive-depth payload therefore cannot reach the recursive +Excessive-depth input therefore cannot reach the recursive walkers. Normal matching still uses the original library and pattern. Rejection produces a native operational failure or provider incomplete coverage, never a passing empty result. Regressions exercise real source globs, workspace globs, package scripts, ordinary nested alternatives, and escaped literal paths. Reinspection on 2026-10-23 UTC confirms the same Knip 5.55.1 and fast-glob 3.3.3 -callers and the same guard binding in both analyzers. Retain exact high-severity -`not-affected` assessments because the exploit prerequisite remains unreachable. -They expire on 2026-11-06. Check for an -official fix by that date and reassess before renewal; adopt an aged fix when -available. Reassess immediately if Knip, fast-glob, braces, the shared guard, -or any expansion caller changes. See the +callers and the same guard binding in both analyzers. The bounds still provide +predictable handling of excessive nesting in repository-selected patterns. +Reassess any new advisory against its actual prerequisites and these callers. +See the [boundary design](../design/javascript-glob-inputs.md) for the binding scope. ## TOML parsing From 894de81caebeb4d8b98db0045f69381ed36dc55d Mon Sep 17 00:00:00 2001 From: Jeff Musgrave <1578618+JeffMusgrave@users.noreply.github.com> Date: Fri, 23 Oct 2026 15:22:25 -0400 Subject: [PATCH 15/15] Assess Zod collection exhaustion at sealed Knip inputs Trace both exact lockfile occurrences to generated string-path configuration and disabled plugins. Retain visible high-severity findings under expiring not-affected assessments through November 11, without dependency or threshold changes. Formatting and doctor pass; native CI owns the final gate. --- .code-polishy.json | 34 +++++++++++++ docs/security/dependency-assessments.md | 64 +++++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/.code-polishy.json b/.code-polishy.json index 9b06ac5a..b08a2847 100644 --- a/.code-polishy.json +++ b/.code-polishy.json @@ -2045,6 +2045,40 @@ } ], "vulnerabilityAssessments": [ + { + "id": "tooling-zod-invalid-collections-unreachable", + "ecosystem": "pnpm", + "advisory": "GHSA-mrhx-c7xc-m3w5", + "package": "zod", + "affectedVersion": "3.25.76", + "scope": "tools/javascript/pnpm-lock.yaml", + "severity": "high", + "status": "not-affected", + "basis": "unreachable", + "reason": "Knip validates only generated configuration containing validated string paths and disabled plugins. The explicit private JSON config prevents target Knip configuration from supplying malformed array elements; manifests use JSON parsing outside Zod.", + "impact": "Repository data cannot cause Zod to accumulate per-element validation failures through the enabled Knip configuration path. Normal work proportional to valid source inventory remains possible.", + "evidence": "https://github.com/riteofstring/code-polishy/blob/main/docs/security/dependency-assessments.md", + "owner": "Codex", + "reviewed": "2026-10-23", + "expires": "2026-11-11" + }, + { + "id": "provider-zod-invalid-collections-unreachable", + "ecosystem": "pnpm", + "advisory": "GHSA-mrhx-c7xc-m3w5", + "package": "zod", + "affectedVersion": "3.25.76", + "scope": "providers/javascript/pnpm-lock.yaml", + "severity": "high", + "status": "not-affected", + "basis": "unreachable", + "reason": "The provider replaces Knip configuration initialization with generated string-path arrays, disabled plugins and its own compiler function. Target Knip configuration is not loaded, manifests use JSON parsing, and compiler arguments and output are strings.", + "impact": "Repository data cannot supply failing collection elements to the enabled Zod schema or compiler wrappers. Normal work proportional to valid source inventory remains possible.", + "evidence": "https://github.com/riteofstring/code-polishy/blob/main/docs/security/dependency-assessments.md", + "owner": "Codex", + "reviewed": "2026-10-23", + "expires": "2026-11-11" + }, { "id": "provider-source-map-unreachable", "ecosystem": "pnpm", diff --git a/docs/security/dependency-assessments.md b/docs/security/dependency-assessments.md index 7102efae..cf717372 100644 --- a/docs/security/dependency-assessments.md +++ b/docs/security/dependency-assessments.md @@ -3,6 +3,7 @@ Brace expansion and URI processing resolved with aged fixes on 2026-10-15; other JavaScript and Go module findings reviewed on 2026-10-08; Go toolchain admission reviewed on 2026-10-21. Guarded glob expansion and the aged TOML fix reviewed on 2026-10-23 UTC. +Zod collection validation reviewed on 2026-10-23 UTC. Assessment owner: Codex. The vulnerability assessments cover `tools/javascript/pnpm-lock.yaml` and @@ -266,6 +267,69 @@ replacement vulnerability assessment. The existing policy-owned configuration boundary remains in place; reassess future advisories if configuration loading or plugin activation changes. +## Zod collection validation + +[GHSA-mrhx-c7xc-m3w5](https://github.com/advisories/GHSA-mrhx-c7xc-m3w5), +also CVE-2023-54404, reports memory exhaustion while Zod accumulates validation +issues for oversized collections. Its affected range includes the exact +`zod` 3.25.76 resolved through Knip 5.55.1 in both JavaScript locks. The +[upstream correction](https://github.com/colinhacks/zod/commit/a0ebf970f2d752e5f58527cd2942ff48be4f46bd) +rejects oversized bounded v4 arrays, maps and sets before parsing their elements; +it leaves unbounded containers unchanged. The advisory description also refers +to unbounded arrays. This assessment covers the broader failing-element +accumulation prerequisite rather than relying on that discrepancy or assuming +that the v3 API is immune. + +The resolved Knip source imports Zod in `dist/schema/configuration.js`, +`dist/schema/plugins.js` and `dist/util/errors.js`. Its configuration initializer +calls `knipConfigurationSchema.parse`; the error utility formats existing +`ZodError` instances through `zod-validation-error` 3.5.4. Neither dependency +validates package-manifest collections with Zod. The lock graphs contain no other +Zod consumer. Knip's root import resolves to Zod's v3 implementation, which also +walks invalid array elements and accumulates their issues. + +The native analyzer's +[`configurationFor`](../../tools/javascript/deadcode.mjs) constructs only +workspace records whose `entry` and `project` arrays contain normalized string +paths, then sets every plugin to `false`. Its protocol validates path types and +ownership before construction and limits the selected source inventory to 4,096 +paths. `analyze` writes this configuration in a private temporary directory and +passes its exact path through `--config`; target `knip.json`, executable Knip +configuration and `package.json#knip` cannot replace it. Workspace manifests and +optional workspace YAML are read separately and are not passed to this schema. + +The provider's +[`configurationFor`](../../providers/javascript/deadcode.mjs) likewise builds +string-only arrays from selected source paths and disables every plugin. +[`bindKnip`](../../providers/javascript/reachability.mjs) replaces +`ConfigurationChief.init` with this in-memory configuration, so no target Knip +configuration is loaded. It reads the manifest as JSON and adds only its own +Astro compiler. Zod's compiler wrappers validate a fixed argument tuple of two +strings and a string result; repository source contents do not become an array +of schema elements. Disabled plugin configuration cannot add another schema +input path. + +Thus the schema is reachable, but the malformed collection elements required +for issue amplification cannot reach it. A diagnostic probe against the +installed Knip schema accepted a generated 4,096-path configuration with zero +issues; a control using 4,096 numbers against `z.array(z.string())` produced +4,096 issues. This corroborates the input-type distinction, not a general +memory bound. Large valid inventories still require proportional work, and +this assessment makes no general denial-of-service claim about source analysis. + +Retain the aged 3.25.76 under two exact high-severity `not-affected` assessments, +one per lockfile, expiring on 2026-11-11. Findings remain visible. The +[npm registry](https://registry.npmjs.org/zod) dates the first advertised fix, +4.6.6, to 2026-10-11 16:18:23.878 UTC; it reaches the conservative 30-day +minimum on 2026-11-11 UTC. It is a different major from Knip's `^3.22.4` range +and `zod-validation-error`'s `^3.24.4` peer range. A forced override would change +schema APIs and is not a compatible patch. By expiry, evaluate an aged, +compatible Knip/Zod update or an official v3 correction and repeat this trace. +Reassess immediately if configuration authority, plugin activation, compiler +wrappers, schema consumers or advisory prerequisites change. No early-release +admission or risk acceptance is made: the decision rests on absent failing +inputs, not on tolerating reachable exhaustion. + ## Source maps [GHSA-68fv-2mgg-jv7q](https://github.com/advisories/GHSA-68fv-2mgg-jv7q)