From ebc8505617420cab130c30f7e6add2ff78390801 Mon Sep 17 00:00:00 2001 From: ashnamehrotra Date: Fri, 26 Apr 2024 00:04:31 +0000 Subject: [PATCH] deploy: 61f7e9ae2a7246f016c347d18f2201114369b105 --- website/404.html | 17 +-- ...tyles.18145892.css => styles.46b1b3b3.css} | 2 +- website/assets/js/040fbf97.7c02e0ce.js | 1 - website/assets/js/040fbf97.ec4b04b2.js | 1 + website/assets/js/0480b142.00de69f7.js | 1 + website/assets/js/0480b142.7aa8457b.js | 1 - website/assets/js/053b5658.2fa8aa35.js | 1 + website/assets/js/053b5658.6494c787.js | 1 - website/assets/js/05a474a1.a98f3ad4.js | 1 + website/assets/js/05a474a1.af8883e8.js | 1 - website/assets/js/0a7bb60d.238534bf.js | 1 - website/assets/js/0a7bb60d.bc8038bc.js | 1 + website/assets/js/0aeccac2.02849ec8.js | 1 + website/assets/js/0aeccac2.920f5236.js | 1 - website/assets/js/0c380bb3.90242cf8.js | 1 - website/assets/js/0c380bb3.ba7d238d.js | 1 + website/assets/js/0d9a188d.5b7da772.js | 1 + website/assets/js/0d9a188d.ace05380.js | 1 - website/assets/js/11d58a17.31780db1.js | 1 - website/assets/js/11d58a17.6adc7a26.js | 1 + website/assets/js/12042f32.0e78f076.js | 1 + website/assets/js/12042f32.aeeebb3d.js | 1 - website/assets/js/14aa7e32.2379c39a.js | 1 + website/assets/js/14aa7e32.4837c057.js | 1 - website/assets/js/14b4e536.64a562da.js | 1 + website/assets/js/14b4e536.fee906c1.js | 1 - website/assets/js/17896441.7677fdee.js | 1 + website/assets/js/17896441.bd1b9c94.js | 1 - website/assets/js/17e02f37.2535a01b.js | 1 + website/assets/js/17e02f37.860083f8.js | 1 - website/assets/js/1b7d20b2.79e8a116.js | 1 - website/assets/js/1b7d20b2.f7af2c6c.js | 1 + website/assets/js/1be78505.61acc940.js | 1 - website/assets/js/2133a534.8db371e7.js | 1 + website/assets/js/2133a534.f95c97ab.js | 1 - website/assets/js/2237.ede9a640.js | 1 + website/assets/js/22539a87.04ad19a2.js | 1 + website/assets/js/22539a87.bed468c8.js | 1 - website/assets/js/22dce6f4.61bd0f54.js | 1 - website/assets/js/22dce6f4.96e84ed3.js | 1 + website/assets/js/27cf52aa.a3987a1f.js | 1 + website/assets/js/27cf52aa.cace1e2c.js | 1 - website/assets/js/282f11b9.66f006eb.js | 1 + website/assets/js/282f11b9.a334c6b0.js | 1 - website/assets/js/299b5310.1a9430b9.js | 1 + website/assets/js/299b5310.a01d158f.js | 1 - website/assets/js/2a636ef6.29da1826.js | 1 + website/assets/js/2a636ef6.df8e9b9f.js | 1 - website/assets/js/34f2f592.0c35ff01.js | 1 - website/assets/js/34f2f592.8fe0bd74.js | 1 + website/assets/js/3b8c55ea.034e3387.js | 1 - website/assets/js/3b8c55ea.995cc6f4.js | 1 + website/assets/js/3fcb412e.58d24b04.js | 1 - website/assets/js/3fcb412e.5addcbea.js | 1 + website/assets/js/4468d236.5fa3d39e.js | 1 + website/assets/js/4468d236.b180c32c.js | 1 - website/assets/js/48ae5635.51490778.js | 1 + website/assets/js/48ae5635.cd5cfcb1.js | 1 - website/assets/js/4972.9374abde.js | 1 - website/assets/js/499c1190.18188f88.js | 1 - website/assets/js/499c1190.5345b6eb.js | 1 + website/assets/js/4cb9f763.0128fb5a.js | 1 + website/assets/js/4cb9f763.c7fd5bba.js | 1 - website/assets/js/4d54d076.05bf40fc.js | 1 - website/assets/js/4d54d076.7f68c18d.js | 1 + website/assets/js/4d7fa969.9b8c384b.js | 1 + website/assets/js/4d7fa969.a26a2980.js | 1 - website/assets/js/4e9dbf89.413c7098.js | 1 + website/assets/js/4e9dbf89.f2c2b4d8.js | 1 - website/assets/js/512645f2.65269ba4.js | 1 + website/assets/js/512645f2.86b67e45.js | 1 - website/assets/js/57b2432c.ba8a447b.js | 1 - website/assets/js/57b2432c.dcb09c4b.js | 1 + website/assets/js/5c8ee200.f51d96f0.js | 1 - website/assets/js/5c8ee200.faac304c.js | 1 + website/assets/js/5e95c892.b1f5748f.js | 1 + website/assets/js/5fc6e4d2.b1cefb99.js | 1 - website/assets/js/5fc6e4d2.bc73c3e3.js | 1 + website/assets/js/6173fde8.2d07e3f9.js | 1 + website/assets/js/6173fde8.55493801.js | 1 - website/assets/js/683b919f.5b96ddd9.js | 1 - website/assets/js/683b919f.f74ca5f0.js | 1 + website/assets/js/6bd0979b.2824c577.js | 1 - website/assets/js/6bd0979b.ffc56042.js | 1 + website/assets/js/72e14192.4b528064.js | 1 - website/assets/js/72e14192.98634abe.js | 1 + website/assets/js/77a9ed43.c56f8af9.js | 1 + website/assets/js/77a9ed43.cdfe4c4f.js | 1 - website/assets/js/7d213ce9.26e02b59.js | 1 - website/assets/js/7d213ce9.8520a379.js | 1 + website/assets/js/7e72de0f.46d6e17a.js | 1 - website/assets/js/7e72de0f.7b9dfc9f.js | 1 + website/assets/js/801664dc.a10c85ef.js | 1 + website/assets/js/801664dc.e764cf5c.js | 1 - ...a5934.e1bcf12f.js => 804a5934.e93947f5.js} | 2 +- website/assets/js/84979900.3a0cf957.js | 1 + website/assets/js/84979900.d30448bf.js | 1 - website/assets/js/86b9c768.1fcb11fa.js | 1 + website/assets/js/86b9c768.6a1c76ae.js | 1 - website/assets/js/89eebbd3.287960d1.js | 1 + website/assets/js/89eebbd3.f1022e5a.js | 1 - website/assets/js/934782ba.2d85275c.js | 1 + website/assets/js/934782ba.c1eb8bb2.js | 1 - website/assets/js/935f2afb.a5a28739.js | 1 + website/assets/js/935f2afb.c1a00716.js | 1 - website/assets/js/98016c8b.9efc2921.js | 1 + website/assets/js/98016c8b.a456db16.js | 1 - website/assets/js/9b66f67b.6e821884.js | 1 + website/assets/js/9b66f67b.7d9c27c7.js | 1 - website/assets/js/9d9f8394.4bdf9bac.js | 1 - website/assets/js/9d9f8394.6a5641e3.js | 1 + website/assets/js/9e350ec0.ad02c27c.js | 1 + website/assets/js/9e350ec0.c4af5b3c.js | 1 - website/assets/js/9f27d5ef.f416e8f9.js | 1 - website/assets/js/9f27d5ef.f6738d54.js | 1 + website/assets/js/9f65b58a.3374a93f.js | 1 - website/assets/js/9f65b58a.37deb826.js | 1 + website/assets/js/a09c2993.2acef99e.js | 1 + website/assets/js/a09c2993.5da876e3.js | 1 - website/assets/js/a7bd4aaa.b82ee0df.js | 1 + website/assets/js/a94703ab.2668bc5b.js | 1 + website/assets/js/a95b810a.3050b258.js | 1 - website/assets/js/a95b810a.dac0b4cb.js | 1 + website/assets/js/b407a98a.2829481b.js | 1 + website/assets/js/b407a98a.7eeb3207.js | 1 - website/assets/js/b5d5e16b.0cb143ee.js | 1 - website/assets/js/b5d5e16b.8d66db9d.js | 1 + website/assets/js/b9421f89.2697740e.js | 1 - website/assets/js/b9421f89.6ce1c578.js | 1 + website/assets/js/baa9408d.07d2dc63.js | 1 + website/assets/js/baa9408d.c3109501.js | 1 - website/assets/js/bab6f5ab.0b153f31.js | 1 + website/assets/js/bab6f5ab.1dcf3cff.js | 1 - website/assets/js/bfa18532.76e61599.js | 1 + website/assets/js/bfa18532.f8fc985e.js | 1 - website/assets/js/c12dc9fd.1489a453.js | 1 + website/assets/js/c12dc9fd.2e134505.js | 1 - website/assets/js/c13ba925.cfea7e03.js | 1 - website/assets/js/c13ba925.e5b48032.js | 1 + website/assets/js/c5934ffc.0e15e6c8.js | 1 - website/assets/js/c5934ffc.658c31dc.js | 1 + website/assets/js/c66bbf8a.356f0a06.js | 1 - website/assets/js/c66bbf8a.9d86a839.js | 1 + website/assets/js/c698fe77.2ced926f.js | 1 - website/assets/js/c698fe77.8fa65e9b.js | 1 + website/assets/js/c73303db.5731fdf2.js | 1 + website/assets/js/c73303db.a8986184.js | 1 - website/assets/js/d2339658.3f5cadcb.js | 1 - website/assets/js/d2339658.5e176ea4.js | 1 + website/assets/js/d832efb0.23e6d38e.js | 1 - website/assets/js/d832efb0.70cfe285.js | 1 + website/assets/js/d9461b6c.63467998.js | 1 + website/assets/js/d9461b6c.cfb2e633.js | 1 - website/assets/js/dea0f9ea.c17bbf7a.js | 1 + website/assets/js/dea0f9ea.d742361f.js | 1 - website/assets/js/e4d3cddf.23b82d37.js | 1 + website/assets/js/e4d3cddf.34856fe1.js | 1 - website/assets/js/e8c0720c.4b83c6ce.js | 1 + website/assets/js/e8c0720c.d9edccdb.js | 1 - website/assets/js/e99d2929.85b262dd.js | 1 - website/assets/js/e99d2929.a259dc08.js | 1 + website/assets/js/ea9eabff.6d2cdf3d.js | 1 - website/assets/js/ea9eabff.7a3cec22.js | 1 + website/assets/js/efdb11b6.1df4609f.js | 1 - website/assets/js/efdb11b6.2663fe04.js | 1 + website/assets/js/f3bd9382.b63c2bc3.js | 1 + website/assets/js/f3bd9382.cfea1d0a.js | 1 - website/assets/js/f9459e94.d4932d2b.js | 1 - website/assets/js/f9459e94.d7263fc2.js | 1 + website/assets/js/fa2b770f.2b296f82.js | 1 + website/assets/js/fa2b770f.684fae56.js | 1 - website/assets/js/fcc13998.5e4b93fa.js | 1 - website/assets/js/fcc13998.bf8a3793.js | 1 + website/assets/js/ff86e818.6052e0ec.js | 1 - website/assets/js/ff86e818.93b13f6e.js | 1 + website/assets/js/main.991a83b1.js | 2 - website/assets/js/main.d9039351.js | 2 + ...CENSE.txt => main.d9039351.js.LICENSE.txt} | 47 ++++--- website/assets/js/runtime~main.62889678.js | 1 + website/assets/js/runtime~main.9639751f.js | 1 - website/best-practices.html | 27 ++-- website/code-of-conduct.html | 113 ++++++++++----- website/contributing.html | 122 ++++++++++++++-- website/custom-address.html | 37 +++-- website/design.html | 85 +++++++++-- website/development-tips.html | 73 ++++++++-- website/faq.html | 30 ++-- website/github-action.html | 18 ++- website/index.html | 60 ++++++-- website/installation.html | 29 ++-- website/maintainer-guidelines.html | 67 +++++++-- website/next.html | 61 ++++++-- website/next/best-practices.html | 27 ++-- website/next/code-of-conduct.html | 113 ++++++++++----- website/next/contributing.html | 122 ++++++++++++++-- website/next/custom-address.html | 37 +++-- website/next/design.html | 85 +++++++++-- website/next/development-tips.html | 73 ++++++++-- website/next/faq.html | 30 ++-- website/next/github-action.html | 18 ++- website/next/installation.html | 29 ++-- website/next/maintainer-guidelines.html | 67 +++++++-- website/next/output.html | 33 +++-- website/next/quick-start.html | 72 ++++++++-- website/next/release.html | 25 ++-- website/next/scanner-plugins.html | 38 +++-- website/next/troubleshooting.html | 31 ++-- website/output.html | 33 +++-- website/quick-start.html | 72 ++++++++-- website/release.html | 25 ++-- website/scanner-plugins.html | 38 +++-- website/troubleshooting.html | 31 ++-- website/v0.1.x.html | 60 ++++++-- website/v0.1.x/code-of-conduct.html | 113 ++++++++++----- website/v0.1.x/contributing.html | 128 +++++++++++++++-- website/v0.1.x/design.html | 85 +++++++++-- website/v0.1.x/faq.html | 21 +-- website/v0.1.x/installation.html | 24 ++-- website/v0.1.x/quick-start.html | 60 ++++++-- website/v0.2.x.html | 60 ++++++-- website/v0.2.x/code-of-conduct.html | 113 ++++++++++----- website/v0.2.x/contributing.html | 128 +++++++++++++++-- website/v0.2.x/design.html | 85 +++++++++-- website/v0.2.x/faq.html | 21 +-- website/v0.2.x/installation.html | 24 ++-- website/v0.2.x/quick-start.html | 60 ++++++-- website/v0.3.x.html | 60 ++++++-- website/v0.3.x/code-of-conduct.html | 113 ++++++++++----- website/v0.3.x/contributing.html | 132 ++++++++++++++++-- website/v0.3.x/design.html | 85 +++++++++-- website/v0.3.x/faq.html | 21 +-- website/v0.3.x/installation.html | 24 ++-- website/v0.3.x/quick-start.html | 60 ++++++-- website/v0.4.x.html | 60 ++++++-- website/v0.4.x/code-of-conduct.html | 113 ++++++++++----- website/v0.4.x/contributing.html | 122 ++++++++++++++-- website/v0.4.x/design.html | 85 +++++++++-- website/v0.4.x/faq.html | 21 +-- website/v0.4.x/github-action.html | 34 +++-- website/v0.4.x/installation.html | 24 ++-- website/v0.4.x/quick-start.html | 90 ++++++++++-- website/v0.4.x/release.html | 25 ++-- website/v0.4.x/troubleshooting.html | 31 ++-- website/v0.5.x.html | 60 ++++++-- website/v0.5.x/code-of-conduct.html | 113 ++++++++++----- website/v0.5.x/contributing.html | 122 ++++++++++++++-- website/v0.5.x/design.html | 85 +++++++++-- website/v0.5.x/faq.html | 30 ++-- website/v0.5.x/github-action.html | 34 +++-- website/v0.5.x/installation.html | 24 ++-- website/v0.5.x/output.html | 33 +++-- website/v0.5.x/quick-start.html | 86 ++++++++++-- website/v0.5.x/release.html | 25 ++-- website/v0.5.x/scanner-plugins.html | 38 +++-- website/v0.5.x/troubleshooting.html | 31 ++-- 255 files changed, 3647 insertions(+), 1083 deletions(-) rename website/assets/css/{styles.18145892.css => styles.46b1b3b3.css} (59%) delete mode 100644 website/assets/js/040fbf97.7c02e0ce.js create mode 100644 website/assets/js/040fbf97.ec4b04b2.js create mode 100644 website/assets/js/0480b142.00de69f7.js delete mode 100644 website/assets/js/0480b142.7aa8457b.js create mode 100644 website/assets/js/053b5658.2fa8aa35.js delete mode 100644 website/assets/js/053b5658.6494c787.js create mode 100644 website/assets/js/05a474a1.a98f3ad4.js delete mode 100644 website/assets/js/05a474a1.af8883e8.js delete mode 100644 website/assets/js/0a7bb60d.238534bf.js create mode 100644 website/assets/js/0a7bb60d.bc8038bc.js create mode 100644 website/assets/js/0aeccac2.02849ec8.js delete mode 100644 website/assets/js/0aeccac2.920f5236.js delete mode 100644 website/assets/js/0c380bb3.90242cf8.js create mode 100644 website/assets/js/0c380bb3.ba7d238d.js create mode 100644 website/assets/js/0d9a188d.5b7da772.js delete mode 100644 website/assets/js/0d9a188d.ace05380.js delete mode 100644 website/assets/js/11d58a17.31780db1.js create mode 100644 website/assets/js/11d58a17.6adc7a26.js create mode 100644 website/assets/js/12042f32.0e78f076.js delete mode 100644 website/assets/js/12042f32.aeeebb3d.js create mode 100644 website/assets/js/14aa7e32.2379c39a.js delete mode 100644 website/assets/js/14aa7e32.4837c057.js create mode 100644 website/assets/js/14b4e536.64a562da.js delete mode 100644 website/assets/js/14b4e536.fee906c1.js create mode 100644 website/assets/js/17896441.7677fdee.js delete mode 100644 website/assets/js/17896441.bd1b9c94.js create mode 100644 website/assets/js/17e02f37.2535a01b.js delete mode 100644 website/assets/js/17e02f37.860083f8.js delete mode 100644 website/assets/js/1b7d20b2.79e8a116.js create mode 100644 website/assets/js/1b7d20b2.f7af2c6c.js delete mode 100644 website/assets/js/1be78505.61acc940.js create mode 100644 website/assets/js/2133a534.8db371e7.js delete mode 100644 website/assets/js/2133a534.f95c97ab.js create mode 100644 website/assets/js/2237.ede9a640.js create mode 100644 website/assets/js/22539a87.04ad19a2.js delete mode 100644 website/assets/js/22539a87.bed468c8.js delete mode 100644 website/assets/js/22dce6f4.61bd0f54.js create mode 100644 website/assets/js/22dce6f4.96e84ed3.js create mode 100644 website/assets/js/27cf52aa.a3987a1f.js delete mode 100644 website/assets/js/27cf52aa.cace1e2c.js create mode 100644 website/assets/js/282f11b9.66f006eb.js delete mode 100644 website/assets/js/282f11b9.a334c6b0.js create mode 100644 website/assets/js/299b5310.1a9430b9.js delete mode 100644 website/assets/js/299b5310.a01d158f.js create mode 100644 website/assets/js/2a636ef6.29da1826.js delete mode 100644 website/assets/js/2a636ef6.df8e9b9f.js delete mode 100644 website/assets/js/34f2f592.0c35ff01.js create mode 100644 website/assets/js/34f2f592.8fe0bd74.js delete mode 100644 website/assets/js/3b8c55ea.034e3387.js create mode 100644 website/assets/js/3b8c55ea.995cc6f4.js delete mode 100644 website/assets/js/3fcb412e.58d24b04.js create mode 100644 website/assets/js/3fcb412e.5addcbea.js create mode 100644 website/assets/js/4468d236.5fa3d39e.js delete mode 100644 website/assets/js/4468d236.b180c32c.js create mode 100644 website/assets/js/48ae5635.51490778.js delete mode 100644 website/assets/js/48ae5635.cd5cfcb1.js delete mode 100644 website/assets/js/4972.9374abde.js delete mode 100644 website/assets/js/499c1190.18188f88.js create mode 100644 website/assets/js/499c1190.5345b6eb.js create mode 100644 website/assets/js/4cb9f763.0128fb5a.js delete mode 100644 website/assets/js/4cb9f763.c7fd5bba.js delete mode 100644 website/assets/js/4d54d076.05bf40fc.js create mode 100644 website/assets/js/4d54d076.7f68c18d.js create mode 100644 website/assets/js/4d7fa969.9b8c384b.js delete mode 100644 website/assets/js/4d7fa969.a26a2980.js create mode 100644 website/assets/js/4e9dbf89.413c7098.js delete mode 100644 website/assets/js/4e9dbf89.f2c2b4d8.js create mode 100644 website/assets/js/512645f2.65269ba4.js delete mode 100644 website/assets/js/512645f2.86b67e45.js delete mode 100644 website/assets/js/57b2432c.ba8a447b.js create mode 100644 website/assets/js/57b2432c.dcb09c4b.js delete mode 100644 website/assets/js/5c8ee200.f51d96f0.js create mode 100644 website/assets/js/5c8ee200.faac304c.js create mode 100644 website/assets/js/5e95c892.b1f5748f.js delete mode 100644 website/assets/js/5fc6e4d2.b1cefb99.js create mode 100644 website/assets/js/5fc6e4d2.bc73c3e3.js create mode 100644 website/assets/js/6173fde8.2d07e3f9.js delete mode 100644 website/assets/js/6173fde8.55493801.js delete mode 100644 website/assets/js/683b919f.5b96ddd9.js create mode 100644 website/assets/js/683b919f.f74ca5f0.js delete mode 100644 website/assets/js/6bd0979b.2824c577.js create mode 100644 website/assets/js/6bd0979b.ffc56042.js delete mode 100644 website/assets/js/72e14192.4b528064.js create mode 100644 website/assets/js/72e14192.98634abe.js create mode 100644 website/assets/js/77a9ed43.c56f8af9.js delete mode 100644 website/assets/js/77a9ed43.cdfe4c4f.js delete mode 100644 website/assets/js/7d213ce9.26e02b59.js create mode 100644 website/assets/js/7d213ce9.8520a379.js delete mode 100644 website/assets/js/7e72de0f.46d6e17a.js create mode 100644 website/assets/js/7e72de0f.7b9dfc9f.js create mode 100644 website/assets/js/801664dc.a10c85ef.js delete mode 100644 website/assets/js/801664dc.e764cf5c.js rename website/assets/js/{804a5934.e1bcf12f.js => 804a5934.e93947f5.js} (63%) create mode 100644 website/assets/js/84979900.3a0cf957.js delete mode 100644 website/assets/js/84979900.d30448bf.js create mode 100644 website/assets/js/86b9c768.1fcb11fa.js delete mode 100644 website/assets/js/86b9c768.6a1c76ae.js create mode 100644 website/assets/js/89eebbd3.287960d1.js delete mode 100644 website/assets/js/89eebbd3.f1022e5a.js create mode 100644 website/assets/js/934782ba.2d85275c.js delete mode 100644 website/assets/js/934782ba.c1eb8bb2.js create mode 100644 website/assets/js/935f2afb.a5a28739.js delete mode 100644 website/assets/js/935f2afb.c1a00716.js create mode 100644 website/assets/js/98016c8b.9efc2921.js delete mode 100644 website/assets/js/98016c8b.a456db16.js create mode 100644 website/assets/js/9b66f67b.6e821884.js delete mode 100644 website/assets/js/9b66f67b.7d9c27c7.js delete mode 100644 website/assets/js/9d9f8394.4bdf9bac.js create mode 100644 website/assets/js/9d9f8394.6a5641e3.js create mode 100644 website/assets/js/9e350ec0.ad02c27c.js delete mode 100644 website/assets/js/9e350ec0.c4af5b3c.js delete mode 100644 website/assets/js/9f27d5ef.f416e8f9.js create mode 100644 website/assets/js/9f27d5ef.f6738d54.js delete mode 100644 website/assets/js/9f65b58a.3374a93f.js create mode 100644 website/assets/js/9f65b58a.37deb826.js create mode 100644 website/assets/js/a09c2993.2acef99e.js delete mode 100644 website/assets/js/a09c2993.5da876e3.js create mode 100644 website/assets/js/a7bd4aaa.b82ee0df.js create mode 100644 website/assets/js/a94703ab.2668bc5b.js delete mode 100644 website/assets/js/a95b810a.3050b258.js create mode 100644 website/assets/js/a95b810a.dac0b4cb.js create mode 100644 website/assets/js/b407a98a.2829481b.js delete mode 100644 website/assets/js/b407a98a.7eeb3207.js delete mode 100644 website/assets/js/b5d5e16b.0cb143ee.js create mode 100644 website/assets/js/b5d5e16b.8d66db9d.js delete mode 100644 website/assets/js/b9421f89.2697740e.js create mode 100644 website/assets/js/b9421f89.6ce1c578.js create mode 100644 website/assets/js/baa9408d.07d2dc63.js delete mode 100644 website/assets/js/baa9408d.c3109501.js create mode 100644 website/assets/js/bab6f5ab.0b153f31.js delete mode 100644 website/assets/js/bab6f5ab.1dcf3cff.js create mode 100644 website/assets/js/bfa18532.76e61599.js delete mode 100644 website/assets/js/bfa18532.f8fc985e.js create mode 100644 website/assets/js/c12dc9fd.1489a453.js delete mode 100644 website/assets/js/c12dc9fd.2e134505.js delete mode 100644 website/assets/js/c13ba925.cfea7e03.js create mode 100644 website/assets/js/c13ba925.e5b48032.js delete mode 100644 website/assets/js/c5934ffc.0e15e6c8.js create mode 100644 website/assets/js/c5934ffc.658c31dc.js delete mode 100644 website/assets/js/c66bbf8a.356f0a06.js create mode 100644 website/assets/js/c66bbf8a.9d86a839.js delete mode 100644 website/assets/js/c698fe77.2ced926f.js create mode 100644 website/assets/js/c698fe77.8fa65e9b.js create mode 100644 website/assets/js/c73303db.5731fdf2.js delete mode 100644 website/assets/js/c73303db.a8986184.js delete mode 100644 website/assets/js/d2339658.3f5cadcb.js create mode 100644 website/assets/js/d2339658.5e176ea4.js delete mode 100644 website/assets/js/d832efb0.23e6d38e.js create mode 100644 website/assets/js/d832efb0.70cfe285.js create mode 100644 website/assets/js/d9461b6c.63467998.js delete mode 100644 website/assets/js/d9461b6c.cfb2e633.js create mode 100644 website/assets/js/dea0f9ea.c17bbf7a.js delete mode 100644 website/assets/js/dea0f9ea.d742361f.js create mode 100644 website/assets/js/e4d3cddf.23b82d37.js delete mode 100644 website/assets/js/e4d3cddf.34856fe1.js create mode 100644 website/assets/js/e8c0720c.4b83c6ce.js delete mode 100644 website/assets/js/e8c0720c.d9edccdb.js delete mode 100644 website/assets/js/e99d2929.85b262dd.js create mode 100644 website/assets/js/e99d2929.a259dc08.js delete mode 100644 website/assets/js/ea9eabff.6d2cdf3d.js create mode 100644 website/assets/js/ea9eabff.7a3cec22.js delete mode 100644 website/assets/js/efdb11b6.1df4609f.js create mode 100644 website/assets/js/efdb11b6.2663fe04.js create mode 100644 website/assets/js/f3bd9382.b63c2bc3.js delete mode 100644 website/assets/js/f3bd9382.cfea1d0a.js delete mode 100644 website/assets/js/f9459e94.d4932d2b.js create mode 100644 website/assets/js/f9459e94.d7263fc2.js create mode 100644 website/assets/js/fa2b770f.2b296f82.js delete mode 100644 website/assets/js/fa2b770f.684fae56.js delete mode 100644 website/assets/js/fcc13998.5e4b93fa.js create mode 100644 website/assets/js/fcc13998.bf8a3793.js delete mode 100644 website/assets/js/ff86e818.6052e0ec.js create mode 100644 website/assets/js/ff86e818.93b13f6e.js delete mode 100644 website/assets/js/main.991a83b1.js create mode 100644 website/assets/js/main.d9039351.js rename website/assets/js/{main.991a83b1.js.LICENSE.txt => main.d9039351.js.LICENSE.txt} (76%) create mode 100644 website/assets/js/runtime~main.62889678.js delete mode 100644 website/assets/js/runtime~main.9639751f.js diff --git a/website/404.html b/website/404.html index ca5e8a0b..70e8ec00 100644 --- a/website/404.html +++ b/website/404.html @@ -1,19 +1,16 @@ - + - -Page Not Found | Copacetic + +Copacetic - - - + + + -
-
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- - +
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

\ No newline at end of file diff --git a/website/assets/css/styles.18145892.css b/website/assets/css/styles.46b1b3b3.css similarity index 59% rename from website/assets/css/styles.18145892.css rename to website/assets/css/styles.46b1b3b3.css index 73a56c7d..691e2c1d 100644 --- a/website/assets/css/styles.18145892.css +++ b/website/assets/css/styles.46b1b3b3.css @@ -1 +1 @@ -.col,.container{padding:0 var(--ifm-spacing-horizontal);width:100%}.markdown>h2,.markdown>h3,.markdown>h4,.markdown>h5,.markdown>h6{margin-bottom:calc(var(--ifm-heading-vertical-rhythm-bottom)*var(--ifm-leading))}.markdown li,body{word-wrap:break-word}body,ol ol,ol ul,ul ol,ul ul{margin:0}pre,table{overflow:auto}blockquote,pre{margin:0 0 var(--ifm-spacing-vertical)}.breadcrumbs__link,.button{transition-timing-function:var(--ifm-transition-timing-default)}.button,code{vertical-align:middle}.button--outline.button--active,.button--outline:active,.button--outline:hover,:root{--ifm-button-color:var(--ifm-font-color-base-inverse)}.menu__link:hover,a{transition:color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.navbar--dark,:root{--ifm-navbar-link-hover-color:var(--ifm-color-primary)}.menu,.navbar-sidebar{overflow-x:hidden}:root,html[data-theme=dark]{--ifm-color-emphasis-500:var(--ifm-color-gray-500)}.toggleButton_gllP,html{-webkit-tap-highlight-color:transparent}.clean-list,.containsTaskList_mC6p,.details_lb9f>summary,.dropdown__menu,.menu__list{list-style:none}:root{--ifm-color-scheme:light;--ifm-dark-value:10%;--ifm-darker-value:15%;--ifm-darkest-value:30%;--ifm-light-value:15%;--ifm-lighter-value:30%;--ifm-lightest-value:50%;--ifm-contrast-background-value:90%;--ifm-contrast-foreground-value:70%;--ifm-contrast-background-dark-value:70%;--ifm-contrast-foreground-dark-value:90%;--ifm-color-primary:#3578e5;--ifm-color-secondary:#ebedf0;--ifm-color-success:#00a400;--ifm-color-info:#54c7ec;--ifm-color-warning:#ffba00;--ifm-color-danger:#fa383e;--ifm-color-primary-dark:#306cce;--ifm-color-primary-darker:#2d66c3;--ifm-color-primary-darkest:#2554a0;--ifm-color-primary-light:#538ce9;--ifm-color-primary-lighter:#72a1ed;--ifm-color-primary-lightest:#9abcf2;--ifm-color-primary-contrast-background:#ebf2fc;--ifm-color-primary-contrast-foreground:#102445;--ifm-color-secondary-dark:#d4d5d8;--ifm-color-secondary-darker:#c8c9cc;--ifm-color-secondary-darkest:#a4a6a8;--ifm-color-secondary-light:#eef0f2;--ifm-color-secondary-lighter:#f1f2f5;--ifm-color-secondary-lightest:#f5f6f8;--ifm-color-secondary-contrast-background:#fdfdfe;--ifm-color-secondary-contrast-foreground:#474748;--ifm-color-success-dark:#009400;--ifm-color-success-darker:#008b00;--ifm-color-success-darkest:#007300;--ifm-color-success-light:#26b226;--ifm-color-success-lighter:#4dbf4d;--ifm-color-success-lightest:#80d280;--ifm-color-success-contrast-background:#e6f6e6;--ifm-color-success-contrast-foreground:#003100;--ifm-color-info-dark:#4cb3d4;--ifm-color-info-darker:#47a9c9;--ifm-color-info-darkest:#3b8ba5;--ifm-color-info-light:#6ecfef;--ifm-color-info-lighter:#87d8f2;--ifm-color-info-lightest:#aae3f6;--ifm-color-info-contrast-background:#eef9fd;--ifm-color-info-contrast-foreground:#193c47;--ifm-color-warning-dark:#e6a700;--ifm-color-warning-darker:#d99e00;--ifm-color-warning-darkest:#b38200;--ifm-color-warning-light:#ffc426;--ifm-color-warning-lighter:#ffcf4d;--ifm-color-warning-lightest:#ffdd80;--ifm-color-warning-contrast-background:#fff8e6;--ifm-color-warning-contrast-foreground:#4d3800;--ifm-color-danger-dark:#e13238;--ifm-color-danger-darker:#d53035;--ifm-color-danger-darkest:#af272b;--ifm-color-danger-light:#fb565b;--ifm-color-danger-lighter:#fb7478;--ifm-color-danger-lightest:#fd9c9f;--ifm-color-danger-contrast-background:#ffebec;--ifm-color-danger-contrast-foreground:#4b1113;--ifm-color-white:#fff;--ifm-color-black:#000;--ifm-color-gray-0:var(--ifm-color-white);--ifm-color-gray-100:#f5f6f7;--ifm-color-gray-200:#ebedf0;--ifm-color-gray-300:#dadde1;--ifm-color-gray-400:#ccd0d5;--ifm-color-gray-500:#bec3c9;--ifm-color-gray-600:#8d949e;--ifm-color-gray-700:#606770;--ifm-color-gray-800:#444950;--ifm-color-gray-900:#1c1e21;--ifm-color-gray-1000:var(--ifm-color-black);--ifm-color-emphasis-0:var(--ifm-color-gray-0);--ifm-color-emphasis-100:var(--ifm-color-gray-100);--ifm-color-emphasis-200:var(--ifm-color-gray-200);--ifm-color-emphasis-300:var(--ifm-color-gray-300);--ifm-color-emphasis-400:var(--ifm-color-gray-400);--ifm-color-emphasis-600:var(--ifm-color-gray-600);--ifm-color-emphasis-700:var(--ifm-color-gray-700);--ifm-color-emphasis-800:var(--ifm-color-gray-800);--ifm-color-emphasis-900:var(--ifm-color-gray-900);--ifm-color-emphasis-1000:var(--ifm-color-gray-1000);--ifm-color-content:var(--ifm-color-emphasis-900);--ifm-color-content-inverse:var(--ifm-color-emphasis-0);--ifm-color-content-secondary:#525860;--ifm-background-color:#0000;--ifm-background-surface-color:var(--ifm-color-content-inverse);--ifm-global-border-width:1px;--ifm-global-radius:0.4rem;--ifm-hover-overlay:#0000000d;--ifm-font-color-base:var(--ifm-color-content);--ifm-font-color-base-inverse:var(--ifm-color-content-inverse);--ifm-font-color-secondary:var(--ifm-color-content-secondary);--ifm-font-family-base:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--ifm-font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--ifm-font-size-base:100%;--ifm-font-weight-light:300;--ifm-font-weight-normal:400;--ifm-font-weight-semibold:500;--ifm-font-weight-bold:700;--ifm-font-weight-base:var(--ifm-font-weight-normal);--ifm-line-height-base:1.65;--ifm-global-spacing:1rem;--ifm-spacing-vertical:var(--ifm-global-spacing);--ifm-spacing-horizontal:var(--ifm-global-spacing);--ifm-transition-fast:200ms;--ifm-transition-slow:400ms;--ifm-transition-timing-default:cubic-bezier(0.08,0.52,0.52,1);--ifm-global-shadow-lw:0 1px 2px 0 #0000001a;--ifm-global-shadow-md:0 5px 40px #0003;--ifm-global-shadow-tl:0 12px 28px 0 #0003,0 2px 4px 0 #0000001a;--ifm-z-index-dropdown:100;--ifm-z-index-fixed:200;--ifm-z-index-overlay:400;--ifm-container-width:1140px;--ifm-container-width-xl:1320px;--ifm-code-background:#f6f7f8;--ifm-code-border-radius:var(--ifm-global-radius);--ifm-code-font-size:90%;--ifm-code-padding-horizontal:0.1rem;--ifm-code-padding-vertical:0.1rem;--ifm-pre-background:var(--ifm-code-background);--ifm-pre-border-radius:var(--ifm-code-border-radius);--ifm-pre-color:inherit;--ifm-pre-line-height:1.45;--ifm-pre-padding:1rem;--ifm-heading-color:inherit;--ifm-heading-margin-top:0;--ifm-heading-margin-bottom:var(--ifm-spacing-vertical);--ifm-heading-font-family:var(--ifm-font-family-base);--ifm-heading-font-weight:var(--ifm-font-weight-bold);--ifm-heading-line-height:1.25;--ifm-h1-font-size:2rem;--ifm-h2-font-size:1.5rem;--ifm-h3-font-size:1.25rem;--ifm-h4-font-size:1rem;--ifm-h5-font-size:0.875rem;--ifm-h6-font-size:0.85rem;--ifm-image-alignment-padding:1.25rem;--ifm-leading-desktop:1.25;--ifm-leading:calc(var(--ifm-leading-desktop)*1rem);--ifm-list-left-padding:2rem;--ifm-list-margin:1rem;--ifm-list-item-margin:0.25rem;--ifm-list-paragraph-margin:1rem;--ifm-table-cell-padding:0.75rem;--ifm-table-background:#0000;--ifm-table-stripe-background:#00000008;--ifm-table-border-width:1px;--ifm-table-border-color:var(--ifm-color-emphasis-300);--ifm-table-head-background:inherit;--ifm-table-head-color:inherit;--ifm-table-head-font-weight:var(--ifm-font-weight-bold);--ifm-table-cell-color:inherit;--ifm-link-color:var(--ifm-color-primary);--ifm-link-decoration:none;--ifm-link-hover-color:var(--ifm-link-color);--ifm-link-hover-decoration:underline;--ifm-paragraph-margin-bottom:var(--ifm-leading);--ifm-blockquote-font-size:var(--ifm-font-size-base);--ifm-blockquote-border-left-width:2px;--ifm-blockquote-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-blockquote-padding-vertical:0;--ifm-blockquote-shadow:none;--ifm-blockquote-color:var(--ifm-color-emphasis-800);--ifm-blockquote-border-color:var(--ifm-color-emphasis-300);--ifm-hr-background-color:var(--ifm-color-emphasis-500);--ifm-hr-height:1px;--ifm-hr-margin-vertical:1.5rem;--ifm-scrollbar-size:7px;--ifm-scrollbar-track-background-color:#f1f1f1;--ifm-scrollbar-thumb-background-color:silver;--ifm-scrollbar-thumb-hover-background-color:#a7a7a7;--ifm-alert-background-color:inherit;--ifm-alert-border-color:inherit;--ifm-alert-border-radius:var(--ifm-global-radius);--ifm-alert-border-width:0px;--ifm-alert-border-left-width:5px;--ifm-alert-color:var(--ifm-font-color-base);--ifm-alert-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-alert-padding-vertical:var(--ifm-spacing-vertical);--ifm-alert-shadow:var(--ifm-global-shadow-lw);--ifm-avatar-intro-margin:1rem;--ifm-avatar-intro-alignment:inherit;--ifm-avatar-photo-size:3rem;--ifm-badge-background-color:inherit;--ifm-badge-border-color:inherit;--ifm-badge-border-radius:var(--ifm-global-radius);--ifm-badge-border-width:var(--ifm-global-border-width);--ifm-badge-color:var(--ifm-color-white);--ifm-badge-padding-horizontal:calc(var(--ifm-spacing-horizontal)*0.5);--ifm-badge-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-breadcrumb-border-radius:1.5rem;--ifm-breadcrumb-spacing:0.5rem;--ifm-breadcrumb-color-active:var(--ifm-color-primary);--ifm-breadcrumb-item-background-active:var(--ifm-hover-overlay);--ifm-breadcrumb-padding-horizontal:0.8rem;--ifm-breadcrumb-padding-vertical:0.4rem;--ifm-breadcrumb-size-multiplier:1;--ifm-breadcrumb-separator:url('data:image/svg+xml;utf8,');--ifm-breadcrumb-separator-filter:none;--ifm-breadcrumb-separator-size:0.5rem;--ifm-breadcrumb-separator-size-multiplier:1.25;--ifm-button-background-color:inherit;--ifm-button-border-color:var(--ifm-button-background-color);--ifm-button-border-width:var(--ifm-global-border-width);--ifm-button-font-weight:var(--ifm-font-weight-bold);--ifm-button-padding-horizontal:1.5rem;--ifm-button-padding-vertical:0.375rem;--ifm-button-size-multiplier:1;--ifm-button-transition-duration:var(--ifm-transition-fast);--ifm-button-border-radius:calc(var(--ifm-global-radius)*var(--ifm-button-size-multiplier));--ifm-button-group-spacing:2px;--ifm-card-background-color:var(--ifm-background-surface-color);--ifm-card-border-radius:calc(var(--ifm-global-radius)*2);--ifm-card-horizontal-spacing:var(--ifm-global-spacing);--ifm-card-vertical-spacing:var(--ifm-global-spacing);--ifm-toc-border-color:var(--ifm-color-emphasis-300);--ifm-toc-link-color:var(--ifm-color-content-secondary);--ifm-toc-padding-vertical:0.5rem;--ifm-toc-padding-horizontal:0.5rem;--ifm-dropdown-background-color:var(--ifm-background-surface-color);--ifm-dropdown-font-weight:var(--ifm-font-weight-semibold);--ifm-dropdown-link-color:var(--ifm-font-color-base);--ifm-dropdown-hover-background-color:var(--ifm-hover-overlay);--ifm-footer-background-color:var(--ifm-color-emphasis-100);--ifm-footer-color:inherit;--ifm-footer-link-color:var(--ifm-color-emphasis-700);--ifm-footer-link-hover-color:var(--ifm-color-primary);--ifm-footer-link-horizontal-spacing:0.5rem;--ifm-footer-padding-horizontal:calc(var(--ifm-spacing-horizontal)*2);--ifm-footer-padding-vertical:calc(var(--ifm-spacing-vertical)*2);--ifm-footer-title-color:inherit;--ifm-footer-logo-max-width:min(30rem,90vw);--ifm-hero-background-color:var(--ifm-background-surface-color);--ifm-hero-text-color:var(--ifm-color-emphasis-800);--ifm-menu-color:var(--ifm-color-emphasis-700);--ifm-menu-color-active:var(--ifm-color-primary);--ifm-menu-color-background-active:var(--ifm-hover-overlay);--ifm-menu-color-background-hover:var(--ifm-hover-overlay);--ifm-menu-link-padding-horizontal:0.75rem;--ifm-menu-link-padding-vertical:0.375rem;--ifm-menu-link-sublist-icon:url('data:image/svg+xml;utf8,');--ifm-menu-link-sublist-icon-filter:none;--ifm-navbar-background-color:var(--ifm-background-surface-color);--ifm-navbar-height:3.75rem;--ifm-navbar-item-padding-horizontal:0.75rem;--ifm-navbar-item-padding-vertical:0.25rem;--ifm-navbar-link-color:var(--ifm-font-color-base);--ifm-navbar-link-active-color:var(--ifm-link-color);--ifm-navbar-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-navbar-padding-vertical:calc(var(--ifm-spacing-vertical)*0.5);--ifm-navbar-shadow:var(--ifm-global-shadow-lw);--ifm-navbar-search-input-background-color:var(--ifm-color-emphasis-200);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-800);--ifm-navbar-search-input-placeholder-color:var(--ifm-color-emphasis-500);--ifm-navbar-search-input-icon:url('data:image/svg+xml;utf8,');--ifm-navbar-sidebar-width:83vw;--ifm-pagination-border-radius:var(--ifm-global-radius);--ifm-pagination-color-active:var(--ifm-color-primary);--ifm-pagination-font-size:1rem;--ifm-pagination-item-active-background:var(--ifm-hover-overlay);--ifm-pagination-page-spacing:0.2em;--ifm-pagination-padding-horizontal:calc(var(--ifm-spacing-horizontal)*1);--ifm-pagination-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-pagination-nav-border-radius:var(--ifm-global-radius);--ifm-pagination-nav-color-hover:var(--ifm-color-primary);--ifm-pills-color-active:var(--ifm-color-primary);--ifm-pills-color-background-active:var(--ifm-hover-overlay);--ifm-pills-spacing:0.125rem;--ifm-tabs-color:var(--ifm-font-color-secondary);--ifm-tabs-color-active:var(--ifm-color-primary);--ifm-tabs-color-active-border:var(--ifm-tabs-color-active);--ifm-tabs-padding-horizontal:1rem;--ifm-tabs-padding-vertical:1rem;--docusaurus-progress-bar-color:var(--ifm-color-primary);--ifm-color-primary:#2e8555;--ifm-color-primary-dark:#29784c;--ifm-color-primary-darker:#277148;--ifm-color-primary-darkest:#205d3b;--ifm-color-primary-light:#33925d;--ifm-color-primary-lighter:#359962;--ifm-color-primary-lightest:#3cad6e;--ifm-code-font-size:95%;--docusaurus-highlighted-code-line-bg:#0000001a;--docusaurus-announcement-bar-height:auto;--docusaurus-tag-list-border:var(--ifm-color-emphasis-300);--docusaurus-collapse-button-bg:#0000;--docusaurus-collapse-button-bg-hover:#0000001a;--doc-sidebar-width:300px;--doc-sidebar-hidden-width:30px}.badge--danger,.badge--info,.badge--primary,.badge--secondary,.badge--success,.badge--warning{--ifm-badge-border-color:var(--ifm-badge-background-color)}.button--link,.button--outline{--ifm-button-background-color:#0000}*{box-sizing:border-box}html{-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:100%;text-size-adjust:100%;background-color:var(--ifm-background-color);color:var(--ifm-font-color-base);color-scheme:var(--ifm-color-scheme);font:var(--ifm-font-size-base)/var(--ifm-line-height-base) var(--ifm-font-family-base);text-rendering:optimizelegibility}iframe{border:0;color-scheme:auto}.container{margin:0 auto;max-width:var(--ifm-container-width)}.container--fluid{max-width:inherit}.row{display:flex;flex-wrap:wrap;margin:0 calc(var(--ifm-spacing-horizontal)*-1)}.margin-bottom--none,.margin-vert--none,.markdown>:last-child{margin-bottom:0!important}.margin-top--none,.margin-vert--none{margin-top:0!important}.row--no-gutters{margin-left:0;margin-right:0}.margin-horiz--none,.margin-right--none{margin-right:0!important}.row--no-gutters>.col{padding-left:0;padding-right:0}.row--align-top{align-items:flex-start}.row--align-bottom{align-items:flex-end}.menuExternalLink_NmtK,.row--align-center{align-items:center}.row--align-stretch{align-items:stretch}.row--align-baseline{align-items:baseline}.col{--ifm-col-width:100%;flex:1 0;margin-left:0;max-width:var(--ifm-col-width)}.padding-bottom--none,.padding-vert--none{padding-bottom:0!important}.padding-top--none,.padding-vert--none{padding-top:0!important}.padding-horiz--none,.padding-left--none{padding-left:0!important}.padding-horiz--none,.padding-right--none{padding-right:0!important}.col[class*=col--]{flex:0 0 var(--ifm-col-width)}.col--1{--ifm-col-width:8.33333%}.col--offset-1{margin-left:8.33333%}.col--2{--ifm-col-width:16.66667%}.col--offset-2{margin-left:16.66667%}.col--3{--ifm-col-width:25%}.col--offset-3{margin-left:25%}.col--4{--ifm-col-width:33.33333%}.col--offset-4{margin-left:33.33333%}.col--5{--ifm-col-width:41.66667%}.col--offset-5{margin-left:41.66667%}.col--6{--ifm-col-width:50%}.col--offset-6{margin-left:50%}.col--7{--ifm-col-width:58.33333%}.col--offset-7{margin-left:58.33333%}.col--8{--ifm-col-width:66.66667%}.col--offset-8{margin-left:66.66667%}.col--9{--ifm-col-width:75%}.col--offset-9{margin-left:75%}.col--10{--ifm-col-width:83.33333%}.col--offset-10{margin-left:83.33333%}.col--11{--ifm-col-width:91.66667%}.col--offset-11{margin-left:91.66667%}.col--12{--ifm-col-width:100%}.col--offset-12{margin-left:100%}.margin-horiz--none,.margin-left--none{margin-left:0!important}.margin--none{margin:0!important}.margin-bottom--xs,.margin-vert--xs{margin-bottom:.25rem!important}.margin-top--xs,.margin-vert--xs{margin-top:.25rem!important}.margin-horiz--xs,.margin-left--xs{margin-left:.25rem!important}.margin-horiz--xs,.margin-right--xs{margin-right:.25rem!important}.margin--xs{margin:.25rem!important}.margin-bottom--sm,.margin-vert--sm{margin-bottom:.5rem!important}.margin-top--sm,.margin-vert--sm{margin-top:.5rem!important}.margin-horiz--sm,.margin-left--sm{margin-left:.5rem!important}.margin-horiz--sm,.margin-right--sm{margin-right:.5rem!important}.margin--sm{margin:.5rem!important}.margin-bottom--md,.margin-vert--md{margin-bottom:1rem!important}.margin-top--md,.margin-vert--md{margin-top:1rem!important}.margin-horiz--md,.margin-left--md{margin-left:1rem!important}.margin-horiz--md,.margin-right--md{margin-right:1rem!important}.margin--md{margin:1rem!important}.margin-bottom--lg,.margin-vert--lg{margin-bottom:2rem!important}.margin-top--lg,.margin-vert--lg{margin-top:2rem!important}.margin-horiz--lg,.margin-left--lg{margin-left:2rem!important}.margin-horiz--lg,.margin-right--lg{margin-right:2rem!important}.margin--lg{margin:2rem!important}.margin-bottom--xl,.margin-vert--xl{margin-bottom:5rem!important}.margin-top--xl,.margin-vert--xl{margin-top:5rem!important}.margin-horiz--xl,.margin-left--xl{margin-left:5rem!important}.margin-horiz--xl,.margin-right--xl{margin-right:5rem!important}.margin--xl{margin:5rem!important}.padding--none{padding:0!important}.padding-bottom--xs,.padding-vert--xs{padding-bottom:.25rem!important}.padding-top--xs,.padding-vert--xs{padding-top:.25rem!important}.padding-horiz--xs,.padding-left--xs{padding-left:.25rem!important}.padding-horiz--xs,.padding-right--xs{padding-right:.25rem!important}.padding--xs{padding:.25rem!important}.padding-bottom--sm,.padding-vert--sm{padding-bottom:.5rem!important}.padding-top--sm,.padding-vert--sm{padding-top:.5rem!important}.padding-horiz--sm,.padding-left--sm{padding-left:.5rem!important}.padding-horiz--sm,.padding-right--sm{padding-right:.5rem!important}.padding--sm{padding:.5rem!important}.padding-bottom--md,.padding-vert--md{padding-bottom:1rem!important}.padding-top--md,.padding-vert--md{padding-top:1rem!important}.padding-horiz--md,.padding-left--md{padding-left:1rem!important}.padding-horiz--md,.padding-right--md{padding-right:1rem!important}.padding--md{padding:1rem!important}.padding-bottom--lg,.padding-vert--lg{padding-bottom:2rem!important}.padding-top--lg,.padding-vert--lg{padding-top:2rem!important}.padding-horiz--lg,.padding-left--lg{padding-left:2rem!important}.padding-horiz--lg,.padding-right--lg{padding-right:2rem!important}.padding--lg{padding:2rem!important}.padding-bottom--xl,.padding-vert--xl{padding-bottom:5rem!important}.padding-top--xl,.padding-vert--xl{padding-top:5rem!important}.padding-horiz--xl,.padding-left--xl{padding-left:5rem!important}.padding-horiz--xl,.padding-right--xl{padding-right:5rem!important}.padding--xl{padding:5rem!important}code{background-color:var(--ifm-code-background);border:.1rem solid #0000001a;border-radius:var(--ifm-code-border-radius);font-family:var(--ifm-font-family-monospace);font-size:var(--ifm-code-font-size);padding:var(--ifm-code-padding-vertical) var(--ifm-code-padding-horizontal)}a code{color:inherit}pre{background-color:var(--ifm-pre-background);border-radius:var(--ifm-pre-border-radius);color:var(--ifm-pre-color);font:var(--ifm-code-font-size)/var(--ifm-pre-line-height) var(--ifm-font-family-monospace);padding:var(--ifm-pre-padding)}pre code{background-color:initial;border:none;font-size:100%;line-height:inherit;padding:0}kbd{background-color:var(--ifm-color-emphasis-0);border:1px solid var(--ifm-color-emphasis-400);border-radius:.2rem;box-shadow:inset 0 -1px 0 var(--ifm-color-emphasis-400);color:var(--ifm-color-emphasis-800);font:80% var(--ifm-font-family-monospace);padding:.15rem .3rem}h1,h2,h3,h4,h5,h6{color:var(--ifm-heading-color);font-family:var(--ifm-heading-font-family);font-weight:var(--ifm-heading-font-weight);line-height:var(--ifm-heading-line-height);margin:var(--ifm-heading-margin-top) 0 var(--ifm-heading-margin-bottom) 0}h1{font-size:var(--ifm-h1-font-size)}h2{font-size:var(--ifm-h2-font-size)}h3{font-size:var(--ifm-h3-font-size)}h4{font-size:var(--ifm-h4-font-size)}h5{font-size:var(--ifm-h5-font-size)}h6{font-size:var(--ifm-h6-font-size)}img{max-width:100%}img[align=right]{padding-left:var(--image-alignment-padding)}img[align=left]{padding-right:var(--image-alignment-padding)}.markdown{--ifm-h1-vertical-rhythm-top:3;--ifm-h2-vertical-rhythm-top:2;--ifm-h3-vertical-rhythm-top:1.5;--ifm-heading-vertical-rhythm-top:1.25;--ifm-h1-vertical-rhythm-bottom:1.25;--ifm-heading-vertical-rhythm-bottom:1}.markdown:after,.markdown:before{content:"";display:table}.markdown:after{clear:both}.markdown h1:first-child{--ifm-h1-font-size:3rem;margin-bottom:calc(var(--ifm-h1-vertical-rhythm-bottom)*var(--ifm-leading))}.markdown>h2{--ifm-h2-font-size:2rem;margin-top:calc(var(--ifm-h2-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h3{--ifm-h3-font-size:1.5rem;margin-top:calc(var(--ifm-h3-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h4,.markdown>h5,.markdown>h6{margin-top:calc(var(--ifm-heading-vertical-rhythm-top)*var(--ifm-leading))}.markdown>p,.markdown>pre,.markdown>ul{margin-bottom:var(--ifm-leading)}.markdown li>p{margin-top:var(--ifm-list-paragraph-margin)}.markdown li+li{margin-top:var(--ifm-list-item-margin)}ol,ul{margin:0 0 var(--ifm-list-margin);padding-left:var(--ifm-list-left-padding)}ol ol,ul ol{list-style-type:lower-roman}ol ol ol,ol ul ol,ul ol ol,ul ul ol{list-style-type:lower-alpha}table{border-collapse:collapse;display:block;margin-bottom:var(--ifm-spacing-vertical)}table thead tr{border-bottom:2px solid var(--ifm-table-border-color)}table thead,table tr:nth-child(2n){background-color:var(--ifm-table-stripe-background)}table tr{background-color:var(--ifm-table-background);border-top:var(--ifm-table-border-width) solid var(--ifm-table-border-color)}table td,table th{border:var(--ifm-table-border-width) solid var(--ifm-table-border-color);padding:var(--ifm-table-cell-padding)}table th{background-color:var(--ifm-table-head-background);color:var(--ifm-table-head-color);font-weight:var(--ifm-table-head-font-weight)}table td{color:var(--ifm-table-cell-color)}strong{font-weight:var(--ifm-font-weight-bold)}a{color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration)}a:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration)}.button:hover,.text--no-decoration,.text--no-decoration:hover,a:not([href]){text-decoration:none}p{margin:0 0 var(--ifm-paragraph-margin-bottom)}blockquote{border-left:var(--ifm-blockquote-border-left-width) solid var(--ifm-blockquote-border-color);box-shadow:var(--ifm-blockquote-shadow);color:var(--ifm-blockquote-color);font-size:var(--ifm-blockquote-font-size);padding:var(--ifm-blockquote-padding-vertical) var(--ifm-blockquote-padding-horizontal)}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{background-color:var(--ifm-hr-background-color);border:0;height:var(--ifm-hr-height);margin:var(--ifm-hr-margin-vertical) 0}.shadow--lw{box-shadow:var(--ifm-global-shadow-lw)!important}.shadow--md{box-shadow:var(--ifm-global-shadow-md)!important}.shadow--tl{box-shadow:var(--ifm-global-shadow-tl)!important}.text--primary,.wordWrapButtonEnabled_EoeP .wordWrapButtonIcon_Bwma{color:var(--ifm-color-primary)}.text--secondary{color:var(--ifm-color-secondary)}.text--success{color:var(--ifm-color-success)}.text--info{color:var(--ifm-color-info)}.text--warning{color:var(--ifm-color-warning)}.text--danger{color:var(--ifm-color-danger)}.text--center{text-align:center}.text--left{text-align:left}.text--justify{text-align:justify}.text--right{text-align:right}.text--capitalize{text-transform:capitalize}.text--lowercase{text-transform:lowercase}.admonitionHeading_tbUL,.alert__heading,.text--uppercase{text-transform:uppercase}.text--light{font-weight:var(--ifm-font-weight-light)}.text--normal{font-weight:var(--ifm-font-weight-normal)}.text--semibold{font-weight:var(--ifm-font-weight-semibold)}.text--bold{font-weight:var(--ifm-font-weight-bold)}.text--italic{font-style:italic}.text--truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text--break{word-wrap:break-word!important;word-break:break-word!important}.clean-btn{background:none;border:none;color:inherit;cursor:pointer;font-family:inherit;padding:0}.alert,.alert .close{color:var(--ifm-alert-foreground-color)}.clean-list{padding-left:0}.alert--primary{--ifm-alert-background-color:var(--ifm-color-primary-contrast-background);--ifm-alert-background-color-highlight:#3578e526;--ifm-alert-foreground-color:var(--ifm-color-primary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-primary-dark)}.alert--secondary{--ifm-alert-background-color:var(--ifm-color-secondary-contrast-background);--ifm-alert-background-color-highlight:#ebedf026;--ifm-alert-foreground-color:var(--ifm-color-secondary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-secondary-dark)}.alert--success{--ifm-alert-background-color:var(--ifm-color-success-contrast-background);--ifm-alert-background-color-highlight:#00a40026;--ifm-alert-foreground-color:var(--ifm-color-success-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-success-dark)}.alert--info{--ifm-alert-background-color:var(--ifm-color-info-contrast-background);--ifm-alert-background-color-highlight:#54c7ec26;--ifm-alert-foreground-color:var(--ifm-color-info-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-info-dark)}.alert--warning{--ifm-alert-background-color:var(--ifm-color-warning-contrast-background);--ifm-alert-background-color-highlight:#ffba0026;--ifm-alert-foreground-color:var(--ifm-color-warning-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-warning-dark)}.alert--danger{--ifm-alert-background-color:var(--ifm-color-danger-contrast-background);--ifm-alert-background-color-highlight:#fa383e26;--ifm-alert-foreground-color:var(--ifm-color-danger-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-danger-dark)}.alert{--ifm-code-background:var(--ifm-alert-background-color-highlight);--ifm-link-color:var(--ifm-alert-foreground-color);--ifm-link-hover-color:var(--ifm-alert-foreground-color);--ifm-link-decoration:underline;--ifm-tabs-color:var(--ifm-alert-foreground-color);--ifm-tabs-color-active:var(--ifm-alert-foreground-color);--ifm-tabs-color-active-border:var(--ifm-alert-border-color);background-color:var(--ifm-alert-background-color);border:var(--ifm-alert-border-width) solid var(--ifm-alert-border-color);border-left-width:var(--ifm-alert-border-left-width);border-radius:var(--ifm-alert-border-radius);box-shadow:var(--ifm-alert-shadow);padding:var(--ifm-alert-padding-vertical) var(--ifm-alert-padding-horizontal)}.alert__heading{align-items:center;display:flex;font:700 var(--ifm-h5-font-size)/var(--ifm-heading-line-height) var(--ifm-heading-font-family);margin-bottom:.5rem}.alert__icon{display:inline-flex;margin-right:.4em}.alert__icon svg{fill:var(--ifm-alert-foreground-color);stroke:var(--ifm-alert-foreground-color);stroke-width:0}.alert .close{margin:calc(var(--ifm-alert-padding-vertical)*-1) calc(var(--ifm-alert-padding-horizontal)*-1) 0 0;opacity:.75}.alert .close:focus,.alert .close:hover{opacity:1}.alert a{text-decoration-color:var(--ifm-alert-border-color)}.alert a:hover{text-decoration-thickness:2px}.avatar{column-gap:var(--ifm-avatar-intro-margin);display:flex}.avatar__photo{border-radius:50%;display:block;height:var(--ifm-avatar-photo-size);overflow:hidden;width:var(--ifm-avatar-photo-size)}.card--full-height,.navbar__logo img,body,html{height:100%}.avatar__photo--sm{--ifm-avatar-photo-size:2rem}.avatar__photo--lg{--ifm-avatar-photo-size:4rem}.avatar__photo--xl{--ifm-avatar-photo-size:6rem}.avatar__intro{display:flex;flex:1 1;flex-direction:column;justify-content:center;text-align:var(--ifm-avatar-intro-alignment)}.badge,.breadcrumbs__item,.breadcrumbs__link,.button,.dropdown>.navbar__link:after{display:inline-block}.avatar__name{font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base)}.avatar__subtitle{margin-top:.25rem}.avatar--vertical{--ifm-avatar-intro-alignment:center;--ifm-avatar-intro-margin:0.5rem;align-items:center;flex-direction:column}.badge{background-color:var(--ifm-badge-background-color);border:var(--ifm-badge-border-width) solid var(--ifm-badge-border-color);border-radius:var(--ifm-badge-border-radius);color:var(--ifm-badge-color);font-size:75%;font-weight:var(--ifm-font-weight-bold);line-height:1;padding:var(--ifm-badge-padding-vertical) var(--ifm-badge-padding-horizontal)}.badge--primary{--ifm-badge-background-color:var(--ifm-color-primary)}.badge--secondary{--ifm-badge-background-color:var(--ifm-color-secondary);color:var(--ifm-color-black)}.breadcrumbs__link,.button.button--secondary.button--outline:not(.button--active):not(:hover){color:var(--ifm-font-color-base)}.badge--success{--ifm-badge-background-color:var(--ifm-color-success)}.badge--info{--ifm-badge-background-color:var(--ifm-color-info)}.badge--warning{--ifm-badge-background-color:var(--ifm-color-warning)}.badge--danger{--ifm-badge-background-color:var(--ifm-color-danger)}.breadcrumbs{margin-bottom:0;padding-left:0}.breadcrumbs__item:not(:last-child):after{background:var(--ifm-breadcrumb-separator) center;content:" ";display:inline-block;filter:var(--ifm-breadcrumb-separator-filter);height:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier));margin:0 var(--ifm-breadcrumb-spacing);opacity:.5;width:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier))}.breadcrumbs__item--active .breadcrumbs__link{background:var(--ifm-breadcrumb-item-background-active);color:var(--ifm-breadcrumb-color-active)}.breadcrumbs__link{border-radius:var(--ifm-breadcrumb-border-radius);font-size:calc(1rem*var(--ifm-breadcrumb-size-multiplier));padding:calc(var(--ifm-breadcrumb-padding-vertical)*var(--ifm-breadcrumb-size-multiplier)) calc(var(--ifm-breadcrumb-padding-horizontal)*var(--ifm-breadcrumb-size-multiplier));transition-duration:var(--ifm-transition-fast);transition-property:background,color}.breadcrumbs__link:any-link:hover,.breadcrumbs__link:link:hover,.breadcrumbs__link:visited:hover,area[href].breadcrumbs__link:hover{background:var(--ifm-breadcrumb-item-background-active);text-decoration:none}.breadcrumbs--sm{--ifm-breadcrumb-size-multiplier:0.8}.breadcrumbs--lg{--ifm-breadcrumb-size-multiplier:1.2}.button{background-color:var(--ifm-button-background-color);border:var(--ifm-button-border-width) solid var(--ifm-button-border-color);border-radius:var(--ifm-button-border-radius);cursor:pointer;font-size:calc(.875rem*var(--ifm-button-size-multiplier));font-weight:var(--ifm-button-font-weight);line-height:1.5;padding:calc(var(--ifm-button-padding-vertical)*var(--ifm-button-size-multiplier)) calc(var(--ifm-button-padding-horizontal)*var(--ifm-button-size-multiplier));text-align:center;transition-duration:var(--ifm-button-transition-duration);transition-property:color,background,border-color;-webkit-user-select:none;user-select:none;white-space:nowrap}.button,.button:hover{color:var(--ifm-button-color)}.button--outline{--ifm-button-color:var(--ifm-button-border-color)}.button--outline:hover{--ifm-button-background-color:var(--ifm-button-border-color)}.button--link{--ifm-button-border-color:#0000;color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration)}.button--link.button--active,.button--link:active,.button--link:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration)}.button.disabled,.button:disabled,.button[disabled]{opacity:.65;pointer-events:none}.button--sm{--ifm-button-size-multiplier:0.8}.button--lg{--ifm-button-size-multiplier:1.35}.button--block{display:block;width:100%}.button.button--secondary{color:var(--ifm-color-gray-900)}:where(.button--primary){--ifm-button-background-color:var(--ifm-color-primary);--ifm-button-border-color:var(--ifm-color-primary)}:where(.button--primary):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-primary-dark);--ifm-button-border-color:var(--ifm-color-primary-dark)}.button--primary.button--active,.button--primary:active{--ifm-button-background-color:var(--ifm-color-primary-darker);--ifm-button-border-color:var(--ifm-color-primary-darker)}:where(.button--secondary){--ifm-button-background-color:var(--ifm-color-secondary);--ifm-button-border-color:var(--ifm-color-secondary)}:where(.button--secondary):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-secondary-dark);--ifm-button-border-color:var(--ifm-color-secondary-dark)}.button--secondary.button--active,.button--secondary:active{--ifm-button-background-color:var(--ifm-color-secondary-darker);--ifm-button-border-color:var(--ifm-color-secondary-darker)}:where(.button--success){--ifm-button-background-color:var(--ifm-color-success);--ifm-button-border-color:var(--ifm-color-success)}:where(.button--success):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-success-dark);--ifm-button-border-color:var(--ifm-color-success-dark)}.button--success.button--active,.button--success:active{--ifm-button-background-color:var(--ifm-color-success-darker);--ifm-button-border-color:var(--ifm-color-success-darker)}:where(.button--info){--ifm-button-background-color:var(--ifm-color-info);--ifm-button-border-color:var(--ifm-color-info)}:where(.button--info):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-info-dark);--ifm-button-border-color:var(--ifm-color-info-dark)}.button--info.button--active,.button--info:active{--ifm-button-background-color:var(--ifm-color-info-darker);--ifm-button-border-color:var(--ifm-color-info-darker)}:where(.button--warning){--ifm-button-background-color:var(--ifm-color-warning);--ifm-button-border-color:var(--ifm-color-warning)}:where(.button--warning):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-warning-dark);--ifm-button-border-color:var(--ifm-color-warning-dark)}.button--warning.button--active,.button--warning:active{--ifm-button-background-color:var(--ifm-color-warning-darker);--ifm-button-border-color:var(--ifm-color-warning-darker)}:where(.button--danger){--ifm-button-background-color:var(--ifm-color-danger);--ifm-button-border-color:var(--ifm-color-danger)}:where(.button--danger):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-danger-dark);--ifm-button-border-color:var(--ifm-color-danger-dark)}.button--danger.button--active,.button--danger:active{--ifm-button-background-color:var(--ifm-color-danger-darker);--ifm-button-border-color:var(--ifm-color-danger-darker)}.button-group{display:inline-flex;gap:var(--ifm-button-group-spacing)}.button-group>.button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.button-group>.button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.button-group--block{display:flex;justify-content:stretch}.button-group--block>.button{flex-grow:1}.card{background-color:var(--ifm-card-background-color);border-radius:var(--ifm-card-border-radius);box-shadow:var(--ifm-global-shadow-lw);display:flex;flex-direction:column;overflow:hidden}.card__image{padding-top:var(--ifm-card-vertical-spacing)}.card__image:first-child{padding-top:0}.card__body,.card__footer,.card__header{padding:var(--ifm-card-vertical-spacing) var(--ifm-card-horizontal-spacing)}.card__body:not(:last-child),.card__footer:not(:last-child),.card__header:not(:last-child){padding-bottom:0}.card__body>:last-child,.card__footer>:last-child,.card__header>:last-child{margin-bottom:0}.card__footer{margin-top:auto}.table-of-contents{font-size:.8rem;margin-bottom:0;padding:var(--ifm-toc-padding-vertical) 0}.table-of-contents,.table-of-contents ul{list-style:none;padding-left:var(--ifm-toc-padding-horizontal)}.table-of-contents li{margin:var(--ifm-toc-padding-vertical) var(--ifm-toc-padding-horizontal)}.table-of-contents__left-border{border-left:1px solid var(--ifm-toc-border-color)}.table-of-contents__link{color:var(--ifm-toc-link-color);display:block}.table-of-contents__link--active,.table-of-contents__link--active code,.table-of-contents__link:hover,.table-of-contents__link:hover code{color:var(--ifm-color-primary);text-decoration:none}.close{color:var(--ifm-color-black);float:right;font-size:1.5rem;font-weight:var(--ifm-font-weight-bold);line-height:1;opacity:.5;padding:1rem;transition:opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.close:hover{opacity:.7}.close:focus,.theme-code-block-highlighted-line .codeLineNumber_Tfdd:before{opacity:.8}.dropdown{display:inline-flex;font-weight:var(--ifm-dropdown-font-weight);position:relative;vertical-align:top}.dropdown--hoverable:hover .dropdown__menu,.dropdown--show .dropdown__menu{opacity:1;pointer-events:all;transform:translateY(-1px);visibility:visible}#nprogress,.dropdown__menu,.navbar__item.dropdown .navbar__link:not([href]){pointer-events:none}.dropdown--right .dropdown__menu{left:inherit;right:0}.dropdown--nocaret .navbar__link:after{content:none!important}.dropdown__menu{background-color:var(--ifm-dropdown-background-color);border-radius:var(--ifm-global-radius);box-shadow:var(--ifm-global-shadow-md);left:0;max-height:80vh;min-width:10rem;opacity:0;overflow-y:auto;padding:.5rem;position:absolute;top:calc(100% - var(--ifm-navbar-item-padding-vertical) + .3rem);transform:translateY(-.625rem);transition-duration:var(--ifm-transition-fast);transition-property:opacity,transform,visibility;transition-timing-function:var(--ifm-transition-timing-default);visibility:hidden;z-index:var(--ifm-z-index-dropdown)}.menu__caret,.menu__link,.menu__list-item-collapsible{border-radius:.25rem;transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.dropdown__link{border-radius:.25rem;color:var(--ifm-dropdown-link-color);display:block;font-size:.875rem;margin-top:.2rem;padding:.25rem .5rem;white-space:nowrap}.dropdown__link--active,.dropdown__link:hover{background-color:var(--ifm-dropdown-hover-background-color);color:var(--ifm-dropdown-link-color);text-decoration:none}.dropdown__link--active,.dropdown__link--active:hover{--ifm-dropdown-link-color:var(--ifm-link-color)}.dropdown>.navbar__link:after{border-color:currentcolor #0000;border-style:solid;border-width:.4em .4em 0;content:"";margin-left:.3em;position:relative;top:2px;transform:translateY(-50%)}.footer{background-color:var(--ifm-footer-background-color);color:var(--ifm-footer-color);padding:var(--ifm-footer-padding-vertical) var(--ifm-footer-padding-horizontal)}.footer--dark{--ifm-footer-background-color:#303846;--ifm-footer-color:var(--ifm-footer-link-color);--ifm-footer-link-color:var(--ifm-color-secondary);--ifm-footer-title-color:var(--ifm-color-white)}.footer__links{margin-bottom:1rem}.footer__link-item{color:var(--ifm-footer-link-color);line-height:2}.footer__link-item:hover{color:var(--ifm-footer-link-hover-color)}.footer__link-separator{margin:0 var(--ifm-footer-link-horizontal-spacing)}.footer__logo{margin-top:1rem;max-width:var(--ifm-footer-logo-max-width)}.footer__title{color:var(--ifm-footer-title-color);font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base);margin-bottom:var(--ifm-heading-margin-bottom)}.menu,.navbar__link{font-weight:var(--ifm-font-weight-semibold)}.docItemContainer_Djhp article>:first-child,.docItemContainer_Djhp header+*,.footer__item{margin-top:0}.admonitionContent_S0QG>:last-child,.collapsibleContent_i85q>:last-child,.footer__items{margin-bottom:0}.codeBlockStandalone_MEMb,[type=checkbox]{padding:0}.hero{align-items:center;background-color:var(--ifm-hero-background-color);color:var(--ifm-hero-text-color);display:flex;padding:4rem 2rem}.hero--primary{--ifm-hero-background-color:var(--ifm-color-primary);--ifm-hero-text-color:var(--ifm-font-color-base-inverse)}.hero--dark{--ifm-hero-background-color:#303846;--ifm-hero-text-color:var(--ifm-color-white)}.hero__title{font-size:3rem}.hero__subtitle{font-size:1.5rem}.menu__list{margin:0;padding-left:0}.menu__caret,.menu__link{padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal)}.menu__list .menu__list{flex:0 0 100%;margin-top:.25rem;padding-left:var(--ifm-menu-link-padding-horizontal)}.menu__list-item:not(:first-child){margin-top:.25rem}.menu__list-item--collapsed .menu__list{height:0;overflow:hidden}.details_lb9f[data-collapsed=false].isBrowser_bmU9>summary:before,.details_lb9f[open]:not(.isBrowser_bmU9)>summary:before,.menu__list-item--collapsed .menu__caret:before,.menu__list-item--collapsed .menu__link--sublist:after{transform:rotate(90deg)}.menu__list-item-collapsible{display:flex;flex-wrap:wrap;position:relative}.menu__caret:hover,.menu__link:hover,.menu__list-item-collapsible--active,.menu__list-item-collapsible:hover{background:var(--ifm-menu-color-background-hover)}.menu__list-item-collapsible .menu__link--active,.menu__list-item-collapsible .menu__link:hover{background:none!important}.menu__caret,.menu__link{align-items:center;display:flex}.menu__link{color:var(--ifm-menu-color);flex:1;line-height:1.25}.menu__link:hover{color:var(--ifm-menu-color);text-decoration:none}.menu__caret:before,.menu__link--sublist-caret:after{height:1.25rem;transform:rotate(180deg);transition:transform var(--ifm-transition-fast) linear;width:1.25rem;filter:var(--ifm-menu-link-sublist-icon-filter);content:""}.menu__link--sublist-caret:after{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem;margin-left:auto;min-width:1.25rem}.menu__link--active,.menu__link--active:hover{color:var(--ifm-menu-color-active)}.navbar__brand,.navbar__link{color:var(--ifm-navbar-link-color)}.menu__link--active:not(.menu__link--sublist){background-color:var(--ifm-menu-color-background-active)}.menu__caret:before{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem}.navbar--dark,html[data-theme=dark]{--ifm-menu-link-sublist-icon-filter:invert(100%) sepia(94%) saturate(17%) hue-rotate(223deg) brightness(104%) contrast(98%)}.navbar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-navbar-shadow);height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal)}.navbar,.navbar>.container,.navbar>.container-fluid{display:flex}.navbar--fixed-top{position:sticky;top:0;z-index:var(--ifm-z-index-fixed)}.navbar-sidebar,.navbar-sidebar__backdrop{bottom:0;opacity:0;position:fixed;transition-duration:var(--ifm-transition-fast);transition-timing-function:ease-in-out;left:0;top:0;visibility:hidden}.navbar__inner{display:flex;flex-wrap:wrap;justify-content:space-between;width:100%}.navbar__brand{align-items:center;display:flex;margin-right:1rem;min-width:0}.navbar__brand:hover{color:var(--ifm-navbar-link-hover-color);text-decoration:none}.announcementBarContent_xLdY,.navbar__title{flex:1 1 auto}.navbar__toggle{display:none;margin-right:.5rem}.navbar__logo{flex:0 0 auto;height:2rem;margin-right:.5rem}.navbar__items{align-items:center;display:flex;flex:1;min-width:0}.navbar__items--center{flex:0 0 auto}.navbar__items--center .navbar__brand{margin:0}.navbar__items--center+.navbar__items--right{flex:1}.navbar__items--right{flex:0 0 auto;justify-content:flex-end}.navbar__items--right>:last-child{padding-right:0}.navbar__item{display:inline-block;padding:var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal)}.navbar__link--active,.navbar__link:hover{color:var(--ifm-navbar-link-hover-color);text-decoration:none}.navbar--dark,.navbar--primary{--ifm-menu-color:var(--ifm-color-gray-300);--ifm-navbar-link-color:var(--ifm-color-gray-100);--ifm-navbar-search-input-background-color:#ffffff1a;--ifm-navbar-search-input-placeholder-color:#ffffff80;color:var(--ifm-color-white)}.navbar--dark{--ifm-navbar-background-color:#242526;--ifm-menu-color-background-active:#ffffff0d;--ifm-navbar-search-input-color:var(--ifm-color-white)}.navbar--primary{--ifm-navbar-background-color:var(--ifm-color-primary);--ifm-navbar-link-hover-color:var(--ifm-color-white);--ifm-menu-color-active:var(--ifm-color-white);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-500)}.navbar__search-input{appearance:none;background:var(--ifm-navbar-search-input-background-color) var(--ifm-navbar-search-input-icon) no-repeat .75rem center/1rem 1rem;border:none;border-radius:2rem;color:var(--ifm-navbar-search-input-color);cursor:text;display:inline-block;font-size:.9rem;height:2rem;padding:0 .5rem 0 2.25rem;width:12.5rem}.navbar__search-input::placeholder{color:var(--ifm-navbar-search-input-placeholder-color)}.navbar-sidebar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-global-shadow-md);transform:translate3d(-100%,0,0);transition-property:opacity,visibility,transform;width:var(--ifm-navbar-sidebar-width)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar__items{transform:translateZ(0)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar--show .navbar-sidebar__backdrop{opacity:1;visibility:visible}.navbar-sidebar__backdrop{background-color:#0009;right:0;transition-property:opacity,visibility}.navbar-sidebar__brand{align-items:center;box-shadow:var(--ifm-navbar-shadow);display:flex;flex:1;height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal)}.navbar-sidebar__items{display:flex;height:calc(100% - var(--ifm-navbar-height));transition:transform var(--ifm-transition-fast) ease-in-out}.navbar-sidebar__items--show-secondary{transform:translate3d(calc((var(--ifm-navbar-sidebar-width))*-1),0,0)}.navbar-sidebar__item{flex-shrink:0;padding:.5rem;width:calc(var(--ifm-navbar-sidebar-width))}.navbar-sidebar__back{background:var(--ifm-menu-color-background-active);font-size:15px;font-weight:var(--ifm-button-font-weight);margin:0 0 .2rem -.5rem;padding:.6rem 1.5rem;position:relative;text-align:left;top:-.5rem;width:calc(100% + 1rem)}.navbar-sidebar__close{display:flex;margin-left:auto}.pagination{column-gap:var(--ifm-pagination-page-spacing);display:flex;font-size:var(--ifm-pagination-font-size);padding-left:0}.pagination--sm{--ifm-pagination-font-size:0.8rem;--ifm-pagination-padding-horizontal:0.8rem;--ifm-pagination-padding-vertical:0.2rem}.pagination--lg{--ifm-pagination-font-size:1.2rem;--ifm-pagination-padding-horizontal:1.2rem;--ifm-pagination-padding-vertical:0.3rem}.pagination__item{display:inline-flex}.pagination__item>span{padding:var(--ifm-pagination-padding-vertical)}.pagination__item--active .pagination__link{color:var(--ifm-pagination-color-active)}.pagination__item--active .pagination__link,.pagination__item:not(.pagination__item--active):hover .pagination__link{background:var(--ifm-pagination-item-active-background)}.pagination__item--disabled,.pagination__item[disabled]{opacity:.25;pointer-events:none}.pagination__link{border-radius:var(--ifm-pagination-border-radius);color:var(--ifm-font-color-base);display:inline-block;padding:var(--ifm-pagination-padding-vertical) var(--ifm-pagination-padding-horizontal);transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pagination__link:hover{text-decoration:none}.pagination-nav{grid-gap:var(--ifm-spacing-horizontal);display:grid;gap:var(--ifm-spacing-horizontal);grid-template-columns:repeat(2,1fr)}.pagination-nav__link{border:1px solid var(--ifm-color-emphasis-300);border-radius:var(--ifm-pagination-nav-border-radius);display:block;height:100%;line-height:var(--ifm-heading-line-height);padding:var(--ifm-global-spacing);transition:border-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pagination-nav__link:hover{border-color:var(--ifm-pagination-nav-color-hover);text-decoration:none}.pagination-nav__link--next{grid-column:2/3;text-align:right}.pagination-nav__label{font-size:var(--ifm-h4-font-size);font-weight:var(--ifm-heading-font-weight);word-break:break-word}.pagination-nav__link--prev .pagination-nav__label:before{content:"« "}.pagination-nav__link--next .pagination-nav__label:after{content:" »"}.pagination-nav__sublabel{color:var(--ifm-color-content-secondary);font-size:var(--ifm-h5-font-size);font-weight:var(--ifm-font-weight-semibold);margin-bottom:.25rem}.pills__item,.tabs{font-weight:var(--ifm-font-weight-bold)}.pills{display:flex;gap:var(--ifm-pills-spacing);padding-left:0}.pills__item{border-radius:.5rem;cursor:pointer;display:inline-block;padding:.25rem 1rem;transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.tabs,:not(.containsTaskList_mC6p>li)>.containsTaskList_mC6p{padding-left:0}.pills__item--active{color:var(--ifm-pills-color-active)}.pills__item--active,.pills__item:not(.pills__item--active):hover{background:var(--ifm-pills-color-background-active)}.pills--block{justify-content:stretch}.pills--block .pills__item{flex-grow:1;text-align:center}.tabs{color:var(--ifm-tabs-color);display:flex;margin-bottom:0;overflow-x:auto}.tabs__item{border-bottom:3px solid #0000;border-radius:var(--ifm-global-radius);cursor:pointer;display:inline-flex;padding:var(--ifm-tabs-padding-vertical) var(--ifm-tabs-padding-horizontal);transition:background-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.tabs__item--active{border-bottom-color:var(--ifm-tabs-color-active-border);border-bottom-left-radius:0;border-bottom-right-radius:0;color:var(--ifm-tabs-color-active)}.tabs__item:hover{background-color:var(--ifm-hover-overlay)}.tabs--block{justify-content:stretch}.tabs--block .tabs__item{flex-grow:1;justify-content:center}html[data-theme=dark]{--ifm-color-scheme:dark;--ifm-color-emphasis-0:var(--ifm-color-gray-1000);--ifm-color-emphasis-100:var(--ifm-color-gray-900);--ifm-color-emphasis-200:var(--ifm-color-gray-800);--ifm-color-emphasis-300:var(--ifm-color-gray-700);--ifm-color-emphasis-400:var(--ifm-color-gray-600);--ifm-color-emphasis-600:var(--ifm-color-gray-400);--ifm-color-emphasis-700:var(--ifm-color-gray-300);--ifm-color-emphasis-800:var(--ifm-color-gray-200);--ifm-color-emphasis-900:var(--ifm-color-gray-100);--ifm-color-emphasis-1000:var(--ifm-color-gray-0);--ifm-background-color:#1b1b1d;--ifm-background-surface-color:#242526;--ifm-hover-overlay:#ffffff0d;--ifm-color-content:#e3e3e3;--ifm-color-content-secondary:#fff;--ifm-breadcrumb-separator-filter:invert(64%) sepia(11%) saturate(0%) hue-rotate(149deg) brightness(99%) contrast(95%);--ifm-code-background:#ffffff1a;--ifm-scrollbar-track-background-color:#444;--ifm-scrollbar-thumb-background-color:#686868;--ifm-scrollbar-thumb-hover-background-color:#7a7a7a;--ifm-table-stripe-background:#ffffff12;--ifm-toc-border-color:var(--ifm-color-emphasis-200);--ifm-color-primary-contrast-background:#102445;--ifm-color-primary-contrast-foreground:#ebf2fc;--ifm-color-secondary-contrast-background:#474748;--ifm-color-secondary-contrast-foreground:#fdfdfe;--ifm-color-success-contrast-background:#003100;--ifm-color-success-contrast-foreground:#e6f6e6;--ifm-color-info-contrast-background:#193c47;--ifm-color-info-contrast-foreground:#eef9fd;--ifm-color-warning-contrast-background:#4d3800;--ifm-color-warning-contrast-foreground:#fff8e6;--ifm-color-danger-contrast-background:#4b1113;--ifm-color-danger-contrast-foreground:#ffebec}#nprogress .bar{background:var(--docusaurus-progress-bar-color);height:2px;left:0;position:fixed;top:0;width:100%;z-index:1031}#nprogress .peg{box-shadow:0 0 10px var(--docusaurus-progress-bar-color),0 0 5px var(--docusaurus-progress-bar-color);height:100%;opacity:1;position:absolute;right:0;transform:rotate(3deg) translateY(-4px);width:100px}[data-theme=dark]{--ifm-color-primary:#25c2a0;--ifm-color-primary-dark:#21af90;--ifm-color-primary-darker:#1fa588;--ifm-color-primary-darkest:#1a8870;--ifm-color-primary-light:#29d5b0;--ifm-color-primary-lighter:#32d8b4;--ifm-color-primary-lightest:#4fddbf;--docusaurus-highlighted-code-line-bg:#0000004d}.header-github-link:hover{opacity:.6}.header-github-link:before{background:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat;content:"";display:flex;height:24px;width:24px}html[data-theme=dark] .header-github-link:before{background:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23fff' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat}body:not(.navigation-with-keyboard) :not(input):focus{outline:0}#__docusaurus-base-url-issue-banner-container,.themedImage_ToTc,[data-theme=dark] .lightToggleIcon_pyhR,[data-theme=light] .darkToggleIcon_wfgR,html[data-announcement-bar-initially-dismissed=true] .announcementBar_mb4j{display:none}.skipToContent_fXgn{background-color:var(--ifm-background-surface-color);color:var(--ifm-color-emphasis-900);left:100%;padding:calc(var(--ifm-global-spacing)/2) var(--ifm-global-spacing);position:fixed;top:1rem;z-index:calc(var(--ifm-z-index-fixed) + 1)}.skipToContent_fXgn:focus{box-shadow:var(--ifm-global-shadow-md);left:1rem}.closeButton_CVFx{line-height:0;padding:0}.content_knG7{font-size:85%;padding:5px 0;text-align:center}.content_knG7 a{color:inherit;text-decoration:underline}.announcementBar_mb4j{align-items:center;background-color:var(--ifm-color-white);border-bottom:1px solid var(--ifm-color-emphasis-100);color:var(--ifm-color-black);display:flex;height:var(--docusaurus-announcement-bar-height)}.announcementBarPlaceholder_vyr4{flex:0 0 10px}.announcementBarClose_gvF7{align-self:stretch;flex:0 0 30px}.toggle_vylO{height:2rem;width:2rem}.toggleButton_gllP{align-items:center;border-radius:50%;display:flex;height:100%;justify-content:center;transition:background var(--ifm-transition-fast);width:100%}.toggleButton_gllP:hover{background:var(--ifm-color-emphasis-200)}.toggleButtonDisabled_aARS{cursor:not-allowed}.darkNavbarColorModeToggle_X3D1:hover{background:var(--ifm-color-gray-800)}[data-theme=dark] .themedImage--dark_i4oU,[data-theme=light] .themedImage--light_HNdA,html:not([data-theme]) .themedComponent--light_NU7w{display:initial}.iconExternalLink_nPIU{margin-left:.3rem}.iconLanguage_nlXk{margin-right:5px;vertical-align:text-bottom}.navbarHideable_m1mJ{transition:transform var(--ifm-transition-fast) ease}.navbarHidden_jGov{transform:translate3d(0,calc(-100% - 2px),0)}.errorBoundaryError_a6uf{color:red;white-space:pre-wrap}.footerLogoLink_BH7S{opacity:.5;transition:opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.footerLogoLink_BH7S:hover,.hash-link:focus,:hover>.hash-link{opacity:1}.mainWrapper_z2l0{display:flex;flex:1 0 auto;flex-direction:column}.docusaurus-mt-lg{margin-top:3rem}#__docusaurus{display:flex;flex-direction:column;min-height:100%}.iconEdit_Z9Sw{margin-right:.3em;vertical-align:sub}.tag_zVej{border:1px solid var(--docusaurus-tag-list-border);transition:border var(--ifm-transition-fast)}.tag_zVej:hover{--docusaurus-tag-list-border:var(--ifm-link-color);text-decoration:none}.tagRegular_sFm0{border-radius:var(--ifm-global-radius);font-size:90%;padding:.2rem .5rem .3rem}.tagWithCount_h2kH{align-items:center;border-left:0;display:flex;padding:0 .5rem 0 1rem;position:relative}.tagWithCount_h2kH:after,.tagWithCount_h2kH:before{border:1px solid var(--docusaurus-tag-list-border);content:"";position:absolute;top:50%;transition:inherit}.tagWithCount_h2kH:before{border-bottom:0;border-right:0;height:1.18rem;right:100%;transform:translate(50%,-50%) rotate(-45deg);width:1.18rem}.tagWithCount_h2kH:after{border-radius:50%;height:.5rem;left:0;transform:translateY(-50%);width:.5rem}.tagWithCount_h2kH span{background:var(--ifm-color-secondary);border-radius:var(--ifm-global-radius);color:var(--ifm-color-black);font-size:.7rem;line-height:1.2;margin-left:.3rem;padding:.1rem .4rem}.tags_jXut{display:inline}.tag_QGVx{display:inline-block;margin:0 .4rem .5rem 0}.lastUpdated_vwxv{font-size:smaller;font-style:italic;margin-top:.2rem}.tocCollapsibleButton_TO0P{align-items:center;display:flex;font-size:inherit;justify-content:space-between;padding:.4rem .8rem;width:100%}.tocCollapsibleButton_TO0P:after{background:var(--ifm-menu-link-sublist-icon) 50% 50%/2rem 2rem no-repeat;content:"";filter:var(--ifm-menu-link-sublist-icon-filter);height:1.25rem;transform:rotate(180deg);transition:transform var(--ifm-transition-fast);width:1.25rem}.tocCollapsibleButtonExpanded_MG3E:after,.tocCollapsibleExpanded_sAul{transform:none}.tocCollapsible_ETCw{background-color:var(--ifm-menu-color-background-active);border-radius:var(--ifm-global-radius);margin:1rem 0}.buttonGroup__atx button,.codeBlockContainer_Ckt0{background:var(--prism-background-color);color:var(--prism-color)}.tocCollapsibleContent_vkbj>ul{border-left:none;border-top:1px solid var(--ifm-color-emphasis-300);font-size:15px;padding:.2rem 0}.tocCollapsibleContent_vkbj ul li{margin:.4rem .8rem}.tocCollapsibleContent_vkbj a{display:block}.tableOfContents_bqdL{max-height:calc(100vh - var(--ifm-navbar-height) - 2rem);overflow-y:auto;position:sticky;top:calc(var(--ifm-navbar-height) + 1rem)}.anchorWithStickyNavbar_LWe7{scroll-margin-top:calc(var(--ifm-navbar-height) + .5rem)}.anchorWithHideOnScrollNavbar_WYt5{scroll-margin-top:.5rem}.hash-link{opacity:0;padding-left:.5rem;transition:opacity var(--ifm-transition-fast);-webkit-user-select:none;user-select:none}.hash-link:before{content:"#"}.codeBlockContainer_Ckt0{border-radius:var(--ifm-code-border-radius);box-shadow:var(--ifm-global-shadow-lw);margin-bottom:var(--ifm-leading)}.codeBlockContent_biex{border-radius:inherit;direction:ltr;position:relative}.codeBlockTitle_Ktv7{border-bottom:1px solid var(--ifm-color-emphasis-300);border-top-left-radius:inherit;border-top-right-radius:inherit;font-size:var(--ifm-code-font-size);font-weight:500;padding:.75rem var(--ifm-pre-padding)}.codeBlock_bY9V{--ifm-pre-background:var(--prism-background-color);margin:0;padding:0}.codeBlockTitle_Ktv7+.codeBlockContent_biex .codeBlock_bY9V{border-top-left-radius:0;border-top-right-radius:0}.codeBlockLines_e6Vv{float:left;font:inherit;min-width:100%;padding:var(--ifm-pre-padding)}.codeBlockLinesWithNumbering_o6Pm{display:table;padding:var(--ifm-pre-padding) 0}.buttonGroup__atx{column-gap:.2rem;display:flex;position:absolute;right:calc(var(--ifm-pre-padding)/2);top:calc(var(--ifm-pre-padding)/2)}.buttonGroup__atx button{align-items:center;border:1px solid var(--ifm-color-emphasis-300);border-radius:var(--ifm-global-radius);display:flex;line-height:0;opacity:0;padding:.4rem;transition:opacity var(--ifm-transition-fast) ease-in-out}.buttonGroup__atx button:focus-visible,.buttonGroup__atx button:hover{opacity:1!important}.theme-code-block:hover .buttonGroup__atx button{opacity:.4}:where(:root){--docusaurus-highlighted-code-line-bg:#484d5b}:where([data-theme=dark]){--docusaurus-highlighted-code-line-bg:#646464}.theme-code-block-highlighted-line{background-color:var(--docusaurus-highlighted-code-line-bg);display:block;margin:0 calc(var(--ifm-pre-padding)*-1);padding:0 var(--ifm-pre-padding)}.codeLine_lJS_{counter-increment:a;display:table-row}.codeLineNumber_Tfdd{background:var(--ifm-pre-background);display:table-cell;left:0;overflow-wrap:normal;padding:0 var(--ifm-pre-padding);position:sticky;text-align:right;width:1%}.codeLineNumber_Tfdd:before{content:counter(a);opacity:.4}.codeLineContent_feaV{padding-right:var(--ifm-pre-padding)}.theme-code-block:hover .copyButtonCopied_obH4{opacity:1!important}.copyButtonIcons_eSgA{height:1.125rem;position:relative;width:1.125rem}.copyButtonIcon_y97N,.copyButtonSuccessIcon_LjdS{fill:currentColor;height:inherit;left:0;opacity:inherit;position:absolute;top:0;transition:all var(--ifm-transition-fast) ease;width:inherit}.copyButtonSuccessIcon_LjdS{color:#00d600;left:50%;opacity:0;top:50%;transform:translate(-50%,-50%) scale(.33)}.copyButtonCopied_obH4 .copyButtonIcon_y97N{opacity:0;transform:scale(.33)}.copyButtonCopied_obH4 .copyButtonSuccessIcon_LjdS{opacity:1;transform:translate(-50%,-50%) scale(1);transition-delay:75ms}.wordWrapButtonIcon_Bwma{height:1.2rem;width:1.2rem}.details_lb9f{--docusaurus-details-summary-arrow-size:0.38rem;--docusaurus-details-transition:transform 200ms ease;--docusaurus-details-decoration-color:grey}.details_lb9f>summary{cursor:pointer;padding-left:1rem;position:relative}.details_lb9f>summary::-webkit-details-marker{display:none}.details_lb9f>summary:before{border-color:#0000 #0000 #0000 var(--docusaurus-details-decoration-color);border-style:solid;border-width:var(--docusaurus-details-summary-arrow-size);content:"";left:0;position:absolute;top:.45rem;transform:rotate(0);transform-origin:calc(var(--docusaurus-details-summary-arrow-size)/2) 50%;transition:var(--docusaurus-details-transition)}.collapsibleContent_i85q{border-top:1px solid var(--docusaurus-details-decoration-color);margin-top:1rem;padding-top:1rem}.details_b_Ee{--docusaurus-details-decoration-color:var(--ifm-alert-border-color);--docusaurus-details-transition:transform var(--ifm-transition-fast) ease;border:1px solid var(--ifm-alert-border-color);margin:0 0 var(--ifm-spacing-vertical)}.img_ev3q{height:auto}.admonition_LlT9{margin-bottom:1em}.admonitionHeading_tbUL{font:var(--ifm-heading-font-weight) var(--ifm-h5-font-size)/var(--ifm-heading-line-height) var(--ifm-heading-font-family);margin-bottom:.3rem}.admonitionHeading_tbUL code{text-transform:none}.admonitionIcon_kALy{display:inline-block;margin-right:.4em;vertical-align:middle}.admonitionIcon_kALy svg{fill:var(--ifm-alert-foreground-color);display:inline-block;height:1.6em;width:1.6em}.breadcrumbHomeIcon_YNFT{height:1.1rem;position:relative;top:1px;vertical-align:top;width:1.1rem}.breadcrumbsContainer_Z_bl{--ifm-breadcrumb-size-multiplier:0.8;margin-bottom:.8rem}.backToTopButton_sjWU{background-color:var(--ifm-color-emphasis-200);border-radius:50%;bottom:1.3rem;box-shadow:var(--ifm-global-shadow-lw);height:3rem;opacity:0;position:fixed;right:1.3rem;transform:scale(0);transition:all var(--ifm-transition-fast) var(--ifm-transition-timing-default);visibility:hidden;width:3rem;z-index:calc(var(--ifm-z-index-fixed) - 1)}.backToTopButton_sjWU:after{background-color:var(--ifm-color-emphasis-1000);content:" ";display:inline-block;height:100%;-webkit-mask:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem no-repeat;mask:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem no-repeat;width:100%}.backToTopButtonShow_xfvO{opacity:1;transform:scale(1);visibility:visible}[data-theme=dark]:root{--docusaurus-collapse-button-bg:#ffffff0d;--docusaurus-collapse-button-bg-hover:#ffffff1a}.collapseSidebarButton_PEFL{display:none;margin:0}.docSidebarContainer_b6E3,.sidebarLogo_isFc{display:none}.docMainContainer_gTbr,.docPage__5DB{display:flex;width:100%}.docPage__5DB{flex:1 0}.docsWrapper_BCFX{display:flex;flex:1 0 auto}@media (min-width:997px){.collapseSidebarButton_PEFL,.expandButton_m80_{background-color:var(--docusaurus-collapse-button-bg)}:root{--docusaurus-announcement-bar-height:30px}.announcementBarClose_gvF7,.announcementBarPlaceholder_vyr4{flex-basis:50px}.searchBox_ZlJk{padding:var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal)}.lastUpdated_vwxv{text-align:right}.tocMobile_ITEo{display:none}.docItemCol_VOVn{max-width:75%!important}.collapseSidebarButton_PEFL{border:1px solid var(--ifm-toc-border-color);border-radius:0;bottom:0;display:block!important;height:40px;position:sticky}.collapseSidebarButtonIcon_kv0_{margin-top:4px;transform:rotate(180deg)}.expandButtonIcon_BlDH,[dir=rtl] .collapseSidebarButtonIcon_kv0_{transform:rotate(0)}.collapseSidebarButton_PEFL:focus,.collapseSidebarButton_PEFL:hover,.expandButton_m80_:focus,.expandButton_m80_:hover{background-color:var(--docusaurus-collapse-button-bg-hover)}.menuHtmlItem_M9Kj{padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal)}.menu_SIkG{flex-grow:1;padding:.5rem}@supports (scrollbar-gutter:stable){.menu_SIkG{padding:.5rem 0 .5rem .5rem;scrollbar-gutter:stable}}.menuWithAnnouncementBar_GW3s{margin-bottom:var(--docusaurus-announcement-bar-height)}.sidebar_njMd{display:flex;flex-direction:column;height:100%;padding-top:var(--ifm-navbar-height);width:var(--doc-sidebar-width)}.sidebarWithHideableNavbar_wUlq{padding-top:0}.sidebarHidden_VK0M{opacity:0;visibility:hidden}.sidebarLogo_isFc{align-items:center;color:inherit!important;display:flex!important;margin:0 var(--ifm-navbar-padding-horizontal);max-height:var(--ifm-navbar-height);min-height:var(--ifm-navbar-height);text-decoration:none!important}.sidebarLogo_isFc img{height:2rem;margin-right:.5rem}.expandButton_m80_{align-items:center;display:flex;height:100%;justify-content:center;position:absolute;right:0;top:0;transition:background-color var(--ifm-transition-fast) ease;width:100%}[dir=rtl] .expandButtonIcon_BlDH{transform:rotate(180deg)}.docSidebarContainer_b6E3{border-right:1px solid var(--ifm-toc-border-color);clip-path:inset(0);display:block;margin-top:calc(var(--ifm-navbar-height)*-1);transition:width var(--ifm-transition-fast) ease;width:var(--doc-sidebar-width);will-change:width}.docSidebarContainerHidden_b3ry{cursor:pointer;width:var(--doc-sidebar-hidden-width)}.sidebarViewport_Xe31{height:100%;max-height:100vh;position:sticky;top:0}.docMainContainer_gTbr{flex-grow:1;max-width:calc(100% - var(--doc-sidebar-width))}.docMainContainerEnhanced_Uz_u{max-width:calc(100% - var(--doc-sidebar-hidden-width))}.docItemWrapperEnhanced_czyv{max-width:calc(var(--ifm-container-width) + var(--doc-sidebar-width))!important}}@media (min-width:1440px){.container{max-width:var(--ifm-container-width-xl)}}@media (max-width:996px){.col{--ifm-col-width:100%;flex-basis:var(--ifm-col-width);margin-left:0}.footer{--ifm-footer-padding-horizontal:0}.colorModeToggle_DEke,.footer__link-separator,.navbar__item,.tableOfContents_bqdL{display:none}.footer__col{margin-bottom:calc(var(--ifm-spacing-vertical)*3)}.footer__link-item{display:block}.hero{padding-left:0;padding-right:0}.navbar>.container,.navbar>.container-fluid{padding:0}.navbar__toggle{display:inherit}.navbar__search-input{width:9rem}.pills--block,.tabs--block{flex-direction:column}.searchBox_ZlJk{position:absolute;right:var(--ifm-navbar-padding-horizontal)}.docItemContainer_F8PC{padding:0 .3rem}}@media (max-width:576px){.markdown h1:first-child{--ifm-h1-font-size:2rem}.markdown>h2{--ifm-h2-font-size:1.5rem}.markdown>h3{--ifm-h3-font-size:1.25rem}}@media (hover:hover){.backToTopButton_sjWU:hover{background-color:var(--ifm-color-emphasis-300)}}@media (pointer:fine){.thin-scrollbar{scrollbar-width:thin}.thin-scrollbar::-webkit-scrollbar{height:var(--ifm-scrollbar-size);width:var(--ifm-scrollbar-size)}.thin-scrollbar::-webkit-scrollbar-track{background:var(--ifm-scrollbar-track-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb{background:var(--ifm-scrollbar-thumb-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb:hover{background:var(--ifm-scrollbar-thumb-hover-background-color)}}@media (prefers-reduced-motion:reduce){:root{--ifm-transition-fast:0ms;--ifm-transition-slow:0ms}}@media print{.announcementBar_mb4j,.footer,.menu,.navbar,.pagination-nav,.table-of-contents,.tocMobile_ITEo{display:none}.tabs{page-break-inside:avoid}.codeBlockLines_e6Vv{white-space:pre-wrap}} \ No newline at end of file +.col,.container{padding:0 var(--ifm-spacing-horizontal);width:100%}.markdown>h2,.markdown>h3,.markdown>h4,.markdown>h5,.markdown>h6{margin-bottom:calc(var(--ifm-heading-vertical-rhythm-bottom)*var(--ifm-leading))}.markdown li,body{word-wrap:break-word}body,ol ol,ol ul,ul ol,ul ul{margin:0}pre,table{overflow:auto}blockquote,pre{margin:0 0 var(--ifm-spacing-vertical)}.breadcrumbs__link,.button{transition-timing-function:var(--ifm-transition-timing-default)}.button,code{vertical-align:middle}.button--outline.button--active,.button--outline:active,.button--outline:hover,:root{--ifm-button-color:var(--ifm-font-color-base-inverse)}.menu__link:hover,a{transition:color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.navbar--dark,:root{--ifm-navbar-link-hover-color:var(--ifm-color-primary)}.menu,.navbar-sidebar{overflow-x:hidden}:root,html[data-theme=dark]{--ifm-color-emphasis-500:var(--ifm-color-gray-500)}.toggleButton_gllP,html{-webkit-tap-highlight-color:transparent}.clean-list,.containsTaskList_mC6p,.details_lb9f>summary,.dropdown__menu,.menu__list{list-style:none}:root{--ifm-color-scheme:light;--ifm-dark-value:10%;--ifm-darker-value:15%;--ifm-darkest-value:30%;--ifm-light-value:15%;--ifm-lighter-value:30%;--ifm-lightest-value:50%;--ifm-contrast-background-value:90%;--ifm-contrast-foreground-value:70%;--ifm-contrast-background-dark-value:70%;--ifm-contrast-foreground-dark-value:90%;--ifm-color-primary:#3578e5;--ifm-color-secondary:#ebedf0;--ifm-color-success:#00a400;--ifm-color-info:#54c7ec;--ifm-color-warning:#ffba00;--ifm-color-danger:#fa383e;--ifm-color-primary-dark:#306cce;--ifm-color-primary-darker:#2d66c3;--ifm-color-primary-darkest:#2554a0;--ifm-color-primary-light:#538ce9;--ifm-color-primary-lighter:#72a1ed;--ifm-color-primary-lightest:#9abcf2;--ifm-color-primary-contrast-background:#ebf2fc;--ifm-color-primary-contrast-foreground:#102445;--ifm-color-secondary-dark:#d4d5d8;--ifm-color-secondary-darker:#c8c9cc;--ifm-color-secondary-darkest:#a4a6a8;--ifm-color-secondary-light:#eef0f2;--ifm-color-secondary-lighter:#f1f2f5;--ifm-color-secondary-lightest:#f5f6f8;--ifm-color-secondary-contrast-background:#fdfdfe;--ifm-color-secondary-contrast-foreground:#474748;--ifm-color-success-dark:#009400;--ifm-color-success-darker:#008b00;--ifm-color-success-darkest:#007300;--ifm-color-success-light:#26b226;--ifm-color-success-lighter:#4dbf4d;--ifm-color-success-lightest:#80d280;--ifm-color-success-contrast-background:#e6f6e6;--ifm-color-success-contrast-foreground:#003100;--ifm-color-info-dark:#4cb3d4;--ifm-color-info-darker:#47a9c9;--ifm-color-info-darkest:#3b8ba5;--ifm-color-info-light:#6ecfef;--ifm-color-info-lighter:#87d8f2;--ifm-color-info-lightest:#aae3f6;--ifm-color-info-contrast-background:#eef9fd;--ifm-color-info-contrast-foreground:#193c47;--ifm-color-warning-dark:#e6a700;--ifm-color-warning-darker:#d99e00;--ifm-color-warning-darkest:#b38200;--ifm-color-warning-light:#ffc426;--ifm-color-warning-lighter:#ffcf4d;--ifm-color-warning-lightest:#ffdd80;--ifm-color-warning-contrast-background:#fff8e6;--ifm-color-warning-contrast-foreground:#4d3800;--ifm-color-danger-dark:#e13238;--ifm-color-danger-darker:#d53035;--ifm-color-danger-darkest:#af272b;--ifm-color-danger-light:#fb565b;--ifm-color-danger-lighter:#fb7478;--ifm-color-danger-lightest:#fd9c9f;--ifm-color-danger-contrast-background:#ffebec;--ifm-color-danger-contrast-foreground:#4b1113;--ifm-color-white:#fff;--ifm-color-black:#000;--ifm-color-gray-0:var(--ifm-color-white);--ifm-color-gray-100:#f5f6f7;--ifm-color-gray-200:#ebedf0;--ifm-color-gray-300:#dadde1;--ifm-color-gray-400:#ccd0d5;--ifm-color-gray-500:#bec3c9;--ifm-color-gray-600:#8d949e;--ifm-color-gray-700:#606770;--ifm-color-gray-800:#444950;--ifm-color-gray-900:#1c1e21;--ifm-color-gray-1000:var(--ifm-color-black);--ifm-color-emphasis-0:var(--ifm-color-gray-0);--ifm-color-emphasis-100:var(--ifm-color-gray-100);--ifm-color-emphasis-200:var(--ifm-color-gray-200);--ifm-color-emphasis-300:var(--ifm-color-gray-300);--ifm-color-emphasis-400:var(--ifm-color-gray-400);--ifm-color-emphasis-600:var(--ifm-color-gray-600);--ifm-color-emphasis-700:var(--ifm-color-gray-700);--ifm-color-emphasis-800:var(--ifm-color-gray-800);--ifm-color-emphasis-900:var(--ifm-color-gray-900);--ifm-color-emphasis-1000:var(--ifm-color-gray-1000);--ifm-color-content:var(--ifm-color-emphasis-900);--ifm-color-content-inverse:var(--ifm-color-emphasis-0);--ifm-color-content-secondary:#525860;--ifm-background-color:#0000;--ifm-background-surface-color:var(--ifm-color-content-inverse);--ifm-global-border-width:1px;--ifm-global-radius:0.4rem;--ifm-hover-overlay:#0000000d;--ifm-font-color-base:var(--ifm-color-content);--ifm-font-color-base-inverse:var(--ifm-color-content-inverse);--ifm-font-color-secondary:var(--ifm-color-content-secondary);--ifm-font-family-base:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--ifm-font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--ifm-font-size-base:100%;--ifm-font-weight-light:300;--ifm-font-weight-normal:400;--ifm-font-weight-semibold:500;--ifm-font-weight-bold:700;--ifm-font-weight-base:var(--ifm-font-weight-normal);--ifm-line-height-base:1.65;--ifm-global-spacing:1rem;--ifm-spacing-vertical:var(--ifm-global-spacing);--ifm-spacing-horizontal:var(--ifm-global-spacing);--ifm-transition-fast:200ms;--ifm-transition-slow:400ms;--ifm-transition-timing-default:cubic-bezier(0.08,0.52,0.52,1);--ifm-global-shadow-lw:0 1px 2px 0 #0000001a;--ifm-global-shadow-md:0 5px 40px #0003;--ifm-global-shadow-tl:0 12px 28px 0 #0003,0 2px 4px 0 #0000001a;--ifm-z-index-dropdown:100;--ifm-z-index-fixed:200;--ifm-z-index-overlay:400;--ifm-container-width:1140px;--ifm-container-width-xl:1320px;--ifm-code-background:#f6f7f8;--ifm-code-border-radius:var(--ifm-global-radius);--ifm-code-font-size:90%;--ifm-code-padding-horizontal:0.1rem;--ifm-code-padding-vertical:0.1rem;--ifm-pre-background:var(--ifm-code-background);--ifm-pre-border-radius:var(--ifm-code-border-radius);--ifm-pre-color:inherit;--ifm-pre-line-height:1.45;--ifm-pre-padding:1rem;--ifm-heading-color:inherit;--ifm-heading-margin-top:0;--ifm-heading-margin-bottom:var(--ifm-spacing-vertical);--ifm-heading-font-family:var(--ifm-font-family-base);--ifm-heading-font-weight:var(--ifm-font-weight-bold);--ifm-heading-line-height:1.25;--ifm-h1-font-size:2rem;--ifm-h2-font-size:1.5rem;--ifm-h3-font-size:1.25rem;--ifm-h4-font-size:1rem;--ifm-h5-font-size:0.875rem;--ifm-h6-font-size:0.85rem;--ifm-image-alignment-padding:1.25rem;--ifm-leading-desktop:1.25;--ifm-leading:calc(var(--ifm-leading-desktop)*1rem);--ifm-list-left-padding:2rem;--ifm-list-margin:1rem;--ifm-list-item-margin:0.25rem;--ifm-list-paragraph-margin:1rem;--ifm-table-cell-padding:0.75rem;--ifm-table-background:#0000;--ifm-table-stripe-background:#00000008;--ifm-table-border-width:1px;--ifm-table-border-color:var(--ifm-color-emphasis-300);--ifm-table-head-background:inherit;--ifm-table-head-color:inherit;--ifm-table-head-font-weight:var(--ifm-font-weight-bold);--ifm-table-cell-color:inherit;--ifm-link-color:var(--ifm-color-primary);--ifm-link-decoration:none;--ifm-link-hover-color:var(--ifm-link-color);--ifm-link-hover-decoration:underline;--ifm-paragraph-margin-bottom:var(--ifm-leading);--ifm-blockquote-font-size:var(--ifm-font-size-base);--ifm-blockquote-border-left-width:2px;--ifm-blockquote-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-blockquote-padding-vertical:0;--ifm-blockquote-shadow:none;--ifm-blockquote-color:var(--ifm-color-emphasis-800);--ifm-blockquote-border-color:var(--ifm-color-emphasis-300);--ifm-hr-background-color:var(--ifm-color-emphasis-500);--ifm-hr-height:1px;--ifm-hr-margin-vertical:1.5rem;--ifm-scrollbar-size:7px;--ifm-scrollbar-track-background-color:#f1f1f1;--ifm-scrollbar-thumb-background-color:silver;--ifm-scrollbar-thumb-hover-background-color:#a7a7a7;--ifm-alert-background-color:inherit;--ifm-alert-border-color:inherit;--ifm-alert-border-radius:var(--ifm-global-radius);--ifm-alert-border-width:0px;--ifm-alert-border-left-width:5px;--ifm-alert-color:var(--ifm-font-color-base);--ifm-alert-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-alert-padding-vertical:var(--ifm-spacing-vertical);--ifm-alert-shadow:var(--ifm-global-shadow-lw);--ifm-avatar-intro-margin:1rem;--ifm-avatar-intro-alignment:inherit;--ifm-avatar-photo-size:3rem;--ifm-badge-background-color:inherit;--ifm-badge-border-color:inherit;--ifm-badge-border-radius:var(--ifm-global-radius);--ifm-badge-border-width:var(--ifm-global-border-width);--ifm-badge-color:var(--ifm-color-white);--ifm-badge-padding-horizontal:calc(var(--ifm-spacing-horizontal)*0.5);--ifm-badge-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-breadcrumb-border-radius:1.5rem;--ifm-breadcrumb-spacing:0.5rem;--ifm-breadcrumb-color-active:var(--ifm-color-primary);--ifm-breadcrumb-item-background-active:var(--ifm-hover-overlay);--ifm-breadcrumb-padding-horizontal:0.8rem;--ifm-breadcrumb-padding-vertical:0.4rem;--ifm-breadcrumb-size-multiplier:1;--ifm-breadcrumb-separator:url('data:image/svg+xml;utf8,');--ifm-breadcrumb-separator-filter:none;--ifm-breadcrumb-separator-size:0.5rem;--ifm-breadcrumb-separator-size-multiplier:1.25;--ifm-button-background-color:inherit;--ifm-button-border-color:var(--ifm-button-background-color);--ifm-button-border-width:var(--ifm-global-border-width);--ifm-button-font-weight:var(--ifm-font-weight-bold);--ifm-button-padding-horizontal:1.5rem;--ifm-button-padding-vertical:0.375rem;--ifm-button-size-multiplier:1;--ifm-button-transition-duration:var(--ifm-transition-fast);--ifm-button-border-radius:calc(var(--ifm-global-radius)*var(--ifm-button-size-multiplier));--ifm-button-group-spacing:2px;--ifm-card-background-color:var(--ifm-background-surface-color);--ifm-card-border-radius:calc(var(--ifm-global-radius)*2);--ifm-card-horizontal-spacing:var(--ifm-global-spacing);--ifm-card-vertical-spacing:var(--ifm-global-spacing);--ifm-toc-border-color:var(--ifm-color-emphasis-300);--ifm-toc-link-color:var(--ifm-color-content-secondary);--ifm-toc-padding-vertical:0.5rem;--ifm-toc-padding-horizontal:0.5rem;--ifm-dropdown-background-color:var(--ifm-background-surface-color);--ifm-dropdown-font-weight:var(--ifm-font-weight-semibold);--ifm-dropdown-link-color:var(--ifm-font-color-base);--ifm-dropdown-hover-background-color:var(--ifm-hover-overlay);--ifm-footer-background-color:var(--ifm-color-emphasis-100);--ifm-footer-color:inherit;--ifm-footer-link-color:var(--ifm-color-emphasis-700);--ifm-footer-link-hover-color:var(--ifm-color-primary);--ifm-footer-link-horizontal-spacing:0.5rem;--ifm-footer-padding-horizontal:calc(var(--ifm-spacing-horizontal)*2);--ifm-footer-padding-vertical:calc(var(--ifm-spacing-vertical)*2);--ifm-footer-title-color:inherit;--ifm-footer-logo-max-width:min(30rem,90vw);--ifm-hero-background-color:var(--ifm-background-surface-color);--ifm-hero-text-color:var(--ifm-color-emphasis-800);--ifm-menu-color:var(--ifm-color-emphasis-700);--ifm-menu-color-active:var(--ifm-color-primary);--ifm-menu-color-background-active:var(--ifm-hover-overlay);--ifm-menu-color-background-hover:var(--ifm-hover-overlay);--ifm-menu-link-padding-horizontal:0.75rem;--ifm-menu-link-padding-vertical:0.375rem;--ifm-menu-link-sublist-icon:url('data:image/svg+xml;utf8,');--ifm-menu-link-sublist-icon-filter:none;--ifm-navbar-background-color:var(--ifm-background-surface-color);--ifm-navbar-height:3.75rem;--ifm-navbar-item-padding-horizontal:0.75rem;--ifm-navbar-item-padding-vertical:0.25rem;--ifm-navbar-link-color:var(--ifm-font-color-base);--ifm-navbar-link-active-color:var(--ifm-link-color);--ifm-navbar-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-navbar-padding-vertical:calc(var(--ifm-spacing-vertical)*0.5);--ifm-navbar-shadow:var(--ifm-global-shadow-lw);--ifm-navbar-search-input-background-color:var(--ifm-color-emphasis-200);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-800);--ifm-navbar-search-input-placeholder-color:var(--ifm-color-emphasis-500);--ifm-navbar-search-input-icon:url('data:image/svg+xml;utf8,');--ifm-navbar-sidebar-width:83vw;--ifm-pagination-border-radius:var(--ifm-global-radius);--ifm-pagination-color-active:var(--ifm-color-primary);--ifm-pagination-font-size:1rem;--ifm-pagination-item-active-background:var(--ifm-hover-overlay);--ifm-pagination-page-spacing:0.2em;--ifm-pagination-padding-horizontal:calc(var(--ifm-spacing-horizontal)*1);--ifm-pagination-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-pagination-nav-border-radius:var(--ifm-global-radius);--ifm-pagination-nav-color-hover:var(--ifm-color-primary);--ifm-pills-color-active:var(--ifm-color-primary);--ifm-pills-color-background-active:var(--ifm-hover-overlay);--ifm-pills-spacing:0.125rem;--ifm-tabs-color:var(--ifm-font-color-secondary);--ifm-tabs-color-active:var(--ifm-color-primary);--ifm-tabs-color-active-border:var(--ifm-tabs-color-active);--ifm-tabs-padding-horizontal:1rem;--ifm-tabs-padding-vertical:1rem;--docusaurus-progress-bar-color:var(--ifm-color-primary);--ifm-color-primary:#2e8555;--ifm-color-primary-dark:#29784c;--ifm-color-primary-darker:#277148;--ifm-color-primary-darkest:#205d3b;--ifm-color-primary-light:#33925d;--ifm-color-primary-lighter:#359962;--ifm-color-primary-lightest:#3cad6e;--ifm-code-font-size:95%;--docusaurus-highlighted-code-line-bg:#0000001a;--docusaurus-announcement-bar-height:auto;--docusaurus-tag-list-border:var(--ifm-color-emphasis-300);--docusaurus-collapse-button-bg:#0000;--docusaurus-collapse-button-bg-hover:#0000001a;--doc-sidebar-width:300px;--doc-sidebar-hidden-width:30px}.badge--danger,.badge--info,.badge--primary,.badge--secondary,.badge--success,.badge--warning{--ifm-badge-border-color:var(--ifm-badge-background-color)}.button--link,.button--outline{--ifm-button-background-color:#0000}*{box-sizing:border-box}html{-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:100%;text-size-adjust:100%;background-color:var(--ifm-background-color);color:var(--ifm-font-color-base);color-scheme:var(--ifm-color-scheme);font:var(--ifm-font-size-base)/var(--ifm-line-height-base) var(--ifm-font-family-base);text-rendering:optimizelegibility}iframe{border:0;color-scheme:auto}.container{margin:0 auto;max-width:var(--ifm-container-width)}.container--fluid{max-width:inherit}.row{display:flex;flex-wrap:wrap;margin:0 calc(var(--ifm-spacing-horizontal)*-1)}.margin-bottom--none,.margin-vert--none,.markdown>:last-child{margin-bottom:0!important}.margin-top--none,.margin-vert--none{margin-top:0!important}.row--no-gutters{margin-left:0;margin-right:0}.margin-horiz--none,.margin-right--none{margin-right:0!important}.row--no-gutters>.col{padding-left:0;padding-right:0}.row--align-top{align-items:flex-start}.row--align-bottom{align-items:flex-end}.menuExternalLink_NmtK,.row--align-center{align-items:center}.row--align-stretch{align-items:stretch}.row--align-baseline{align-items:baseline}.col{--ifm-col-width:100%;flex:1 0;margin-left:0;max-width:var(--ifm-col-width)}.padding-bottom--none,.padding-vert--none{padding-bottom:0!important}.padding-top--none,.padding-vert--none{padding-top:0!important}.padding-horiz--none,.padding-left--none{padding-left:0!important}.padding-horiz--none,.padding-right--none{padding-right:0!important}.col[class*=col--]{flex:0 0 var(--ifm-col-width)}.col--1{--ifm-col-width:8.33333%}.col--offset-1{margin-left:8.33333%}.col--2{--ifm-col-width:16.66667%}.col--offset-2{margin-left:16.66667%}.col--3{--ifm-col-width:25%}.col--offset-3{margin-left:25%}.col--4{--ifm-col-width:33.33333%}.col--offset-4{margin-left:33.33333%}.col--5{--ifm-col-width:41.66667%}.col--offset-5{margin-left:41.66667%}.col--6{--ifm-col-width:50%}.col--offset-6{margin-left:50%}.col--7{--ifm-col-width:58.33333%}.col--offset-7{margin-left:58.33333%}.col--8{--ifm-col-width:66.66667%}.col--offset-8{margin-left:66.66667%}.col--9{--ifm-col-width:75%}.col--offset-9{margin-left:75%}.col--10{--ifm-col-width:83.33333%}.col--offset-10{margin-left:83.33333%}.col--11{--ifm-col-width:91.66667%}.col--offset-11{margin-left:91.66667%}.col--12{--ifm-col-width:100%}.col--offset-12{margin-left:100%}.margin-horiz--none,.margin-left--none{margin-left:0!important}.margin--none{margin:0!important}.margin-bottom--xs,.margin-vert--xs{margin-bottom:.25rem!important}.margin-top--xs,.margin-vert--xs{margin-top:.25rem!important}.margin-horiz--xs,.margin-left--xs{margin-left:.25rem!important}.margin-horiz--xs,.margin-right--xs{margin-right:.25rem!important}.margin--xs{margin:.25rem!important}.margin-bottom--sm,.margin-vert--sm{margin-bottom:.5rem!important}.margin-top--sm,.margin-vert--sm{margin-top:.5rem!important}.margin-horiz--sm,.margin-left--sm{margin-left:.5rem!important}.margin-horiz--sm,.margin-right--sm{margin-right:.5rem!important}.margin--sm{margin:.5rem!important}.margin-bottom--md,.margin-vert--md{margin-bottom:1rem!important}.margin-top--md,.margin-vert--md{margin-top:1rem!important}.margin-horiz--md,.margin-left--md{margin-left:1rem!important}.margin-horiz--md,.margin-right--md{margin-right:1rem!important}.margin--md{margin:1rem!important}.margin-bottom--lg,.margin-vert--lg{margin-bottom:2rem!important}.margin-top--lg,.margin-vert--lg{margin-top:2rem!important}.margin-horiz--lg,.margin-left--lg{margin-left:2rem!important}.margin-horiz--lg,.margin-right--lg{margin-right:2rem!important}.margin--lg{margin:2rem!important}.margin-bottom--xl,.margin-vert--xl{margin-bottom:5rem!important}.margin-top--xl,.margin-vert--xl{margin-top:5rem!important}.margin-horiz--xl,.margin-left--xl{margin-left:5rem!important}.margin-horiz--xl,.margin-right--xl{margin-right:5rem!important}.margin--xl{margin:5rem!important}.padding--none{padding:0!important}.padding-bottom--xs,.padding-vert--xs{padding-bottom:.25rem!important}.padding-top--xs,.padding-vert--xs{padding-top:.25rem!important}.padding-horiz--xs,.padding-left--xs{padding-left:.25rem!important}.padding-horiz--xs,.padding-right--xs{padding-right:.25rem!important}.padding--xs{padding:.25rem!important}.padding-bottom--sm,.padding-vert--sm{padding-bottom:.5rem!important}.padding-top--sm,.padding-vert--sm{padding-top:.5rem!important}.padding-horiz--sm,.padding-left--sm{padding-left:.5rem!important}.padding-horiz--sm,.padding-right--sm{padding-right:.5rem!important}.padding--sm{padding:.5rem!important}.padding-bottom--md,.padding-vert--md{padding-bottom:1rem!important}.padding-top--md,.padding-vert--md{padding-top:1rem!important}.padding-horiz--md,.padding-left--md{padding-left:1rem!important}.padding-horiz--md,.padding-right--md{padding-right:1rem!important}.padding--md{padding:1rem!important}.padding-bottom--lg,.padding-vert--lg{padding-bottom:2rem!important}.padding-top--lg,.padding-vert--lg{padding-top:2rem!important}.padding-horiz--lg,.padding-left--lg{padding-left:2rem!important}.padding-horiz--lg,.padding-right--lg{padding-right:2rem!important}.padding--lg{padding:2rem!important}.padding-bottom--xl,.padding-vert--xl{padding-bottom:5rem!important}.padding-top--xl,.padding-vert--xl{padding-top:5rem!important}.padding-horiz--xl,.padding-left--xl{padding-left:5rem!important}.padding-horiz--xl,.padding-right--xl{padding-right:5rem!important}.padding--xl{padding:5rem!important}code{background-color:var(--ifm-code-background);border:.1rem solid #0000001a;border-radius:var(--ifm-code-border-radius);font-family:var(--ifm-font-family-monospace);font-size:var(--ifm-code-font-size);padding:var(--ifm-code-padding-vertical) var(--ifm-code-padding-horizontal)}a code{color:inherit}pre{background-color:var(--ifm-pre-background);border-radius:var(--ifm-pre-border-radius);color:var(--ifm-pre-color);font:var(--ifm-code-font-size)/var(--ifm-pre-line-height) var(--ifm-font-family-monospace);padding:var(--ifm-pre-padding)}pre code{background-color:initial;border:none;font-size:100%;line-height:inherit;padding:0}kbd{background-color:var(--ifm-color-emphasis-0);border:1px solid var(--ifm-color-emphasis-400);border-radius:.2rem;box-shadow:inset 0 -1px 0 var(--ifm-color-emphasis-400);color:var(--ifm-color-emphasis-800);font:80% var(--ifm-font-family-monospace);padding:.15rem .3rem}h1,h2,h3,h4,h5,h6{color:var(--ifm-heading-color);font-family:var(--ifm-heading-font-family);font-weight:var(--ifm-heading-font-weight);line-height:var(--ifm-heading-line-height);margin:var(--ifm-heading-margin-top) 0 var(--ifm-heading-margin-bottom) 0}h1{font-size:var(--ifm-h1-font-size)}h2{font-size:var(--ifm-h2-font-size)}h3{font-size:var(--ifm-h3-font-size)}h4{font-size:var(--ifm-h4-font-size)}h5{font-size:var(--ifm-h5-font-size)}h6{font-size:var(--ifm-h6-font-size)}img{max-width:100%}img[align=right]{padding-left:var(--image-alignment-padding)}img[align=left]{padding-right:var(--image-alignment-padding)}.markdown{--ifm-h1-vertical-rhythm-top:3;--ifm-h2-vertical-rhythm-top:2;--ifm-h3-vertical-rhythm-top:1.5;--ifm-heading-vertical-rhythm-top:1.25;--ifm-h1-vertical-rhythm-bottom:1.25;--ifm-heading-vertical-rhythm-bottom:1}.markdown:after,.markdown:before{content:"";display:table}.markdown:after{clear:both}.markdown h1:first-child{--ifm-h1-font-size:3rem;margin-bottom:calc(var(--ifm-h1-vertical-rhythm-bottom)*var(--ifm-leading))}.markdown>h2{--ifm-h2-font-size:2rem;margin-top:calc(var(--ifm-h2-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h3{--ifm-h3-font-size:1.5rem;margin-top:calc(var(--ifm-h3-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h4,.markdown>h5,.markdown>h6{margin-top:calc(var(--ifm-heading-vertical-rhythm-top)*var(--ifm-leading))}.markdown>p,.markdown>pre,.markdown>ul{margin-bottom:var(--ifm-leading)}.markdown li>p{margin-top:var(--ifm-list-paragraph-margin)}.markdown li+li{margin-top:var(--ifm-list-item-margin)}ol,ul{margin:0 0 var(--ifm-list-margin);padding-left:var(--ifm-list-left-padding)}ol ol,ul ol{list-style-type:lower-roman}ol ol ol,ol ul ol,ul ol ol,ul ul ol{list-style-type:lower-alpha}table{border-collapse:collapse;display:block;margin-bottom:var(--ifm-spacing-vertical)}table thead tr{border-bottom:2px solid var(--ifm-table-border-color)}table thead,table tr:nth-child(2n){background-color:var(--ifm-table-stripe-background)}table tr{background-color:var(--ifm-table-background);border-top:var(--ifm-table-border-width) solid var(--ifm-table-border-color)}table td,table th{border:var(--ifm-table-border-width) solid var(--ifm-table-border-color);padding:var(--ifm-table-cell-padding)}table th{background-color:var(--ifm-table-head-background);color:var(--ifm-table-head-color);font-weight:var(--ifm-table-head-font-weight)}table td{color:var(--ifm-table-cell-color)}strong{font-weight:var(--ifm-font-weight-bold)}a{color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration)}a:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration)}.button:hover,.text--no-decoration,.text--no-decoration:hover,a:not([href]){text-decoration:none}p{margin:0 0 var(--ifm-paragraph-margin-bottom)}blockquote{border-left:var(--ifm-blockquote-border-left-width) solid var(--ifm-blockquote-border-color);box-shadow:var(--ifm-blockquote-shadow);color:var(--ifm-blockquote-color);font-size:var(--ifm-blockquote-font-size);padding:var(--ifm-blockquote-padding-vertical) var(--ifm-blockquote-padding-horizontal)}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{background-color:var(--ifm-hr-background-color);border:0;height:var(--ifm-hr-height);margin:var(--ifm-hr-margin-vertical) 0}.shadow--lw{box-shadow:var(--ifm-global-shadow-lw)!important}.shadow--md{box-shadow:var(--ifm-global-shadow-md)!important}.shadow--tl{box-shadow:var(--ifm-global-shadow-tl)!important}.text--primary,.wordWrapButtonEnabled_EoeP .wordWrapButtonIcon_Bwma{color:var(--ifm-color-primary)}.text--secondary{color:var(--ifm-color-secondary)}.text--success{color:var(--ifm-color-success)}.text--info{color:var(--ifm-color-info)}.text--warning{color:var(--ifm-color-warning)}.text--danger{color:var(--ifm-color-danger)}.text--center{text-align:center}.text--left{text-align:left}.text--justify{text-align:justify}.text--right{text-align:right}.text--capitalize{text-transform:capitalize}.text--lowercase{text-transform:lowercase}.admonitionHeading_Gvgb,.alert__heading,.text--uppercase{text-transform:uppercase}.text--light{font-weight:var(--ifm-font-weight-light)}.text--normal{font-weight:var(--ifm-font-weight-normal)}.text--semibold{font-weight:var(--ifm-font-weight-semibold)}.text--bold{font-weight:var(--ifm-font-weight-bold)}.text--italic{font-style:italic}.text--truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text--break{word-wrap:break-word!important;word-break:break-word!important}.clean-btn{background:none;border:none;color:inherit;cursor:pointer;font-family:inherit;padding:0}.alert,.alert .close{color:var(--ifm-alert-foreground-color)}.clean-list{padding-left:0}.alert--primary{--ifm-alert-background-color:var(--ifm-color-primary-contrast-background);--ifm-alert-background-color-highlight:#3578e526;--ifm-alert-foreground-color:var(--ifm-color-primary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-primary-dark)}.alert--secondary{--ifm-alert-background-color:var(--ifm-color-secondary-contrast-background);--ifm-alert-background-color-highlight:#ebedf026;--ifm-alert-foreground-color:var(--ifm-color-secondary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-secondary-dark)}.alert--success{--ifm-alert-background-color:var(--ifm-color-success-contrast-background);--ifm-alert-background-color-highlight:#00a40026;--ifm-alert-foreground-color:var(--ifm-color-success-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-success-dark)}.alert--info{--ifm-alert-background-color:var(--ifm-color-info-contrast-background);--ifm-alert-background-color-highlight:#54c7ec26;--ifm-alert-foreground-color:var(--ifm-color-info-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-info-dark)}.alert--warning{--ifm-alert-background-color:var(--ifm-color-warning-contrast-background);--ifm-alert-background-color-highlight:#ffba0026;--ifm-alert-foreground-color:var(--ifm-color-warning-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-warning-dark)}.alert--danger{--ifm-alert-background-color:var(--ifm-color-danger-contrast-background);--ifm-alert-background-color-highlight:#fa383e26;--ifm-alert-foreground-color:var(--ifm-color-danger-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-danger-dark)}.alert{--ifm-code-background:var(--ifm-alert-background-color-highlight);--ifm-link-color:var(--ifm-alert-foreground-color);--ifm-link-hover-color:var(--ifm-alert-foreground-color);--ifm-link-decoration:underline;--ifm-tabs-color:var(--ifm-alert-foreground-color);--ifm-tabs-color-active:var(--ifm-alert-foreground-color);--ifm-tabs-color-active-border:var(--ifm-alert-border-color);background-color:var(--ifm-alert-background-color);border:var(--ifm-alert-border-width) solid var(--ifm-alert-border-color);border-left-width:var(--ifm-alert-border-left-width);border-radius:var(--ifm-alert-border-radius);box-shadow:var(--ifm-alert-shadow);padding:var(--ifm-alert-padding-vertical) var(--ifm-alert-padding-horizontal)}.alert__heading{align-items:center;display:flex;font:700 var(--ifm-h5-font-size)/var(--ifm-heading-line-height) var(--ifm-heading-font-family);margin-bottom:.5rem}.alert__icon{display:inline-flex;margin-right:.4em}.alert__icon svg{fill:var(--ifm-alert-foreground-color);stroke:var(--ifm-alert-foreground-color);stroke-width:0}.alert .close{margin:calc(var(--ifm-alert-padding-vertical)*-1) calc(var(--ifm-alert-padding-horizontal)*-1) 0 0;opacity:.75}.alert .close:focus,.alert .close:hover{opacity:1}.alert a{text-decoration-color:var(--ifm-alert-border-color)}.alert a:hover{text-decoration-thickness:2px}.avatar{column-gap:var(--ifm-avatar-intro-margin);display:flex}.avatar__photo{border-radius:50%;display:block;height:var(--ifm-avatar-photo-size);overflow:hidden;width:var(--ifm-avatar-photo-size)}.card--full-height,.navbar__logo img,body,html{height:100%}.avatar__photo--sm{--ifm-avatar-photo-size:2rem}.avatar__photo--lg{--ifm-avatar-photo-size:4rem}.avatar__photo--xl{--ifm-avatar-photo-size:6rem}.avatar__intro{display:flex;flex:1 1;flex-direction:column;justify-content:center;text-align:var(--ifm-avatar-intro-alignment)}.badge,.breadcrumbs__item,.breadcrumbs__link,.button,.dropdown>.navbar__link:after{display:inline-block}.avatar__name{font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base)}.avatar__subtitle{margin-top:.25rem}.avatar--vertical{--ifm-avatar-intro-alignment:center;--ifm-avatar-intro-margin:0.5rem;align-items:center;flex-direction:column}.badge{background-color:var(--ifm-badge-background-color);border:var(--ifm-badge-border-width) solid var(--ifm-badge-border-color);border-radius:var(--ifm-badge-border-radius);color:var(--ifm-badge-color);font-size:75%;font-weight:var(--ifm-font-weight-bold);line-height:1;padding:var(--ifm-badge-padding-vertical) var(--ifm-badge-padding-horizontal)}.badge--primary{--ifm-badge-background-color:var(--ifm-color-primary)}.badge--secondary{--ifm-badge-background-color:var(--ifm-color-secondary);color:var(--ifm-color-black)}.breadcrumbs__link,.button.button--secondary.button--outline:not(.button--active):not(:hover){color:var(--ifm-font-color-base)}.badge--success{--ifm-badge-background-color:var(--ifm-color-success)}.badge--info{--ifm-badge-background-color:var(--ifm-color-info)}.badge--warning{--ifm-badge-background-color:var(--ifm-color-warning)}.badge--danger{--ifm-badge-background-color:var(--ifm-color-danger)}.breadcrumbs{margin-bottom:0;padding-left:0}.breadcrumbs__item:not(:last-child):after{background:var(--ifm-breadcrumb-separator) center;content:" ";display:inline-block;filter:var(--ifm-breadcrumb-separator-filter);height:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier));margin:0 var(--ifm-breadcrumb-spacing);opacity:.5;width:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier))}.breadcrumbs__item--active .breadcrumbs__link{background:var(--ifm-breadcrumb-item-background-active);color:var(--ifm-breadcrumb-color-active)}.breadcrumbs__link{border-radius:var(--ifm-breadcrumb-border-radius);font-size:calc(1rem*var(--ifm-breadcrumb-size-multiplier));padding:calc(var(--ifm-breadcrumb-padding-vertical)*var(--ifm-breadcrumb-size-multiplier)) calc(var(--ifm-breadcrumb-padding-horizontal)*var(--ifm-breadcrumb-size-multiplier));transition-duration:var(--ifm-transition-fast);transition-property:background,color}.breadcrumbs__link:any-link:hover,.breadcrumbs__link:link:hover,.breadcrumbs__link:visited:hover,area[href].breadcrumbs__link:hover{background:var(--ifm-breadcrumb-item-background-active);text-decoration:none}.breadcrumbs--sm{--ifm-breadcrumb-size-multiplier:0.8}.breadcrumbs--lg{--ifm-breadcrumb-size-multiplier:1.2}.button{background-color:var(--ifm-button-background-color);border:var(--ifm-button-border-width) solid var(--ifm-button-border-color);border-radius:var(--ifm-button-border-radius);cursor:pointer;font-size:calc(.875rem*var(--ifm-button-size-multiplier));font-weight:var(--ifm-button-font-weight);line-height:1.5;padding:calc(var(--ifm-button-padding-vertical)*var(--ifm-button-size-multiplier)) calc(var(--ifm-button-padding-horizontal)*var(--ifm-button-size-multiplier));text-align:center;transition-duration:var(--ifm-button-transition-duration);transition-property:color,background,border-color;-webkit-user-select:none;user-select:none;white-space:nowrap}.button,.button:hover{color:var(--ifm-button-color)}.button--outline{--ifm-button-color:var(--ifm-button-border-color)}.button--outline:hover{--ifm-button-background-color:var(--ifm-button-border-color)}.button--link{--ifm-button-border-color:#0000;color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration)}.button--link.button--active,.button--link:active,.button--link:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration)}.button.disabled,.button:disabled,.button[disabled]{opacity:.65;pointer-events:none}.button--sm{--ifm-button-size-multiplier:0.8}.button--lg{--ifm-button-size-multiplier:1.35}.button--block{display:block;width:100%}.button.button--secondary{color:var(--ifm-color-gray-900)}:where(.button--primary){--ifm-button-background-color:var(--ifm-color-primary);--ifm-button-border-color:var(--ifm-color-primary)}:where(.button--primary):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-primary-dark);--ifm-button-border-color:var(--ifm-color-primary-dark)}.button--primary.button--active,.button--primary:active{--ifm-button-background-color:var(--ifm-color-primary-darker);--ifm-button-border-color:var(--ifm-color-primary-darker)}:where(.button--secondary){--ifm-button-background-color:var(--ifm-color-secondary);--ifm-button-border-color:var(--ifm-color-secondary)}:where(.button--secondary):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-secondary-dark);--ifm-button-border-color:var(--ifm-color-secondary-dark)}.button--secondary.button--active,.button--secondary:active{--ifm-button-background-color:var(--ifm-color-secondary-darker);--ifm-button-border-color:var(--ifm-color-secondary-darker)}:where(.button--success){--ifm-button-background-color:var(--ifm-color-success);--ifm-button-border-color:var(--ifm-color-success)}:where(.button--success):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-success-dark);--ifm-button-border-color:var(--ifm-color-success-dark)}.button--success.button--active,.button--success:active{--ifm-button-background-color:var(--ifm-color-success-darker);--ifm-button-border-color:var(--ifm-color-success-darker)}:where(.button--info){--ifm-button-background-color:var(--ifm-color-info);--ifm-button-border-color:var(--ifm-color-info)}:where(.button--info):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-info-dark);--ifm-button-border-color:var(--ifm-color-info-dark)}.button--info.button--active,.button--info:active{--ifm-button-background-color:var(--ifm-color-info-darker);--ifm-button-border-color:var(--ifm-color-info-darker)}:where(.button--warning){--ifm-button-background-color:var(--ifm-color-warning);--ifm-button-border-color:var(--ifm-color-warning)}:where(.button--warning):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-warning-dark);--ifm-button-border-color:var(--ifm-color-warning-dark)}.button--warning.button--active,.button--warning:active{--ifm-button-background-color:var(--ifm-color-warning-darker);--ifm-button-border-color:var(--ifm-color-warning-darker)}:where(.button--danger){--ifm-button-background-color:var(--ifm-color-danger);--ifm-button-border-color:var(--ifm-color-danger)}:where(.button--danger):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-danger-dark);--ifm-button-border-color:var(--ifm-color-danger-dark)}.button--danger.button--active,.button--danger:active{--ifm-button-background-color:var(--ifm-color-danger-darker);--ifm-button-border-color:var(--ifm-color-danger-darker)}.button-group{display:inline-flex;gap:var(--ifm-button-group-spacing)}.button-group>.button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.button-group>.button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.button-group--block{display:flex;justify-content:stretch}.button-group--block>.button{flex-grow:1}.card{background-color:var(--ifm-card-background-color);border-radius:var(--ifm-card-border-radius);box-shadow:var(--ifm-global-shadow-lw);display:flex;flex-direction:column;overflow:hidden}.card__image{padding-top:var(--ifm-card-vertical-spacing)}.card__image:first-child{padding-top:0}.card__body,.card__footer,.card__header{padding:var(--ifm-card-vertical-spacing) var(--ifm-card-horizontal-spacing)}.card__body:not(:last-child),.card__footer:not(:last-child),.card__header:not(:last-child){padding-bottom:0}.card__body>:last-child,.card__footer>:last-child,.card__header>:last-child{margin-bottom:0}.card__footer{margin-top:auto}.table-of-contents{font-size:.8rem;margin-bottom:0;padding:var(--ifm-toc-padding-vertical) 0}.table-of-contents,.table-of-contents ul{list-style:none;padding-left:var(--ifm-toc-padding-horizontal)}.table-of-contents li{margin:var(--ifm-toc-padding-vertical) var(--ifm-toc-padding-horizontal)}.table-of-contents__left-border{border-left:1px solid var(--ifm-toc-border-color)}.table-of-contents__link{color:var(--ifm-toc-link-color);display:block}.table-of-contents__link--active,.table-of-contents__link--active code,.table-of-contents__link:hover,.table-of-contents__link:hover code{color:var(--ifm-color-primary);text-decoration:none}.close{color:var(--ifm-color-black);float:right;font-size:1.5rem;font-weight:var(--ifm-font-weight-bold);line-height:1;opacity:.5;padding:1rem;transition:opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.close:hover{opacity:.7}.close:focus,.theme-code-block-highlighted-line .codeLineNumber_Tfdd:before{opacity:.8}.dropdown{display:inline-flex;font-weight:var(--ifm-dropdown-font-weight);position:relative;vertical-align:top}.dropdown--hoverable:hover .dropdown__menu,.dropdown--show .dropdown__menu{opacity:1;pointer-events:all;transform:translateY(-1px);visibility:visible}#nprogress,.dropdown__menu,.navbar__item.dropdown .navbar__link:not([href]){pointer-events:none}.dropdown--right .dropdown__menu{left:inherit;right:0}.dropdown--nocaret .navbar__link:after{content:none!important}.dropdown__menu{background-color:var(--ifm-dropdown-background-color);border-radius:var(--ifm-global-radius);box-shadow:var(--ifm-global-shadow-md);left:0;max-height:80vh;min-width:10rem;opacity:0;overflow-y:auto;padding:.5rem;position:absolute;top:calc(100% - var(--ifm-navbar-item-padding-vertical) + .3rem);transform:translateY(-.625rem);transition-duration:var(--ifm-transition-fast);transition-property:opacity,transform,visibility;transition-timing-function:var(--ifm-transition-timing-default);visibility:hidden;z-index:var(--ifm-z-index-dropdown)}.menu__caret,.menu__link,.menu__list-item-collapsible{border-radius:.25rem;transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.dropdown__link{border-radius:.25rem;color:var(--ifm-dropdown-link-color);display:block;font-size:.875rem;margin-top:.2rem;padding:.25rem .5rem;white-space:nowrap}.dropdown__link--active,.dropdown__link:hover{background-color:var(--ifm-dropdown-hover-background-color);color:var(--ifm-dropdown-link-color);text-decoration:none}.dropdown__link--active,.dropdown__link--active:hover{--ifm-dropdown-link-color:var(--ifm-link-color)}.dropdown>.navbar__link:after{border-color:currentcolor #0000;border-style:solid;border-width:.4em .4em 0;content:"";margin-left:.3em;position:relative;top:2px;transform:translateY(-50%)}.footer{background-color:var(--ifm-footer-background-color);color:var(--ifm-footer-color);padding:var(--ifm-footer-padding-vertical) var(--ifm-footer-padding-horizontal)}.footer--dark{--ifm-footer-background-color:#303846;--ifm-footer-color:var(--ifm-footer-link-color);--ifm-footer-link-color:var(--ifm-color-secondary);--ifm-footer-title-color:var(--ifm-color-white)}.footer__links{margin-bottom:1rem}.footer__link-item{color:var(--ifm-footer-link-color);line-height:2}.footer__link-item:hover{color:var(--ifm-footer-link-hover-color)}.footer__link-separator{margin:0 var(--ifm-footer-link-horizontal-spacing)}.footer__logo{margin-top:1rem;max-width:var(--ifm-footer-logo-max-width)}.footer__title{color:var(--ifm-footer-title-color);font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base);margin-bottom:var(--ifm-heading-margin-bottom)}.menu,.navbar__link{font-weight:var(--ifm-font-weight-semibold)}.docItemContainer_Djhp article>:first-child,.docItemContainer_Djhp header+*,.footer__item{margin-top:0}.admonitionContent_BuS1>:last-child,.collapsibleContent_i85q p:last-child,.details_lb9f>summary>p:last-child,.footer__items{margin-bottom:0}.codeBlockStandalone_MEMb,[type=checkbox]{padding:0}.hero{align-items:center;background-color:var(--ifm-hero-background-color);color:var(--ifm-hero-text-color);display:flex;padding:4rem 2rem}.hero--primary{--ifm-hero-background-color:var(--ifm-color-primary);--ifm-hero-text-color:var(--ifm-font-color-base-inverse)}.hero--dark{--ifm-hero-background-color:#303846;--ifm-hero-text-color:var(--ifm-color-white)}.hero__title{font-size:3rem}.hero__subtitle{font-size:1.5rem}.menu__list{margin:0;padding-left:0}.menu__caret,.menu__link{padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal)}.menu__list .menu__list{flex:0 0 100%;margin-top:.25rem;padding-left:var(--ifm-menu-link-padding-horizontal)}.menu__list-item:not(:first-child){margin-top:.25rem}.menu__list-item--collapsed .menu__list{height:0;overflow:hidden}.details_lb9f[data-collapsed=false].isBrowser_bmU9>summary:before,.details_lb9f[open]:not(.isBrowser_bmU9)>summary:before,.menu__list-item--collapsed .menu__caret:before,.menu__list-item--collapsed .menu__link--sublist:after{transform:rotate(90deg)}.menu__list-item-collapsible{display:flex;flex-wrap:wrap;position:relative}.menu__caret:hover,.menu__link:hover,.menu__list-item-collapsible--active,.menu__list-item-collapsible:hover{background:var(--ifm-menu-color-background-hover)}.menu__list-item-collapsible .menu__link--active,.menu__list-item-collapsible .menu__link:hover{background:none!important}.menu__caret,.menu__link{align-items:center;display:flex}.menu__link{color:var(--ifm-menu-color);flex:1;line-height:1.25}.menu__link:hover{color:var(--ifm-menu-color);text-decoration:none}.menu__caret:before,.menu__link--sublist-caret:after{height:1.25rem;transform:rotate(180deg);transition:transform var(--ifm-transition-fast) linear;width:1.25rem;filter:var(--ifm-menu-link-sublist-icon-filter);content:""}.menu__link--sublist-caret:after{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem;margin-left:auto;min-width:1.25rem}.menu__link--active,.menu__link--active:hover{color:var(--ifm-menu-color-active)}.navbar__brand,.navbar__link{color:var(--ifm-navbar-link-color)}.menu__link--active:not(.menu__link--sublist){background-color:var(--ifm-menu-color-background-active)}.menu__caret:before{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem}.navbar--dark,html[data-theme=dark]{--ifm-menu-link-sublist-icon-filter:invert(100%) sepia(94%) saturate(17%) hue-rotate(223deg) brightness(104%) contrast(98%)}.navbar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-navbar-shadow);height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal)}.navbar,.navbar>.container,.navbar>.container-fluid{display:flex}.navbar--fixed-top{position:sticky;top:0;z-index:var(--ifm-z-index-fixed)}.navbar-sidebar,.navbar-sidebar__backdrop{bottom:0;opacity:0;position:fixed;transition-duration:var(--ifm-transition-fast);transition-timing-function:ease-in-out;left:0;top:0;visibility:hidden}.navbar__inner{display:flex;flex-wrap:wrap;justify-content:space-between;width:100%}.navbar__brand{align-items:center;display:flex;margin-right:1rem;min-width:0}.navbar__brand:hover{color:var(--ifm-navbar-link-hover-color);text-decoration:none}.announcementBarContent_xLdY,.navbar__title{flex:1 1 auto}.navbar__toggle{display:none;margin-right:.5rem}.navbar__logo{flex:0 0 auto;height:2rem;margin-right:.5rem}.navbar__items{align-items:center;display:flex;flex:1;min-width:0}.navbar__items--center{flex:0 0 auto}.navbar__items--center .navbar__brand{margin:0}.navbar__items--center+.navbar__items--right{flex:1}.navbar__items--right{flex:0 0 auto;justify-content:flex-end}.navbar__items--right>:last-child{padding-right:0}.navbar__item{display:inline-block;padding:var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal)}.navbar__link--active,.navbar__link:hover{color:var(--ifm-navbar-link-hover-color);text-decoration:none}.navbar--dark,.navbar--primary{--ifm-menu-color:var(--ifm-color-gray-300);--ifm-navbar-link-color:var(--ifm-color-gray-100);--ifm-navbar-search-input-background-color:#ffffff1a;--ifm-navbar-search-input-placeholder-color:#ffffff80;color:var(--ifm-color-white)}.navbar--dark{--ifm-navbar-background-color:#242526;--ifm-menu-color-background-active:#ffffff0d;--ifm-navbar-search-input-color:var(--ifm-color-white)}.navbar--primary{--ifm-navbar-background-color:var(--ifm-color-primary);--ifm-navbar-link-hover-color:var(--ifm-color-white);--ifm-menu-color-active:var(--ifm-color-white);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-500)}.navbar__search-input{appearance:none;background:var(--ifm-navbar-search-input-background-color) var(--ifm-navbar-search-input-icon) no-repeat .75rem center/1rem 1rem;border:none;border-radius:2rem;color:var(--ifm-navbar-search-input-color);cursor:text;display:inline-block;font-size:.9rem;height:2rem;padding:0 .5rem 0 2.25rem;width:12.5rem}.navbar__search-input::placeholder{color:var(--ifm-navbar-search-input-placeholder-color)}.navbar-sidebar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-global-shadow-md);transform:translate3d(-100%,0,0);transition-property:opacity,visibility,transform;width:var(--ifm-navbar-sidebar-width)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar__items{transform:translateZ(0)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar--show .navbar-sidebar__backdrop{opacity:1;visibility:visible}.navbar-sidebar__backdrop{background-color:#0009;right:0;transition-property:opacity,visibility}.navbar-sidebar__brand{align-items:center;box-shadow:var(--ifm-navbar-shadow);display:flex;flex:1;height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal)}.navbar-sidebar__items{display:flex;height:calc(100% - var(--ifm-navbar-height));transition:transform var(--ifm-transition-fast) ease-in-out}.navbar-sidebar__items--show-secondary{transform:translate3d(calc((var(--ifm-navbar-sidebar-width))*-1),0,0)}.navbar-sidebar__item{flex-shrink:0;padding:.5rem;width:calc(var(--ifm-navbar-sidebar-width))}.navbar-sidebar__back{background:var(--ifm-menu-color-background-active);font-size:15px;font-weight:var(--ifm-button-font-weight);margin:0 0 .2rem -.5rem;padding:.6rem 1.5rem;position:relative;text-align:left;top:-.5rem;width:calc(100% + 1rem)}.navbar-sidebar__close{display:flex;margin-left:auto}.pagination{column-gap:var(--ifm-pagination-page-spacing);display:flex;font-size:var(--ifm-pagination-font-size);padding-left:0}.pagination--sm{--ifm-pagination-font-size:0.8rem;--ifm-pagination-padding-horizontal:0.8rem;--ifm-pagination-padding-vertical:0.2rem}.pagination--lg{--ifm-pagination-font-size:1.2rem;--ifm-pagination-padding-horizontal:1.2rem;--ifm-pagination-padding-vertical:0.3rem}.pagination__item{display:inline-flex}.pagination__item>span{padding:var(--ifm-pagination-padding-vertical)}.pagination__item--active .pagination__link{color:var(--ifm-pagination-color-active)}.pagination__item--active .pagination__link,.pagination__item:not(.pagination__item--active):hover .pagination__link{background:var(--ifm-pagination-item-active-background)}.pagination__item--disabled,.pagination__item[disabled]{opacity:.25;pointer-events:none}.pagination__link{border-radius:var(--ifm-pagination-border-radius);color:var(--ifm-font-color-base);display:inline-block;padding:var(--ifm-pagination-padding-vertical) var(--ifm-pagination-padding-horizontal);transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pagination__link:hover{text-decoration:none}.pagination-nav{grid-gap:var(--ifm-spacing-horizontal);display:grid;gap:var(--ifm-spacing-horizontal);grid-template-columns:repeat(2,1fr)}.pagination-nav__link{border:1px solid var(--ifm-color-emphasis-300);border-radius:var(--ifm-pagination-nav-border-radius);display:block;height:100%;line-height:var(--ifm-heading-line-height);padding:var(--ifm-global-spacing);transition:border-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pagination-nav__link:hover{border-color:var(--ifm-pagination-nav-color-hover);text-decoration:none}.pagination-nav__link--next{grid-column:2/3;text-align:right}.pagination-nav__label{font-size:var(--ifm-h4-font-size);font-weight:var(--ifm-heading-font-weight);word-break:break-word}.pagination-nav__link--prev .pagination-nav__label:before{content:"« "}.pagination-nav__link--next .pagination-nav__label:after{content:" »"}.pagination-nav__sublabel{color:var(--ifm-color-content-secondary);font-size:var(--ifm-h5-font-size);font-weight:var(--ifm-font-weight-semibold);margin-bottom:.25rem}.pills__item,.tabs{font-weight:var(--ifm-font-weight-bold)}.pills{display:flex;gap:var(--ifm-pills-spacing);padding-left:0}.pills__item{border-radius:.5rem;cursor:pointer;display:inline-block;padding:.25rem 1rem;transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.tabs,:not(.containsTaskList_mC6p>li)>.containsTaskList_mC6p{padding-left:0}.pills__item--active{color:var(--ifm-pills-color-active)}.pills__item--active,.pills__item:not(.pills__item--active):hover{background:var(--ifm-pills-color-background-active)}.pills--block{justify-content:stretch}.pills--block .pills__item{flex-grow:1;text-align:center}.tabs{color:var(--ifm-tabs-color);display:flex;margin-bottom:0;overflow-x:auto}.tabs__item{border-bottom:3px solid #0000;border-radius:var(--ifm-global-radius);cursor:pointer;display:inline-flex;padding:var(--ifm-tabs-padding-vertical) var(--ifm-tabs-padding-horizontal);transition:background-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.tabs__item--active{border-bottom-color:var(--ifm-tabs-color-active-border);border-bottom-left-radius:0;border-bottom-right-radius:0;color:var(--ifm-tabs-color-active)}.tabs__item:hover{background-color:var(--ifm-hover-overlay)}.tabs--block{justify-content:stretch}.tabs--block .tabs__item{flex-grow:1;justify-content:center}html[data-theme=dark]{--ifm-color-scheme:dark;--ifm-color-emphasis-0:var(--ifm-color-gray-1000);--ifm-color-emphasis-100:var(--ifm-color-gray-900);--ifm-color-emphasis-200:var(--ifm-color-gray-800);--ifm-color-emphasis-300:var(--ifm-color-gray-700);--ifm-color-emphasis-400:var(--ifm-color-gray-600);--ifm-color-emphasis-600:var(--ifm-color-gray-400);--ifm-color-emphasis-700:var(--ifm-color-gray-300);--ifm-color-emphasis-800:var(--ifm-color-gray-200);--ifm-color-emphasis-900:var(--ifm-color-gray-100);--ifm-color-emphasis-1000:var(--ifm-color-gray-0);--ifm-background-color:#1b1b1d;--ifm-background-surface-color:#242526;--ifm-hover-overlay:#ffffff0d;--ifm-color-content:#e3e3e3;--ifm-color-content-secondary:#fff;--ifm-breadcrumb-separator-filter:invert(64%) sepia(11%) saturate(0%) hue-rotate(149deg) brightness(99%) contrast(95%);--ifm-code-background:#ffffff1a;--ifm-scrollbar-track-background-color:#444;--ifm-scrollbar-thumb-background-color:#686868;--ifm-scrollbar-thumb-hover-background-color:#7a7a7a;--ifm-table-stripe-background:#ffffff12;--ifm-toc-border-color:var(--ifm-color-emphasis-200);--ifm-color-primary-contrast-background:#102445;--ifm-color-primary-contrast-foreground:#ebf2fc;--ifm-color-secondary-contrast-background:#474748;--ifm-color-secondary-contrast-foreground:#fdfdfe;--ifm-color-success-contrast-background:#003100;--ifm-color-success-contrast-foreground:#e6f6e6;--ifm-color-info-contrast-background:#193c47;--ifm-color-info-contrast-foreground:#eef9fd;--ifm-color-warning-contrast-background:#4d3800;--ifm-color-warning-contrast-foreground:#fff8e6;--ifm-color-danger-contrast-background:#4b1113;--ifm-color-danger-contrast-foreground:#ffebec}#nprogress .bar{background:var(--docusaurus-progress-bar-color);height:2px;left:0;position:fixed;top:0;width:100%;z-index:1031}#nprogress .peg{box-shadow:0 0 10px var(--docusaurus-progress-bar-color),0 0 5px var(--docusaurus-progress-bar-color);height:100%;opacity:1;position:absolute;right:0;transform:rotate(3deg) translateY(-4px);width:100px}[data-theme=dark]{--ifm-color-primary:#25c2a0;--ifm-color-primary-dark:#21af90;--ifm-color-primary-darker:#1fa588;--ifm-color-primary-darkest:#1a8870;--ifm-color-primary-light:#29d5b0;--ifm-color-primary-lighter:#32d8b4;--ifm-color-primary-lightest:#4fddbf;--docusaurus-highlighted-code-line-bg:#0000004d}.header-github-link:hover{opacity:.6}.header-github-link:before{background:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat;content:"";display:flex;height:24px;width:24px}html[data-theme=dark] .header-github-link:before{background:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23fff' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat}body:not(.navigation-with-keyboard) :not(input):focus{outline:0}#__docusaurus-base-url-issue-banner-container,.docSidebarContainer_YfHR,.sidebarLogo_isFc,.themedComponent_mlkZ,[data-theme=dark] .lightToggleIcon_pyhR,[data-theme=light] .darkToggleIcon_wfgR,html[data-announcement-bar-initially-dismissed=true] .announcementBar_mb4j{display:none}.skipToContent_fXgn{background-color:var(--ifm-background-surface-color);color:var(--ifm-color-emphasis-900);left:100%;padding:calc(var(--ifm-global-spacing)/2) var(--ifm-global-spacing);position:fixed;top:1rem;z-index:calc(var(--ifm-z-index-fixed) + 1)}.skipToContent_fXgn:focus{box-shadow:var(--ifm-global-shadow-md);left:1rem}.closeButton_CVFx{line-height:0;padding:0}.content_knG7{font-size:85%;padding:5px 0;text-align:center}.content_knG7 a{color:inherit;text-decoration:underline}.announcementBar_mb4j{align-items:center;background-color:var(--ifm-color-white);border-bottom:1px solid var(--ifm-color-emphasis-100);color:var(--ifm-color-black);display:flex;height:var(--docusaurus-announcement-bar-height)}.announcementBarPlaceholder_vyr4{flex:0 0 10px}.announcementBarClose_gvF7{align-self:stretch;flex:0 0 30px}.toggle_vylO{height:2rem;width:2rem}.toggleButton_gllP{align-items:center;border-radius:50%;display:flex;height:100%;justify-content:center;transition:background var(--ifm-transition-fast);width:100%}.toggleButton_gllP:hover{background:var(--ifm-color-emphasis-200)}.toggleButtonDisabled_aARS{cursor:not-allowed}.darkNavbarColorModeToggle_X3D1:hover{background:var(--ifm-color-gray-800)}.tag_zVej{border:1px solid var(--docusaurus-tag-list-border);transition:border var(--ifm-transition-fast)}.tag_zVej:hover{--docusaurus-tag-list-border:var(--ifm-link-color);text-decoration:none}.tagRegular_sFm0{border-radius:var(--ifm-global-radius);font-size:90%;padding:.2rem .5rem .3rem}.tagWithCount_h2kH{align-items:center;border-left:0;display:flex;padding:0 .5rem 0 1rem;position:relative}.tagWithCount_h2kH:after,.tagWithCount_h2kH:before{border:1px solid var(--docusaurus-tag-list-border);content:"";position:absolute;top:50%;transition:inherit}.tagWithCount_h2kH:before{border-bottom:0;border-right:0;height:1.18rem;right:100%;transform:translate(50%,-50%) rotate(-45deg);width:1.18rem}.tagWithCount_h2kH:after{border-radius:50%;height:.5rem;left:0;transform:translateY(-50%);width:.5rem}.tagWithCount_h2kH span{background:var(--ifm-color-secondary);border-radius:var(--ifm-global-radius);color:var(--ifm-color-black);font-size:.7rem;line-height:1.2;margin-left:.3rem;padding:.1rem .4rem}.tags_jXut{display:inline}.tag_QGVx{display:inline-block;margin:0 .4rem .5rem 0}.iconEdit_Z9Sw{margin-right:.3em;vertical-align:sub}.lastUpdated_JAkA{font-size:smaller;font-style:italic;margin-top:.2rem}.tocCollapsibleButton_TO0P{align-items:center;display:flex;font-size:inherit;justify-content:space-between;padding:.4rem .8rem;width:100%}.tocCollapsibleButton_TO0P:after{background:var(--ifm-menu-link-sublist-icon) 50% 50%/2rem 2rem no-repeat;content:"";filter:var(--ifm-menu-link-sublist-icon-filter);height:1.25rem;transform:rotate(180deg);transition:transform var(--ifm-transition-fast);width:1.25rem}.tocCollapsibleButtonExpanded_MG3E:after,.tocCollapsibleExpanded_sAul{transform:none}.tocCollapsible_ETCw{background-color:var(--ifm-menu-color-background-active);border-radius:var(--ifm-global-radius);margin:1rem 0}.tocCollapsibleContent_vkbj>ul{border-left:none;border-top:1px solid var(--ifm-color-emphasis-300);font-size:15px;padding:.2rem 0}.tocCollapsibleContent_vkbj ul li{margin:.4rem .8rem}.tocCollapsibleContent_vkbj a{display:block}.tableOfContents_bqdL{max-height:calc(100vh - var(--ifm-navbar-height) - 2rem);overflow-y:auto;position:sticky;top:calc(var(--ifm-navbar-height) + 1rem)}.backToTopButton_sjWU{background-color:var(--ifm-color-emphasis-200);border-radius:50%;bottom:1.3rem;box-shadow:var(--ifm-global-shadow-lw);height:3rem;opacity:0;position:fixed;right:1.3rem;transform:scale(0);transition:all var(--ifm-transition-fast) var(--ifm-transition-timing-default);visibility:hidden;width:3rem;z-index:calc(var(--ifm-z-index-fixed) - 1)}.backToTopButton_sjWU:after{background-color:var(--ifm-color-emphasis-1000);content:" ";display:inline-block;height:100%;-webkit-mask:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem no-repeat;mask:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem no-repeat;width:100%}.backToTopButtonShow_xfvO{opacity:1;transform:scale(1);visibility:visible}[data-theme=dark] .themedComponent--dark_xIcU,[data-theme=light] .themedComponent--light_NVdE,html:not([data-theme]) .themedComponent--light_NVdE{display:initial}[data-theme=dark]:root{--docusaurus-collapse-button-bg:#ffffff0d;--docusaurus-collapse-button-bg-hover:#ffffff1a}.collapseSidebarButton_PEFL{display:none;margin:0}.iconExternalLink_nPIU{margin-left:.3rem}.dropdownNavbarItemMobile_S0Fm{cursor:pointer}.iconLanguage_nlXk{margin-right:5px;vertical-align:text-bottom}@supports selector(:has(*)){.navbarSearchContainer_Bca1:not(:has(>*)){display:none}}.navbarHideable_m1mJ{transition:transform var(--ifm-transition-fast) ease}.navbarHidden_jGov{transform:translate3d(0,calc(-100% - 2px),0)}.errorBoundaryError_a6uf{color:red;white-space:pre-wrap}.errorBoundaryFallback_VBag{color:red;padding:.55rem}.buttonGroup__atx button,.codeBlockContainer_Ckt0{background:var(--prism-background-color);color:var(--prism-color)}.footerLogoLink_BH7S{opacity:.5;transition:opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.footerLogoLink_BH7S:hover,.hash-link:focus,:hover>.hash-link{opacity:1}.docMainContainer_TBSr,.docRoot_UBD9{display:flex;width:100%}.docsWrapper_hBAB{display:flex;flex:1 0 auto}.anchorWithStickyNavbar_LWe7{scroll-margin-top:calc(var(--ifm-navbar-height) + .5rem)}.anchorWithHideOnScrollNavbar_WYt5{scroll-margin-top:.5rem}.hash-link{opacity:0;padding-left:.5rem;transition:opacity var(--ifm-transition-fast);-webkit-user-select:none;user-select:none}.hash-link:before{content:"#"}.mainWrapper_z2l0{display:flex;flex:1 0 auto;flex-direction:column}.docusaurus-mt-lg{margin-top:3rem}#__docusaurus{display:flex;flex-direction:column;min-height:100%}.codeBlockContainer_Ckt0{border-radius:var(--ifm-code-border-radius);box-shadow:var(--ifm-global-shadow-lw);margin-bottom:var(--ifm-leading)}.codeBlockContent_biex{border-radius:inherit;direction:ltr;position:relative}.codeBlockTitle_Ktv7{border-bottom:1px solid var(--ifm-color-emphasis-300);border-top-left-radius:inherit;border-top-right-radius:inherit;font-size:var(--ifm-code-font-size);font-weight:500;padding:.75rem var(--ifm-pre-padding)}.codeBlock_bY9V{--ifm-pre-background:var(--prism-background-color);margin:0;padding:0}.codeBlockTitle_Ktv7+.codeBlockContent_biex .codeBlock_bY9V{border-top-left-radius:0;border-top-right-radius:0}.codeBlockLines_e6Vv{float:left;font:inherit;min-width:100%;padding:var(--ifm-pre-padding)}.codeBlockLinesWithNumbering_o6Pm{display:table;padding:var(--ifm-pre-padding) 0}.buttonGroup__atx{column-gap:.2rem;display:flex;position:absolute;right:calc(var(--ifm-pre-padding)/2);top:calc(var(--ifm-pre-padding)/2)}.buttonGroup__atx button{align-items:center;border:1px solid var(--ifm-color-emphasis-300);border-radius:var(--ifm-global-radius);display:flex;line-height:0;opacity:0;padding:.4rem;transition:opacity var(--ifm-transition-fast) ease-in-out}.buttonGroup__atx button:focus-visible,.buttonGroup__atx button:hover{opacity:1!important}.theme-code-block:hover .buttonGroup__atx button{opacity:.4}:where(:root){--docusaurus-highlighted-code-line-bg:#484d5b}:where([data-theme=dark]){--docusaurus-highlighted-code-line-bg:#646464}.theme-code-block-highlighted-line{background-color:var(--docusaurus-highlighted-code-line-bg);display:block;margin:0 calc(var(--ifm-pre-padding)*-1);padding:0 var(--ifm-pre-padding)}.codeLine_lJS_{counter-increment:a;display:table-row}.codeLineNumber_Tfdd{background:var(--ifm-pre-background);display:table-cell;left:0;overflow-wrap:normal;padding:0 var(--ifm-pre-padding);position:sticky;text-align:right;width:1%}.codeLineNumber_Tfdd:before{content:counter(a);opacity:.4}.codeLineContent_feaV{padding-right:var(--ifm-pre-padding)}.theme-code-block:hover .copyButtonCopied_obH4{opacity:1!important}.copyButtonIcons_eSgA{height:1.125rem;position:relative;width:1.125rem}.copyButtonIcon_y97N,.copyButtonSuccessIcon_LjdS{fill:currentColor;height:inherit;left:0;opacity:inherit;position:absolute;top:0;transition:all var(--ifm-transition-fast) ease;width:inherit}.copyButtonSuccessIcon_LjdS{color:#00d600;left:50%;opacity:0;top:50%;transform:translate(-50%,-50%) scale(.33)}.copyButtonCopied_obH4 .copyButtonIcon_y97N{opacity:0;transform:scale(.33)}.copyButtonCopied_obH4 .copyButtonSuccessIcon_LjdS{opacity:1;transform:translate(-50%,-50%) scale(1);transition-delay:75ms}.wordWrapButtonIcon_Bwma{height:1.2rem;width:1.2rem}.details_lb9f{--docusaurus-details-summary-arrow-size:0.38rem;--docusaurus-details-transition:transform 200ms ease;--docusaurus-details-decoration-color:grey}.details_lb9f>summary{cursor:pointer;padding-left:1rem;position:relative}.details_lb9f>summary::-webkit-details-marker{display:none}.details_lb9f>summary:before{border-color:#0000 #0000 #0000 var(--docusaurus-details-decoration-color);border-style:solid;border-width:var(--docusaurus-details-summary-arrow-size);content:"";left:0;position:absolute;top:.45rem;transform:rotate(0);transform-origin:calc(var(--docusaurus-details-summary-arrow-size)/2) 50%;transition:var(--docusaurus-details-transition)}.collapsibleContent_i85q{border-top:1px solid var(--docusaurus-details-decoration-color);margin-top:1rem;padding-top:1rem}.details_b_Ee{--docusaurus-details-decoration-color:var(--ifm-alert-border-color);--docusaurus-details-transition:transform var(--ifm-transition-fast) ease;border:1px solid var(--ifm-alert-border-color);margin:0 0 var(--ifm-spacing-vertical)}.img_ev3q{height:auto}.admonition_xJq3{margin-bottom:1em}.admonitionHeading_Gvgb{font:var(--ifm-heading-font-weight) var(--ifm-h5-font-size)/var(--ifm-heading-line-height) var(--ifm-heading-font-family)}.admonitionHeading_Gvgb:not(:last-child){margin-bottom:.3rem}.admonitionHeading_Gvgb code{text-transform:none}.admonitionIcon_Rf37{display:inline-block;margin-right:.4em;vertical-align:middle}.admonitionIcon_Rf37 svg{fill:var(--ifm-alert-foreground-color);display:inline-block;height:1.6em;width:1.6em}.breadcrumbHomeIcon_YNFT{height:1.1rem;position:relative;top:1px;vertical-align:top;width:1.1rem}.breadcrumbsContainer_Z_bl{--ifm-breadcrumb-size-multiplier:0.8;margin-bottom:.8rem}@media (min-width:997px){.collapseSidebarButton_PEFL,.expandButton_TmdG{background-color:var(--docusaurus-collapse-button-bg)}:root{--docusaurus-announcement-bar-height:30px}.announcementBarClose_gvF7,.announcementBarPlaceholder_vyr4{flex-basis:50px}.lastUpdated_JAkA{text-align:right}.tocMobile_ITEo{display:none}.collapseSidebarButton_PEFL{border:1px solid var(--ifm-toc-border-color);border-radius:0;bottom:0;display:block!important;height:40px;position:sticky}.collapseSidebarButtonIcon_kv0_{margin-top:4px;transform:rotate(180deg)}.expandButtonIcon_i1dp,[dir=rtl] .collapseSidebarButtonIcon_kv0_{transform:rotate(0)}.collapseSidebarButton_PEFL:focus,.collapseSidebarButton_PEFL:hover,.expandButton_TmdG:focus,.expandButton_TmdG:hover{background-color:var(--docusaurus-collapse-button-bg-hover)}.navbarSearchContainer_Bca1{padding:var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal)}.menuHtmlItem_M9Kj{padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal)}.menu_SIkG{flex-grow:1;padding:.5rem}@supports (scrollbar-gutter:stable){.menu_SIkG{padding:.5rem 0 .5rem .5rem;scrollbar-gutter:stable}}.menuWithAnnouncementBar_GW3s{margin-bottom:var(--docusaurus-announcement-bar-height)}.sidebar_njMd{display:flex;flex-direction:column;height:100%;padding-top:var(--ifm-navbar-height);width:var(--doc-sidebar-width)}.sidebarWithHideableNavbar_wUlq{padding-top:0}.sidebarHidden_VK0M{opacity:0;visibility:hidden}.sidebarLogo_isFc{align-items:center;color:inherit!important;display:flex!important;margin:0 var(--ifm-navbar-padding-horizontal);max-height:var(--ifm-navbar-height);min-height:var(--ifm-navbar-height);text-decoration:none!important}.sidebarLogo_isFc img{height:2rem;margin-right:.5rem}.expandButton_TmdG{align-items:center;display:flex;height:100%;justify-content:center;position:absolute;right:0;top:0;transition:background-color var(--ifm-transition-fast) ease;width:100%}[dir=rtl] .expandButtonIcon_i1dp{transform:rotate(180deg)}.docSidebarContainer_YfHR{border-right:1px solid var(--ifm-toc-border-color);clip-path:inset(0);display:block;margin-top:calc(var(--ifm-navbar-height)*-1);transition:width var(--ifm-transition-fast) ease;width:var(--doc-sidebar-width);will-change:width}.docSidebarContainerHidden_DPk8{cursor:pointer;width:var(--doc-sidebar-hidden-width)}.sidebarViewport_aRkj{height:100%;max-height:100vh;position:sticky;top:0}.docMainContainer_TBSr{flex-grow:1;max-width:calc(100% - var(--doc-sidebar-width))}.docMainContainerEnhanced_lQrH{max-width:calc(100% - var(--doc-sidebar-hidden-width))}.docItemWrapperEnhanced_JWYK{max-width:calc(var(--ifm-container-width) + var(--doc-sidebar-width))!important}.docItemCol_VOVn{max-width:75%!important}}@media (min-width:1440px){.container{max-width:var(--ifm-container-width-xl)}}@media (max-width:996px){.col{--ifm-col-width:100%;flex-basis:var(--ifm-col-width);margin-left:0}.footer{--ifm-footer-padding-horizontal:0}.colorModeToggle_DEke,.footer__link-separator,.navbar__item,.tableOfContents_bqdL{display:none}.footer__col{margin-bottom:calc(var(--ifm-spacing-vertical)*3)}.footer__link-item{display:block}.hero{padding-left:0;padding-right:0}.navbar>.container,.navbar>.container-fluid{padding:0}.navbar__toggle{display:inherit}.navbar__search-input{width:9rem}.pills--block,.tabs--block{flex-direction:column}.docItemContainer_F8PC{padding:0 .3rem}.navbarSearchContainer_Bca1{position:absolute;right:var(--ifm-navbar-padding-horizontal)}}@media (max-width:576px){.markdown h1:first-child{--ifm-h1-font-size:2rem}.markdown>h2{--ifm-h2-font-size:1.5rem}.markdown>h3{--ifm-h3-font-size:1.25rem}}@media (hover:hover){.backToTopButton_sjWU:hover{background-color:var(--ifm-color-emphasis-300)}}@media (pointer:fine){.thin-scrollbar{scrollbar-width:thin}.thin-scrollbar::-webkit-scrollbar{height:var(--ifm-scrollbar-size);width:var(--ifm-scrollbar-size)}.thin-scrollbar::-webkit-scrollbar-track{background:var(--ifm-scrollbar-track-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb{background:var(--ifm-scrollbar-thumb-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb:hover{background:var(--ifm-scrollbar-thumb-hover-background-color)}}@media (prefers-reduced-motion:reduce){:root{--ifm-transition-fast:0ms;--ifm-transition-slow:0ms}}@media print{.announcementBar_mb4j,.footer,.menu,.navbar,.pagination-nav,.table-of-contents,.tocMobile_ITEo{display:none}.tabs{page-break-inside:avoid}.codeBlockLines_e6Vv{white-space:pre-wrap}} \ No newline at end of file diff --git a/website/assets/js/040fbf97.7c02e0ce.js b/website/assets/js/040fbf97.7c02e0ce.js deleted file mode 100644 index 0d4bebe0..00000000 --- a/website/assets/js/040fbf97.7c02e0ce.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1201],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>b});var r=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var c=r.createContext({}),s=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(c.Provider,{value:t},e.children)},p="mdxType",g={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,c=e.parentName,u=a(e,["components","mdxType","originalType","parentName"]),p=s(n),f=i,b=p["".concat(c,".").concat(f)]||p[f]||g[f]||o;return n?r.createElement(b,l(l({ref:t},u),{},{components:n})):r.createElement(b,l({ref:t},u))}));function b(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,l=new Array(o);l[0]=f;var a={};for(var c in t)hasOwnProperty.call(t,c)&&(a[c]=t[c]);a.originalType=e,a[p]="string"==typeof e?e:i,l[1]=a;for(var s=2;s{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>l,default:()=>p,frontMatter:()=>o,metadata:()=>a,toc:()=>s});var r=n(7462),i=(n(7294),n(3905));const o={title:"Troubleshooting"},l=void 0,a={unversionedId:"troubleshooting",id:"version-v0.4.x/troubleshooting",title:"Troubleshooting",description:"Filtering Vulnerabilities",source:"@site/versioned_docs/version-v0.4.x/troubleshooting.md",sourceDirName:".",slug:"/troubleshooting",permalink:"/copacetic/website/v0.4.x/troubleshooting",draft:!1,tags:[],version:"v0.4.x",frontMatter:{title:"Troubleshooting"},sidebar:"sidebar",previous:{title:"Quick Start",permalink:"/copacetic/website/v0.4.x/quick-start"},next:{title:"Design",permalink:"/copacetic/website/v0.4.x/design"}},c={},s=[{value:"Filtering Vulnerabilities",id:"filtering-vulnerabilities",level:2},{value:"Rego Policy",id:"rego-policy",level:3},{value:"Ignore File",id:"ignore-file",level:3}],u={toc:s};function p(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h2",{id:"filtering-vulnerabilities"},"Filtering Vulnerabilities"),(0,i.kt)("p",null,"You might want to filter/ignore some of the vulnerabilities while patching. To do so, you need to first filter those undesired vulnerabilities from your scanner output."),(0,i.kt)("p",null,"For Trivy, vulnerabilities can be filtered by the following 2 ways:"),(0,i.kt)("h3",{id:"rego-policy"},"Rego Policy"),(0,i.kt)("p",null,"An example rego file which demonstrates how to ignore certain Vulnerability IDs or Package Names:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},'$ cat trivy_ignore.rego\n\npackage trivy\n\nimport data.lib.trivy\n\ndefault ignore = false\n\n\n# Ignore the following Vulnerability IDs\nignore_vulnerability_ids := {\n "CVE-2018-14618"\n}\n# Ignore the following Package Names\nignore_pkgs := {"bash", "vim"}\n\n\n# For ignoring vulnID\nignore {\n input.VulnerabilityID == ignore_vulnerability_ids[_]\n}\n# For ignoring pkgName\nignore {\n input.PkgName == ignore_pkgs[_]\n}\n\n')),(0,i.kt)("p",null,"After adding the above rego file, run the image scan with the ",(0,i.kt)("inlineCode",{parentName:"p"},"--ignore-policy")," flag followed by the file name to ignore them while scanning:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --ignore-policy trivy_ignore.rego ruby:2.4.0\n")),(0,i.kt)("p",null,'In the above example, the vulnerability "CVE-2018-14618" and the packages "bash" & "vim" are ignored while scanning, and hence patching the image.'),(0,i.kt)("h3",{id:"ignore-file"},"Ignore File"),(0,i.kt)("p",null,"Use a ",(0,i.kt)("inlineCode",{parentName:"p"},".trivyignore")," file to list all the vulnerabilities you want to ignore."),(0,i.kt)("p",null,"Example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"$ cat .trivyignore\n\n# Accept the risk\nCVE-2018-14618\n")),(0,i.kt)("p",null,"In the above example, the vulnerability CVE-2018-14618 is ignored while scanning, and hence while patching the image."),(0,i.kt)("p",null,"For a more detailed explanation on how to ignore certain vulnerabilities with Trivy, please refer to the official documentation ",(0,i.kt)("a",{parentName:"p",href:"https://aquasecurity.github.io/trivy/v0.44/docs/configuration/filtering/"},"here"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/040fbf97.ec4b04b2.js b/website/assets/js/040fbf97.ec4b04b2.js new file mode 100644 index 00000000..ec068a91 --- /dev/null +++ b/website/assets/js/040fbf97.ec4b04b2.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3105],{7478:(e,i,n)=>{n.r(i),n.d(i,{assets:()=>s,contentTitle:()=>l,default:()=>h,frontMatter:()=>o,metadata:()=>a,toc:()=>c});var t=n(4848),r=n(8453);const o={title:"Troubleshooting"},l=void 0,a={id:"troubleshooting",title:"Troubleshooting",description:"Filtering Vulnerabilities",source:"@site/versioned_docs/version-v0.4.x/troubleshooting.md",sourceDirName:".",slug:"/troubleshooting",permalink:"/copacetic/website/v0.4.x/troubleshooting",draft:!1,unlisted:!1,tags:[],version:"v0.4.x",frontMatter:{title:"Troubleshooting"},sidebar:"sidebar",previous:{title:"Quick Start",permalink:"/copacetic/website/v0.4.x/quick-start"},next:{title:"Design",permalink:"/copacetic/website/v0.4.x/design"}},s={},c=[{value:"Filtering Vulnerabilities",id:"filtering-vulnerabilities",level:2},{value:"Rego Policy",id:"rego-policy",level:3},{value:"Ignore File",id:"ignore-file",level:3}];function g(e){const i={a:"a",code:"code",h2:"h2",h3:"h3",p:"p",pre:"pre",...(0,r.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i.h2,{id:"filtering-vulnerabilities",children:"Filtering Vulnerabilities"}),"\n",(0,t.jsx)(i.p,{children:"You might want to filter/ignore some of the vulnerabilities while patching. To do so, you need to first filter those undesired vulnerabilities from your scanner output."}),"\n",(0,t.jsx)(i.p,{children:"For Trivy, vulnerabilities can be filtered by the following 2 ways:"}),"\n",(0,t.jsx)(i.h3,{id:"rego-policy",children:"Rego Policy"}),"\n",(0,t.jsx)(i.p,{children:"An example rego file which demonstrates how to ignore certain Vulnerability IDs or Package Names:"}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-bash",children:'$ cat trivy_ignore.rego\n\npackage trivy\n\nimport data.lib.trivy\n\ndefault ignore = false\n\n\n# Ignore the following Vulnerability IDs\nignore_vulnerability_ids := {\n "CVE-2018-14618"\n}\n# Ignore the following Package Names\nignore_pkgs := {"bash", "vim"}\n\n\n# For ignoring vulnID\nignore {\n input.VulnerabilityID == ignore_vulnerability_ids[_]\n}\n# For ignoring pkgName\nignore {\n\tinput.PkgName == ignore_pkgs[_]\n}\n\n'})}),"\n",(0,t.jsxs)(i.p,{children:["After adding the above rego file, run the image scan with the ",(0,t.jsx)(i.code,{children:"--ignore-policy"})," flag followed by the file name to ignore them while scanning:"]}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-bash",children:"trivy image --ignore-policy trivy_ignore.rego ruby:2.4.0\n"})}),"\n",(0,t.jsx)(i.p,{children:'In the above example, the vulnerability "CVE-2018-14618" and the packages "bash" & "vim" are ignored while scanning, and hence patching the image.'}),"\n",(0,t.jsx)(i.h3,{id:"ignore-file",children:"Ignore File"}),"\n",(0,t.jsxs)(i.p,{children:["Use a ",(0,t.jsx)(i.code,{children:".trivyignore"})," file to list all the vulnerabilities you want to ignore."]}),"\n",(0,t.jsx)(i.p,{children:"Example:"}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-bash",children:"$ cat .trivyignore\n\n# Accept the risk\nCVE-2018-14618\n"})}),"\n",(0,t.jsx)(i.p,{children:"In the above example, the vulnerability CVE-2018-14618 is ignored while scanning, and hence while patching the image."}),"\n",(0,t.jsxs)(i.p,{children:["For a more detailed explanation on how to ignore certain vulnerabilities with Trivy, please refer to the official documentation ",(0,t.jsx)(i.a,{href:"https://aquasecurity.github.io/trivy/v0.44/docs/configuration/filtering/",children:"here"}),"."]})]})}function h(e={}){const{wrapper:i}={...(0,r.R)(),...e.components};return i?(0,t.jsx)(i,{...e,children:(0,t.jsx)(g,{...e})}):g(e)}},8453:(e,i,n)=>{n.d(i,{R:()=>l,x:()=>a});var t=n(6540);const r={},o=t.createContext(r);function l(e){const i=t.useContext(o);return t.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function a(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:l(e.components),t.createElement(o.Provider,{value:i},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/0480b142.00de69f7.js b/website/assets/js/0480b142.00de69f7.js new file mode 100644 index 00000000..509e83df --- /dev/null +++ b/website/assets/js/0480b142.00de69f7.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8070],{8614:(e,i,a)=>{a.r(i),a.d(i,{assets:()=>r,contentTitle:()=>c,default:()=>d,frontMatter:()=>o,metadata:()=>s,toc:()=>l});var n=a(4848),t=a(8453);const o={title:"FAQ"},c=void 0,s={id:"faq",title:"FAQ",description:"What kind of vulnerabilities can Copa patch?",source:"@site/docs/faq.md",sourceDirName:".",slug:"/faq",permalink:"/copacetic/website/next/faq",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{title:"FAQ"},sidebar:"sidebar",previous:{title:"Troubleshooting",permalink:"/copacetic/website/next/troubleshooting"},next:{title:"Github Action",permalink:"/copacetic/website/next/github-action"}},r={},l=[{value:"What kind of vulnerabilities can Copa patch?",id:"what-kind-of-vulnerabilities-can-copa-patch",level:2},{value:"What kind of vulnerabilities can Copa not patch?",id:"what-kind-of-vulnerabilities-can-copa-not-patch",level:2},{value:"Can I replace the package repositories in the image with my own?",id:"can-i-replace-the-package-repositories-in-the-image-with-my-own",level:2}];function p(e){const i={a:"a",admonition:"admonition",blockquote:"blockquote",code:"code",h2:"h2",p:"p",pre:"pre",...(0,t.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(i.h2,{id:"what-kind-of-vulnerabilities-can-copa-patch",children:"What kind of vulnerabilities can Copa patch?"}),"\n",(0,n.jsxs)(i.p,{children:['Copa is capable of patching "OS level" vulnerabilities. This includes packages (like ',(0,n.jsx)(i.code,{children:"openssl"}),") in the image that are managed by a package manager such as ",(0,n.jsx)(i.code,{children:"apt"})," or ",(0,n.jsx)(i.code,{children:"yum"}),'. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see ',(0,n.jsx)(i.a,{href:"#what-kind-of-vulnerabilities-can-copa-not-patch",children:"below"})," for more details)."]}),"\n",(0,n.jsx)(i.h2,{id:"what-kind-of-vulnerabilities-can-copa-not-patch",children:"What kind of vulnerabilities can Copa not patch?"}),"\n",(0,n.jsxs)(i.p,{children:['Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the ',(0,n.jsx)(i.code,{children:"golang.org/x/net"})," module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level."]}),"\n",(0,n.jsxs)(i.p,{children:["To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like ",(0,n.jsx)(i.code,{children:"http://archive.ubuntu.com/ubuntu/"})," for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications."]}),"\n",(0,n.jsx)(i.h2,{id:"can-i-replace-the-package-repositories-in-the-image-with-my-own",children:"Can I replace the package repositories in the image with my own?"}),"\n",(0,n.jsx)(i.admonition,{type:"caution",children:(0,n.jsx)(i.p,{children:"Experimental: This feature might change without preserving backwards compatibility."})}),"\n",(0,n.jsxs)(i.p,{children:["Copa does not support replacing the repositories in the package managers with alternatives. Images must already use the intended package repositories. For example, for debian, updating ",(0,n.jsx)(i.code,{children:"/etc/apt/sources.list"})," from ",(0,n.jsx)(i.code,{children:"http://archive.ubuntu.com/ubuntu/"})," to a mirror, such as ",(0,n.jsx)(i.code,{children:"https://mirrors.wikimedia.org/ubuntu/"}),"."]}),"\n",(0,n.jsxs)(i.p,{children:["If you need the tooling image to use a different package repository, you can create a source policy to define a replacement image and/or pin to a digest. For example, the following source policy replaces ",(0,n.jsx)(i.code,{children:"docker.io/library/debian:11-slim"})," image with ",(0,n.jsx)(i.code,{children:"foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5"}),":"]}),"\n",(0,n.jsx)(i.pre,{children:(0,n.jsx)(i.code,{className:"language-shell",children:'cat < source-policy.json\n{\n "rules": [\n {\n "action": "CONVERT",\n "selector": {\n "identifier": "docker-image://docker.io/library/debian:11-slim"\n },\n "updates": {\n "identifier": "docker-image://foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5"\n }\n }\n ]\n}\nEOF\n\nexport EXPERIMENTAL_BUILDKIT_SOURCE_POLICY=source-policy.json\n'})}),"\n",(0,n.jsxs)(i.blockquote,{children:["\n",(0,n.jsxs)(i.p,{children:["Tooling image for Debian-based images are ",(0,n.jsx)(i.code,{children:"docker.io/library/debian:11-slim"})," and RPM-based repos are ",(0,n.jsx)(i.code,{children:"mcr.microsoft.com/cbl-mariner/base/core:2.0"}),"."]}),"\n"]}),"\n",(0,n.jsxs)(i.p,{children:["For more information on source policies, see ",(0,n.jsx)(i.a,{href:"https://docs.docker.com/build/building/env-vars/#experimental_buildkit_source_policy",children:"Buildkit Source Policies"}),"."]})]})}function d(e={}){const{wrapper:i}={...(0,t.R)(),...e.components};return i?(0,n.jsx)(i,{...e,children:(0,n.jsx)(p,{...e})}):p(e)}},8453:(e,i,a)=>{a.d(i,{R:()=>c,x:()=>s});var n=a(6540);const t={},o=n.createContext(t);function c(e){const i=n.useContext(o);return n.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function s(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:c(e.components),n.createElement(o.Provider,{value:i},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/0480b142.7aa8457b.js b/website/assets/js/0480b142.7aa8457b.js deleted file mode 100644 index e3e7f9be..00000000 --- a/website/assets/js/0480b142.7aa8457b.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[836],{3905:(e,t,a)=>{a.d(t,{Zo:()=>s,kt:()=>h});var n=a(7294);function i(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function o(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function r(e){for(var t=1;t=0||(i[a]=e[a]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(i[a]=e[a])}return i}var c=n.createContext({}),p=function(e){var t=n.useContext(c),a=t;return e&&(a="function"==typeof e?e(t):r(r({},t),e)),a},s=function(e){var t=p(e.components);return n.createElement(c.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var a=e.components,i=e.mdxType,o=e.originalType,c=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),u=p(a),m=i,h=u["".concat(c,".").concat(m)]||u[m]||d[m]||o;return a?n.createElement(h,r(r({ref:t},s),{},{components:a})):n.createElement(h,r({ref:t},s))}));function h(e,t){var a=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=a.length,r=new Array(o);r[0]=m;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[u]="string"==typeof e?e:i,r[1]=l;for(var p=2;p{a.r(t),a.d(t,{assets:()=>c,contentTitle:()=>r,default:()=>u,frontMatter:()=>o,metadata:()=>l,toc:()=>p});var n=a(7462),i=(a(7294),a(3905));const o={title:"FAQ"},r=void 0,l={unversionedId:"faq",id:"faq",title:"FAQ",description:"What kind of vulnerabilities can Copa patch?",source:"@site/docs/faq.md",sourceDirName:".",slug:"/faq",permalink:"/copacetic/website/next/faq",draft:!1,tags:[],version:"current",frontMatter:{title:"FAQ"},sidebar:"sidebar",previous:{title:"Troubleshooting",permalink:"/copacetic/website/next/troubleshooting"},next:{title:"Github Action",permalink:"/copacetic/website/next/github-action"}},c={},p=[{value:"What kind of vulnerabilities can Copa patch?",id:"what-kind-of-vulnerabilities-can-copa-patch",level:2},{value:"What kind of vulnerabilities can Copa not patch?",id:"what-kind-of-vulnerabilities-can-copa-not-patch",level:2},{value:"Can I replace the package repositories in the image with my own?",id:"can-i-replace-the-package-repositories-in-the-image-with-my-own",level:2}],s={toc:p};function u(e){let{components:t,...a}=e;return(0,i.kt)("wrapper",(0,n.Z)({},s,a,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h2",{id:"what-kind-of-vulnerabilities-can-copa-patch"},"What kind of vulnerabilities can Copa patch?"),(0,i.kt)("p",null,'Copa is capable of patching "OS level" vulnerabilities. This includes packages (like ',(0,i.kt)("inlineCode",{parentName:"p"},"openssl"),") in the image that are managed by a package manager such as ",(0,i.kt)("inlineCode",{parentName:"p"},"apt")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"yum"),'. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see ',(0,i.kt)("a",{parentName:"p",href:"#what-kind-of-vulnerabilities-can-copa-not-patch"},"below")," for more details)."),(0,i.kt)("h2",{id:"what-kind-of-vulnerabilities-can-copa-not-patch"},"What kind of vulnerabilities can Copa not patch?"),(0,i.kt)("p",null,'Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the ',(0,i.kt)("inlineCode",{parentName:"p"},"golang.org/x/net")," module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level."),(0,i.kt)("p",null,"To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like ",(0,i.kt)("inlineCode",{parentName:"p"},"http://archive.ubuntu.com/ubuntu/")," for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications."),(0,i.kt)("h2",{id:"can-i-replace-the-package-repositories-in-the-image-with-my-own"},"Can I replace the package repositories in the image with my own?"),(0,i.kt)("admonition",{type:"caution"},(0,i.kt)("p",{parentName:"admonition"},"Experimental: This feature might change without preserving backwards compatibility.")),(0,i.kt)("p",null,"Copa does not support replacing the repositories in the package managers with alternatives. Images must already use the intended package repositories. For example, for debian, updating ",(0,i.kt)("inlineCode",{parentName:"p"},"/etc/apt/sources.list")," from ",(0,i.kt)("inlineCode",{parentName:"p"},"http://archive.ubuntu.com/ubuntu/")," to a mirror, such as ",(0,i.kt)("inlineCode",{parentName:"p"},"https://mirrors.wikimedia.org/ubuntu/"),"."),(0,i.kt)("p",null,"If you need the tooling image to use a different package repository, you can create a source policy to define a replacement image and/or pin to a digest. For example, the following source policy replaces ",(0,i.kt)("inlineCode",{parentName:"p"},"docker.io/library/debian:11-slim")," image with ",(0,i.kt)("inlineCode",{parentName:"p"},"foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-shell"},'cat < source-policy.json\n{\n "rules": [\n {\n "action": "CONVERT",\n "selector": {\n "identifier": "docker-image://docker.io/library/debian:11-slim"\n },\n "updates": {\n "identifier": "docker-image://foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5"\n }\n }\n ]\n}\nEOF\n\nexport EXPERIMENTAL_BUILDKIT_SOURCE_POLICY=source-policy.json\n')),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},"Tooling image for Debian-based images are ",(0,i.kt)("inlineCode",{parentName:"p"},"docker.io/library/debian:11-slim")," and RPM-based repos are ",(0,i.kt)("inlineCode",{parentName:"p"},"mcr.microsoft.com/cbl-mariner/base/core:2.0"),".")),(0,i.kt)("p",null,"For more information on source policies, see ",(0,i.kt)("a",{parentName:"p",href:"https://docs.docker.com/build/building/env-vars/#experimental_buildkit_source_policy"},"Buildkit Source Policies"),"."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/053b5658.2fa8aa35.js b/website/assets/js/053b5658.2fa8aa35.js new file mode 100644 index 00000000..219b49a9 --- /dev/null +++ b/website/assets/js/053b5658.2fa8aa35.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3831],{8243:(e,a,t)=>{t.r(a),t.d(a,{assets:()=>l,contentTitle:()=>c,default:()=>h,frontMatter:()=>o,metadata:()=>s,toc:()=>r});var i=t(4848),n=t(8453);const o={title:"FAQ"},c=void 0,s={id:"faq",title:"FAQ",description:"What kind of vulnerabilities can Copa patch?",source:"@site/versioned_docs/version-v0.1.x/faq.md",sourceDirName:".",slug:"/faq",permalink:"/copacetic/website/v0.1.x/faq",draft:!1,unlisted:!1,tags:[],version:"v0.1.x",frontMatter:{title:"FAQ"},sidebar:"sidebar",previous:{title:"Design",permalink:"/copacetic/website/v0.1.x/design"},next:{title:"Contributing",permalink:"/copacetic/website/v0.1.x/contributing"}},l={},r=[{value:"What kind of vulnerabilities can Copa patch?",id:"what-kind-of-vulnerabilities-can-copa-patch",level:2},{value:"What kind of vulnerabilities can Copa not patch?",id:"what-kind-of-vulnerabilities-can-copa-not-patch",level:2}];function p(e){const a={a:"a",code:"code",h2:"h2",p:"p",...(0,n.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(a.h2,{id:"what-kind-of-vulnerabilities-can-copa-patch",children:"What kind of vulnerabilities can Copa patch?"}),"\n",(0,i.jsxs)(a.p,{children:['Copa is capable of patching "OS level" vulnerabilities. This includes packages (like ',(0,i.jsx)(a.code,{children:"openssl"}),") in the image that are managed by a package manager such as ",(0,i.jsx)(a.code,{children:"apt"})," or ",(0,i.jsx)(a.code,{children:"yum"}),'. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see ',(0,i.jsx)(a.a,{href:"#what-kind-of-vulnerabilities-can-copa-not-patch",children:"below"})," for more details)."]}),"\n",(0,i.jsx)(a.h2,{id:"what-kind-of-vulnerabilities-can-copa-not-patch",children:"What kind of vulnerabilities can Copa not patch?"}),"\n",(0,i.jsxs)(a.p,{children:['Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the ',(0,i.jsx)(a.code,{children:"golang.org/x/net"})," module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level."]}),"\n",(0,i.jsxs)(a.p,{children:["To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like ",(0,i.jsx)(a.code,{children:"http://archive.ubuntu.com/ubuntu/"})," for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications."]})]})}function h(e={}){const{wrapper:a}={...(0,n.R)(),...e.components};return a?(0,i.jsx)(a,{...e,children:(0,i.jsx)(p,{...e})}):p(e)}},8453:(e,a,t)=>{t.d(a,{R:()=>c,x:()=>s});var i=t(6540);const n={},o=i.createContext(n);function c(e){const a=i.useContext(o);return i.useMemo((function(){return"function"==typeof e?e(a):{...a,...e}}),[a,e])}function s(e){let a;return a=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:c(e.components),i.createElement(o.Provider,{value:a},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/053b5658.6494c787.js b/website/assets/js/053b5658.6494c787.js deleted file mode 100644 index a6d2818f..00000000 --- a/website/assets/js/053b5658.6494c787.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[335],{3905:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>d});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=a.createContext({}),p=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},s=function(e){var t=p(e.components);return a.createElement(l.Provider,{value:t},e.children)},u="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},h=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,l=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),u=p(n),h=i,d=u["".concat(l,".").concat(h)]||u[h]||f[h]||r;return n?a.createElement(d,o(o({ref:t},s),{},{components:n})):a.createElement(d,o({ref:t},s))}));function d(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=h;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[u]="string"==typeof e?e:i,o[1]=c;for(var p=2;p{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>c,toc:()=>p});var a=n(7462),i=(n(7294),n(3905));const r={title:"FAQ"},o=void 0,c={unversionedId:"faq",id:"version-v0.1.x/faq",title:"FAQ",description:"What kind of vulnerabilities can Copa patch?",source:"@site/versioned_docs/version-v0.1.x/faq.md",sourceDirName:".",slug:"/faq",permalink:"/copacetic/website/v0.1.x/faq",draft:!1,tags:[],version:"v0.1.x",frontMatter:{title:"FAQ"},sidebar:"sidebar",previous:{title:"Design",permalink:"/copacetic/website/v0.1.x/design"},next:{title:"Contributing",permalink:"/copacetic/website/v0.1.x/contributing"}},l={},p=[{value:"What kind of vulnerabilities can Copa patch?",id:"what-kind-of-vulnerabilities-can-copa-patch",level:2},{value:"What kind of vulnerabilities can Copa not patch?",id:"what-kind-of-vulnerabilities-can-copa-not-patch",level:2}],s={toc:p};function u(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h2",{id:"what-kind-of-vulnerabilities-can-copa-patch"},"What kind of vulnerabilities can Copa patch?"),(0,i.kt)("p",null,'Copa is capable of patching "OS level" vulnerabilities. This includes packages (like ',(0,i.kt)("inlineCode",{parentName:"p"},"openssl"),") in the image that are managed by a package manager such as ",(0,i.kt)("inlineCode",{parentName:"p"},"apt")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"yum"),'. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see ',(0,i.kt)("a",{parentName:"p",href:"#what-kind-of-vulnerabilities-can-copa-not-patch"},"below")," for more details)."),(0,i.kt)("h2",{id:"what-kind-of-vulnerabilities-can-copa-not-patch"},"What kind of vulnerabilities can Copa not patch?"),(0,i.kt)("p",null,'Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the ',(0,i.kt)("inlineCode",{parentName:"p"},"golang.org/x/net")," module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level."),(0,i.kt)("p",null,"To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like ",(0,i.kt)("inlineCode",{parentName:"p"},"http://archive.ubuntu.com/ubuntu/")," for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/05a474a1.a98f3ad4.js b/website/assets/js/05a474a1.a98f3ad4.js new file mode 100644 index 00000000..242340de --- /dev/null +++ b/website/assets/js/05a474a1.a98f3ad4.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2338],{6679:(e,t,i)=>{i.r(t),i.d(t,{assets:()=>a,contentTitle:()=>r,default:()=>h,frontMatter:()=>s,metadata:()=>o,toc:()=>l});var c=i(4848),n=i(8453);const s={title:"Release Process"},r=void 0,o={id:"release",title:"Release Process",description:"Overview",source:"@site/docs/release.md",sourceDirName:".",slug:"/release",permalink:"/copacetic/website/next/release",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{title:"Release Process"},sidebar:"sidebar",previous:{title:"Maintainer Guidelines",permalink:"/copacetic/website/next/maintainer-guidelines"}},a={},l=[{value:"Overview",id:"overview",level:2},{value:"Publishing",id:"publishing",level:2}];function p(e){const t={a:"a",code:"code",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",...(0,n.R)(),...e.components};return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(t.h2,{id:"overview",children:"Overview"}),"\n",(0,c.jsxs)(t.p,{children:["The release process for Copacetic uses ",(0,c.jsx)(t.a,{href:"https://goreleaser.com/",children:"GoReleaser"}),"."]}),"\n",(0,c.jsx)(t.p,{children:"Once you are ready to cut a new release, checkout the release branch and tag it with the respective version."}),"\n",(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{children:"git checkout \ngit pull origin \ngit tag -a -m ''\ngit push origin \n"})}),"\n",(0,c.jsx)(t.h2,{id:"publishing",children:"Publishing"}),"\n",(0,c.jsxs)(t.ol,{children:["\n",(0,c.jsxs)(t.li,{children:["GoReleaser will create a new release, review and edit it at ",(0,c.jsx)(t.a,{href:"https://github.com/project-copacetic/copacetic/releases",children:"https://github.com/project-copacetic/copacetic/releases"})]}),"\n",(0,c.jsxs)(t.li,{children:["Review the respective copa-action image at: ",(0,c.jsx)(t.a,{href:"https://github.com/orgs/project-copacetic/packages/container/package/copa-action",children:"https://github.com/orgs/project-copacetic/packages/container/package/copa-action"})]}),"\n"]})]})}function h(e={}){const{wrapper:t}={...(0,n.R)(),...e.components};return t?(0,c.jsx)(t,{...e,children:(0,c.jsx)(p,{...e})}):p(e)}},8453:(e,t,i)=>{i.d(t,{R:()=>r,x:()=>o});var c=i(6540);const n={},s=c.createContext(n);function r(e){const t=c.useContext(s);return c.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function o(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:r(e.components),c.createElement(s.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/05a474a1.af8883e8.js b/website/assets/js/05a474a1.af8883e8.js deleted file mode 100644 index 0f9141f4..00000000 --- a/website/assets/js/05a474a1.af8883e8.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4073],{3905:(e,t,r)=>{r.d(t,{Zo:()=>s,kt:()=>g});var n=r(7294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var l=n.createContext({}),p=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},s=function(e){var t=p(e.components);return n.createElement(l.Provider,{value:t},e.children)},u="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,i=e.originalType,l=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),u=p(r),m=a,g=u["".concat(l,".").concat(m)]||u[m]||f[m]||i;return r?n.createElement(g,o(o({ref:t},s),{},{components:r})):n.createElement(g,o({ref:t},s))}));function g(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=r.length,o=new Array(i);o[0]=m;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[u]="string"==typeof e?e:a,o[1]=c;for(var p=2;p{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>i,metadata:()=>c,toc:()=>p});var n=r(7462),a=(r(7294),r(3905));const i={title:"Release Process"},o=void 0,c={unversionedId:"release",id:"release",title:"Release Process",description:"Overview",source:"@site/docs/release.md",sourceDirName:".",slug:"/release",permalink:"/copacetic/website/next/release",draft:!1,tags:[],version:"current",frontMatter:{title:"Release Process"},sidebar:"sidebar",previous:{title:"Maintainer Guidelines",permalink:"/copacetic/website/next/maintainer-guidelines"}},l={},p=[{value:"Overview",id:"overview",level:2},{value:"Publishing",id:"publishing",level:2}],s={toc:p};function u(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},s,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h2",{id:"overview"},"Overview"),(0,a.kt)("p",null,"The release process for Copacetic uses ",(0,a.kt)("a",{parentName:"p",href:"https://goreleaser.com/"},"GoReleaser"),". "),(0,a.kt)("p",null,"Once you are ready to cut a new release, checkout the release branch and tag it with the respective version."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"```\ngit checkout \ngit pull origin \ngit tag -a -m ''\ngit push origin \n```\n")),(0,a.kt)("h2",{id:"publishing"},"Publishing"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"GoReleaser will create a new release, review and edit it at ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/project-copacetic/copacetic/releases"},"https://github.com/project-copacetic/copacetic/releases")),(0,a.kt)("li",{parentName:"ol"},"Review the respective copa-action image at: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/orgs/project-copacetic/packages/container/package/copa-action"},"https://github.com/orgs/project-copacetic/packages/container/package/copa-action"))))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/0a7bb60d.238534bf.js b/website/assets/js/0a7bb60d.238534bf.js deleted file mode 100644 index e02bcdc0..00000000 --- a/website/assets/js/0a7bb60d.238534bf.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9691],{3905:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>u});var i=a(7294);function n(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,i)}return a}function o(e){for(var t=1;t=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var l=i.createContext({}),p=function(e){var t=i.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},c=function(e){var t=p(e.components);return i.createElement(l.Provider,{value:t},e.children)},d="mdxType",g={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},h=i.forwardRef((function(e,t){var a=e.components,n=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=p(a),h=n,u=d["".concat(l,".").concat(h)]||d[h]||g[h]||r;return a?i.createElement(u,o(o({ref:t},c),{},{components:a})):i.createElement(u,o({ref:t},c))}));function u(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var r=a.length,o=new Array(r);o[0]=h;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[d]="string"==typeof e?e:n,o[1]=s;for(var p=2;p{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>d,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var i=a(7462),n=(a(7294),a(3905));const r={title:"Design"},o=void 0,s={unversionedId:"design",id:"version-v0.1.x/design",title:"Design",description:"Design Tenets",source:"@site/versioned_docs/version-v0.1.x/design.md",sourceDirName:".",slug:"/design",permalink:"/copacetic/website/v0.1.x/design",draft:!1,tags:[],version:"v0.1.x",frontMatter:{title:"Design"},sidebar:"sidebar",previous:{title:"Quick Start",permalink:"/copacetic/website/v0.1.x/quick-start"},next:{title:"FAQ",permalink:"/copacetic/website/v0.1.x/faq"}},l={},p=[{value:"Design Tenets",id:"design-tenets",level:2},{value:"Design Reasoning",id:"design-reasoning",level:2},{value:"Architecture",id:"architecture",level:2},{value:"Implementation",id:"implementation",level:2},{value:"Tradeoffs",id:"tradeoffs",level:2}],c={toc:p};function d(e){let{components:t,...a}=e;return(0,n.kt)("wrapper",(0,i.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h2",{id:"design-tenets"},"Design Tenets"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update.")," This is a raison d\u2019etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to work with the existing ecosystem of container images.")," The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to allow parties other than the image authors to address container vulnerabilities.")," Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to do one thing well and be composable with other tools and processes.")," Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane."))),(0,n.kt)("h2",{id:"design-reasoning"},"Design Reasoning"),(0,n.kt)("p",null,"The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/google/go-containerregistry/blob/main/cmd/crane/rebase.md"},(0,n.kt)("inlineCode",{parentName:"a"},"crane rebase")),":"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},"Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an ",(0,n.kt)("inlineCode",{parentName:"p"},"apt install")," during image creation will overwrite the dpkg ",(0,n.kt)("inlineCode",{parentName:"p"},"status")," file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners."),(0,n.kt)("p",{parentName:"li"},"To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},"Rebasing also requires that the user knows ",(0,n.kt)("em",{parentName:"p"},"a priori")," what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets."),(0,n.kt)("p",{parentName:"li"},"While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose."),(0,n.kt)("p",{parentName:"li"},"The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well."))),(0,n.kt)("h2",{id:"architecture"},"Architecture"),(0,n.kt)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),(0,n.kt)("p",null,"The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"The data schema of various vulnerability scanners producing the input vulnerability report."),(0,n.kt)("li",{parentName:"ul"},"The state management of various package managers and process for applying patches appropriately through them.")),(0,n.kt)("p",null,"Effectively, ",(0,n.kt)("inlineCode",{parentName:"p"},"copa patch")," can be considered a command that bridges an extensible ",(0,n.kt)("inlineCode",{parentName:"p"},"Parse")," action with an extensible ",(0,n.kt)("inlineCode",{parentName:"p"},"Apply")," action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-go"},"type UpdatePackage struct {\n Name string\n Version string\n}\n\ntype UpdateManifest struct {\n OSType string\n OSVersion string\n Arch string\n Updates []UpdatePackage\n}\n\ntype ScanReportParser interface {\n Parse(reportPath string) (*UpdateManifest, error)\n}\n\ntype PackageManager interface {\n Apply(imagePath string, report *UpdateManifest) error\n}\n")),(0,n.kt)("h2",{id:"implementation"},"Implementation"),(0,n.kt)("img",{title:"buildkit graph execution",src:"/copacetic/website/img/graph-execution.png"}),(0,n.kt)("p",null,(0,n.kt)("inlineCode",{parentName:"p"},"copa")," is a pseudo-frontend to ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit"},"buildkit")," implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit/tree/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4#exploring-llb"},"LLB graphs")," for buildkit to execute:"),(0,n.kt)("ol",null,(0,n.kt)("li",{parentName:"ol"},"Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions."))),(0,n.kt)("li",{parentName:"ol"},"Actions to fetch and deploy tools that can be injected into the target image to perform the patching.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches."),(0,n.kt)("li",{parentName:"ul"},"In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image."),(0,n.kt)("li",{parentName:"ul"},"Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well."))),(0,n.kt)("li",{parentName:"ol"},"Actions to deploy the required patch packages to the target image.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"copa")," integrates with buildkit at the API level because it uses the ",(0,n.kt)("a",{parentName:"li",href:"https://github.com/moby/buildkit/blob/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4/docs/merge%2Bdiff.md"},"diff and merge")," graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.")))),(0,n.kt)("h2",{id:"tradeoffs"},"Tradeoffs"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa."),(0,n.kt)("li",{parentName:"ul"},"copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this."))),(0,n.kt)("li",{parentName:"ul"},"No support for windows containers given the dependency on buildkit.")))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/0a7bb60d.bc8038bc.js b/website/assets/js/0a7bb60d.bc8038bc.js new file mode 100644 index 00000000..aceef7e7 --- /dev/null +++ b/website/assets/js/0a7bb60d.bc8038bc.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7173],{6353:(e,t,i)=>{i.r(t),i.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>h,frontMatter:()=>s,metadata:()=>r,toc:()=>l});var n=i(4848),a=i(8453);const s={title:"Design"},o=void 0,r={id:"design",title:"Design",description:"Design Tenets",source:"@site/versioned_docs/version-v0.1.x/design.md",sourceDirName:".",slug:"/design",permalink:"/copacetic/website/v0.1.x/design",draft:!1,unlisted:!1,tags:[],version:"v0.1.x",frontMatter:{title:"Design"},sidebar:"sidebar",previous:{title:"Quick Start",permalink:"/copacetic/website/v0.1.x/quick-start"},next:{title:"FAQ",permalink:"/copacetic/website/v0.1.x/faq"}},c={},l=[{value:"Design Tenets",id:"design-tenets",level:2},{value:"Design Reasoning",id:"design-reasoning",level:2},{value:"Architecture",id:"architecture",level:2},{value:"Implementation",id:"implementation",level:2},{value:"Tradeoffs",id:"tradeoffs",level:2}];function d(e){const t={a:"a",code:"code",em:"em",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(t.h2,{id:"design-tenets",children:"Design Tenets"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update."})," This is a raison d\u2019etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to work with the existing ecosystem of container images."})," The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to allow parties other than the image authors to address container vulnerabilities."})," Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to do one thing well and be composable with other tools and processes."})," Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane."]}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"design-reasoning",children:"Design Reasoning"}),"\n",(0,n.jsxs)(t.p,{children:["The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental ",(0,n.jsx)(t.a,{href:"https://github.com/google/go-containerregistry/blob/main/cmd/crane/rebase.md",children:(0,n.jsx)(t.code,{children:"crane rebase"})}),":"]}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:["Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an ",(0,n.jsx)(t.code,{children:"apt install"})," during image creation will overwrite the dpkg ",(0,n.jsx)(t.code,{children:"status"})," file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners."]}),"\n",(0,n.jsx)(t.p,{children:"To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on."}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:["Rebasing also requires that the user knows ",(0,n.jsx)(t.em,{children:"a priori"})," what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets."]}),"\n",(0,n.jsx)(t.p,{children:"While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose."}),"\n",(0,n.jsx)(t.p,{children:"The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well."}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"architecture",children:"Architecture"}),"\n",(0,n.jsx)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),"\n",(0,n.jsx)(t.p,{children:"The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"The data schema of various vulnerability scanners producing the input vulnerability report."}),"\n",(0,n.jsx)(t.li,{children:"The state management of various package managers and process for applying patches appropriately through them."}),"\n"]}),"\n",(0,n.jsxs)(t.p,{children:["Effectively, ",(0,n.jsx)(t.code,{children:"copa patch"})," can be considered a command that bridges an extensible ",(0,n.jsx)(t.code,{children:"Parse"})," action with an extensible ",(0,n.jsx)(t.code,{children:"Apply"})," action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:"]}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-go",children:"type UpdatePackage struct {\n Name string\n Version string\n}\n\ntype UpdateManifest struct {\n OSType string\n OSVersion string\n Arch string\n Updates []UpdatePackage\n}\n\ntype ScanReportParser interface {\n Parse(reportPath string) (*UpdateManifest, error)\n}\n\ntype PackageManager interface {\n Apply(imagePath string, report *UpdateManifest) error\n}\n"})}),"\n",(0,n.jsx)(t.h2,{id:"implementation",children:"Implementation"}),"\n",(0,n.jsx)("img",{title:"buildkit graph execution",src:"/copacetic/website/img/graph-execution.png"}),"\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.code,{children:"copa"})," is a pseudo-frontend to ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit",children:"buildkit"})," implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit/tree/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4#exploring-llb",children:"LLB graphs"})," for buildkit to execute:"]}),"\n",(0,n.jsxs)(t.ol,{children:["\n",(0,n.jsxs)(t.li,{children:["Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions."}),"\n"]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["Actions to fetch and deploy tools that can be injected into the target image to perform the patching.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches."}),"\n",(0,n.jsx)(t.li,{children:"In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image."}),"\n",(0,n.jsx)(t.li,{children:"Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well."}),"\n"]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["Actions to deploy the required patch packages to the target image.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.code,{children:"copa"})," integrates with buildkit at the API level because it uses the ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit/blob/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4/docs/merge%2Bdiff.md",children:"diff and merge"})," graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches."]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"tradeoffs",children:"Tradeoffs"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa."}),"\n",(0,n.jsx)(t.li,{children:"copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this."}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.li,{children:"No support for windows containers given the dependency on buildkit."}),"\n"]})]})}function h(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,n.jsx)(t,{...e,children:(0,n.jsx)(d,{...e})}):d(e)}},8453:(e,t,i)=>{i.d(t,{R:()=>o,x:()=>r});var n=i(6540);const a={},s=n.createContext(a);function o(e){const t=n.useContext(s);return n.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function r(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),n.createElement(s.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/0aeccac2.02849ec8.js b/website/assets/js/0aeccac2.02849ec8.js new file mode 100644 index 00000000..d0c07239 --- /dev/null +++ b/website/assets/js/0aeccac2.02849ec8.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1965],{1970:(e,t,i)=>{i.r(t),i.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>h,frontMatter:()=>s,metadata:()=>r,toc:()=>l});var n=i(4848),a=i(8453);const s={title:"Design"},o=void 0,r={id:"design",title:"Design",description:"Design Tenets",source:"@site/docs/design.md",sourceDirName:".",slug:"/design",permalink:"/copacetic/website/next/design",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{title:"Design"},sidebar:"sidebar",previous:{title:"Code of Conduct",permalink:"/copacetic/website/next/code-of-conduct"},next:{title:"Development and Testing Tips",permalink:"/copacetic/website/next/development-tips"}},c={},l=[{value:"Design Tenets",id:"design-tenets",level:2},{value:"Design Reasoning",id:"design-reasoning",level:2},{value:"Architecture",id:"architecture",level:2},{value:"Implementation",id:"implementation",level:2},{value:"Tradeoffs",id:"tradeoffs",level:2}];function d(e){const t={a:"a",code:"code",em:"em",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(t.h2,{id:"design-tenets",children:"Design Tenets"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update."})," This is a raison d\u2019etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to work with the existing ecosystem of container images."})," The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to allow parties other than the image authors to address container vulnerabilities."})," Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to do one thing well and be composable with other tools and processes."})," Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane."]}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"design-reasoning",children:"Design Reasoning"}),"\n",(0,n.jsxs)(t.p,{children:["The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental ",(0,n.jsx)(t.a,{href:"https://github.com/google/go-containerregistry/blob/main/cmd/crane/rebase.md",children:(0,n.jsx)(t.code,{children:"crane rebase"})}),":"]}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:["Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an ",(0,n.jsx)(t.code,{children:"apt install"})," during image creation will overwrite the dpkg ",(0,n.jsx)(t.code,{children:"status"})," file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners."]}),"\n",(0,n.jsx)(t.p,{children:"To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on."}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:["Rebasing also requires that the user knows ",(0,n.jsx)(t.em,{children:"a priori"})," what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets."]}),"\n",(0,n.jsx)(t.p,{children:"While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose."}),"\n",(0,n.jsx)(t.p,{children:"The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well."}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"architecture",children:"Architecture"}),"\n",(0,n.jsx)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),"\n",(0,n.jsx)(t.p,{children:"The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"The data schema of various vulnerability scanners producing the input vulnerability report."}),"\n",(0,n.jsx)(t.li,{children:"The state management of various package managers and process for applying patches appropriately through them."}),"\n"]}),"\n",(0,n.jsxs)(t.p,{children:["Effectively, ",(0,n.jsx)(t.code,{children:"copa patch"})," can be considered a command that bridges an extensible ",(0,n.jsx)(t.code,{children:"Parse"})," action with an extensible ",(0,n.jsx)(t.code,{children:"Apply"})," action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:"]}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-go",children:"type UpdatePackage struct {\n Name string\n Version string\n}\n\ntype UpdateManifest struct {\n OSType string\n OSVersion string\n Arch string\n Updates []UpdatePackage\n}\n\ntype ScanReportParser interface {\n Parse(reportPath string) (*UpdateManifest, error)\n}\n\ntype PackageManager interface {\n Apply(imagePath string, report *UpdateManifest) error\n}\n"})}),"\n",(0,n.jsx)(t.h2,{id:"implementation",children:"Implementation"}),"\n",(0,n.jsx)("img",{title:"buildkit graph execution",src:"/copacetic/website/img/graph-execution.png"}),"\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.code,{children:"copa"})," is a pseudo-frontend to ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit",children:"buildkit"})," implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit/tree/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4#exploring-llb",children:"LLB graphs"})," for buildkit to execute:"]}),"\n",(0,n.jsxs)(t.ol,{children:["\n",(0,n.jsxs)(t.li,{children:["Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions."}),"\n"]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["Actions to fetch and deploy tools that can be injected into the target image to perform the patching.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches."}),"\n",(0,n.jsx)(t.li,{children:"In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image."}),"\n",(0,n.jsx)(t.li,{children:"Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well."}),"\n"]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["Actions to deploy the required patch packages to the target image.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.code,{children:"copa"})," integrates with buildkit at the API level because it uses the ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit/blob/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4/docs/merge%2Bdiff.md",children:"diff and merge"})," graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches."]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"tradeoffs",children:"Tradeoffs"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa."}),"\n",(0,n.jsx)(t.li,{children:"copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this."}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.li,{children:"No support for windows containers given the dependency on buildkit."}),"\n"]})]})}function h(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,n.jsx)(t,{...e,children:(0,n.jsx)(d,{...e})}):d(e)}},8453:(e,t,i)=>{i.d(t,{R:()=>o,x:()=>r});var n=i(6540);const a={},s=n.createContext(a);function o(e){const t=n.useContext(s);return n.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function r(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),n.createElement(s.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/0aeccac2.920f5236.js b/website/assets/js/0aeccac2.920f5236.js deleted file mode 100644 index e4d301b1..00000000 --- a/website/assets/js/0aeccac2.920f5236.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[414],{3905:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>u});var i=a(7294);function n(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,i)}return a}function o(e){for(var t=1;t=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var l=i.createContext({}),p=function(e){var t=i.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},c=function(e){var t=p(e.components);return i.createElement(l.Provider,{value:t},e.children)},d="mdxType",g={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},h=i.forwardRef((function(e,t){var a=e.components,n=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=p(a),h=n,u=d["".concat(l,".").concat(h)]||d[h]||g[h]||r;return a?i.createElement(u,o(o({ref:t},c),{},{components:a})):i.createElement(u,o({ref:t},c))}));function u(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var r=a.length,o=new Array(r);o[0]=h;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[d]="string"==typeof e?e:n,o[1]=s;for(var p=2;p{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>d,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var i=a(7462),n=(a(7294),a(3905));const r={title:"Design"},o=void 0,s={unversionedId:"design",id:"design",title:"Design",description:"Design Tenets",source:"@site/docs/design.md",sourceDirName:".",slug:"/design",permalink:"/copacetic/website/next/design",draft:!1,tags:[],version:"current",frontMatter:{title:"Design"},sidebar:"sidebar",previous:{title:"Code of Conduct",permalink:"/copacetic/website/next/code-of-conduct"},next:{title:"Development and Testing Tips",permalink:"/copacetic/website/next/development-tips"}},l={},p=[{value:"Design Tenets",id:"design-tenets",level:2},{value:"Design Reasoning",id:"design-reasoning",level:2},{value:"Architecture",id:"architecture",level:2},{value:"Implementation",id:"implementation",level:2},{value:"Tradeoffs",id:"tradeoffs",level:2}],c={toc:p};function d(e){let{components:t,...a}=e;return(0,n.kt)("wrapper",(0,i.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h2",{id:"design-tenets"},"Design Tenets"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update.")," This is a raison d\u2019etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to work with the existing ecosystem of container images.")," The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to allow parties other than the image authors to address container vulnerabilities.")," Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to do one thing well and be composable with other tools and processes.")," Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane."))),(0,n.kt)("h2",{id:"design-reasoning"},"Design Reasoning"),(0,n.kt)("p",null,"The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/google/go-containerregistry/blob/main/cmd/crane/rebase.md"},(0,n.kt)("inlineCode",{parentName:"a"},"crane rebase")),":"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},"Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an ",(0,n.kt)("inlineCode",{parentName:"p"},"apt install")," during image creation will overwrite the dpkg ",(0,n.kt)("inlineCode",{parentName:"p"},"status")," file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners."),(0,n.kt)("p",{parentName:"li"},"To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},"Rebasing also requires that the user knows ",(0,n.kt)("em",{parentName:"p"},"a priori")," what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets."),(0,n.kt)("p",{parentName:"li"},"While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose."),(0,n.kt)("p",{parentName:"li"},"The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well."))),(0,n.kt)("h2",{id:"architecture"},"Architecture"),(0,n.kt)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),(0,n.kt)("p",null,"The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"The data schema of various vulnerability scanners producing the input vulnerability report."),(0,n.kt)("li",{parentName:"ul"},"The state management of various package managers and process for applying patches appropriately through them.")),(0,n.kt)("p",null,"Effectively, ",(0,n.kt)("inlineCode",{parentName:"p"},"copa patch")," can be considered a command that bridges an extensible ",(0,n.kt)("inlineCode",{parentName:"p"},"Parse")," action with an extensible ",(0,n.kt)("inlineCode",{parentName:"p"},"Apply")," action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-go"},"type UpdatePackage struct {\n Name string\n Version string\n}\n\ntype UpdateManifest struct {\n OSType string\n OSVersion string\n Arch string\n Updates []UpdatePackage\n}\n\ntype ScanReportParser interface {\n Parse(reportPath string) (*UpdateManifest, error)\n}\n\ntype PackageManager interface {\n Apply(imagePath string, report *UpdateManifest) error\n}\n")),(0,n.kt)("h2",{id:"implementation"},"Implementation"),(0,n.kt)("img",{title:"buildkit graph execution",src:"/copacetic/website/img/graph-execution.png"}),(0,n.kt)("p",null,(0,n.kt)("inlineCode",{parentName:"p"},"copa")," is a pseudo-frontend to ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit"},"buildkit")," implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit/tree/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4#exploring-llb"},"LLB graphs")," for buildkit to execute:"),(0,n.kt)("ol",null,(0,n.kt)("li",{parentName:"ol"},"Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions."))),(0,n.kt)("li",{parentName:"ol"},"Actions to fetch and deploy tools that can be injected into the target image to perform the patching.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches."),(0,n.kt)("li",{parentName:"ul"},"In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image."),(0,n.kt)("li",{parentName:"ul"},"Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well."))),(0,n.kt)("li",{parentName:"ol"},"Actions to deploy the required patch packages to the target image.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"copa")," integrates with buildkit at the API level because it uses the ",(0,n.kt)("a",{parentName:"li",href:"https://github.com/moby/buildkit/blob/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4/docs/merge%2Bdiff.md"},"diff and merge")," graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.")))),(0,n.kt)("h2",{id:"tradeoffs"},"Tradeoffs"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa."),(0,n.kt)("li",{parentName:"ul"},"copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this."))),(0,n.kt)("li",{parentName:"ul"},"No support for windows containers given the dependency on buildkit.")))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/0c380bb3.90242cf8.js b/website/assets/js/0c380bb3.90242cf8.js deleted file mode 100644 index 94806148..00000000 --- a/website/assets/js/0c380bb3.90242cf8.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6948],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>k});var i=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=i.createContext({}),c=function(e){var t=i.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return i.createElement(l.Provider,{value:t},e.children)},d="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},m=i.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),d=c(n),m=r,k=d["".concat(l,".").concat(m)]||d[m]||u[m]||a;return n?i.createElement(k,o(o({ref:t},p),{},{components:n})):i.createElement(k,o({ref:t},p))}));function k(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,o=new Array(a);o[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[d]="string"==typeof e?e:r,o[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>d,frontMatter:()=>a,metadata:()=>s,toc:()=>c});var i=n(7462),r=(n(7294),n(3905));const a={title:"Quick Start"},o=void 0,s={unversionedId:"quick-start",id:"version-v0.2.x/quick-start",title:"Quick Start",description:"This sample illustrates how to patch containers using vulnerability reports with copa.",source:"@site/versioned_docs/version-v0.2.x/quick-start.md",sourceDirName:".",slug:"/quick-start",permalink:"/copacetic/website/v0.2.x/quick-start",draft:!1,tags:[],version:"v0.2.x",frontMatter:{title:"Quick Start"},sidebar:"sidebar",previous:{title:"Installation",permalink:"/copacetic/website/v0.2.x/installation"},next:{title:"Design",permalink:"/copacetic/website/v0.2.x/design"}},l={},c=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Sample Steps",id:"sample-steps",level:2}],p={toc:c};function d(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,i.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"This sample illustrates how to patch containers using vulnerability reports with ",(0,r.kt)("inlineCode",{parentName:"p"},"copa"),"."),(0,r.kt)("h2",{id:"prerequisites"},"Prerequisites"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"An Ubuntu 22.04 VM configured through the ",(0,r.kt)("a",{parentName:"li",href:"/copacetic/website/v0.2.x/installation"},"setup instructions")," or a VSCode ",(0,r.kt)("a",{parentName:"li",href:"/copacetic/website/v0.2.x/contributing/#visual-studio-code-development-container"},"devcontainer")," environment. This includes:",(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"copa")," tool ",(0,r.kt)("a",{parentName:"li",href:"/copacetic/website/v0.2.x/installation"},"built & pathed"),"."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://github.com/moby/buildkit/#quick-start"},"buildkit")," daemon installed & pathed."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://docs.docker.com/desktop/linux/install/#generic-installation-steps"},"docker")," daemon running and CLI installed & pathed."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://aquasecurity.github.io/trivy/latest/getting-started/installation/"},"trivy CLI")," installed & pathed.")))),(0,r.kt)("h2",{id:"sample-steps"},"Sample Steps"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("p",{parentName:"li"},"Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6\n")),(0,r.kt)("p",{parentName:"li"},"You can also see the existing patchable vulnerabilities in table form on the shell with:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6\n\n"))),(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("p",{parentName:"li"},"Patch the image using the Trivy report. You will need to start ",(0,r.kt)("inlineCode",{parentName:"p"},"buildkitd")," if it is not already running:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"sudo buildkitd &\nsudo copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched\n")),(0,r.kt)("p",{parentName:"li"},"Alternatively, you can run ",(0,r.kt)("inlineCode",{parentName:"p"},"buildkitd")," in a container, which allows copa to be run without root access to the local buildkit socket:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'export BUILDKIT_VERSION=v0.11.4\nexport BUILDKIT_PORT=8888\ndocker run \\\n --detach \\\n --rm \\\n --privileged \\\n -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION" \\\n --addr tcp://0.0.0.0:$BUILDKIT_PORT\ncopa patch \\\n -i docker.io/library/nginx:1.21.6 \\\n -r nginx.1.21.6.json \\\n -t 1.21.6-patched \\\n -a tcp://0.0.0.0:$BUILDKIT_PORT\n')),(0,r.kt)("p",{parentName:"li"},"In either case, ",(0,r.kt)("inlineCode",{parentName:"p"},"copa")," is non-destructive and exports a new image with the specified ",(0,r.kt)("inlineCode",{parentName:"p"},"1.21.6-patched")," label to the local Docker daemon."),(0,r.kt)("blockquote",{parentName:"li"},(0,r.kt)("p",{parentName:"blockquote"},(0,r.kt)("strong",{parentName:"p"},"NOTE:")," if you're running this sample against an image from a private registry instead,\nensure that the credentials are configured in the default Docker config.json before running ",(0,r.kt)("inlineCode",{parentName:"p"},"copa patch"),",\nfor example, via ",(0,r.kt)("inlineCode",{parentName:"p"},"sudo docker login -u -p "),"."))),(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("p",{parentName:"li"},"Scan the patched image and verify that the vulnerabilities have been patched:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched\n")),(0,r.kt)("p",{parentName:"li"},"You can also inspect the structure of the patched image with ",(0,r.kt)("inlineCode",{parentName:"p"},"docker history")," to see the new patch layer appended to the image:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'$ docker history docker.io/library/nginx:1.21.6-patched\nIMAGE CREATED CREATED BY SIZE COMMENT\na372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins\u2026 26.1MB buildkit.exporter.image.v0\n 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0\n 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0\n 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0\n 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0\n 3 months ago COPY 30-tune-worker-processes.sh /docker-ent\u2026 4.61kB buildkit.dockerfile.v0\n 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent\u2026 1.04kB buildkit.dockerfile.v0\n 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker\u2026 1.96kB buildkit.dockerfile.v0\n 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0\n 3 months ago RUN /bin/sh -c set -x && addgroup --syst\u2026 61.1MB buildkit.dockerfile.v0\n 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0\n 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0\n 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0\n 3 months ago LABEL maintainer=NGINX Docker Maintainers 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B\n 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55\u2026 80.4MB\n'))),(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("p",{parentName:"li"},"Run the container to verify that the image has no regressions:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'$ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched\n/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration\n/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh\n10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf\n10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh\n/docker-entrypoint.sh: Configuration complete; ready for start up\n2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method\n2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2\n2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)\n2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2\n2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576\n2022/05/16 18:00:17 [notice] 1#1: start worker processes\n2022/05/16 18:00:17 [notice] 1#1: start worker process 31\n2022/05/16 18:00:17 [notice] 1#1: start worker process 32\n2022/05/16 18:00:17 [notice] 1#1: start worker process 33\n2022/05/16 18:00:17 [notice] 1#1: start worker process 34\n2022/05/16 18:00:17 [notice] 1#1: start worker process 35\n2022/05/16 18:00:17 [notice] 1#1: start worker process 36\n2022/05/16 18:00:17 [notice] 1#1: start worker process 37\n2022/05/16 18:00:17 [notice] 1#1: start worker process 38\n2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received\n')),(0,r.kt)("p",{parentName:"li"},"You can stop the container by opening a new shell instance and running: ",(0,r.kt)("inlineCode",{parentName:"p"},"docker stop nginx-test")))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/0c380bb3.ba7d238d.js b/website/assets/js/0c380bb3.ba7d238d.js new file mode 100644 index 00000000..9c5f5572 --- /dev/null +++ b/website/assets/js/0c380bb3.ba7d238d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2150],{5420:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>a,contentTitle:()=>r,default:()=>h,frontMatter:()=>o,metadata:()=>c,toc:()=>l});var t=i(4848),s=i(8453);const o={title:"Quick Start"},r=void 0,c={id:"quick-start",title:"Quick Start",description:"This sample illustrates how to patch containers using vulnerability reports with copa.",source:"@site/versioned_docs/version-v0.2.x/quick-start.md",sourceDirName:".",slug:"/quick-start",permalink:"/copacetic/website/v0.2.x/quick-start",draft:!1,unlisted:!1,tags:[],version:"v0.2.x",frontMatter:{title:"Quick Start"},sidebar:"sidebar",previous:{title:"Installation",permalink:"/copacetic/website/v0.2.x/installation"},next:{title:"Design",permalink:"/copacetic/website/v0.2.x/design"}},a={},l=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Sample Steps",id:"sample-steps",level:2}];function d(e){const n={a:"a",blockquote:"blockquote",code:"code",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(n.p,{children:["This sample illustrates how to patch containers using vulnerability reports with ",(0,t.jsx)(n.code,{children:"copa"}),"."]}),"\n",(0,t.jsx)(n.h2,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["An Ubuntu 22.04 VM configured through the ",(0,t.jsx)(n.a,{href:"/copacetic/website/v0.2.x/installation",children:"setup instructions"})," or a VSCode ",(0,t.jsx)(n.a,{href:"./contributing.md/#visual-studio-code-development-container",children:"devcontainer"})," environment. This includes:","\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"copa"})," tool ",(0,t.jsx)(n.a,{href:"/copacetic/website/v0.2.x/installation",children:"built & pathed"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://github.com/moby/buildkit/#quick-start",children:"buildkit"})," daemon installed & pathed."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://docs.docker.com/desktop/linux/install/#generic-installation-steps",children:"docker"})," daemon running and CLI installed & pathed."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://aquasecurity.github.io/trivy/latest/getting-started/installation/",children:"trivy CLI"})," installed & pathed."]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,t.jsx)(n.h2,{id:"sample-steps",children:"Sample Steps"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6\n"})}),"\n",(0,t.jsx)(n.p,{children:"You can also see the existing patchable vulnerabilities in table form on the shell with:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6\n\n"})}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsxs)(n.p,{children:["Patch the image using the Trivy report. You will need to start ",(0,t.jsx)(n.code,{children:"buildkitd"})," if it is not already running:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"sudo buildkitd &\nsudo copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched\n"})}),"\n",(0,t.jsxs)(n.p,{children:["Alternatively, you can run ",(0,t.jsx)(n.code,{children:"buildkitd"})," in a container, which allows copa to be run without root access to the local buildkit socket:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'export BUILDKIT_VERSION=v0.11.4\nexport BUILDKIT_PORT=8888\ndocker run \\\n --detach \\\n --rm \\\n --privileged \\\n -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION" \\\n --addr tcp://0.0.0.0:$BUILDKIT_PORT\ncopa patch \\\n -i docker.io/library/nginx:1.21.6 \\\n -r nginx.1.21.6.json \\\n -t 1.21.6-patched \\\n -a tcp://0.0.0.0:$BUILDKIT_PORT\n'})}),"\n",(0,t.jsxs)(n.p,{children:["In either case, ",(0,t.jsx)(n.code,{children:"copa"})," is non-destructive and exports a new image with the specified ",(0,t.jsx)(n.code,{children:"1.21.6-patched"})," label to the local Docker daemon."]}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"NOTE:"})," if you're running this sample against an image from a private registry instead,\nensure that the credentials are configured in the default Docker config.json before running ",(0,t.jsx)(n.code,{children:"copa patch"}),",\nfor example, via ",(0,t.jsx)(n.code,{children:"sudo docker login -u -p "}),"."]}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Scan the patched image and verify that the vulnerabilities have been patched:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched\n"})}),"\n",(0,t.jsxs)(n.p,{children:["You can also inspect the structure of the patched image with ",(0,t.jsx)(n.code,{children:"docker history"})," to see the new patch layer appended to the image:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'$ docker history docker.io/library/nginx:1.21.6-patched\nIMAGE CREATED CREATED BY SIZE COMMENT\na372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins\u2026 26.1MB buildkit.exporter.image.v0\n 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0\n 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0\n 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0\n 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0\n 3 months ago COPY 30-tune-worker-processes.sh /docker-ent\u2026 4.61kB buildkit.dockerfile.v0\n 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent\u2026 1.04kB buildkit.dockerfile.v0\n 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker\u2026 1.96kB buildkit.dockerfile.v0\n 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0\n 3 months ago RUN /bin/sh -c set -x && addgroup --syst\u2026 61.1MB buildkit.dockerfile.v0\n 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0\n 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0\n 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0\n 3 months ago LABEL maintainer=NGINX Docker Maintainers 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B\n 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55\u2026 80.4MB\n'})}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Run the container to verify that the image has no regressions:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'$ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched\n/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration\n/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh\n10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf\n10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh\n/docker-entrypoint.sh: Configuration complete; ready for start up\n2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method\n2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2\n2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)\n2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2\n2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576\n2022/05/16 18:00:17 [notice] 1#1: start worker processes\n2022/05/16 18:00:17 [notice] 1#1: start worker process 31\n2022/05/16 18:00:17 [notice] 1#1: start worker process 32\n2022/05/16 18:00:17 [notice] 1#1: start worker process 33\n2022/05/16 18:00:17 [notice] 1#1: start worker process 34\n2022/05/16 18:00:17 [notice] 1#1: start worker process 35\n2022/05/16 18:00:17 [notice] 1#1: start worker process 36\n2022/05/16 18:00:17 [notice] 1#1: start worker process 37\n2022/05/16 18:00:17 [notice] 1#1: start worker process 38\n2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received\n'})}),"\n",(0,t.jsxs)(n.p,{children:["You can stop the container by opening a new shell instance and running: ",(0,t.jsx)(n.code,{children:"docker stop nginx-test"})]}),"\n"]}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(d,{...e})}):d(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>r,x:()=>c});var t=i(6540);const s={},o=t.createContext(s);function r(e){const n=t.useContext(o);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),t.createElement(o.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/0d9a188d.5b7da772.js b/website/assets/js/0d9a188d.5b7da772.js new file mode 100644 index 00000000..b018437a --- /dev/null +++ b/website/assets/js/0d9a188d.5b7da772.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1882],{2687:(e,t,i)=>{i.r(t),i.d(t,{assets:()=>d,contentTitle:()=>r,default:()=>h,frontMatter:()=>o,metadata:()=>c,toc:()=>a});var n=i(4848),s=i(8453);const o={title:"Contributing"},r=void 0,c={id:"contributing",title:"Contributing",description:"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.",source:"@site/versioned_docs/version-v0.5.x/contributing.md",sourceDirName:".",slug:"/contributing",permalink:"/copacetic/website/v0.5.x/contributing",draft:!1,unlisted:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Contributing"},sidebar:"sidebar",previous:{title:"Scanner Plugins",permalink:"/copacetic/website/v0.5.x/scanner-plugins"},next:{title:"Code of Conduct",permalink:"/copacetic/website/v0.5.x/code-of-conduct"}},d={},a=[{value:"Bi-Weekly Community Meeting",id:"bi-weekly-community-meeting",level:2},{value:"Slack",id:"slack",level:2},{value:"Contributing Issues",id:"contributing-issues",level:2},{value:"Contributing Code",id:"contributing-code",level:2},{value:"Getting Started",id:"getting-started",level:3},{value:"Visual Studio Code Development Container",id:"visual-studio-code-development-container",level:3},{value:"Prerequisites",id:"prerequisites",level:4},{value:"Personalizing user settings in a dev container",id:"personalizing-user-settings-in-a-dev-container",level:4},{value:"Tests",id:"tests",level:3},{value:"Pull Requests",id:"pull-requests",level:3},{value:"Developer Certificate of Origin (DCO)",id:"developer-certificate-of-origin-dco",level:2},{value:"I didn't sign my commit, now what?",id:"i-didnt-sign-my-commit-now-what",level:3},{value:"Code of Conduct",id:"code-of-conduct",level:2}];function l(e){const t={a:"a",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h3:"h3",h4:"h4",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsxs)(t.p,{children:["Welcome! We are very happy to accept community contributions to the project, whether through ",(0,n.jsx)(t.a,{href:"#contributing-issues",children:"filing issues"})," or ",(0,n.jsx)(t.a,{href:"#contributing-code",children:"code"})," in the form of ",(0,n.jsx)(t.a,{href:"#pull-requests",children:"Pull Requests"}),". Please note that by participating in this project, you agree to abide by the ",(0,n.jsx)(t.a,{href:"/copacetic/website/v0.5.x/code-of-conduct",children:"Code of Conduct"}),", as well as the terms of the ",(0,n.jsx)(t.a,{href:"#developer-certificate-of-origin-dco",children:"Developer Certificate of Origin"}),"."]}),"\n",(0,n.jsx)(t.h2,{id:"bi-weekly-community-meeting",children:"Bi-Weekly Community Meeting"}),"\n",(0,n.jsxs)(t.p,{children:["A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join ",(0,n.jsx)(t.a,{href:"https://docs.google.com/document/d/1QdskbeCtgKcdWYHI6EXkLFxyzTCyVT6e8MgB3CaAhWI/edit?usp=sharing",children:"here"})]}),"\n",(0,n.jsx)(t.h2,{id:"slack",children:"Slack"}),"\n",(0,n.jsxs)(t.p,{children:["To discuss issues with Copa, features, or development, you can join the ",(0,n.jsx)(t.code,{children:"#copa"})," channel on the ",(0,n.jsx)(t.a,{href:"https://communityinviter.com/apps/opencontainers/join-the-oci-community",children:"OCI Slack"}),"."]}),"\n",(0,n.jsx)(t.h2,{id:"contributing-issues",children:"Contributing Issues"}),"\n",(0,n.jsxs)(t.p,{children:["Before opening any new issues, please search our ",(0,n.jsx)(t.a,{href:"https://github.com/project-copacetic/copacetic/issues",children:"existing GitHub issues"})," to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead."]}),"\n",(0,n.jsx)(t.p,{children:"When opening an issue, please select the most appropriate template for what you're contributing:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"Bug Report:"})," If you would like to report the project or tool behaving in unexpected ways."]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"Documentation Improvement:"})," If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content."]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"Request:"})," If you have a feature request, suggestion, or a even a design proposal to review."]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"Question:"})," If you would like to ask the maintainers a question about the project."]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"contributing-code",children:"Contributing Code"}),"\n",(0,n.jsx)(t.h3,{id:"getting-started",children:"Getting Started"}),"\n",(0,n.jsx)(t.p,{children:"Follow the instructions to either:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.a,{href:"/copacetic/website/v0.5.x/installation",children:"Setup your dev environment to build copa"}),"."]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.a,{href:"#visual-studio-code-development-container",children:"Use the copa development container"})," in ",(0,n.jsx)(t.a,{href:"https://code.visualstudio.com/",children:"Visual Studio Code"}),"."]}),"\n"]}),"\n",(0,n.jsxs)(t.p,{children:["For an overview of the project components, refer to the ",(0,n.jsx)(t.a,{href:"/copacetic/website/v0.5.x/design",children:"copa design"})," document."]}),"\n",(0,n.jsx)(t.h3,{id:"visual-studio-code-development-container",children:"Visual Studio Code Development Container"}),"\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.a,{href:"https://code.visualstudio.com/",children:"VSCode"})," supports development in a containerized environment through its ",(0,n.jsx)(t.a,{href:"https://code.visualstudio.com/docs/remote/containers",children:"Remote - Container extension"}),". This folder provides a development container which encapsulates the dependencies specified in the ",(0,n.jsx)(t.a,{href:"/copacetic/website/v0.5.x/installation",children:"instructions to build and run copa"}),"."]}),"\n",(0,n.jsx)(t.h4,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,n.jsxs)(t.ol,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.a,{href:"https://docs.docker.com/get-docker/",children:"Docker"}),"\n",(0,n.jsxs)(t.blockquote,{children:["\n",(0,n.jsxs)(t.p,{children:["For Windows users, enabling ",(0,n.jsx)(t.a,{href:"https://docs.docker.com/docker-for-windows/wsl/",children:"WSL2 back-end integration with Docker"})," is recommended."]}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.li,{children:(0,n.jsx)(t.a,{href:"https://code.visualstudio.com/",children:"Visual Studio Code"})}),"\n",(0,n.jsx)(t.li,{children:(0,n.jsx)(t.a,{href:"https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers",children:"Visual Studio Code Remote - Containers extension"})}),"\n"]}),"\n",(0,n.jsxs)(t.blockquote,{children:["\n",(0,n.jsx)(t.p,{children:(0,n.jsx)(t.strong,{children:"\u26a0 If running via Docker Desktop for Windows"})}),"\n",(0,n.jsxs)(t.p,{children:["Note that the ",(0,n.jsxs)(t.a,{href:"https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user",children:["mounted workspace files appear owned by ",(0,n.jsx)(t.code,{children:"root"})]})," in the dev container, which will cause ",(0,n.jsx)(t.code,{children:"git"})," commands to fail with a ",(0,n.jsx)(t.code,{children:"fatal: detected dubious ownership in a repository"})," error due to ",(0,n.jsx)(t.a,{href:"https://git-scm.com/docs/git-config/2.35.2#Documentation/git-config.txt-safedirectory",children:"safe.directory"})," checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the ",(0,n.jsx)(t.code,{children:"vscode"})," user:"]}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-bash",children:"sudo chown -R vscode:vscode /workspace/copacetic\n"})}),"\n"]}),"\n",(0,n.jsx)(t.h4,{id:"personalizing-user-settings-in-a-dev-container",children:"Personalizing user settings in a dev container"}),"\n",(0,n.jsxs)(t.p,{children:["VSCode supports applying your user settings, such as your ",(0,n.jsx)(t.code,{children:".gitconfig"}),", to a dev container through the use of ",(0,n.jsx)(t.a,{href:"https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories",children:"dotfiles repositories"}),". This can be done through your own VSCode ",(0,n.jsx)(t.code,{children:"settings.json"})," file without changing the dev container image or configuration."]}),"\n",(0,n.jsx)(t.h3,{id:"tests",children:"Tests"}),"\n",(0,n.jsxs)(t.p,{children:["Once you can successfully ",(0,n.jsx)(t.code,{children:"make"})," the project, any code contributions should also successfully:"]}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["Pass unit tests via ",(0,n.jsx)(t.code,{children:"make test"}),"."]}),"\n",(0,n.jsxs)(t.li,{children:["Lint cleanly via ",(0,n.jsx)(t.code,{children:"make lint"}),"."]}),"\n"]}),"\n",(0,n.jsxs)(t.p,{children:["Pull requests will also be expected to pass the PR functional tests specified by ",(0,n.jsx)(t.code,{children:".github/workflows/build.yml"}),"."]}),"\n",(0,n.jsx)(t.h3,{id:"pull-requests",children:"Pull Requests"}),"\n",(0,n.jsxs)(t.p,{children:["If you'd like to start contributing code to the project, you can search for ",(0,n.jsxs)(t.a,{href:"https://github.com/project-copacetic/copacetic/labels/good%20first%20issue",children:["issues with the ",(0,n.jsx)(t.code,{children:"good first issue"})," label"]}),". Other kinds of PR contributions we would look for include:"]}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"Fixes for bugs and other correctness issues."}),"\n",(0,n.jsx)(t.li,{children:"Docs and other content improvements (e.g. samples)."}),"\n",(0,n.jsx)(t.li,{children:"Extensions to support parsing new scanning report formats."}),"\n",(0,n.jsx)(t.li,{children:"Extensions to support patching images based on new distros or using new package managers."}),"\n"]}),"\n",(0,n.jsx)(t.p,{children:"For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["Architectural changes (e.g. breaking interfaces or violations of ",(0,n.jsx)(t.a,{href:"/copacetic/website/v0.5.x/design",children:"this project's design tenets"}),")."]}),"\n",(0,n.jsx)(t.li,{children:"Unsolicited features that significantly expand the functional scope of the tool."}),"\n"]}),"\n",(0,n.jsxs)(t.p,{children:["Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the ",(0,n.jsx)(t.a,{href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format",children:"Angular commit message format"})," for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs."]}),"\n",(0,n.jsx)(t.p,{children:"We suggest:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["Use the standard header format of ",(0,n.jsx)(t.code,{children:'": "'})," where the ",(0,n.jsx)(t.code,{children:""})," is one of the following:","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"build:"})," Changes that affect the build system or external dependencies"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"ci:"})," Changes to the GitHub workflows and configurations"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"docs:"})," Documentation only changes"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"feat:"})," A new feature"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"fix:"})," A bug fix"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"perf:"})," A code change that improves performance"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"refactor:"})," A code change that neither fixes a bug nor adds a feature"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"test:"})," Adding missing tests or correcting existing tests"]}),"\n"]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["Use a ",(0,n.jsx)(t.a,{href:"https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html",children:"concise, imperative description"})," of the changes included in the ",(0,n.jsx)(t.code,{children:""})," of the header, the body of the PR, and generally in your commit messages."]}),"\n",(0,n.jsxs)(t.li,{children:["Use ",(0,n.jsx)(t.a,{href:"https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests",children:"GitHub keywords"})," in the footer of your PR description, such as ",(0,n.jsx)(t.code,{children:"closes"})," to automatically close issues the PR intends to address."]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"developer-certificate-of-origin-dco",children:"Developer Certificate of Origin (DCO)"}),"\n",(0,n.jsxs)(t.p,{children:["The ",(0,n.jsx)(t.a,{href:"https://wiki.linuxfoundation.org/dco",children:"Developer Certificate of Origin"})," (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the ",(0,n.jsx)(t.a,{href:"https://developercertificate.org/",children:"full text of the DCO"}),", reformatted for readability:"]}),"\n",(0,n.jsxs)(t.blockquote,{children:["\n",(0,n.jsx)(t.p,{children:"By making a contribution to this project, I certify that:"}),"\n",(0,n.jsx)(t.p,{children:"(a) The contribution was created in whole or in part by me and I\nhave the right to submit it under the open source license\nindicated in the file; or"}),"\n",(0,n.jsx)(t.p,{children:"(b) The contribution is based upon previous work that, to the best\nof my knowledge, is covered under an appropriate open source\nlicense and I have the right under that license to submit that\nwork with modifications, whether created in whole or in part\nby me, under the same open source license (unless I am\npermitted to submit under a different license), as indicated\nin the file; or"}),"\n",(0,n.jsx)(t.p,{children:"(c) The contribution was provided directly to me by some other\nperson who certified (a), (b) or (c) and I have not modified\nit."}),"\n",(0,n.jsx)(t.p,{children:"(d) I understand and agree that this project and the contribution\nare public and that a record of the contribution (including all\npersonal information I submit with it, including my sign-off) is\nmaintained indefinitely and may be redistributed consistent with\nthis project or the open source license(s) involved."}),"\n"]}),"\n",(0,n.jsxs)(t.p,{children:["Contributors ",(0,n.jsx)(t.em,{children:"sign-off"})," that they adhere to these requirements by adding a ",(0,n.jsx)(t.code,{children:"Signed-off-by"})," line to commit messages."]}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-text",children:"This is my commit message\n\nSigned-off-by: Random J Developer \n"})}),"\n",(0,n.jsxs)(t.p,{children:["Git even has a ",(0,n.jsx)(t.code,{children:"-s"})," command line option to append this automatically to your commit message:"]}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-bash",children:"git commit -s -m 'This is my commit message'\n"})}),"\n",(0,n.jsxs)(t.p,{children:["Pull requests that do not contain a valid ",(0,n.jsx)(t.code,{children:"Signed-off-by"})," line cannot be merged."]}),"\n",(0,n.jsx)(t.h3,{id:"i-didnt-sign-my-commit-now-what",children:"I didn't sign my commit, now what?"}),"\n",(0,n.jsx)(t.p,{children:"No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:"}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-bash",children:"git switch \ngit commit --amend --no-edit --signoff\ngit push --force-with-lease \n"})}),"\n",(0,n.jsx)(t.h2,{id:"code-of-conduct",children:"Code of Conduct"}),"\n",(0,n.jsxs)(t.p,{children:["This project has adopted the ",(0,n.jsx)(t.a,{href:"/copacetic/website/v0.5.x/code-of-conduct",children:"Contributor Covenant Code of Conduct"}),"."]})]})}function h(e={}){const{wrapper:t}={...(0,s.R)(),...e.components};return t?(0,n.jsx)(t,{...e,children:(0,n.jsx)(l,{...e})}):l(e)}},8453:(e,t,i)=>{i.d(t,{R:()=>r,x:()=>c});var n=i(6540);const s={},o=n.createContext(s);function r(e){const t=n.useContext(o);return n.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function c(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),n.createElement(o.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/0d9a188d.ace05380.js b/website/assets/js/0d9a188d.ace05380.js deleted file mode 100644 index 84846fcf..00000000 --- a/website/assets/js/0d9a188d.ace05380.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1865],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var o=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function r(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=o.createContext({}),c=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},u=function(e){var t=c(e.components);return o.createElement(l.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),p=c(n),m=i,h=p["".concat(l,".").concat(m)]||p[m]||d[m]||a;return n?o.createElement(h,r(r({ref:t},u),{},{components:n})):o.createElement(h,r({ref:t},u))}));function h(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,r=new Array(a);r[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[p]="string"==typeof e?e:i,r[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>p,frontMatter:()=>a,metadata:()=>s,toc:()=>c});var o=n(7462),i=(n(7294),n(3905));const a={title:"Contributing"},r=void 0,s={unversionedId:"contributing",id:"version-v0.5.x/contributing",title:"Contributing",description:"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.",source:"@site/versioned_docs/version-v0.5.x/contributing.md",sourceDirName:".",slug:"/contributing",permalink:"/copacetic/website/v0.5.x/contributing",draft:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Contributing"},sidebar:"sidebar",previous:{title:"Scanner Plugins",permalink:"/copacetic/website/v0.5.x/scanner-plugins"},next:{title:"Code of Conduct",permalink:"/copacetic/website/v0.5.x/code-of-conduct"}},l={},c=[{value:"Bi-Weekly Community Meeting",id:"bi-weekly-community-meeting",level:2},{value:"Slack",id:"slack",level:2},{value:"Contributing Issues",id:"contributing-issues",level:2},{value:"Contributing Code",id:"contributing-code",level:2},{value:"Getting Started",id:"getting-started",level:3},{value:"Visual Studio Code Development Container",id:"visual-studio-code-development-container",level:3},{value:"Prerequisites",id:"prerequisites",level:4},{value:"Personalizing user settings in a dev container",id:"personalizing-user-settings-in-a-dev-container",level:4},{value:"Tests",id:"tests",level:3},{value:"Pull Requests",id:"pull-requests",level:3},{value:"Developer Certificate of Origin (DCO)",id:"developer-certificate-of-origin-dco",level:2},{value:"I didn't sign my commit, now what?",id:"i-didnt-sign-my-commit-now-what",level:3},{value:"Code of Conduct",id:"code-of-conduct",level:2}],u={toc:c};function p(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,o.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"Welcome! We are very happy to accept community contributions to the project, whether through ",(0,i.kt)("a",{parentName:"p",href:"#contributing-issues"},"filing issues")," or ",(0,i.kt)("a",{parentName:"p",href:"#contributing-code"},"code")," in the form of ",(0,i.kt)("a",{parentName:"p",href:"#pull-requests"},"Pull Requests"),". Please note that by participating in this project, you agree to abide by the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.5.x/code-of-conduct"},"Code of Conduct"),", as well as the terms of the ",(0,i.kt)("a",{parentName:"p",href:"#developer-certificate-of-origin-dco"},"Developer Certificate of Origin"),"."),(0,i.kt)("h2",{id:"bi-weekly-community-meeting"},"Bi-Weekly Community Meeting"),(0,i.kt)("p",null,"A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join ",(0,i.kt)("a",{parentName:"p",href:"https://docs.google.com/document/d/1QdskbeCtgKcdWYHI6EXkLFxyzTCyVT6e8MgB3CaAhWI/edit?usp=sharing"},"here")),(0,i.kt)("h2",{id:"slack"},"Slack"),(0,i.kt)("p",null,"To discuss issues with Copa, features, or development, you can join the ",(0,i.kt)("inlineCode",{parentName:"p"},"#copa")," channel on the ",(0,i.kt)("a",{parentName:"p",href:"https://communityinviter.com/apps/opencontainers/join-the-oci-community"},"OCI Slack"),"."),(0,i.kt)("h2",{id:"contributing-issues"},"Contributing Issues"),(0,i.kt)("p",null,"Before opening any new issues, please search our ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/issues"},"existing GitHub issues")," to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead."),(0,i.kt)("p",null,"When opening an issue, please select the most appropriate template for what you're contributing:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Bug Report:")," If you would like to report the project or tool behaving in unexpected ways."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Documentation Improvement:")," If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Request:")," If you have a feature request, suggestion, or a even a design proposal to review."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Question:")," If you would like to ask the maintainers a question about the project.")),(0,i.kt)("h2",{id:"contributing-code"},"Contributing Code"),(0,i.kt)("h3",{id:"getting-started"},"Getting Started"),(0,i.kt)("p",null,"Follow the instructions to either:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/copacetic/website/v0.5.x/installation"},"Setup your dev environment to build copa"),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"#visual-studio-code-development-container"},"Use the copa development container")," in ",(0,i.kt)("a",{parentName:"li",href:"https://code.visualstudio.com/"},"Visual Studio Code"),".")),(0,i.kt)("p",null,"For an overview of the project components, refer to the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.5.x/design"},"copa design")," document."),(0,i.kt)("h3",{id:"visual-studio-code-development-container"},"Visual Studio Code Development Container"),(0,i.kt)("p",null,(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/"},"VSCode")," supports development in a containerized environment through its ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/docs/remote/containers"},"Remote - Container extension"),". This folder provides a development container which encapsulates the dependencies specified in the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.5.x/installation"},"instructions to build and run copa"),"."),(0,i.kt)("h4",{id:"prerequisites"},"Prerequisites"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://docs.docker.com/get-docker/"},"Docker"),(0,i.kt)("blockquote",{parentName:"li"},(0,i.kt)("p",{parentName:"blockquote"},"For Windows users, enabling ",(0,i.kt)("a",{parentName:"p",href:"https://docs.docker.com/docker-for-windows/wsl/"},"WSL2 back-end integration with Docker")," is recommended."))),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://code.visualstudio.com/"},"Visual Studio Code")),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers"},"Visual Studio Code Remote - Containers extension"))),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},(0,i.kt)("strong",{parentName:"p"},"\u26a0 If running via Docker Desktop for Windows")),(0,i.kt)("p",{parentName:"blockquote"},"Note that the ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user"},"mounted workspace files appear owned by ",(0,i.kt)("inlineCode",{parentName:"a"},"root"))," in the dev container, which will cause ",(0,i.kt)("inlineCode",{parentName:"p"},"git")," commands to fail with a ",(0,i.kt)("inlineCode",{parentName:"p"},"fatal: detected dubious ownership in a repository")," error due to ",(0,i.kt)("a",{parentName:"p",href:"https://git-scm.com/docs/git-config/2.35.2#Documentation/git-config.txt-safedirectory"},"safe.directory")," checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the ",(0,i.kt)("inlineCode",{parentName:"p"},"vscode")," user:"),(0,i.kt)("pre",{parentName:"blockquote"},(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"sudo chown -R vscode:vscode /workspace/copacetic\n"))),(0,i.kt)("h4",{id:"personalizing-user-settings-in-a-dev-container"},"Personalizing user settings in a dev container"),(0,i.kt)("p",null,"VSCode supports applying your user settings, such as your ",(0,i.kt)("inlineCode",{parentName:"p"},".gitconfig"),", to a dev container through the use of ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories"},"dotfiles repositories"),". This can be done through your own VSCode ",(0,i.kt)("inlineCode",{parentName:"p"},"settings.json")," file without changing the dev container image or configuration."),(0,i.kt)("h3",{id:"tests"},"Tests"),(0,i.kt)("p",null,"Once you can successfully ",(0,i.kt)("inlineCode",{parentName:"p"},"make")," the project, any code contributions should also successfully:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Pass unit tests via ",(0,i.kt)("inlineCode",{parentName:"li"},"make test"),"."),(0,i.kt)("li",{parentName:"ul"},"Lint cleanly via ",(0,i.kt)("inlineCode",{parentName:"li"},"make lint"),".")),(0,i.kt)("p",null,"Pull requests will also be expected to pass the PR functional tests specified by ",(0,i.kt)("inlineCode",{parentName:"p"},".github/workflows/build.yml"),"."),(0,i.kt)("h3",{id:"pull-requests"},"Pull Requests"),(0,i.kt)("p",null,"If you'd like to start contributing code to the project, you can search for ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/labels/good%20first%20issue"},"issues with the ",(0,i.kt)("inlineCode",{parentName:"a"},"good first issue")," label"),". Other kinds of PR contributions we would look for include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Fixes for bugs and other correctness issues."),(0,i.kt)("li",{parentName:"ul"},"Docs and other content improvements (e.g. samples)."),(0,i.kt)("li",{parentName:"ul"},"Extensions to support parsing new scanning report formats."),(0,i.kt)("li",{parentName:"ul"},"Extensions to support patching images based on new distros or using new package managers.")),(0,i.kt)("p",null,"For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Architectural changes (e.g. breaking interfaces or violations of ",(0,i.kt)("a",{parentName:"li",href:"/copacetic/website/v0.5.x/design"},"this project's design tenets"),")."),(0,i.kt)("li",{parentName:"ul"},"Unsolicited features that significantly expand the functional scope of the tool.")),(0,i.kt)("p",null,"Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format"},"Angular commit message format")," for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs."),(0,i.kt)("p",null,"We suggest:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Use the standard header format of ",(0,i.kt)("inlineCode",{parentName:"li"},'": "')," where the ",(0,i.kt)("inlineCode",{parentName:"li"},"")," is one of the following:",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"build:")," Changes that affect the build system or external dependencies"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"ci:")," Changes to the GitHub workflows and configurations"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"docs:")," Documentation only changes"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"feat:")," A new feature"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"fix:")," A bug fix"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"perf:")," A code change that improves performance"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"refactor:")," A code change that neither fixes a bug nor adds a feature"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"test:")," Adding missing tests or correcting existing tests"))),(0,i.kt)("li",{parentName:"ul"},"Use a ",(0,i.kt)("a",{parentName:"li",href:"https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html"},"concise, imperative description")," of the changes included in the ",(0,i.kt)("inlineCode",{parentName:"li"},"")," of the header, the body of the PR, and generally in your commit messages."),(0,i.kt)("li",{parentName:"ul"},"Use ",(0,i.kt)("a",{parentName:"li",href:"https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests"},"GitHub keywords")," in the footer of your PR description, such as ",(0,i.kt)("inlineCode",{parentName:"li"},"closes")," to automatically close issues the PR intends to address.")),(0,i.kt)("h2",{id:"developer-certificate-of-origin-dco"},"Developer Certificate of Origin (DCO)"),(0,i.kt)("p",null,"The ",(0,i.kt)("a",{parentName:"p",href:"https://wiki.linuxfoundation.org/dco"},"Developer Certificate of Origin")," (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the ",(0,i.kt)("a",{parentName:"p",href:"https://developercertificate.org/"},"full text of the DCO"),", reformatted for readability:"),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},"By making a contribution to this project, I certify that:"),(0,i.kt)("p",{parentName:"blockquote"},"(a) The contribution was created in whole or in part by me and I\nhave the right to submit it under the open source license\nindicated in the file; or"),(0,i.kt)("p",{parentName:"blockquote"},"(b) The contribution is based upon previous work that, to the best\nof my knowledge, is covered under an appropriate open source\nlicense and I have the right under that license to submit that\nwork with modifications, whether created in whole or in part\nby me, under the same open source license (unless I am\npermitted to submit under a different license), as indicated\nin the file; or"),(0,i.kt)("p",{parentName:"blockquote"},"(c) The contribution was provided directly to me by some other\nperson who certified (a), (b) or (c) and I have not modified\nit."),(0,i.kt)("p",{parentName:"blockquote"},"(d) I understand and agree that this project and the contribution\nare public and that a record of the contribution (including all\npersonal information I submit with it, including my sign-off) is\nmaintained indefinitely and may be redistributed consistent with\nthis project or the open source license(s) involved.")),(0,i.kt)("p",null,"Contributors ",(0,i.kt)("em",{parentName:"p"},"sign-off")," that they adhere to these requirements by adding a ",(0,i.kt)("inlineCode",{parentName:"p"},"Signed-off-by")," line to commit messages."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-text"},"This is my commit message\n\nSigned-off-by: Random J Developer \n")),(0,i.kt)("p",null,"Git even has a ",(0,i.kt)("inlineCode",{parentName:"p"},"-s")," command line option to append this automatically to your commit message:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"git commit -s -m 'This is my commit message'\n")),(0,i.kt)("p",null,"Pull requests that do not contain a valid ",(0,i.kt)("inlineCode",{parentName:"p"},"Signed-off-by")," line cannot be merged."),(0,i.kt)("h3",{id:"i-didnt-sign-my-commit-now-what"},"I didn't sign my commit, now what?"),(0,i.kt)("p",null,"No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"git switch \ngit commit --amend --no-edit --signoff\ngit push --force-with-lease \n")),(0,i.kt)("h2",{id:"code-of-conduct"},"Code of Conduct"),(0,i.kt)("p",null,"This project has adopted the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.5.x/code-of-conduct"},"Contributor Covenant Code of Conduct"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/11d58a17.31780db1.js b/website/assets/js/11d58a17.31780db1.js deleted file mode 100644 index 3f6c9c47..00000000 --- a/website/assets/js/11d58a17.31780db1.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[154],{3905:(e,n,t)=>{t.d(n,{Zo:()=>p,kt:()=>f});var o=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);n&&(o=o.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,o)}return t}function a(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var c=o.createContext({}),s=function(e){var n=o.useContext(c),t=n;return e&&(t="function"==typeof e?e(n):a(a({},n),e)),t},p=function(e){var n=s(e.components);return o.createElement(c.Provider,{value:n},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var n=e.children;return o.createElement(o.Fragment,{},n)}},m=o.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,c=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=s(t),m=i,f=u["".concat(c,".").concat(m)]||u[m]||d[m]||r;return t?o.createElement(f,a(a({ref:n},p),{},{components:t})):o.createElement(f,a({ref:n},p))}));function f(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,a=new Array(r);a[0]=m;var l={};for(var c in n)hasOwnProperty.call(n,c)&&(l[c]=n[c]);l.originalType=e,l[u]="string"==typeof e?e:i,a[1]=l;for(var s=2;s{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>r,metadata:()=>l,toc:()=>s});var o=t(7462),i=(t(7294),t(3905));const r={title:"Code of Conduct"},a="Contributor Covenant Code of Conduct",l={unversionedId:"code-of-conduct",id:"version-v0.5.x/code-of-conduct",title:"Code of Conduct",description:"Our Pledge",source:"@site/versioned_docs/version-v0.5.x/code-of-conduct.md",sourceDirName:".",slug:"/code-of-conduct",permalink:"/copacetic/website/v0.5.x/code-of-conduct",draft:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Code of Conduct"},sidebar:"sidebar",previous:{title:"Contributing",permalink:"/copacetic/website/v0.5.x/contributing"},next:{title:"Copa Github Action",permalink:"/copacetic/website/v0.5.x/github-action"}},c={},s=[{value:"Our Pledge",id:"our-pledge",level:2},{value:"Our Standards",id:"our-standards",level:2},{value:"Enforcement Responsibilities",id:"enforcement-responsibilities",level:2},{value:"Scope",id:"scope",level:2},{value:"Enforcement",id:"enforcement",level:2},{value:"Enforcement Guidelines",id:"enforcement-guidelines",level:2},{value:"1. Correction",id:"1-correction",level:3},{value:"2. Warning",id:"2-warning",level:3},{value:"3. Temporary Ban",id:"3-temporary-ban",level:3},{value:"4. Permanent Ban",id:"4-permanent-ban",level:3},{value:"Attribution",id:"attribution",level:2}],p={toc:s};function u(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,o.Z)({},p,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"contributor-covenant-code-of-conduct"},"Contributor Covenant Code of Conduct"),(0,i.kt)("h2",{id:"our-pledge"},"Our Pledge"),(0,i.kt)("p",null,"We as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation."),(0,i.kt)("p",null,"We pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community."),(0,i.kt)("h2",{id:"our-standards"},"Our Standards"),(0,i.kt)("p",null,"Examples of behavior that contributes to a positive environment for our\ncommunity include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Demonstrating empathy and kindness toward other people"),(0,i.kt)("li",{parentName:"ul"},"Being respectful of differing opinions, viewpoints, and experiences"),(0,i.kt)("li",{parentName:"ul"},"Giving and gracefully accepting constructive feedback"),(0,i.kt)("li",{parentName:"ul"},"Accepting responsibility and apologizing to those affected by our mistakes,\nand learning from the experience"),(0,i.kt)("li",{parentName:"ul"},"Focusing on what is best not just for us as individuals, but for the overall\ncommunity")),(0,i.kt)("p",null,"Examples of unacceptable behavior include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"The use of sexualized language or imagery, and sexual attention or advances of\nany kind"),(0,i.kt)("li",{parentName:"ul"},"Trolling, insulting or derogatory comments, and personal or political attacks"),(0,i.kt)("li",{parentName:"ul"},"Public or private harassment"),(0,i.kt)("li",{parentName:"ul"},"Publishing others' private information, such as a physical or email address,\nwithout their explicit permission"),(0,i.kt)("li",{parentName:"ul"},"Other conduct which could reasonably be considered inappropriate in a\nprofessional setting")),(0,i.kt)("h2",{id:"enforcement-responsibilities"},"Enforcement Responsibilities"),(0,i.kt)("p",null,"Community leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful."),(0,i.kt)("p",null,"Community leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate."),(0,i.kt)("h2",{id:"scope"},"Scope"),(0,i.kt)("p",null,"This Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event."),(0,i.kt)("h2",{id:"enforcement"},"Enforcement"),(0,i.kt)("p",null,"Instances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n",(0,i.kt)("inlineCode",{parentName:"p"},"project-copacetic@googlegroups.com"),".\nAll complaints will be reviewed and investigated promptly and fairly."),(0,i.kt)("p",null,"All community leaders are obligated to respect the privacy and security of the\nreporter of any incident."),(0,i.kt)("h2",{id:"enforcement-guidelines"},"Enforcement Guidelines"),(0,i.kt)("p",null,"Community leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:"),(0,i.kt)("h3",{id:"1-correction"},"1. Correction"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested."),(0,i.kt)("h3",{id:"2-warning"},"2. Warning"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": A violation through a single incident or series of\nactions."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban."),(0,i.kt)("h3",{id:"3-temporary-ban"},"3. Temporary Ban"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": A serious violation of community standards, including\nsustained inappropriate behavior."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban."),(0,i.kt)("h3",{id:"4-permanent-ban"},"4. Permanent Ban"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A permanent ban from any sort of public interaction within the\ncommunity."),(0,i.kt)("h2",{id:"attribution"},"Attribution"),(0,i.kt)("p",null,"This Code of Conduct is adapted from the ",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org"},"Contributor Covenant"),",\nversion 2.1, available at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"},"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"),"."),(0,i.kt)("p",null,"Community Impact Guidelines were inspired by\n",(0,i.kt)("a",{parentName:"p",href:"https://github.com/mozilla/diversity"},"Mozilla's code of conduct enforcement ladder"),"."),(0,i.kt)("p",null,"For answers to common questions about this code of conduct, see the FAQ at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/faq"},"https://www.contributor-covenant.org/faq"),". Translations are available at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/translations"},"https://www.contributor-covenant.org/translations"),"."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/11d58a17.6adc7a26.js b/website/assets/js/11d58a17.6adc7a26.js new file mode 100644 index 00000000..2a17ca66 --- /dev/null +++ b/website/assets/js/11d58a17.6adc7a26.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7973],{7695:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>l});var o=i(4848),t=i(8453);const r={title:"Code of Conduct"},a="Contributor Covenant Code of Conduct",s={id:"code-of-conduct",title:"Code of Conduct",description:"Our Pledge",source:"@site/versioned_docs/version-v0.5.x/code-of-conduct.md",sourceDirName:".",slug:"/code-of-conduct",permalink:"/copacetic/website/v0.5.x/code-of-conduct",draft:!1,unlisted:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Code of Conduct"},sidebar:"sidebar",previous:{title:"Contributing",permalink:"/copacetic/website/v0.5.x/contributing"},next:{title:"Copa Github Action",permalink:"/copacetic/website/v0.5.x/github-action"}},c={},l=[{value:"Our Pledge",id:"our-pledge",level:2},{value:"Our Standards",id:"our-standards",level:2},{value:"Enforcement Responsibilities",id:"enforcement-responsibilities",level:2},{value:"Scope",id:"scope",level:2},{value:"Enforcement",id:"enforcement",level:2},{value:"Enforcement Guidelines",id:"enforcement-guidelines",level:2},{value:"1. Correction",id:"1-correction",level:3},{value:"2. Warning",id:"2-warning",level:3},{value:"3. Temporary Ban",id:"3-temporary-ban",level:3},{value:"4. Permanent Ban",id:"4-permanent-ban",level:3},{value:"Attribution",id:"attribution",level:2}];function d(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",li:"li",p:"p",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.h1,{id:"contributor-covenant-code-of-conduct",children:"Contributor Covenant Code of Conduct"}),"\n",(0,o.jsx)(n.h2,{id:"our-pledge",children:"Our Pledge"}),"\n",(0,o.jsx)(n.p,{children:"We as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation."}),"\n",(0,o.jsx)(n.p,{children:"We pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community."}),"\n",(0,o.jsx)(n.h2,{id:"our-standards",children:"Our Standards"}),"\n",(0,o.jsx)(n.p,{children:"Examples of behavior that contributes to a positive environment for our\ncommunity include:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"Demonstrating empathy and kindness toward other people"}),"\n",(0,o.jsx)(n.li,{children:"Being respectful of differing opinions, viewpoints, and experiences"}),"\n",(0,o.jsx)(n.li,{children:"Giving and gracefully accepting constructive feedback"}),"\n",(0,o.jsx)(n.li,{children:"Accepting responsibility and apologizing to those affected by our mistakes,\nand learning from the experience"}),"\n",(0,o.jsx)(n.li,{children:"Focusing on what is best not just for us as individuals, but for the overall\ncommunity"}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"Examples of unacceptable behavior include:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"The use of sexualized language or imagery, and sexual attention or advances of\nany kind"}),"\n",(0,o.jsx)(n.li,{children:"Trolling, insulting or derogatory comments, and personal or political attacks"}),"\n",(0,o.jsx)(n.li,{children:"Public or private harassment"}),"\n",(0,o.jsx)(n.li,{children:"Publishing others' private information, such as a physical or email address,\nwithout their explicit permission"}),"\n",(0,o.jsx)(n.li,{children:"Other conduct which could reasonably be considered inappropriate in a\nprofessional setting"}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"enforcement-responsibilities",children:"Enforcement Responsibilities"}),"\n",(0,o.jsx)(n.p,{children:"Community leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful."}),"\n",(0,o.jsx)(n.p,{children:"Community leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate."}),"\n",(0,o.jsx)(n.h2,{id:"scope",children:"Scope"}),"\n",(0,o.jsx)(n.p,{children:"This Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event."}),"\n",(0,o.jsx)(n.h2,{id:"enforcement",children:"Enforcement"}),"\n",(0,o.jsxs)(n.p,{children:["Instances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n",(0,o.jsx)(n.code,{children:"project-copacetic@googlegroups.com"}),".\nAll complaints will be reviewed and investigated promptly and fairly."]}),"\n",(0,o.jsx)(n.p,{children:"All community leaders are obligated to respect the privacy and security of the\nreporter of any incident."}),"\n",(0,o.jsx)(n.h2,{id:"enforcement-guidelines",children:"Enforcement Guidelines"}),"\n",(0,o.jsx)(n.p,{children:"Community leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:"}),"\n",(0,o.jsx)(n.h3,{id:"1-correction",children:"1. Correction"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested."]}),"\n",(0,o.jsx)(n.h3,{id:"2-warning",children:"2. Warning"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": A violation through a single incident or series of\nactions."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban."]}),"\n",(0,o.jsx)(n.h3,{id:"3-temporary-ban",children:"3. Temporary Ban"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": A serious violation of community standards, including\nsustained inappropriate behavior."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban."]}),"\n",(0,o.jsx)(n.h3,{id:"4-permanent-ban",children:"4. Permanent Ban"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A permanent ban from any sort of public interaction within the\ncommunity."]}),"\n",(0,o.jsx)(n.h2,{id:"attribution",children:"Attribution"}),"\n",(0,o.jsxs)(n.p,{children:["This Code of Conduct is adapted from the ",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org",children:"Contributor Covenant"}),",\nversion 2.1, available at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html",children:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["Community Impact Guidelines were inspired by\n",(0,o.jsx)(n.a,{href:"https://github.com/mozilla/diversity",children:"Mozilla's code of conduct enforcement ladder"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["For answers to common questions about this code of conduct, see the FAQ at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/faq",children:"https://www.contributor-covenant.org/faq"}),". Translations are available at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/translations",children:"https://www.contributor-covenant.org/translations"}),"."]})]})}function u(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>a,x:()=>s});var o=i(6540);const t={},r=o.createContext(t);function a(e){const n=o.useContext(r);return o.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function s(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:a(e.components),o.createElement(r.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/12042f32.0e78f076.js b/website/assets/js/12042f32.0e78f076.js new file mode 100644 index 00000000..2214f846 --- /dev/null +++ b/website/assets/js/12042f32.0e78f076.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1653],{7894:(e,i,a)=>{a.r(i),a.d(i,{assets:()=>r,contentTitle:()=>c,default:()=>d,frontMatter:()=>o,metadata:()=>s,toc:()=>l});var n=a(4848),t=a(8453);const o={title:"FAQ"},c=void 0,s={id:"faq",title:"FAQ",description:"What kind of vulnerabilities can Copa patch?",source:"@site/versioned_docs/version-v0.6.x/faq.md",sourceDirName:".",slug:"/faq",permalink:"/copacetic/website/faq",draft:!1,unlisted:!1,tags:[],version:"v0.6.x",frontMatter:{title:"FAQ"},sidebar:"sidebar",previous:{title:"Troubleshooting",permalink:"/copacetic/website/troubleshooting"},next:{title:"Github Action",permalink:"/copacetic/website/github-action"}},r={},l=[{value:"What kind of vulnerabilities can Copa patch?",id:"what-kind-of-vulnerabilities-can-copa-patch",level:2},{value:"What kind of vulnerabilities can Copa not patch?",id:"what-kind-of-vulnerabilities-can-copa-not-patch",level:2},{value:"Can I replace the package repositories in the image with my own?",id:"can-i-replace-the-package-repositories-in-the-image-with-my-own",level:2}];function p(e){const i={a:"a",admonition:"admonition",blockquote:"blockquote",code:"code",h2:"h2",p:"p",pre:"pre",...(0,t.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(i.h2,{id:"what-kind-of-vulnerabilities-can-copa-patch",children:"What kind of vulnerabilities can Copa patch?"}),"\n",(0,n.jsxs)(i.p,{children:['Copa is capable of patching "OS level" vulnerabilities. This includes packages (like ',(0,n.jsx)(i.code,{children:"openssl"}),") in the image that are managed by a package manager such as ",(0,n.jsx)(i.code,{children:"apt"})," or ",(0,n.jsx)(i.code,{children:"yum"}),'. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see ',(0,n.jsx)(i.a,{href:"#what-kind-of-vulnerabilities-can-copa-not-patch",children:"below"})," for more details)."]}),"\n",(0,n.jsx)(i.h2,{id:"what-kind-of-vulnerabilities-can-copa-not-patch",children:"What kind of vulnerabilities can Copa not patch?"}),"\n",(0,n.jsxs)(i.p,{children:['Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the ',(0,n.jsx)(i.code,{children:"golang.org/x/net"})," module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level."]}),"\n",(0,n.jsxs)(i.p,{children:["To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like ",(0,n.jsx)(i.code,{children:"http://archive.ubuntu.com/ubuntu/"})," for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications."]}),"\n",(0,n.jsx)(i.h2,{id:"can-i-replace-the-package-repositories-in-the-image-with-my-own",children:"Can I replace the package repositories in the image with my own?"}),"\n",(0,n.jsx)(i.admonition,{type:"caution",children:(0,n.jsx)(i.p,{children:"Experimental: This feature might change without preserving backwards compatibility."})}),"\n",(0,n.jsxs)(i.p,{children:["Copa does not support replacing the repositories in the package managers with alternatives. Images must already use the intended package repositories. For example, for debian, updating ",(0,n.jsx)(i.code,{children:"/etc/apt/sources.list"})," from ",(0,n.jsx)(i.code,{children:"http://archive.ubuntu.com/ubuntu/"})," to a mirror, such as ",(0,n.jsx)(i.code,{children:"https://mirrors.wikimedia.org/ubuntu/"}),"."]}),"\n",(0,n.jsxs)(i.p,{children:["If you need the tooling image to use a different package repository, you can create a source policy to define a replacement image and/or pin to a digest. For example, the following source policy replaces ",(0,n.jsx)(i.code,{children:"docker.io/library/debian:11-slim"})," image with ",(0,n.jsx)(i.code,{children:"foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5"}),":"]}),"\n",(0,n.jsx)(i.pre,{children:(0,n.jsx)(i.code,{className:"language-shell",children:'cat < source-policy.json\n{\n "rules": [\n {\n "action": "CONVERT",\n "selector": {\n "identifier": "docker-image://docker.io/library/debian:11-slim"\n },\n "updates": {\n "identifier": "docker-image://foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5"\n }\n }\n ]\n}\nEOF\n\nexport EXPERIMENTAL_BUILDKIT_SOURCE_POLICY=source-policy.json\n'})}),"\n",(0,n.jsxs)(i.blockquote,{children:["\n",(0,n.jsxs)(i.p,{children:["Tooling image for Debian-based images are ",(0,n.jsx)(i.code,{children:"docker.io/library/debian:11-slim"})," and RPM-based repos are ",(0,n.jsx)(i.code,{children:"mcr.microsoft.com/cbl-mariner/base/core:2.0"}),"."]}),"\n"]}),"\n",(0,n.jsxs)(i.p,{children:["For more information on source policies, see ",(0,n.jsx)(i.a,{href:"https://docs.docker.com/build/building/env-vars/#experimental_buildkit_source_policy",children:"Buildkit Source Policies"}),"."]})]})}function d(e={}){const{wrapper:i}={...(0,t.R)(),...e.components};return i?(0,n.jsx)(i,{...e,children:(0,n.jsx)(p,{...e})}):p(e)}},8453:(e,i,a)=>{a.d(i,{R:()=>c,x:()=>s});var n=a(6540);const t={},o=n.createContext(t);function c(e){const i=n.useContext(o);return n.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function s(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:c(e.components),n.createElement(o.Provider,{value:i},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/12042f32.aeeebb3d.js b/website/assets/js/12042f32.aeeebb3d.js deleted file mode 100644 index 5d1b8ca1..00000000 --- a/website/assets/js/12042f32.aeeebb3d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1802],{3905:(e,t,a)=>{a.d(t,{Zo:()=>s,kt:()=>h});var n=a(7294);function i(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function o(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function r(e){for(var t=1;t=0||(i[a]=e[a]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(i[a]=e[a])}return i}var c=n.createContext({}),p=function(e){var t=n.useContext(c),a=t;return e&&(a="function"==typeof e?e(t):r(r({},t),e)),a},s=function(e){var t=p(e.components);return n.createElement(c.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var a=e.components,i=e.mdxType,o=e.originalType,c=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),u=p(a),m=i,h=u["".concat(c,".").concat(m)]||u[m]||d[m]||o;return a?n.createElement(h,r(r({ref:t},s),{},{components:a})):n.createElement(h,r({ref:t},s))}));function h(e,t){var a=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=a.length,r=new Array(o);r[0]=m;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[u]="string"==typeof e?e:i,r[1]=l;for(var p=2;p{a.r(t),a.d(t,{assets:()=>c,contentTitle:()=>r,default:()=>u,frontMatter:()=>o,metadata:()=>l,toc:()=>p});var n=a(7462),i=(a(7294),a(3905));const o={title:"FAQ"},r=void 0,l={unversionedId:"faq",id:"version-v0.6.x/faq",title:"FAQ",description:"What kind of vulnerabilities can Copa patch?",source:"@site/versioned_docs/version-v0.6.x/faq.md",sourceDirName:".",slug:"/faq",permalink:"/copacetic/website/faq",draft:!1,tags:[],version:"v0.6.x",frontMatter:{title:"FAQ"},sidebar:"sidebar",previous:{title:"Troubleshooting",permalink:"/copacetic/website/troubleshooting"},next:{title:"Github Action",permalink:"/copacetic/website/github-action"}},c={},p=[{value:"What kind of vulnerabilities can Copa patch?",id:"what-kind-of-vulnerabilities-can-copa-patch",level:2},{value:"What kind of vulnerabilities can Copa not patch?",id:"what-kind-of-vulnerabilities-can-copa-not-patch",level:2},{value:"Can I replace the package repositories in the image with my own?",id:"can-i-replace-the-package-repositories-in-the-image-with-my-own",level:2}],s={toc:p};function u(e){let{components:t,...a}=e;return(0,i.kt)("wrapper",(0,n.Z)({},s,a,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h2",{id:"what-kind-of-vulnerabilities-can-copa-patch"},"What kind of vulnerabilities can Copa patch?"),(0,i.kt)("p",null,'Copa is capable of patching "OS level" vulnerabilities. This includes packages (like ',(0,i.kt)("inlineCode",{parentName:"p"},"openssl"),") in the image that are managed by a package manager such as ",(0,i.kt)("inlineCode",{parentName:"p"},"apt")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"yum"),'. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see ',(0,i.kt)("a",{parentName:"p",href:"#what-kind-of-vulnerabilities-can-copa-not-patch"},"below")," for more details)."),(0,i.kt)("h2",{id:"what-kind-of-vulnerabilities-can-copa-not-patch"},"What kind of vulnerabilities can Copa not patch?"),(0,i.kt)("p",null,'Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the ',(0,i.kt)("inlineCode",{parentName:"p"},"golang.org/x/net")," module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level."),(0,i.kt)("p",null,"To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like ",(0,i.kt)("inlineCode",{parentName:"p"},"http://archive.ubuntu.com/ubuntu/")," for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications."),(0,i.kt)("h2",{id:"can-i-replace-the-package-repositories-in-the-image-with-my-own"},"Can I replace the package repositories in the image with my own?"),(0,i.kt)("admonition",{type:"caution"},(0,i.kt)("p",{parentName:"admonition"},"Experimental: This feature might change without preserving backwards compatibility.")),(0,i.kt)("p",null,"Copa does not support replacing the repositories in the package managers with alternatives. Images must already use the intended package repositories. For example, for debian, updating ",(0,i.kt)("inlineCode",{parentName:"p"},"/etc/apt/sources.list")," from ",(0,i.kt)("inlineCode",{parentName:"p"},"http://archive.ubuntu.com/ubuntu/")," to a mirror, such as ",(0,i.kt)("inlineCode",{parentName:"p"},"https://mirrors.wikimedia.org/ubuntu/"),"."),(0,i.kt)("p",null,"If you need the tooling image to use a different package repository, you can create a source policy to define a replacement image and/or pin to a digest. For example, the following source policy replaces ",(0,i.kt)("inlineCode",{parentName:"p"},"docker.io/library/debian:11-slim")," image with ",(0,i.kt)("inlineCode",{parentName:"p"},"foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-shell"},'cat < source-policy.json\n{\n "rules": [\n {\n "action": "CONVERT",\n "selector": {\n "identifier": "docker-image://docker.io/library/debian:11-slim"\n },\n "updates": {\n "identifier": "docker-image://foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5"\n }\n }\n ]\n}\nEOF\n\nexport EXPERIMENTAL_BUILDKIT_SOURCE_POLICY=source-policy.json\n')),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},"Tooling image for Debian-based images are ",(0,i.kt)("inlineCode",{parentName:"p"},"docker.io/library/debian:11-slim")," and RPM-based repos are ",(0,i.kt)("inlineCode",{parentName:"p"},"mcr.microsoft.com/cbl-mariner/base/core:2.0"),".")),(0,i.kt)("p",null,"For more information on source policies, see ",(0,i.kt)("a",{parentName:"p",href:"https://docs.docker.com/build/building/env-vars/#experimental_buildkit_source_policy"},"Buildkit Source Policies"),"."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/14aa7e32.2379c39a.js b/website/assets/js/14aa7e32.2379c39a.js new file mode 100644 index 00000000..6030c6e6 --- /dev/null +++ b/website/assets/js/14aa7e32.2379c39a.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3409],{4186:(t,e,n)=>{n.r(e),n.d(e,{assets:()=>a,contentTitle:()=>s,default:()=>l,frontMatter:()=>c,metadata:()=>r,toc:()=>u});var o=n(4848),i=n(8453);const c={title:"Github Action"},s=void 0,r={id:"github-action",title:"Github Action",description:"The Copa Github Action allows you patch vulnerable containers in your GitHub Actions workflows using Copa.",source:"@site/docs/github-action.md",sourceDirName:".",slug:"/github-action",permalink:"/copacetic/website/next/github-action",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{title:"Github Action"},sidebar:"sidebar",previous:{title:"FAQ",permalink:"/copacetic/website/next/faq"},next:{title:"Custom buildkit addresses",permalink:"/copacetic/website/next/custom-address"}},a={},u=[];function p(t){const e={a:"a",p:"p",...(0,i.R)(),...t.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsxs)(e.p,{children:["The ",(0,o.jsx)(e.a,{href:"https://github.com/project-copacetic/copa-action",children:"Copa Github Action"})," allows you patch vulnerable containers in your GitHub Actions workflows using Copa."]}),"\n",(0,o.jsxs)(e.p,{children:["Please refer to ",(0,o.jsx)(e.a,{href:"https://github.com/project-copacetic/copa-action",children:"Copa Github Action"})," for more details on how to use it."]})]})}function l(t={}){const{wrapper:e}={...(0,i.R)(),...t.components};return e?(0,o.jsx)(e,{...t,children:(0,o.jsx)(p,{...t})}):p(t)}},8453:(t,e,n)=>{n.d(e,{R:()=>s,x:()=>r});var o=n(6540);const i={},c=o.createContext(i);function s(t){const e=o.useContext(c);return o.useMemo((function(){return"function"==typeof t?t(e):{...e,...t}}),[e,t])}function r(t){let e;return e=t.disableParentContext?"function"==typeof t.components?t.components(i):t.components||i:s(t.components),o.createElement(c.Provider,{value:e},t.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/14aa7e32.4837c057.js b/website/assets/js/14aa7e32.4837c057.js deleted file mode 100644 index d73da21b..00000000 --- a/website/assets/js/14aa7e32.4837c057.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5897],{3905:(e,t,r)=>{r.d(t,{Zo:()=>l,kt:()=>m});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var p=n.createContext({}),u=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},l=function(e){var t=u(e.components);return n.createElement(p.Provider,{value:t},e.children)},s="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},b=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,i=e.originalType,p=e.parentName,l=c(e,["components","mdxType","originalType","parentName"]),s=u(r),b=o,m=s["".concat(p,".").concat(b)]||s[b]||f[b]||i;return r?n.createElement(m,a(a({ref:t},l),{},{components:r})):n.createElement(m,a({ref:t},l))}));function m(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=r.length,a=new Array(i);a[0]=b;var c={};for(var p in t)hasOwnProperty.call(t,p)&&(c[p]=t[p]);c.originalType=e,c[s]="string"==typeof e?e:o,a[1]=c;for(var u=2;u{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>a,default:()=>s,frontMatter:()=>i,metadata:()=>c,toc:()=>u});var n=r(7462),o=(r(7294),r(3905));const i={title:"Github Action"},a=void 0,c={unversionedId:"github-action",id:"github-action",title:"Github Action",description:"The Copa Github Action allows you patch vulnerable containers in your GitHub Actions workflows using Copa.",source:"@site/docs/github-action.md",sourceDirName:".",slug:"/github-action",permalink:"/copacetic/website/next/github-action",draft:!1,tags:[],version:"current",frontMatter:{title:"Github Action"},sidebar:"sidebar",previous:{title:"FAQ",permalink:"/copacetic/website/next/faq"},next:{title:"Custom buildkit addresses",permalink:"/copacetic/website/next/custom-address"}},p={},u=[],l={toc:u};function s(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},l,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"The ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copa-action"},"Copa Github Action")," allows you patch vulnerable containers in your GitHub Actions workflows using Copa."),(0,o.kt)("p",null,"Please refer to ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copa-action"},"Copa Github Action")," for more details on how to use it."))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/14b4e536.64a562da.js b/website/assets/js/14b4e536.64a562da.js new file mode 100644 index 00000000..d119fa50 --- /dev/null +++ b/website/assets/js/14b4e536.64a562da.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8355],{5306:(t,e,o)=>{o.r(e),o.d(e,{assets:()=>a,contentTitle:()=>s,default:()=>l,frontMatter:()=>c,metadata:()=>r,toc:()=>u});var n=o(4848),i=o(8453);const c={title:"Github Action"},s=void 0,r={id:"github-action",title:"Github Action",description:"The Copa Github Action allows you patch vulnerable containers in your GitHub Actions workflows using Copa.",source:"@site/versioned_docs/version-v0.6.x/github-action.md",sourceDirName:".",slug:"/github-action",permalink:"/copacetic/website/github-action",draft:!1,unlisted:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Github Action"},sidebar:"sidebar",previous:{title:"FAQ",permalink:"/copacetic/website/faq"},next:{title:"Custom buildkit addresses",permalink:"/copacetic/website/custom-address"}},a={},u=[];function p(t){const e={a:"a",p:"p",...(0,i.R)(),...t.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsxs)(e.p,{children:["The ",(0,n.jsx)(e.a,{href:"https://github.com/project-copacetic/copa-action",children:"Copa Github Action"})," allows you patch vulnerable containers in your GitHub Actions workflows using Copa."]}),"\n",(0,n.jsxs)(e.p,{children:["Please refer to ",(0,n.jsx)(e.a,{href:"https://github.com/project-copacetic/copa-action",children:"Copa Github Action"})," for more details on how to use it."]})]})}function l(t={}){const{wrapper:e}={...(0,i.R)(),...t.components};return e?(0,n.jsx)(e,{...t,children:(0,n.jsx)(p,{...t})}):p(t)}},8453:(t,e,o)=>{o.d(e,{R:()=>s,x:()=>r});var n=o(6540);const i={},c=n.createContext(i);function s(t){const e=n.useContext(c);return n.useMemo((function(){return"function"==typeof t?t(e):{...e,...t}}),[e,t])}function r(t){let e;return e=t.disableParentContext?"function"==typeof t.components?t.components(i):t.components||i:s(t.components),n.createElement(c.Provider,{value:e},t.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/14b4e536.fee906c1.js b/website/assets/js/14b4e536.fee906c1.js deleted file mode 100644 index 809a624d..00000000 --- a/website/assets/js/14b4e536.fee906c1.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3186],{3905:(e,t,r)=>{r.d(t,{Zo:()=>l,kt:()=>m});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var p=n.createContext({}),u=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},l=function(e){var t=u(e.components);return n.createElement(p.Provider,{value:t},e.children)},s="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},b=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,i=e.originalType,p=e.parentName,l=c(e,["components","mdxType","originalType","parentName"]),s=u(r),b=o,m=s["".concat(p,".").concat(b)]||s[b]||f[b]||i;return r?n.createElement(m,a(a({ref:t},l),{},{components:r})):n.createElement(m,a({ref:t},l))}));function m(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=r.length,a=new Array(i);a[0]=b;var c={};for(var p in t)hasOwnProperty.call(t,p)&&(c[p]=t[p]);c.originalType=e,c[s]="string"==typeof e?e:o,a[1]=c;for(var u=2;u{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>a,default:()=>s,frontMatter:()=>i,metadata:()=>c,toc:()=>u});var n=r(7462),o=(r(7294),r(3905));const i={title:"Github Action"},a=void 0,c={unversionedId:"github-action",id:"version-v0.6.x/github-action",title:"Github Action",description:"The Copa Github Action allows you patch vulnerable containers in your GitHub Actions workflows using Copa.",source:"@site/versioned_docs/version-v0.6.x/github-action.md",sourceDirName:".",slug:"/github-action",permalink:"/copacetic/website/github-action",draft:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Github Action"},sidebar:"sidebar",previous:{title:"FAQ",permalink:"/copacetic/website/faq"},next:{title:"Custom buildkit addresses",permalink:"/copacetic/website/custom-address"}},p={},u=[],l={toc:u};function s(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},l,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"The ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copa-action"},"Copa Github Action")," allows you patch vulnerable containers in your GitHub Actions workflows using Copa."),(0,o.kt)("p",null,"Please refer to ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copa-action"},"Copa Github Action")," for more details on how to use it."))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/17896441.7677fdee.js b/website/assets/js/17896441.7677fdee.js new file mode 100644 index 00000000..84fcc9ef --- /dev/null +++ b/website/assets/js/17896441.7677fdee.js @@ -0,0 +1 @@ +(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8401],{4313:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>qt});var s=n(6540),a=n(1003),o=n(9532),i=n(4848);const l=s.createContext(null);function c(e){let{children:t,content:n}=e;const a=function(e){return(0,s.useMemo)((()=>({metadata:e.metadata,frontMatter:e.frontMatter,assets:e.assets,contentTitle:e.contentTitle,toc:e.toc})),[e])}(n);return(0,i.jsx)(l.Provider,{value:a,children:t})}function r(){const e=(0,s.useContext)(l);if(null===e)throw new o.dV("DocProvider");return e}function d(){const{metadata:e,frontMatter:t,assets:n}=r();return(0,i.jsx)(a.be,{title:e.title,description:e.description,keywords:t.keywords,image:n.image??t.image})}var u=n(4164),m=n(4581),h=n(1312),p=n(8774);function f(e){const{permalink:t,title:n,subLabel:s,isNext:a}=e;return(0,i.jsxs)(p.A,{className:(0,u.A)("pagination-nav__link",a?"pagination-nav__link--next":"pagination-nav__link--prev"),to:t,children:[s&&(0,i.jsx)("div",{className:"pagination-nav__sublabel",children:s}),(0,i.jsx)("div",{className:"pagination-nav__label",children:n})]})}function x(e){const{previous:t,next:n}=e;return(0,i.jsxs)("nav",{className:"pagination-nav docusaurus-mt-lg","aria-label":(0,h.T)({id:"theme.docs.paginator.navAriaLabel",message:"Docs pages",description:"The ARIA label for the docs pagination"}),children:[t&&(0,i.jsx)(f,{...t,subLabel:(0,i.jsx)(h.A,{id:"theme.docs.paginator.previous",description:"The label used to navigate to the previous doc",children:"Previous"})}),n&&(0,i.jsx)(f,{...n,subLabel:(0,i.jsx)(h.A,{id:"theme.docs.paginator.next",description:"The label used to navigate to the next doc",children:"Next"}),isNext:!0})]})}function b(){const{metadata:e}=r();return(0,i.jsx)(x,{previous:e.previous,next:e.next})}var g=n(4586),j=n(4070),v=n(7559),N=n(5597),C=n(2252);const A={unreleased:function(e){let{siteTitle:t,versionMetadata:n}=e;return(0,i.jsx)(h.A,{id:"theme.docs.versions.unreleasedVersionLabel",description:"The label used to tell the user that he's browsing an unreleased doc version",values:{siteTitle:t,versionLabel:(0,i.jsx)("b",{children:n.label})},children:"This is unreleased documentation for {siteTitle} {versionLabel} version."})},unmaintained:function(e){let{siteTitle:t,versionMetadata:n}=e;return(0,i.jsx)(h.A,{id:"theme.docs.versions.unmaintainedVersionLabel",description:"The label used to tell the user that he's browsing an unmaintained doc version",values:{siteTitle:t,versionLabel:(0,i.jsx)("b",{children:n.label})},children:"This is documentation for {siteTitle} {versionLabel}, which is no longer actively maintained."})}};function k(e){const t=A[e.versionMetadata.banner];return(0,i.jsx)(t,{...e})}function y(e){let{versionLabel:t,to:n,onClick:s}=e;return(0,i.jsx)(h.A,{id:"theme.docs.versions.latestVersionSuggestionLabel",description:"The label used to tell the user to check the latest version",values:{versionLabel:t,latestVersionLink:(0,i.jsx)("b",{children:(0,i.jsx)(p.A,{to:n,onClick:s,children:(0,i.jsx)(h.A,{id:"theme.docs.versions.latestVersionLinkLabel",description:"The label used for the latest version suggestion link label",children:"latest version"})})})},children:"For up-to-date documentation, see the {latestVersionLink} ({versionLabel})."})}function L(e){let{className:t,versionMetadata:n}=e;const{siteConfig:{title:s}}=(0,g.A)(),{pluginId:a}=(0,j.vT)({failfast:!0}),{savePreferredVersionName:o}=(0,N.g1)(a),{latestDocSuggestion:l,latestVersionSuggestion:c}=(0,j.HW)(a),r=l??(d=c).docs.find((e=>e.id===d.mainDocId));var d;return(0,i.jsxs)("div",{className:(0,u.A)(t,v.G.docs.docVersionBanner,"alert alert--warning margin-bottom--md"),role:"alert",children:[(0,i.jsx)("div",{children:(0,i.jsx)(k,{siteTitle:s,versionMetadata:n})}),(0,i.jsx)("div",{className:"margin-top--md",children:(0,i.jsx)(y,{versionLabel:c.label,to:r.path,onClick:()=>o(c.name)})})]})}function _(e){let{className:t}=e;const n=(0,C.r)();return n.banner?(0,i.jsx)(L,{className:t,versionMetadata:n}):null}function B(e){let{className:t}=e;const n=(0,C.r)();return n.badge?(0,i.jsx)("span",{className:(0,u.A)(t,v.G.docs.docVersionBadge,"badge badge--secondary"),children:(0,i.jsx)(h.A,{id:"theme.docs.versionBadge.label",values:{versionLabel:n.label},children:"Version: {versionLabel}"})}):null}const w={tag:"tag_zVej",tagRegular:"tagRegular_sFm0",tagWithCount:"tagWithCount_h2kH"};function T(e){let{permalink:t,label:n,count:s}=e;return(0,i.jsxs)(p.A,{href:t,className:(0,u.A)(w.tag,s?w.tagWithCount:w.tagRegular),children:[n,s&&(0,i.jsx)("span",{children:s})]})}const E={tags:"tags_jXut",tag:"tag_QGVx"};function H(e){let{tags:t}=e;return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)("b",{children:(0,i.jsx)(h.A,{id:"theme.tags.tagsListLabel",description:"The label alongside a tag list",children:"Tags:"})}),(0,i.jsx)("ul",{className:(0,u.A)(E.tags,"padding--none","margin-left--sm"),children:t.map((e=>{let{label:t,permalink:n}=e;return(0,i.jsx)("li",{className:E.tag,children:(0,i.jsx)(T,{label:t,permalink:n})},n)}))})]})}const M={iconEdit:"iconEdit_Z9Sw"};function I(e){let{className:t,...n}=e;return(0,i.jsx)("svg",{fill:"currentColor",height:"20",width:"20",viewBox:"0 0 40 40",className:(0,u.A)(M.iconEdit,t),"aria-hidden":"true",...n,children:(0,i.jsx)("g",{children:(0,i.jsx)("path",{d:"m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z"})})})}function S(e){let{editUrl:t}=e;return(0,i.jsxs)(p.A,{to:t,className:v.G.common.editThisPage,children:[(0,i.jsx)(I,{}),(0,i.jsx)(h.A,{id:"theme.common.editThisPage",description:"The link label to edit the current page",children:"Edit this page"})]})}function U(e){void 0===e&&(e={});const{i18n:{currentLocale:t}}=(0,g.A)(),n=function(){const{i18n:{currentLocale:e,localeConfigs:t}}=(0,g.A)();return t[e].calendar}();return new Intl.DateTimeFormat(t,{calendar:n,...e})}function V(e){let{lastUpdatedAt:t}=e;const n=new Date(t),s=U({day:"numeric",month:"short",year:"numeric",timeZone:"UTC"}).format(n);return(0,i.jsx)(h.A,{id:"theme.lastUpdated.atDate",description:"The words used to describe on which date a page has been last updated",values:{date:(0,i.jsx)("b",{children:(0,i.jsx)("time",{dateTime:n.toISOString(),itemProp:"dateModified",children:s})})},children:" on {date}"})}function R(e){let{lastUpdatedBy:t}=e;return(0,i.jsx)(h.A,{id:"theme.lastUpdated.byUser",description:"The words used to describe by who the page has been last updated",values:{user:(0,i.jsx)("b",{children:t})},children:" by {user}"})}function z(e){let{lastUpdatedAt:t,lastUpdatedBy:n}=e;return(0,i.jsxs)("span",{className:v.G.common.lastUpdated,children:[(0,i.jsx)(h.A,{id:"theme.lastUpdated.lastUpdatedAtBy",description:"The sentence used to display when a page has been last updated, and by who",values:{atDate:t?(0,i.jsx)(V,{lastUpdatedAt:t}):"",byUser:n?(0,i.jsx)(R,{lastUpdatedBy:n}):""},children:"Last updated{atDate}{byUser}"}),!1]})}const O={lastUpdated:"lastUpdated_JAkA"};function P(e){let{className:t,editUrl:n,lastUpdatedAt:s,lastUpdatedBy:a}=e;return(0,i.jsxs)("div",{className:(0,u.A)("row",t),children:[(0,i.jsx)("div",{className:"col",children:n&&(0,i.jsx)(S,{editUrl:n})}),(0,i.jsx)("div",{className:(0,u.A)("col",O.lastUpdated),children:(s||a)&&(0,i.jsx)(z,{lastUpdatedAt:s,lastUpdatedBy:a})})]})}function G(){const{metadata:e}=r(),{editUrl:t,lastUpdatedAt:n,lastUpdatedBy:s,tags:a}=e,o=a.length>0,l=!!(t||n||s);return o||l?(0,i.jsxs)("footer",{className:(0,u.A)(v.G.docs.docFooter,"docusaurus-mt-lg"),children:[o&&(0,i.jsx)("div",{className:(0,u.A)("row margin-top--sm",v.G.docs.docFooterTagsRow),children:(0,i.jsx)("div",{className:"col",children:(0,i.jsx)(H,{tags:a})})}),l&&(0,i.jsx)(P,{className:(0,u.A)("margin-top--sm",v.G.docs.docFooterEditMetaRow),editUrl:t,lastUpdatedAt:n,lastUpdatedBy:s})]}):null}var D=n(1422),W=n(6342);function $(e){const t=e.map((e=>({...e,parentIndex:-1,children:[]}))),n=Array(7).fill(-1);t.forEach(((e,t)=>{const s=n.slice(2,e.level);e.parentIndex=Math.max(...s),n[e.level]=t}));const s=[];return t.forEach((e=>{const{parentIndex:n,...a}=e;n>=0?t[n].children.push(a):s.push(a)})),s}function F(e){let{toc:t,minHeadingLevel:n,maxHeadingLevel:s}=e;return t.flatMap((e=>{const t=F({toc:e.children,minHeadingLevel:n,maxHeadingLevel:s});return function(e){return e.level>=n&&e.level<=s}(e)?[{...e,children:t}]:t}))}function q(e){const t=e.getBoundingClientRect();return t.top===t.bottom?q(e.parentNode):t}function Z(e,t){let{anchorTopOffset:n}=t;const s=e.find((e=>q(e).top>=n));if(s){return function(e){return e.top>0&&e.bottom{e.current=t?0:document.querySelector(".navbar").clientHeight}),[t]),e}function Y(e){const t=(0,s.useRef)(void 0),n=J();(0,s.useEffect)((()=>{if(!e)return()=>{};const{linkClassName:s,linkActiveClassName:a,minHeadingLevel:o,maxHeadingLevel:i}=e;function l(){const e=function(e){return Array.from(document.getElementsByClassName(e))}(s),l=function(e){let{minHeadingLevel:t,maxHeadingLevel:n}=e;const s=[];for(let a=t;a<=n;a+=1)s.push(`h${a}.anchor`);return Array.from(document.querySelectorAll(s.join()))}({minHeadingLevel:o,maxHeadingLevel:i}),c=Z(l,{anchorTopOffset:n.current}),r=e.find((e=>c&&c.id===function(e){return decodeURIComponent(e.href.substring(e.href.indexOf("#")+1))}(e)));e.forEach((e=>{!function(e,n){n?(t.current&&t.current!==e&&t.current.classList.remove(a),e.classList.add(a),t.current=e):e.classList.remove(a)}(e,e===r)}))}return document.addEventListener("scroll",l),document.addEventListener("resize",l),l(),()=>{document.removeEventListener("scroll",l),document.removeEventListener("resize",l)}}),[e,n])}function K(e){let{toc:t,className:n,linkClassName:s,isChild:a}=e;return t.length?(0,i.jsx)("ul",{className:a?void 0:n,children:t.map((e=>(0,i.jsxs)("li",{children:[(0,i.jsx)(p.A,{to:`#${e.id}`,className:s??void 0,dangerouslySetInnerHTML:{__html:e.value}}),(0,i.jsx)(K,{isChild:!0,toc:e.children,className:n,linkClassName:s})]},e.id)))}):null}const Q=s.memo(K);function X(e){let{toc:t,className:n="table-of-contents table-of-contents__left-border",linkClassName:a="table-of-contents__link",linkActiveClassName:o,minHeadingLevel:l,maxHeadingLevel:c,...r}=e;const d=(0,W.p)(),u=l??d.tableOfContents.minHeadingLevel,m=c??d.tableOfContents.maxHeadingLevel,h=function(e){let{toc:t,minHeadingLevel:n,maxHeadingLevel:a}=e;return(0,s.useMemo)((()=>F({toc:$(t),minHeadingLevel:n,maxHeadingLevel:a})),[t,n,a])}({toc:t,minHeadingLevel:u,maxHeadingLevel:m});return Y((0,s.useMemo)((()=>{if(a&&o)return{linkClassName:a,linkActiveClassName:o,minHeadingLevel:u,maxHeadingLevel:m}}),[a,o,u,m])),(0,i.jsx)(Q,{toc:h,className:n,linkClassName:a,...r})}const ee={tocCollapsibleButton:"tocCollapsibleButton_TO0P",tocCollapsibleButtonExpanded:"tocCollapsibleButtonExpanded_MG3E"};function te(e){let{collapsed:t,...n}=e;return(0,i.jsx)("button",{type:"button",...n,className:(0,u.A)("clean-btn",ee.tocCollapsibleButton,!t&&ee.tocCollapsibleButtonExpanded,n.className),children:(0,i.jsx)(h.A,{id:"theme.TOCCollapsible.toggleButtonLabel",description:"The label used by the button on the collapsible TOC component",children:"On this page"})})}const ne={tocCollapsible:"tocCollapsible_ETCw",tocCollapsibleContent:"tocCollapsibleContent_vkbj",tocCollapsibleExpanded:"tocCollapsibleExpanded_sAul"};function se(e){let{toc:t,className:n,minHeadingLevel:s,maxHeadingLevel:a}=e;const{collapsed:o,toggleCollapsed:l}=(0,D.u)({initialState:!0});return(0,i.jsxs)("div",{className:(0,u.A)(ne.tocCollapsible,!o&&ne.tocCollapsibleExpanded,n),children:[(0,i.jsx)(te,{collapsed:o,onClick:l}),(0,i.jsx)(D.N,{lazy:!0,className:ne.tocCollapsibleContent,collapsed:o,children:(0,i.jsx)(X,{toc:t,minHeadingLevel:s,maxHeadingLevel:a})})]})}const ae={tocMobile:"tocMobile_ITEo"};function oe(){const{toc:e,frontMatter:t}=r();return(0,i.jsx)(se,{toc:e,minHeadingLevel:t.toc_min_heading_level,maxHeadingLevel:t.toc_max_heading_level,className:(0,u.A)(v.G.docs.docTocMobile,ae.tocMobile)})}const ie={tableOfContents:"tableOfContents_bqdL",docItemContainer:"docItemContainer_F8PC"},le="table-of-contents__link toc-highlight",ce="table-of-contents__link--active";function re(e){let{className:t,...n}=e;return(0,i.jsx)("div",{className:(0,u.A)(ie.tableOfContents,"thin-scrollbar",t),children:(0,i.jsx)(X,{...n,linkClassName:le,linkActiveClassName:ce})})}function de(){const{toc:e,frontMatter:t}=r();return(0,i.jsx)(re,{toc:e,minHeadingLevel:t.toc_min_heading_level,maxHeadingLevel:t.toc_max_heading_level,className:v.G.docs.docTocDesktop})}var ue=n(1107),me=n(8453),he=n(5260),pe=n(2303),fe=n(5293);function xe(){const{prism:e}=(0,W.p)(),{colorMode:t}=(0,fe.G)(),n=e.theme,s=e.darkTheme||n;return"dark"===t?s:n}var be=n(8426),ge=n.n(be);const je=/title=(?["'])(?.*?)\1/,ve=/\{(?<range>[\d,-]+)\}/,Ne={js:{start:"\\/\\/",end:""},jsBlock:{start:"\\/\\*",end:"\\*\\/"},jsx:{start:"\\{\\s*\\/\\*",end:"\\*\\/\\s*\\}"},bash:{start:"#",end:""},html:{start:"\x3c!--",end:"--\x3e"}},Ce={...Ne,lua:{start:"--",end:""},wasm:{start:"\\;\\;",end:""},tex:{start:"%",end:""},vb:{start:"['\u2018\u2019]",end:""},vbnet:{start:"(?:_\\s*)?['\u2018\u2019]",end:""},rem:{start:"[Rr][Ee][Mm]\\b",end:""},f90:{start:"!",end:""},ml:{start:"\\(\\*",end:"\\*\\)"},cobol:{start:"\\*>",end:""}},Ae=Object.keys(Ne);function ke(e,t){const n=e.map((e=>{const{start:n,end:s}=Ce[e];return`(?:${n}\\s*(${t.flatMap((e=>[e.line,e.block?.start,e.block?.end].filter(Boolean))).join("|")})\\s*${s})`})).join("|");return new RegExp(`^\\s*(?:${n})\\s*$`)}function ye(e,t){let n=e.replace(/\n$/,"");const{language:s,magicComments:a,metastring:o}=t;if(o&&ve.test(o)){const e=o.match(ve).groups.range;if(0===a.length)throw new Error(`A highlight range has been given in code block's metastring (\`\`\` ${o}), but no magic comment config is available. Docusaurus applies the first magic comment entry's className for metastring ranges.`);const t=a[0].className,s=ge()(e).filter((e=>e>0)).map((e=>[e-1,[t]]));return{lineClassNames:Object.fromEntries(s),code:n}}if(void 0===s)return{lineClassNames:{},code:n};const i=function(e,t){switch(e){case"js":case"javascript":case"ts":case"typescript":return ke(["js","jsBlock"],t);case"jsx":case"tsx":return ke(["js","jsBlock","jsx"],t);case"html":return ke(["js","jsBlock","html"],t);case"python":case"py":case"bash":return ke(["bash"],t);case"markdown":case"md":return ke(["html","jsx","bash"],t);case"tex":case"latex":case"matlab":return ke(["tex"],t);case"lua":case"haskell":case"sql":return ke(["lua"],t);case"wasm":return ke(["wasm"],t);case"vb":case"vba":case"visual-basic":return ke(["vb","rem"],t);case"vbnet":return ke(["vbnet","rem"],t);case"batch":return ke(["rem"],t);case"basic":return ke(["rem","f90"],t);case"fsharp":return ke(["js","ml"],t);case"ocaml":case"sml":return ke(["ml"],t);case"fortran":return ke(["f90"],t);case"cobol":return ke(["cobol"],t);default:return ke(Ae,t)}}(s,a),l=n.split("\n"),c=Object.fromEntries(a.map((e=>[e.className,{start:0,range:""}]))),r=Object.fromEntries(a.filter((e=>e.line)).map((e=>{let{className:t,line:n}=e;return[n,t]}))),d=Object.fromEntries(a.filter((e=>e.block)).map((e=>{let{className:t,block:n}=e;return[n.start,t]}))),u=Object.fromEntries(a.filter((e=>e.block)).map((e=>{let{className:t,block:n}=e;return[n.end,t]})));for(let h=0;h<l.length;){const e=l[h].match(i);if(!e){h+=1;continue}const t=e.slice(1).find((e=>void 0!==e));r[t]?c[r[t]].range+=`${h},`:d[t]?c[d[t]].start=h:u[t]&&(c[u[t]].range+=`${c[u[t]].start}-${h-1},`),l.splice(h,1)}n=l.join("\n");const m={};return Object.entries(c).forEach((e=>{let[t,{range:n}]=e;ge()(n).forEach((e=>{m[e]??=[],m[e].push(t)}))})),{lineClassNames:m,code:n}}const Le={codeBlockContainer:"codeBlockContainer_Ckt0"};function _e(e){let{as:t,...n}=e;const s=function(e){const t={color:"--prism-color",backgroundColor:"--prism-background-color"},n={};return Object.entries(e.plain).forEach((e=>{let[s,a]=e;const o=t[s];o&&"string"==typeof a&&(n[o]=a)})),n}(xe());return(0,i.jsx)(t,{...n,style:s,className:(0,u.A)(n.className,Le.codeBlockContainer,v.G.common.codeBlock)})}const Be={codeBlockContent:"codeBlockContent_biex",codeBlockTitle:"codeBlockTitle_Ktv7",codeBlock:"codeBlock_bY9V",codeBlockStandalone:"codeBlockStandalone_MEMb",codeBlockLines:"codeBlockLines_e6Vv",codeBlockLinesWithNumbering:"codeBlockLinesWithNumbering_o6Pm",buttonGroup:"buttonGroup__atx"};function we(e){let{children:t,className:n}=e;return(0,i.jsx)(_e,{as:"pre",tabIndex:0,className:(0,u.A)(Be.codeBlockStandalone,"thin-scrollbar",n),children:(0,i.jsx)("code",{className:Be.codeBlockLines,children:t})})}const Te={attributes:!0,characterData:!0,childList:!0,subtree:!0};function Ee(e,t){const[n,a]=(0,s.useState)(),i=(0,s.useCallback)((()=>{a(e.current?.closest("[role=tabpanel][hidden]"))}),[e,a]);(0,s.useEffect)((()=>{i()}),[i]),function(e,t,n){void 0===n&&(n=Te);const a=(0,o._q)(t),i=(0,o.Be)(n);(0,s.useEffect)((()=>{const t=new MutationObserver(a);return e&&t.observe(e,i),()=>t.disconnect()}),[e,a,i])}(n,(e=>{e.forEach((e=>{"attributes"===e.type&&"hidden"===e.attributeName&&(t(),i())}))}),{attributes:!0,characterData:!1,childList:!1,subtree:!1})}var He=n(1765);const Me={codeLine:"codeLine_lJS_",codeLineNumber:"codeLineNumber_Tfdd",codeLineContent:"codeLineContent_feaV"};function Ie(e){let{line:t,classNames:n,showLineNumbers:s,getLineProps:a,getTokenProps:o}=e;1===t.length&&"\n"===t[0].content&&(t[0].content="");const l=a({line:t,className:(0,u.A)(n,s&&Me.codeLine)}),c=t.map(((e,t)=>(0,i.jsx)("span",{...o({token:e,key:t})},t)));return(0,i.jsxs)("span",{...l,children:[s?(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)("span",{className:Me.codeLineNumber}),(0,i.jsx)("span",{className:Me.codeLineContent,children:c})]}):c,(0,i.jsx)("br",{})]})}function Se(e){return(0,i.jsx)("svg",{viewBox:"0 0 24 24",...e,children:(0,i.jsx)("path",{fill:"currentColor",d:"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"})})}function Ue(e){return(0,i.jsx)("svg",{viewBox:"0 0 24 24",...e,children:(0,i.jsx)("path",{fill:"currentColor",d:"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"})})}const Ve={copyButtonCopied:"copyButtonCopied_obH4",copyButtonIcons:"copyButtonIcons_eSgA",copyButtonIcon:"copyButtonIcon_y97N",copyButtonSuccessIcon:"copyButtonSuccessIcon_LjdS"};function Re(e){let{code:t,className:n}=e;const[a,o]=(0,s.useState)(!1),l=(0,s.useRef)(void 0),c=(0,s.useCallback)((()=>{!function(e,t){let{target:n=document.body}=void 0===t?{}:t;if("string"!=typeof e)throw new TypeError(`Expected parameter \`text\` to be a \`string\`, got \`${typeof e}\`.`);const s=document.createElement("textarea"),a=document.activeElement;s.value=e,s.setAttribute("readonly",""),s.style.contain="strict",s.style.position="absolute",s.style.left="-9999px",s.style.fontSize="12pt";const o=document.getSelection(),i=o.rangeCount>0&&o.getRangeAt(0);n.append(s),s.select(),s.selectionStart=0,s.selectionEnd=e.length;let l=!1;try{l=document.execCommand("copy")}catch{}s.remove(),i&&(o.removeAllRanges(),o.addRange(i)),a&&a.focus()}(t),o(!0),l.current=window.setTimeout((()=>{o(!1)}),1e3)}),[t]);return(0,s.useEffect)((()=>()=>window.clearTimeout(l.current)),[]),(0,i.jsx)("button",{type:"button","aria-label":a?(0,h.T)({id:"theme.CodeBlock.copied",message:"Copied",description:"The copied button label on code blocks"}):(0,h.T)({id:"theme.CodeBlock.copyButtonAriaLabel",message:"Copy code to clipboard",description:"The ARIA label for copy code blocks button"}),title:(0,h.T)({id:"theme.CodeBlock.copy",message:"Copy",description:"The copy button label on code blocks"}),className:(0,u.A)("clean-btn",n,Ve.copyButton,a&&Ve.copyButtonCopied),onClick:c,children:(0,i.jsxs)("span",{className:Ve.copyButtonIcons,"aria-hidden":"true",children:[(0,i.jsx)(Se,{className:Ve.copyButtonIcon}),(0,i.jsx)(Ue,{className:Ve.copyButtonSuccessIcon})]})})}function ze(e){return(0,i.jsx)("svg",{viewBox:"0 0 24 24",...e,children:(0,i.jsx)("path",{fill:"currentColor",d:"M4 19h6v-2H4v2zM20 5H4v2h16V5zm-3 6H4v2h13.25c1.1 0 2 .9 2 2s-.9 2-2 2H15v-2l-3 3l3 3v-2h2c2.21 0 4-1.79 4-4s-1.79-4-4-4z"})})}const Oe={wordWrapButtonIcon:"wordWrapButtonIcon_Bwma",wordWrapButtonEnabled:"wordWrapButtonEnabled_EoeP"};function Pe(e){let{className:t,onClick:n,isEnabled:s}=e;const a=(0,h.T)({id:"theme.CodeBlock.wordWrapToggle",message:"Toggle word wrap",description:"The title attribute for toggle word wrapping button of code block lines"});return(0,i.jsx)("button",{type:"button",onClick:n,className:(0,u.A)("clean-btn",t,s&&Oe.wordWrapButtonEnabled),"aria-label":a,title:a,children:(0,i.jsx)(ze,{className:Oe.wordWrapButtonIcon,"aria-hidden":"true"})})}function Ge(e){let{children:t,className:n="",metastring:a,title:o,showLineNumbers:l,language:c}=e;const{prism:{defaultLanguage:r,magicComments:d}}=(0,W.p)(),m=function(e){return e?.toLowerCase()}(c??function(e){const t=e.split(" ").find((e=>e.startsWith("language-")));return t?.replace(/language-/,"")}(n)??r),h=xe(),p=function(){const[e,t]=(0,s.useState)(!1),[n,a]=(0,s.useState)(!1),o=(0,s.useRef)(null),i=(0,s.useCallback)((()=>{const n=o.current.querySelector("code");e?n.removeAttribute("style"):(n.style.whiteSpace="pre-wrap",n.style.overflowWrap="anywhere"),t((e=>!e))}),[o,e]),l=(0,s.useCallback)((()=>{const{scrollWidth:e,clientWidth:t}=o.current,n=e>t||o.current.querySelector("code").hasAttribute("style");a(n)}),[o]);return Ee(o,l),(0,s.useEffect)((()=>{l()}),[e,l]),(0,s.useEffect)((()=>(window.addEventListener("resize",l,{passive:!0}),()=>{window.removeEventListener("resize",l)})),[l]),{codeBlockRef:o,isEnabled:e,isCodeScrollable:n,toggle:i}}(),f=function(e){return e?.match(je)?.groups.title??""}(a)||o,{lineClassNames:x,code:b}=ye(t,{metastring:a,language:m,magicComments:d}),g=l??function(e){return Boolean(e?.includes("showLineNumbers"))}(a);return(0,i.jsxs)(_e,{as:"div",className:(0,u.A)(n,m&&!n.includes(`language-${m}`)&&`language-${m}`),children:[f&&(0,i.jsx)("div",{className:Be.codeBlockTitle,children:f}),(0,i.jsxs)("div",{className:Be.codeBlockContent,children:[(0,i.jsx)(He.f4,{theme:h,code:b,language:m??"text",children:e=>{let{className:t,style:n,tokens:s,getLineProps:a,getTokenProps:o}=e;return(0,i.jsx)("pre",{tabIndex:0,ref:p.codeBlockRef,className:(0,u.A)(t,Be.codeBlock,"thin-scrollbar"),style:n,children:(0,i.jsx)("code",{className:(0,u.A)(Be.codeBlockLines,g&&Be.codeBlockLinesWithNumbering),children:s.map(((e,t)=>(0,i.jsx)(Ie,{line:e,getLineProps:a,getTokenProps:o,classNames:x[t],showLineNumbers:g},t)))})})}}),(0,i.jsxs)("div",{className:Be.buttonGroup,children:[(p.isEnabled||p.isCodeScrollable)&&(0,i.jsx)(Pe,{className:Be.codeButton,onClick:()=>p.toggle(),isEnabled:p.isEnabled}),(0,i.jsx)(Re,{className:Be.codeButton,code:b})]})]})]})}function De(e){let{children:t,...n}=e;const a=(0,pe.A)(),o=function(e){return s.Children.toArray(e).some((e=>(0,s.isValidElement)(e)))?e:Array.isArray(e)?e.join(""):e}(t),l="string"==typeof o?Ge:we;return(0,i.jsx)(l,{...n,children:o},String(a))}function We(e){return(0,i.jsx)("code",{...e})}var $e=n(3427);const Fe={details:"details_lb9f",isBrowser:"isBrowser_bmU9",collapsibleContent:"collapsibleContent_i85q"};function qe(e){return!!e&&("SUMMARY"===e.tagName||qe(e.parentElement))}function Ze(e,t){return!!e&&(e===t||Ze(e.parentElement,t))}function Je(e){let{summary:t,children:n,...a}=e;(0,$e.A)().collectAnchor(a.id);const o=(0,pe.A)(),l=(0,s.useRef)(null),{collapsed:c,setCollapsed:r}=(0,D.u)({initialState:!a.open}),[d,m]=(0,s.useState)(a.open),h=s.isValidElement(t)?t:(0,i.jsx)("summary",{children:t??"Details"});return(0,i.jsxs)("details",{...a,ref:l,open:d,"data-collapsed":c,className:(0,u.A)(Fe.details,o&&Fe.isBrowser,a.className),onMouseDown:e=>{qe(e.target)&&e.detail>1&&e.preventDefault()},onClick:e=>{e.stopPropagation();const t=e.target;qe(t)&&Ze(t,l.current)&&(e.preventDefault(),c?(r(!1),m(!0)):r(!0))},children:[h,(0,i.jsx)(D.N,{lazy:!1,collapsed:c,disableSSRStyle:!0,onCollapseTransitionEnd:e=>{r(e),m(!e)},children:(0,i.jsx)("div",{className:Fe.collapsibleContent,children:n})})]})}const Ye={details:"details_b_Ee"},Ke="alert alert--info";function Qe(e){let{...t}=e;return(0,i.jsx)(Je,{...t,className:(0,u.A)(Ke,Ye.details,t.className)})}function Xe(e){const t=s.Children.toArray(e.children),n=t.find((e=>s.isValidElement(e)&&"summary"===e.type)),a=(0,i.jsx)(i.Fragment,{children:t.filter((e=>e!==n))});return(0,i.jsx)(Qe,{...e,summary:n,children:a})}function et(e){return(0,i.jsx)(ue.A,{...e})}const tt={containsTaskList:"containsTaskList_mC6p"};function nt(e){if(void 0!==e)return(0,u.A)(e,e?.includes("contains-task-list")&&tt.containsTaskList)}const st={img:"img_ev3q"};function at(e){const{mdxAdmonitionTitle:t,rest:n}=function(e){const t=s.Children.toArray(e),n=t.find((e=>s.isValidElement(e)&&"mdxAdmonitionTitle"===e.type)),a=t.filter((e=>e!==n)),o=n?.props.children;return{mdxAdmonitionTitle:o,rest:a.length>0?(0,i.jsx)(i.Fragment,{children:a}):null}}(e.children),a=e.title??t;return{...e,...a&&{title:a},children:n}}const ot={admonition:"admonition_xJq3",admonitionHeading:"admonitionHeading_Gvgb",admonitionIcon:"admonitionIcon_Rf37",admonitionContent:"admonitionContent_BuS1"};function it(e){let{type:t,className:n,children:s}=e;return(0,i.jsx)("div",{className:(0,u.A)(v.G.common.admonition,v.G.common.admonitionType(t),ot.admonition,n),children:s})}function lt(e){let{icon:t,title:n}=e;return(0,i.jsxs)("div",{className:ot.admonitionHeading,children:[(0,i.jsx)("span",{className:ot.admonitionIcon,children:t}),n]})}function ct(e){let{children:t}=e;return t?(0,i.jsx)("div",{className:ot.admonitionContent,children:t}):null}function rt(e){const{type:t,icon:n,title:s,children:a,className:o}=e;return(0,i.jsxs)(it,{type:t,className:o,children:[(0,i.jsx)(lt,{title:s,icon:n}),(0,i.jsx)(ct,{children:a})]})}function dt(e){return(0,i.jsx)("svg",{viewBox:"0 0 14 16",...e,children:(0,i.jsx)("path",{fillRule:"evenodd",d:"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"})})}const ut={icon:(0,i.jsx)(dt,{}),title:(0,i.jsx)(h.A,{id:"theme.admonition.note",description:"The default label used for the Note admonition (:::note)",children:"note"})};function mt(e){return(0,i.jsx)(rt,{...ut,...e,className:(0,u.A)("alert alert--secondary",e.className),children:e.children})}function ht(e){return(0,i.jsx)("svg",{viewBox:"0 0 12 16",...e,children:(0,i.jsx)("path",{fillRule:"evenodd",d:"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"})})}const pt={icon:(0,i.jsx)(ht,{}),title:(0,i.jsx)(h.A,{id:"theme.admonition.tip",description:"The default label used for the Tip admonition (:::tip)",children:"tip"})};function ft(e){return(0,i.jsx)(rt,{...pt,...e,className:(0,u.A)("alert alert--success",e.className),children:e.children})}function xt(e){return(0,i.jsx)("svg",{viewBox:"0 0 14 16",...e,children:(0,i.jsx)("path",{fillRule:"evenodd",d:"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"})})}const bt={icon:(0,i.jsx)(xt,{}),title:(0,i.jsx)(h.A,{id:"theme.admonition.info",description:"The default label used for the Info admonition (:::info)",children:"info"})};function gt(e){return(0,i.jsx)(rt,{...bt,...e,className:(0,u.A)("alert alert--info",e.className),children:e.children})}function jt(e){return(0,i.jsx)("svg",{viewBox:"0 0 16 16",...e,children:(0,i.jsx)("path",{fillRule:"evenodd",d:"M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"})})}const vt={icon:(0,i.jsx)(jt,{}),title:(0,i.jsx)(h.A,{id:"theme.admonition.warning",description:"The default label used for the Warning admonition (:::warning)",children:"warning"})};function Nt(e){return(0,i.jsx)("svg",{viewBox:"0 0 12 16",...e,children:(0,i.jsx)("path",{fillRule:"evenodd",d:"M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"})})}const Ct={icon:(0,i.jsx)(Nt,{}),title:(0,i.jsx)(h.A,{id:"theme.admonition.danger",description:"The default label used for the Danger admonition (:::danger)",children:"danger"})};const At={icon:(0,i.jsx)(jt,{}),title:(0,i.jsx)(h.A,{id:"theme.admonition.caution",description:"The default label used for the Caution admonition (:::caution)",children:"caution"})};const kt={...{note:mt,tip:ft,info:gt,warning:function(e){return(0,i.jsx)(rt,{...vt,...e,className:(0,u.A)("alert alert--warning",e.className),children:e.children})},danger:function(e){return(0,i.jsx)(rt,{...Ct,...e,className:(0,u.A)("alert alert--danger",e.className),children:e.children})}},...{secondary:e=>(0,i.jsx)(mt,{title:"secondary",...e}),important:e=>(0,i.jsx)(gt,{title:"important",...e}),success:e=>(0,i.jsx)(ft,{title:"success",...e}),caution:function(e){return(0,i.jsx)(rt,{...At,...e,className:(0,u.A)("alert alert--warning",e.className),children:e.children})}}};function yt(e){const t=at(e),n=(s=t.type,kt[s]||(console.warn(`No admonition component found for admonition type "${s}". Using Info as fallback.`),kt.info));var s;return(0,i.jsx)(n,{...t})}var Lt=n(418);const _t={Head:he.A,details:Xe,Details:Xe,code:function(e){return function(e){return void 0!==e.children&&s.Children.toArray(e.children).every((e=>"string"==typeof e&&!e.includes("\n")))}(e)?(0,i.jsx)(We,{...e}):(0,i.jsx)(De,{...e})},a:function(e){return(0,i.jsx)(p.A,{...e})},pre:function(e){return(0,i.jsx)(i.Fragment,{children:e.children})},ul:function(e){return(0,i.jsx)("ul",{...e,className:nt(e.className)})},li:function(e){return(0,$e.A)().collectAnchor(e.id),(0,i.jsx)("li",{...e})},img:function(e){return(0,i.jsx)("img",{decoding:"async",loading:"lazy",...e,className:(t=e.className,(0,u.A)(t,st.img))});var t},h1:e=>(0,i.jsx)(et,{as:"h1",...e}),h2:e=>(0,i.jsx)(et,{as:"h2",...e}),h3:e=>(0,i.jsx)(et,{as:"h3",...e}),h4:e=>(0,i.jsx)(et,{as:"h4",...e}),h5:e=>(0,i.jsx)(et,{as:"h5",...e}),h6:e=>(0,i.jsx)(et,{as:"h6",...e}),admonition:yt,mermaid:Lt.A};function Bt(e){let{children:t}=e;return(0,i.jsx)(me.x,{components:_t,children:t})}function wt(e){let{children:t}=e;const n=function(){const{metadata:e,frontMatter:t,contentTitle:n}=r();return t.hide_title||void 0!==n?null:e.title}();return(0,i.jsxs)("div",{className:(0,u.A)(v.G.docs.docMarkdown,"markdown"),children:[n&&(0,i.jsx)("header",{children:(0,i.jsx)(ue.A,{as:"h1",children:n})}),(0,i.jsx)(Bt,{children:t})]})}var Tt=n(1754),Et=n(9169),Ht=n(6025);function Mt(e){return(0,i.jsx)("svg",{viewBox:"0 0 24 24",...e,children:(0,i.jsx)("path",{d:"M10 19v-5h4v5c0 .55.45 1 1 1h3c.55 0 1-.45 1-1v-7h1.7c.46 0 .68-.57.33-.87L12.67 3.6c-.38-.34-.96-.34-1.34 0l-8.36 7.53c-.34.3-.13.87.33.87H5v7c0 .55.45 1 1 1h3c.55 0 1-.45 1-1z",fill:"currentColor"})})}const It={breadcrumbHomeIcon:"breadcrumbHomeIcon_YNFT"};function St(){const e=(0,Ht.A)("/");return(0,i.jsx)("li",{className:"breadcrumbs__item",children:(0,i.jsx)(p.A,{"aria-label":(0,h.T)({id:"theme.docs.breadcrumbs.home",message:"Home page",description:"The ARIA label for the home page in the breadcrumbs"}),className:"breadcrumbs__link",href:e,children:(0,i.jsx)(Mt,{className:It.breadcrumbHomeIcon})})})}const Ut={breadcrumbsContainer:"breadcrumbsContainer_Z_bl"};function Vt(e){let{children:t,href:n,isLast:s}=e;const a="breadcrumbs__link";return s?(0,i.jsx)("span",{className:a,itemProp:"name",children:t}):n?(0,i.jsx)(p.A,{className:a,href:n,itemProp:"item",children:(0,i.jsx)("span",{itemProp:"name",children:t})}):(0,i.jsx)("span",{className:a,children:t})}function Rt(e){let{children:t,active:n,index:s,addMicrodata:a}=e;return(0,i.jsxs)("li",{...a&&{itemScope:!0,itemProp:"itemListElement",itemType:"https://schema.org/ListItem"},className:(0,u.A)("breadcrumbs__item",{"breadcrumbs__item--active":n}),children:[t,(0,i.jsx)("meta",{itemProp:"position",content:String(s+1)})]})}function zt(){const e=(0,Tt.OF)(),t=(0,Et.Dt)();return e?(0,i.jsx)("nav",{className:(0,u.A)(v.G.docs.docBreadcrumbs,Ut.breadcrumbsContainer),"aria-label":(0,h.T)({id:"theme.docs.breadcrumbs.navAriaLabel",message:"Breadcrumbs",description:"The ARIA label for the breadcrumbs"}),children:(0,i.jsxs)("ul",{className:"breadcrumbs",itemScope:!0,itemType:"https://schema.org/BreadcrumbList",children:[t&&(0,i.jsx)(St,{}),e.map(((t,n)=>{const s=n===e.length-1,a="category"===t.type&&t.linkUnlisted?void 0:t.href;return(0,i.jsx)(Rt,{active:s,index:n,addMicrodata:!!a,children:(0,i.jsx)(Vt,{href:a,isLast:s,children:t.label})},n)}))]})}):null}function Ot(){return(0,i.jsx)(h.A,{id:"theme.unlistedContent.title",description:"The unlisted content banner title",children:"Unlisted page"})}function Pt(){return(0,i.jsx)(h.A,{id:"theme.unlistedContent.message",description:"The unlisted content banner message",children:"This page is unlisted. Search engines will not index it, and only users having a direct link can access it."})}function Gt(){return(0,i.jsx)(he.A,{children:(0,i.jsx)("meta",{name:"robots",content:"noindex, nofollow"})})}function Dt(e){let{className:t}=e;return(0,i.jsx)(yt,{type:"caution",title:(0,i.jsx)(Ot,{}),className:(0,u.A)(t,v.G.common.unlistedBanner),children:(0,i.jsx)(Pt,{})})}function Wt(e){return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(Gt,{}),(0,i.jsx)(Dt,{...e})]})}const $t={docItemContainer:"docItemContainer_Djhp",docItemCol:"docItemCol_VOVn"};function Ft(e){let{children:t}=e;const n=function(){const{frontMatter:e,toc:t}=r(),n=(0,m.l)(),s=e.hide_table_of_contents,a=!s&&t.length>0;return{hidden:s,mobile:a?(0,i.jsx)(oe,{}):void 0,desktop:!a||"desktop"!==n&&"ssr"!==n?void 0:(0,i.jsx)(de,{})}}(),{metadata:{unlisted:s}}=r();return(0,i.jsxs)("div",{className:"row",children:[(0,i.jsxs)("div",{className:(0,u.A)("col",!n.hidden&&$t.docItemCol),children:[s&&(0,i.jsx)(Wt,{}),(0,i.jsx)(_,{}),(0,i.jsxs)("div",{className:$t.docItemContainer,children:[(0,i.jsxs)("article",{children:[(0,i.jsx)(zt,{}),(0,i.jsx)(B,{}),n.mobile,(0,i.jsx)(wt,{children:t}),(0,i.jsx)(G,{})]}),(0,i.jsx)(b,{})]})]}),n.desktop&&(0,i.jsx)("div",{className:"col col--3",children:n.desktop})]})}function qt(e){const t=`docs-doc-id-${e.content.metadata.id}`,n=e.content;return(0,i.jsx)(c,{content:e.content,children:(0,i.jsxs)(a.e3,{className:t,children:[(0,i.jsx)(d,{}),(0,i.jsx)(Ft,{children:(0,i.jsx)(n,{})})]})})}},8426:(e,t)=>{function n(e){let t,n=[];for(let s of e.split(",").map((e=>e.trim())))if(/^-?\d+$/.test(s))n.push(parseInt(s,10));else if(t=s.match(/^(-?\d+)(-|\.\.\.?|\u2025|\u2026|\u22EF)(-?\d+)$/)){let[e,s,a,o]=t;if(s&&o){s=parseInt(s),o=parseInt(o);const e=s<o?1:-1;"-"!==a&&".."!==a&&"\u2025"!==a||(o+=e);for(let t=s;t!==o;t+=e)n.push(t)}}return n}t.default=n,e.exports=n},8453:(e,t,n)=>{"use strict";n.d(t,{R:()=>i,x:()=>l});var s=n(6540);const a={},o=s.createContext(a);function i(e){const t=s.useContext(o);return s.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function l(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:i(e.components),s.createElement(o.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/17896441.bd1b9c94.js b/website/assets/js/17896441.bd1b9c94.js deleted file mode 100644 index ab71831f..00000000 --- a/website/assets/js/17896441.bd1b9c94.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7918],{3905:(e,t,n)=>{"use strict";n.d(t,{Zo:()=>m,kt:()=>f});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function l(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){r(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function c(e,t){if(null==e)return{};var n,a,r=function(e,t){if(null==e)return{};var n,a,r={},o=Object.keys(e);for(a=0;a<o.length;a++)n=o[a],t.indexOf(n)>=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a<o.length;a++)n=o[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),i=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},m=function(e){var t=i(e.components);return a.createElement(s.Provider,{value:t},e.children)},d="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},p=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,s=e.parentName,m=c(e,["components","mdxType","originalType","parentName"]),d=i(n),p=r,f=d["".concat(s,".").concat(p)]||d[p]||u[p]||o;return n?a.createElement(f,l(l({ref:t},m),{},{components:n})):a.createElement(f,l({ref:t},m))}));function f(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,l=new Array(o);l[0]=p;var c={};for(var s in t)hasOwnProperty.call(t,s)&&(c[s]=t[s]);c.originalType=e,c[d]="string"==typeof e?e:r,l[1]=c;for(var i=2;i<o;i++)l[i]=n[i];return a.createElement.apply(null,l)}return a.createElement.apply(null,n)}p.displayName="MDXCreateElement"},8300:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>Dt});var a=n(7294),r=n(1944),o=n(902);const l=a.createContext(null);function c(e){let{children:t,content:n}=e;const r=function(e){return(0,a.useMemo)((()=>({metadata:e.metadata,frontMatter:e.frontMatter,assets:e.assets,contentTitle:e.contentTitle,toc:e.toc})),[e])}(n);return a.createElement(l.Provider,{value:r},t)}function s(){const e=(0,a.useContext)(l);if(null===e)throw new o.i6("DocProvider");return e}function i(){const{metadata:e,frontMatter:t,assets:n}=s();return a.createElement(r.d,{title:e.title,description:e.description,keywords:t.keywords,image:n.image??t.image})}var m=n(4334),d=n(7524),u=n(7462),p=n(5999),f=n(9960);function h(e){const{permalink:t,title:n,subLabel:r,isNext:o}=e;return a.createElement(f.Z,{className:(0,m.Z)("pagination-nav__link",o?"pagination-nav__link--next":"pagination-nav__link--prev"),to:t},r&&a.createElement("div",{className:"pagination-nav__sublabel"},r),a.createElement("div",{className:"pagination-nav__label"},n))}function g(e){const{previous:t,next:n}=e;return a.createElement("nav",{className:"pagination-nav docusaurus-mt-lg","aria-label":(0,p.I)({id:"theme.docs.paginator.navAriaLabel",message:"Docs pages",description:"The ARIA label for the docs pagination"})},t&&a.createElement(h,(0,u.Z)({},t,{subLabel:a.createElement(p.Z,{id:"theme.docs.paginator.previous",description:"The label used to navigate to the previous doc"},"Previous")})),n&&a.createElement(h,(0,u.Z)({},n,{subLabel:a.createElement(p.Z,{id:"theme.docs.paginator.next",description:"The label used to navigate to the next doc"},"Next"),isNext:!0})))}function b(){const{metadata:e}=s();return a.createElement(g,{previous:e.previous,next:e.next})}var v=n(2263),E=n(143),y=n(5281),k=n(373),N=n(4477);const L={unreleased:function(e){let{siteTitle:t,versionMetadata:n}=e;return a.createElement(p.Z,{id:"theme.docs.versions.unreleasedVersionLabel",description:"The label used to tell the user that he's browsing an unreleased doc version",values:{siteTitle:t,versionLabel:a.createElement("b",null,n.label)}},"This is unreleased documentation for {siteTitle} {versionLabel} version.")},unmaintained:function(e){let{siteTitle:t,versionMetadata:n}=e;return a.createElement(p.Z,{id:"theme.docs.versions.unmaintainedVersionLabel",description:"The label used to tell the user that he's browsing an unmaintained doc version",values:{siteTitle:t,versionLabel:a.createElement("b",null,n.label)}},"This is documentation for {siteTitle} {versionLabel}, which is no longer actively maintained.")}};function C(e){const t=L[e.versionMetadata.banner];return a.createElement(t,e)}function T(e){let{versionLabel:t,to:n,onClick:r}=e;return a.createElement(p.Z,{id:"theme.docs.versions.latestVersionSuggestionLabel",description:"The label used to tell the user to check the latest version",values:{versionLabel:t,latestVersionLink:a.createElement("b",null,a.createElement(f.Z,{to:n,onClick:r},a.createElement(p.Z,{id:"theme.docs.versions.latestVersionLinkLabel",description:"The label used for the latest version suggestion link label"},"latest version")))}},"For up-to-date documentation, see the {latestVersionLink} ({versionLabel}).")}function _(e){let{className:t,versionMetadata:n}=e;const{siteConfig:{title:r}}=(0,v.Z)(),{pluginId:o}=(0,E.gA)({failfast:!0}),{savePreferredVersionName:l}=(0,k.J)(o),{latestDocSuggestion:c,latestVersionSuggestion:s}=(0,E.Jo)(o),i=c??(d=s).docs.find((e=>e.id===d.mainDocId));var d;return a.createElement("div",{className:(0,m.Z)(t,y.k.docs.docVersionBanner,"alert alert--warning margin-bottom--md"),role:"alert"},a.createElement("div",null,a.createElement(C,{siteTitle:r,versionMetadata:n})),a.createElement("div",{className:"margin-top--md"},a.createElement(T,{versionLabel:s.label,to:i.path,onClick:()=>l(s.name)})))}function Z(e){let{className:t}=e;const n=(0,N.E)();return n.banner?a.createElement(_,{className:t,versionMetadata:n}):null}function w(e){let{className:t}=e;const n=(0,N.E)();return n.badge?a.createElement("span",{className:(0,m.Z)(t,y.k.docs.docVersionBadge,"badge badge--secondary")},a.createElement(p.Z,{id:"theme.docs.versionBadge.label",values:{versionLabel:n.label}},"Version: {versionLabel}")):null}function x(e){let{lastUpdatedAt:t,formattedLastUpdatedAt:n}=e;return a.createElement(p.Z,{id:"theme.lastUpdated.atDate",description:"The words used to describe on which date a page has been last updated",values:{date:a.createElement("b",null,a.createElement("time",{dateTime:new Date(1e3*t).toISOString()},n))}}," on {date}")}function B(e){let{lastUpdatedBy:t}=e;return a.createElement(p.Z,{id:"theme.lastUpdated.byUser",description:"The words used to describe by who the page has been last updated",values:{user:a.createElement("b",null,t)}}," by {user}")}function O(e){let{lastUpdatedAt:t,formattedLastUpdatedAt:n,lastUpdatedBy:r}=e;return a.createElement("span",{className:y.k.common.lastUpdated},a.createElement(p.Z,{id:"theme.lastUpdated.lastUpdatedAtBy",description:"The sentence used to display when a page has been last updated, and by who",values:{atDate:t&&n?a.createElement(x,{lastUpdatedAt:t,formattedLastUpdatedAt:n}):"",byUser:r?a.createElement(B,{lastUpdatedBy:r}):""}},"Last updated{atDate}{byUser}"),!1)}const A="iconEdit_Z9Sw";function j(e){let{className:t,...n}=e;return a.createElement("svg",(0,u.Z)({fill:"currentColor",height:"20",width:"20",viewBox:"0 0 40 40",className:(0,m.Z)(A,t),"aria-hidden":"true"},n),a.createElement("g",null,a.createElement("path",{d:"m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z"})))}function H(e){let{editUrl:t}=e;return a.createElement("a",{href:t,target:"_blank",rel:"noreferrer noopener",className:y.k.common.editThisPage},a.createElement(j,null),a.createElement(p.Z,{id:"theme.common.editThisPage",description:"The link label to edit the current page"},"Edit this page"))}const S="tag_zVej",M="tagRegular_sFm0",P="tagWithCount_h2kH";function I(e){let{permalink:t,label:n,count:r}=e;return a.createElement(f.Z,{href:t,className:(0,m.Z)(S,r?P:M)},n,r&&a.createElement("span",null,r))}const U="tags_jXut",z="tag_QGVx";function V(e){let{tags:t}=e;return a.createElement(a.Fragment,null,a.createElement("b",null,a.createElement(p.Z,{id:"theme.tags.tagsListLabel",description:"The label alongside a tag list"},"Tags:")),a.createElement("ul",{className:(0,m.Z)(U,"padding--none","margin-left--sm")},t.map((e=>{let{label:t,permalink:n}=e;return a.createElement("li",{key:n,className:z},a.createElement(I,{label:t,permalink:n}))}))))}const D="lastUpdated_vwxv";function R(e){return a.createElement("div",{className:(0,m.Z)(y.k.docs.docFooterTagsRow,"row margin-bottom--sm")},a.createElement("div",{className:"col"},a.createElement(V,e)))}function $(e){let{editUrl:t,lastUpdatedAt:n,lastUpdatedBy:r,formattedLastUpdatedAt:o}=e;return a.createElement("div",{className:(0,m.Z)(y.k.docs.docFooterEditMetaRow,"row")},a.createElement("div",{className:"col"},t&&a.createElement(H,{editUrl:t})),a.createElement("div",{className:(0,m.Z)("col",D)},(n||r)&&a.createElement(O,{lastUpdatedAt:n,formattedLastUpdatedAt:o,lastUpdatedBy:r})))}function W(){const{metadata:e}=s(),{editUrl:t,lastUpdatedAt:n,formattedLastUpdatedAt:r,lastUpdatedBy:o,tags:l}=e,c=l.length>0,i=!!(t||n||o);return c||i?a.createElement("footer",{className:(0,m.Z)(y.k.docs.docFooter,"docusaurus-mt-lg")},c&&a.createElement(R,{tags:l}),i&&a.createElement($,{editUrl:t,lastUpdatedAt:n,lastUpdatedBy:o,formattedLastUpdatedAt:r})):null}var F=n(6043),q=n(6668);function G(e){const t=e.map((e=>({...e,parentIndex:-1,children:[]}))),n=Array(7).fill(-1);t.forEach(((e,t)=>{const a=n.slice(2,e.level);e.parentIndex=Math.max(...a),n[e.level]=t}));const a=[];return t.forEach((e=>{const{parentIndex:n,...r}=e;n>=0?t[n].children.push(r):a.push(r)})),a}function Y(e){let{toc:t,minHeadingLevel:n,maxHeadingLevel:a}=e;return t.flatMap((e=>{const t=Y({toc:e.children,minHeadingLevel:n,maxHeadingLevel:a});return function(e){return e.level>=n&&e.level<=a}(e)?[{...e,children:t}]:t}))}function J(e){const t=e.getBoundingClientRect();return t.top===t.bottom?J(e.parentNode):t}function Q(e,t){let{anchorTopOffset:n}=t;const a=e.find((e=>J(e).top>=n));if(a){return function(e){return e.top>0&&e.bottom<window.innerHeight/2}(J(a))?a:e[e.indexOf(a)-1]??null}return e[e.length-1]??null}function X(){const e=(0,a.useRef)(0),{navbar:{hideOnScroll:t}}=(0,q.L)();return(0,a.useEffect)((()=>{e.current=t?0:document.querySelector(".navbar").clientHeight}),[t]),e}function K(e){const t=(0,a.useRef)(void 0),n=X();(0,a.useEffect)((()=>{if(!e)return()=>{};const{linkClassName:a,linkActiveClassName:r,minHeadingLevel:o,maxHeadingLevel:l}=e;function c(){const e=function(e){return Array.from(document.getElementsByClassName(e))}(a),c=function(e){let{minHeadingLevel:t,maxHeadingLevel:n}=e;const a=[];for(let r=t;r<=n;r+=1)a.push(`h${r}.anchor`);return Array.from(document.querySelectorAll(a.join()))}({minHeadingLevel:o,maxHeadingLevel:l}),s=Q(c,{anchorTopOffset:n.current}),i=e.find((e=>s&&s.id===function(e){return decodeURIComponent(e.href.substring(e.href.indexOf("#")+1))}(e)));e.forEach((e=>{!function(e,n){n?(t.current&&t.current!==e&&t.current.classList.remove(r),e.classList.add(r),t.current=e):e.classList.remove(r)}(e,e===i)}))}return document.addEventListener("scroll",c),document.addEventListener("resize",c),c(),()=>{document.removeEventListener("scroll",c),document.removeEventListener("resize",c)}}),[e,n])}function ee(e){let{toc:t,className:n,linkClassName:r,isChild:o}=e;return t.length?a.createElement("ul",{className:o?void 0:n},t.map((e=>a.createElement("li",{key:e.id},a.createElement("a",{href:`#${e.id}`,className:r??void 0,dangerouslySetInnerHTML:{__html:e.value}}),a.createElement(ee,{isChild:!0,toc:e.children,className:n,linkClassName:r}))))):null}const te=a.memo(ee);function ne(e){let{toc:t,className:n="table-of-contents table-of-contents__left-border",linkClassName:r="table-of-contents__link",linkActiveClassName:o,minHeadingLevel:l,maxHeadingLevel:c,...s}=e;const i=(0,q.L)(),m=l??i.tableOfContents.minHeadingLevel,d=c??i.tableOfContents.maxHeadingLevel,p=function(e){let{toc:t,minHeadingLevel:n,maxHeadingLevel:r}=e;return(0,a.useMemo)((()=>Y({toc:G(t),minHeadingLevel:n,maxHeadingLevel:r})),[t,n,r])}({toc:t,minHeadingLevel:m,maxHeadingLevel:d});return K((0,a.useMemo)((()=>{if(r&&o)return{linkClassName:r,linkActiveClassName:o,minHeadingLevel:m,maxHeadingLevel:d}}),[r,o,m,d])),a.createElement(te,(0,u.Z)({toc:p,className:n,linkClassName:r},s))}const ae="tocCollapsibleButton_TO0P",re="tocCollapsibleButtonExpanded_MG3E";function oe(e){let{collapsed:t,...n}=e;return a.createElement("button",(0,u.Z)({type:"button"},n,{className:(0,m.Z)("clean-btn",ae,!t&&re,n.className)}),a.createElement(p.Z,{id:"theme.TOCCollapsible.toggleButtonLabel",description:"The label used by the button on the collapsible TOC component"},"On this page"))}const le="tocCollapsible_ETCw",ce="tocCollapsibleContent_vkbj",se="tocCollapsibleExpanded_sAul";function ie(e){let{toc:t,className:n,minHeadingLevel:r,maxHeadingLevel:o}=e;const{collapsed:l,toggleCollapsed:c}=(0,F.u)({initialState:!0});return a.createElement("div",{className:(0,m.Z)(le,!l&&se,n)},a.createElement(oe,{collapsed:l,onClick:c}),a.createElement(F.z,{lazy:!0,className:ce,collapsed:l},a.createElement(ne,{toc:t,minHeadingLevel:r,maxHeadingLevel:o})))}const me="tocMobile_ITEo";function de(){const{toc:e,frontMatter:t}=s();return a.createElement(ie,{toc:e,minHeadingLevel:t.toc_min_heading_level,maxHeadingLevel:t.toc_max_heading_level,className:(0,m.Z)(y.k.docs.docTocMobile,me)})}const ue="tableOfContents_bqdL";function pe(e){let{className:t,...n}=e;return a.createElement("div",{className:(0,m.Z)(ue,"thin-scrollbar",t)},a.createElement(ne,(0,u.Z)({},n,{linkClassName:"table-of-contents__link toc-highlight",linkActiveClassName:"table-of-contents__link--active"})))}function fe(){const{toc:e,frontMatter:t}=s();return a.createElement(pe,{toc:e,minHeadingLevel:t.toc_min_heading_level,maxHeadingLevel:t.toc_max_heading_level,className:y.k.docs.docTocDesktop})}const he="anchorWithStickyNavbar_LWe7",ge="anchorWithHideOnScrollNavbar_WYt5";function be(e){let{as:t,id:n,...r}=e;const{navbar:{hideOnScroll:o}}=(0,q.L)();if("h1"===t||!n)return a.createElement(t,(0,u.Z)({},r,{id:void 0}));const l=(0,p.I)({id:"theme.common.headingLinkTitle",message:"Direct link to {heading}",description:"Title for link to heading"},{heading:"string"==typeof r.children?r.children:n});return a.createElement(t,(0,u.Z)({},r,{className:(0,m.Z)("anchor",o?ge:he,r.className),id:n}),r.children,a.createElement(f.Z,{className:"hash-link",to:`#${n}`,"aria-label":l,title:l},"\u200b"))}var ve=n(3905),Ee=n(5742);var ye=n(2389),ke=n(2949);function Ne(){const{prism:e}=(0,q.L)(),{colorMode:t}=(0,ke.I)(),n=e.theme,a=e.darkTheme||n;return"dark"===t?a:n}var Le=n(7594),Ce=n.n(Le);const Te=/title=(?<quote>["'])(?<title>.*?)\1/,_e=/\{(?<range>[\d,-]+)\}/,Ze={js:{start:"\\/\\/",end:""},jsBlock:{start:"\\/\\*",end:"\\*\\/"},jsx:{start:"\\{\\s*\\/\\*",end:"\\*\\/\\s*\\}"},bash:{start:"#",end:""},html:{start:"\x3c!--",end:"--\x3e"}};function we(e,t){const n=e.map((e=>{const{start:n,end:a}=Ze[e];return`(?:${n}\\s*(${t.flatMap((e=>[e.line,e.block?.start,e.block?.end].filter(Boolean))).join("|")})\\s*${a})`})).join("|");return new RegExp(`^\\s*(?:${n})\\s*$`)}function xe(e,t){let n=e.replace(/\n$/,"");const{language:a,magicComments:r,metastring:o}=t;if(o&&_e.test(o)){const e=o.match(_e).groups.range;if(0===r.length)throw new Error(`A highlight range has been given in code block's metastring (\`\`\` ${o}), but no magic comment config is available. Docusaurus applies the first magic comment entry's className for metastring ranges.`);const t=r[0].className,a=Ce()(e).filter((e=>e>0)).map((e=>[e-1,[t]]));return{lineClassNames:Object.fromEntries(a),code:n}}if(void 0===a)return{lineClassNames:{},code:n};const l=function(e,t){switch(e){case"js":case"javascript":case"ts":case"typescript":return we(["js","jsBlock"],t);case"jsx":case"tsx":return we(["js","jsBlock","jsx"],t);case"html":return we(["js","jsBlock","html"],t);case"python":case"py":case"bash":return we(["bash"],t);case"markdown":case"md":return we(["html","jsx","bash"],t);default:return we(Object.keys(Ze),t)}}(a,r),c=n.split("\n"),s=Object.fromEntries(r.map((e=>[e.className,{start:0,range:""}]))),i=Object.fromEntries(r.filter((e=>e.line)).map((e=>{let{className:t,line:n}=e;return[n,t]}))),m=Object.fromEntries(r.filter((e=>e.block)).map((e=>{let{className:t,block:n}=e;return[n.start,t]}))),d=Object.fromEntries(r.filter((e=>e.block)).map((e=>{let{className:t,block:n}=e;return[n.end,t]})));for(let p=0;p<c.length;){const e=c[p].match(l);if(!e){p+=1;continue}const t=e.slice(1).find((e=>void 0!==e));i[t]?s[i[t]].range+=`${p},`:m[t]?s[m[t]].start=p:d[t]&&(s[d[t]].range+=`${s[d[t]].start}-${p-1},`),c.splice(p,1)}n=c.join("\n");const u={};return Object.entries(s).forEach((e=>{let[t,{range:n}]=e;Ce()(n).forEach((e=>{u[e]??=[],u[e].push(t)}))})),{lineClassNames:u,code:n}}const Be="codeBlockContainer_Ckt0";function Oe(e){let{as:t,...n}=e;const r=function(e){const t={color:"--prism-color",backgroundColor:"--prism-background-color"},n={};return Object.entries(e.plain).forEach((e=>{let[a,r]=e;const o=t[a];o&&"string"==typeof r&&(n[o]=r)})),n}(Ne());return a.createElement(t,(0,u.Z)({},n,{style:r,className:(0,m.Z)(n.className,Be,y.k.common.codeBlock)}))}const Ae={codeBlockContent:"codeBlockContent_biex",codeBlockTitle:"codeBlockTitle_Ktv7",codeBlock:"codeBlock_bY9V",codeBlockStandalone:"codeBlockStandalone_MEMb",codeBlockLines:"codeBlockLines_e6Vv",codeBlockLinesWithNumbering:"codeBlockLinesWithNumbering_o6Pm",buttonGroup:"buttonGroup__atx"};function je(e){let{children:t,className:n}=e;return a.createElement(Oe,{as:"pre",tabIndex:0,className:(0,m.Z)(Ae.codeBlockStandalone,"thin-scrollbar",n)},a.createElement("code",{className:Ae.codeBlockLines},t))}const He={attributes:!0,characterData:!0,childList:!0,subtree:!0};function Se(e,t){const[n,r]=(0,a.useState)(),l=(0,a.useCallback)((()=>{r(e.current?.closest("[role=tabpanel][hidden]"))}),[e,r]);(0,a.useEffect)((()=>{l()}),[l]),function(e,t,n){void 0===n&&(n=He);const r=(0,o.zX)(t),l=(0,o.Ql)(n);(0,a.useEffect)((()=>{const t=new MutationObserver(r);return e&&t.observe(e,l),()=>t.disconnect()}),[e,r,l])}(n,(e=>{e.forEach((e=>{"attributes"===e.type&&"hidden"===e.attributeName&&(t(),l())}))}),{attributes:!0,characterData:!1,childList:!1,subtree:!1})}const Me={plain:{backgroundColor:"#2a2734",color:"#9a86fd"},styles:[{types:["comment","prolog","doctype","cdata","punctuation"],style:{color:"#6c6783"}},{types:["namespace"],style:{opacity:.7}},{types:["tag","operator","number"],style:{color:"#e09142"}},{types:["property","function"],style:{color:"#9a86fd"}},{types:["tag-id","selector","atrule-id"],style:{color:"#eeebff"}},{types:["attr-name"],style:{color:"#c4b9fe"}},{types:["boolean","string","entity","url","attr-value","keyword","control","directive","unit","statement","regex","atrule","placeholder","variable"],style:{color:"#ffcc99"}},{types:["deleted"],style:{textDecorationLine:"line-through"}},{types:["inserted"],style:{textDecorationLine:"underline"}},{types:["italic"],style:{fontStyle:"italic"}},{types:["important","bold"],style:{fontWeight:"bold"}},{types:["important"],style:{color:"#c4b9fe"}}]};var Pe={Prism:n(1205).Z,theme:Me};function Ie(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function Ue(){return Ue=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var a in n)Object.prototype.hasOwnProperty.call(n,a)&&(e[a]=n[a])}return e},Ue.apply(this,arguments)}var ze=/\r\n|\r|\n/,Ve=function(e){0===e.length?e.push({types:["plain"],content:"\n",empty:!0}):1===e.length&&""===e[0].content&&(e[0].content="\n",e[0].empty=!0)},De=function(e,t){var n=e.length;return n>0&&e[n-1]===t?e:e.concat(t)},Re=function(e,t){var n=e.plain,a=Object.create(null),r=e.styles.reduce((function(e,n){var a=n.languages,r=n.style;return a&&!a.includes(t)||n.types.forEach((function(t){var n=Ue({},e[t],r);e[t]=n})),e}),a);return r.root=n,r.plain=Ue({},n,{backgroundColor:null}),r};function $e(e,t){var n={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&-1===t.indexOf(a)&&(n[a]=e[a]);return n}const We=function(e){function t(){for(var t=this,n=[],a=arguments.length;a--;)n[a]=arguments[a];e.apply(this,n),Ie(this,"getThemeDict",(function(e){if(void 0!==t.themeDict&&e.theme===t.prevTheme&&e.language===t.prevLanguage)return t.themeDict;t.prevTheme=e.theme,t.prevLanguage=e.language;var n=e.theme?Re(e.theme,e.language):void 0;return t.themeDict=n})),Ie(this,"getLineProps",(function(e){var n=e.key,a=e.className,r=e.style,o=Ue({},$e(e,["key","className","style","line"]),{className:"token-line",style:void 0,key:void 0}),l=t.getThemeDict(t.props);return void 0!==l&&(o.style=l.plain),void 0!==r&&(o.style=void 0!==o.style?Ue({},o.style,r):r),void 0!==n&&(o.key=n),a&&(o.className+=" "+a),o})),Ie(this,"getStyleForToken",(function(e){var n=e.types,a=e.empty,r=n.length,o=t.getThemeDict(t.props);if(void 0!==o){if(1===r&&"plain"===n[0])return a?{display:"inline-block"}:void 0;if(1===r&&!a)return o[n[0]];var l=a?{display:"inline-block"}:{},c=n.map((function(e){return o[e]}));return Object.assign.apply(Object,[l].concat(c))}})),Ie(this,"getTokenProps",(function(e){var n=e.key,a=e.className,r=e.style,o=e.token,l=Ue({},$e(e,["key","className","style","token"]),{className:"token "+o.types.join(" "),children:o.content,style:t.getStyleForToken(o),key:void 0});return void 0!==r&&(l.style=void 0!==l.style?Ue({},l.style,r):r),void 0!==n&&(l.key=n),a&&(l.className+=" "+a),l})),Ie(this,"tokenize",(function(e,t,n,a){var r={code:t,grammar:n,language:a,tokens:[]};e.hooks.run("before-tokenize",r);var o=r.tokens=e.tokenize(r.code,r.grammar,r.language);return e.hooks.run("after-tokenize",r),o}))}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.render=function(){var e=this.props,t=e.Prism,n=e.language,a=e.code,r=e.children,o=this.getThemeDict(this.props),l=t.languages[n];return r({tokens:function(e){for(var t=[[]],n=[e],a=[0],r=[e.length],o=0,l=0,c=[],s=[c];l>-1;){for(;(o=a[l]++)<r[l];){var i=void 0,m=t[l],d=n[l][o];if("string"==typeof d?(m=l>0?m:["plain"],i=d):(m=De(m,d.type),d.alias&&(m=De(m,d.alias)),i=d.content),"string"==typeof i){var u=i.split(ze),p=u.length;c.push({types:m,content:u[0]});for(var f=1;f<p;f++)Ve(c),s.push(c=[]),c.push({types:m,content:u[f]})}else l++,t.push(m),n.push(i),a.push(0),r.push(i.length)}l--,t.pop(),n.pop(),a.pop(),r.pop()}return Ve(c),s}(void 0!==l?this.tokenize(t,a,l,n):[a]),className:"prism-code language-"+n,style:void 0!==o?o.root:{},getLineProps:this.getLineProps,getTokenProps:this.getTokenProps})},t}(a.Component),Fe="codeLine_lJS_",qe="codeLineNumber_Tfdd",Ge="codeLineContent_feaV";function Ye(e){let{line:t,classNames:n,showLineNumbers:r,getLineProps:o,getTokenProps:l}=e;1===t.length&&"\n"===t[0].content&&(t[0].content="");const c=o({line:t,className:(0,m.Z)(n,r&&Fe)}),s=t.map(((e,t)=>a.createElement("span",(0,u.Z)({key:t},l({token:e,key:t})))));return a.createElement("span",c,r?a.createElement(a.Fragment,null,a.createElement("span",{className:qe}),a.createElement("span",{className:Ge},s)):s,a.createElement("br",null))}function Je(e){return a.createElement("svg",(0,u.Z)({viewBox:"0 0 24 24"},e),a.createElement("path",{fill:"currentColor",d:"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"}))}function Qe(e){return a.createElement("svg",(0,u.Z)({viewBox:"0 0 24 24"},e),a.createElement("path",{fill:"currentColor",d:"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"}))}const Xe={copyButtonCopied:"copyButtonCopied_obH4",copyButtonIcons:"copyButtonIcons_eSgA",copyButtonIcon:"copyButtonIcon_y97N",copyButtonSuccessIcon:"copyButtonSuccessIcon_LjdS"};function Ke(e){let{code:t,className:n}=e;const[r,o]=(0,a.useState)(!1),l=(0,a.useRef)(void 0),c=(0,a.useCallback)((()=>{!function(e,t){let{target:n=document.body}=void 0===t?{}:t;const a=document.createElement("textarea"),r=document.activeElement;a.value=e,a.setAttribute("readonly",""),a.style.contain="strict",a.style.position="absolute",a.style.left="-9999px",a.style.fontSize="12pt";const o=document.getSelection();let l=!1;o.rangeCount>0&&(l=o.getRangeAt(0)),n.append(a),a.select(),a.selectionStart=0,a.selectionEnd=e.length;let c=!1;try{c=document.execCommand("copy")}catch{}a.remove(),l&&(o.removeAllRanges(),o.addRange(l)),r&&r.focus()}(t),o(!0),l.current=window.setTimeout((()=>{o(!1)}),1e3)}),[t]);return(0,a.useEffect)((()=>()=>window.clearTimeout(l.current)),[]),a.createElement("button",{type:"button","aria-label":r?(0,p.I)({id:"theme.CodeBlock.copied",message:"Copied",description:"The copied button label on code blocks"}):(0,p.I)({id:"theme.CodeBlock.copyButtonAriaLabel",message:"Copy code to clipboard",description:"The ARIA label for copy code blocks button"}),title:(0,p.I)({id:"theme.CodeBlock.copy",message:"Copy",description:"The copy button label on code blocks"}),className:(0,m.Z)("clean-btn",n,Xe.copyButton,r&&Xe.copyButtonCopied),onClick:c},a.createElement("span",{className:Xe.copyButtonIcons,"aria-hidden":"true"},a.createElement(Je,{className:Xe.copyButtonIcon}),a.createElement(Qe,{className:Xe.copyButtonSuccessIcon})))}function et(e){return a.createElement("svg",(0,u.Z)({viewBox:"0 0 24 24"},e),a.createElement("path",{fill:"currentColor",d:"M4 19h6v-2H4v2zM20 5H4v2h16V5zm-3 6H4v2h13.25c1.1 0 2 .9 2 2s-.9 2-2 2H15v-2l-3 3l3 3v-2h2c2.21 0 4-1.79 4-4s-1.79-4-4-4z"}))}const tt="wordWrapButtonIcon_Bwma",nt="wordWrapButtonEnabled_EoeP";function at(e){let{className:t,onClick:n,isEnabled:r}=e;const o=(0,p.I)({id:"theme.CodeBlock.wordWrapToggle",message:"Toggle word wrap",description:"The title attribute for toggle word wrapping button of code block lines"});return a.createElement("button",{type:"button",onClick:n,className:(0,m.Z)("clean-btn",t,r&&nt),"aria-label":o,title:o},a.createElement(et,{className:tt,"aria-hidden":"true"}))}function rt(e){let{children:t,className:n="",metastring:r,title:o,showLineNumbers:l,language:c}=e;const{prism:{defaultLanguage:s,magicComments:i}}=(0,q.L)(),d=c??n.split(" ").find((e=>e.startsWith("language-")))?.replace(/language-/,"")??s;const p=Ne(),f=function(){const[e,t]=(0,a.useState)(!1),[n,r]=(0,a.useState)(!1),o=(0,a.useRef)(null),l=(0,a.useCallback)((()=>{const n=o.current.querySelector("code");e?n.removeAttribute("style"):(n.style.whiteSpace="pre-wrap",n.style.overflowWrap="anywhere"),t((e=>!e))}),[o,e]),c=(0,a.useCallback)((()=>{const{scrollWidth:e,clientWidth:t}=o.current,n=e>t||o.current.querySelector("code").hasAttribute("style");r(n)}),[o]);return Se(o,c),(0,a.useEffect)((()=>{c()}),[e,c]),(0,a.useEffect)((()=>(window.addEventListener("resize",c,{passive:!0}),()=>{window.removeEventListener("resize",c)})),[c]),{codeBlockRef:o,isEnabled:e,isCodeScrollable:n,toggle:l}}(),h=function(e){return e?.match(Te)?.groups.title??""}(r)||o,{lineClassNames:g,code:b}=xe(t,{metastring:r,language:d,magicComments:i}),v=l??function(e){return Boolean(e?.includes("showLineNumbers"))}(r);return a.createElement(Oe,{as:"div",className:(0,m.Z)(n,d&&!n.includes(`language-${d}`)&&`language-${d}`)},h&&a.createElement("div",{className:Ae.codeBlockTitle},h),a.createElement("div",{className:Ae.codeBlockContent},a.createElement(We,(0,u.Z)({},Pe,{theme:p,code:b,language:d??"text"}),(e=>{let{className:t,tokens:n,getLineProps:r,getTokenProps:o}=e;return a.createElement("pre",{tabIndex:0,ref:f.codeBlockRef,className:(0,m.Z)(t,Ae.codeBlock,"thin-scrollbar")},a.createElement("code",{className:(0,m.Z)(Ae.codeBlockLines,v&&Ae.codeBlockLinesWithNumbering)},n.map(((e,t)=>a.createElement(Ye,{key:t,line:e,getLineProps:r,getTokenProps:o,classNames:g[t],showLineNumbers:v})))))})),a.createElement("div",{className:Ae.buttonGroup},(f.isEnabled||f.isCodeScrollable)&&a.createElement(at,{className:Ae.codeButton,onClick:()=>f.toggle(),isEnabled:f.isEnabled}),a.createElement(Ke,{className:Ae.codeButton,code:b}))))}function ot(e){let{children:t,...n}=e;const r=(0,ye.Z)(),o=function(e){return a.Children.toArray(e).some((e=>(0,a.isValidElement)(e)))?e:Array.isArray(e)?e.join(""):e}(t),l="string"==typeof o?rt:je;return a.createElement(l,(0,u.Z)({key:String(r)},n),o)}var lt=n(7459);const ct="details_lb9f",st="isBrowser_bmU9",it="collapsibleContent_i85q";function mt(e){return!!e&&("SUMMARY"===e.tagName||mt(e.parentElement))}function dt(e,t){return!!e&&(e===t||dt(e.parentElement,t))}function ut(e){let{summary:t,children:n,...r}=e;const o=(0,ye.Z)(),l=(0,a.useRef)(null),{collapsed:c,setCollapsed:s}=(0,F.u)({initialState:!r.open}),[i,m]=(0,a.useState)(r.open),d=a.isValidElement(t)?t:a.createElement("summary",null,t??"Details");return a.createElement("details",(0,u.Z)({},r,{ref:l,open:i,"data-collapsed":c,className:(0,lt.Z)(ct,o&&st,r.className),onMouseDown:e=>{mt(e.target)&&e.detail>1&&e.preventDefault()},onClick:e=>{e.stopPropagation();const t=e.target;mt(t)&&dt(t,l.current)&&(e.preventDefault(),c?(s(!1),m(!0)):s(!0))}}),d,a.createElement(F.z,{lazy:!1,collapsed:c,disableSSRStyle:!0,onCollapseTransitionEnd:e=>{s(e),m(!e)}},a.createElement("div",{className:it},n)))}const pt="details_b_Ee";function ft(e){let{...t}=e;return a.createElement(ut,(0,u.Z)({},t,{className:(0,m.Z)("alert alert--info",pt,t.className)}))}function ht(e){return a.createElement(be,e)}const gt="containsTaskList_mC6p";function bt(e){if(void 0!==e)return(0,m.Z)(e,e?.includes("contains-task-list")&>)}const vt="img_ev3q";const Et="admonition_LlT9",yt="admonitionHeading_tbUL",kt="admonitionIcon_kALy",Nt="admonitionContent_S0QG";const Lt={note:{infimaClassName:"secondary",iconComponent:function(){return a.createElement("svg",{viewBox:"0 0 14 16"},a.createElement("path",{fillRule:"evenodd",d:"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"}))},label:a.createElement(p.Z,{id:"theme.admonition.note",description:"The default label used for the Note admonition (:::note)"},"note")},tip:{infimaClassName:"success",iconComponent:function(){return a.createElement("svg",{viewBox:"0 0 12 16"},a.createElement("path",{fillRule:"evenodd",d:"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"}))},label:a.createElement(p.Z,{id:"theme.admonition.tip",description:"The default label used for the Tip admonition (:::tip)"},"tip")},danger:{infimaClassName:"danger",iconComponent:function(){return a.createElement("svg",{viewBox:"0 0 12 16"},a.createElement("path",{fillRule:"evenodd",d:"M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"}))},label:a.createElement(p.Z,{id:"theme.admonition.danger",description:"The default label used for the Danger admonition (:::danger)"},"danger")},info:{infimaClassName:"info",iconComponent:function(){return a.createElement("svg",{viewBox:"0 0 14 16"},a.createElement("path",{fillRule:"evenodd",d:"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"}))},label:a.createElement(p.Z,{id:"theme.admonition.info",description:"The default label used for the Info admonition (:::info)"},"info")},caution:{infimaClassName:"warning",iconComponent:function(){return a.createElement("svg",{viewBox:"0 0 16 16"},a.createElement("path",{fillRule:"evenodd",d:"M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"}))},label:a.createElement(p.Z,{id:"theme.admonition.caution",description:"The default label used for the Caution admonition (:::caution)"},"caution")}},Ct={secondary:"note",important:"info",success:"tip",warning:"danger"};function Tt(e){const{mdxAdmonitionTitle:t,rest:n}=function(e){const t=a.Children.toArray(e),n=t.find((e=>a.isValidElement(e)&&"mdxAdmonitionTitle"===e.props?.mdxType)),r=a.createElement(a.Fragment,null,t.filter((e=>e!==n)));return{mdxAdmonitionTitle:n,rest:r}}(e.children);return{...e,title:e.title??t,children:n}}const _t={head:function(e){const t=a.Children.map(e.children,(e=>a.isValidElement(e)?function(e){if(e.props?.mdxType&&e.props.originalType){const{mdxType:t,originalType:n,...r}=e.props;return a.createElement(e.props.originalType,r)}return e}(e):e));return a.createElement(Ee.Z,e,t)},code:function(e){const t=["a","abbr","b","br","button","cite","code","del","dfn","em","i","img","input","ins","kbd","label","object","output","q","ruby","s","small","span","strong","sub","sup","time","u","var","wbr"];return a.Children.toArray(e.children).every((e=>"string"==typeof e&&!e.includes("\n")||(0,a.isValidElement)(e)&&t.includes(e.props?.mdxType)))?a.createElement("code",e):a.createElement(ot,e)},a:function(e){return a.createElement(f.Z,e)},pre:function(e){return a.createElement(ot,(0,a.isValidElement)(e.children)&&"code"===e.children.props?.originalType?e.children.props:{...e})},details:function(e){const t=a.Children.toArray(e.children),n=t.find((e=>a.isValidElement(e)&&"summary"===e.props?.mdxType)),r=a.createElement(a.Fragment,null,t.filter((e=>e!==n)));return a.createElement(ft,(0,u.Z)({},e,{summary:n}),r)},ul:function(e){return a.createElement("ul",(0,u.Z)({},e,{className:bt(e.className)}))},img:function(e){return a.createElement("img",(0,u.Z)({loading:"lazy"},e,{className:(t=e.className,(0,m.Z)(t,vt))}));var t},h1:e=>a.createElement(ht,(0,u.Z)({as:"h1"},e)),h2:e=>a.createElement(ht,(0,u.Z)({as:"h2"},e)),h3:e=>a.createElement(ht,(0,u.Z)({as:"h3"},e)),h4:e=>a.createElement(ht,(0,u.Z)({as:"h4"},e)),h5:e=>a.createElement(ht,(0,u.Z)({as:"h5"},e)),h6:e=>a.createElement(ht,(0,u.Z)({as:"h6"},e)),admonition:function(e){const{children:t,type:n,title:r,icon:o}=Tt(e),l=function(e){const t=Ct[e]??e,n=Lt[t];return n||(console.warn(`No admonition config found for admonition type "${t}". Using Info as fallback.`),Lt.info)}(n),c=r??l.label,{iconComponent:s}=l,i=o??a.createElement(s,null);return a.createElement("div",{className:(0,m.Z)(y.k.common.admonition,y.k.common.admonitionType(e.type),"alert",`alert--${l.infimaClassName}`,Et)},a.createElement("div",{className:yt},a.createElement("span",{className:kt},i),c),a.createElement("div",{className:Nt},t))},mermaid:n(1875).Z};function Zt(e){let{children:t}=e;return a.createElement(ve.Zo,{components:_t},t)}function wt(e){let{children:t}=e;const n=function(){const{metadata:e,frontMatter:t,contentTitle:n}=s();return t.hide_title||void 0!==n?null:e.title}();return a.createElement("div",{className:(0,m.Z)(y.k.docs.docMarkdown,"markdown")},n&&a.createElement("header",null,a.createElement(be,{as:"h1"},n)),a.createElement(Zt,null,t))}var xt=n(2802),Bt=n(8596),Ot=n(4996);function At(e){return a.createElement("svg",(0,u.Z)({viewBox:"0 0 24 24"},e),a.createElement("path",{d:"M10 19v-5h4v5c0 .55.45 1 1 1h3c.55 0 1-.45 1-1v-7h1.7c.46 0 .68-.57.33-.87L12.67 3.6c-.38-.34-.96-.34-1.34 0l-8.36 7.53c-.34.3-.13.87.33.87H5v7c0 .55.45 1 1 1h3c.55 0 1-.45 1-1z",fill:"currentColor"}))}const jt="breadcrumbHomeIcon_YNFT";function Ht(){const e=(0,Ot.Z)("/");return a.createElement("li",{className:"breadcrumbs__item"},a.createElement(f.Z,{"aria-label":(0,p.I)({id:"theme.docs.breadcrumbs.home",message:"Home page",description:"The ARIA label for the home page in the breadcrumbs"}),className:"breadcrumbs__link",href:e},a.createElement(At,{className:jt})))}const St="breadcrumbsContainer_Z_bl";function Mt(e){let{children:t,href:n,isLast:r}=e;const o="breadcrumbs__link";return r?a.createElement("span",{className:o,itemProp:"name"},t):n?a.createElement(f.Z,{className:o,href:n,itemProp:"item"},a.createElement("span",{itemProp:"name"},t)):a.createElement("span",{className:o},t)}function Pt(e){let{children:t,active:n,index:r,addMicrodata:o}=e;return a.createElement("li",(0,u.Z)({},o&&{itemScope:!0,itemProp:"itemListElement",itemType:"https://schema.org/ListItem"},{className:(0,m.Z)("breadcrumbs__item",{"breadcrumbs__item--active":n})}),t,a.createElement("meta",{itemProp:"position",content:String(r+1)}))}function It(){const e=(0,xt.s1)(),t=(0,Bt.Ns)();return e?a.createElement("nav",{className:(0,m.Z)(y.k.docs.docBreadcrumbs,St),"aria-label":(0,p.I)({id:"theme.docs.breadcrumbs.navAriaLabel",message:"Breadcrumbs",description:"The ARIA label for the breadcrumbs"})},a.createElement("ul",{className:"breadcrumbs",itemScope:!0,itemType:"https://schema.org/BreadcrumbList"},t&&a.createElement(Ht,null),e.map(((t,n)=>{const r=n===e.length-1;return a.createElement(Pt,{key:n,active:r,index:n,addMicrodata:!!t.href},a.createElement(Mt,{href:t.href,isLast:r},t.label))})))):null}const Ut="docItemContainer_Djhp",zt="docItemCol_VOVn";function Vt(e){let{children:t}=e;const n=function(){const{frontMatter:e,toc:t}=s(),n=(0,d.i)(),r=e.hide_table_of_contents,o=!r&&t.length>0;return{hidden:r,mobile:o?a.createElement(de,null):void 0,desktop:!o||"desktop"!==n&&"ssr"!==n?void 0:a.createElement(fe,null)}}();return a.createElement("div",{className:"row"},a.createElement("div",{className:(0,m.Z)("col",!n.hidden&&zt)},a.createElement(Z,null),a.createElement("div",{className:Ut},a.createElement("article",null,a.createElement(It,null),a.createElement(w,null),n.mobile,a.createElement(wt,null,t),a.createElement(W,null)),a.createElement(b,null))),n.desktop&&a.createElement("div",{className:"col col--3"},n.desktop))}function Dt(e){const t=`docs-doc-id-${e.content.metadata.unversionedId}`,n=e.content;return a.createElement(c,{content:e.content},a.createElement(r.FG,{className:t},a.createElement(i,null),a.createElement(Vt,null,a.createElement(n,null))))}},4477:(e,t,n)=>{"use strict";n.d(t,{E:()=>c,q:()=>l});var a=n(7294),r=n(902);const o=a.createContext(null);function l(e){let{children:t,version:n}=e;return a.createElement(o.Provider,{value:n},t)}function c(){const e=(0,a.useContext)(o);if(null===e)throw new r.i6("DocsVersionProvider");return e}},7594:(e,t)=>{function n(e){let t,n=[];for(let a of e.split(",").map((e=>e.trim())))if(/^-?\d+$/.test(a))n.push(parseInt(a,10));else if(t=a.match(/^(-?\d+)(-|\.\.\.?|\u2025|\u2026|\u22EF)(-?\d+)$/)){let[e,a,r,o]=t;if(a&&o){a=parseInt(a),o=parseInt(o);const e=a<o?1:-1;"-"!==r&&".."!==r&&"\u2025"!==r||(o+=e);for(let t=a;t!==o;t+=e)n.push(t)}}return n}t.default=n,e.exports=n}}]); \ No newline at end of file diff --git a/website/assets/js/17e02f37.2535a01b.js b/website/assets/js/17e02f37.2535a01b.js new file mode 100644 index 00000000..5324cba2 --- /dev/null +++ b/website/assets/js/17e02f37.2535a01b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7594],{1264:(e,i,n)=>{n.r(i),n.d(i,{assets:()=>c,contentTitle:()=>o,default:()=>h,frontMatter:()=>s,metadata:()=>r,toc:()=>l});var t=n(4848),a=n(8453);const s={title:"Introduction",slug:"/"},o="Project Copacetic: Directly patch container image vulnerabilities",r={id:"introduction",title:"Introduction",description:"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.",source:"@site/versioned_docs/version-v0.6.x/introduction.md",sourceDirName:".",slug:"/",permalink:"/copacetic/website/",draft:!1,unlisted:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Introduction",slug:"/"},sidebar:"sidebar",next:{title:"Installation",permalink:"/copacetic/website/installation"}},c={},l=[{value:"Why?",id:"why",level:2},{value:"How?",id:"how",level:2}];function d(e){const i={a:"a",code:"code",em:"em",h1:"h1",h2:"h2",li:"li",ol:"ol",p:"p",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i.h1,{id:"project-copacetic-directly-patch-container-image-vulnerabilities",children:"Project Copacetic: Directly patch container image vulnerabilities"}),"\n",(0,t.jsxs)(i.p,{children:[(0,t.jsx)(i.code,{children:"copa"})," is a CLI tool written in ",(0,t.jsx)(i.a,{href:"https://golang.org",children:"Go"})," and based on ",(0,t.jsx)(i.a,{href:"https://github.com/moby/buildkit",children:"buildkit"})," that can be used to directly patch container images given the vulnerability scanning results from popular tools like ",(0,t.jsx)(i.a,{href:"https://github.com/aquasecurity/trivy",children:"Trivy"}),"."]}),"\n",(0,t.jsx)(i.h2,{id:"why",children:"Why?"}),"\n",(0,t.jsxs)(i.p,{children:["We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between ",(0,t.jsx)(i.a,{href:"https://www.bleepingcomputer.com/news/security/hackers-scan-for-vulnerabilities-within-15-minutes-of-disclosure/",children:"vulnerability disclosure and active exploitation continues to narrow"}),", there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:"]}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option"}),"\n",(0,t.jsx)(i.li,{children:"found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs."}),"\n"]}),"\n",(0,t.jsx)("img",{title:"direct image patching",src:"/copacetic/website/img/direct-image-patching.png"}),"\n",(0,t.jsxs)(i.p,{children:["In addition to filling the operational gap not met by left-shift security practices and tools, the ability of ",(0,t.jsx)(i.code,{children:"copa"})," to patch a container without requiring a rebuild of the container image provides other benefits:"]}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Allows users other than the image publishers to also patch container images, such as DevSecOps engineers."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image."}),"\n"]}),"\n",(0,t.jsx)(i.h2,{id:"how",children:"How?"}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"copa"})," tool is an extensible engine that:"]}),"\n",(0,t.jsxs)(i.ol,{children:["\n",(0,t.jsx)(i.li,{children:"Parses the needed update packages from the container image\u2019s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats."}),"\n",(0,t.jsx)(i.li,{children:"Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers."}),"\n",(0,t.jsx)(i.li,{children:"Applies the resulting update binaries to the container image using buildkit."}),"\n"]}),"\n",(0,t.jsx)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),"\n",(0,t.jsx)(i.p,{children:"This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:"}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:[(0,t.jsxs)(i.strong,{children:["Copa supports patching ",(0,t.jsx)(i.em,{children:"existing"})," container images"]}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Devs don't need to build their images using specific tools or modify them in some way just to support container patching."}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Copa works with the existing vulnerability scanning and mitigation ecosystems"}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today."}),"\n",(0,t.jsx)(i.li,{children:"Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports."}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image"}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today."}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.p,{children:["For more details, refer to the ",(0,t.jsx)(i.a,{href:"/copacetic/website/design",children:"copa design"})," documentation."]})]})}function h(e={}){const{wrapper:i}={...(0,a.R)(),...e.components};return i?(0,t.jsx)(i,{...e,children:(0,t.jsx)(d,{...e})}):d(e)}},8453:(e,i,n)=>{n.d(i,{R:()=>o,x:()=>r});var t=n(6540);const a={},s=t.createContext(a);function o(e){const i=t.useContext(s);return t.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function r(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),t.createElement(s.Provider,{value:i},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/17e02f37.860083f8.js b/website/assets/js/17e02f37.860083f8.js deleted file mode 100644 index 71755931..00000000 --- a/website/assets/js/17e02f37.860083f8.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>g});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),c=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(s.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=c(n),m=i,g=u["".concat(s,".").concat(m)]||u[m]||d[m]||r;return n?a.createElement(g,o(o({ref:t},p),{},{components:n})):a.createElement(g,o({ref:t},p))}));function g(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[u]="string"==typeof e?e:i,o[1]=l;for(var c=2;c<r;c++)o[c]=n[c];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}m.displayName="MDXCreateElement"},4109:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>l,toc:()=>c});var a=n(7462),i=(n(7294),n(3905));const r={title:"Introduction",slug:"/"},o="Project Copacetic: Directly patch container image vulnerabilities",l={unversionedId:"introduction",id:"version-v0.6.x/introduction",title:"Introduction",description:"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.",source:"@site/versioned_docs/version-v0.6.x/introduction.md",sourceDirName:".",slug:"/",permalink:"/copacetic/website/",draft:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Introduction",slug:"/"},sidebar:"sidebar",next:{title:"Installation",permalink:"/copacetic/website/installation"}},s={},c=[{value:"Why?",id:"why",level:2},{value:"How?",id:"how",level:2}],p={toc:c};function u(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"project-copacetic-directly-patch-container-image-vulnerabilities"},"Project Copacetic: Directly patch container image vulnerabilities"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"copa")," is a CLI tool written in ",(0,i.kt)("a",{parentName:"p",href:"https://golang.org"},"Go")," and based on ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit"},"buildkit")," that can be used to directly patch container images given the vulnerability scanning results from popular tools like ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/aquasecurity/trivy"},"Trivy"),"."),(0,i.kt)("h2",{id:"why"},"Why?"),(0,i.kt)("p",null,"We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between ",(0,i.kt)("a",{parentName:"p",href:"https://www.bleepingcomputer.com/news/security/hackers-scan-for-vulnerabilities-within-15-minutes-of-disclosure/"},"vulnerability disclosure and active exploitation continues to narrow"),", there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option"),(0,i.kt)("li",{parentName:"ul"},"found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.")),(0,i.kt)("img",{title:"direct image patching",src:"/copacetic/website/img/direct-image-patching.png"}),(0,i.kt)("p",null,"In addition to filling the operational gap not met by left-shift security practices and tools, the ability of ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," to patch a container without requiring a rebuild of the container image provides other benefits:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Allows users other than the image publishers to also patch container images, such as DevSecOps engineers."),(0,i.kt)("li",{parentName:"ul"},"Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching."),(0,i.kt)("li",{parentName:"ul"},"Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild."),(0,i.kt)("li",{parentName:"ul"},"Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.")),(0,i.kt)("h2",{id:"how"},"How?"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," tool is an extensible engine that:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Parses the needed update packages from the container image\u2019s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats."),(0,i.kt)("li",{parentName:"ol"},"Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers."),(0,i.kt)("li",{parentName:"ol"},"Applies the resulting update binaries to the container image using buildkit.")),(0,i.kt)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),(0,i.kt)("p",null,"This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa supports patching ",(0,i.kt)("em",{parentName:"strong"},"existing")," container images"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"Devs don't need to build their images using specific tools or modify them in some way just to support container patching."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa works with the existing vulnerability scanning and mitigation ecosystems"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today."),(0,i.kt)("li",{parentName:"ul"},"Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.")))),(0,i.kt)("p",null,"For more details, refer to the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/design"},"copa design")," documentation."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/1b7d20b2.79e8a116.js b/website/assets/js/1b7d20b2.79e8a116.js deleted file mode 100644 index 3240eca4..00000000 --- a/website/assets/js/1b7d20b2.79e8a116.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2676],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var o=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function a(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?i(Object(n),!0).forEach((function(t){r(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):i(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function c(e,t){if(null==e)return{};var n,o,r=function(e,t){if(null==e)return{};var n,o,r={},i=Object.keys(e);for(o=0;o<i.length;o++)n=i[o],t.indexOf(n)>=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(o=0;o<i.length;o++)n=i[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=o.createContext({}),p=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=p(e.components);return o.createElement(l.Provider,{value:t},e.children)},s="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},v=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,l=e.parentName,u=c(e,["components","mdxType","originalType","parentName"]),s=p(n),v=r,m=s["".concat(l,".").concat(v)]||s[v]||d[v]||i;return n?o.createElement(m,a(a({ref:t},u),{},{components:n})):o.createElement(m,a({ref:t},u))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,a=new Array(i);a[0]=v;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[s]="string"==typeof e?e:r,a[1]=c;for(var p=2;p<i;p++)a[p]=n[p];return o.createElement.apply(null,a)}return o.createElement.apply(null,n)}v.displayName="MDXCreateElement"},9303:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>s,frontMatter:()=>i,metadata:()=>c,toc:()=>p});var o=n(7462),r=(n(7294),n(3905));const i={title:"Copa Github Action"},a=void 0,c={unversionedId:"github-action",id:"version-v0.4.x/github-action",title:"Copa Github Action",description:"Overview",source:"@site/versioned_docs/version-v0.4.x/github-action.md",sourceDirName:".",slug:"/github-action",permalink:"/copacetic/website/v0.4.x/github-action",draft:!1,tags:[],version:"v0.4.x",frontMatter:{title:"Copa Github Action"},sidebar:"sidebar",previous:{title:"Code of Conduct",permalink:"/copacetic/website/v0.4.x/code-of-conduct"},next:{title:"Release Process",permalink:"/copacetic/website/v0.4.x/release"}},l={},p=[{value:"Overview",id:"overview",level:2},{value:"Inputs",id:"inputs",level:2},{value:"<code>image</code>",id:"image",level:2},{value:"<code>image-report</code>",id:"image-report",level:2},{value:"<code>patched-tag</code>",id:"patched-tag",level:2},{value:"<code>buildkit-version</code>",id:"buildkit-version",level:2},{value:"<code>copa-version</code>",id:"copa-version",level:2},{value:"Output",id:"output",level:2},{value:"<code>patched-image</code>",id:"patched-image",level:2},{value:"Example Workflow",id:"example-workflow",level:2}],u={toc:p};function s(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,o.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h2",{id:"overview"},"Overview"),(0,r.kt)("p",null,"The ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copa-action"},"Copa Github Action")," allows you patch vulnerable containers in your workflows using Copa. "),(0,r.kt)("h2",{id:"inputs"},"Inputs"),(0,r.kt)("h2",{id:"image"},(0,r.kt)("inlineCode",{parentName:"h2"},"image")),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Required")," The image reference to patch."),(0,r.kt)("h2",{id:"image-report"},(0,r.kt)("inlineCode",{parentName:"h2"},"image-report")),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Required")," The trivy json vulnerability report of the image to patch."),(0,r.kt)("h2",{id:"patched-tag"},(0,r.kt)("inlineCode",{parentName:"h2"},"patched-tag")),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Required")," The new patched image tag."),(0,r.kt)("h2",{id:"buildkit-version"},(0,r.kt)("inlineCode",{parentName:"h2"},"buildkit-version")),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Optional")," The buildkit version used in the action, default is latest."),(0,r.kt)("h2",{id:"copa-version"},(0,r.kt)("inlineCode",{parentName:"h2"},"copa-version")),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Optional")," The Copa version used in the action, default is latest."),(0,r.kt)("h2",{id:"output"},"Output"),(0,r.kt)("h2",{id:"patched-image"},(0,r.kt)("inlineCode",{parentName:"h2"},"patched-image")),(0,r.kt)("p",null,"Image reference of the resulting patched image."),(0,r.kt)("h2",{id:"example-workflow"},"Example Workflow"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"on: [push]\n\njobs:\n test:\n runs-on: ubuntu-latest\n\n strategy:\n fail-fast: false\n matrix:\n # provide relevant list of images to scan on each run\n images: ['docker.io/library/nginx:1.21.6', 'docker.io/openpolicyagent/opa:0.46.0', 'docker.io/library/hello-world:latest']\n\n steps:\n - name: Set up Docker Buildx\n uses: docker/setup-buildx-action@dedd61cf5d839122591f5027c89bf3ad27691d18\n\n - name: Generate Trivy Report\n uses: aquasecurity/trivy-action@69cbbc0cbbf6a2b0bab8dcf0e9f2d7ead08e87e4\n with:\n scan-type: 'image'\n format: 'json'\n output: 'report.json'\n ignore-unfixed: true\n vuln-type: 'os'\n image-ref: ${{ matrix.images }}\n\n - name: Check Vuln Count\n id: vuln_count\n run: |\n report_file=\"report.json\"\n vuln_count=$(jq '.Results | length' \"$report_file\")\n echo \"vuln_count=$vuln_count\" >> $GITHUB_OUTPUT\n\n - name: Copa Action\n if: steps.vuln_count.outputs.vuln_count != '0'\n id: copa\n uses: project-copacetic/copa-action@v1\n with:\n image: ${{ matrix.images }}\n image-report: 'report.json'\n patched-tag: 'patched'\n buildkit-version: 'v0.11.6'\n # optional, default is latest\n copa-version: '0.4.1'\n\n - name: Login to Docker Hub\n if: steps.copa.conclusion == 'success'\n id: login\n uses: docker/login-action@b4bedf8053341df3b5a9f9e0f2cf4e79e27360c6\n with:\n username: 'user'\n password: ${{ secrets.DOCKERHUB_TOKEN }}\n\n - name: Docker Push Patched Image\n if: steps.login.conclusion == 'success'\n run: |\n docker push ${{ steps.copa.outputs.patched-image }}\n\n")))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/1b7d20b2.f7af2c6c.js b/website/assets/js/1b7d20b2.f7af2c6c.js new file mode 100644 index 00000000..1847b021 --- /dev/null +++ b/website/assets/js/1b7d20b2.f7af2c6c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8313],{3988:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>a,contentTitle:()=>s,default:()=>u,frontMatter:()=>c,metadata:()=>r,toc:()=>d});var i=t(4848),o=t(8453);const c={title:"Copa Github Action"},s=void 0,r={id:"github-action",title:"Copa Github Action",description:"Overview",source:"@site/versioned_docs/version-v0.4.x/github-action.md",sourceDirName:".",slug:"/github-action",permalink:"/copacetic/website/v0.4.x/github-action",draft:!1,unlisted:!1,tags:[],version:"v0.4.x",frontMatter:{title:"Copa Github Action"},sidebar:"sidebar",previous:{title:"Code of Conduct",permalink:"/copacetic/website/v0.4.x/code-of-conduct"},next:{title:"Release Process",permalink:"/copacetic/website/v0.4.x/release"}},a={},d=[{value:"Overview",id:"overview",level:2},{value:"Inputs",id:"inputs",level:2},{value:"<code>image</code>",id:"image",level:2},{value:"<code>image-report</code>",id:"image-report",level:2},{value:"<code>patched-tag</code>",id:"patched-tag",level:2},{value:"<code>buildkit-version</code>",id:"buildkit-version",level:2},{value:"<code>copa-version</code>",id:"copa-version",level:2},{value:"Output",id:"output",level:2},{value:"<code>patched-image</code>",id:"patched-image",level:2},{value:"Example Workflow",id:"example-workflow",level:2}];function l(e){const n={a:"a",code:"code",h2:"h2",p:"p",pre:"pre",strong:"strong",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.h2,{id:"overview",children:"Overview"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.a,{href:"https://github.com/project-copacetic/copa-action",children:"Copa Github Action"})," allows you patch vulnerable containers in your workflows using Copa."]}),"\n",(0,i.jsx)(n.h2,{id:"inputs",children:"Inputs"}),"\n",(0,i.jsx)(n.h2,{id:"image",children:(0,i.jsx)(n.code,{children:"image"})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Required"})," The image reference to patch."]}),"\n",(0,i.jsx)(n.h2,{id:"image-report",children:(0,i.jsx)(n.code,{children:"image-report"})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Required"})," The trivy json vulnerability report of the image to patch."]}),"\n",(0,i.jsx)(n.h2,{id:"patched-tag",children:(0,i.jsx)(n.code,{children:"patched-tag"})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Required"})," The new patched image tag."]}),"\n",(0,i.jsx)(n.h2,{id:"buildkit-version",children:(0,i.jsx)(n.code,{children:"buildkit-version"})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Optional"})," The buildkit version used in the action, default is latest."]}),"\n",(0,i.jsx)(n.h2,{id:"copa-version",children:(0,i.jsx)(n.code,{children:"copa-version"})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Optional"})," The Copa version used in the action, default is latest."]}),"\n",(0,i.jsx)(n.h2,{id:"output",children:"Output"}),"\n",(0,i.jsx)(n.h2,{id:"patched-image",children:(0,i.jsx)(n.code,{children:"patched-image"})}),"\n",(0,i.jsx)(n.p,{children:"Image reference of the resulting patched image."}),"\n",(0,i.jsx)(n.h2,{id:"example-workflow",children:"Example Workflow"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"on: [push]\n\njobs:\n test:\n runs-on: ubuntu-latest\n\n strategy:\n fail-fast: false\n matrix:\n # provide relevant list of images to scan on each run\n images: ['docker.io/library/nginx:1.21.6', 'docker.io/openpolicyagent/opa:0.46.0', 'docker.io/library/hello-world:latest']\n\n steps:\n - name: Set up Docker Buildx\n uses: docker/setup-buildx-action@dedd61cf5d839122591f5027c89bf3ad27691d18\n\n - name: Generate Trivy Report\n uses: aquasecurity/trivy-action@69cbbc0cbbf6a2b0bab8dcf0e9f2d7ead08e87e4\n with:\n scan-type: 'image'\n format: 'json'\n output: 'report.json'\n ignore-unfixed: true\n vuln-type: 'os'\n image-ref: ${{ matrix.images }}\n\n - name: Check Vuln Count\n id: vuln_count\n run: |\n report_file=\"report.json\"\n vuln_count=$(jq '.Results | length' \"$report_file\")\n echo \"vuln_count=$vuln_count\" >> $GITHUB_OUTPUT\n\n - name: Copa Action\n if: steps.vuln_count.outputs.vuln_count != '0'\n id: copa\n uses: project-copacetic/copa-action@v1\n with:\n image: ${{ matrix.images }}\n image-report: 'report.json'\n patched-tag: 'patched'\n buildkit-version: 'v0.11.6'\n # optional, default is latest\n copa-version: '0.4.1'\n\n - name: Login to Docker Hub\n if: steps.copa.conclusion == 'success'\n id: login\n uses: docker/login-action@b4bedf8053341df3b5a9f9e0f2cf4e79e27360c6\n with:\n username: 'user'\n password: ${{ secrets.DOCKERHUB_TOKEN }}\n\n - name: Docker Push Patched Image\n if: steps.login.conclusion == 'success'\n run: |\n docker push ${{ steps.copa.outputs.patched-image }}\n\n"})})]})}function u(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(l,{...e})}):l(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>s,x:()=>r});var i=t(6540);const o={},c=i.createContext(o);function s(e){const n=i.useContext(c);return i.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function r(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:s(e.components),i.createElement(c.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/1be78505.61acc940.js b/website/assets/js/1be78505.61acc940.js deleted file mode 100644 index c1f797f4..00000000 --- a/website/assets/js/1be78505.61acc940.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9514,4972],{9963:(e,t,n)=>{n.r(t),n.d(t,{default:()=>Se});var a=n(7294),l=n(4334),o=n(1944),r=n(5281),i=n(3320),c=n(2802),s=n(4477),d=n(1116),m=n(7961),u=n(5999),b=n(2466),p=n(5936);const h="backToTopButton_sjWU",E="backToTopButtonShow_xfvO";function f(){const{shown:e,scrollToTop:t}=function(e){let{threshold:t}=e;const[n,l]=(0,a.useState)(!1),o=(0,a.useRef)(!1),{startScroll:r,cancelScroll:i}=(0,b.Ct)();return(0,b.RF)(((e,n)=>{let{scrollY:a}=e;const r=n?.scrollY;r&&(o.current?o.current=!1:a>=r?(i(),l(!1)):a<t?l(!1):a+window.innerHeight<document.documentElement.scrollHeight&&l(!0))})),(0,p.S)((e=>{e.location.hash&&(o.current=!0,l(!1))})),{shown:n,scrollToTop:()=>r(0)}}({threshold:300});return a.createElement("button",{"aria-label":(0,u.I)({id:"theme.BackToTopButton.buttonAriaLabel",message:"Scroll back to top",description:"The ARIA label for the back to top button"}),className:(0,l.Z)("clean-btn",r.k.common.backToTopButton,h,e&&E),type:"button",onClick:t})}var v=n(1442),g=n(6550),_=n(7524),k=n(6668),C=n(1327),S=n(7462);function I(e){return a.createElement("svg",(0,S.Z)({width:"20",height:"20","aria-hidden":"true"},e),a.createElement("g",{fill:"#7a7a7a"},a.createElement("path",{d:"M9.992 10.023c0 .2-.062.399-.172.547l-4.996 7.492a.982.982 0 01-.828.454H1c-.55 0-1-.453-1-1 0-.2.059-.403.168-.551l4.629-6.942L.168 3.078A.939.939 0 010 2.528c0-.548.45-.997 1-.997h2.996c.352 0 .649.18.828.45L9.82 9.472c.11.148.172.347.172.55zm0 0"}),a.createElement("path",{d:"M19.98 10.023c0 .2-.058.399-.168.547l-4.996 7.492a.987.987 0 01-.828.454h-3c-.547 0-.996-.453-.996-1 0-.2.059-.403.168-.551l4.625-6.942-4.625-6.945a.939.939 0 01-.168-.55 1 1 0 01.996-.997h3c.348 0 .649.18.828.45l4.996 7.492c.11.148.168.347.168.55zm0 0"})))}const N="collapseSidebarButton_PEFL",Z="collapseSidebarButtonIcon_kv0_";function x(e){let{onClick:t}=e;return a.createElement("button",{type:"button",title:(0,u.I)({id:"theme.docs.sidebar.collapseButtonTitle",message:"Collapse sidebar",description:"The title attribute for collapse button of doc sidebar"}),"aria-label":(0,u.I)({id:"theme.docs.sidebar.collapseButtonAriaLabel",message:"Collapse sidebar",description:"The title attribute for collapse button of doc sidebar"}),className:(0,l.Z)("button button--secondary button--outline",N),onClick:t},a.createElement(I,{className:Z}))}var T=n(9689),y=n(902);const w=Symbol("EmptyContext"),L=a.createContext(w);function A(e){let{children:t}=e;const[n,l]=(0,a.useState)(null),o=(0,a.useMemo)((()=>({expandedItem:n,setExpandedItem:l})),[n]);return a.createElement(L.Provider,{value:o},t)}var M=n(6043),B=n(8596),F=n(9960),H=n(2389);function P(e){let{categoryLabel:t,onClick:n}=e;return a.createElement("button",{"aria-label":(0,u.I)({id:"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel",message:"Toggle the collapsible sidebar category '{label}'",description:"The ARIA label to toggle the collapsible sidebar category"},{label:t}),type:"button",className:"clean-btn menu__caret",onClick:n})}function D(e){let{item:t,onItemClick:n,activePath:o,level:i,index:s,...d}=e;const{items:m,label:u,collapsible:b,className:p,href:h}=t,{docs:{sidebar:{autoCollapseCategories:E}}}=(0,k.L)(),f=function(e){const t=(0,H.Z)();return(0,a.useMemo)((()=>e.href?e.href:!t&&e.collapsible?(0,c.Wl)(e):void 0),[e,t])}(t),v=(0,c._F)(t,o),g=(0,B.Mg)(h,o),{collapsed:_,setCollapsed:C}=(0,M.u)({initialState:()=>!!b&&(!v&&t.collapsed)}),{expandedItem:I,setExpandedItem:N}=function(){const e=(0,a.useContext)(L);if(e===w)throw new y.i6("DocSidebarItemsExpandedStateProvider");return e}(),Z=function(e){void 0===e&&(e=!_),N(e?null:s),C(e)};return function(e){let{isActive:t,collapsed:n,updateCollapsed:l}=e;const o=(0,y.D9)(t);(0,a.useEffect)((()=>{t&&!o&&n&&l(!1)}),[t,o,n,l])}({isActive:v,collapsed:_,updateCollapsed:Z}),(0,a.useEffect)((()=>{b&&null!=I&&I!==s&&E&&C(!0)}),[b,I,s,C,E]),a.createElement("li",{className:(0,l.Z)(r.k.docs.docSidebarItemCategory,r.k.docs.docSidebarItemCategoryLevel(i),"menu__list-item",{"menu__list-item--collapsed":_},p)},a.createElement("div",{className:(0,l.Z)("menu__list-item-collapsible",{"menu__list-item-collapsible--active":g})},a.createElement(F.Z,(0,S.Z)({className:(0,l.Z)("menu__link",{"menu__link--sublist":b,"menu__link--sublist-caret":!h&&b,"menu__link--active":v}),onClick:b?e=>{n?.(t),h?Z(!1):(e.preventDefault(),Z())}:()=>{n?.(t)},"aria-current":g?"page":void 0,"aria-expanded":b?!_:void 0,href:b?f??"#":f},d),u),h&&b&&a.createElement(P,{categoryLabel:u,onClick:e=>{e.preventDefault(),Z()}})),a.createElement(M.z,{lazy:!0,as:"ul",className:"menu__list",collapsed:_},a.createElement(G,{items:m,tabIndex:_?-1:0,onItemClick:n,activePath:o,level:i+1})))}var W=n(3919),R=n(9471);const V="menuExternalLink_NmtK";function z(e){let{item:t,onItemClick:n,activePath:o,level:i,index:s,...d}=e;const{href:m,label:u,className:b,autoAddBaseUrl:p}=t,h=(0,c._F)(t,o),E=(0,W.Z)(m);return a.createElement("li",{className:(0,l.Z)(r.k.docs.docSidebarItemLink,r.k.docs.docSidebarItemLinkLevel(i),"menu__list-item",b),key:u},a.createElement(F.Z,(0,S.Z)({className:(0,l.Z)("menu__link",!E&&V,{"menu__link--active":h}),autoAddBaseUrl:p,"aria-current":h?"page":void 0,to:m},E&&{onClick:n?()=>n(t):void 0},d),u,!E&&a.createElement(R.Z,null)))}const U="menuHtmlItem_M9Kj";function K(e){let{item:t,level:n,index:o}=e;const{value:i,defaultStyle:c,className:s}=t;return a.createElement("li",{className:(0,l.Z)(r.k.docs.docSidebarItemLink,r.k.docs.docSidebarItemLinkLevel(n),c&&[U,"menu__list-item"],s),key:o,dangerouslySetInnerHTML:{__html:i}})}function j(e){let{item:t,...n}=e;switch(t.type){case"category":return a.createElement(D,(0,S.Z)({item:t},n));case"html":return a.createElement(K,(0,S.Z)({item:t},n));default:return a.createElement(z,(0,S.Z)({item:t},n))}}function q(e){let{items:t,...n}=e;return a.createElement(A,null,t.map(((e,t)=>a.createElement(j,(0,S.Z)({key:t,item:e,index:t},n)))))}const G=(0,a.memo)(q),Y="menu_SIkG",O="menuWithAnnouncementBar_GW3s";function X(e){let{path:t,sidebar:n,className:o}=e;const i=function(){const{isActive:e}=(0,T.nT)(),[t,n]=(0,a.useState)(e);return(0,b.RF)((t=>{let{scrollY:a}=t;e&&n(0===a)}),[e]),e&&t}();return a.createElement("nav",{"aria-label":(0,u.I)({id:"theme.docs.sidebar.navAriaLabel",message:"Docs sidebar",description:"The ARIA label for the sidebar navigation"}),className:(0,l.Z)("menu thin-scrollbar",Y,i&&O,o)},a.createElement("ul",{className:(0,l.Z)(r.k.docs.docSidebarMenu,"menu__list")},a.createElement(G,{items:n,activePath:t,level:1})))}const J="sidebar_njMd",Q="sidebarWithHideableNavbar_wUlq",$="sidebarHidden_VK0M",ee="sidebarLogo_isFc";function te(e){let{path:t,sidebar:n,onCollapse:o,isHidden:r}=e;const{navbar:{hideOnScroll:i},docs:{sidebar:{hideable:c}}}=(0,k.L)();return a.createElement("div",{className:(0,l.Z)(J,i&&Q,r&&$)},i&&a.createElement(C.Z,{tabIndex:-1,className:ee}),a.createElement(X,{path:t,sidebar:n}),c&&a.createElement(x,{onClick:o}))}const ne=a.memo(te);var ae=n(3102),le=n(2961);const oe=e=>{let{sidebar:t,path:n}=e;const o=(0,le.e)();return a.createElement("ul",{className:(0,l.Z)(r.k.docs.docSidebarMenu,"menu__list")},a.createElement(G,{items:t,activePath:n,onItemClick:e=>{"category"===e.type&&e.href&&o.toggle(),"link"===e.type&&o.toggle()},level:1}))};function re(e){return a.createElement(ae.Zo,{component:oe,props:e})}const ie=a.memo(re);function ce(e){const t=(0,_.i)(),n="desktop"===t||"ssr"===t,l="mobile"===t;return a.createElement(a.Fragment,null,n&&a.createElement(ne,e),l&&a.createElement(ie,e))}const se="expandButton_m80_",de="expandButtonIcon_BlDH";function me(e){let{toggleSidebar:t}=e;return a.createElement("div",{className:se,title:(0,u.I)({id:"theme.docs.sidebar.expandButtonTitle",message:"Expand sidebar",description:"The ARIA label and title attribute for expand button of doc sidebar"}),"aria-label":(0,u.I)({id:"theme.docs.sidebar.expandButtonAriaLabel",message:"Expand sidebar",description:"The ARIA label and title attribute for expand button of doc sidebar"}),tabIndex:0,role:"button",onKeyDown:t,onClick:t},a.createElement(I,{className:de}))}const ue={docSidebarContainer:"docSidebarContainer_b6E3",docSidebarContainerHidden:"docSidebarContainerHidden_b3ry",sidebarViewport:"sidebarViewport_Xe31"};function be(e){let{children:t}=e;const n=(0,d.V)();return a.createElement(a.Fragment,{key:n?.name??"noSidebar"},t)}function pe(e){let{sidebar:t,hiddenSidebarContainer:n,setHiddenSidebarContainer:o}=e;const{pathname:i}=(0,g.TH)(),[c,s]=(0,a.useState)(!1),d=(0,a.useCallback)((()=>{c&&s(!1),!c&&(0,v.n)()&&s(!0),o((e=>!e))}),[o,c]);return a.createElement("aside",{className:(0,l.Z)(r.k.docs.docSidebarContainer,ue.docSidebarContainer,n&&ue.docSidebarContainerHidden),onTransitionEnd:e=>{e.currentTarget.classList.contains(ue.docSidebarContainer)&&n&&s(!0)}},a.createElement(be,null,a.createElement("div",{className:(0,l.Z)(ue.sidebarViewport,c&&ue.sidebarViewportHidden)},a.createElement(ce,{sidebar:t,path:i,onCollapse:d,isHidden:c}),c&&a.createElement(me,{toggleSidebar:d}))))}const he={docMainContainer:"docMainContainer_gTbr",docMainContainerEnhanced:"docMainContainerEnhanced_Uz_u",docItemWrapperEnhanced:"docItemWrapperEnhanced_czyv"};function Ee(e){let{hiddenSidebarContainer:t,children:n}=e;const o=(0,d.V)();return a.createElement("main",{className:(0,l.Z)(he.docMainContainer,(t||!o)&&he.docMainContainerEnhanced)},a.createElement("div",{className:(0,l.Z)("container padding-top--md padding-bottom--lg",he.docItemWrapper,t&&he.docItemWrapperEnhanced)},n))}const fe="docPage__5DB",ve="docsWrapper_BCFX";function ge(e){let{children:t}=e;const n=(0,d.V)(),[l,o]=(0,a.useState)(!1);return a.createElement(m.Z,{wrapperClassName:ve},a.createElement(f,null),a.createElement("div",{className:fe},n&&a.createElement(pe,{sidebar:n.items,hiddenSidebarContainer:l,setHiddenSidebarContainer:o}),a.createElement(Ee,{hiddenSidebarContainer:l},t)))}var _e=n(4972),ke=n(197);function Ce(e){const{versionMetadata:t}=e;return a.createElement(a.Fragment,null,a.createElement(ke.Z,{version:t.version,tag:(0,i.os)(t.pluginId,t.version)}),a.createElement(o.d,null,t.noIndex&&a.createElement("meta",{name:"robots",content:"noindex, nofollow"})))}function Se(e){const{versionMetadata:t}=e,n=(0,c.hI)(e);if(!n)return a.createElement(_e.default,null);const{docElement:i,sidebarName:m,sidebarItems:u}=n;return a.createElement(a.Fragment,null,a.createElement(Ce,e),a.createElement(o.FG,{className:(0,l.Z)(r.k.wrapper.docsPages,r.k.page.docsDocPage,e.versionMetadata.className)},a.createElement(s.q,{version:t},a.createElement(d.b,{name:m,items:u},a.createElement(ge,null,i)))))}},4972:(e,t,n)=>{n.r(t),n.d(t,{default:()=>i});var a=n(7294),l=n(5999),o=n(1944),r=n(7961);function i(){return a.createElement(a.Fragment,null,a.createElement(o.d,{title:(0,l.I)({id:"theme.NotFound.title",message:"Page Not Found"})}),a.createElement(r.Z,null,a.createElement("main",{className:"container margin-vert--xl"},a.createElement("div",{className:"row"},a.createElement("div",{className:"col col--6 col--offset-3"},a.createElement("h1",{className:"hero__title"},a.createElement(l.Z,{id:"theme.NotFound.title",description:"The title of the 404 page"},"Page Not Found")),a.createElement("p",null,a.createElement(l.Z,{id:"theme.NotFound.p1",description:"The first paragraph of the 404 page"},"We could not find what you were looking for.")),a.createElement("p",null,a.createElement(l.Z,{id:"theme.NotFound.p2",description:"The 2nd paragraph of the 404 page"},"Please contact the owner of the site that linked you to the original URL and let them know their link is broken.")))))))}},4477:(e,t,n)=>{n.d(t,{E:()=>i,q:()=>r});var a=n(7294),l=n(902);const o=a.createContext(null);function r(e){let{children:t,version:n}=e;return a.createElement(o.Provider,{value:n},t)}function i(){const e=(0,a.useContext)(o);if(null===e)throw new l.i6("DocsVersionProvider");return e}}}]); \ No newline at end of file diff --git a/website/assets/js/2133a534.8db371e7.js b/website/assets/js/2133a534.8db371e7.js new file mode 100644 index 00000000..f2318e47 --- /dev/null +++ b/website/assets/js/2133a534.8db371e7.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2193],{7763:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>r,default:()=>p,frontMatter:()=>s,metadata:()=>a,toc:()=>l});var o=n(4848),i=n(8453);const s={title:"Installation"},r=void 0,a={id:"installation",title:"Installation",description:"Homebrew",source:"@site/versioned_docs/version-v0.1.x/installation.md",sourceDirName:".",slug:"/installation",permalink:"/copacetic/website/v0.1.x/installation",draft:!1,unlisted:!1,tags:[],version:"v0.1.x",frontMatter:{title:"Installation"},sidebar:"sidebar",previous:{title:"Introduction",permalink:"/copacetic/website/v0.1.x/"},next:{title:"Quick Start",permalink:"/copacetic/website/v0.1.x/quick-start"}},c={},l=[{value:"Homebrew",id:"homebrew",level:2},{value:"GitHub",id:"github",level:2},{value:"Development Setup",id:"development-setup",level:2}];function d(e){const t={a:"a",code:"code",h2:"h2",p:"p",pre:"pre",strong:"strong",...(0,i.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(t.h2,{id:"homebrew",children:"Homebrew"}),"\n",(0,o.jsxs)(t.p,{children:["On macOS and Linux, ",(0,o.jsx)(t.code,{children:"copa"})," can be installed via ",(0,o.jsx)(t.a,{href:"https://brew.sh/",children:"Homebrew"}),":"]}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-bash",children:"brew install copa\n"})}),"\n",(0,o.jsx)(t.h2,{id:"github",children:"GitHub"}),"\n",(0,o.jsxs)(t.p,{children:["You can download the latest and previous versions of ",(0,o.jsx)(t.code,{children:"copa"})," from the ",(0,o.jsx)(t.a,{href:"https://github.com/project-copacetic/copacetic/releases",children:"GitHub releases page"}),"."]}),"\n",(0,o.jsx)(t.h2,{id:"development-setup",children:"Development Setup"}),"\n",(0,o.jsxs)(t.p,{children:["The following instructions are for ",(0,o.jsx)(t.strong,{children:"Ubuntu 22.04"})," with the dependency versions supported as part of the ",(0,o.jsx)(t.a,{href:"./contributing.md/#visual-studio-code-development-container",children:"dev container"})," environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead."]}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-bash",children:"git clone https://github.com/project-copacetic/copacetic\ncd copacetic\nmake\n# OPTIONAL: install copa to a pathed folder\nsudo mv dist/linux_amd64/release/copa /usr/local/bin/\n"})})]})}function p(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,o.jsx)(t,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>r,x:()=>a});var o=n(6540);const i={},s=o.createContext(i);function r(e){const t=o.useContext(s);return o.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function a(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:r(e.components),o.createElement(s.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/2133a534.f95c97ab.js b/website/assets/js/2133a534.f95c97ab.js deleted file mode 100644 index 48b52b4d..00000000 --- a/website/assets/js/2133a534.f95c97ab.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5737],{3905:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function c(e,t){if(null==e)return{};var n,r,a=function(e,t){if(null==e)return{};var n,r,a={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=r.createContext({}),p=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},s=function(e){var t=p(e.components);return r.createElement(l.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,l=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),u=p(n),m=a,f=u["".concat(l,".").concat(m)]||u[m]||d[m]||o;return n?r.createElement(f,i(i({ref:t},s),{},{components:n})):r.createElement(f,i({ref:t},s))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=m;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[u]="string"==typeof e?e:a,i[1]=c;for(var p=2;p<o;p++)i[p]=n[p];return r.createElement.apply(null,i)}return r.createElement.apply(null,n)}m.displayName="MDXCreateElement"},5389:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>c,toc:()=>p});var r=n(7462),a=(n(7294),n(3905));const o={title:"Installation"},i=void 0,c={unversionedId:"installation",id:"version-v0.1.x/installation",title:"Installation",description:"Homebrew",source:"@site/versioned_docs/version-v0.1.x/installation.md",sourceDirName:".",slug:"/installation",permalink:"/copacetic/website/v0.1.x/installation",draft:!1,tags:[],version:"v0.1.x",frontMatter:{title:"Installation"},sidebar:"sidebar",previous:{title:"Introduction",permalink:"/copacetic/website/v0.1.x/"},next:{title:"Quick Start",permalink:"/copacetic/website/v0.1.x/quick-start"}},l={},p=[{value:"Homebrew",id:"homebrew",level:2},{value:"GitHub",id:"github",level:2},{value:"Development Setup",id:"development-setup",level:2}],s={toc:p};function u(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h2",{id:"homebrew"},"Homebrew"),(0,a.kt)("p",null,"On macOS and Linux, ",(0,a.kt)("inlineCode",{parentName:"p"},"copa")," can be installed via ",(0,a.kt)("a",{parentName:"p",href:"https://brew.sh/"},"Homebrew"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"brew install copa\n")),(0,a.kt)("h2",{id:"github"},"GitHub"),(0,a.kt)("p",null,"You can download the latest and previous versions of ",(0,a.kt)("inlineCode",{parentName:"p"},"copa")," from the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/releases"},"GitHub releases page"),"."),(0,a.kt)("h2",{id:"development-setup"},"Development Setup"),(0,a.kt)("p",null,"The following instructions are for ",(0,a.kt)("strong",{parentName:"p"},"Ubuntu 22.04")," with the dependency versions supported as part of the ",(0,a.kt)("a",{parentName:"p",href:"/copacetic/website/v0.1.x/contributing/#visual-studio-code-development-container"},"dev container")," environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"git clone https://github.com/project-copacetic/copacetic\ncd copacetic\nmake\n# OPTIONAL: install copa to a pathed folder\nsudo mv dist/linux_amd64/release/copa /usr/local/bin/\n")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/2237.ede9a640.js b/website/assets/js/2237.ede9a640.js new file mode 100644 index 00000000..4303d8ec --- /dev/null +++ b/website/assets/js/2237.ede9a640.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2237],{3363:(e,t,i)=>{i.d(t,{A:()=>a});i(6540);var n=i(4164),s=i(1312),o=i(1107),r=i(4848);function a(e){let{className:t}=e;return(0,r.jsx)("main",{className:(0,n.A)("container margin-vert--xl",t),children:(0,r.jsx)("div",{className:"row",children:(0,r.jsxs)("div",{className:"col col--6 col--offset-3",children:[(0,r.jsx)(o.A,{as:"h1",className:"hero__title",children:(0,r.jsx)(s.A,{id:"theme.NotFound.title",description:"The title of the 404 page",children:"Page Not Found"})}),(0,r.jsx)("p",{children:(0,r.jsx)(s.A,{id:"theme.NotFound.p1",description:"The first paragraph of the 404 page",children:"We could not find what you were looking for."})}),(0,r.jsx)("p",{children:(0,r.jsx)(s.A,{id:"theme.NotFound.p2",description:"The 2nd paragraph of the 404 page",children:"Please contact the owner of the site that linked you to the original URL and let them know their link is broken."})})]})})})}},2237:(e,t,i)=>{i.r(t),i.d(t,{default:()=>l});i(6540);var n=i(1312),s=i(1003),o=i(781),r=i(3363),a=i(4848);function l(){const e=(0,n.T)({id:"theme.NotFound.title",message:"Page Not Found"});return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(s.be,{title:e}),(0,a.jsx)(o.A,{children:(0,a.jsx)(r.A,{})})]})}}}]); \ No newline at end of file diff --git a/website/assets/js/22539a87.04ad19a2.js b/website/assets/js/22539a87.04ad19a2.js new file mode 100644 index 00000000..6df586c1 --- /dev/null +++ b/website/assets/js/22539a87.04ad19a2.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9702],{5890:(e,i,n)=>{n.r(i),n.d(i,{assets:()=>c,contentTitle:()=>o,default:()=>h,frontMatter:()=>s,metadata:()=>r,toc:()=>l});var t=n(4848),a=n(8453);const s={title:"Introduction",slug:"/"},o="Project Copacetic: Directly patch container image vulnerabilities",r={id:"introduction",title:"Introduction",description:"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.",source:"@site/versioned_docs/version-v0.4.x/introduction.md",sourceDirName:".",slug:"/",permalink:"/copacetic/website/v0.4.x/",draft:!1,unlisted:!1,tags:[],version:"v0.4.x",frontMatter:{title:"Introduction",slug:"/"},sidebar:"sidebar",next:{title:"Installation",permalink:"/copacetic/website/v0.4.x/installation"}},c={},l=[{value:"Why?",id:"why",level:2},{value:"How?",id:"how",level:2}];function d(e){const i={a:"a",code:"code",em:"em",h1:"h1",h2:"h2",li:"li",ol:"ol",p:"p",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i.h1,{id:"project-copacetic-directly-patch-container-image-vulnerabilities",children:"Project Copacetic: Directly patch container image vulnerabilities"}),"\n",(0,t.jsxs)(i.p,{children:[(0,t.jsx)(i.code,{children:"copa"})," is a CLI tool written in ",(0,t.jsx)(i.a,{href:"https://golang.org",children:"Go"})," and based on ",(0,t.jsx)(i.a,{href:"https://github.com/moby/buildkit",children:"buildkit"})," that can be used to directly patch container images given the vulnerability scanning results from popular tools like ",(0,t.jsx)(i.a,{href:"https://github.com/aquasecurity/trivy",children:"Trivy"}),"."]}),"\n",(0,t.jsx)(i.h2,{id:"why",children:"Why?"}),"\n",(0,t.jsxs)(i.p,{children:["We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between ",(0,t.jsx)(i.a,{href:"https://www.bleepingcomputer.com/news/security/hackers-scan-for-vulnerabilities-within-15-minutes-of-disclosure/",children:"vulnerability disclosure and active exploitation continues to narrow"}),", there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:"]}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option"}),"\n",(0,t.jsx)(i.li,{children:"found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs."}),"\n"]}),"\n",(0,t.jsx)("img",{title:"direct image patching",src:"/copacetic/website/img/direct-image-patching.png"}),"\n",(0,t.jsxs)(i.p,{children:["In addition to filling the operational gap not met by left-shift security practices and tools, the ability of ",(0,t.jsx)(i.code,{children:"copa"})," to patch a container without requiring a rebuild of the container image provides other benefits:"]}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Allows users other than the image publishers to also patch container images, such as DevSecOps engineers."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image."}),"\n"]}),"\n",(0,t.jsx)(i.h2,{id:"how",children:"How?"}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"copa"})," tool is an extensible engine that:"]}),"\n",(0,t.jsxs)(i.ol,{children:["\n",(0,t.jsx)(i.li,{children:"Parses the needed update packages from the container image\u2019s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats."}),"\n",(0,t.jsx)(i.li,{children:"Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers."}),"\n",(0,t.jsx)(i.li,{children:"Applies the resulting update binaries to the container image using buildkit."}),"\n"]}),"\n",(0,t.jsx)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),"\n",(0,t.jsx)(i.p,{children:"This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:"}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:[(0,t.jsxs)(i.strong,{children:["Copa supports patching ",(0,t.jsx)(i.em,{children:"existing"})," container images"]}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Devs don't need to build their images using specific tools or modify them in some way just to support container patching."}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Copa works with the existing vulnerability scanning and mitigation ecosystems"}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today."}),"\n",(0,t.jsx)(i.li,{children:"Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports."}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image"}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today."}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.p,{children:["For more details, refer to the ",(0,t.jsx)(i.a,{href:"/copacetic/website/v0.4.x/design",children:"copa design"})," documentation."]})]})}function h(e={}){const{wrapper:i}={...(0,a.R)(),...e.components};return i?(0,t.jsx)(i,{...e,children:(0,t.jsx)(d,{...e})}):d(e)}},8453:(e,i,n)=>{n.d(i,{R:()=>o,x:()=>r});var t=n(6540);const a={},s=t.createContext(a);function o(e){const i=t.useContext(s);return t.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function r(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),t.createElement(s.Provider,{value:i},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/22539a87.bed468c8.js b/website/assets/js/22539a87.bed468c8.js deleted file mode 100644 index 119e2a62..00000000 --- a/website/assets/js/22539a87.bed468c8.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5964],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>g});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),c=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(s.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=c(n),m=i,g=u["".concat(s,".").concat(m)]||u[m]||d[m]||r;return n?a.createElement(g,o(o({ref:t},p),{},{components:n})):a.createElement(g,o({ref:t},p))}));function g(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[u]="string"==typeof e?e:i,o[1]=l;for(var c=2;c<r;c++)o[c]=n[c];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}m.displayName="MDXCreateElement"},9684:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>l,toc:()=>c});var a=n(7462),i=(n(7294),n(3905));const r={title:"Introduction",slug:"/"},o="Project Copacetic: Directly patch container image vulnerabilities",l={unversionedId:"introduction",id:"version-v0.4.x/introduction",title:"Introduction",description:"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.",source:"@site/versioned_docs/version-v0.4.x/introduction.md",sourceDirName:".",slug:"/",permalink:"/copacetic/website/v0.4.x/",draft:!1,tags:[],version:"v0.4.x",frontMatter:{title:"Introduction",slug:"/"},sidebar:"sidebar",next:{title:"Installation",permalink:"/copacetic/website/v0.4.x/installation"}},s={},c=[{value:"Why?",id:"why",level:2},{value:"How?",id:"how",level:2}],p={toc:c};function u(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"project-copacetic-directly-patch-container-image-vulnerabilities"},"Project Copacetic: Directly patch container image vulnerabilities"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"copa")," is a CLI tool written in ",(0,i.kt)("a",{parentName:"p",href:"https://golang.org"},"Go")," and based on ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit"},"buildkit")," that can be used to directly patch container images given the vulnerability scanning results from popular tools like ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/aquasecurity/trivy"},"Trivy"),"."),(0,i.kt)("h2",{id:"why"},"Why?"),(0,i.kt)("p",null,"We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between ",(0,i.kt)("a",{parentName:"p",href:"https://www.bleepingcomputer.com/news/security/hackers-scan-for-vulnerabilities-within-15-minutes-of-disclosure/"},"vulnerability disclosure and active exploitation continues to narrow"),", there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option"),(0,i.kt)("li",{parentName:"ul"},"found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.")),(0,i.kt)("img",{title:"direct image patching",src:"/copacetic/website/img/direct-image-patching.png"}),(0,i.kt)("p",null,"In addition to filling the operational gap not met by left-shift security practices and tools, the ability of ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," to patch a container without requiring a rebuild of the container image provides other benefits:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Allows users other than the image publishers to also patch container images, such as DevSecOps engineers."),(0,i.kt)("li",{parentName:"ul"},"Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching."),(0,i.kt)("li",{parentName:"ul"},"Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild."),(0,i.kt)("li",{parentName:"ul"},"Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.")),(0,i.kt)("h2",{id:"how"},"How?"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," tool is an extensible engine that:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Parses the needed update packages from the container image\u2019s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats."),(0,i.kt)("li",{parentName:"ol"},"Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers."),(0,i.kt)("li",{parentName:"ol"},"Applies the resulting update binaries to the container image using buildkit.")),(0,i.kt)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),(0,i.kt)("p",null,"This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa supports patching ",(0,i.kt)("em",{parentName:"strong"},"existing")," container images"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"Devs don't need to build their images using specific tools or modify them in some way just to support container patching."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa works with the existing vulnerability scanning and mitigation ecosystems"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today."),(0,i.kt)("li",{parentName:"ul"},"Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.")))),(0,i.kt)("p",null,"For more details, refer to the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.4.x/design"},"copa design")," documentation."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/22dce6f4.61bd0f54.js b/website/assets/js/22dce6f4.61bd0f54.js deleted file mode 100644 index ee48f2b3..00000000 --- a/website/assets/js/22dce6f4.61bd0f54.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7252],{3905:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>u});var i=a(7294);function n(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,i)}return a}function o(e){for(var t=1;t<arguments.length;t++){var a=null!=arguments[t]?arguments[t]:{};t%2?r(Object(a),!0).forEach((function(t){n(e,t,a[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):r(Object(a)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))}))}return e}function s(e,t){if(null==e)return{};var a,i,n=function(e,t){if(null==e)return{};var a,i,n={},r=Object.keys(e);for(i=0;i<r.length;i++)a=r[i],t.indexOf(a)>=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i<r.length;i++)a=r[i],t.indexOf(a)>=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var l=i.createContext({}),p=function(e){var t=i.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},c=function(e){var t=p(e.components);return i.createElement(l.Provider,{value:t},e.children)},d="mdxType",g={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},h=i.forwardRef((function(e,t){var a=e.components,n=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=p(a),h=n,u=d["".concat(l,".").concat(h)]||d[h]||g[h]||r;return a?i.createElement(u,o(o({ref:t},c),{},{components:a})):i.createElement(u,o({ref:t},c))}));function u(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var r=a.length,o=new Array(r);o[0]=h;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[d]="string"==typeof e?e:n,o[1]=s;for(var p=2;p<r;p++)o[p]=a[p];return i.createElement.apply(null,o)}return i.createElement.apply(null,a)}h.displayName="MDXCreateElement"},2014:(e,t,a)=>{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>d,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var i=a(7462),n=(a(7294),a(3905));const r={title:"Design"},o=void 0,s={unversionedId:"design",id:"version-v0.5.x/design",title:"Design",description:"Design Tenets",source:"@site/versioned_docs/version-v0.5.x/design.md",sourceDirName:".",slug:"/design",permalink:"/copacetic/website/v0.5.x/design",draft:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Design"},sidebar:"sidebar",previous:{title:"Troubleshooting",permalink:"/copacetic/website/v0.5.x/troubleshooting"},next:{title:"FAQ",permalink:"/copacetic/website/v0.5.x/faq"}},l={},p=[{value:"Design Tenets",id:"design-tenets",level:2},{value:"Design Reasoning",id:"design-reasoning",level:2},{value:"Architecture",id:"architecture",level:2},{value:"Implementation",id:"implementation",level:2},{value:"Tradeoffs",id:"tradeoffs",level:2}],c={toc:p};function d(e){let{components:t,...a}=e;return(0,n.kt)("wrapper",(0,i.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h2",{id:"design-tenets"},"Design Tenets"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update.")," This is a raison d\u2019etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to work with the existing ecosystem of container images.")," The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to allow parties other than the image authors to address container vulnerabilities.")," Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to do one thing well and be composable with other tools and processes.")," Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane."))),(0,n.kt)("h2",{id:"design-reasoning"},"Design Reasoning"),(0,n.kt)("p",null,"The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/google/go-containerregistry/blob/main/cmd/crane/rebase.md"},(0,n.kt)("inlineCode",{parentName:"a"},"crane rebase")),":"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},"Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an ",(0,n.kt)("inlineCode",{parentName:"p"},"apt install")," during image creation will overwrite the dpkg ",(0,n.kt)("inlineCode",{parentName:"p"},"status")," file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners."),(0,n.kt)("p",{parentName:"li"},"To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},"Rebasing also requires that the user knows ",(0,n.kt)("em",{parentName:"p"},"a priori")," what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets."),(0,n.kt)("p",{parentName:"li"},"While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose."),(0,n.kt)("p",{parentName:"li"},"The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well."))),(0,n.kt)("h2",{id:"architecture"},"Architecture"),(0,n.kt)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),(0,n.kt)("p",null,"The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"The data schema of various vulnerability scanners producing the input vulnerability report."),(0,n.kt)("li",{parentName:"ul"},"The state management of various package managers and process for applying patches appropriately through them.")),(0,n.kt)("p",null,"Effectively, ",(0,n.kt)("inlineCode",{parentName:"p"},"copa patch")," can be considered a command that bridges an extensible ",(0,n.kt)("inlineCode",{parentName:"p"},"Parse")," action with an extensible ",(0,n.kt)("inlineCode",{parentName:"p"},"Apply")," action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-go"},"type UpdatePackage struct {\n Name string\n Version string\n}\n\ntype UpdateManifest struct {\n OSType string\n OSVersion string\n Arch string\n Updates []UpdatePackage\n}\n\ntype ScanReportParser interface {\n Parse(reportPath string) (*UpdateManifest, error)\n}\n\ntype PackageManager interface {\n Apply(imagePath string, report *UpdateManifest) error\n}\n")),(0,n.kt)("h2",{id:"implementation"},"Implementation"),(0,n.kt)("img",{title:"buildkit graph execution",src:"/copacetic/website/img/graph-execution.png"}),(0,n.kt)("p",null,(0,n.kt)("inlineCode",{parentName:"p"},"copa")," is a pseudo-frontend to ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit"},"buildkit")," implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit/tree/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4#exploring-llb"},"LLB graphs")," for buildkit to execute:"),(0,n.kt)("ol",null,(0,n.kt)("li",{parentName:"ol"},"Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions."))),(0,n.kt)("li",{parentName:"ol"},"Actions to fetch and deploy tools that can be injected into the target image to perform the patching.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches."),(0,n.kt)("li",{parentName:"ul"},"In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image."),(0,n.kt)("li",{parentName:"ul"},"Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well."))),(0,n.kt)("li",{parentName:"ol"},"Actions to deploy the required patch packages to the target image.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"copa")," integrates with buildkit at the API level because it uses the ",(0,n.kt)("a",{parentName:"li",href:"https://github.com/moby/buildkit/blob/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4/docs/merge%2Bdiff.md"},"diff and merge")," graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.")))),(0,n.kt)("h2",{id:"tradeoffs"},"Tradeoffs"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa."),(0,n.kt)("li",{parentName:"ul"},"copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this."))),(0,n.kt)("li",{parentName:"ul"},"No support for windows containers given the dependency on buildkit.")))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/22dce6f4.96e84ed3.js b/website/assets/js/22dce6f4.96e84ed3.js new file mode 100644 index 00000000..70647f1b --- /dev/null +++ b/website/assets/js/22dce6f4.96e84ed3.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3227],{8013:(e,t,i)=>{i.r(t),i.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>h,frontMatter:()=>s,metadata:()=>r,toc:()=>l});var n=i(4848),a=i(8453);const s={title:"Design"},o=void 0,r={id:"design",title:"Design",description:"Design Tenets",source:"@site/versioned_docs/version-v0.5.x/design.md",sourceDirName:".",slug:"/design",permalink:"/copacetic/website/v0.5.x/design",draft:!1,unlisted:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Design"},sidebar:"sidebar",previous:{title:"Troubleshooting",permalink:"/copacetic/website/v0.5.x/troubleshooting"},next:{title:"FAQ",permalink:"/copacetic/website/v0.5.x/faq"}},c={},l=[{value:"Design Tenets",id:"design-tenets",level:2},{value:"Design Reasoning",id:"design-reasoning",level:2},{value:"Architecture",id:"architecture",level:2},{value:"Implementation",id:"implementation",level:2},{value:"Tradeoffs",id:"tradeoffs",level:2}];function d(e){const t={a:"a",code:"code",em:"em",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(t.h2,{id:"design-tenets",children:"Design Tenets"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update."})," This is a raison d\u2019etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to work with the existing ecosystem of container images."})," The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to allow parties other than the image authors to address container vulnerabilities."})," Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to do one thing well and be composable with other tools and processes."})," Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane."]}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"design-reasoning",children:"Design Reasoning"}),"\n",(0,n.jsxs)(t.p,{children:["The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental ",(0,n.jsx)(t.a,{href:"https://github.com/google/go-containerregistry/blob/main/cmd/crane/rebase.md",children:(0,n.jsx)(t.code,{children:"crane rebase"})}),":"]}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:["Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an ",(0,n.jsx)(t.code,{children:"apt install"})," during image creation will overwrite the dpkg ",(0,n.jsx)(t.code,{children:"status"})," file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners."]}),"\n",(0,n.jsx)(t.p,{children:"To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on."}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:["Rebasing also requires that the user knows ",(0,n.jsx)(t.em,{children:"a priori"})," what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets."]}),"\n",(0,n.jsx)(t.p,{children:"While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose."}),"\n",(0,n.jsx)(t.p,{children:"The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well."}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"architecture",children:"Architecture"}),"\n",(0,n.jsx)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),"\n",(0,n.jsx)(t.p,{children:"The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"The data schema of various vulnerability scanners producing the input vulnerability report."}),"\n",(0,n.jsx)(t.li,{children:"The state management of various package managers and process for applying patches appropriately through them."}),"\n"]}),"\n",(0,n.jsxs)(t.p,{children:["Effectively, ",(0,n.jsx)(t.code,{children:"copa patch"})," can be considered a command that bridges an extensible ",(0,n.jsx)(t.code,{children:"Parse"})," action with an extensible ",(0,n.jsx)(t.code,{children:"Apply"})," action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:"]}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-go",children:"type UpdatePackage struct {\n Name string\n Version string\n}\n\ntype UpdateManifest struct {\n OSType string\n OSVersion string\n Arch string\n Updates []UpdatePackage\n}\n\ntype ScanReportParser interface {\n Parse(reportPath string) (*UpdateManifest, error)\n}\n\ntype PackageManager interface {\n Apply(imagePath string, report *UpdateManifest) error\n}\n"})}),"\n",(0,n.jsx)(t.h2,{id:"implementation",children:"Implementation"}),"\n",(0,n.jsx)("img",{title:"buildkit graph execution",src:"/copacetic/website/img/graph-execution.png"}),"\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.code,{children:"copa"})," is a pseudo-frontend to ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit",children:"buildkit"})," implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit/tree/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4#exploring-llb",children:"LLB graphs"})," for buildkit to execute:"]}),"\n",(0,n.jsxs)(t.ol,{children:["\n",(0,n.jsxs)(t.li,{children:["Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions."}),"\n"]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["Actions to fetch and deploy tools that can be injected into the target image to perform the patching.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches."}),"\n",(0,n.jsx)(t.li,{children:"In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image."}),"\n",(0,n.jsx)(t.li,{children:"Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well."}),"\n"]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["Actions to deploy the required patch packages to the target image.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.code,{children:"copa"})," integrates with buildkit at the API level because it uses the ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit/blob/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4/docs/merge%2Bdiff.md",children:"diff and merge"})," graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches."]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"tradeoffs",children:"Tradeoffs"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa."}),"\n",(0,n.jsx)(t.li,{children:"copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this."}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.li,{children:"No support for windows containers given the dependency on buildkit."}),"\n"]})]})}function h(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,n.jsx)(t,{...e,children:(0,n.jsx)(d,{...e})}):d(e)}},8453:(e,t,i)=>{i.d(t,{R:()=>o,x:()=>r});var n=i(6540);const a={},s=n.createContext(a);function o(e){const t=n.useContext(s);return n.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function r(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),n.createElement(s.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/27cf52aa.a3987a1f.js b/website/assets/js/27cf52aa.a3987a1f.js new file mode 100644 index 00000000..01b8be55 --- /dev/null +++ b/website/assets/js/27cf52aa.a3987a1f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4938],{2986:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>s,contentTitle:()=>d,default:()=>u,frontMatter:()=>c,metadata:()=>r,toc:()=>l});var t=i(4848),o=i(8453);const c={title:"Custom buildkit addresses"},d=void 0,r={id:"custom-address",title:"Custom buildkit addresses",description:"You may need to specify a custom address using the --addr flag. Here are the supported formats:",source:"@site/docs/custom-address.md",sourceDirName:".",slug:"/custom-address",permalink:"/copacetic/website/next/custom-address",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{title:"Custom buildkit addresses"},sidebar:"sidebar",previous:{title:"Github Action",permalink:"/copacetic/website/next/github-action"},next:{title:"Output",permalink:"/copacetic/website/next/output"}},s={},l=[{value:"Buildkit Connection Examples",id:"buildkit-connection-examples",level:2},{value:"Option 1: Connect using defaults",id:"option-1-connect-using-defaults",level:3},{value:"Option 2: Connect to buildx",id:"option-2-connect-to-buildx",level:3},{value:"Option 3: Buildkit in a container",id:"option-3-buildkit-in-a-container",level:3},{value:"Option 4: Buildkit over TCP",id:"option-4-buildkit-over-tcp",level:3}];function a(e){const n={code:"code",em:"em",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",ul:"ul",...(0,o.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(n.p,{children:["You may need to specify a custom address using the ",(0,t.jsx)(n.code,{children:"--addr"})," flag. Here are the supported formats:"]}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"unix:///path/to/buildkit.sock"})," - Connect to buildkit over unix socket."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"tcp://$BUILDKIT_ADDR:$PORT"})," - Connect to buildkit over TCP. (not recommended for security reasons)"]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"docker://<docker connection spec>"})," - Connect to docker, currently only unix sockets are supported, e.g. ",(0,t.jsx)(n.code,{children:"docker://unix:///var/run/docker.sock"})," (or just ",(0,t.jsx)(n.code,{children:"docker://"}),")."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"docker-container://my-buildkit-container"})," - Connect to a buildkitd running in a docker container."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"buildx://my-builder"})," - Connect to a buildx builder (or ",(0,t.jsx)(n.code,{children:"buildx://"})," for the currently selected builder). ",(0,t.jsx)(n.em,{children:"Note: only container-backed buildx instances are currently supported"})]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"nerdctl-container://my-container-name"})," - Similar to ",(0,t.jsx)(n.code,{children:"docker-container"})," but uses ",(0,t.jsx)(n.code,{children:"nerdctl"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"podman-container://my-container-name"})," - Similar to ",(0,t.jsx)(n.code,{children:"docker-container"})," but uses ",(0,t.jsx)(n.code,{children:"podman"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"ssh://myhost"})," - Connect to a buildkit instance over SSH. Format of the host spec should mimic the SSH command."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"kubepod://mypod"})," - Connect to buildkit running in a Kubernetes pod. Can also specify kubectl context and pod namespace (",(0,t.jsx)(n.code,{children:"kubepod://mypod?context=foo&namespace=notdefault"}),")."]}),"\n"]}),"\n",(0,t.jsx)(n.h2,{id:"buildkit-connection-examples",children:"Buildkit Connection Examples"}),"\n",(0,t.jsx)(n.h3,{id:"option-1-connect-using-defaults",children:"Option 1: Connect using defaults"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched\n"})}),"\n",(0,t.jsx)(n.h3,{id:"option-2-connect-to-buildx",children:"Option 2: Connect to buildx"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"docker buildx create --name demo\ncopa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr buildx://demo\n"})}),"\n",(0,t.jsx)(n.h3,{id:"option-3-buildkit-in-a-container",children:"Option 3: Buildkit in a container"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'export BUILDKIT_VERSION=v0.12.4\ndocker run \\\n --detach \\\n --rm \\\n --privileged \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION"\n\ncopa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr docker-container://buildkitd\n'})}),"\n",(0,t.jsx)(n.h3,{id:"option-4-buildkit-over-tcp",children:"Option 4: Buildkit over TCP"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'export BUILDKIT_VERSION=v0.12.4\nexport BUILDKIT_PORT=8888\ndocker run \\\n --detach \\\n --rm \\\n --privileged \\\n -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION" \\\n --addr tcp://0.0.0.0:$BUILDKIT_PORT\n\ncopa patch \\\n -i docker.io/library/nginx:1.21.6 \\\n -r nginx.1.21.6.json \\\n -t 1.21.6-patched \\\n -a tcp://0.0.0.0:$BUILDKIT_PORT\n'})})]})}function u(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(a,{...e})}):a(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>d,x:()=>r});var t=i(6540);const o={},c=t.createContext(o);function d(e){const n=t.useContext(c);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function r(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:d(e.components),t.createElement(c.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/27cf52aa.cace1e2c.js b/website/assets/js/27cf52aa.cace1e2c.js deleted file mode 100644 index c5b24838..00000000 --- a/website/assets/js/27cf52aa.cace1e2c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[138],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>k});var i=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function a(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){r(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,i,r=function(e,t){if(null==e)return{};var n,i,r={},o=Object.keys(e);for(i=0;i<o.length;i++)n=o[i],t.indexOf(n)>=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i<o.length;i++)n=o[i],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var c=i.createContext({}),d=function(e){var t=i.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=d(e.components);return i.createElement(c.Provider,{value:t},e.children)},p="mdxType",s={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},m=i.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,c=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),p=d(n),m=r,k=p["".concat(c,".").concat(m)]||p[m]||s[m]||o;return n?i.createElement(k,a(a({ref:t},u),{},{components:n})):i.createElement(k,a({ref:t},u))}));function k(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,a=new Array(o);a[0]=m;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[p]="string"==typeof e?e:r,a[1]=l;for(var d=2;d<o;d++)a[d]=n[d];return i.createElement.apply(null,a)}return i.createElement.apply(null,n)}m.displayName="MDXCreateElement"},5579:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>a,default:()=>p,frontMatter:()=>o,metadata:()=>l,toc:()=>d});var i=n(7462),r=(n(7294),n(3905));const o={title:"Custom buildkit addresses"},a=void 0,l={unversionedId:"custom-address",id:"custom-address",title:"Custom buildkit addresses",description:"You may need to specify a custom address using the --addr flag. Here are the supported formats:",source:"@site/docs/custom-address.md",sourceDirName:".",slug:"/custom-address",permalink:"/copacetic/website/next/custom-address",draft:!1,tags:[],version:"current",frontMatter:{title:"Custom buildkit addresses"},sidebar:"sidebar",previous:{title:"Github Action",permalink:"/copacetic/website/next/github-action"},next:{title:"Output",permalink:"/copacetic/website/next/output"}},c={},d=[{value:"Buildkit Connection Examples",id:"buildkit-connection-examples",level:2},{value:"Option 1: Connect using defaults",id:"option-1-connect-using-defaults",level:3},{value:"Option 2: Connect to buildx",id:"option-2-connect-to-buildx",level:3},{value:"Option 3: Buildkit in a container",id:"option-3-buildkit-in-a-container",level:3},{value:"Option 4: Buildkit over TCP",id:"option-4-buildkit-over-tcp",level:3}],u={toc:d};function p(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,i.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"You may need to specify a custom address using the ",(0,r.kt)("inlineCode",{parentName:"p"},"--addr")," flag. Here are the supported formats:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"unix:///path/to/buildkit.sock")," - Connect to buildkit over unix socket."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"tcp://$BUILDKIT_ADDR:$PORT")," - Connect to buildkit over TCP. (not recommended for security reasons)"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"docker://<docker connection spec>")," - Connect to docker, currently only unix sockets are supported, e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"docker://unix:///var/run/docker.sock")," (or just ",(0,r.kt)("inlineCode",{parentName:"li"},"docker://"),")."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"docker-container://my-buildkit-container")," - Connect to a buildkitd running in a docker container."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"buildx://my-builder")," - Connect to a buildx builder (or ",(0,r.kt)("inlineCode",{parentName:"li"},"buildx://")," for the currently selected builder). ",(0,r.kt)("em",{parentName:"li"},"Note: only container-backed buildx instances are currently supported")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"nerdctl-container://my-container-name")," - Similar to ",(0,r.kt)("inlineCode",{parentName:"li"},"docker-container")," but uses ",(0,r.kt)("inlineCode",{parentName:"li"},"nerdctl"),"."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"podman-container://my-container-name")," - Similar to ",(0,r.kt)("inlineCode",{parentName:"li"},"docker-container")," but uses ",(0,r.kt)("inlineCode",{parentName:"li"},"podman"),"."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"ssh://myhost")," - Connect to a buildkit instance over SSH. Format of the host spec should mimic the SSH command."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"kubepod://mypod")," - Connect to buildkit running in a Kubernetes pod. Can also specify kubectl context and pod namespace (",(0,r.kt)("inlineCode",{parentName:"li"},"kubepod://mypod?context=foo&namespace=notdefault"),").")),(0,r.kt)("h2",{id:"buildkit-connection-examples"},"Buildkit Connection Examples"),(0,r.kt)("h3",{id:"option-1-connect-using-defaults"},"Option 1: Connect using defaults"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched\n")),(0,r.kt)("h3",{id:"option-2-connect-to-buildx"},"Option 2: Connect to buildx"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"docker buildx create --name demo\ncopa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr buildx://demo\n")),(0,r.kt)("h3",{id:"option-3-buildkit-in-a-container"},"Option 3: Buildkit in a container"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'export BUILDKIT_VERSION=v0.12.4\ndocker run \\\n --detach \\\n --rm \\\n --privileged \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION"\n\ncopa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr docker-container://buildkitd\n')),(0,r.kt)("h3",{id:"option-4-buildkit-over-tcp"},"Option 4: Buildkit over TCP"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'export BUILDKIT_VERSION=v0.12.4\nexport BUILDKIT_PORT=8888\ndocker run \\\n --detach \\\n --rm \\\n --privileged \\\n -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION" \\\n --addr tcp://0.0.0.0:$BUILDKIT_PORT\n\ncopa patch \\\n -i docker.io/library/nginx:1.21.6 \\\n -r nginx.1.21.6.json \\\n -t 1.21.6-patched \\\n -a tcp://0.0.0.0:$BUILDKIT_PORT\n')))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/282f11b9.66f006eb.js b/website/assets/js/282f11b9.66f006eb.js new file mode 100644 index 00000000..422b1b87 --- /dev/null +++ b/website/assets/js/282f11b9.66f006eb.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2180],{7928:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>o,contentTitle:()=>r,default:()=>d,frontMatter:()=>s,metadata:()=>l,toc:()=>c});var t=i(4848),a=i(8453);const s={},r="Maintainer Guidelines",l={id:"maintainer-guidelines",title:"Maintainer Guidelines",description:"Semantic Release Management",source:"@site/docs/maintainer-guidelines.md",sourceDirName:".",slug:"/maintainer-guidelines",permalink:"/copacetic/website/next/maintainer-guidelines",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{},sidebar:"sidebar",previous:{title:"Development and Testing Tips",permalink:"/copacetic/website/next/development-tips"},next:{title:"Release Process",permalink:"/copacetic/website/next/release"}},o={},c=[{value:"Semantic Release Management",id:"semantic-release-management",level:2},{value:"Breaking change",id:"breaking-change",level:3},{value:"Deprecation",id:"deprecation",level:3},{value:"Publishing a Release",id:"publishing-a-release",level:2},{value:"Publish a new major/minor version release",id:"publish-a-new-majorminor-version-release",level:3},{value:"Publish a patch revision release",id:"publish-a-patch-revision-release",level:3}];function h(e){const n={a:"a",blockquote:"blockquote",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h1,{id:"maintainer-guidelines",children:"Maintainer Guidelines"}),"\n",(0,t.jsx)(n.h2,{id:"semantic-release-management",children:"Semantic Release Management"}),"\n",(0,t.jsxs)(n.p,{children:["This project uses ",(0,t.jsx)(n.a,{href:"https://github.com/go-semantic-release/semantic-release",children:"go-semantic-release"})," to automatically generate the appropriate ",(0,t.jsx)(n.a,{href:"https://semver.org/",children:"semantic version"})," and changelog for a release based on ",(0,t.jsx)(n.a,{href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format",children:"Angular commit message format"}),". Of note to maintainers is the need to enforce an empty line-separate format:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-xml",children:"<HEADER>\n<-- blank line --\x3e\n<BODY>\n<-- blank line --\x3e\n<FOOTER>\n"})}),"\n",(0,t.jsxs)(n.p,{children:["For contributor PRs, instead of trying to ensure adherence in every commit message, it's easiest to adopt a ",(0,t.jsx)(n.a,{href:"https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/configuring-commit-squashing-for-pull-requests",children:"squash and merge"})," strategy so that the PR description is used as the final commit description with the appropriate semantic release format."]}),"\n",(0,t.jsxs)(n.p,{children:["In addition to the semantic release types called out in the ",(0,t.jsx)(n.a,{href:"../CONTRIBUTING.md#pull-requests",children:"contributor pull request guidelines"}),", there are several other categories supported by the ",(0,t.jsx)(n.a,{href:"https://github.com/go-semantic-release/changelog-generator-default",children:"default changelog generator"})," that maintainers should be aware of:"]}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"chore:"})," Reserved for automated maintenance changes, such as minor version go dependency updates initiated by Dependabot."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"revert:"})," Maintainers should use this to mark commits that revert a previous commit, followed by the header of the reverted commit. The message body should include the SHA of the reverted commit, as well as a clear description of the reason for the revert."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"style:"})," This is unused for this project."]}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["There are also two special categories to be added to the ",(0,t.jsx)(n.a,{href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit-message-footer",children:"message footer"})," that maintainers need to pay special attention to when merging changes:"]}),"\n",(0,t.jsx)(n.h3,{id:"breaking-change",children:"Breaking change"}),"\n",(0,t.jsx)(n.p,{children:"Breaking changes should be described in the footer as follows:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-text",children:"BREAKING CHANGE: <breaking change summary>\n<-- blank line --\x3e\n<breaking change description & migration instructions>\n<-- blank line --\x3e\n<-- blank line --\x3e\nCloses #<issue number>\n"})}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:["Note that this project currently uses the ",(0,t.jsx)(n.code,{children:"allow-initial-development-versions"})," flag for go-semantic-release, so ",(0,t.jsx)(n.strong,{children:"breaking changes will still be handled as minor releases"})," until the workflow is updated for the v1.0.0 release."]}),"\n"]}),"\n",(0,t.jsx)(n.h3,{id:"deprecation",children:"Deprecation"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-text",children:"DEPRECATED: <summary of deprecated feature>\n<-- blank line --\x3e\n<deprecated feature description & migration/workaround instructions>\n<-- blank line --\x3e\n<-- blank line --\x3e\nCloses #<issue number>\n"})}),"\n",(0,t.jsx)(n.h2,{id:"publishing-a-release",children:"Publishing a Release"}),"\n",(0,t.jsxs)(n.p,{children:["To avoid inconsistencies in tagging and release branching, this project uses the ",(0,t.jsx)(n.a,{href:"https://github.com/project-copacetic/copacetic/actions/workflows/release.yml",children:"Publish release"})," GitHub Actions workflow to automate the creation of releases."]}),"\n",(0,t.jsx)(n.h3,{id:"publish-a-new-majorminor-version-release",children:"Publish a new major/minor version release"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["Review the ",(0,t.jsx)(n.code,{children:"main"})," branch to ensure that it has all the desired changes for the new release branch and that there are no PR merge workflows in flight."]}),"\n",(0,t.jsxs)(n.li,{children:["Click ",(0,t.jsx)(n.em,{children:"Run workflow"})," on the ",(0,t.jsx)(n.a,{href:"https://github.com/project-copacetic/copacetic/actions/workflows/release.yml",children:"Publish release"})," against the ",(0,t.jsx)(n.code,{children:"main"})," branch. This will:","\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["Create a new tag with the incremented semantic version (e.g. ",(0,t.jsx)(n.code,{children:"v0.9.0"}),") against the latest commit in ",(0,t.jsx)(n.code,{children:"main"}),"."]}),"\n",(0,t.jsx)(n.li,{children:"Create a new GitHub release against that tag with an automatically generated changelog."}),"\n",(0,t.jsx)(n.li,{children:"Build and upload the new release version of Copa to the GitHub release."}),"\n",(0,t.jsxs)(n.li,{children:["Create a new release branch if it does not already exist (e.g. ",(0,t.jsx)(n.code,{children:"release-0.9"}),")"]}),"\n"]}),"\n"]}),"\n",(0,t.jsx)(n.li,{children:"Verify that the workflow ran successfully and review the expected outputs listed above."}),"\n"]}),"\n",(0,t.jsx)(n.h3,{id:"publish-a-patch-revision-release",children:"Publish a patch revision release"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["Review the appropriate release branch that the revision patches (e.g. ",(0,t.jsx)(n.code,{children:"release-0.9"})," for an anticipated new ",(0,t.jsx)(n.code,{children:"v0.9.x"})," tag) to ensure that it has all the desired changes for the release and that there are no PR merge workflows in flight.","\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["If there are fixes in ",(0,t.jsx)(n.code,{children:"main"})," intended for the patch release in the latest release branch, they need to be manually ported to the release branch first and the revision released from there."]}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["Click ",(0,t.jsx)(n.em,{children:"Run workflow"})," on the ",(0,t.jsx)(n.a,{href:"https://github.com/project-copacetic/copacetic/actions/workflows/release.yml",children:"Publish release"})," against the target ",(0,t.jsx)(n.code,{children:"release-x.y"})," branch. This will:","\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["Create a new tag with the incremented semantic version (e.g. ",(0,t.jsx)(n.code,{children:"v0.9.4"}),") against the latest commit in the release branch."]}),"\n",(0,t.jsx)(n.li,{children:"Create a new GitHub release against that tag with an automatically generated changelog."}),"\n",(0,t.jsx)(n.li,{children:"Build and upload the new release version of Copa to the GitHub release."}),"\n"]}),"\n"]}),"\n",(0,t.jsx)(n.li,{children:"Verify that the workflow ran successfully and review the expected outputs listed above."}),"\n"]})]})}function d(e={}){const{wrapper:n}={...(0,a.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(h,{...e})}):h(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>r,x:()=>l});var t=i(6540);const a={},s=t.createContext(a);function r(e){const n=t.useContext(s);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:r(e.components),t.createElement(s.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/282f11b9.a334c6b0.js b/website/assets/js/282f11b9.a334c6b0.js deleted file mode 100644 index 6e36acfd..00000000 --- a/website/assets/js/282f11b9.a334c6b0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2072],{3905:(e,t,a)=>{a.d(t,{Zo:()=>p,kt:()=>d});var n=a(7294);function r(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function i(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function l(e){for(var t=1;t<arguments.length;t++){var a=null!=arguments[t]?arguments[t]:{};t%2?i(Object(a),!0).forEach((function(t){r(e,t,a[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):i(Object(a)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))}))}return e}function o(e,t){if(null==e)return{};var a,n,r=function(e,t){if(null==e)return{};var a,n,r={},i=Object.keys(e);for(n=0;n<i.length;n++)a=i[n],t.indexOf(a)>=0||(r[a]=e[a]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n<i.length;n++)a=i[n],t.indexOf(a)>=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(r[a]=e[a])}return r}var s=n.createContext({}),c=function(e){var t=n.useContext(s),a=t;return e&&(a="function"==typeof e?e(t):l(l({},t),e)),a},p=function(e){var t=c(e.components);return n.createElement(s.Provider,{value:t},e.children)},m="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},h=n.forwardRef((function(e,t){var a=e.components,r=e.mdxType,i=e.originalType,s=e.parentName,p=o(e,["components","mdxType","originalType","parentName"]),m=c(a),h=r,d=m["".concat(s,".").concat(h)]||m[h]||u[h]||i;return a?n.createElement(d,l(l({ref:t},p),{},{components:a})):n.createElement(d,l({ref:t},p))}));function d(e,t){var a=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=a.length,l=new Array(i);l[0]=h;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o[m]="string"==typeof e?e:r,l[1]=o;for(var c=2;c<i;c++)l[c]=a[c];return n.createElement.apply(null,l)}return n.createElement.apply(null,a)}h.displayName="MDXCreateElement"},2352:(e,t,a)=>{a.r(t),a.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>m,frontMatter:()=>i,metadata:()=>o,toc:()=>c});var n=a(7462),r=(a(7294),a(3905));const i={},l="Maintainer Guidelines",o={unversionedId:"maintainer-guidelines",id:"maintainer-guidelines",title:"Maintainer Guidelines",description:"Semantic Release Management",source:"@site/docs/maintainer-guidelines.md",sourceDirName:".",slug:"/maintainer-guidelines",permalink:"/copacetic/website/next/maintainer-guidelines",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"sidebar",previous:{title:"Development and Testing Tips",permalink:"/copacetic/website/next/development-tips"},next:{title:"Release Process",permalink:"/copacetic/website/next/release"}},s={},c=[{value:"Semantic Release Management",id:"semantic-release-management",level:2},{value:"Breaking change",id:"breaking-change",level:3},{value:"Deprecation",id:"deprecation",level:3},{value:"Publishing a Release",id:"publishing-a-release",level:2},{value:"Publish a new major/minor version release",id:"publish-a-new-majorminor-version-release",level:3},{value:"Publish a patch revision release",id:"publish-a-patch-revision-release",level:3}],p={toc:c};function m(e){let{components:t,...a}=e;return(0,r.kt)("wrapper",(0,n.Z)({},p,a,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"maintainer-guidelines"},"Maintainer Guidelines"),(0,r.kt)("h2",{id:"semantic-release-management"},"Semantic Release Management"),(0,r.kt)("p",null,"This project uses ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/go-semantic-release/semantic-release"},"go-semantic-release")," to automatically generate the appropriate ",(0,r.kt)("a",{parentName:"p",href:"https://semver.org/"},"semantic version")," and changelog for a release based on ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format"},"Angular commit message format"),". Of note to maintainers is the need to enforce an empty line-separate format:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-xml"},"<HEADER>\n<-- blank line --\x3e\n<BODY>\n<-- blank line --\x3e\n<FOOTER>\n")),(0,r.kt)("p",null,"For contributor PRs, instead of trying to ensure adherence in every commit message, it's easiest to adopt a ",(0,r.kt)("a",{parentName:"p",href:"https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/configuring-commit-squashing-for-pull-requests"},"squash and merge")," strategy so that the PR description is used as the final commit description with the appropriate semantic release format."),(0,r.kt)("p",null,"In addition to the semantic release types called out in the ",(0,r.kt)("a",{parentName:"p",href:"../CONTRIBUTING.md#pull-requests"},"contributor pull request guidelines"),", there are several other categories supported by the ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/go-semantic-release/changelog-generator-default"},"default changelog generator")," that maintainers should be aware of:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"chore:")," Reserved for automated maintenance changes, such as minor version go dependency updates initiated by Dependabot."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"revert:")," Maintainers should use this to mark commits that revert a previous commit, followed by the header of the reverted commit. The message body should include the SHA of the reverted commit, as well as a clear description of the reason for the revert."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"style:")," This is unused for this project.")),(0,r.kt)("p",null,"There are also two special categories to be added to the ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit-message-footer"},"message footer")," that maintainers need to pay special attention to when merging changes:"),(0,r.kt)("h3",{id:"breaking-change"},"Breaking change"),(0,r.kt)("p",null,"Breaking changes should be described in the footer as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-text"},"BREAKING CHANGE: <breaking change summary>\n<-- blank line --\x3e\n<breaking change description & migration instructions>\n<-- blank line --\x3e\n<-- blank line --\x3e\nCloses #<issue number>\n")),(0,r.kt)("blockquote",null,(0,r.kt)("p",{parentName:"blockquote"},"Note that this project currently uses the ",(0,r.kt)("inlineCode",{parentName:"p"},"allow-initial-development-versions")," flag for go-semantic-release, so ",(0,r.kt)("strong",{parentName:"p"},"breaking changes will still be handled as minor releases")," until the workflow is updated for the v1.0.0 release.")),(0,r.kt)("h3",{id:"deprecation"},"Deprecation"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-text"},"DEPRECATED: <summary of deprecated feature>\n<-- blank line --\x3e\n<deprecated feature description & migration/workaround instructions>\n<-- blank line --\x3e\n<-- blank line --\x3e\nCloses #<issue number>\n")),(0,r.kt)("h2",{id:"publishing-a-release"},"Publishing a Release"),(0,r.kt)("p",null,"To avoid inconsistencies in tagging and release branching, this project uses the ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/actions/workflows/release.yml"},"Publish release")," GitHub Actions workflow to automate the creation of releases."),(0,r.kt)("h3",{id:"publish-a-new-majorminor-version-release"},"Publish a new major/minor version release"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"Review the ",(0,r.kt)("inlineCode",{parentName:"li"},"main")," branch to ensure that it has all the desired changes for the new release branch and that there are no PR merge workflows in flight."),(0,r.kt)("li",{parentName:"ol"},"Click ",(0,r.kt)("em",{parentName:"li"},"Run workflow")," on the ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/project-copacetic/copacetic/actions/workflows/release.yml"},"Publish release")," against the ",(0,r.kt)("inlineCode",{parentName:"li"},"main")," branch. This will:",(0,r.kt)("ol",{parentName:"li"},(0,r.kt)("li",{parentName:"ol"},"Create a new tag with the incremented semantic version (e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"v0.9.0"),") against the latest commit in ",(0,r.kt)("inlineCode",{parentName:"li"},"main"),"."),(0,r.kt)("li",{parentName:"ol"},"Create a new GitHub release against that tag with an automatically generated changelog."),(0,r.kt)("li",{parentName:"ol"},"Build and upload the new release version of Copa to the GitHub release."),(0,r.kt)("li",{parentName:"ol"},"Create a new release branch if it does not already exist (e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"release-0.9"),")"))),(0,r.kt)("li",{parentName:"ol"},"Verify that the workflow ran successfully and review the expected outputs listed above.")),(0,r.kt)("h3",{id:"publish-a-patch-revision-release"},"Publish a patch revision release"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"Review the appropriate release branch that the revision patches (e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"release-0.9")," for an anticipated new ",(0,r.kt)("inlineCode",{parentName:"li"},"v0.9.x")," tag) to ensure that it has all the desired changes for the release and that there are no PR merge workflows in flight.",(0,r.kt)("ol",{parentName:"li"},(0,r.kt)("li",{parentName:"ol"},"If there are fixes in ",(0,r.kt)("inlineCode",{parentName:"li"},"main")," intended for the patch release in the latest release branch, they need to be manually ported to the release branch first and the revision released from there."))),(0,r.kt)("li",{parentName:"ol"},"Click ",(0,r.kt)("em",{parentName:"li"},"Run workflow")," on the ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/project-copacetic/copacetic/actions/workflows/release.yml"},"Publish release")," against the target ",(0,r.kt)("inlineCode",{parentName:"li"},"release-x.y")," branch. This will:",(0,r.kt)("ol",{parentName:"li"},(0,r.kt)("li",{parentName:"ol"},"Create a new tag with the incremented semantic version (e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"v0.9.4"),") against the latest commit in the release branch."),(0,r.kt)("li",{parentName:"ol"},"Create a new GitHub release against that tag with an automatically generated changelog."),(0,r.kt)("li",{parentName:"ol"},"Build and upload the new release version of Copa to the GitHub release."))),(0,r.kt)("li",{parentName:"ol"},"Verify that the workflow ran successfully and review the expected outputs listed above.")))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/299b5310.1a9430b9.js b/website/assets/js/299b5310.1a9430b9.js new file mode 100644 index 00000000..7fd987e1 --- /dev/null +++ b/website/assets/js/299b5310.1a9430b9.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1842],{9043:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"v0.2.x","label":"v0.2.x","banner":"unmaintained","badge":true,"noIndex":false,"className":"docs-version-v0.2.x","isLast":false,"docsSidebars":{"sidebar":[{"type":"link","label":"Introduction","href":"/copacetic/website/v0.2.x/","docId":"introduction","unlisted":false},{"type":"link","label":"Installation","href":"/copacetic/website/v0.2.x/installation","docId":"installation","unlisted":false},{"type":"link","label":"Quick Start","href":"/copacetic/website/v0.2.x/quick-start","docId":"quick-start","unlisted":false},{"type":"link","label":"Design","href":"/copacetic/website/v0.2.x/design","docId":"design","unlisted":false},{"type":"link","label":"FAQ","href":"/copacetic/website/v0.2.x/faq","docId":"faq","unlisted":false},{"type":"link","label":"Contributing","href":"/copacetic/website/v0.2.x/contributing","docId":"contributing","unlisted":false},{"type":"link","label":"Code of Conduct","href":"/copacetic/website/v0.2.x/code-of-conduct","docId":"code-of-conduct","unlisted":false}]},"docs":{"code-of-conduct":{"id":"code-of-conduct","title":"Code of Conduct","description":"Our Pledge","sidebar":"sidebar"},"contributing":{"id":"contributing","title":"Contributing","description":"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.","sidebar":"sidebar"},"design":{"id":"design","title":"Design","description":"Design Tenets","sidebar":"sidebar"},"faq":{"id":"faq","title":"FAQ","description":"What kind of vulnerabilities can Copa patch?","sidebar":"sidebar"},"installation":{"id":"installation","title":"Installation","description":"Homebrew","sidebar":"sidebar"},"introduction":{"id":"introduction","title":"Introduction","description":"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.","sidebar":"sidebar"},"quick-start":{"id":"quick-start","title":"Quick Start","description":"This sample illustrates how to patch containers using vulnerability reports with copa.","sidebar":"sidebar"}}}')}}]); \ No newline at end of file diff --git a/website/assets/js/299b5310.a01d158f.js b/website/assets/js/299b5310.a01d158f.js deleted file mode 100644 index 82e9c218..00000000 --- a/website/assets/js/299b5310.a01d158f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5145],{5369:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"v0.2.x","label":"v0.2.x","banner":"unmaintained","badge":true,"noIndex":false,"className":"docs-version-v0.2.x","isLast":false,"docsSidebars":{"sidebar":[{"type":"link","label":"Introduction","href":"/copacetic/website/v0.2.x/","docId":"introduction"},{"type":"link","label":"Installation","href":"/copacetic/website/v0.2.x/installation","docId":"installation"},{"type":"link","label":"Quick Start","href":"/copacetic/website/v0.2.x/quick-start","docId":"quick-start"},{"type":"link","label":"Design","href":"/copacetic/website/v0.2.x/design","docId":"design"},{"type":"link","label":"FAQ","href":"/copacetic/website/v0.2.x/faq","docId":"faq"},{"type":"link","label":"Contributing","href":"/copacetic/website/v0.2.x/contributing","docId":"contributing"},{"type":"link","label":"Code of Conduct","href":"/copacetic/website/v0.2.x/code-of-conduct","docId":"code-of-conduct"}]},"docs":{"code-of-conduct":{"id":"code-of-conduct","title":"Code of Conduct","description":"Our Pledge","sidebar":"sidebar"},"contributing":{"id":"contributing","title":"Contributing","description":"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.","sidebar":"sidebar"},"design":{"id":"design","title":"Design","description":"Design Tenets","sidebar":"sidebar"},"faq":{"id":"faq","title":"FAQ","description":"What kind of vulnerabilities can Copa patch?","sidebar":"sidebar"},"installation":{"id":"installation","title":"Installation","description":"Homebrew","sidebar":"sidebar"},"introduction":{"id":"introduction","title":"Introduction","description":"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.","sidebar":"sidebar"},"quick-start":{"id":"quick-start","title":"Quick Start","description":"This sample illustrates how to patch containers using vulnerability reports with copa.","sidebar":"sidebar"}}}')}}]); \ No newline at end of file diff --git a/website/assets/js/2a636ef6.29da1826.js b/website/assets/js/2a636ef6.29da1826.js new file mode 100644 index 00000000..b4cd224c --- /dev/null +++ b/website/assets/js/2a636ef6.29da1826.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1866],{5865:(e,a,t)=>{t.r(a),t.d(a,{assets:()=>l,contentTitle:()=>c,default:()=>h,frontMatter:()=>o,metadata:()=>s,toc:()=>r});var i=t(4848),n=t(8453);const o={title:"FAQ"},c=void 0,s={id:"faq",title:"FAQ",description:"What kind of vulnerabilities can Copa patch?",source:"@site/versioned_docs/version-v0.3.x/faq.md",sourceDirName:".",slug:"/faq",permalink:"/copacetic/website/v0.3.x/faq",draft:!1,unlisted:!1,tags:[],version:"v0.3.x",frontMatter:{title:"FAQ"},sidebar:"sidebar",previous:{title:"Design",permalink:"/copacetic/website/v0.3.x/design"},next:{title:"Contributing",permalink:"/copacetic/website/v0.3.x/contributing"}},l={},r=[{value:"What kind of vulnerabilities can Copa patch?",id:"what-kind-of-vulnerabilities-can-copa-patch",level:2},{value:"What kind of vulnerabilities can Copa not patch?",id:"what-kind-of-vulnerabilities-can-copa-not-patch",level:2}];function p(e){const a={a:"a",code:"code",h2:"h2",p:"p",...(0,n.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(a.h2,{id:"what-kind-of-vulnerabilities-can-copa-patch",children:"What kind of vulnerabilities can Copa patch?"}),"\n",(0,i.jsxs)(a.p,{children:['Copa is capable of patching "OS level" vulnerabilities. This includes packages (like ',(0,i.jsx)(a.code,{children:"openssl"}),") in the image that are managed by a package manager such as ",(0,i.jsx)(a.code,{children:"apt"})," or ",(0,i.jsx)(a.code,{children:"yum"}),'. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see ',(0,i.jsx)(a.a,{href:"#what-kind-of-vulnerabilities-can-copa-not-patch",children:"below"})," for more details)."]}),"\n",(0,i.jsx)(a.h2,{id:"what-kind-of-vulnerabilities-can-copa-not-patch",children:"What kind of vulnerabilities can Copa not patch?"}),"\n",(0,i.jsxs)(a.p,{children:['Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the ',(0,i.jsx)(a.code,{children:"golang.org/x/net"})," module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level."]}),"\n",(0,i.jsxs)(a.p,{children:["To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like ",(0,i.jsx)(a.code,{children:"http://archive.ubuntu.com/ubuntu/"})," for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications."]})]})}function h(e={}){const{wrapper:a}={...(0,n.R)(),...e.components};return a?(0,i.jsx)(a,{...e,children:(0,i.jsx)(p,{...e})}):p(e)}},8453:(e,a,t)=>{t.d(a,{R:()=>c,x:()=>s});var i=t(6540);const n={},o=i.createContext(n);function c(e){const a=i.useContext(o);return i.useMemo((function(){return"function"==typeof e?e(a):{...a,...e}}),[a,e])}function s(e){let a;return a=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:c(e.components),i.createElement(o.Provider,{value:a},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/2a636ef6.df8e9b9f.js b/website/assets/js/2a636ef6.df8e9b9f.js deleted file mode 100644 index f94c2064..00000000 --- a/website/assets/js/2a636ef6.df8e9b9f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2702],{3905:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>d});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function c(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=a.createContext({}),p=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},s=function(e){var t=p(e.components);return a.createElement(l.Provider,{value:t},e.children)},u="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},h=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,l=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),u=p(n),h=i,d=u["".concat(l,".").concat(h)]||u[h]||f[h]||r;return n?a.createElement(d,o(o({ref:t},s),{},{components:n})):a.createElement(d,o({ref:t},s))}));function d(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=h;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[u]="string"==typeof e?e:i,o[1]=c;for(var p=2;p<r;p++)o[p]=n[p];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}h.displayName="MDXCreateElement"},4820:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>c,toc:()=>p});var a=n(7462),i=(n(7294),n(3905));const r={title:"FAQ"},o=void 0,c={unversionedId:"faq",id:"version-v0.3.x/faq",title:"FAQ",description:"What kind of vulnerabilities can Copa patch?",source:"@site/versioned_docs/version-v0.3.x/faq.md",sourceDirName:".",slug:"/faq",permalink:"/copacetic/website/v0.3.x/faq",draft:!1,tags:[],version:"v0.3.x",frontMatter:{title:"FAQ"},sidebar:"sidebar",previous:{title:"Design",permalink:"/copacetic/website/v0.3.x/design"},next:{title:"Contributing",permalink:"/copacetic/website/v0.3.x/contributing"}},l={},p=[{value:"What kind of vulnerabilities can Copa patch?",id:"what-kind-of-vulnerabilities-can-copa-patch",level:2},{value:"What kind of vulnerabilities can Copa not patch?",id:"what-kind-of-vulnerabilities-can-copa-not-patch",level:2}],s={toc:p};function u(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h2",{id:"what-kind-of-vulnerabilities-can-copa-patch"},"What kind of vulnerabilities can Copa patch?"),(0,i.kt)("p",null,'Copa is capable of patching "OS level" vulnerabilities. This includes packages (like ',(0,i.kt)("inlineCode",{parentName:"p"},"openssl"),") in the image that are managed by a package manager such as ",(0,i.kt)("inlineCode",{parentName:"p"},"apt")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"yum"),'. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see ',(0,i.kt)("a",{parentName:"p",href:"#what-kind-of-vulnerabilities-can-copa-not-patch"},"below")," for more details)."),(0,i.kt)("h2",{id:"what-kind-of-vulnerabilities-can-copa-not-patch"},"What kind of vulnerabilities can Copa not patch?"),(0,i.kt)("p",null,'Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the ',(0,i.kt)("inlineCode",{parentName:"p"},"golang.org/x/net")," module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level."),(0,i.kt)("p",null,"To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like ",(0,i.kt)("inlineCode",{parentName:"p"},"http://archive.ubuntu.com/ubuntu/")," for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/34f2f592.0c35ff01.js b/website/assets/js/34f2f592.0c35ff01.js deleted file mode 100644 index 618c634b..00000000 --- a/website/assets/js/34f2f592.0c35ff01.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9269],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>k});var i=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,i,a=function(e,t){if(null==e)return{};var n,i,a={},r=Object.keys(e);for(i=0;i<r.length;i++)n=r[i],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i<r.length;i++)n=r[i],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=i.createContext({}),c=function(e){var t=i.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return i.createElement(s.Provider,{value:t},e.children)},d="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},m=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),d=c(n),m=a,k=d["".concat(s,".").concat(m)]||d[m]||u[m]||r;return n?i.createElement(k,o(o({ref:t},p),{},{components:n})):i.createElement(k,o({ref:t},p))}));function k(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,o=new Array(r);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[d]="string"==typeof e?e:a,o[1]=l;for(var c=2;c<r;c++)o[c]=n[c];return i.createElement.apply(null,o)}return i.createElement.apply(null,n)}m.displayName="MDXCreateElement"},5374:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>d,frontMatter:()=>r,metadata:()=>l,toc:()=>c});var i=n(7462),a=(n(7294),n(3905));const r={title:"Quick Start"},o=void 0,l={unversionedId:"quick-start",id:"version-v0.5.x/quick-start",title:"Quick Start",description:"This sample illustrates how to patch containers using vulnerability reports with copa.",source:"@site/versioned_docs/version-v0.5.x/quick-start.md",sourceDirName:".",slug:"/quick-start",permalink:"/copacetic/website/v0.5.x/quick-start",draft:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Quick Start"},sidebar:"sidebar",previous:{title:"Installation",permalink:"/copacetic/website/v0.5.x/installation"},next:{title:"Output",permalink:"/copacetic/website/v0.5.x/output"}},s={},c=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Sample Steps",id:"sample-steps",level:2}],p={toc:c};function d(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,i.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"This sample illustrates how to patch containers using vulnerability reports with ",(0,a.kt)("inlineCode",{parentName:"p"},"copa"),"."),(0,a.kt)("h2",{id:"prerequisites"},"Prerequisites"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"An Ubuntu 22.04 VM configured through the ",(0,a.kt)("a",{parentName:"li",href:"/copacetic/website/v0.5.x/installation"},"setup instructions"),". This includes:",(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"copa")," tool ",(0,a.kt)("a",{parentName:"li",href:"/copacetic/website/v0.5.x/installation"},"built & pathed"),"."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/moby/buildkit/#quick-start"},"buildkit")," daemon installed & pathed. ",(0,a.kt)("a",{parentName:"li",href:"#buildkit-connection-examples"},"Examples")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://docs.docker.com/desktop/linux/install/#generic-installation-steps"},"docker")," daemon running and CLI installed & pathed."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://aquasecurity.github.io/trivy/latest/getting-started/installation/"},"trivy CLI")," installed & pathed.")))),(0,a.kt)("h2",{id:"sample-steps"},"Sample Steps"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6\n")),(0,a.kt)("p",{parentName:"li"},"You can also see the existing patchable vulnerabilities in table form on the shell with:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6\n\n"))),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"To patch the image, use the Trivy report and specify a buildkit instance to connect to:"),(0,a.kt)("p",{parentName:"li"},"By default copa will attempt to auto-connect to an instance in order:"),(0,a.kt)("ol",{parentName:"li"},(0,a.kt)("li",{parentName:"ol"},"Default docker buildkit endpoint (requires at least docker v24.0 with ",(0,a.kt)("a",{parentName:"li",href:"https://docs.docker.com/storage/containerd/#enable-containerd-image-store-on-docker-engine"},"containerd snapshotter")," support enabled)"),(0,a.kt)("li",{parentName:"ol"},"Currently selected buildx builder (see: ",(0,a.kt)("inlineCode",{parentName:"li"},"docker buildx --help"),")"),(0,a.kt)("li",{parentName:"ol"},"buildkit daemon at the default address ",(0,a.kt)("inlineCode",{parentName:"li"},"/run/buildkit/buildkitd.sock"))),(0,a.kt)("p",{parentName:"li"},"If an instance doesn't exist or that instance doesn't support all the features copa needs the next will be attempted.\nYou may need to specify a custom address using the ",(0,a.kt)("inlineCode",{parentName:"p"},"--addr")," flag. Here are the supported formats:"),(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"unix:///path/to/buildkit.sock")," - Connect to buildkit over unix socket."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"tcp://$BUILDKIT_ADDR:$PORT")," - Connect to buildkit over TCP. (not recommended for security reasons)"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"docker://<docker connection spec>")," - Connect to docker, currently only unix sockets are supported, e.g. ",(0,a.kt)("inlineCode",{parentName:"li"},"docker://unix:///var/run/docker.sock")," (or just ",(0,a.kt)("inlineCode",{parentName:"li"},"docker://"),")."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"docker-container://my-buildkit-container")," - Connect to a buildkitd running in a docker container."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"buildx://my-builder")," - Connect to a buildx builder (or ",(0,a.kt)("inlineCode",{parentName:"li"},"buildx://")," for the currently selected builder). ",(0,a.kt)("em",{parentName:"li"},"Note: only container-backed buildx instances are currently supported")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"nerdctl-container://my-container-name")," - Similar to ",(0,a.kt)("inlineCode",{parentName:"li"},"docker-container")," but uses ",(0,a.kt)("inlineCode",{parentName:"li"},"nerdctl"),"."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"podman-container://my-container-name")," - Similar to ",(0,a.kt)("inlineCode",{parentName:"li"},"docker-container")," but uses ",(0,a.kt)("inlineCode",{parentName:"li"},"podman"),"."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"ssh://myhost")," - Connect to a buildkit instance over SSH. Format of the host spec should mimic the SSH command."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"kubepod://mypod")," - Connect to buildkit running in a Kubernetes pod. Can also specify kubectl context and pod namespace (",(0,a.kt)("inlineCode",{parentName:"li"},"kubepod://mypod?context=foo&namespace=notdefault"),").")),(0,a.kt)("h4",{parentName:"li",id:"buildkit-connection-examples"},"Buildkit Connection Examples"),(0,a.kt)("p",{parentName:"li"},"Example: Connect using defaults:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched\n")),(0,a.kt)("p",{parentName:"li"},"Example: Connect to buildx"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre"},"docker buildx create --name demo\ncopa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr buildx://demo\n")),(0,a.kt)("p",{parentName:"li"},"Example: Buildkit in a container"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre"},'export BUILDKIT_VERSION=v0.12.0\ndocker run \\\n --detach \\\n --rm \\\n --privileged \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION"\n\ncopa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr docker-container://buildkitd\n')),(0,a.kt)("p",{parentName:"li"},"Example: Buildkit over TCP"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre"},'export BUILDKIT_VERSION=v0.12.0\n export BUILDKIT_PORT=8888\n docker run \\\n --detach \\\n --rm \\\n --privileged \\\n -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION" \\\n --addr tcp://0.0.0.0:$BUILDKIT_PORT\n copa patch \\\n -i docker.io/library/nginx:1.21.6 \\\n -r nginx.1.21.6.json \\\n -t 1.21.6-patched \\\n -a tcp://0.0.0.0:$BUILDKIT_PORT\n')),(0,a.kt)("p",{parentName:"li"},"In either case, ",(0,a.kt)("inlineCode",{parentName:"p"},"copa")," is non-destructive and exports a new image with the specified ",(0,a.kt)("inlineCode",{parentName:"p"},"1.21.6-patched")," label to the local Docker daemon."),(0,a.kt)("blockquote",{parentName:"li"},(0,a.kt)("p",{parentName:"blockquote"},(0,a.kt)("strong",{parentName:"p"},"NOTE:")," if you're running this sample against an image from a private registry instead,\nensure that the credentials are configured in the default Docker config.json before running ",(0,a.kt)("inlineCode",{parentName:"p"},"copa patch"),",\nfor example, via ",(0,a.kt)("inlineCode",{parentName:"p"},"sudo docker login -u <user> -p <password> <registry>"),"."))),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"Scan the patched image and verify that the vulnerabilities have been patched:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched\n")),(0,a.kt)("p",{parentName:"li"},"You can also inspect the structure of the patched image with ",(0,a.kt)("inlineCode",{parentName:"p"},"docker history")," to see the new patch layer appended to the image:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre",className:"language-bash"},'$ docker history docker.io/library/nginx:1.21.6-patched\nIMAGE CREATED CREATED BY SIZE COMMENT\na372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins\u2026 26.1MB buildkit.exporter.image.v0\n<missing> 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0\n<missing> 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0\n<missing> 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0\n<missing> 3 months ago COPY 30-tune-worker-processes.sh /docker-ent\u2026 4.61kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent\u2026 1.04kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker\u2026 1.96kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0\n<missing> 3 months ago RUN /bin/sh -c set -x && addgroup --syst\u2026 61.1MB buildkit.dockerfile.v0\n<missing> 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0\n<missing> 3 months ago LABEL maintainer=NGINX Docker Maintainers <d\u2026 0B buildkit.dockerfile.v0\n<missing> 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B\n<missing> 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55\u2026 80.4MB\n'))),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"Run the container to verify that the image has no regressions:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre",className:"language-bash"},'$ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched\n/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration\n/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh\n10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf\n10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh\n/docker-entrypoint.sh: Configuration complete; ready for start up\n2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method\n2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2\n2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)\n2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2\n2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576\n2022/05/16 18:00:17 [notice] 1#1: start worker processes\n2022/05/16 18:00:17 [notice] 1#1: start worker process 31\n2022/05/16 18:00:17 [notice] 1#1: start worker process 32\n2022/05/16 18:00:17 [notice] 1#1: start worker process 33\n2022/05/16 18:00:17 [notice] 1#1: start worker process 34\n2022/05/16 18:00:17 [notice] 1#1: start worker process 35\n2022/05/16 18:00:17 [notice] 1#1: start worker process 36\n2022/05/16 18:00:17 [notice] 1#1: start worker process 37\n2022/05/16 18:00:17 [notice] 1#1: start worker process 38\n2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received\n')),(0,a.kt)("p",{parentName:"li"},"You can stop the container by opening a new shell instance and running: ",(0,a.kt)("inlineCode",{parentName:"p"},"docker stop nginx-test")))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/34f2f592.8fe0bd74.js b/website/assets/js/34f2f592.8fe0bd74.js new file mode 100644 index 00000000..ee890ce1 --- /dev/null +++ b/website/assets/js/34f2f592.8fe0bd74.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4364],{5645:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>d,contentTitle:()=>r,default:()=>h,frontMatter:()=>o,metadata:()=>c,toc:()=>l});var t=i(4848),s=i(8453);const o={title:"Quick Start"},r=void 0,c={id:"quick-start",title:"Quick Start",description:"This sample illustrates how to patch containers using vulnerability reports with copa.",source:"@site/versioned_docs/version-v0.5.x/quick-start.md",sourceDirName:".",slug:"/quick-start",permalink:"/copacetic/website/v0.5.x/quick-start",draft:!1,unlisted:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Quick Start"},sidebar:"sidebar",previous:{title:"Installation",permalink:"/copacetic/website/v0.5.x/installation"},next:{title:"Output",permalink:"/copacetic/website/v0.5.x/output"}},d={},l=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Sample Steps",id:"sample-steps",level:2},{value:"Buildkit Connection Examples",id:"buildkit-connection-examples",level:4}];function a(e){const n={a:"a",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h4:"h4",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(n.p,{children:["This sample illustrates how to patch containers using vulnerability reports with ",(0,t.jsx)(n.code,{children:"copa"}),"."]}),"\n",(0,t.jsx)(n.h2,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["An Ubuntu 22.04 VM configured through the ",(0,t.jsx)(n.a,{href:"/copacetic/website/v0.5.x/installation",children:"setup instructions"}),". This includes:","\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"copa"})," tool ",(0,t.jsx)(n.a,{href:"/copacetic/website/v0.5.x/installation",children:"built & pathed"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://github.com/moby/buildkit/#quick-start",children:"buildkit"})," daemon installed & pathed. ",(0,t.jsx)(n.a,{href:"#buildkit-connection-examples",children:"Examples"})]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://docs.docker.com/desktop/linux/install/#generic-installation-steps",children:"docker"})," daemon running and CLI installed & pathed."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://aquasecurity.github.io/trivy/latest/getting-started/installation/",children:"trivy CLI"})," installed & pathed."]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,t.jsx)(n.h2,{id:"sample-steps",children:"Sample Steps"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6\n"})}),"\n",(0,t.jsx)(n.p,{children:"You can also see the existing patchable vulnerabilities in table form on the shell with:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6\n\n"})}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"To patch the image, use the Trivy report and specify a buildkit instance to connect to:"}),"\n",(0,t.jsx)(n.p,{children:"By default copa will attempt to auto-connect to an instance in order:"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["Default docker buildkit endpoint (requires at least docker v24.0 with ",(0,t.jsx)(n.a,{href:"https://docs.docker.com/storage/containerd/#enable-containerd-image-store-on-docker-engine",children:"containerd snapshotter"})," support enabled)"]}),"\n",(0,t.jsxs)(n.li,{children:["Currently selected buildx builder (see: ",(0,t.jsx)(n.code,{children:"docker buildx --help"}),")"]}),"\n",(0,t.jsxs)(n.li,{children:["buildkit daemon at the default address ",(0,t.jsx)(n.code,{children:"/run/buildkit/buildkitd.sock"})]}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["If an instance doesn't exist or that instance doesn't support all the features copa needs the next will be attempted.\nYou may need to specify a custom address using the ",(0,t.jsx)(n.code,{children:"--addr"})," flag. Here are the supported formats:"]}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"unix:///path/to/buildkit.sock"})," - Connect to buildkit over unix socket."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"tcp://$BUILDKIT_ADDR:$PORT"})," - Connect to buildkit over TCP. (not recommended for security reasons)"]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"docker://<docker connection spec>"})," - Connect to docker, currently only unix sockets are supported, e.g. ",(0,t.jsx)(n.code,{children:"docker://unix:///var/run/docker.sock"})," (or just ",(0,t.jsx)(n.code,{children:"docker://"}),")."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"docker-container://my-buildkit-container"})," - Connect to a buildkitd running in a docker container."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"buildx://my-builder"})," - Connect to a buildx builder (or ",(0,t.jsx)(n.code,{children:"buildx://"})," for the currently selected builder). ",(0,t.jsx)(n.em,{children:"Note: only container-backed buildx instances are currently supported"})]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"nerdctl-container://my-container-name"})," - Similar to ",(0,t.jsx)(n.code,{children:"docker-container"})," but uses ",(0,t.jsx)(n.code,{children:"nerdctl"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"podman-container://my-container-name"})," - Similar to ",(0,t.jsx)(n.code,{children:"docker-container"})," but uses ",(0,t.jsx)(n.code,{children:"podman"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"ssh://myhost"})," - Connect to a buildkit instance over SSH. Format of the host spec should mimic the SSH command."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"kubepod://mypod"})," - Connect to buildkit running in a Kubernetes pod. Can also specify kubectl context and pod namespace (",(0,t.jsx)(n.code,{children:"kubepod://mypod?context=foo&namespace=notdefault"}),")."]}),"\n"]}),"\n",(0,t.jsx)(n.h4,{id:"buildkit-connection-examples",children:"Buildkit Connection Examples"}),"\n",(0,t.jsx)(n.p,{children:"Example: Connect using defaults:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched\n"})}),"\n",(0,t.jsx)(n.p,{children:"Example: Connect to buildx"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:"docker buildx create --name demo\ncopa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr buildx://demo\n"})}),"\n",(0,t.jsx)(n.p,{children:"Example: Buildkit in a container"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:'export BUILDKIT_VERSION=v0.12.0\ndocker run \\\n --detach \\\n --rm \\\n --privileged \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION"\n\ncopa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr docker-container://buildkitd\n'})}),"\n",(0,t.jsx)(n.p,{children:"Example: Buildkit over TCP"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:'export BUILDKIT_VERSION=v0.12.0\n export BUILDKIT_PORT=8888\n docker run \\\n --detach \\\n --rm \\\n --privileged \\\n -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION" \\\n --addr tcp://0.0.0.0:$BUILDKIT_PORT\n copa patch \\\n -i docker.io/library/nginx:1.21.6 \\\n -r nginx.1.21.6.json \\\n -t 1.21.6-patched \\\n -a tcp://0.0.0.0:$BUILDKIT_PORT\n'})}),"\n",(0,t.jsxs)(n.p,{children:["In either case, ",(0,t.jsx)(n.code,{children:"copa"})," is non-destructive and exports a new image with the specified ",(0,t.jsx)(n.code,{children:"1.21.6-patched"})," label to the local Docker daemon."]}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"NOTE:"})," if you're running this sample against an image from a private registry instead,\nensure that the credentials are configured in the default Docker config.json before running ",(0,t.jsx)(n.code,{children:"copa patch"}),",\nfor example, via ",(0,t.jsx)(n.code,{children:"sudo docker login -u <user> -p <password> <registry>"}),"."]}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Scan the patched image and verify that the vulnerabilities have been patched:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched\n"})}),"\n",(0,t.jsxs)(n.p,{children:["You can also inspect the structure of the patched image with ",(0,t.jsx)(n.code,{children:"docker history"})," to see the new patch layer appended to the image:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'$ docker history docker.io/library/nginx:1.21.6-patched\nIMAGE CREATED CREATED BY SIZE COMMENT\na372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins\u2026 26.1MB buildkit.exporter.image.v0\n<missing> 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0\n<missing> 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0\n<missing> 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0\n<missing> 3 months ago COPY 30-tune-worker-processes.sh /docker-ent\u2026 4.61kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent\u2026 1.04kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker\u2026 1.96kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0\n<missing> 3 months ago RUN /bin/sh -c set -x && addgroup --syst\u2026 61.1MB buildkit.dockerfile.v0\n<missing> 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0\n<missing> 3 months ago LABEL maintainer=NGINX Docker Maintainers <d\u2026 0B buildkit.dockerfile.v0\n<missing> 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B\n<missing> 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55\u2026 80.4MB\n'})}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Run the container to verify that the image has no regressions:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'$ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched\n/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration\n/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh\n10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf\n10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh\n/docker-entrypoint.sh: Configuration complete; ready for start up\n2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method\n2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2\n2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)\n2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2\n2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576\n2022/05/16 18:00:17 [notice] 1#1: start worker processes\n2022/05/16 18:00:17 [notice] 1#1: start worker process 31\n2022/05/16 18:00:17 [notice] 1#1: start worker process 32\n2022/05/16 18:00:17 [notice] 1#1: start worker process 33\n2022/05/16 18:00:17 [notice] 1#1: start worker process 34\n2022/05/16 18:00:17 [notice] 1#1: start worker process 35\n2022/05/16 18:00:17 [notice] 1#1: start worker process 36\n2022/05/16 18:00:17 [notice] 1#1: start worker process 37\n2022/05/16 18:00:17 [notice] 1#1: start worker process 38\n2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received\n'})}),"\n",(0,t.jsxs)(n.p,{children:["You can stop the container by opening a new shell instance and running: ",(0,t.jsx)(n.code,{children:"docker stop nginx-test"})]}),"\n"]}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(a,{...e})}):a(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>r,x:()=>c});var t=i(6540);const s={},o=t.createContext(s);function r(e){const n=t.useContext(o);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),t.createElement(o.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/3b8c55ea.034e3387.js b/website/assets/js/3b8c55ea.034e3387.js deleted file mode 100644 index 6ef84a3d..00000000 --- a/website/assets/js/3b8c55ea.034e3387.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3217],{3905:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?i(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):i(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,r,a=function(e,t){if(null==e)return{};var n,r,a={},i=Object.keys(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),p=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},s=function(e){var t=p(e.components);return r.createElement(c.Provider,{value:t},e.children)},u="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,c=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),u=p(n),d=a,f=u["".concat(c,".").concat(d)]||u[d]||m[d]||i;return n?r.createElement(f,o(o({ref:t},s),{},{components:n})):r.createElement(f,o({ref:t},s))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=d;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[u]="string"==typeof e?e:a,o[1]=l;for(var p=2;p<i;p++)o[p]=n[p];return r.createElement.apply(null,o)}return r.createElement.apply(null,n)}d.displayName="MDXCreateElement"},9803:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>u,frontMatter:()=>i,metadata:()=>l,toc:()=>p});var r=n(7462),a=(n(7294),n(3905));const i={title:"Installation"},o=void 0,l={unversionedId:"installation",id:"installation",title:"Installation",description:"Homebrew",source:"@site/docs/installation.md",sourceDirName:".",slug:"/installation",permalink:"/copacetic/website/next/installation",draft:!1,tags:[],version:"current",frontMatter:{title:"Installation"},sidebar:"sidebar",previous:{title:"Introduction",permalink:"/copacetic/website/next/"},next:{title:"Quick Start",permalink:"/copacetic/website/next/quick-start"}},c={},p=[{value:"Homebrew",id:"homebrew",level:2},{value:"GitHub",id:"github",level:2},{value:"Development Setup",id:"development-setup",level:2},{value:"Prequisitives",id:"prequisitives",level:3}],s={toc:p};function u(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h2",{id:"homebrew"},"Homebrew"),(0,a.kt)("p",null,"On macOS and Linux, ",(0,a.kt)("inlineCode",{parentName:"p"},"copa")," can be installed via ",(0,a.kt)("a",{parentName:"p",href:"https://brew.sh/"},"Homebrew"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"brew install copa\n")),(0,a.kt)("h2",{id:"github"},"GitHub"),(0,a.kt)("p",null,"You can download the latest and previous versions of ",(0,a.kt)("inlineCode",{parentName:"p"},"copa")," from the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/releases"},"GitHub releases page"),"."),(0,a.kt)("h2",{id:"development-setup"},"Development Setup"),(0,a.kt)("h3",{id:"prequisitives"},"Prequisitives"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://go.dev/doc/install"},"Go")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://docs.docker.com/engine/install/"},"Docker")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://aquasecurity.github.io/trivy/latest/getting-started/installation/"},"Trivy")," (optional as a scanner)")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"git clone https://github.com/project-copacetic/copacetic\ncd copacetic\nmake\n# OPTIONAL: install copa to a pathed folder\nsudo mv dist/linux_amd64/release/copa /usr/local/bin/\n")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/3b8c55ea.995cc6f4.js b/website/assets/js/3b8c55ea.995cc6f4.js new file mode 100644 index 00000000..378ad4fe --- /dev/null +++ b/website/assets/js/3b8c55ea.995cc6f4.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6803],{3668:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>o,contentTitle:()=>a,default:()=>u,frontMatter:()=>c,metadata:()=>l,toc:()=>r});var i=n(4848),s=n(8453);const c={title:"Installation"},a=void 0,l={id:"installation",title:"Installation",description:"Homebrew",source:"@site/docs/installation.md",sourceDirName:".",slug:"/installation",permalink:"/copacetic/website/next/installation",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{title:"Installation"},sidebar:"sidebar",previous:{title:"Introduction",permalink:"/copacetic/website/next/"},next:{title:"Quick Start",permalink:"/copacetic/website/next/quick-start"}},o={},r=[{value:"Homebrew",id:"homebrew",level:2},{value:"GitHub",id:"github",level:2},{value:"Development Setup",id:"development-setup",level:2},{value:"Prequisitives",id:"prequisitives",level:3}];function d(e){const t={a:"a",code:"code",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(t.h2,{id:"homebrew",children:"Homebrew"}),"\n",(0,i.jsxs)(t.p,{children:["On macOS and Linux, ",(0,i.jsx)(t.code,{children:"copa"})," can be installed via ",(0,i.jsx)(t.a,{href:"https://brew.sh/",children:"Homebrew"}),":"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:"brew install copa\n"})}),"\n",(0,i.jsx)(t.h2,{id:"github",children:"GitHub"}),"\n",(0,i.jsxs)(t.p,{children:["You can download the latest and previous versions of ",(0,i.jsx)(t.code,{children:"copa"})," from the ",(0,i.jsx)(t.a,{href:"https://github.com/project-copacetic/copacetic/releases",children:"GitHub releases page"}),"."]}),"\n",(0,i.jsx)(t.h2,{id:"development-setup",children:"Development Setup"}),"\n",(0,i.jsx)(t.h3,{id:"prequisitives",children:"Prequisitives"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://go.dev/doc/install",children:"Go"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://docs.docker.com/engine/install/",children:"Docker"})}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.a,{href:"https://aquasecurity.github.io/trivy/latest/getting-started/installation/",children:"Trivy"})," (optional as a scanner)"]}),"\n"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:"git clone https://github.com/project-copacetic/copacetic\ncd copacetic\nmake\n# OPTIONAL: install copa to a pathed folder\nsudo mv dist/linux_amd64/release/copa /usr/local/bin/\n"})})]})}function u(e={}){const{wrapper:t}={...(0,s.R)(),...e.components};return t?(0,i.jsx)(t,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>a,x:()=>l});var i=n(6540);const s={},c=i.createContext(s);function a(e){const t=i.useContext(c);return i.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function l(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:a(e.components),i.createElement(c.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/3fcb412e.58d24b04.js b/website/assets/js/3fcb412e.58d24b04.js deleted file mode 100644 index 80784a78..00000000 --- a/website/assets/js/3fcb412e.58d24b04.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3242],{3905:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function c(e,t){if(null==e)return{};var n,r,a=function(e,t){if(null==e)return{};var n,r,a={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=r.createContext({}),p=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},s=function(e){var t=p(e.components);return r.createElement(l.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,l=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),u=p(n),m=a,f=u["".concat(l,".").concat(m)]||u[m]||d[m]||o;return n?r.createElement(f,i(i({ref:t},s),{},{components:n})):r.createElement(f,i({ref:t},s))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=m;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[u]="string"==typeof e?e:a,i[1]=c;for(var p=2;p<o;p++)i[p]=n[p];return r.createElement.apply(null,i)}return r.createElement.apply(null,n)}m.displayName="MDXCreateElement"},1450:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>c,toc:()=>p});var r=n(7462),a=(n(7294),n(3905));const o={title:"Installation"},i=void 0,c={unversionedId:"installation",id:"version-v0.4.x/installation",title:"Installation",description:"Homebrew",source:"@site/versioned_docs/version-v0.4.x/installation.md",sourceDirName:".",slug:"/installation",permalink:"/copacetic/website/v0.4.x/installation",draft:!1,tags:[],version:"v0.4.x",frontMatter:{title:"Installation"},sidebar:"sidebar",previous:{title:"Introduction",permalink:"/copacetic/website/v0.4.x/"},next:{title:"Quick Start",permalink:"/copacetic/website/v0.4.x/quick-start"}},l={},p=[{value:"Homebrew",id:"homebrew",level:2},{value:"GitHub",id:"github",level:2},{value:"Development Setup",id:"development-setup",level:2}],s={toc:p};function u(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h2",{id:"homebrew"},"Homebrew"),(0,a.kt)("p",null,"On macOS and Linux, ",(0,a.kt)("inlineCode",{parentName:"p"},"copa")," can be installed via ",(0,a.kt)("a",{parentName:"p",href:"https://brew.sh/"},"Homebrew"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"brew install copa\n")),(0,a.kt)("h2",{id:"github"},"GitHub"),(0,a.kt)("p",null,"You can download the latest and previous versions of ",(0,a.kt)("inlineCode",{parentName:"p"},"copa")," from the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/releases"},"GitHub releases page"),"."),(0,a.kt)("h2",{id:"development-setup"},"Development Setup"),(0,a.kt)("p",null,"The following instructions are for ",(0,a.kt)("strong",{parentName:"p"},"Ubuntu 22.04")," with the dependency versions supported as part of the ",(0,a.kt)("a",{parentName:"p",href:"/copacetic/website/v0.4.x/contributing/#visual-studio-code-development-container"},"dev container")," environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"git clone https://github.com/project-copacetic/copacetic\ncd copacetic\nmake\n# OPTIONAL: install copa to a pathed folder\nsudo mv dist/linux_amd64/release/copa /usr/local/bin/\n")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/3fcb412e.5addcbea.js b/website/assets/js/3fcb412e.5addcbea.js new file mode 100644 index 00000000..e812029b --- /dev/null +++ b/website/assets/js/3fcb412e.5addcbea.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2201],{646:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>r,default:()=>p,frontMatter:()=>s,metadata:()=>a,toc:()=>l});var o=n(4848),i=n(8453);const s={title:"Installation"},r=void 0,a={id:"installation",title:"Installation",description:"Homebrew",source:"@site/versioned_docs/version-v0.4.x/installation.md",sourceDirName:".",slug:"/installation",permalink:"/copacetic/website/v0.4.x/installation",draft:!1,unlisted:!1,tags:[],version:"v0.4.x",frontMatter:{title:"Installation"},sidebar:"sidebar",previous:{title:"Introduction",permalink:"/copacetic/website/v0.4.x/"},next:{title:"Quick Start",permalink:"/copacetic/website/v0.4.x/quick-start"}},c={},l=[{value:"Homebrew",id:"homebrew",level:2},{value:"GitHub",id:"github",level:2},{value:"Development Setup",id:"development-setup",level:2}];function d(e){const t={a:"a",code:"code",h2:"h2",p:"p",pre:"pre",strong:"strong",...(0,i.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(t.h2,{id:"homebrew",children:"Homebrew"}),"\n",(0,o.jsxs)(t.p,{children:["On macOS and Linux, ",(0,o.jsx)(t.code,{children:"copa"})," can be installed via ",(0,o.jsx)(t.a,{href:"https://brew.sh/",children:"Homebrew"}),":"]}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-bash",children:"brew install copa\n"})}),"\n",(0,o.jsx)(t.h2,{id:"github",children:"GitHub"}),"\n",(0,o.jsxs)(t.p,{children:["You can download the latest and previous versions of ",(0,o.jsx)(t.code,{children:"copa"})," from the ",(0,o.jsx)(t.a,{href:"https://github.com/project-copacetic/copacetic/releases",children:"GitHub releases page"}),"."]}),"\n",(0,o.jsx)(t.h2,{id:"development-setup",children:"Development Setup"}),"\n",(0,o.jsxs)(t.p,{children:["The following instructions are for ",(0,o.jsx)(t.strong,{children:"Ubuntu 22.04"})," with the dependency versions supported as part of the ",(0,o.jsx)(t.a,{href:"./contributing.md/#visual-studio-code-development-container",children:"dev container"})," environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead."]}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-bash",children:"git clone https://github.com/project-copacetic/copacetic\ncd copacetic\nmake\n# OPTIONAL: install copa to a pathed folder\nsudo mv dist/linux_amd64/release/copa /usr/local/bin/\n"})})]})}function p(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,o.jsx)(t,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>r,x:()=>a});var o=n(6540);const i={},s=o.createContext(i);function r(e){const t=o.useContext(s);return o.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function a(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:r(e.components),o.createElement(s.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/4468d236.5fa3d39e.js b/website/assets/js/4468d236.5fa3d39e.js new file mode 100644 index 00000000..5e54ade1 --- /dev/null +++ b/website/assets/js/4468d236.5fa3d39e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2676],{8059:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>o,contentTitle:()=>r,default:()=>d,frontMatter:()=>a,metadata:()=>l,toc:()=>c});var t=i(4848),s=i(8453);const a={},r="Maintainer Guidelines",l={id:"maintainer-guidelines",title:"Maintainer Guidelines",description:"Semantic Release Management",source:"@site/versioned_docs/version-v0.6.x/maintainer-guidelines.md",sourceDirName:".",slug:"/maintainer-guidelines",permalink:"/copacetic/website/maintainer-guidelines",draft:!1,unlisted:!1,tags:[],version:"v0.6.x",frontMatter:{},sidebar:"sidebar",previous:{title:"Development and Testing Tips",permalink:"/copacetic/website/development-tips"},next:{title:"Release Process",permalink:"/copacetic/website/release"}},o={},c=[{value:"Semantic Release Management",id:"semantic-release-management",level:2},{value:"Breaking change",id:"breaking-change",level:3},{value:"Deprecation",id:"deprecation",level:3},{value:"Publishing a Release",id:"publishing-a-release",level:2},{value:"Publish a new major/minor version release",id:"publish-a-new-majorminor-version-release",level:3},{value:"Publish a patch revision release",id:"publish-a-patch-revision-release",level:3}];function h(e){const n={a:"a",blockquote:"blockquote",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h1,{id:"maintainer-guidelines",children:"Maintainer Guidelines"}),"\n",(0,t.jsx)(n.h2,{id:"semantic-release-management",children:"Semantic Release Management"}),"\n",(0,t.jsxs)(n.p,{children:["This project uses ",(0,t.jsx)(n.a,{href:"https://github.com/go-semantic-release/semantic-release",children:"go-semantic-release"})," to automatically generate the appropriate ",(0,t.jsx)(n.a,{href:"https://semver.org/",children:"semantic version"})," and changelog for a release based on ",(0,t.jsx)(n.a,{href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format",children:"Angular commit message format"}),". Of note to maintainers is the need to enforce an empty line-separate format:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-xml",children:"<HEADER>\n<-- blank line --\x3e\n<BODY>\n<-- blank line --\x3e\n<FOOTER>\n"})}),"\n",(0,t.jsxs)(n.p,{children:["For contributor PRs, instead of trying to ensure adherence in every commit message, it's easiest to adopt a ",(0,t.jsx)(n.a,{href:"https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/configuring-commit-squashing-for-pull-requests",children:"squash and merge"})," strategy so that the PR description is used as the final commit description with the appropriate semantic release format."]}),"\n",(0,t.jsxs)(n.p,{children:["In addition to the semantic release types called out in the ",(0,t.jsx)(n.a,{href:"../CONTRIBUTING.md#pull-requests",children:"contributor pull request guidelines"}),", there are several other categories supported by the ",(0,t.jsx)(n.a,{href:"https://github.com/go-semantic-release/changelog-generator-default",children:"default changelog generator"})," that maintainers should be aware of:"]}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"chore:"})," Reserved for automated maintenance changes, such as minor version go dependency updates initiated by Dependabot."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"revert:"})," Maintainers should use this to mark commits that revert a previous commit, followed by the header of the reverted commit. The message body should include the SHA of the reverted commit, as well as a clear description of the reason for the revert."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"style:"})," This is unused for this project."]}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["There are also two special categories to be added to the ",(0,t.jsx)(n.a,{href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit-message-footer",children:"message footer"})," that maintainers need to pay special attention to when merging changes:"]}),"\n",(0,t.jsx)(n.h3,{id:"breaking-change",children:"Breaking change"}),"\n",(0,t.jsx)(n.p,{children:"Breaking changes should be described in the footer as follows:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-text",children:"BREAKING CHANGE: <breaking change summary>\n<-- blank line --\x3e\n<breaking change description & migration instructions>\n<-- blank line --\x3e\n<-- blank line --\x3e\nCloses #<issue number>\n"})}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:["Note that this project currently uses the ",(0,t.jsx)(n.code,{children:"allow-initial-development-versions"})," flag for go-semantic-release, so ",(0,t.jsx)(n.strong,{children:"breaking changes will still be handled as minor releases"})," until the workflow is updated for the v1.0.0 release."]}),"\n"]}),"\n",(0,t.jsx)(n.h3,{id:"deprecation",children:"Deprecation"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-text",children:"DEPRECATED: <summary of deprecated feature>\n<-- blank line --\x3e\n<deprecated feature description & migration/workaround instructions>\n<-- blank line --\x3e\n<-- blank line --\x3e\nCloses #<issue number>\n"})}),"\n",(0,t.jsx)(n.h2,{id:"publishing-a-release",children:"Publishing a Release"}),"\n",(0,t.jsxs)(n.p,{children:["To avoid inconsistencies in tagging and release branching, this project uses the ",(0,t.jsx)(n.a,{href:"https://github.com/project-copacetic/copacetic/actions/workflows/release.yml",children:"Publish release"})," GitHub Actions workflow to automate the creation of releases."]}),"\n",(0,t.jsx)(n.h3,{id:"publish-a-new-majorminor-version-release",children:"Publish a new major/minor version release"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["Review the ",(0,t.jsx)(n.code,{children:"main"})," branch to ensure that it has all the desired changes for the new release branch and that there are no PR merge workflows in flight."]}),"\n",(0,t.jsxs)(n.li,{children:["Click ",(0,t.jsx)(n.em,{children:"Run workflow"})," on the ",(0,t.jsx)(n.a,{href:"https://github.com/project-copacetic/copacetic/actions/workflows/release.yml",children:"Publish release"})," against the ",(0,t.jsx)(n.code,{children:"main"})," branch. This will:","\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["Create a new tag with the incremented semantic version (e.g. ",(0,t.jsx)(n.code,{children:"v0.9.0"}),") against the latest commit in ",(0,t.jsx)(n.code,{children:"main"}),"."]}),"\n",(0,t.jsx)(n.li,{children:"Create a new GitHub release against that tag with an automatically generated changelog."}),"\n",(0,t.jsx)(n.li,{children:"Build and upload the new release version of Copa to the GitHub release."}),"\n",(0,t.jsxs)(n.li,{children:["Create a new release branch if it does not already exist (e.g. ",(0,t.jsx)(n.code,{children:"release-0.9"}),")"]}),"\n"]}),"\n"]}),"\n",(0,t.jsx)(n.li,{children:"Verify that the workflow ran successfully and review the expected outputs listed above."}),"\n"]}),"\n",(0,t.jsx)(n.h3,{id:"publish-a-patch-revision-release",children:"Publish a patch revision release"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["Review the appropriate release branch that the revision patches (e.g. ",(0,t.jsx)(n.code,{children:"release-0.9"})," for an anticipated new ",(0,t.jsx)(n.code,{children:"v0.9.x"})," tag) to ensure that it has all the desired changes for the release and that there are no PR merge workflows in flight.","\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["If there are fixes in ",(0,t.jsx)(n.code,{children:"main"})," intended for the patch release in the latest release branch, they need to be manually ported to the release branch first and the revision released from there."]}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["Click ",(0,t.jsx)(n.em,{children:"Run workflow"})," on the ",(0,t.jsx)(n.a,{href:"https://github.com/project-copacetic/copacetic/actions/workflows/release.yml",children:"Publish release"})," against the target ",(0,t.jsx)(n.code,{children:"release-x.y"})," branch. This will:","\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["Create a new tag with the incremented semantic version (e.g. ",(0,t.jsx)(n.code,{children:"v0.9.4"}),") against the latest commit in the release branch."]}),"\n",(0,t.jsx)(n.li,{children:"Create a new GitHub release against that tag with an automatically generated changelog."}),"\n",(0,t.jsx)(n.li,{children:"Build and upload the new release version of Copa to the GitHub release."}),"\n"]}),"\n"]}),"\n",(0,t.jsx)(n.li,{children:"Verify that the workflow ran successfully and review the expected outputs listed above."}),"\n"]})]})}function d(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(h,{...e})}):h(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>r,x:()=>l});var t=i(6540);const s={},a=t.createContext(s);function r(e){const n=t.useContext(a);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),t.createElement(a.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/4468d236.b180c32c.js b/website/assets/js/4468d236.b180c32c.js deleted file mode 100644 index d437b177..00000000 --- a/website/assets/js/4468d236.b180c32c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6198],{3905:(e,t,a)=>{a.d(t,{Zo:()=>p,kt:()=>d});var n=a(7294);function r(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function i(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function l(e){for(var t=1;t<arguments.length;t++){var a=null!=arguments[t]?arguments[t]:{};t%2?i(Object(a),!0).forEach((function(t){r(e,t,a[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):i(Object(a)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))}))}return e}function o(e,t){if(null==e)return{};var a,n,r=function(e,t){if(null==e)return{};var a,n,r={},i=Object.keys(e);for(n=0;n<i.length;n++)a=i[n],t.indexOf(a)>=0||(r[a]=e[a]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n<i.length;n++)a=i[n],t.indexOf(a)>=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(r[a]=e[a])}return r}var s=n.createContext({}),c=function(e){var t=n.useContext(s),a=t;return e&&(a="function"==typeof e?e(t):l(l({},t),e)),a},p=function(e){var t=c(e.components);return n.createElement(s.Provider,{value:t},e.children)},m="mdxType",h={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},u=n.forwardRef((function(e,t){var a=e.components,r=e.mdxType,i=e.originalType,s=e.parentName,p=o(e,["components","mdxType","originalType","parentName"]),m=c(a),u=r,d=m["".concat(s,".").concat(u)]||m[u]||h[u]||i;return a?n.createElement(d,l(l({ref:t},p),{},{components:a})):n.createElement(d,l({ref:t},p))}));function d(e,t){var a=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=a.length,l=new Array(i);l[0]=u;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o[m]="string"==typeof e?e:r,l[1]=o;for(var c=2;c<i;c++)l[c]=a[c];return n.createElement.apply(null,l)}return n.createElement.apply(null,a)}u.displayName="MDXCreateElement"},540:(e,t,a)=>{a.r(t),a.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>m,frontMatter:()=>i,metadata:()=>o,toc:()=>c});var n=a(7462),r=(a(7294),a(3905));const i={},l="Maintainer Guidelines",o={unversionedId:"maintainer-guidelines",id:"version-v0.6.x/maintainer-guidelines",title:"Maintainer Guidelines",description:"Semantic Release Management",source:"@site/versioned_docs/version-v0.6.x/maintainer-guidelines.md",sourceDirName:".",slug:"/maintainer-guidelines",permalink:"/copacetic/website/maintainer-guidelines",draft:!1,tags:[],version:"v0.6.x",frontMatter:{},sidebar:"sidebar",previous:{title:"Development and Testing Tips",permalink:"/copacetic/website/development-tips"},next:{title:"Release Process",permalink:"/copacetic/website/release"}},s={},c=[{value:"Semantic Release Management",id:"semantic-release-management",level:2},{value:"Breaking change",id:"breaking-change",level:3},{value:"Deprecation",id:"deprecation",level:3},{value:"Publishing a Release",id:"publishing-a-release",level:2},{value:"Publish a new major/minor version release",id:"publish-a-new-majorminor-version-release",level:3},{value:"Publish a patch revision release",id:"publish-a-patch-revision-release",level:3}],p={toc:c};function m(e){let{components:t,...a}=e;return(0,r.kt)("wrapper",(0,n.Z)({},p,a,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"maintainer-guidelines"},"Maintainer Guidelines"),(0,r.kt)("h2",{id:"semantic-release-management"},"Semantic Release Management"),(0,r.kt)("p",null,"This project uses ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/go-semantic-release/semantic-release"},"go-semantic-release")," to automatically generate the appropriate ",(0,r.kt)("a",{parentName:"p",href:"https://semver.org/"},"semantic version")," and changelog for a release based on ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format"},"Angular commit message format"),". Of note to maintainers is the need to enforce an empty line-separate format:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-xml"},"<HEADER>\n<-- blank line --\x3e\n<BODY>\n<-- blank line --\x3e\n<FOOTER>\n")),(0,r.kt)("p",null,"For contributor PRs, instead of trying to ensure adherence in every commit message, it's easiest to adopt a ",(0,r.kt)("a",{parentName:"p",href:"https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/configuring-commit-squashing-for-pull-requests"},"squash and merge")," strategy so that the PR description is used as the final commit description with the appropriate semantic release format."),(0,r.kt)("p",null,"In addition to the semantic release types called out in the ",(0,r.kt)("a",{parentName:"p",href:"../CONTRIBUTING.md#pull-requests"},"contributor pull request guidelines"),", there are several other categories supported by the ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/go-semantic-release/changelog-generator-default"},"default changelog generator")," that maintainers should be aware of:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"chore:")," Reserved for automated maintenance changes, such as minor version go dependency updates initiated by Dependabot."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"revert:")," Maintainers should use this to mark commits that revert a previous commit, followed by the header of the reverted commit. The message body should include the SHA of the reverted commit, as well as a clear description of the reason for the revert."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("strong",{parentName:"li"},"style:")," This is unused for this project.")),(0,r.kt)("p",null,"There are also two special categories to be added to the ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit-message-footer"},"message footer")," that maintainers need to pay special attention to when merging changes:"),(0,r.kt)("h3",{id:"breaking-change"},"Breaking change"),(0,r.kt)("p",null,"Breaking changes should be described in the footer as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-text"},"BREAKING CHANGE: <breaking change summary>\n<-- blank line --\x3e\n<breaking change description & migration instructions>\n<-- blank line --\x3e\n<-- blank line --\x3e\nCloses #<issue number>\n")),(0,r.kt)("blockquote",null,(0,r.kt)("p",{parentName:"blockquote"},"Note that this project currently uses the ",(0,r.kt)("inlineCode",{parentName:"p"},"allow-initial-development-versions")," flag for go-semantic-release, so ",(0,r.kt)("strong",{parentName:"p"},"breaking changes will still be handled as minor releases")," until the workflow is updated for the v1.0.0 release.")),(0,r.kt)("h3",{id:"deprecation"},"Deprecation"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-text"},"DEPRECATED: <summary of deprecated feature>\n<-- blank line --\x3e\n<deprecated feature description & migration/workaround instructions>\n<-- blank line --\x3e\n<-- blank line --\x3e\nCloses #<issue number>\n")),(0,r.kt)("h2",{id:"publishing-a-release"},"Publishing a Release"),(0,r.kt)("p",null,"To avoid inconsistencies in tagging and release branching, this project uses the ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/actions/workflows/release.yml"},"Publish release")," GitHub Actions workflow to automate the creation of releases."),(0,r.kt)("h3",{id:"publish-a-new-majorminor-version-release"},"Publish a new major/minor version release"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"Review the ",(0,r.kt)("inlineCode",{parentName:"li"},"main")," branch to ensure that it has all the desired changes for the new release branch and that there are no PR merge workflows in flight."),(0,r.kt)("li",{parentName:"ol"},"Click ",(0,r.kt)("em",{parentName:"li"},"Run workflow")," on the ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/project-copacetic/copacetic/actions/workflows/release.yml"},"Publish release")," against the ",(0,r.kt)("inlineCode",{parentName:"li"},"main")," branch. This will:",(0,r.kt)("ol",{parentName:"li"},(0,r.kt)("li",{parentName:"ol"},"Create a new tag with the incremented semantic version (e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"v0.9.0"),") against the latest commit in ",(0,r.kt)("inlineCode",{parentName:"li"},"main"),"."),(0,r.kt)("li",{parentName:"ol"},"Create a new GitHub release against that tag with an automatically generated changelog."),(0,r.kt)("li",{parentName:"ol"},"Build and upload the new release version of Copa to the GitHub release."),(0,r.kt)("li",{parentName:"ol"},"Create a new release branch if it does not already exist (e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"release-0.9"),")"))),(0,r.kt)("li",{parentName:"ol"},"Verify that the workflow ran successfully and review the expected outputs listed above.")),(0,r.kt)("h3",{id:"publish-a-patch-revision-release"},"Publish a patch revision release"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"Review the appropriate release branch that the revision patches (e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"release-0.9")," for an anticipated new ",(0,r.kt)("inlineCode",{parentName:"li"},"v0.9.x")," tag) to ensure that it has all the desired changes for the release and that there are no PR merge workflows in flight.",(0,r.kt)("ol",{parentName:"li"},(0,r.kt)("li",{parentName:"ol"},"If there are fixes in ",(0,r.kt)("inlineCode",{parentName:"li"},"main")," intended for the patch release in the latest release branch, they need to be manually ported to the release branch first and the revision released from there."))),(0,r.kt)("li",{parentName:"ol"},"Click ",(0,r.kt)("em",{parentName:"li"},"Run workflow")," on the ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/project-copacetic/copacetic/actions/workflows/release.yml"},"Publish release")," against the target ",(0,r.kt)("inlineCode",{parentName:"li"},"release-x.y")," branch. This will:",(0,r.kt)("ol",{parentName:"li"},(0,r.kt)("li",{parentName:"ol"},"Create a new tag with the incremented semantic version (e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"v0.9.4"),") against the latest commit in the release branch."),(0,r.kt)("li",{parentName:"ol"},"Create a new GitHub release against that tag with an automatically generated changelog."),(0,r.kt)("li",{parentName:"ol"},"Build and upload the new release version of Copa to the GitHub release."))),(0,r.kt)("li",{parentName:"ol"},"Verify that the workflow ran successfully and review the expected outputs listed above.")))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/48ae5635.51490778.js b/website/assets/js/48ae5635.51490778.js new file mode 100644 index 00000000..388657b9 --- /dev/null +++ b/website/assets/js/48ae5635.51490778.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1508],{1690:(e,t,i)=>{i.r(t),i.d(t,{assets:()=>d,contentTitle:()=>r,default:()=>h,frontMatter:()=>o,metadata:()=>c,toc:()=>a});var n=i(4848),s=i(8453);const o={title:"Contributing"},r=void 0,c={id:"contributing",title:"Contributing",description:"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.",source:"@site/versioned_docs/version-v0.4.x/contributing.md",sourceDirName:".",slug:"/contributing",permalink:"/copacetic/website/v0.4.x/contributing",draft:!1,unlisted:!1,tags:[],version:"v0.4.x",frontMatter:{title:"Contributing"},sidebar:"sidebar",previous:{title:"FAQ",permalink:"/copacetic/website/v0.4.x/faq"},next:{title:"Code of Conduct",permalink:"/copacetic/website/v0.4.x/code-of-conduct"}},d={},a=[{value:"Bi-Weekly Community Meeting",id:"bi-weekly-community-meeting",level:2},{value:"Slack",id:"slack",level:2},{value:"Contributing Issues",id:"contributing-issues",level:2},{value:"Contributing Code",id:"contributing-code",level:2},{value:"Getting Started",id:"getting-started",level:3},{value:"Visual Studio Code Development Container",id:"visual-studio-code-development-container",level:3},{value:"Prerequisites",id:"prerequisites",level:4},{value:"Personalizing user settings in a dev container",id:"personalizing-user-settings-in-a-dev-container",level:4},{value:"Tests",id:"tests",level:3},{value:"Pull Requests",id:"pull-requests",level:3},{value:"Developer Certificate of Origin (DCO)",id:"developer-certificate-of-origin-dco",level:2},{value:"I didn't sign my commit, now what?",id:"i-didnt-sign-my-commit-now-what",level:3},{value:"Code of Conduct",id:"code-of-conduct",level:2}];function l(e){const t={a:"a",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h3:"h3",h4:"h4",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsxs)(t.p,{children:["Welcome! We are very happy to accept community contributions to the project, whether through ",(0,n.jsx)(t.a,{href:"#contributing-issues",children:"filing issues"})," or ",(0,n.jsx)(t.a,{href:"#contributing-code",children:"code"})," in the form of ",(0,n.jsx)(t.a,{href:"#pull-requests",children:"Pull Requests"}),". Please note that by participating in this project, you agree to abide by the ",(0,n.jsx)(t.a,{href:"/copacetic/website/v0.4.x/code-of-conduct",children:"Code of Conduct"}),", as well as the terms of the ",(0,n.jsx)(t.a,{href:"#developer-certificate-of-origin-dco",children:"Developer Certificate of Origin"}),"."]}),"\n",(0,n.jsx)(t.h2,{id:"bi-weekly-community-meeting",children:"Bi-Weekly Community Meeting"}),"\n",(0,n.jsxs)(t.p,{children:["A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join ",(0,n.jsx)(t.a,{href:"https://docs.google.com/document/d/1QdskbeCtgKcdWYHI6EXkLFxyzTCyVT6e8MgB3CaAhWI/edit?usp=sharing",children:"here"})]}),"\n",(0,n.jsx)(t.h2,{id:"slack",children:"Slack"}),"\n",(0,n.jsxs)(t.p,{children:["To discuss issues with Copa, features, or development, you can join the ",(0,n.jsx)(t.code,{children:"#copa"})," channel on the ",(0,n.jsx)(t.a,{href:"https://communityinviter.com/apps/opencontainers/join-the-oci-community",children:"OCI Slack"}),"."]}),"\n",(0,n.jsx)(t.h2,{id:"contributing-issues",children:"Contributing Issues"}),"\n",(0,n.jsxs)(t.p,{children:["Before opening any new issues, please search our ",(0,n.jsx)(t.a,{href:"https://github.com/project-copacetic/copacetic/issues",children:"existing GitHub issues"})," to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead."]}),"\n",(0,n.jsx)(t.p,{children:"When opening an issue, please select the most appropriate template for what you're contributing:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"Bug Report:"})," If you would like to report the project or tool behaving in unexpected ways."]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"Documentation Improvement:"})," If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content."]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"Request:"})," If you have a feature request, suggestion, or a even a design proposal to review."]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"Question:"})," If you would like to ask the maintainers a question about the project."]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"contributing-code",children:"Contributing Code"}),"\n",(0,n.jsx)(t.h3,{id:"getting-started",children:"Getting Started"}),"\n",(0,n.jsx)(t.p,{children:"Follow the instructions to either:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.a,{href:"/copacetic/website/v0.4.x/installation",children:"Setup your dev environment to build copa"}),"."]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.a,{href:"#visual-studio-code-development-container",children:"Use the copa development container"})," in ",(0,n.jsx)(t.a,{href:"https://code.visualstudio.com/",children:"Visual Studio Code"}),"."]}),"\n"]}),"\n",(0,n.jsxs)(t.p,{children:["For an overview of the project components, refer to the ",(0,n.jsx)(t.a,{href:"/copacetic/website/v0.4.x/design",children:"copa design"})," document."]}),"\n",(0,n.jsx)(t.h3,{id:"visual-studio-code-development-container",children:"Visual Studio Code Development Container"}),"\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.a,{href:"https://code.visualstudio.com/",children:"VSCode"})," supports development in a containerized environment through its ",(0,n.jsx)(t.a,{href:"https://code.visualstudio.com/docs/remote/containers",children:"Remote - Container extension"}),". This folder provides a development container which encapsulates the dependencies specified in the ",(0,n.jsx)(t.a,{href:"/copacetic/website/v0.4.x/installation",children:"instructions to build and run copa"}),"."]}),"\n",(0,n.jsx)(t.h4,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,n.jsxs)(t.ol,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.a,{href:"https://docs.docker.com/get-docker/",children:"Docker"}),"\n",(0,n.jsxs)(t.blockquote,{children:["\n",(0,n.jsxs)(t.p,{children:["For Windows users, enabling ",(0,n.jsx)(t.a,{href:"https://docs.docker.com/docker-for-windows/wsl/",children:"WSL2 back-end integration with Docker"})," is recommended."]}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.li,{children:(0,n.jsx)(t.a,{href:"https://code.visualstudio.com/",children:"Visual Studio Code"})}),"\n",(0,n.jsx)(t.li,{children:(0,n.jsx)(t.a,{href:"https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers",children:"Visual Studio Code Remote - Containers extension"})}),"\n"]}),"\n",(0,n.jsxs)(t.blockquote,{children:["\n",(0,n.jsx)(t.p,{children:(0,n.jsx)(t.strong,{children:"\u26a0 If running via Docker Desktop for Windows"})}),"\n",(0,n.jsxs)(t.p,{children:["Note that the ",(0,n.jsxs)(t.a,{href:"https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user",children:["mounted workspace files appear owned by ",(0,n.jsx)(t.code,{children:"root"})]})," in the dev container, which will cause ",(0,n.jsx)(t.code,{children:"git"})," commands to fail with a ",(0,n.jsx)(t.code,{children:"fatal: detected dubious ownership in a repository"})," error due to ",(0,n.jsx)(t.a,{href:"https://git-scm.com/docs/git-config/2.35.2#Documentation/git-config.txt-safedirectory",children:"safe.directory"})," checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the ",(0,n.jsx)(t.code,{children:"vscode"})," user:"]}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-bash",children:"sudo chown -R vscode:vscode /workspace/copacetic\n"})}),"\n"]}),"\n",(0,n.jsx)(t.h4,{id:"personalizing-user-settings-in-a-dev-container",children:"Personalizing user settings in a dev container"}),"\n",(0,n.jsxs)(t.p,{children:["VSCode supports applying your user settings, such as your ",(0,n.jsx)(t.code,{children:".gitconfig"}),", to a dev container through the use of ",(0,n.jsx)(t.a,{href:"https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories",children:"dotfiles repositories"}),". This can be done through your own VSCode ",(0,n.jsx)(t.code,{children:"settings.json"})," file without changing the dev container image or configuration."]}),"\n",(0,n.jsx)(t.h3,{id:"tests",children:"Tests"}),"\n",(0,n.jsxs)(t.p,{children:["Once you can successfully ",(0,n.jsx)(t.code,{children:"make"})," the project, any code contributions should also successfully:"]}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["Pass unit tests via ",(0,n.jsx)(t.code,{children:"make test"}),"."]}),"\n",(0,n.jsxs)(t.li,{children:["Lint cleanly via ",(0,n.jsx)(t.code,{children:"make lint"}),"."]}),"\n"]}),"\n",(0,n.jsxs)(t.p,{children:["Pull requests will also be expected to pass the PR functional tests specified by ",(0,n.jsx)(t.code,{children:".github/workflows/build.yml"}),"."]}),"\n",(0,n.jsx)(t.h3,{id:"pull-requests",children:"Pull Requests"}),"\n",(0,n.jsxs)(t.p,{children:["If you'd like to start contributing code to the project, you can search for ",(0,n.jsxs)(t.a,{href:"https://github.com/project-copacetic/copacetic/labels/good%20first%20issue",children:["issues with the ",(0,n.jsx)(t.code,{children:"good first issue"})," label"]}),". Other kinds of PR contributions we would look for include:"]}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"Fixes for bugs and other correctness issues."}),"\n",(0,n.jsx)(t.li,{children:"Docs and other content improvements (e.g. samples)."}),"\n",(0,n.jsx)(t.li,{children:"Extensions to support parsing new scanning report formats."}),"\n",(0,n.jsx)(t.li,{children:"Extensions to support patching images based on new distros or using new package managers."}),"\n"]}),"\n",(0,n.jsx)(t.p,{children:"For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["Architectural changes (e.g. breaking interfaces or violations of ",(0,n.jsx)(t.a,{href:"/copacetic/website/v0.4.x/design",children:"this project's design tenets"}),")."]}),"\n",(0,n.jsx)(t.li,{children:"Unsolicited features that significantly expand the functional scope of the tool."}),"\n"]}),"\n",(0,n.jsxs)(t.p,{children:["Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the ",(0,n.jsx)(t.a,{href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format",children:"Angular commit message format"})," for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs."]}),"\n",(0,n.jsx)(t.p,{children:"We suggest:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["Use the standard header format of ",(0,n.jsx)(t.code,{children:'"<type>: <short summary>"'})," where the ",(0,n.jsx)(t.code,{children:"<type>"})," is one of the following:","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"build:"})," Changes that affect the build system or external dependencies"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"ci:"})," Changes to the GitHub workflows and configurations"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"docs:"})," Documentation only changes"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"feat:"})," A new feature"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"fix:"})," A bug fix"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"perf:"})," A code change that improves performance"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"refactor:"})," A code change that neither fixes a bug nor adds a feature"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"test:"})," Adding missing tests or correcting existing tests"]}),"\n"]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["Use a ",(0,n.jsx)(t.a,{href:"https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html",children:"concise, imperative description"})," of the changes included in the ",(0,n.jsx)(t.code,{children:"<short summary>"})," of the header, the body of the PR, and generally in your commit messages."]}),"\n",(0,n.jsxs)(t.li,{children:["Use ",(0,n.jsx)(t.a,{href:"https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests",children:"GitHub keywords"})," in the footer of your PR description, such as ",(0,n.jsx)(t.code,{children:"closes"})," to automatically close issues the PR intends to address."]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"developer-certificate-of-origin-dco",children:"Developer Certificate of Origin (DCO)"}),"\n",(0,n.jsxs)(t.p,{children:["The ",(0,n.jsx)(t.a,{href:"https://wiki.linuxfoundation.org/dco",children:"Developer Certificate of Origin"})," (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the ",(0,n.jsx)(t.a,{href:"https://developercertificate.org/",children:"full text of the DCO"}),", reformatted for readability:"]}),"\n",(0,n.jsxs)(t.blockquote,{children:["\n",(0,n.jsx)(t.p,{children:"By making a contribution to this project, I certify that:"}),"\n",(0,n.jsx)(t.p,{children:"(a) The contribution was created in whole or in part by me and I\nhave the right to submit it under the open source license\nindicated in the file; or"}),"\n",(0,n.jsx)(t.p,{children:"(b) The contribution is based upon previous work that, to the best\nof my knowledge, is covered under an appropriate open source\nlicense and I have the right under that license to submit that\nwork with modifications, whether created in whole or in part\nby me, under the same open source license (unless I am\npermitted to submit under a different license), as indicated\nin the file; or"}),"\n",(0,n.jsx)(t.p,{children:"(c) The contribution was provided directly to me by some other\nperson who certified (a), (b) or (c) and I have not modified\nit."}),"\n",(0,n.jsx)(t.p,{children:"(d) I understand and agree that this project and the contribution\nare public and that a record of the contribution (including all\npersonal information I submit with it, including my sign-off) is\nmaintained indefinitely and may be redistributed consistent with\nthis project or the open source license(s) involved."}),"\n"]}),"\n",(0,n.jsxs)(t.p,{children:["Contributors ",(0,n.jsx)(t.em,{children:"sign-off"})," that they adhere to these requirements by adding a ",(0,n.jsx)(t.code,{children:"Signed-off-by"})," line to commit messages."]}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-text",children:"This is my commit message\n\nSigned-off-by: Random J Developer <random@developer.example.org>\n"})}),"\n",(0,n.jsxs)(t.p,{children:["Git even has a ",(0,n.jsx)(t.code,{children:"-s"})," command line option to append this automatically to your commit message:"]}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-bash",children:"git commit -s -m 'This is my commit message'\n"})}),"\n",(0,n.jsxs)(t.p,{children:["Pull requests that do not contain a valid ",(0,n.jsx)(t.code,{children:"Signed-off-by"})," line cannot be merged."]}),"\n",(0,n.jsx)(t.h3,{id:"i-didnt-sign-my-commit-now-what",children:"I didn't sign my commit, now what?"}),"\n",(0,n.jsx)(t.p,{children:"No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:"}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-bash",children:"git switch <branch-name>\ngit commit --amend --no-edit --signoff\ngit push --force-with-lease <remote-name> <branch-name>\n"})}),"\n",(0,n.jsx)(t.h2,{id:"code-of-conduct",children:"Code of Conduct"}),"\n",(0,n.jsxs)(t.p,{children:["This project has adopted the ",(0,n.jsx)(t.a,{href:"/copacetic/website/v0.4.x/code-of-conduct",children:"Contributor Covenant Code of Conduct"}),"."]})]})}function h(e={}){const{wrapper:t}={...(0,s.R)(),...e.components};return t?(0,n.jsx)(t,{...e,children:(0,n.jsx)(l,{...e})}):l(e)}},8453:(e,t,i)=>{i.d(t,{R:()=>r,x:()=>c});var n=i(6540);const s={},o=n.createContext(s);function r(e){const t=n.useContext(o);return n.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function c(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),n.createElement(o.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/48ae5635.cd5cfcb1.js b/website/assets/js/48ae5635.cd5cfcb1.js deleted file mode 100644 index 3a93215f..00000000 --- a/website/assets/js/48ae5635.cd5cfcb1.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9212],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var o=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function r(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?a(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):a(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,o,i=function(e,t){if(null==e)return{};var n,o,i={},a=Object.keys(e);for(o=0;o<a.length;o++)n=a[o],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o<a.length;o++)n=a[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=o.createContext({}),c=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},u=function(e){var t=c(e.components);return o.createElement(l.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),p=c(n),m=i,h=p["".concat(l,".").concat(m)]||p[m]||d[m]||a;return n?o.createElement(h,r(r({ref:t},u),{},{components:n})):o.createElement(h,r({ref:t},u))}));function h(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,r=new Array(a);r[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[p]="string"==typeof e?e:i,r[1]=s;for(var c=2;c<a;c++)r[c]=n[c];return o.createElement.apply(null,r)}return o.createElement.apply(null,n)}m.displayName="MDXCreateElement"},6954:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>p,frontMatter:()=>a,metadata:()=>s,toc:()=>c});var o=n(7462),i=(n(7294),n(3905));const a={title:"Contributing"},r=void 0,s={unversionedId:"contributing",id:"version-v0.4.x/contributing",title:"Contributing",description:"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.",source:"@site/versioned_docs/version-v0.4.x/contributing.md",sourceDirName:".",slug:"/contributing",permalink:"/copacetic/website/v0.4.x/contributing",draft:!1,tags:[],version:"v0.4.x",frontMatter:{title:"Contributing"},sidebar:"sidebar",previous:{title:"FAQ",permalink:"/copacetic/website/v0.4.x/faq"},next:{title:"Code of Conduct",permalink:"/copacetic/website/v0.4.x/code-of-conduct"}},l={},c=[{value:"Bi-Weekly Community Meeting",id:"bi-weekly-community-meeting",level:2},{value:"Slack",id:"slack",level:2},{value:"Contributing Issues",id:"contributing-issues",level:2},{value:"Contributing Code",id:"contributing-code",level:2},{value:"Getting Started",id:"getting-started",level:3},{value:"Visual Studio Code Development Container",id:"visual-studio-code-development-container",level:3},{value:"Prerequisites",id:"prerequisites",level:4},{value:"Personalizing user settings in a dev container",id:"personalizing-user-settings-in-a-dev-container",level:4},{value:"Tests",id:"tests",level:3},{value:"Pull Requests",id:"pull-requests",level:3},{value:"Developer Certificate of Origin (DCO)",id:"developer-certificate-of-origin-dco",level:2},{value:"I didn't sign my commit, now what?",id:"i-didnt-sign-my-commit-now-what",level:3},{value:"Code of Conduct",id:"code-of-conduct",level:2}],u={toc:c};function p(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,o.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"Welcome! We are very happy to accept community contributions to the project, whether through ",(0,i.kt)("a",{parentName:"p",href:"#contributing-issues"},"filing issues")," or ",(0,i.kt)("a",{parentName:"p",href:"#contributing-code"},"code")," in the form of ",(0,i.kt)("a",{parentName:"p",href:"#pull-requests"},"Pull Requests"),". Please note that by participating in this project, you agree to abide by the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.4.x/code-of-conduct"},"Code of Conduct"),", as well as the terms of the ",(0,i.kt)("a",{parentName:"p",href:"#developer-certificate-of-origin-dco"},"Developer Certificate of Origin"),"."),(0,i.kt)("h2",{id:"bi-weekly-community-meeting"},"Bi-Weekly Community Meeting"),(0,i.kt)("p",null,"A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join ",(0,i.kt)("a",{parentName:"p",href:"https://docs.google.com/document/d/1QdskbeCtgKcdWYHI6EXkLFxyzTCyVT6e8MgB3CaAhWI/edit?usp=sharing"},"here")),(0,i.kt)("h2",{id:"slack"},"Slack"),(0,i.kt)("p",null,"To discuss issues with Copa, features, or development, you can join the ",(0,i.kt)("inlineCode",{parentName:"p"},"#copa")," channel on the ",(0,i.kt)("a",{parentName:"p",href:"https://communityinviter.com/apps/opencontainers/join-the-oci-community"},"OCI Slack"),"."),(0,i.kt)("h2",{id:"contributing-issues"},"Contributing Issues"),(0,i.kt)("p",null,"Before opening any new issues, please search our ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/issues"},"existing GitHub issues")," to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead."),(0,i.kt)("p",null,"When opening an issue, please select the most appropriate template for what you're contributing:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Bug Report:")," If you would like to report the project or tool behaving in unexpected ways."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Documentation Improvement:")," If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Request:")," If you have a feature request, suggestion, or a even a design proposal to review."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Question:")," If you would like to ask the maintainers a question about the project.")),(0,i.kt)("h2",{id:"contributing-code"},"Contributing Code"),(0,i.kt)("h3",{id:"getting-started"},"Getting Started"),(0,i.kt)("p",null,"Follow the instructions to either:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/copacetic/website/v0.4.x/installation"},"Setup your dev environment to build copa"),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"#visual-studio-code-development-container"},"Use the copa development container")," in ",(0,i.kt)("a",{parentName:"li",href:"https://code.visualstudio.com/"},"Visual Studio Code"),".")),(0,i.kt)("p",null,"For an overview of the project components, refer to the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.4.x/design"},"copa design")," document."),(0,i.kt)("h3",{id:"visual-studio-code-development-container"},"Visual Studio Code Development Container"),(0,i.kt)("p",null,(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/"},"VSCode")," supports development in a containerized environment through its ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/docs/remote/containers"},"Remote - Container extension"),". This folder provides a development container which encapsulates the dependencies specified in the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.4.x/installation"},"instructions to build and run copa"),"."),(0,i.kt)("h4",{id:"prerequisites"},"Prerequisites"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://docs.docker.com/get-docker/"},"Docker"),(0,i.kt)("blockquote",{parentName:"li"},(0,i.kt)("p",{parentName:"blockquote"},"For Windows users, enabling ",(0,i.kt)("a",{parentName:"p",href:"https://docs.docker.com/docker-for-windows/wsl/"},"WSL2 back-end integration with Docker")," is recommended."))),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://code.visualstudio.com/"},"Visual Studio Code")),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers"},"Visual Studio Code Remote - Containers extension"))),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},(0,i.kt)("strong",{parentName:"p"},"\u26a0 If running via Docker Desktop for Windows")),(0,i.kt)("p",{parentName:"blockquote"},"Note that the ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user"},"mounted workspace files appear owned by ",(0,i.kt)("inlineCode",{parentName:"a"},"root"))," in the dev container, which will cause ",(0,i.kt)("inlineCode",{parentName:"p"},"git")," commands to fail with a ",(0,i.kt)("inlineCode",{parentName:"p"},"fatal: detected dubious ownership in a repository")," error due to ",(0,i.kt)("a",{parentName:"p",href:"https://git-scm.com/docs/git-config/2.35.2#Documentation/git-config.txt-safedirectory"},"safe.directory")," checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the ",(0,i.kt)("inlineCode",{parentName:"p"},"vscode")," user:"),(0,i.kt)("pre",{parentName:"blockquote"},(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"sudo chown -R vscode:vscode /workspace/copacetic\n"))),(0,i.kt)("h4",{id:"personalizing-user-settings-in-a-dev-container"},"Personalizing user settings in a dev container"),(0,i.kt)("p",null,"VSCode supports applying your user settings, such as your ",(0,i.kt)("inlineCode",{parentName:"p"},".gitconfig"),", to a dev container through the use of ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories"},"dotfiles repositories"),". This can be done through your own VSCode ",(0,i.kt)("inlineCode",{parentName:"p"},"settings.json")," file without changing the dev container image or configuration."),(0,i.kt)("h3",{id:"tests"},"Tests"),(0,i.kt)("p",null,"Once you can successfully ",(0,i.kt)("inlineCode",{parentName:"p"},"make")," the project, any code contributions should also successfully:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Pass unit tests via ",(0,i.kt)("inlineCode",{parentName:"li"},"make test"),"."),(0,i.kt)("li",{parentName:"ul"},"Lint cleanly via ",(0,i.kt)("inlineCode",{parentName:"li"},"make lint"),".")),(0,i.kt)("p",null,"Pull requests will also be expected to pass the PR functional tests specified by ",(0,i.kt)("inlineCode",{parentName:"p"},".github/workflows/build.yml"),"."),(0,i.kt)("h3",{id:"pull-requests"},"Pull Requests"),(0,i.kt)("p",null,"If you'd like to start contributing code to the project, you can search for ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/labels/good%20first%20issue"},"issues with the ",(0,i.kt)("inlineCode",{parentName:"a"},"good first issue")," label"),". Other kinds of PR contributions we would look for include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Fixes for bugs and other correctness issues."),(0,i.kt)("li",{parentName:"ul"},"Docs and other content improvements (e.g. samples)."),(0,i.kt)("li",{parentName:"ul"},"Extensions to support parsing new scanning report formats."),(0,i.kt)("li",{parentName:"ul"},"Extensions to support patching images based on new distros or using new package managers.")),(0,i.kt)("p",null,"For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Architectural changes (e.g. breaking interfaces or violations of ",(0,i.kt)("a",{parentName:"li",href:"/copacetic/website/v0.4.x/design"},"this project's design tenets"),")."),(0,i.kt)("li",{parentName:"ul"},"Unsolicited features that significantly expand the functional scope of the tool.")),(0,i.kt)("p",null,"Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format"},"Angular commit message format")," for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs."),(0,i.kt)("p",null,"We suggest:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Use the standard header format of ",(0,i.kt)("inlineCode",{parentName:"li"},'"<type>: <short summary>"')," where the ",(0,i.kt)("inlineCode",{parentName:"li"},"<type>")," is one of the following:",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"build:")," Changes that affect the build system or external dependencies"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"ci:")," Changes to the GitHub workflows and configurations"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"docs:")," Documentation only changes"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"feat:")," A new feature"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"fix:")," A bug fix"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"perf:")," A code change that improves performance"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"refactor:")," A code change that neither fixes a bug nor adds a feature"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"test:")," Adding missing tests or correcting existing tests"))),(0,i.kt)("li",{parentName:"ul"},"Use a ",(0,i.kt)("a",{parentName:"li",href:"https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html"},"concise, imperative description")," of the changes included in the ",(0,i.kt)("inlineCode",{parentName:"li"},"<short summary>")," of the header, the body of the PR, and generally in your commit messages."),(0,i.kt)("li",{parentName:"ul"},"Use ",(0,i.kt)("a",{parentName:"li",href:"https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests"},"GitHub keywords")," in the footer of your PR description, such as ",(0,i.kt)("inlineCode",{parentName:"li"},"closes")," to automatically close issues the PR intends to address.")),(0,i.kt)("h2",{id:"developer-certificate-of-origin-dco"},"Developer Certificate of Origin (DCO)"),(0,i.kt)("p",null,"The ",(0,i.kt)("a",{parentName:"p",href:"https://wiki.linuxfoundation.org/dco"},"Developer Certificate of Origin")," (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the ",(0,i.kt)("a",{parentName:"p",href:"https://developercertificate.org/"},"full text of the DCO"),", reformatted for readability:"),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},"By making a contribution to this project, I certify that:"),(0,i.kt)("p",{parentName:"blockquote"},"(a) The contribution was created in whole or in part by me and I\nhave the right to submit it under the open source license\nindicated in the file; or"),(0,i.kt)("p",{parentName:"blockquote"},"(b) The contribution is based upon previous work that, to the best\nof my knowledge, is covered under an appropriate open source\nlicense and I have the right under that license to submit that\nwork with modifications, whether created in whole or in part\nby me, under the same open source license (unless I am\npermitted to submit under a different license), as indicated\nin the file; or"),(0,i.kt)("p",{parentName:"blockquote"},"(c) The contribution was provided directly to me by some other\nperson who certified (a), (b) or (c) and I have not modified\nit."),(0,i.kt)("p",{parentName:"blockquote"},"(d) I understand and agree that this project and the contribution\nare public and that a record of the contribution (including all\npersonal information I submit with it, including my sign-off) is\nmaintained indefinitely and may be redistributed consistent with\nthis project or the open source license(s) involved.")),(0,i.kt)("p",null,"Contributors ",(0,i.kt)("em",{parentName:"p"},"sign-off")," that they adhere to these requirements by adding a ",(0,i.kt)("inlineCode",{parentName:"p"},"Signed-off-by")," line to commit messages."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-text"},"This is my commit message\n\nSigned-off-by: Random J Developer <random@developer.example.org>\n")),(0,i.kt)("p",null,"Git even has a ",(0,i.kt)("inlineCode",{parentName:"p"},"-s")," command line option to append this automatically to your commit message:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"git commit -s -m 'This is my commit message'\n")),(0,i.kt)("p",null,"Pull requests that do not contain a valid ",(0,i.kt)("inlineCode",{parentName:"p"},"Signed-off-by")," line cannot be merged."),(0,i.kt)("h3",{id:"i-didnt-sign-my-commit-now-what"},"I didn't sign my commit, now what?"),(0,i.kt)("p",null,"No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"git switch <branch-name>\ngit commit --amend --no-edit --signoff\ngit push --force-with-lease <remote-name> <branch-name>\n")),(0,i.kt)("h2",{id:"code-of-conduct"},"Code of Conduct"),(0,i.kt)("p",null,"This project has adopted the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.4.x/code-of-conduct"},"Contributor Covenant Code of Conduct"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/4972.9374abde.js b/website/assets/js/4972.9374abde.js deleted file mode 100644 index 27b3f752..00000000 --- a/website/assets/js/4972.9374abde.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4972],{4972:(e,t,n)=>{n.r(t),n.d(t,{default:()=>i});var a=n(7294),l=n(5999),o=n(1944),r=n(7961);function i(){return a.createElement(a.Fragment,null,a.createElement(o.d,{title:(0,l.I)({id:"theme.NotFound.title",message:"Page Not Found"})}),a.createElement(r.Z,null,a.createElement("main",{className:"container margin-vert--xl"},a.createElement("div",{className:"row"},a.createElement("div",{className:"col col--6 col--offset-3"},a.createElement("h1",{className:"hero__title"},a.createElement(l.Z,{id:"theme.NotFound.title",description:"The title of the 404 page"},"Page Not Found")),a.createElement("p",null,a.createElement(l.Z,{id:"theme.NotFound.p1",description:"The first paragraph of the 404 page"},"We could not find what you were looking for.")),a.createElement("p",null,a.createElement(l.Z,{id:"theme.NotFound.p2",description:"The 2nd paragraph of the 404 page"},"Please contact the owner of the site that linked you to the original URL and let them know their link is broken.")))))))}}}]); \ No newline at end of file diff --git a/website/assets/js/499c1190.18188f88.js b/website/assets/js/499c1190.18188f88.js deleted file mode 100644 index 51ff8baf..00000000 --- a/website/assets/js/499c1190.18188f88.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3891],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>d});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=a.createContext({}),c=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(l.Provider,{value:t},e.children)},u="mdxType",g={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),u=c(n),m=i,d=u["".concat(l,".").concat(m)]||u[m]||g[m]||r;return n?a.createElement(d,o(o({ref:t},p),{},{components:n})):a.createElement(d,o({ref:t},p))}));function d(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[u]="string"==typeof e?e:i,o[1]=s;for(var c=2;c<r;c++)o[c]=n[c];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}m.displayName="MDXCreateElement"},5837:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>c});var a=n(7462),i=(n(7294),n(3905));const r={title:"Tagging Guidelines"},o=void 0,s={unversionedId:"best-practices",id:"version-v0.6.x/best-practices",title:"Tagging Guidelines",description:"There are some patterns and practices you may want to consider when using Copa to patch images. Remember that these are suggestions that may not fit into your workflow, but we think that staying as close as possible to these practices offers the best experience with Copa.",source:"@site/versioned_docs/version-v0.6.x/best-practices.md",sourceDirName:".",slug:"/best-practices",permalink:"/copacetic/website/best-practices",draft:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Tagging Guidelines"},sidebar:"sidebar",previous:{title:"Quick Start",permalink:"/copacetic/website/quick-start"},next:{title:"Troubleshooting",permalink:"/copacetic/website/troubleshooting"}},l={},c=[{value:"Patch from Unmodified image",id:"patch-from-unmodified-image",level:2},{value:"Tagging",id:"tagging",level:2},{value:"Static Incremental Tags",id:"static-incremental-tags",level:3},{value:"Dynamic Tags",id:"dynamic-tags",level:3}],p={toc:c};function u(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"There are some patterns and practices you may want to consider when using Copa to patch images. Remember that these are suggestions that may not fit into your workflow, but we think that staying as close as possible to these practices offers the best experience with Copa."),(0,i.kt)("h2",{id:"patch-from-unmodified-image"},"Patch from Unmodified image"),(0,i.kt)("p",null,"When patching vulnerabilities in an image, it helps to always work from the initial unmodified image. For example, say you have an image tagged ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0")," that contains a vulnerability. You run Copa to patch the image and produce a new image tagged ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0-1"),". Then if another vulnerability shows up in your ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0-1")," image, you should again patch from the unmodified ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0")," image. This will help prevent the buildup of patch layers (",(0,i.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/issues/389"},"discarding subsequent patch layers")," is a potential future enhancement)."),(0,i.kt)("h2",{id:"tagging"},"Tagging"),(0,i.kt)("p",null,"There are a couple possible patterns that you could follow when tagging patched images."),(0,i.kt)("h3",{id:"static-incremental-tags"},"Static Incremental Tags"),(0,i.kt)("p",null,"The first approach you could take is incrementing a number you append to the end of an image tag. For example, if you have an image tagged ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0"),", following patches would be tagged as ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0-1"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0-2"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0-3"),", and so on."),(0,i.kt)("p",null,"With this pattern you are always explicitly aware of the patch state of the image you are using. The downside is that dependabot is currently unable bump to patched images from unmodified images or bump from one patched image to the next."),(0,i.kt)("h3",{id:"dynamic-tags"},"Dynamic Tags"),(0,i.kt)("p",null,"Another option is a static tag that is continually reused as new patches are applied. For example, you could have an initial unmodified image that you've tagged ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0-0")," (in this case the ",(0,i.kt)("inlineCode",{parentName:"p"},"-0")," at the end helps identify the base unpatched image). All following patched images are then tagged as ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0"),". You then know that the one tagged image always has the latest patches applied."),(0,i.kt)("p",null,"This method makes it easy to continually consume the latest patched version of an image, but does contain some tradeoffs. First is that without pinning, image digests could change causing unpredictable behavior. Secondly, if an ",(0,i.kt)("inlineCode",{parentName:"p"},"ImagePullPolicy")," is set to ",(0,i.kt)("inlineCode",{parentName:"p"},"IfNotPresent"),", newly patched images would not be pulled since the tag hasn't changed."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/499c1190.5345b6eb.js b/website/assets/js/499c1190.5345b6eb.js new file mode 100644 index 00000000..353b7d6a --- /dev/null +++ b/website/assets/js/499c1190.5345b6eb.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3163],{4701:(e,t,i)=>{i.r(t),i.d(t,{assets:()=>r,contentTitle:()=>o,default:()=>l,frontMatter:()=>s,metadata:()=>c,toc:()=>h});var a=i(4848),n=i(8453);const s={title:"Tagging Guidelines"},o=void 0,c={id:"best-practices",title:"Tagging Guidelines",description:"There are some patterns and practices you may want to consider when using Copa to patch images. Remember that these are suggestions that may not fit into your workflow, but we think that staying as close as possible to these practices offers the best experience with Copa.",source:"@site/versioned_docs/version-v0.6.x/best-practices.md",sourceDirName:".",slug:"/best-practices",permalink:"/copacetic/website/best-practices",draft:!1,unlisted:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Tagging Guidelines"},sidebar:"sidebar",previous:{title:"Quick Start",permalink:"/copacetic/website/quick-start"},next:{title:"Troubleshooting",permalink:"/copacetic/website/troubleshooting"}},r={},h=[{value:"Patch from Unmodified image",id:"patch-from-unmodified-image",level:2},{value:"Tagging",id:"tagging",level:2},{value:"Static Incremental Tags",id:"static-incremental-tags",level:3},{value:"Dynamic Tags",id:"dynamic-tags",level:3}];function d(e){const t={a:"a",code:"code",h2:"h2",h3:"h3",p:"p",...(0,n.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(t.p,{children:"There are some patterns and practices you may want to consider when using Copa to patch images. Remember that these are suggestions that may not fit into your workflow, but we think that staying as close as possible to these practices offers the best experience with Copa."}),"\n",(0,a.jsx)(t.h2,{id:"patch-from-unmodified-image",children:"Patch from Unmodified image"}),"\n",(0,a.jsxs)(t.p,{children:["When patching vulnerabilities in an image, it helps to always work from the initial unmodified image. For example, say you have an image tagged ",(0,a.jsx)(t.code,{children:"nginx:1.24.0"})," that contains a vulnerability. You run Copa to patch the image and produce a new image tagged ",(0,a.jsx)(t.code,{children:"nginx:1.24.0-1"}),". Then if another vulnerability shows up in your ",(0,a.jsx)(t.code,{children:"nginx:1.24.0-1"})," image, you should again patch from the unmodified ",(0,a.jsx)(t.code,{children:"nginx:1.24.0"})," image. This will help prevent the buildup of patch layers (",(0,a.jsx)(t.a,{href:"https://github.com/project-copacetic/copacetic/issues/389",children:"discarding subsequent patch layers"})," is a potential future enhancement)."]}),"\n",(0,a.jsx)(t.h2,{id:"tagging",children:"Tagging"}),"\n",(0,a.jsx)(t.p,{children:"There are a couple possible patterns that you could follow when tagging patched images."}),"\n",(0,a.jsx)(t.h3,{id:"static-incremental-tags",children:"Static Incremental Tags"}),"\n",(0,a.jsxs)(t.p,{children:["The first approach you could take is incrementing a number you append to the end of an image tag. For example, if you have an image tagged ",(0,a.jsx)(t.code,{children:"nginx:1.24.0"}),", following patches would be tagged as ",(0,a.jsx)(t.code,{children:"nginx:1.24.0-1"}),", ",(0,a.jsx)(t.code,{children:"nginx:1.24.0-2"}),", ",(0,a.jsx)(t.code,{children:"nginx:1.24.0-3"}),", and so on."]}),"\n",(0,a.jsx)(t.p,{children:"With this pattern you are always explicitly aware of the patch state of the image you are using. The downside is that dependabot is currently unable bump to patched images from unmodified images or bump from one patched image to the next."}),"\n",(0,a.jsx)(t.h3,{id:"dynamic-tags",children:"Dynamic Tags"}),"\n",(0,a.jsxs)(t.p,{children:["Another option is a static tag that is continually reused as new patches are applied. For example, you could have an initial unmodified image that you've tagged ",(0,a.jsx)(t.code,{children:"nginx:1.24.0-0"})," (in this case the ",(0,a.jsx)(t.code,{children:"-0"})," at the end helps identify the base unpatched image). All following patched images are then tagged as ",(0,a.jsx)(t.code,{children:"nginx:1.24.0"}),". You then know that the one tagged image always has the latest patches applied."]}),"\n",(0,a.jsxs)(t.p,{children:["This method makes it easy to continually consume the latest patched version of an image, but does contain some tradeoffs. First is that without pinning, image digests could change causing unpredictable behavior. Secondly, if an ",(0,a.jsx)(t.code,{children:"ImagePullPolicy"})," is set to ",(0,a.jsx)(t.code,{children:"IfNotPresent"}),", newly patched images would not be pulled since the tag hasn't changed."]})]})}function l(e={}){const{wrapper:t}={...(0,n.R)(),...e.components};return t?(0,a.jsx)(t,{...e,children:(0,a.jsx)(d,{...e})}):d(e)}},8453:(e,t,i)=>{i.d(t,{R:()=>o,x:()=>c});var a=i(6540);const n={},s=a.createContext(n);function o(e){const t=a.useContext(s);return a.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function c(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:o(e.components),a.createElement(s.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/4cb9f763.0128fb5a.js b/website/assets/js/4cb9f763.0128fb5a.js new file mode 100644 index 00000000..40183948 --- /dev/null +++ b/website/assets/js/4cb9f763.0128fb5a.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6615],{3062:(e,t,i)=>{i.r(t),i.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>h,frontMatter:()=>s,metadata:()=>r,toc:()=>l});var n=i(4848),a=i(8453);const s={title:"Design"},o=void 0,r={id:"design",title:"Design",description:"Design Tenets",source:"@site/versioned_docs/version-v0.2.x/design.md",sourceDirName:".",slug:"/design",permalink:"/copacetic/website/v0.2.x/design",draft:!1,unlisted:!1,tags:[],version:"v0.2.x",frontMatter:{title:"Design"},sidebar:"sidebar",previous:{title:"Quick Start",permalink:"/copacetic/website/v0.2.x/quick-start"},next:{title:"FAQ",permalink:"/copacetic/website/v0.2.x/faq"}},c={},l=[{value:"Design Tenets",id:"design-tenets",level:2},{value:"Design Reasoning",id:"design-reasoning",level:2},{value:"Architecture",id:"architecture",level:2},{value:"Implementation",id:"implementation",level:2},{value:"Tradeoffs",id:"tradeoffs",level:2}];function d(e){const t={a:"a",code:"code",em:"em",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(t.h2,{id:"design-tenets",children:"Design Tenets"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update."})," This is a raison d\u2019etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to work with the existing ecosystem of container images."})," The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to allow parties other than the image authors to address container vulnerabilities."})," Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to do one thing well and be composable with other tools and processes."})," Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane."]}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"design-reasoning",children:"Design Reasoning"}),"\n",(0,n.jsxs)(t.p,{children:["The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental ",(0,n.jsx)(t.a,{href:"https://github.com/google/go-containerregistry/blob/main/cmd/crane/rebase.md",children:(0,n.jsx)(t.code,{children:"crane rebase"})}),":"]}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:["Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an ",(0,n.jsx)(t.code,{children:"apt install"})," during image creation will overwrite the dpkg ",(0,n.jsx)(t.code,{children:"status"})," file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners."]}),"\n",(0,n.jsx)(t.p,{children:"To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on."}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:["Rebasing also requires that the user knows ",(0,n.jsx)(t.em,{children:"a priori"})," what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets."]}),"\n",(0,n.jsx)(t.p,{children:"While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose."}),"\n",(0,n.jsx)(t.p,{children:"The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well."}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"architecture",children:"Architecture"}),"\n",(0,n.jsx)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),"\n",(0,n.jsx)(t.p,{children:"The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"The data schema of various vulnerability scanners producing the input vulnerability report."}),"\n",(0,n.jsx)(t.li,{children:"The state management of various package managers and process for applying patches appropriately through them."}),"\n"]}),"\n",(0,n.jsxs)(t.p,{children:["Effectively, ",(0,n.jsx)(t.code,{children:"copa patch"})," can be considered a command that bridges an extensible ",(0,n.jsx)(t.code,{children:"Parse"})," action with an extensible ",(0,n.jsx)(t.code,{children:"Apply"})," action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:"]}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-go",children:"type UpdatePackage struct {\n Name string\n Version string\n}\n\ntype UpdateManifest struct {\n OSType string\n OSVersion string\n Arch string\n Updates []UpdatePackage\n}\n\ntype ScanReportParser interface {\n Parse(reportPath string) (*UpdateManifest, error)\n}\n\ntype PackageManager interface {\n Apply(imagePath string, report *UpdateManifest) error\n}\n"})}),"\n",(0,n.jsx)(t.h2,{id:"implementation",children:"Implementation"}),"\n",(0,n.jsx)("img",{title:"buildkit graph execution",src:"/copacetic/website/img/graph-execution.png"}),"\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.code,{children:"copa"})," is a pseudo-frontend to ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit",children:"buildkit"})," implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit/tree/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4#exploring-llb",children:"LLB graphs"})," for buildkit to execute:"]}),"\n",(0,n.jsxs)(t.ol,{children:["\n",(0,n.jsxs)(t.li,{children:["Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions."}),"\n"]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["Actions to fetch and deploy tools that can be injected into the target image to perform the patching.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches."}),"\n",(0,n.jsx)(t.li,{children:"In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image."}),"\n",(0,n.jsx)(t.li,{children:"Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well."}),"\n"]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["Actions to deploy the required patch packages to the target image.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.code,{children:"copa"})," integrates with buildkit at the API level because it uses the ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit/blob/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4/docs/merge%2Bdiff.md",children:"diff and merge"})," graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches."]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"tradeoffs",children:"Tradeoffs"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa."}),"\n",(0,n.jsx)(t.li,{children:"copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this."}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.li,{children:"No support for windows containers given the dependency on buildkit."}),"\n"]})]})}function h(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,n.jsx)(t,{...e,children:(0,n.jsx)(d,{...e})}):d(e)}},8453:(e,t,i)=>{i.d(t,{R:()=>o,x:()=>r});var n=i(6540);const a={},s=n.createContext(a);function o(e){const t=n.useContext(s);return n.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function r(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),n.createElement(s.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/4cb9f763.c7fd5bba.js b/website/assets/js/4cb9f763.c7fd5bba.js deleted file mode 100644 index ee2b3f0c..00000000 --- a/website/assets/js/4cb9f763.c7fd5bba.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2088],{3905:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>u});var i=a(7294);function n(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,i)}return a}function o(e){for(var t=1;t<arguments.length;t++){var a=null!=arguments[t]?arguments[t]:{};t%2?r(Object(a),!0).forEach((function(t){n(e,t,a[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):r(Object(a)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))}))}return e}function s(e,t){if(null==e)return{};var a,i,n=function(e,t){if(null==e)return{};var a,i,n={},r=Object.keys(e);for(i=0;i<r.length;i++)a=r[i],t.indexOf(a)>=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i<r.length;i++)a=r[i],t.indexOf(a)>=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var l=i.createContext({}),p=function(e){var t=i.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},c=function(e){var t=p(e.components);return i.createElement(l.Provider,{value:t},e.children)},d="mdxType",g={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},h=i.forwardRef((function(e,t){var a=e.components,n=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=p(a),h=n,u=d["".concat(l,".").concat(h)]||d[h]||g[h]||r;return a?i.createElement(u,o(o({ref:t},c),{},{components:a})):i.createElement(u,o({ref:t},c))}));function u(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var r=a.length,o=new Array(r);o[0]=h;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[d]="string"==typeof e?e:n,o[1]=s;for(var p=2;p<r;p++)o[p]=a[p];return i.createElement.apply(null,o)}return i.createElement.apply(null,a)}h.displayName="MDXCreateElement"},3656:(e,t,a)=>{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>d,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var i=a(7462),n=(a(7294),a(3905));const r={title:"Design"},o=void 0,s={unversionedId:"design",id:"version-v0.2.x/design",title:"Design",description:"Design Tenets",source:"@site/versioned_docs/version-v0.2.x/design.md",sourceDirName:".",slug:"/design",permalink:"/copacetic/website/v0.2.x/design",draft:!1,tags:[],version:"v0.2.x",frontMatter:{title:"Design"},sidebar:"sidebar",previous:{title:"Quick Start",permalink:"/copacetic/website/v0.2.x/quick-start"},next:{title:"FAQ",permalink:"/copacetic/website/v0.2.x/faq"}},l={},p=[{value:"Design Tenets",id:"design-tenets",level:2},{value:"Design Reasoning",id:"design-reasoning",level:2},{value:"Architecture",id:"architecture",level:2},{value:"Implementation",id:"implementation",level:2},{value:"Tradeoffs",id:"tradeoffs",level:2}],c={toc:p};function d(e){let{components:t,...a}=e;return(0,n.kt)("wrapper",(0,i.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h2",{id:"design-tenets"},"Design Tenets"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update.")," This is a raison d\u2019etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to work with the existing ecosystem of container images.")," The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to allow parties other than the image authors to address container vulnerabilities.")," Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to do one thing well and be composable with other tools and processes.")," Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane."))),(0,n.kt)("h2",{id:"design-reasoning"},"Design Reasoning"),(0,n.kt)("p",null,"The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/google/go-containerregistry/blob/main/cmd/crane/rebase.md"},(0,n.kt)("inlineCode",{parentName:"a"},"crane rebase")),":"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},"Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an ",(0,n.kt)("inlineCode",{parentName:"p"},"apt install")," during image creation will overwrite the dpkg ",(0,n.kt)("inlineCode",{parentName:"p"},"status")," file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners."),(0,n.kt)("p",{parentName:"li"},"To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},"Rebasing also requires that the user knows ",(0,n.kt)("em",{parentName:"p"},"a priori")," what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets."),(0,n.kt)("p",{parentName:"li"},"While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose."),(0,n.kt)("p",{parentName:"li"},"The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well."))),(0,n.kt)("h2",{id:"architecture"},"Architecture"),(0,n.kt)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),(0,n.kt)("p",null,"The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"The data schema of various vulnerability scanners producing the input vulnerability report."),(0,n.kt)("li",{parentName:"ul"},"The state management of various package managers and process for applying patches appropriately through them.")),(0,n.kt)("p",null,"Effectively, ",(0,n.kt)("inlineCode",{parentName:"p"},"copa patch")," can be considered a command that bridges an extensible ",(0,n.kt)("inlineCode",{parentName:"p"},"Parse")," action with an extensible ",(0,n.kt)("inlineCode",{parentName:"p"},"Apply")," action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-go"},"type UpdatePackage struct {\n Name string\n Version string\n}\n\ntype UpdateManifest struct {\n OSType string\n OSVersion string\n Arch string\n Updates []UpdatePackage\n}\n\ntype ScanReportParser interface {\n Parse(reportPath string) (*UpdateManifest, error)\n}\n\ntype PackageManager interface {\n Apply(imagePath string, report *UpdateManifest) error\n}\n")),(0,n.kt)("h2",{id:"implementation"},"Implementation"),(0,n.kt)("img",{title:"buildkit graph execution",src:"/copacetic/website/img/graph-execution.png"}),(0,n.kt)("p",null,(0,n.kt)("inlineCode",{parentName:"p"},"copa")," is a pseudo-frontend to ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit"},"buildkit")," implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit/tree/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4#exploring-llb"},"LLB graphs")," for buildkit to execute:"),(0,n.kt)("ol",null,(0,n.kt)("li",{parentName:"ol"},"Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions."))),(0,n.kt)("li",{parentName:"ol"},"Actions to fetch and deploy tools that can be injected into the target image to perform the patching.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches."),(0,n.kt)("li",{parentName:"ul"},"In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image."),(0,n.kt)("li",{parentName:"ul"},"Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well."))),(0,n.kt)("li",{parentName:"ol"},"Actions to deploy the required patch packages to the target image.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"copa")," integrates with buildkit at the API level because it uses the ",(0,n.kt)("a",{parentName:"li",href:"https://github.com/moby/buildkit/blob/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4/docs/merge%2Bdiff.md"},"diff and merge")," graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.")))),(0,n.kt)("h2",{id:"tradeoffs"},"Tradeoffs"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa."),(0,n.kt)("li",{parentName:"ul"},"copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this."))),(0,n.kt)("li",{parentName:"ul"},"No support for windows containers given the dependency on buildkit.")))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/4d54d076.05bf40fc.js b/website/assets/js/4d54d076.05bf40fc.js deleted file mode 100644 index b9e00ac0..00000000 --- a/website/assets/js/4d54d076.05bf40fc.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7080],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var o=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function r(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?a(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):a(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,o,i=function(e,t){if(null==e)return{};var n,o,i={},a=Object.keys(e);for(o=0;o<a.length;o++)n=a[o],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o<a.length;o++)n=a[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=o.createContext({}),c=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},u=function(e){var t=c(e.components);return o.createElement(l.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),p=c(n),m=i,h=p["".concat(l,".").concat(m)]||p[m]||d[m]||a;return n?o.createElement(h,r(r({ref:t},u),{},{components:n})):o.createElement(h,r({ref:t},u))}));function h(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,r=new Array(a);r[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[p]="string"==typeof e?e:i,r[1]=s;for(var c=2;c<a;c++)r[c]=n[c];return o.createElement.apply(null,r)}return o.createElement.apply(null,n)}m.displayName="MDXCreateElement"},1933:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>p,frontMatter:()=>a,metadata:()=>s,toc:()=>c});var o=n(7462),i=(n(7294),n(3905));const a={title:"Contributing"},r=void 0,s={unversionedId:"contributing",id:"contributing",title:"Contributing",description:"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.",source:"@site/docs/contributing.md",sourceDirName:".",slug:"/contributing",permalink:"/copacetic/website/next/contributing",draft:!1,tags:[],version:"current",frontMatter:{title:"Contributing"},sidebar:"sidebar",previous:{title:"Scanner Plugins",permalink:"/copacetic/website/next/scanner-plugins"},next:{title:"Code of Conduct",permalink:"/copacetic/website/next/code-of-conduct"}},l={},c=[{value:"Bi-Weekly Community Meeting",id:"bi-weekly-community-meeting",level:2},{value:"Slack",id:"slack",level:2},{value:"Contributing Issues",id:"contributing-issues",level:2},{value:"Contributing Code",id:"contributing-code",level:2},{value:"Getting Started",id:"getting-started",level:3},{value:"Visual Studio Code Development Container",id:"visual-studio-code-development-container",level:3},{value:"Prerequisites",id:"prerequisites",level:4},{value:"Personalizing user settings in a dev container",id:"personalizing-user-settings-in-a-dev-container",level:4},{value:"Tests",id:"tests",level:3},{value:"Pull Requests",id:"pull-requests",level:3},{value:"Developer Certificate of Origin (DCO)",id:"developer-certificate-of-origin-dco",level:2},{value:"I didn't sign my commit, now what?",id:"i-didnt-sign-my-commit-now-what",level:3},{value:"Code of Conduct",id:"code-of-conduct",level:2}],u={toc:c};function p(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,o.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"Welcome! We are very happy to accept community contributions to the project, whether through ",(0,i.kt)("a",{parentName:"p",href:"#contributing-issues"},"filing issues")," or ",(0,i.kt)("a",{parentName:"p",href:"#contributing-code"},"code")," in the form of ",(0,i.kt)("a",{parentName:"p",href:"#pull-requests"},"Pull Requests"),". Please note that by participating in this project, you agree to abide by the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/next/code-of-conduct"},"Code of Conduct"),", as well as the terms of the ",(0,i.kt)("a",{parentName:"p",href:"#developer-certificate-of-origin-dco"},"Developer Certificate of Origin"),"."),(0,i.kt)("h2",{id:"bi-weekly-community-meeting"},"Bi-Weekly Community Meeting"),(0,i.kt)("p",null,"A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join ",(0,i.kt)("a",{parentName:"p",href:"https://docs.google.com/document/d/1QdskbeCtgKcdWYHI6EXkLFxyzTCyVT6e8MgB3CaAhWI/edit?usp=sharing"},"here")),(0,i.kt)("h2",{id:"slack"},"Slack"),(0,i.kt)("p",null,"To discuss issues with Copa, features, or development, you can join the ",(0,i.kt)("inlineCode",{parentName:"p"},"#copa")," channel on the ",(0,i.kt)("a",{parentName:"p",href:"https://communityinviter.com/apps/opencontainers/join-the-oci-community"},"OCI Slack"),"."),(0,i.kt)("h2",{id:"contributing-issues"},"Contributing Issues"),(0,i.kt)("p",null,"Before opening any new issues, please search our ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/issues"},"existing GitHub issues")," to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead."),(0,i.kt)("p",null,"When opening an issue, please select the most appropriate template for what you're contributing:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Bug Report:")," If you would like to report the project or tool behaving in unexpected ways."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Documentation Improvement:")," If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Request:")," If you have a feature request, suggestion, or a even a design proposal to review."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Question:")," If you would like to ask the maintainers a question about the project.")),(0,i.kt)("h2",{id:"contributing-code"},"Contributing Code"),(0,i.kt)("h3",{id:"getting-started"},"Getting Started"),(0,i.kt)("p",null,"Follow the instructions to either:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/copacetic/website/next/installation"},"Setup your dev environment to build copa"),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"#visual-studio-code-development-container"},"Use the copa development container")," in ",(0,i.kt)("a",{parentName:"li",href:"https://code.visualstudio.com/"},"Visual Studio Code"),".")),(0,i.kt)("p",null,"For an overview of the project components, refer to the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/next/design"},"copa design")," document."),(0,i.kt)("h3",{id:"visual-studio-code-development-container"},"Visual Studio Code Development Container"),(0,i.kt)("p",null,(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/"},"VSCode")," supports development in a containerized environment through its ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/docs/remote/containers"},"Remote - Container extension"),". This folder provides a development container which encapsulates the dependencies specified in the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/next/installation"},"instructions to build and run copa"),"."),(0,i.kt)("h4",{id:"prerequisites"},"Prerequisites"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://docs.docker.com/get-docker/"},"Docker"),(0,i.kt)("blockquote",{parentName:"li"},(0,i.kt)("p",{parentName:"blockquote"},"For Windows users, enabling ",(0,i.kt)("a",{parentName:"p",href:"https://docs.docker.com/docker-for-windows/wsl/"},"WSL2 back-end integration with Docker")," is recommended."))),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://code.visualstudio.com/"},"Visual Studio Code")),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers"},"Visual Studio Code Remote - Containers extension"))),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},(0,i.kt)("strong",{parentName:"p"},"\u26a0 If running via Docker Desktop for Windows")),(0,i.kt)("p",{parentName:"blockquote"},"Note that the ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user"},"mounted workspace files appear owned by ",(0,i.kt)("inlineCode",{parentName:"a"},"root"))," in the dev container, which will cause ",(0,i.kt)("inlineCode",{parentName:"p"},"git")," commands to fail with a ",(0,i.kt)("inlineCode",{parentName:"p"},"fatal: detected dubious ownership in a repository")," error due to ",(0,i.kt)("a",{parentName:"p",href:"https://git-scm.com/docs/git-config/2.35.2#Documentation/git-config.txt-safedirectory"},"safe.directory")," checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the ",(0,i.kt)("inlineCode",{parentName:"p"},"vscode")," user:"),(0,i.kt)("pre",{parentName:"blockquote"},(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"sudo chown -R vscode:vscode /workspace/copacetic\n"))),(0,i.kt)("h4",{id:"personalizing-user-settings-in-a-dev-container"},"Personalizing user settings in a dev container"),(0,i.kt)("p",null,"VSCode supports applying your user settings, such as your ",(0,i.kt)("inlineCode",{parentName:"p"},".gitconfig"),", to a dev container through the use of ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories"},"dotfiles repositories"),". This can be done through your own VSCode ",(0,i.kt)("inlineCode",{parentName:"p"},"settings.json")," file without changing the dev container image or configuration."),(0,i.kt)("h3",{id:"tests"},"Tests"),(0,i.kt)("p",null,"Once you can successfully ",(0,i.kt)("inlineCode",{parentName:"p"},"make")," the project, any code contributions should also successfully:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Pass unit tests via ",(0,i.kt)("inlineCode",{parentName:"li"},"make test"),"."),(0,i.kt)("li",{parentName:"ul"},"Lint cleanly via ",(0,i.kt)("inlineCode",{parentName:"li"},"make lint"),".")),(0,i.kt)("p",null,"Pull requests will also be expected to pass the PR functional tests specified by ",(0,i.kt)("inlineCode",{parentName:"p"},".github/workflows/build.yml"),"."),(0,i.kt)("h3",{id:"pull-requests"},"Pull Requests"),(0,i.kt)("p",null,"If you'd like to start contributing code to the project, you can search for ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/labels/good%20first%20issue"},"issues with the ",(0,i.kt)("inlineCode",{parentName:"a"},"good first issue")," label"),". Other kinds of PR contributions we would look for include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Fixes for bugs and other correctness issues."),(0,i.kt)("li",{parentName:"ul"},"Docs and other content improvements (e.g. samples)."),(0,i.kt)("li",{parentName:"ul"},"Extensions to support parsing new scanning report formats."),(0,i.kt)("li",{parentName:"ul"},"Extensions to support patching images based on new distros or using new package managers.")),(0,i.kt)("p",null,"For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Architectural changes (e.g. breaking interfaces or violations of ",(0,i.kt)("a",{parentName:"li",href:"/copacetic/website/next/design"},"this project's design tenets"),")."),(0,i.kt)("li",{parentName:"ul"},"Unsolicited features that significantly expand the functional scope of the tool.")),(0,i.kt)("p",null,"Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format"},"Angular commit message format")," for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs."),(0,i.kt)("p",null,"We suggest:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Use the standard header format of ",(0,i.kt)("inlineCode",{parentName:"li"},'"<type>: <short summary>"')," where the ",(0,i.kt)("inlineCode",{parentName:"li"},"<type>")," is one of the following:",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"build:")," Changes that affect the build system or external dependencies"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"ci:")," Changes to the GitHub workflows and configurations"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"docs:")," Documentation only changes"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"feat:")," A new feature"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"fix:")," A bug fix"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"perf:")," A code change that improves performance"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"refactor:")," A code change that neither fixes a bug nor adds a feature"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"test:")," Adding missing tests or correcting existing tests"))),(0,i.kt)("li",{parentName:"ul"},"Use a ",(0,i.kt)("a",{parentName:"li",href:"https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html"},"concise, imperative description")," of the changes included in the ",(0,i.kt)("inlineCode",{parentName:"li"},"<short summary>")," of the header, the body of the PR, and generally in your commit messages."),(0,i.kt)("li",{parentName:"ul"},"Use ",(0,i.kt)("a",{parentName:"li",href:"https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests"},"GitHub keywords")," in the footer of your PR description, such as ",(0,i.kt)("inlineCode",{parentName:"li"},"closes")," to automatically close issues the PR intends to address.")),(0,i.kt)("h2",{id:"developer-certificate-of-origin-dco"},"Developer Certificate of Origin (DCO)"),(0,i.kt)("p",null,"The ",(0,i.kt)("a",{parentName:"p",href:"https://wiki.linuxfoundation.org/dco"},"Developer Certificate of Origin")," (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the ",(0,i.kt)("a",{parentName:"p",href:"https://developercertificate.org/"},"full text of the DCO"),", reformatted for readability:"),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},"By making a contribution to this project, I certify that:"),(0,i.kt)("p",{parentName:"blockquote"},"(a) The contribution was created in whole or in part by me and I\nhave the right to submit it under the open source license\nindicated in the file; or"),(0,i.kt)("p",{parentName:"blockquote"},"(b) The contribution is based upon previous work that, to the best\nof my knowledge, is covered under an appropriate open source\nlicense and I have the right under that license to submit that\nwork with modifications, whether created in whole or in part\nby me, under the same open source license (unless I am\npermitted to submit under a different license), as indicated\nin the file; or"),(0,i.kt)("p",{parentName:"blockquote"},"(c) The contribution was provided directly to me by some other\nperson who certified (a), (b) or (c) and I have not modified\nit."),(0,i.kt)("p",{parentName:"blockquote"},"(d) I understand and agree that this project and the contribution\nare public and that a record of the contribution (including all\npersonal information I submit with it, including my sign-off) is\nmaintained indefinitely and may be redistributed consistent with\nthis project or the open source license(s) involved.")),(0,i.kt)("p",null,"Contributors ",(0,i.kt)("em",{parentName:"p"},"sign-off")," that they adhere to these requirements by adding a ",(0,i.kt)("inlineCode",{parentName:"p"},"Signed-off-by")," line to commit messages."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-text"},"This is my commit message\n\nSigned-off-by: Random J Developer <random@developer.example.org>\n")),(0,i.kt)("p",null,"Git even has a ",(0,i.kt)("inlineCode",{parentName:"p"},"-s")," command line option to append this automatically to your commit message:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"git commit -s -m 'This is my commit message'\n")),(0,i.kt)("p",null,"Pull requests that do not contain a valid ",(0,i.kt)("inlineCode",{parentName:"p"},"Signed-off-by")," line cannot be merged."),(0,i.kt)("h3",{id:"i-didnt-sign-my-commit-now-what"},"I didn't sign my commit, now what?"),(0,i.kt)("p",null,"No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"git switch <branch-name>\ngit commit --amend --no-edit --signoff\ngit push --force-with-lease <remote-name> <branch-name>\n")),(0,i.kt)("h2",{id:"code-of-conduct"},"Code of Conduct"),(0,i.kt)("p",null,"This project has adopted the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/next/code-of-conduct"},"Contributor Covenant Code of Conduct"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/4d54d076.7f68c18d.js b/website/assets/js/4d54d076.7f68c18d.js new file mode 100644 index 00000000..08bd8ff8 --- /dev/null +++ b/website/assets/js/4d54d076.7f68c18d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1459],{6564:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>r,default:()=>h,frontMatter:()=>o,metadata:()=>c,toc:()=>a});var i=n(4848),s=n(8453);const o={title:"Contributing"},r=void 0,c={id:"contributing",title:"Contributing",description:"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.",source:"@site/docs/contributing.md",sourceDirName:".",slug:"/contributing",permalink:"/copacetic/website/next/contributing",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{title:"Contributing"},sidebar:"sidebar",previous:{title:"Scanner Plugins",permalink:"/copacetic/website/next/scanner-plugins"},next:{title:"Code of Conduct",permalink:"/copacetic/website/next/code-of-conduct"}},d={},a=[{value:"Bi-Weekly Community Meeting",id:"bi-weekly-community-meeting",level:2},{value:"Slack",id:"slack",level:2},{value:"Contributing Issues",id:"contributing-issues",level:2},{value:"Contributing Code",id:"contributing-code",level:2},{value:"Getting Started",id:"getting-started",level:3},{value:"Visual Studio Code Development Container",id:"visual-studio-code-development-container",level:3},{value:"Prerequisites",id:"prerequisites",level:4},{value:"Personalizing user settings in a dev container",id:"personalizing-user-settings-in-a-dev-container",level:4},{value:"Tests",id:"tests",level:3},{value:"Pull Requests",id:"pull-requests",level:3},{value:"Developer Certificate of Origin (DCO)",id:"developer-certificate-of-origin-dco",level:2},{value:"I didn't sign my commit, now what?",id:"i-didnt-sign-my-commit-now-what",level:3},{value:"Code of Conduct",id:"code-of-conduct",level:2}];function l(e){const t={a:"a",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h3:"h3",h4:"h4",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsxs)(t.p,{children:["Welcome! We are very happy to accept community contributions to the project, whether through ",(0,i.jsx)(t.a,{href:"#contributing-issues",children:"filing issues"})," or ",(0,i.jsx)(t.a,{href:"#contributing-code",children:"code"})," in the form of ",(0,i.jsx)(t.a,{href:"#pull-requests",children:"Pull Requests"}),". Please note that by participating in this project, you agree to abide by the ",(0,i.jsx)(t.a,{href:"/copacetic/website/next/code-of-conduct",children:"Code of Conduct"}),", as well as the terms of the ",(0,i.jsx)(t.a,{href:"#developer-certificate-of-origin-dco",children:"Developer Certificate of Origin"}),"."]}),"\n",(0,i.jsx)(t.h2,{id:"bi-weekly-community-meeting",children:"Bi-Weekly Community Meeting"}),"\n",(0,i.jsxs)(t.p,{children:["A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join ",(0,i.jsx)(t.a,{href:"https://docs.google.com/document/d/1QdskbeCtgKcdWYHI6EXkLFxyzTCyVT6e8MgB3CaAhWI/edit?usp=sharing",children:"here"})]}),"\n",(0,i.jsx)(t.h2,{id:"slack",children:"Slack"}),"\n",(0,i.jsxs)(t.p,{children:["To discuss issues with Copa, features, or development, you can join the ",(0,i.jsx)(t.code,{children:"#copa"})," channel on the ",(0,i.jsx)(t.a,{href:"https://communityinviter.com/apps/opencontainers/join-the-oci-community",children:"OCI Slack"}),"."]}),"\n",(0,i.jsx)(t.h2,{id:"contributing-issues",children:"Contributing Issues"}),"\n",(0,i.jsxs)(t.p,{children:["Before opening any new issues, please search our ",(0,i.jsx)(t.a,{href:"https://github.com/project-copacetic/copacetic/issues",children:"existing GitHub issues"})," to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead."]}),"\n",(0,i.jsx)(t.p,{children:"When opening an issue, please select the most appropriate template for what you're contributing:"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Bug Report:"})," If you would like to report the project or tool behaving in unexpected ways."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Documentation Improvement:"})," If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Request:"})," If you have a feature request, suggestion, or a even a design proposal to review."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Question:"})," If you would like to ask the maintainers a question about the project."]}),"\n"]}),"\n",(0,i.jsx)(t.h2,{id:"contributing-code",children:"Contributing Code"}),"\n",(0,i.jsx)(t.h3,{id:"getting-started",children:"Getting Started"}),"\n",(0,i.jsx)(t.p,{children:"Follow the instructions to either:"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.a,{href:"/copacetic/website/next/installation",children:"Setup your dev environment to build copa"}),"."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.a,{href:"#visual-studio-code-development-container",children:"Use the copa development container"})," in ",(0,i.jsx)(t.a,{href:"https://code.visualstudio.com/",children:"Visual Studio Code"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(t.p,{children:["For an overview of the project components, refer to the ",(0,i.jsx)(t.a,{href:"/copacetic/website/next/design",children:"copa design"})," document."]}),"\n",(0,i.jsx)(t.h3,{id:"visual-studio-code-development-container",children:"Visual Studio Code Development Container"}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.a,{href:"https://code.visualstudio.com/",children:"VSCode"})," supports development in a containerized environment through its ",(0,i.jsx)(t.a,{href:"https://code.visualstudio.com/docs/remote/containers",children:"Remote - Container extension"}),". This folder provides a development container which encapsulates the dependencies specified in the ",(0,i.jsx)(t.a,{href:"/copacetic/website/next/installation",children:"instructions to build and run copa"}),"."]}),"\n",(0,i.jsx)(t.h4,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,i.jsxs)(t.ol,{children:["\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.a,{href:"https://docs.docker.com/get-docker/",children:"Docker"}),"\n",(0,i.jsxs)(t.blockquote,{children:["\n",(0,i.jsxs)(t.p,{children:["For Windows users, enabling ",(0,i.jsx)(t.a,{href:"https://docs.docker.com/docker-for-windows/wsl/",children:"WSL2 back-end integration with Docker"})," is recommended."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://code.visualstudio.com/",children:"Visual Studio Code"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers",children:"Visual Studio Code Remote - Containers extension"})}),"\n"]}),"\n",(0,i.jsxs)(t.blockquote,{children:["\n",(0,i.jsx)(t.p,{children:(0,i.jsx)(t.strong,{children:"\u26a0 If running via Docker Desktop for Windows"})}),"\n",(0,i.jsxs)(t.p,{children:["Note that the ",(0,i.jsxs)(t.a,{href:"https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user",children:["mounted workspace files appear owned by ",(0,i.jsx)(t.code,{children:"root"})]})," in the dev container, which will cause ",(0,i.jsx)(t.code,{children:"git"})," commands to fail with a ",(0,i.jsx)(t.code,{children:"fatal: detected dubious ownership in a repository"})," error due to ",(0,i.jsx)(t.a,{href:"https://git-scm.com/docs/git-config/2.35.2#Documentation/git-config.txt-safedirectory",children:"safe.directory"})," checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the ",(0,i.jsx)(t.code,{children:"vscode"})," user:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:"sudo chown -R vscode:vscode /workspace/copacetic\n"})}),"\n"]}),"\n",(0,i.jsx)(t.h4,{id:"personalizing-user-settings-in-a-dev-container",children:"Personalizing user settings in a dev container"}),"\n",(0,i.jsxs)(t.p,{children:["VSCode supports applying your user settings, such as your ",(0,i.jsx)(t.code,{children:".gitconfig"}),", to a dev container through the use of ",(0,i.jsx)(t.a,{href:"https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories",children:"dotfiles repositories"}),". This can be done through your own VSCode ",(0,i.jsx)(t.code,{children:"settings.json"})," file without changing the dev container image or configuration."]}),"\n",(0,i.jsx)(t.h3,{id:"tests",children:"Tests"}),"\n",(0,i.jsxs)(t.p,{children:["Once you can successfully ",(0,i.jsx)(t.code,{children:"make"})," the project, any code contributions should also successfully:"]}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:["Pass unit tests via ",(0,i.jsx)(t.code,{children:"make test"}),"."]}),"\n",(0,i.jsxs)(t.li,{children:["Lint cleanly via ",(0,i.jsx)(t.code,{children:"make lint"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(t.p,{children:["Pull requests will also be expected to pass the PR functional tests specified by ",(0,i.jsx)(t.code,{children:".github/workflows/build.yml"}),"."]}),"\n",(0,i.jsx)(t.h3,{id:"pull-requests",children:"Pull Requests"}),"\n",(0,i.jsxs)(t.p,{children:["If you'd like to start contributing code to the project, you can search for ",(0,i.jsxs)(t.a,{href:"https://github.com/project-copacetic/copacetic/labels/good%20first%20issue",children:["issues with the ",(0,i.jsx)(t.code,{children:"good first issue"})," label"]}),". Other kinds of PR contributions we would look for include:"]}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsx)(t.li,{children:"Fixes for bugs and other correctness issues."}),"\n",(0,i.jsx)(t.li,{children:"Docs and other content improvements (e.g. samples)."}),"\n",(0,i.jsx)(t.li,{children:"Extensions to support parsing new scanning report formats."}),"\n",(0,i.jsx)(t.li,{children:"Extensions to support patching images based on new distros or using new package managers."}),"\n"]}),"\n",(0,i.jsx)(t.p,{children:"For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:["Architectural changes (e.g. breaking interfaces or violations of ",(0,i.jsx)(t.a,{href:"/copacetic/website/next/design",children:"this project's design tenets"}),")."]}),"\n",(0,i.jsx)(t.li,{children:"Unsolicited features that significantly expand the functional scope of the tool."}),"\n"]}),"\n",(0,i.jsxs)(t.p,{children:["Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the ",(0,i.jsx)(t.a,{href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format",children:"Angular commit message format"})," for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs."]}),"\n",(0,i.jsx)(t.p,{children:"We suggest:"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:["Use the standard header format of ",(0,i.jsx)(t.code,{children:'"<type>: <short summary>"'})," where the ",(0,i.jsx)(t.code,{children:"<type>"})," is one of the following:","\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"build:"})," Changes that affect the build system or external dependencies"]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"ci:"})," Changes to the GitHub workflows and configurations"]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"docs:"})," Documentation only changes"]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"feat:"})," A new feature"]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"fix:"})," A bug fix"]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"perf:"})," A code change that improves performance"]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"refactor:"})," A code change that neither fixes a bug nor adds a feature"]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"test:"})," Adding missing tests or correcting existing tests"]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["Use a ",(0,i.jsx)(t.a,{href:"https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html",children:"concise, imperative description"})," of the changes included in the ",(0,i.jsx)(t.code,{children:"<short summary>"})," of the header, the body of the PR, and generally in your commit messages."]}),"\n",(0,i.jsxs)(t.li,{children:["Use ",(0,i.jsx)(t.a,{href:"https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests",children:"GitHub keywords"})," in the footer of your PR description, such as ",(0,i.jsx)(t.code,{children:"closes"})," to automatically close issues the PR intends to address."]}),"\n"]}),"\n",(0,i.jsx)(t.h2,{id:"developer-certificate-of-origin-dco",children:"Developer Certificate of Origin (DCO)"}),"\n",(0,i.jsxs)(t.p,{children:["The ",(0,i.jsx)(t.a,{href:"https://wiki.linuxfoundation.org/dco",children:"Developer Certificate of Origin"})," (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the ",(0,i.jsx)(t.a,{href:"https://developercertificate.org/",children:"full text of the DCO"}),", reformatted for readability:"]}),"\n",(0,i.jsxs)(t.blockquote,{children:["\n",(0,i.jsx)(t.p,{children:"By making a contribution to this project, I certify that:"}),"\n",(0,i.jsx)(t.p,{children:"(a) The contribution was created in whole or in part by me and I\nhave the right to submit it under the open source license\nindicated in the file; or"}),"\n",(0,i.jsx)(t.p,{children:"(b) The contribution is based upon previous work that, to the best\nof my knowledge, is covered under an appropriate open source\nlicense and I have the right under that license to submit that\nwork with modifications, whether created in whole or in part\nby me, under the same open source license (unless I am\npermitted to submit under a different license), as indicated\nin the file; or"}),"\n",(0,i.jsx)(t.p,{children:"(c) The contribution was provided directly to me by some other\nperson who certified (a), (b) or (c) and I have not modified\nit."}),"\n",(0,i.jsx)(t.p,{children:"(d) I understand and agree that this project and the contribution\nare public and that a record of the contribution (including all\npersonal information I submit with it, including my sign-off) is\nmaintained indefinitely and may be redistributed consistent with\nthis project or the open source license(s) involved."}),"\n"]}),"\n",(0,i.jsxs)(t.p,{children:["Contributors ",(0,i.jsx)(t.em,{children:"sign-off"})," that they adhere to these requirements by adding a ",(0,i.jsx)(t.code,{children:"Signed-off-by"})," line to commit messages."]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-text",children:"This is my commit message\n\nSigned-off-by: Random J Developer <random@developer.example.org>\n"})}),"\n",(0,i.jsxs)(t.p,{children:["Git even has a ",(0,i.jsx)(t.code,{children:"-s"})," command line option to append this automatically to your commit message:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:"git commit -s -m 'This is my commit message'\n"})}),"\n",(0,i.jsxs)(t.p,{children:["Pull requests that do not contain a valid ",(0,i.jsx)(t.code,{children:"Signed-off-by"})," line cannot be merged."]}),"\n",(0,i.jsx)(t.h3,{id:"i-didnt-sign-my-commit-now-what",children:"I didn't sign my commit, now what?"}),"\n",(0,i.jsx)(t.p,{children:"No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:"git switch <branch-name>\ngit commit --amend --no-edit --signoff\ngit push --force-with-lease <remote-name> <branch-name>\n"})}),"\n",(0,i.jsx)(t.h2,{id:"code-of-conduct",children:"Code of Conduct"}),"\n",(0,i.jsxs)(t.p,{children:["This project has adopted the ",(0,i.jsx)(t.a,{href:"/copacetic/website/next/code-of-conduct",children:"Contributor Covenant Code of Conduct"}),"."]})]})}function h(e={}){const{wrapper:t}={...(0,s.R)(),...e.components};return t?(0,i.jsx)(t,{...e,children:(0,i.jsx)(l,{...e})}):l(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>r,x:()=>c});var i=n(6540);const s={},o=i.createContext(s);function r(e){const t=i.useContext(o);return i.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function c(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),i.createElement(o.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/4d7fa969.9b8c384b.js b/website/assets/js/4d7fa969.9b8c384b.js new file mode 100644 index 00000000..36aa993f --- /dev/null +++ b/website/assets/js/4d7fa969.9b8c384b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[105],{5405:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>s,contentTitle:()=>l,default:()=>h,frontMatter:()=>o,metadata:()=>a,toc:()=>c});var t=i(4848),r=i(8453);const o={title:"Troubleshooting"},l=void 0,a={id:"troubleshooting",title:"Troubleshooting",description:"Filtering Vulnerabilities",source:"@site/versioned_docs/version-v0.5.x/troubleshooting.md",sourceDirName:".",slug:"/troubleshooting",permalink:"/copacetic/website/v0.5.x/troubleshooting",draft:!1,unlisted:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Troubleshooting"},sidebar:"sidebar",previous:{title:"Output",permalink:"/copacetic/website/v0.5.x/output"},next:{title:"Design",permalink:"/copacetic/website/v0.5.x/design"}},s={},c=[{value:"Filtering Vulnerabilities",id:"filtering-vulnerabilities",level:2},{value:"Rego Policy",id:"rego-policy",level:3},{value:"Ignore File",id:"ignore-file",level:3}];function g(e){const n={a:"a",code:"code",h2:"h2",h3:"h3",p:"p",pre:"pre",...(0,r.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h2,{id:"filtering-vulnerabilities",children:"Filtering Vulnerabilities"}),"\n",(0,t.jsx)(n.p,{children:"You might want to filter/ignore some of the vulnerabilities while patching. To do so, you need to first filter those undesired vulnerabilities from your scanner output."}),"\n",(0,t.jsx)(n.p,{children:"For Trivy, vulnerabilities can be filtered by the following 2 ways:"}),"\n",(0,t.jsx)(n.h3,{id:"rego-policy",children:"Rego Policy"}),"\n",(0,t.jsx)(n.p,{children:"An example rego file which demonstrates how to ignore certain Vulnerability IDs or Package Names:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'$ cat trivy_ignore.rego\n\npackage trivy\n\nimport data.lib.trivy\n\ndefault ignore = false\n\n\n# Ignore the following Vulnerability IDs\nignore_vulnerability_ids := {\n "CVE-2018-14618"\n}\n# Ignore the following Package Names\nignore_pkgs := {"bash", "vim"}\n\n\n# For ignoring vulnID\nignore {\n input.VulnerabilityID == ignore_vulnerability_ids[_]\n}\n# For ignoring pkgName\nignore {\n\tinput.PkgName == ignore_pkgs[_]\n}\n\n'})}),"\n",(0,t.jsxs)(n.p,{children:["After adding the above rego file, run the image scan with the ",(0,t.jsx)(n.code,{children:"--ignore-policy"})," flag followed by the file name to ignore them while scanning:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --ignore-policy trivy_ignore.rego ruby:2.4.0\n"})}),"\n",(0,t.jsx)(n.p,{children:'In the above example, the vulnerability "CVE-2018-14618" and the packages "bash" & "vim" are ignored while scanning, and hence patching the image.'}),"\n",(0,t.jsx)(n.h3,{id:"ignore-file",children:"Ignore File"}),"\n",(0,t.jsxs)(n.p,{children:["Use a ",(0,t.jsx)(n.code,{children:".trivyignore"})," file to list all the vulnerabilities you want to ignore."]}),"\n",(0,t.jsx)(n.p,{children:"Example:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"$ cat .trivyignore\n\n# Accept the risk\nCVE-2018-14618\n"})}),"\n",(0,t.jsx)(n.p,{children:"In the above example, the vulnerability CVE-2018-14618 is ignored while scanning, and hence while patching the image."}),"\n",(0,t.jsxs)(n.p,{children:["For a more detailed explanation on how to ignore certain vulnerabilities with Trivy, please refer to the official documentation ",(0,t.jsx)(n.a,{href:"https://aquasecurity.github.io/trivy/v0.44/docs/configuration/filtering/",children:"here"}),"."]})]})}function h(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(g,{...e})}):g(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>l,x:()=>a});var t=i(6540);const r={},o=t.createContext(r);function l(e){const n=t.useContext(o);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:l(e.components),t.createElement(o.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/4d7fa969.a26a2980.js b/website/assets/js/4d7fa969.a26a2980.js deleted file mode 100644 index 50c992e3..00000000 --- a/website/assets/js/4d7fa969.a26a2980.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9848],{3905:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>b});var r=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function a(e,t){if(null==e)return{};var n,r,i=function(e,t){if(null==e)return{};var n,r,i={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var c=r.createContext({}),u=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},s=function(e){var t=u(e.components);return r.createElement(c.Provider,{value:t},e.children)},p="mdxType",g={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,c=e.parentName,s=a(e,["components","mdxType","originalType","parentName"]),p=u(n),f=i,b=p["".concat(c,".").concat(f)]||p[f]||g[f]||o;return n?r.createElement(b,l(l({ref:t},s),{},{components:n})):r.createElement(b,l({ref:t},s))}));function b(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,l=new Array(o);l[0]=f;var a={};for(var c in t)hasOwnProperty.call(t,c)&&(a[c]=t[c]);a.originalType=e,a[p]="string"==typeof e?e:i,l[1]=a;for(var u=2;u<o;u++)l[u]=n[u];return r.createElement.apply(null,l)}return r.createElement.apply(null,n)}f.displayName="MDXCreateElement"},8703:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>l,default:()=>p,frontMatter:()=>o,metadata:()=>a,toc:()=>u});var r=n(7462),i=(n(7294),n(3905));const o={title:"Troubleshooting"},l=void 0,a={unversionedId:"troubleshooting",id:"version-v0.5.x/troubleshooting",title:"Troubleshooting",description:"Filtering Vulnerabilities",source:"@site/versioned_docs/version-v0.5.x/troubleshooting.md",sourceDirName:".",slug:"/troubleshooting",permalink:"/copacetic/website/v0.5.x/troubleshooting",draft:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Troubleshooting"},sidebar:"sidebar",previous:{title:"Output",permalink:"/copacetic/website/v0.5.x/output"},next:{title:"Design",permalink:"/copacetic/website/v0.5.x/design"}},c={},u=[{value:"Filtering Vulnerabilities",id:"filtering-vulnerabilities",level:2},{value:"Rego Policy",id:"rego-policy",level:3},{value:"Ignore File",id:"ignore-file",level:3}],s={toc:u};function p(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,r.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h2",{id:"filtering-vulnerabilities"},"Filtering Vulnerabilities"),(0,i.kt)("p",null,"You might want to filter/ignore some of the vulnerabilities while patching. To do so, you need to first filter those undesired vulnerabilities from your scanner output."),(0,i.kt)("p",null,"For Trivy, vulnerabilities can be filtered by the following 2 ways:"),(0,i.kt)("h3",{id:"rego-policy"},"Rego Policy"),(0,i.kt)("p",null,"An example rego file which demonstrates how to ignore certain Vulnerability IDs or Package Names:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},'$ cat trivy_ignore.rego\n\npackage trivy\n\nimport data.lib.trivy\n\ndefault ignore = false\n\n\n# Ignore the following Vulnerability IDs\nignore_vulnerability_ids := {\n "CVE-2018-14618"\n}\n# Ignore the following Package Names\nignore_pkgs := {"bash", "vim"}\n\n\n# For ignoring vulnID\nignore {\n input.VulnerabilityID == ignore_vulnerability_ids[_]\n}\n# For ignoring pkgName\nignore {\n input.PkgName == ignore_pkgs[_]\n}\n\n')),(0,i.kt)("p",null,"After adding the above rego file, run the image scan with the ",(0,i.kt)("inlineCode",{parentName:"p"},"--ignore-policy")," flag followed by the file name to ignore them while scanning:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --ignore-policy trivy_ignore.rego ruby:2.4.0\n")),(0,i.kt)("p",null,'In the above example, the vulnerability "CVE-2018-14618" and the packages "bash" & "vim" are ignored while scanning, and hence patching the image.'),(0,i.kt)("h3",{id:"ignore-file"},"Ignore File"),(0,i.kt)("p",null,"Use a ",(0,i.kt)("inlineCode",{parentName:"p"},".trivyignore")," file to list all the vulnerabilities you want to ignore."),(0,i.kt)("p",null,"Example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"$ cat .trivyignore\n\n# Accept the risk\nCVE-2018-14618\n")),(0,i.kt)("p",null,"In the above example, the vulnerability CVE-2018-14618 is ignored while scanning, and hence while patching the image."),(0,i.kt)("p",null,"For a more detailed explanation on how to ignore certain vulnerabilities with Trivy, please refer to the official documentation ",(0,i.kt)("a",{parentName:"p",href:"https://aquasecurity.github.io/trivy/v0.44/docs/configuration/filtering/"},"here"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/4e9dbf89.413c7098.js b/website/assets/js/4e9dbf89.413c7098.js new file mode 100644 index 00000000..b6f2dcbb --- /dev/null +++ b/website/assets/js/4e9dbf89.413c7098.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5336],{8214:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>s,default:()=>h,frontMatter:()=>o,metadata:()=>r,toc:()=>c});var i=n(4848),a=n(8453);const o={},s="Development and Testing Tips",r={id:"development-tips",title:"Development and Testing Tips",description:"This document provides some tips and tricks for devs to better understand what is happening under the hood of copa.",source:"@site/versioned_docs/version-v0.6.x/development-tips.md",sourceDirName:".",slug:"/development-tips",permalink:"/copacetic/website/development-tips",draft:!1,unlisted:!1,tags:[],version:"v0.6.x",frontMatter:{},sidebar:"sidebar",previous:{title:"Design",permalink:"/copacetic/website/design"},next:{title:"Maintainer Guidelines",permalink:"/copacetic/website/maintainer-guidelines"}},d={},c=[{value:"Use the <code>--debug</code> flag with <code>copa patch</code>",id:"use-the---debug-flag-with-copa-patch",level:2},{value:"Verify the intermediate stages of building a patched image",id:"verify-the-intermediate-stages-of-building-a-patched-image",level:2},{value:"Inspect a Docker image",id:"inspect-a-docker-image",level:2},{value:"Use <code>docker</code> to inspect the metadata of the image",id:"use-docker-to-inspect-the-metadata-of-the-image",level:3},{value:"Use <code>dive</code> to inspect the filesystem differences at each layer of the image",id:"use-dive-to-inspect-the-filesystem-differences-at-each-layer-of-the-image",level:3},{value:"Extract individual files from the image to inspect them",id:"extract-individual-files-from-the-image-to-inspect-them",level:3},{value:"Use <code>crane</code> to manipulate the image or extract the image filesystem",id:"use-crane-to-manipulate-the-image-or-extract-the-image-filesystem",level:3},{value:"Run scripts interactively in an image",id:"run-scripts-interactively-in-an-image",level:2},{value:"Dump the LLB Graph",id:"dump-the-llb-graph",level:2}];function l(e){const t={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",ul:"ul",...(0,a.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(t.h1,{id:"development-and-testing-tips",children:"Development and Testing Tips"}),"\n",(0,i.jsxs)(t.p,{children:["This document provides some tips and tricks for devs to better understand what is happening under the hood of ",(0,i.jsx)(t.code,{children:"copa"}),"."]}),"\n",(0,i.jsxs)(t.p,{children:["Much of the functionality of ",(0,i.jsx)(t.code,{children:"copa"})," is implemented through the use of the ",(0,i.jsx)(t.a,{href:"https://docs.docker.com/build/buildkit/",children:"BuildKit"})," library, and in particular, direct building a ",(0,i.jsx)(t.a,{href:"https://github.com/moby/buildkit#exploring-llb",children:"Low-Level Build (LLB)"})," intermediate representation. Most patching operations are implemented as a series of LLB stages that form a Directed Acyclic Graph (DAG) to produce the final patched image, and we'll walk through some ways to deal with the opaque nature of each operation in that graph which can otherwise make it difficult to debug or test ",(0,i.jsx)(t.code,{children:"copa"}),"."]}),"\n",(0,i.jsxs)(t.h2,{id:"use-the---debug-flag-with-copa-patch",children:["Use the ",(0,i.jsx)(t.code,{children:"--debug"})," flag with ",(0,i.jsx)(t.code,{children:"copa patch"})]}),"\n",(0,i.jsxs)(t.p,{children:["It's always useful to know that code on the ",(0,i.jsx)(t.code,{children:"copa"})," side is behaving as expected first before diving into the weeds of its interactions with BuildKit. The ",(0,i.jsx)(t.code,{children:"--debug"})," flag will do two useful things when enabled:"]}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:["Log debug state to stdout with the ",(0,i.jsx)(t.code,{children:"DEBU"})," tag, including useful information such as the type of image it expects to be operating on, the list of updates and their versions it expects to apply, and any detailed errors."]}),"\n",(0,i.jsxs)(t.li,{children:["Leave the working folder in place so that you can inspect the contents of the working files ",(0,i.jsx)(t.code,{children:"copa"})," writes for its own use during the patching process."]}),"\n"]}),"\n",(0,i.jsxs)(t.p,{children:["For example, if you run ",(0,i.jsx)(t.code,{children:"copa patch"})," with the ",(0,i.jsx)(t.code,{children:"--debug"})," flag, you'll see something like the following output:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:"$ copa patch -i <image> -r <report> --debug\nDEBU[0000] updates to apply: ...\n...\nWARN[0000] --debug specified, working folder at /var/folders/fx/nbhd5jln1qq3t405hz_hl4000000gn/T/copa-806164554 needs to be manually cleaned up \n"})}),"\n",(0,i.jsxs)(t.p,{children:["The folder specified defaults to the system temp folder unless the ",(0,i.jsx)(t.code,{children:"--working-folder"})," option was specified, and you can delete it with ",(0,i.jsx)(t.code,{children:"rm -r <folder>"})," when you're done. The working folder will usually contain the ",(0,i.jsx)(t.code,{children:"copa-out"})," directory which contains files depending on the ",(0,i.jsx)(t.code,{children:"pkgmgr"})," implementation, such as the probed package state or post-patching package state file for the package manager. Searching for ",(0,i.jsx)(t.code,{children:"SolveToLocal()"})," invocations in the ",(0,i.jsx)(t.code,{children:"copa"})," codebase will show you where these files are written."]}),"\n",(0,i.jsx)(t.h2,{id:"verify-the-intermediate-stages-of-building-a-patched-image",children:"Verify the intermediate stages of building a patched image"}),"\n",(0,i.jsxs)(t.p,{children:["It's often useful to be able to inspect what the output of an intermediate LLB stage would look like after it has executed, and you can perform an analog to ",(0,i.jsx)(t.code,{children:"printf"})," debugging by solving the LLB stage to a Docker image and then inspecting the resulting image:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:'// DEBUG: Solve the LLB stage to a Docker image.\nif err := buildkit.SolveToDocker(ctx, dm.config.Client, &<llb.Stage>, dm.config.ConfigData, dm.config.ImageName+"-<llb.Stage suffix>"); err != nil {\n return nil, err\n}\n'})}),"\n",(0,i.jsxs)(t.p,{children:["For example, if you want to see what the resulting Docker image looks like at the ",(0,i.jsx)(t.code,{children:"busyBoxApplied"})," stage, you can add the ",(0,i.jsx)(t.code,{children:"buildkit.SolveToDocker"})," call to the end of the ",(0,i.jsx)(t.code,{children:"busyBoxApplied"})," stage as follows. The result will be a Docker image with the ",(0,i.jsx)(t.code,{children:"-busyBoxApplied"})," suffix to the tag that you can inspect with the ",(0,i.jsx)(t.code,{children:"docker"})," CLI or ",(0,i.jsx)(t.code,{children:"dive"})," tool:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:'busyBoxApplied := dm.config.ImageState.File(llb.Copy(toolImage, "/bin/busybox", "/bin/busybox"))\nif err := buildkit.SolveToDocker(ctx, dm.config.Client, &busyBoxApplied, dm.config.ConfigData, dm.config.ImageName+"-busyBoxApplied"); err != nil {\n return nil, err\n}\n'})}),"\n",(0,i.jsx)(t.h2,{id:"inspect-a-docker-image",children:"Inspect a Docker image"}),"\n",(0,i.jsxs)(t.h3,{id:"use-docker-to-inspect-the-metadata-of-the-image",children:["Use ",(0,i.jsx)(t.code,{children:"docker"})," to inspect the metadata of the image"]}),"\n",(0,i.jsxs)(t.p,{children:["For a quick check of a Docker image, the built in ",(0,i.jsx)(t.code,{children:"docker"})," CLI commands can be useful:"]}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.a,{href:"https://docs.docker.com/engine/reference/commandline/inspect/",children:(0,i.jsx)(t.code,{children:"docker inspect"})})," can show you the metadata for the image and verifying that the patching process generally preserves the metadata of the original image."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.a,{href:"https://docs.docker.com/engine/reference/commandline/history/",children:(0,i.jsx)(t.code,{children:"docker history"})})," can give you a quick overview of the layers in the image, and with the ",(0,i.jsx)(t.code,{children:"--no-trunc"})," flag, can provide the commands that were run to create each layer."]}),"\n"]}),"\n",(0,i.jsxs)(t.h3,{id:"use-dive-to-inspect-the-filesystem-differences-at-each-layer-of-the-image",children:["Use ",(0,i.jsx)(t.code,{children:"dive"})," to inspect the filesystem differences at each layer of the image"]}),"\n",(0,i.jsxs)(t.p,{children:["Instructions for installing and using the ",(0,i.jsx)(t.code,{children:"dive"})," CLI tool are at ",(0,i.jsx)(t.a,{href:"https://github.com/wagoodman/dive",children:"https://github.com/wagoodman/dive"}),"."]}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"dive"})," provides a simple interface for walking the layers of the image and inspecting the files that were added or changed at each layer with your arrow keys."]}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"Tab"})," will toggle between navigating the layers and the files in the layer."]}),"\n",(0,i.jsxs)(t.li,{children:["Filtering out unmodified files with ",(0,i.jsx)(t.code,{children:"Ctrl+U"})," while in files view will effectively show you the file diff introduced by that layer."]}),"\n"]}),"\n",(0,i.jsx)(t.p,{children:"In particular, if you are adding or changing any of the patching functionality, the diff view of the files in the image can be useful to verify that the expected files have actually been written to the target image."}),"\n",(0,i.jsx)(t.h3,{id:"extract-individual-files-from-the-image-to-inspect-them",children:"Extract individual files from the image to inspect them"}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"dive"})," won't let you read the contents of the files in the image though; to do that, you can use the ",(0,i.jsx)(t.code,{children:"docker cp"})," command to copy the files out of the image to a local folder. Note that ",(0,i.jsx)(t.code,{children:"docker cp"})," only works with containers and not just container images, so you will need to create a container from the image and then copy the files out of the container:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:"id=$(docker create <image name>:<tag>)\ndocker cp $id:<filepath> <destination path>\ndocker rm -v $id\n"})}),"\n",(0,i.jsxs)(t.h3,{id:"use-crane-to-manipulate-the-image-or-extract-the-image-filesystem",children:["Use ",(0,i.jsx)(t.code,{children:"crane"})," to manipulate the image or extract the image filesystem"]}),"\n",(0,i.jsxs)(t.p,{children:["Sometimes it's useful to be able to manipulate the image in ways that ",(0,i.jsx)(t.code,{children:"docker"})," or ",(0,i.jsx)(t.code,{children:"dive"})," don't support, such as extracting the entire image filesystem to a local folder. ",(0,i.jsx)(t.a,{href:"https://github.com/google/go-containerregistry/tree/main/cmd/crane",children:(0,i.jsx)(t.code,{children:"crane"})})," can be useful for this and also provides ",(0,i.jsx)(t.a,{href:"https://github.com/google/go-containerregistry/blob/main/cmd/crane/doc/crane.md",children:"many other convenient utilities"})," for working with container images."]}),"\n",(0,i.jsxs)(t.p,{children:["For instance, to extract the filesystem of the image to a local folder, you can use ",(0,i.jsx)(t.code,{children:"crane export"}),":"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:"crane export <image name>:<tag> - | tar -xvf -\n"})}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"crane"})," is a very flexible tool designed to work well with pipes to existing shell tools. For example, you can also use ",(0,i.jsx)(t.code,{children:"crane"})," to do a full diff between two images as well:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:" diff \\\n <(crane export image:tag - | tar -tvf - | sort) \\\n <(crane export image:tag-patched - | tar -tvf - | sort)\n"})}),"\n",(0,i.jsx)(t.h2,{id:"run-scripts-interactively-in-an-image",children:"Run scripts interactively in an image"}),"\n",(0,i.jsxs)(t.p,{children:["Some of the LLB stages effectively run shell scripts defined by ",(0,i.jsx)(t.code,{children:"copa"})," in the image, and sometimes these need to be debugged or modified. The easiest way to do this is usually just solving the ",(0,i.jsx)(t.code,{children:"llb.Stage"})," of interest to Docker and then running the image interactively with ",(0,i.jsx)(t.code,{children:"docker run"}),":"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:"docker run --rm -it --entrypoint sh <image name>:<tag>-<llb.Stage suffix>\n"})}),"\n",(0,i.jsxs)(t.p,{children:["One thing to note is that the scripts embedded in the ",(0,i.jsx)(t.code,{children:".go"})," files will often have an additional layer of character escapes to make them valid Go strings, so you may need to unescape them before running them interactively."]}),"\n",(0,i.jsx)(t.h2,{id:"dump-the-llb-graph",children:"Dump the LLB Graph"}),"\n",(0,i.jsxs)(t.p,{children:["Ultimately, ",(0,i.jsx)(t.code,{children:"copa"})," is just a tool for building a BuildKit LLB graph, and you may need to understand if the LLB graph being constructed is reasonable or expected. It's helpful to have a basic understanding of how BuildKit and the operations commonly used by ",(0,i.jsx)(t.code,{children:"copa"})," here, so it's good to be familiar with some key resources here:"]}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://pkg.go.dev/github.com/moby/buildkit#section-readme",children:"BuildKit pkg.go.dev README"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://github.com/moby/buildkit/tree/master/docs/dev",children:"BuildKit Developer Docs"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://www.docker.com/blog/mergediff-building-dags-more-efficiently-and-elegantly/",children:"Merge+Diff: Building DAGs More Efficiently and Elegantly"})}),"\n"]}),"\n",(0,i.jsxs)(t.p,{children:["The LLB graph up to any ",(0,i.jsx)(t.code,{children:"llb.Stage"})," can be written out by marshalling it to a ",(0,i.jsx)(t.code,{children:"Definition"})," and enumerating each of the operations to output. Using the ",(0,i.jsx)(t.code,{children:"buildctl"})," implementation of ",(0,i.jsx)(t.a,{href:"https://github.com/moby/buildkit/blob/master/cmd/buildctl/debug/dumpllb.go#L30",children:"dumpLLB"})," as a reference, you can write a function to output the LLB graph as JSON nodes to stdout:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:'import "github.com/moby/buildkit/solver/pb"\n\n// Definition of the LLB graph node to display, modify as desired.\n// This version is what the buildctl tool uses.\ntype llbOp struct {\n Op pb.Op\n Digest digest.Digest\n OpMetadata pb.OpMetadata\n}\n\nfunc outputLLBGraph(ctx context.Context, llbState *llb.State) error {\n // Marshal the llb.State to a LLB definition.\n def, err := llbState.Marshal(ctx)\n if err != nil {\n log.Errorf("Marshal to LLB failed with %s", err)\n return err\n }\n\n // Format each operation node in the LLB definition into a struct.\n var ops []llbOp\n for _, dt := range def.Def {\n var op pb.Op\n if err := (&op).Unmarshal(dt); err != nil {\n return errors.Wrap(err, "failed to parse op")\n }\n hash := digest.FromBytes(dt)\n ent := llbOp{Op: op, Digest: hash, OpMetadata: def.Metadata[hash]}\n ops = append(ops, ent)\n }\n\n // Output the LLB graph as JSON nodes to stdout.\n // Modify as desired to output to file or other formats.\n enc := json.NewEncoder(os.Stdout)\n for _, op := range ops {\n if err := enc.Encode(op); err != nil {\n return err\n }\n }\n return nil\n}\n\n// Within the function (e.g. with the llb.State where you want to dump the LLB graph:\n...\n // DEBUG: dump the LLB graph to stdout\n if err := outputLLBGraph(ctx, &merged); err != nil {\n return nil, err\n }\n...\n'})}),"\n",(0,i.jsxs)(t.p,{children:["For the definition of an LLB vertex (an ",(0,i.jsx)(t.code,{children:"Op"})," node struct enumerated by the code snippet above), refer to ",(0,i.jsx)(t.a,{href:"https://github.com/moby/buildkit/blob/master/solver/pb/ops.proto",children:"https://github.com/moby/buildkit/blob/master/solver/pb/ops.proto"}),"."]}),"\n",(0,i.jsxs)(t.p,{children:["Following the edges between the LLB nodes is a matter of following the resulting ",(0,i.jsx)(t.code,{children:"Digest"})," value for the node to where it is consumed as one of the ",(0,i.jsx)(t.code,{children:"Op.inputs"})," in another node. For example, a pretty-printed version of a LLB graph in json format focusing on a few key nodes might look like:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",children:'// Initial target image source node\n{\n "Op": {\n "Op": {\n "source": {\n "identifier": "docker-image://mcr.microsoft.com/oss/open-policy-agent/opa:0.46.0"\n }\n },\n "platform": {\n "Architecture": "amd64",\n "OS": "linux"\n },\n "constraints": {}\n },\n "Digest": "sha256:a86ddb9065d07c67dc838e11a81ff54020531c4ca2d85fb20574088222da8b30",\n "OpMetadata": {\n "caps": {\n "source.image": true\n }\n }\n}\n\n// ..\n// Skipping intermediate graph nodes\n// ...\n\n// Diffing out the manifest updates layer\n{\n "Op": {\n "inputs": [\n {\n "digest": "sha256:cbc31a96266caa8cd5ced38a1f8e97de9f13fafb23dbe9e342125569cd4d5018",\n "index": 0\n },\n {\n "digest": "sha256:9f798b2e38e054aadf1ee66c7eb7230c65be324c26d8739a3d5fa2d5da90e5de",\n "index": 0\n }\n ],\n "Op": {\n "diff": {\n "lower": {\n "input": 0\n },\n "upper": {\n "input": 1\n }\n }\n },\n "constraints": {}\n },\n "Digest": "sha256:f337f99144ab75fee8593ec6531caa9ebace06aaca07614778b7c0ca5c816135",\n "OpMetadata": {\n "caps": {\n "diffop": true\n }\n }\n}\n\n// Merging all the target image with the patch layer and the manifest updates layer\n{\n "Op": {\n "inputs": [\n {\n "digest": "sha256:a86ddb9065d07c67dc838e11a81ff54020531c4ca2d85fb20574088222da8b30",\n "index": 0\n },\n {\n "digest": "sha256:1c3ad84c0de7e1384d727f6168db3f1f8fb632c0086760aff1786a7e89562d13",\n "index": 0\n },\n {\n "digest": "sha256:f337f99144ab75fee8593ec6531caa9ebace06aaca07614778b7c0ca5c816135",\n "index": 0\n }\n ],\n "Op": {\n "merge": {\n "inputs": [\n {\n "input": 0\n },\n {\n "input": 1\n },\n {\n "input": 2\n }\n ]\n }\n },\n "constraints": {}\n },\n "Digest": "sha256:e6a4086e2caf03c8814fc5388dd7d2e45420e1c5281e0df0d3db375d3f00358a",\n "OpMetadata": {\n "caps": {\n "mergeop": true\n }\n }\n}\n\n//...\n\n'})})]})}function h(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,i.jsx)(t,{...e,children:(0,i.jsx)(l,{...e})}):l(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>s,x:()=>r});var i=n(6540);const a={},o=i.createContext(a);function s(e){const t=i.useContext(o);return i.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function r(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:s(e.components),i.createElement(o.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/4e9dbf89.f2c2b4d8.js b/website/assets/js/4e9dbf89.f2c2b4d8.js deleted file mode 100644 index fd7f9a6d..00000000 --- a/website/assets/js/4e9dbf89.f2c2b4d8.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2747],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>m});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function r(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},o=Object.keys(e);for(a=0;a<o.length;a++)n=o[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a<o.length;a++)n=o[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var p=a.createContext({}),s=function(e){var t=a.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},d=function(e){var t=s(e.components);return a.createElement(p.Provider,{value:t},e.children)},c="mdxType",h={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,p=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),c=s(n),u=i,m=c["".concat(p,".").concat(u)]||c[u]||h[u]||o;return n?a.createElement(m,r(r({ref:t},d),{},{components:n})):a.createElement(m,r({ref:t},d))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,r=new Array(o);r[0]=u;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l[c]="string"==typeof e?e:i,r[1]=l;for(var s=2;s<o;s++)r[s]=n[s];return a.createElement.apply(null,r)}return a.createElement.apply(null,n)}u.displayName="MDXCreateElement"},684:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>r,default:()=>c,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var a=n(7462),i=(n(7294),n(3905));const o={},r="Development and Testing Tips",l={unversionedId:"development-tips",id:"version-v0.6.x/development-tips",title:"Development and Testing Tips",description:"This document provides some tips and tricks for devs to better understand what is happening under the hood of copa.",source:"@site/versioned_docs/version-v0.6.x/development-tips.md",sourceDirName:".",slug:"/development-tips",permalink:"/copacetic/website/development-tips",draft:!1,tags:[],version:"v0.6.x",frontMatter:{},sidebar:"sidebar",previous:{title:"Design",permalink:"/copacetic/website/design"},next:{title:"Maintainer Guidelines",permalink:"/copacetic/website/maintainer-guidelines"}},p={},s=[{value:"Use the <code>--debug</code> flag with <code>copa patch</code>",id:"use-the---debug-flag-with-copa-patch",level:2},{value:"Verify the intermediate stages of building a patched image",id:"verify-the-intermediate-stages-of-building-a-patched-image",level:2},{value:"Inspect a Docker image",id:"inspect-a-docker-image",level:2},{value:"Use <code>docker</code> to inspect the metadata of the image",id:"use-docker-to-inspect-the-metadata-of-the-image",level:3},{value:"Use <code>dive</code> to inspect the filesystem differences at each layer of the image",id:"use-dive-to-inspect-the-filesystem-differences-at-each-layer-of-the-image",level:3},{value:"Extract individual files from the image to inspect them",id:"extract-individual-files-from-the-image-to-inspect-them",level:3},{value:"Use <code>crane</code> to manipulate the image or extract the image filesystem",id:"use-crane-to-manipulate-the-image-or-extract-the-image-filesystem",level:3},{value:"Run scripts interactively in an image",id:"run-scripts-interactively-in-an-image",level:2},{value:"Dump the LLB Graph",id:"dump-the-llb-graph",level:2}],d={toc:s};function c(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"development-and-testing-tips"},"Development and Testing Tips"),(0,i.kt)("p",null,"This document provides some tips and tricks for devs to better understand what is happening under the hood of ",(0,i.kt)("inlineCode",{parentName:"p"},"copa"),"."),(0,i.kt)("p",null,"Much of the functionality of ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," is implemented through the use of the ",(0,i.kt)("a",{parentName:"p",href:"https://docs.docker.com/build/buildkit/"},"BuildKit")," library, and in particular, direct building a ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit#exploring-llb"},"Low-Level Build (LLB)")," intermediate representation. Most patching operations are implemented as a series of LLB stages that form a Directed Acyclic Graph (DAG) to produce the final patched image, and we'll walk through some ways to deal with the opaque nature of each operation in that graph which can otherwise make it difficult to debug or test ",(0,i.kt)("inlineCode",{parentName:"p"},"copa"),"."),(0,i.kt)("h2",{id:"use-the---debug-flag-with-copa-patch"},"Use the ",(0,i.kt)("inlineCode",{parentName:"h2"},"--debug")," flag with ",(0,i.kt)("inlineCode",{parentName:"h2"},"copa patch")),(0,i.kt)("p",null,"It's always useful to know that code on the ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," side is behaving as expected first before diving into the weeds of its interactions with BuildKit. The ",(0,i.kt)("inlineCode",{parentName:"p"},"--debug")," flag will do two useful things when enabled:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Log debug state to stdout with the ",(0,i.kt)("inlineCode",{parentName:"li"},"DEBU")," tag, including useful information such as the type of image it expects to be operating on, the list of updates and their versions it expects to apply, and any detailed errors."),(0,i.kt)("li",{parentName:"ul"},"Leave the working folder in place so that you can inspect the contents of the working files ",(0,i.kt)("inlineCode",{parentName:"li"},"copa")," writes for its own use during the patching process.")),(0,i.kt)("p",null,"For example, if you run ",(0,i.kt)("inlineCode",{parentName:"p"},"copa patch")," with the ",(0,i.kt)("inlineCode",{parentName:"p"},"--debug")," flag, you'll see something like the following output:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"$ copa patch -i <image> -r <report> --debug\nDEBU[0000] updates to apply: ...\n...\nWARN[0000] --debug specified, working folder at /var/folders/fx/nbhd5jln1qq3t405hz_hl4000000gn/T/copa-806164554 needs to be manually cleaned up \n")),(0,i.kt)("p",null,"The folder specified defaults to the system temp folder unless the ",(0,i.kt)("inlineCode",{parentName:"p"},"--working-folder")," option was specified, and you can delete it with ",(0,i.kt)("inlineCode",{parentName:"p"},"rm -r <folder>")," when you're done. The working folder will usually contain the ",(0,i.kt)("inlineCode",{parentName:"p"},"copa-out")," directory which contains files depending on the ",(0,i.kt)("inlineCode",{parentName:"p"},"pkgmgr")," implementation, such as the probed package state or post-patching package state file for the package manager. Searching for ",(0,i.kt)("inlineCode",{parentName:"p"},"SolveToLocal()")," invocations in the ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," codebase will show you where these files are written."),(0,i.kt)("h2",{id:"verify-the-intermediate-stages-of-building-a-patched-image"},"Verify the intermediate stages of building a patched image"),(0,i.kt)("p",null,"It's often useful to be able to inspect what the output of an intermediate LLB stage would look like after it has executed, and you can perform an analog to ",(0,i.kt)("inlineCode",{parentName:"p"},"printf")," debugging by solving the LLB stage to a Docker image and then inspecting the resulting image:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-go"},'// DEBUG: Solve the LLB stage to a Docker image.\nif err := buildkit.SolveToDocker(ctx, dm.config.Client, &<llb.Stage>, dm.config.ConfigData, dm.config.ImageName+"-<llb.Stage suffix>"); err != nil {\n return nil, err\n}\n')),(0,i.kt)("p",null,"For example, if you want to see what the resulting Docker image looks like at the ",(0,i.kt)("inlineCode",{parentName:"p"},"busyBoxApplied")," stage, you can add the ",(0,i.kt)("inlineCode",{parentName:"p"},"buildkit.SolveToDocker")," call to the end of the ",(0,i.kt)("inlineCode",{parentName:"p"},"busyBoxApplied")," stage as follows. The result will be a Docker image with the ",(0,i.kt)("inlineCode",{parentName:"p"},"-busyBoxApplied")," suffix to the tag that you can inspect with the ",(0,i.kt)("inlineCode",{parentName:"p"},"docker")," CLI or ",(0,i.kt)("inlineCode",{parentName:"p"},"dive")," tool:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-go"},'busyBoxApplied := dm.config.ImageState.File(llb.Copy(toolImage, "/bin/busybox", "/bin/busybox"))\nif err := buildkit.SolveToDocker(ctx, dm.config.Client, &busyBoxApplied, dm.config.ConfigData, dm.config.ImageName+"-busyBoxApplied"); err != nil {\n return nil, err\n}\n')),(0,i.kt)("h2",{id:"inspect-a-docker-image"},"Inspect a Docker image"),(0,i.kt)("h3",{id:"use-docker-to-inspect-the-metadata-of-the-image"},"Use ",(0,i.kt)("inlineCode",{parentName:"h3"},"docker")," to inspect the metadata of the image"),(0,i.kt)("p",null,"For a quick check of a Docker image, the built in ",(0,i.kt)("inlineCode",{parentName:"p"},"docker")," CLI commands can be useful:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://docs.docker.com/engine/reference/commandline/inspect/"},(0,i.kt)("inlineCode",{parentName:"a"},"docker inspect"))," can show you the metadata for the image and verifying that the patching process generally preserves the metadata of the original image."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://docs.docker.com/engine/reference/commandline/history/"},(0,i.kt)("inlineCode",{parentName:"a"},"docker history"))," can give you a quick overview of the layers in the image, and with the ",(0,i.kt)("inlineCode",{parentName:"li"},"--no-trunc")," flag, can provide the commands that were run to create each layer.")),(0,i.kt)("h3",{id:"use-dive-to-inspect-the-filesystem-differences-at-each-layer-of-the-image"},"Use ",(0,i.kt)("inlineCode",{parentName:"h3"},"dive")," to inspect the filesystem differences at each layer of the image"),(0,i.kt)("p",null,"Instructions for installing and using the ",(0,i.kt)("inlineCode",{parentName:"p"},"dive")," CLI tool are at ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/wagoodman/dive"},"https://github.com/wagoodman/dive"),"."),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"dive")," provides a simple interface for walking the layers of the image and inspecting the files that were added or changed at each layer with your arrow keys."),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"Tab")," will toggle between navigating the layers and the files in the layer."),(0,i.kt)("li",{parentName:"ul"},"Filtering out unmodified files with ",(0,i.kt)("inlineCode",{parentName:"li"},"Ctrl+U")," while in files view will effectively show you the file diff introduced by that layer.")),(0,i.kt)("p",null,"In particular, if you are adding or changing any of the patching functionality, the diff view of the files in the image can be useful to verify that the expected files have actually been written to the target image."),(0,i.kt)("h3",{id:"extract-individual-files-from-the-image-to-inspect-them"},"Extract individual files from the image to inspect them"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"dive")," won't let you read the contents of the files in the image though; to do that, you can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"docker cp")," command to copy the files out of the image to a local folder. Note that ",(0,i.kt)("inlineCode",{parentName:"p"},"docker cp")," only works with containers and not just container images, so you will need to create a container from the image and then copy the files out of the container:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"id=$(docker create <image name>:<tag>)\ndocker cp $id:<filepath> <destination path>\ndocker rm -v $id\n")),(0,i.kt)("h3",{id:"use-crane-to-manipulate-the-image-or-extract-the-image-filesystem"},"Use ",(0,i.kt)("inlineCode",{parentName:"h3"},"crane")," to manipulate the image or extract the image filesystem"),(0,i.kt)("p",null,"Sometimes it's useful to be able to manipulate the image in ways that ",(0,i.kt)("inlineCode",{parentName:"p"},"docker")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"dive")," don't support, such as extracting the entire image filesystem to a local folder. ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/google/go-containerregistry/tree/main/cmd/crane"},(0,i.kt)("inlineCode",{parentName:"a"},"crane"))," can be useful for this and also provides ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/google/go-containerregistry/blob/main/cmd/crane/doc/crane.md"},"many other convenient utilities")," for working with container images."),(0,i.kt)("p",null,"For instance, to extract the filesystem of the image to a local folder, you can use ",(0,i.kt)("inlineCode",{parentName:"p"},"crane export"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"crane export <image name>:<tag> - | tar -xvf -\n")),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"crane")," is a very flexible tool designed to work well with pipes to existing shell tools. For example, you can also use ",(0,i.kt)("inlineCode",{parentName:"p"},"crane")," to do a full diff between two images as well:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"}," diff \\\n <(crane export image:tag - | tar -tvf - | sort) \\\n <(crane export image:tag-patched - | tar -tvf - | sort)\n")),(0,i.kt)("h2",{id:"run-scripts-interactively-in-an-image"},"Run scripts interactively in an image"),(0,i.kt)("p",null,"Some of the LLB stages effectively run shell scripts defined by ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," in the image, and sometimes these need to be debugged or modified. The easiest way to do this is usually just solving the ",(0,i.kt)("inlineCode",{parentName:"p"},"llb.Stage")," of interest to Docker and then running the image interactively with ",(0,i.kt)("inlineCode",{parentName:"p"},"docker run"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"docker run --rm -it --entrypoint sh <image name>:<tag>-<llb.Stage suffix>\n")),(0,i.kt)("p",null,"One thing to note is that the scripts embedded in the ",(0,i.kt)("inlineCode",{parentName:"p"},".go")," files will often have an additional layer of character escapes to make them valid Go strings, so you may need to unescape them before running them interactively."),(0,i.kt)("h2",{id:"dump-the-llb-graph"},"Dump the LLB Graph"),(0,i.kt)("p",null,"Ultimately, ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," is just a tool for building a BuildKit LLB graph, and you may need to understand if the LLB graph being constructed is reasonable or expected. It's helpful to have a basic understanding of how BuildKit and the operations commonly used by ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," here, so it's good to be familiar with some key resources here:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://pkg.go.dev/github.com/moby/buildkit#section-readme"},"BuildKit pkg.go.dev README")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://github.com/moby/buildkit/tree/master/docs/dev"},"BuildKit Developer Docs")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://www.docker.com/blog/mergediff-building-dags-more-efficiently-and-elegantly/"},"Merge+Diff: Building DAGs More Efficiently and Elegantly"))),(0,i.kt)("p",null,"The LLB graph up to any ",(0,i.kt)("inlineCode",{parentName:"p"},"llb.Stage")," can be written out by marshalling it to a ",(0,i.kt)("inlineCode",{parentName:"p"},"Definition")," and enumerating each of the operations to output. Using the ",(0,i.kt)("inlineCode",{parentName:"p"},"buildctl")," implementation of ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit/blob/master/cmd/buildctl/debug/dumpllb.go#L30"},"dumpLLB")," as a reference, you can write a function to output the LLB graph as JSON nodes to stdout:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-go"},'import "github.com/moby/buildkit/solver/pb"\n\n// Definition of the LLB graph node to display, modify as desired.\n// This version is what the buildctl tool uses.\ntype llbOp struct {\n Op pb.Op\n Digest digest.Digest\n OpMetadata pb.OpMetadata\n}\n\nfunc outputLLBGraph(ctx context.Context, llbState *llb.State) error {\n // Marshal the llb.State to a LLB definition.\n def, err := llbState.Marshal(ctx)\n if err != nil {\n log.Errorf("Marshal to LLB failed with %s", err)\n return err\n }\n\n // Format each operation node in the LLB definition into a struct.\n var ops []llbOp\n for _, dt := range def.Def {\n var op pb.Op\n if err := (&op).Unmarshal(dt); err != nil {\n return errors.Wrap(err, "failed to parse op")\n }\n hash := digest.FromBytes(dt)\n ent := llbOp{Op: op, Digest: hash, OpMetadata: def.Metadata[hash]}\n ops = append(ops, ent)\n }\n\n // Output the LLB graph as JSON nodes to stdout.\n // Modify as desired to output to file or other formats.\n enc := json.NewEncoder(os.Stdout)\n for _, op := range ops {\n if err := enc.Encode(op); err != nil {\n return err\n }\n }\n return nil\n}\n\n// Within the function (e.g. with the llb.State where you want to dump the LLB graph:\n...\n // DEBUG: dump the LLB graph to stdout\n if err := outputLLBGraph(ctx, &merged); err != nil {\n return nil, err\n }\n...\n')),(0,i.kt)("p",null,"For the definition of an LLB vertex (an ",(0,i.kt)("inlineCode",{parentName:"p"},"Op")," node struct enumerated by the code snippet above), refer to ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit/blob/master/solver/pb/ops.proto"},"https://github.com/moby/buildkit/blob/master/solver/pb/ops.proto"),". "),(0,i.kt)("p",null,"Following the edges between the LLB nodes is a matter of following the resulting ",(0,i.kt)("inlineCode",{parentName:"p"},"Digest")," value for the node to where it is consumed as one of the ",(0,i.kt)("inlineCode",{parentName:"p"},"Op.inputs")," in another node. For example, a pretty-printed version of a LLB graph in json format focusing on a few key nodes might look like:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'// Initial target image source node\n{\n "Op": {\n "Op": {\n "source": {\n "identifier": "docker-image://mcr.microsoft.com/oss/open-policy-agent/opa:0.46.0"\n }\n },\n "platform": {\n "Architecture": "amd64",\n "OS": "linux"\n },\n "constraints": {}\n },\n "Digest": "sha256:a86ddb9065d07c67dc838e11a81ff54020531c4ca2d85fb20574088222da8b30",\n "OpMetadata": {\n "caps": {\n "source.image": true\n }\n }\n}\n\n// ..\n// Skipping intermediate graph nodes\n// ...\n\n// Diffing out the manifest updates layer\n{\n "Op": {\n "inputs": [\n {\n "digest": "sha256:cbc31a96266caa8cd5ced38a1f8e97de9f13fafb23dbe9e342125569cd4d5018",\n "index": 0\n },\n {\n "digest": "sha256:9f798b2e38e054aadf1ee66c7eb7230c65be324c26d8739a3d5fa2d5da90e5de",\n "index": 0\n }\n ],\n "Op": {\n "diff": {\n "lower": {\n "input": 0\n },\n "upper": {\n "input": 1\n }\n }\n },\n "constraints": {}\n },\n "Digest": "sha256:f337f99144ab75fee8593ec6531caa9ebace06aaca07614778b7c0ca5c816135",\n "OpMetadata": {\n "caps": {\n "diffop": true\n }\n }\n}\n\n// Merging all the target image with the patch layer and the manifest updates layer\n{\n "Op": {\n "inputs": [\n {\n "digest": "sha256:a86ddb9065d07c67dc838e11a81ff54020531c4ca2d85fb20574088222da8b30",\n "index": 0\n },\n {\n "digest": "sha256:1c3ad84c0de7e1384d727f6168db3f1f8fb632c0086760aff1786a7e89562d13",\n "index": 0\n },\n {\n "digest": "sha256:f337f99144ab75fee8593ec6531caa9ebace06aaca07614778b7c0ca5c816135",\n "index": 0\n }\n ],\n "Op": {\n "merge": {\n "inputs": [\n {\n "input": 0\n },\n {\n "input": 1\n },\n {\n "input": 2\n }\n ]\n }\n },\n "constraints": {}\n },\n "Digest": "sha256:e6a4086e2caf03c8814fc5388dd7d2e45420e1c5281e0df0d3db375d3f00358a",\n "OpMetadata": {\n "caps": {\n "mergeop": true\n }\n }\n}\n\n//...\n\n')))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/512645f2.65269ba4.js b/website/assets/js/512645f2.65269ba4.js new file mode 100644 index 00000000..6099e797 --- /dev/null +++ b/website/assets/js/512645f2.65269ba4.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9516],{2963:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>a,contentTitle:()=>s,default:()=>u,frontMatter:()=>c,metadata:()=>r,toc:()=>d});var i=t(4848),o=t(8453);const c={title:"Copa Github Action"},s=void 0,r={id:"github-action",title:"Copa Github Action",description:"Overview",source:"@site/versioned_docs/version-v0.5.x/github-action.md",sourceDirName:".",slug:"/github-action",permalink:"/copacetic/website/v0.5.x/github-action",draft:!1,unlisted:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Copa Github Action"},sidebar:"sidebar",previous:{title:"Code of Conduct",permalink:"/copacetic/website/v0.5.x/code-of-conduct"},next:{title:"Release Process",permalink:"/copacetic/website/v0.5.x/release"}},a={},d=[{value:"Overview",id:"overview",level:2},{value:"Inputs",id:"inputs",level:2},{value:"<code>image</code>",id:"image",level:2},{value:"<code>image-report</code>",id:"image-report",level:2},{value:"<code>patched-tag</code>",id:"patched-tag",level:2},{value:"<code>buildkit-version</code>",id:"buildkit-version",level:2},{value:"<code>copa-version</code>",id:"copa-version",level:2},{value:"Output",id:"output",level:2},{value:"<code>patched-image</code>",id:"patched-image",level:2},{value:"Example Workflow",id:"example-workflow",level:2}];function l(e){const n={a:"a",code:"code",h2:"h2",p:"p",pre:"pre",strong:"strong",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.h2,{id:"overview",children:"Overview"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.a,{href:"https://github.com/project-copacetic/copa-action",children:"Copa Github Action"})," allows you patch vulnerable containers in your workflows using Copa."]}),"\n",(0,i.jsx)(n.h2,{id:"inputs",children:"Inputs"}),"\n",(0,i.jsx)(n.h2,{id:"image",children:(0,i.jsx)(n.code,{children:"image"})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Required"})," The image reference to patch."]}),"\n",(0,i.jsx)(n.h2,{id:"image-report",children:(0,i.jsx)(n.code,{children:"image-report"})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Required"})," The trivy json vulnerability report of the image to patch."]}),"\n",(0,i.jsx)(n.h2,{id:"patched-tag",children:(0,i.jsx)(n.code,{children:"patched-tag"})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Required"})," The new patched image tag."]}),"\n",(0,i.jsx)(n.h2,{id:"buildkit-version",children:(0,i.jsx)(n.code,{children:"buildkit-version"})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Optional"})," The buildkit version used in the action, default is latest."]}),"\n",(0,i.jsx)(n.h2,{id:"copa-version",children:(0,i.jsx)(n.code,{children:"copa-version"})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Optional"})," The Copa version used in the action, default is latest."]}),"\n",(0,i.jsx)(n.h2,{id:"output",children:"Output"}),"\n",(0,i.jsx)(n.h2,{id:"patched-image",children:(0,i.jsx)(n.code,{children:"patched-image"})}),"\n",(0,i.jsx)(n.p,{children:"Image reference of the resulting patched image."}),"\n",(0,i.jsx)(n.h2,{id:"example-workflow",children:"Example Workflow"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"on: [push]\n\njobs:\n test:\n runs-on: ubuntu-latest\n\n strategy:\n fail-fast: false\n matrix:\n # provide relevant list of images to scan on each run\n images: ['docker.io/library/nginx:1.21.6', 'docker.io/openpolicyagent/opa:0.46.0', 'docker.io/library/hello-world:latest']\n\n steps:\n - name: Set up Docker Buildx\n uses: docker/setup-buildx-action@dedd61cf5d839122591f5027c89bf3ad27691d18\n\n - name: Generate Trivy Report\n uses: aquasecurity/trivy-action@69cbbc0cbbf6a2b0bab8dcf0e9f2d7ead08e87e4\n with:\n scan-type: 'image'\n format: 'json'\n output: 'report.json'\n ignore-unfixed: true\n vuln-type: 'os'\n image-ref: ${{ matrix.images }}\n\n - name: Check Vuln Count\n id: vuln_count\n run: |\n report_file=\"report.json\"\n vuln_count=$(jq '.Results | length' \"$report_file\")\n echo \"vuln_count=$vuln_count\" >> $GITHUB_OUTPUT\n\n - name: Copa Action\n if: steps.vuln_count.outputs.vuln_count != '0'\n id: copa\n uses: project-copacetic/copa-action@v1\n with:\n image: ${{ matrix.images }}\n image-report: 'report.json'\n patched-tag: 'patched'\n buildkit-version: 'v0.11.6'\n # optional, default is latest\n copa-version: '0.4.1'\n\n - name: Login to Docker Hub\n if: steps.copa.conclusion == 'success'\n id: login\n uses: docker/login-action@b4bedf8053341df3b5a9f9e0f2cf4e79e27360c6\n with:\n username: 'user'\n password: ${{ secrets.DOCKERHUB_TOKEN }}\n\n - name: Docker Push Patched Image\n if: steps.login.conclusion == 'success'\n run: |\n docker push ${{ steps.copa.outputs.patched-image }}\n\n"})})]})}function u(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(l,{...e})}):l(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>s,x:()=>r});var i=t(6540);const o={},c=i.createContext(o);function s(e){const n=i.useContext(c);return i.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function r(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:s(e.components),i.createElement(c.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/512645f2.86b67e45.js b/website/assets/js/512645f2.86b67e45.js deleted file mode 100644 index c15f278f..00000000 --- a/website/assets/js/512645f2.86b67e45.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2279],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var o=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function a(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?i(Object(n),!0).forEach((function(t){r(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):i(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function c(e,t){if(null==e)return{};var n,o,r=function(e,t){if(null==e)return{};var n,o,r={},i=Object.keys(e);for(o=0;o<i.length;o++)n=i[o],t.indexOf(n)>=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(o=0;o<i.length;o++)n=i[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=o.createContext({}),p=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=p(e.components);return o.createElement(l.Provider,{value:t},e.children)},s="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},v=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,l=e.parentName,u=c(e,["components","mdxType","originalType","parentName"]),s=p(n),v=r,m=s["".concat(l,".").concat(v)]||s[v]||d[v]||i;return n?o.createElement(m,a(a({ref:t},u),{},{components:n})):o.createElement(m,a({ref:t},u))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,a=new Array(i);a[0]=v;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[s]="string"==typeof e?e:r,a[1]=c;for(var p=2;p<i;p++)a[p]=n[p];return o.createElement.apply(null,a)}return o.createElement.apply(null,n)}v.displayName="MDXCreateElement"},233:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>s,frontMatter:()=>i,metadata:()=>c,toc:()=>p});var o=n(7462),r=(n(7294),n(3905));const i={title:"Copa Github Action"},a=void 0,c={unversionedId:"github-action",id:"version-v0.5.x/github-action",title:"Copa Github Action",description:"Overview",source:"@site/versioned_docs/version-v0.5.x/github-action.md",sourceDirName:".",slug:"/github-action",permalink:"/copacetic/website/v0.5.x/github-action",draft:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Copa Github Action"},sidebar:"sidebar",previous:{title:"Code of Conduct",permalink:"/copacetic/website/v0.5.x/code-of-conduct"},next:{title:"Release Process",permalink:"/copacetic/website/v0.5.x/release"}},l={},p=[{value:"Overview",id:"overview",level:2},{value:"Inputs",id:"inputs",level:2},{value:"<code>image</code>",id:"image",level:2},{value:"<code>image-report</code>",id:"image-report",level:2},{value:"<code>patched-tag</code>",id:"patched-tag",level:2},{value:"<code>buildkit-version</code>",id:"buildkit-version",level:2},{value:"<code>copa-version</code>",id:"copa-version",level:2},{value:"Output",id:"output",level:2},{value:"<code>patched-image</code>",id:"patched-image",level:2},{value:"Example Workflow",id:"example-workflow",level:2}],u={toc:p};function s(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,o.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h2",{id:"overview"},"Overview"),(0,r.kt)("p",null,"The ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copa-action"},"Copa Github Action")," allows you patch vulnerable containers in your workflows using Copa. "),(0,r.kt)("h2",{id:"inputs"},"Inputs"),(0,r.kt)("h2",{id:"image"},(0,r.kt)("inlineCode",{parentName:"h2"},"image")),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Required")," The image reference to patch."),(0,r.kt)("h2",{id:"image-report"},(0,r.kt)("inlineCode",{parentName:"h2"},"image-report")),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Required")," The trivy json vulnerability report of the image to patch."),(0,r.kt)("h2",{id:"patched-tag"},(0,r.kt)("inlineCode",{parentName:"h2"},"patched-tag")),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Required")," The new patched image tag."),(0,r.kt)("h2",{id:"buildkit-version"},(0,r.kt)("inlineCode",{parentName:"h2"},"buildkit-version")),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Optional")," The buildkit version used in the action, default is latest."),(0,r.kt)("h2",{id:"copa-version"},(0,r.kt)("inlineCode",{parentName:"h2"},"copa-version")),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Optional")," The Copa version used in the action, default is latest."),(0,r.kt)("h2",{id:"output"},"Output"),(0,r.kt)("h2",{id:"patched-image"},(0,r.kt)("inlineCode",{parentName:"h2"},"patched-image")),(0,r.kt)("p",null,"Image reference of the resulting patched image."),(0,r.kt)("h2",{id:"example-workflow"},"Example Workflow"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"on: [push]\n\njobs:\n test:\n runs-on: ubuntu-latest\n\n strategy:\n fail-fast: false\n matrix:\n # provide relevant list of images to scan on each run\n images: ['docker.io/library/nginx:1.21.6', 'docker.io/openpolicyagent/opa:0.46.0', 'docker.io/library/hello-world:latest']\n\n steps:\n - name: Set up Docker Buildx\n uses: docker/setup-buildx-action@dedd61cf5d839122591f5027c89bf3ad27691d18\n\n - name: Generate Trivy Report\n uses: aquasecurity/trivy-action@69cbbc0cbbf6a2b0bab8dcf0e9f2d7ead08e87e4\n with:\n scan-type: 'image'\n format: 'json'\n output: 'report.json'\n ignore-unfixed: true\n vuln-type: 'os'\n image-ref: ${{ matrix.images }}\n\n - name: Check Vuln Count\n id: vuln_count\n run: |\n report_file=\"report.json\"\n vuln_count=$(jq '.Results | length' \"$report_file\")\n echo \"vuln_count=$vuln_count\" >> $GITHUB_OUTPUT\n\n - name: Copa Action\n if: steps.vuln_count.outputs.vuln_count != '0'\n id: copa\n uses: project-copacetic/copa-action@v1\n with:\n image: ${{ matrix.images }}\n image-report: 'report.json'\n patched-tag: 'patched'\n buildkit-version: 'v0.11.6'\n # optional, default is latest\n copa-version: '0.4.1'\n\n - name: Login to Docker Hub\n if: steps.copa.conclusion == 'success'\n id: login\n uses: docker/login-action@b4bedf8053341df3b5a9f9e0f2cf4e79e27360c6\n with:\n username: 'user'\n password: ${{ secrets.DOCKERHUB_TOKEN }}\n\n - name: Docker Push Patched Image\n if: steps.login.conclusion == 'success'\n run: |\n docker push ${{ steps.copa.outputs.patched-image }}\n\n")))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/57b2432c.ba8a447b.js b/website/assets/js/57b2432c.ba8a447b.js deleted file mode 100644 index 182d8233..00000000 --- a/website/assets/js/57b2432c.ba8a447b.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8765],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>g});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),c=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(s.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=c(n),m=i,g=u["".concat(s,".").concat(m)]||u[m]||d[m]||r;return n?a.createElement(g,o(o({ref:t},p),{},{components:n})):a.createElement(g,o({ref:t},p))}));function g(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[u]="string"==typeof e?e:i,o[1]=l;for(var c=2;c<r;c++)o[c]=n[c];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}m.displayName="MDXCreateElement"},3358:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>l,toc:()=>c});var a=n(7462),i=(n(7294),n(3905));const r={title:"Introduction",slug:"/"},o="Project Copacetic: Directly patch container image vulnerabilities",l={unversionedId:"introduction",id:"version-v0.3.x/introduction",title:"Introduction",description:"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.",source:"@site/versioned_docs/version-v0.3.x/introduction.md",sourceDirName:".",slug:"/",permalink:"/copacetic/website/v0.3.x/",draft:!1,tags:[],version:"v0.3.x",frontMatter:{title:"Introduction",slug:"/"},sidebar:"sidebar",next:{title:"Installation",permalink:"/copacetic/website/v0.3.x/installation"}},s={},c=[{value:"Why?",id:"why",level:2},{value:"How?",id:"how",level:2}],p={toc:c};function u(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"project-copacetic-directly-patch-container-image-vulnerabilities"},"Project Copacetic: Directly patch container image vulnerabilities"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"copa")," is a CLI tool written in ",(0,i.kt)("a",{parentName:"p",href:"https://golang.org"},"Go")," and based on ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit"},"buildkit")," that can be used to directly patch container images given the vulnerability scanning results from popular tools like ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/aquasecurity/trivy"},"Trivy"),"."),(0,i.kt)("h2",{id:"why"},"Why?"),(0,i.kt)("p",null,"We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between ",(0,i.kt)("a",{parentName:"p",href:"https://www.bleepingcomputer.com/news/security/hackers-scan-for-vulnerabilities-within-15-minutes-of-disclosure/"},"vulnerability disclosure and active exploitation continues to narrow"),", there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option"),(0,i.kt)("li",{parentName:"ul"},"found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.")),(0,i.kt)("img",{title:"direct image patching",src:"/copacetic/website/img/direct-image-patching.png"}),(0,i.kt)("p",null,"In addition to filling the operational gap not met by left-shift security practices and tools, the ability of ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," to patch a container without requiring a rebuild of the container image provides other benefits:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Allows users other than the image publishers to also patch container images, such as DevSecOps engineers."),(0,i.kt)("li",{parentName:"ul"},"Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching."),(0,i.kt)("li",{parentName:"ul"},"Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild."),(0,i.kt)("li",{parentName:"ul"},"Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.")),(0,i.kt)("h2",{id:"how"},"How?"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," tool is an extensible engine that:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Parses the needed update packages from the container image\u2019s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats."),(0,i.kt)("li",{parentName:"ol"},"Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers."),(0,i.kt)("li",{parentName:"ol"},"Applies the resulting update binaries to the container image using buildkit.")),(0,i.kt)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),(0,i.kt)("p",null,"This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa supports patching ",(0,i.kt)("em",{parentName:"strong"},"existing")," container images"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"Devs don't need to build their images using specific tools or modify them in some way just to support container patching."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa works with the existing vulnerability scanning and mitigation ecosystems"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today."),(0,i.kt)("li",{parentName:"ul"},"Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.")))),(0,i.kt)("p",null,"For more details, refer to the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.3.x/design"},"copa design")," documentation."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/57b2432c.dcb09c4b.js b/website/assets/js/57b2432c.dcb09c4b.js new file mode 100644 index 00000000..77db2598 --- /dev/null +++ b/website/assets/js/57b2432c.dcb09c4b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3773],{2597:(e,i,n)=>{n.r(i),n.d(i,{assets:()=>c,contentTitle:()=>o,default:()=>h,frontMatter:()=>s,metadata:()=>r,toc:()=>l});var t=n(4848),a=n(8453);const s={title:"Introduction",slug:"/"},o="Project Copacetic: Directly patch container image vulnerabilities",r={id:"introduction",title:"Introduction",description:"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.",source:"@site/versioned_docs/version-v0.3.x/introduction.md",sourceDirName:".",slug:"/",permalink:"/copacetic/website/v0.3.x/",draft:!1,unlisted:!1,tags:[],version:"v0.3.x",frontMatter:{title:"Introduction",slug:"/"},sidebar:"sidebar",next:{title:"Installation",permalink:"/copacetic/website/v0.3.x/installation"}},c={},l=[{value:"Why?",id:"why",level:2},{value:"How?",id:"how",level:2}];function d(e){const i={a:"a",code:"code",em:"em",h1:"h1",h2:"h2",li:"li",ol:"ol",p:"p",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i.h1,{id:"project-copacetic-directly-patch-container-image-vulnerabilities",children:"Project Copacetic: Directly patch container image vulnerabilities"}),"\n",(0,t.jsxs)(i.p,{children:[(0,t.jsx)(i.code,{children:"copa"})," is a CLI tool written in ",(0,t.jsx)(i.a,{href:"https://golang.org",children:"Go"})," and based on ",(0,t.jsx)(i.a,{href:"https://github.com/moby/buildkit",children:"buildkit"})," that can be used to directly patch container images given the vulnerability scanning results from popular tools like ",(0,t.jsx)(i.a,{href:"https://github.com/aquasecurity/trivy",children:"Trivy"}),"."]}),"\n",(0,t.jsx)(i.h2,{id:"why",children:"Why?"}),"\n",(0,t.jsxs)(i.p,{children:["We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between ",(0,t.jsx)(i.a,{href:"https://www.bleepingcomputer.com/news/security/hackers-scan-for-vulnerabilities-within-15-minutes-of-disclosure/",children:"vulnerability disclosure and active exploitation continues to narrow"}),", there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:"]}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option"}),"\n",(0,t.jsx)(i.li,{children:"found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs."}),"\n"]}),"\n",(0,t.jsx)("img",{title:"direct image patching",src:"/copacetic/website/img/direct-image-patching.png"}),"\n",(0,t.jsxs)(i.p,{children:["In addition to filling the operational gap not met by left-shift security practices and tools, the ability of ",(0,t.jsx)(i.code,{children:"copa"})," to patch a container without requiring a rebuild of the container image provides other benefits:"]}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Allows users other than the image publishers to also patch container images, such as DevSecOps engineers."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image."}),"\n"]}),"\n",(0,t.jsx)(i.h2,{id:"how",children:"How?"}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"copa"})," tool is an extensible engine that:"]}),"\n",(0,t.jsxs)(i.ol,{children:["\n",(0,t.jsx)(i.li,{children:"Parses the needed update packages from the container image\u2019s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats."}),"\n",(0,t.jsx)(i.li,{children:"Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers."}),"\n",(0,t.jsx)(i.li,{children:"Applies the resulting update binaries to the container image using buildkit."}),"\n"]}),"\n",(0,t.jsx)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),"\n",(0,t.jsx)(i.p,{children:"This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:"}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:[(0,t.jsxs)(i.strong,{children:["Copa supports patching ",(0,t.jsx)(i.em,{children:"existing"})," container images"]}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Devs don't need to build their images using specific tools or modify them in some way just to support container patching."}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Copa works with the existing vulnerability scanning and mitigation ecosystems"}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today."}),"\n",(0,t.jsx)(i.li,{children:"Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports."}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image"}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today."}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.p,{children:["For more details, refer to the ",(0,t.jsx)(i.a,{href:"/copacetic/website/v0.3.x/design",children:"copa design"})," documentation."]})]})}function h(e={}){const{wrapper:i}={...(0,a.R)(),...e.components};return i?(0,t.jsx)(i,{...e,children:(0,t.jsx)(d,{...e})}):d(e)}},8453:(e,i,n)=>{n.d(i,{R:()=>o,x:()=>r});var t=n(6540);const a={},s=t.createContext(a);function o(e){const i=t.useContext(s);return t.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function r(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),t.createElement(s.Provider,{value:i},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/5c8ee200.f51d96f0.js b/website/assets/js/5c8ee200.f51d96f0.js deleted file mode 100644 index 951d6fd8..00000000 --- a/website/assets/js/5c8ee200.f51d96f0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1057],{3905:(e,n,t)=>{t.d(n,{Zo:()=>c,kt:()=>g});var a=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function o(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?r(Object(t),!0).forEach((function(n){i(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):r(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))}))}return e}function s(e,n){if(null==e)return{};var t,a,i=function(e,n){if(null==e)return{};var t,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)t=r[a],n.indexOf(t)>=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)t=r[a],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var l=a.createContext({}),p=function(e){var n=a.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},c=function(e){var n=p(e.components);return a.createElement(l.Provider,{value:n},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},m=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),u=p(t),m=i,g=u["".concat(l,".").concat(m)]||u[m]||d[m]||r;return t?a.createElement(g,o(o({ref:n},c),{},{components:t})):a.createElement(g,o({ref:n},c))}));function g(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,o=new Array(r);o[0]=m;var s={};for(var l in n)hasOwnProperty.call(n,l)&&(s[l]=n[l]);s.originalType=e,s[u]="string"==typeof e?e:i,o[1]=s;for(var p=2;p<r;p++)o[p]=t[p];return a.createElement.apply(null,o)}return a.createElement.apply(null,t)}m.displayName="MDXCreateElement"},4633:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var a=t(7462),i=(t(7294),t(3905));const r={title:"Scanner Plugins"},o="Motivation",s={unversionedId:"scanner-plugins",id:"version-v0.5.x/scanner-plugins",title:"Scanner Plugins",description:"By default, copa uses Trivy to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners.",source:"@site/versioned_docs/version-v0.5.x/scanner-plugins.md",sourceDirName:".",slug:"/scanner-plugins",permalink:"/copacetic/website/v0.5.x/scanner-plugins",draft:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Scanner Plugins"},sidebar:"sidebar",previous:{title:"FAQ",permalink:"/copacetic/website/v0.5.x/faq"},next:{title:"Contributing",permalink:"/copacetic/website/v0.5.x/contributing"}},l={},p=[{value:"v1alpha1",id:"v1alpha1",level:2}],c={toc:p};function u(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,a.Z)({},c,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"motivation"},"Motivation"),(0,i.kt)("p",null,"By default, ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," uses ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/aquasecurity/trivy"},"Trivy")," to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners."),(0,i.kt)("p",null,"Starting with v0.5.0 and later, ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," offers extensibility to support different vulnerability scanners. Plugin architecture allows users to use the vulnerability scanner of their choice to patch container images without having to modify ",(0,i.kt)("inlineCode",{parentName:"p"},"copa"),"'s core codebase."),(0,i.kt)("h1",{id:"usage"},"Usage"),(0,i.kt)("p",null,"Scanner plugin binaries must be in ",(0,i.kt)("inlineCode",{parentName:"p"},"$PATH"),", and should be prefixed with ",(0,i.kt)("inlineCode",{parentName:"p"},"copa-")," and have executable permissions. Copa will automatically detect and use the scanner plugin if it is in ",(0,i.kt)("inlineCode",{parentName:"p"},"$PATH"),"."),(0,i.kt)("p",null,"For example, if you have a scanner plugin binary called ",(0,i.kt)("inlineCode",{parentName:"p"},"copa-foo")," in ",(0,i.kt)("inlineCode",{parentName:"p"},"$PATH"),", you can run ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," with the following command:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"copa patch --scanner foo --image $IMAGE ...\n")),(0,i.kt)("h1",{id:"scanner-plugins-from-the-community"},"Scanner Plugins from the Community"),(0,i.kt)("p",null,"If you have built a scanner plugin and would like to add it to this list, please submit a PR to update this section with your plugin."),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},"If you have any issues with a specific plugin, please open an issue in the applicable plugin's repository.")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Grype: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/anubhav06/copa-grype"},"https://github.com/anubhav06/copa-grype"))),(0,i.kt)("h1",{id:"writing-a-scanner-plugin"},"Writing a Scanner Plugin"),(0,i.kt)("p",null,"Please see instructions at ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/scanner-plugin-template"},"Scanner Plugin Template")," for a template to get started with writing a scanner plugin."),(0,i.kt)("h1",{id:"scanner-plugin-interface"},"Scanner Plugin Interface"),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},(0,i.kt)("inlineCode",{parentName:"p"},"alpha")," versions of the API are not guarenteed to be backwards compatible. Once the API graduates to ",(0,i.kt)("inlineCode",{parentName:"p"},"beta")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"stable"),", it will be backwards compatible.")),(0,i.kt)("p",null,"Scanner plugins must implement the following interface:"),(0,i.kt)("h2",{id:"v1alpha1"},"v1alpha1"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-go"},'type UpdateManifest struct {\n // API version of the interface (e.g. v1alpha1)\n APIVersion string `json:"apiVersion"`\n // Metadata contains information about the OS and config\n Metadata Metadata `json:"metadata"`\n // Updates is a list of UpdatePackage that contains information about the package updates\n Updates UpdatePackages `json:"updates"`\n}\n\n// UpdatePackages is a list of UpdatePackage\ntype UpdatePackages []UpdatePackage\n\n// Metadata contains information about the OS and config\ntype Metadata struct {\n OS OS `json:"os"`\n Config Config `json:"config"`\n}\n\ntype OS struct {\n // OS Type (e.g. debian, alpine, etc.)\n Type string `json:"type"`\n // OS Version (e.g. 11.3)\n Version string `json:"version"`\n}\n\n// Config contains information about the config\ntype Config struct {\n // OS Architecture (e.g. amd64, arm64)\n Arch string `json:"arch"`\n}\n\n// UpdatePackage contains information about the package update\ntype UpdatePackage struct {\n // Package name\n Name string `json:"name"`\n // Installed version\n InstalledVersion string `json:"installedVersion"`\n // Fixed version\n FixedVersion string `json:"fixedVersion"`\n // Vulnerability ID\n VulnerabilityID string `json:"vulnerabilityID"`\n}\n')),(0,i.kt)("p",null,"From the above, we can see that the plugin must return a JSON object via standard out with the following fields. For example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'{\n "apiVersion": "v1alpha1",\n "metadata": {\n "os": {\n "type": "debian",\n "version": "11.3",\n },\n "config": {\n "arch": "amd64"\n }\n },\n "updates": [\n {\n "name": "libcurl4",\n "installedVersion": "7.74.0-1.3+deb11u1",\n "fixedVersion": "7.74.0-1.3+deb11u2",\n "vulnerabilityID": "CVE-2021-22945"\n }\n ]\n}\n')))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/5c8ee200.faac304c.js b/website/assets/js/5c8ee200.faac304c.js new file mode 100644 index 00000000..a7dbb2f4 --- /dev/null +++ b/website/assets/js/5c8ee200.faac304c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5911],{6776:(n,e,t)=>{t.r(e),t.d(e,{assets:()=>c,contentTitle:()=>r,default:()=>u,frontMatter:()=>s,metadata:()=>o,toc:()=>l});var i=t(4848),a=t(8453);const s={title:"Scanner Plugins"},r="Motivation",o={id:"scanner-plugins",title:"Scanner Plugins",description:"By default, copa uses Trivy to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners.",source:"@site/versioned_docs/version-v0.5.x/scanner-plugins.md",sourceDirName:".",slug:"/scanner-plugins",permalink:"/copacetic/website/v0.5.x/scanner-plugins",draft:!1,unlisted:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Scanner Plugins"},sidebar:"sidebar",previous:{title:"FAQ",permalink:"/copacetic/website/v0.5.x/faq"},next:{title:"Contributing",permalink:"/copacetic/website/v0.5.x/contributing"}},c={},l=[{value:"v1alpha1",id:"v1alpha1",level:2}];function d(n){const e={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",li:"li",p:"p",pre:"pre",ul:"ul",...(0,a.R)(),...n.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(e.h1,{id:"motivation",children:"Motivation"}),"\n",(0,i.jsxs)(e.p,{children:["By default, ",(0,i.jsx)(e.code,{children:"copa"})," uses ",(0,i.jsx)(e.a,{href:"https://github.com/aquasecurity/trivy",children:"Trivy"})," to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners."]}),"\n",(0,i.jsxs)(e.p,{children:["Starting with v0.5.0 and later, ",(0,i.jsx)(e.code,{children:"copa"})," offers extensibility to support different vulnerability scanners. Plugin architecture allows users to use the vulnerability scanner of their choice to patch container images without having to modify ",(0,i.jsx)(e.code,{children:"copa"}),"'s core codebase."]}),"\n",(0,i.jsx)(e.h1,{id:"usage",children:"Usage"}),"\n",(0,i.jsxs)(e.p,{children:["Scanner plugin binaries must be in ",(0,i.jsx)(e.code,{children:"$PATH"}),", and should be prefixed with ",(0,i.jsx)(e.code,{children:"copa-"})," and have executable permissions. Copa will automatically detect and use the scanner plugin if it is in ",(0,i.jsx)(e.code,{children:"$PATH"}),"."]}),"\n",(0,i.jsxs)(e.p,{children:["For example, if you have a scanner plugin binary called ",(0,i.jsx)(e.code,{children:"copa-foo"})," in ",(0,i.jsx)(e.code,{children:"$PATH"}),", you can run ",(0,i.jsx)(e.code,{children:"copa"})," with the following command:"]}),"\n",(0,i.jsx)(e.pre,{children:(0,i.jsx)(e.code,{className:"language-bash",children:"copa patch --scanner foo --image $IMAGE ...\n"})}),"\n",(0,i.jsx)(e.h1,{id:"scanner-plugins-from-the-community",children:"Scanner Plugins from the Community"}),"\n",(0,i.jsx)(e.p,{children:"If you have built a scanner plugin and would like to add it to this list, please submit a PR to update this section with your plugin."}),"\n",(0,i.jsx)(e.admonition,{type:"note",children:(0,i.jsx)(e.p,{children:"If you have any issues with a specific plugin, please open an issue in the applicable plugin's repository."})}),"\n",(0,i.jsxs)(e.ul,{children:["\n",(0,i.jsxs)(e.li,{children:["Grype: ",(0,i.jsx)(e.a,{href:"https://github.com/anubhav06/copa-grype",children:"https://github.com/anubhav06/copa-grype"})]}),"\n"]}),"\n",(0,i.jsx)(e.h1,{id:"writing-a-scanner-plugin",children:"Writing a Scanner Plugin"}),"\n",(0,i.jsxs)(e.p,{children:["Please see instructions at ",(0,i.jsx)(e.a,{href:"https://github.com/project-copacetic/scanner-plugin-template",children:"Scanner Plugin Template"})," for a template to get started with writing a scanner plugin."]}),"\n",(0,i.jsx)(e.h1,{id:"scanner-plugin-interface",children:"Scanner Plugin Interface"}),"\n",(0,i.jsx)(e.admonition,{type:"note",children:(0,i.jsxs)(e.p,{children:[(0,i.jsx)(e.code,{children:"alpha"})," versions of the API are not guarenteed to be backwards compatible. Once the API graduates to ",(0,i.jsx)(e.code,{children:"beta"})," and ",(0,i.jsx)(e.code,{children:"stable"}),", it will be backwards compatible."]})}),"\n",(0,i.jsx)(e.p,{children:"Scanner plugins must implement the following interface:"}),"\n",(0,i.jsx)(e.h2,{id:"v1alpha1",children:"v1alpha1"}),"\n",(0,i.jsx)(e.pre,{children:(0,i.jsx)(e.code,{className:"language-go",children:'type UpdateManifest struct {\n // API version of the interface (e.g. v1alpha1)\n APIVersion string `json:"apiVersion"`\n // Metadata contains information about the OS and config\n Metadata Metadata `json:"metadata"`\n // Updates is a list of UpdatePackage that contains information about the package updates\n Updates UpdatePackages `json:"updates"`\n}\n\n// UpdatePackages is a list of UpdatePackage\ntype UpdatePackages []UpdatePackage\n\n// Metadata contains information about the OS and config\ntype Metadata struct {\n OS OS `json:"os"`\n Config Config `json:"config"`\n}\n\ntype OS struct {\n // OS Type (e.g. debian, alpine, etc.)\n Type string `json:"type"`\n // OS Version (e.g. 11.3)\n Version string `json:"version"`\n}\n\n// Config contains information about the config\ntype Config struct {\n // OS Architecture (e.g. amd64, arm64)\n Arch string `json:"arch"`\n}\n\n// UpdatePackage contains information about the package update\ntype UpdatePackage struct {\n // Package name\n Name string `json:"name"`\n // Installed version\n InstalledVersion string `json:"installedVersion"`\n // Fixed version\n FixedVersion string `json:"fixedVersion"`\n // Vulnerability ID\n VulnerabilityID string `json:"vulnerabilityID"`\n}\n'})}),"\n",(0,i.jsx)(e.p,{children:"From the above, we can see that the plugin must return a JSON object via standard out with the following fields. For example:"}),"\n",(0,i.jsx)(e.pre,{children:(0,i.jsx)(e.code,{className:"language-json",children:'{\n "apiVersion": "v1alpha1",\n "metadata": {\n "os": {\n "type": "debian",\n "version": "11.3",\n },\n "config": {\n "arch": "amd64"\n }\n },\n "updates": [\n {\n "name": "libcurl4",\n "installedVersion": "7.74.0-1.3+deb11u1",\n "fixedVersion": "7.74.0-1.3+deb11u2",\n "vulnerabilityID": "CVE-2021-22945"\n }\n ]\n}\n'})})]})}function u(n={}){const{wrapper:e}={...(0,a.R)(),...n.components};return e?(0,i.jsx)(e,{...n,children:(0,i.jsx)(d,{...n})}):d(n)}},8453:(n,e,t)=>{t.d(e,{R:()=>r,x:()=>o});var i=t(6540);const a={},s=i.createContext(a);function r(n){const e=i.useContext(s);return i.useMemo((function(){return"function"==typeof n?n(e):{...e,...n}}),[e,n])}function o(n){let e;return e=n.disableParentContext?"function"==typeof n.components?n.components(a):n.components||a:r(n.components),i.createElement(s.Provider,{value:e},n.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/5e95c892.b1f5748f.js b/website/assets/js/5e95c892.b1f5748f.js new file mode 100644 index 00000000..b9efe80f --- /dev/null +++ b/website/assets/js/5e95c892.b1f5748f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9647],{7121:(e,s,r)=>{r.r(s),r.d(s,{default:()=>l});r(6540);var t=r(4164),u=r(1003),a=r(7559),c=r(2831),n=r(781),i=r(4848);function l(e){return(0,i.jsx)(u.e3,{className:(0,t.A)(a.G.wrapper.docsPages),children:(0,i.jsx)(n.A,{children:(0,c.v)(e.route.routes)})})}}}]); \ No newline at end of file diff --git a/website/assets/js/5fc6e4d2.b1cefb99.js b/website/assets/js/5fc6e4d2.b1cefb99.js deleted file mode 100644 index 98618212..00000000 --- a/website/assets/js/5fc6e4d2.b1cefb99.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3602],{3905:(e,n,t)=>{t.d(n,{Zo:()=>p,kt:()=>f});var o=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);n&&(o=o.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,o)}return t}function a(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?r(Object(t),!0).forEach((function(n){i(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):r(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))}))}return e}function l(e,n){if(null==e)return{};var t,o,i=function(e,n){if(null==e)return{};var t,o,i={},r=Object.keys(e);for(o=0;o<r.length;o++)t=r[o],n.indexOf(t)>=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o<r.length;o++)t=r[o],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var c=o.createContext({}),s=function(e){var n=o.useContext(c),t=n;return e&&(t="function"==typeof e?e(n):a(a({},n),e)),t},p=function(e){var n=s(e.components);return o.createElement(c.Provider,{value:n},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var n=e.children;return o.createElement(o.Fragment,{},n)}},m=o.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,c=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=s(t),m=i,f=u["".concat(c,".").concat(m)]||u[m]||d[m]||r;return t?o.createElement(f,a(a({ref:n},p),{},{components:t})):o.createElement(f,a({ref:n},p))}));function f(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,a=new Array(r);a[0]=m;var l={};for(var c in n)hasOwnProperty.call(n,c)&&(l[c]=n[c]);l.originalType=e,l[u]="string"==typeof e?e:i,a[1]=l;for(var s=2;s<r;s++)a[s]=t[s];return o.createElement.apply(null,a)}return o.createElement.apply(null,t)}m.displayName="MDXCreateElement"},9675:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>r,metadata:()=>l,toc:()=>s});var o=t(7462),i=(t(7294),t(3905));const r={title:"Code of Conduct"},a="Contributor Covenant Code of Conduct",l={unversionedId:"code-of-conduct",id:"version-v0.6.x/code-of-conduct",title:"Code of Conduct",description:"Our Pledge",source:"@site/versioned_docs/version-v0.6.x/code-of-conduct.md",sourceDirName:".",slug:"/code-of-conduct",permalink:"/copacetic/website/code-of-conduct",draft:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Code of Conduct"},sidebar:"sidebar",previous:{title:"Contributing",permalink:"/copacetic/website/contributing"},next:{title:"Design",permalink:"/copacetic/website/design"}},c={},s=[{value:"Our Pledge",id:"our-pledge",level:2},{value:"Our Standards",id:"our-standards",level:2},{value:"Enforcement Responsibilities",id:"enforcement-responsibilities",level:2},{value:"Scope",id:"scope",level:2},{value:"Enforcement",id:"enforcement",level:2},{value:"Enforcement Guidelines",id:"enforcement-guidelines",level:2},{value:"1. Correction",id:"1-correction",level:3},{value:"2. Warning",id:"2-warning",level:3},{value:"3. Temporary Ban",id:"3-temporary-ban",level:3},{value:"4. Permanent Ban",id:"4-permanent-ban",level:3},{value:"Attribution",id:"attribution",level:2}],p={toc:s};function u(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,o.Z)({},p,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"contributor-covenant-code-of-conduct"},"Contributor Covenant Code of Conduct"),(0,i.kt)("h2",{id:"our-pledge"},"Our Pledge"),(0,i.kt)("p",null,"We as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation."),(0,i.kt)("p",null,"We pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community."),(0,i.kt)("h2",{id:"our-standards"},"Our Standards"),(0,i.kt)("p",null,"Examples of behavior that contributes to a positive environment for our\ncommunity include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Demonstrating empathy and kindness toward other people"),(0,i.kt)("li",{parentName:"ul"},"Being respectful of differing opinions, viewpoints, and experiences"),(0,i.kt)("li",{parentName:"ul"},"Giving and gracefully accepting constructive feedback"),(0,i.kt)("li",{parentName:"ul"},"Accepting responsibility and apologizing to those affected by our mistakes,\nand learning from the experience"),(0,i.kt)("li",{parentName:"ul"},"Focusing on what is best not just for us as individuals, but for the overall\ncommunity")),(0,i.kt)("p",null,"Examples of unacceptable behavior include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"The use of sexualized language or imagery, and sexual attention or advances of\nany kind"),(0,i.kt)("li",{parentName:"ul"},"Trolling, insulting or derogatory comments, and personal or political attacks"),(0,i.kt)("li",{parentName:"ul"},"Public or private harassment"),(0,i.kt)("li",{parentName:"ul"},"Publishing others' private information, such as a physical or email address,\nwithout their explicit permission"),(0,i.kt)("li",{parentName:"ul"},"Other conduct which could reasonably be considered inappropriate in a\nprofessional setting")),(0,i.kt)("h2",{id:"enforcement-responsibilities"},"Enforcement Responsibilities"),(0,i.kt)("p",null,"Community leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful."),(0,i.kt)("p",null,"Community leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate."),(0,i.kt)("h2",{id:"scope"},"Scope"),(0,i.kt)("p",null,"This Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event."),(0,i.kt)("h2",{id:"enforcement"},"Enforcement"),(0,i.kt)("p",null,"Instances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n",(0,i.kt)("inlineCode",{parentName:"p"},"project-copacetic@googlegroups.com"),".\nAll complaints will be reviewed and investigated promptly and fairly."),(0,i.kt)("p",null,"All community leaders are obligated to respect the privacy and security of the\nreporter of any incident."),(0,i.kt)("h2",{id:"enforcement-guidelines"},"Enforcement Guidelines"),(0,i.kt)("p",null,"Community leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:"),(0,i.kt)("h3",{id:"1-correction"},"1. Correction"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested."),(0,i.kt)("h3",{id:"2-warning"},"2. Warning"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": A violation through a single incident or series of\nactions."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban."),(0,i.kt)("h3",{id:"3-temporary-ban"},"3. Temporary Ban"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": A serious violation of community standards, including\nsustained inappropriate behavior."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban."),(0,i.kt)("h3",{id:"4-permanent-ban"},"4. Permanent Ban"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A permanent ban from any sort of public interaction within the\ncommunity."),(0,i.kt)("h2",{id:"attribution"},"Attribution"),(0,i.kt)("p",null,"This Code of Conduct is adapted from the ",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org"},"Contributor Covenant"),",\nversion 2.1, available at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"},"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"),"."),(0,i.kt)("p",null,"Community Impact Guidelines were inspired by\n",(0,i.kt)("a",{parentName:"p",href:"https://github.com/mozilla/diversity"},"Mozilla's code of conduct enforcement ladder"),"."),(0,i.kt)("p",null,"For answers to common questions about this code of conduct, see the FAQ at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/faq"},"https://www.contributor-covenant.org/faq"),". Translations are available at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/translations"},"https://www.contributor-covenant.org/translations"),"."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/5fc6e4d2.bc73c3e3.js b/website/assets/js/5fc6e4d2.bc73c3e3.js new file mode 100644 index 00000000..f4487bd8 --- /dev/null +++ b/website/assets/js/5fc6e4d2.bc73c3e3.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4140],{3350:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>l});var o=i(4848),t=i(8453);const r={title:"Code of Conduct"},a="Contributor Covenant Code of Conduct",s={id:"code-of-conduct",title:"Code of Conduct",description:"Our Pledge",source:"@site/versioned_docs/version-v0.6.x/code-of-conduct.md",sourceDirName:".",slug:"/code-of-conduct",permalink:"/copacetic/website/code-of-conduct",draft:!1,unlisted:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Code of Conduct"},sidebar:"sidebar",previous:{title:"Contributing",permalink:"/copacetic/website/contributing"},next:{title:"Design",permalink:"/copacetic/website/design"}},c={},l=[{value:"Our Pledge",id:"our-pledge",level:2},{value:"Our Standards",id:"our-standards",level:2},{value:"Enforcement Responsibilities",id:"enforcement-responsibilities",level:2},{value:"Scope",id:"scope",level:2},{value:"Enforcement",id:"enforcement",level:2},{value:"Enforcement Guidelines",id:"enforcement-guidelines",level:2},{value:"1. Correction",id:"1-correction",level:3},{value:"2. Warning",id:"2-warning",level:3},{value:"3. Temporary Ban",id:"3-temporary-ban",level:3},{value:"4. Permanent Ban",id:"4-permanent-ban",level:3},{value:"Attribution",id:"attribution",level:2}];function d(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",li:"li",p:"p",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.h1,{id:"contributor-covenant-code-of-conduct",children:"Contributor Covenant Code of Conduct"}),"\n",(0,o.jsx)(n.h2,{id:"our-pledge",children:"Our Pledge"}),"\n",(0,o.jsx)(n.p,{children:"We as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation."}),"\n",(0,o.jsx)(n.p,{children:"We pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community."}),"\n",(0,o.jsx)(n.h2,{id:"our-standards",children:"Our Standards"}),"\n",(0,o.jsx)(n.p,{children:"Examples of behavior that contributes to a positive environment for our\ncommunity include:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"Demonstrating empathy and kindness toward other people"}),"\n",(0,o.jsx)(n.li,{children:"Being respectful of differing opinions, viewpoints, and experiences"}),"\n",(0,o.jsx)(n.li,{children:"Giving and gracefully accepting constructive feedback"}),"\n",(0,o.jsx)(n.li,{children:"Accepting responsibility and apologizing to those affected by our mistakes,\nand learning from the experience"}),"\n",(0,o.jsx)(n.li,{children:"Focusing on what is best not just for us as individuals, but for the overall\ncommunity"}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"Examples of unacceptable behavior include:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"The use of sexualized language or imagery, and sexual attention or advances of\nany kind"}),"\n",(0,o.jsx)(n.li,{children:"Trolling, insulting or derogatory comments, and personal or political attacks"}),"\n",(0,o.jsx)(n.li,{children:"Public or private harassment"}),"\n",(0,o.jsx)(n.li,{children:"Publishing others' private information, such as a physical or email address,\nwithout their explicit permission"}),"\n",(0,o.jsx)(n.li,{children:"Other conduct which could reasonably be considered inappropriate in a\nprofessional setting"}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"enforcement-responsibilities",children:"Enforcement Responsibilities"}),"\n",(0,o.jsx)(n.p,{children:"Community leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful."}),"\n",(0,o.jsx)(n.p,{children:"Community leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate."}),"\n",(0,o.jsx)(n.h2,{id:"scope",children:"Scope"}),"\n",(0,o.jsx)(n.p,{children:"This Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event."}),"\n",(0,o.jsx)(n.h2,{id:"enforcement",children:"Enforcement"}),"\n",(0,o.jsxs)(n.p,{children:["Instances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n",(0,o.jsx)(n.code,{children:"project-copacetic@googlegroups.com"}),".\nAll complaints will be reviewed and investigated promptly and fairly."]}),"\n",(0,o.jsx)(n.p,{children:"All community leaders are obligated to respect the privacy and security of the\nreporter of any incident."}),"\n",(0,o.jsx)(n.h2,{id:"enforcement-guidelines",children:"Enforcement Guidelines"}),"\n",(0,o.jsx)(n.p,{children:"Community leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:"}),"\n",(0,o.jsx)(n.h3,{id:"1-correction",children:"1. Correction"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested."]}),"\n",(0,o.jsx)(n.h3,{id:"2-warning",children:"2. Warning"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": A violation through a single incident or series of\nactions."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban."]}),"\n",(0,o.jsx)(n.h3,{id:"3-temporary-ban",children:"3. Temporary Ban"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": A serious violation of community standards, including\nsustained inappropriate behavior."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban."]}),"\n",(0,o.jsx)(n.h3,{id:"4-permanent-ban",children:"4. Permanent Ban"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A permanent ban from any sort of public interaction within the\ncommunity."]}),"\n",(0,o.jsx)(n.h2,{id:"attribution",children:"Attribution"}),"\n",(0,o.jsxs)(n.p,{children:["This Code of Conduct is adapted from the ",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org",children:"Contributor Covenant"}),",\nversion 2.1, available at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html",children:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["Community Impact Guidelines were inspired by\n",(0,o.jsx)(n.a,{href:"https://github.com/mozilla/diversity",children:"Mozilla's code of conduct enforcement ladder"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["For answers to common questions about this code of conduct, see the FAQ at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/faq",children:"https://www.contributor-covenant.org/faq"}),". Translations are available at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/translations",children:"https://www.contributor-covenant.org/translations"}),"."]})]})}function u(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>a,x:()=>s});var o=i(6540);const t={},r=o.createContext(t);function a(e){const n=o.useContext(r);return o.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function s(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:a(e.components),o.createElement(r.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/6173fde8.2d07e3f9.js b/website/assets/js/6173fde8.2d07e3f9.js new file mode 100644 index 00000000..5f16c8c1 --- /dev/null +++ b/website/assets/js/6173fde8.2d07e3f9.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1439],{4489:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>a,contentTitle:()=>r,default:()=>h,frontMatter:()=>o,metadata:()=>c,toc:()=>l});var t=i(4848),s=i(8453);const o={title:"Quick Start"},r=void 0,c={id:"quick-start",title:"Quick Start",description:"This sample illustrates how to patch containers using vulnerability reports with copa.",source:"@site/versioned_docs/version-v0.1.x/quick-start.md",sourceDirName:".",slug:"/quick-start",permalink:"/copacetic/website/v0.1.x/quick-start",draft:!1,unlisted:!1,tags:[],version:"v0.1.x",frontMatter:{title:"Quick Start"},sidebar:"sidebar",previous:{title:"Installation",permalink:"/copacetic/website/v0.1.x/installation"},next:{title:"Design",permalink:"/copacetic/website/v0.1.x/design"}},a={},l=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Sample Steps",id:"sample-steps",level:2}];function d(e){const n={a:"a",blockquote:"blockquote",code:"code",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(n.p,{children:["This sample illustrates how to patch containers using vulnerability reports with ",(0,t.jsx)(n.code,{children:"copa"}),"."]}),"\n",(0,t.jsx)(n.h2,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["An Ubuntu 22.04 VM configured through the ",(0,t.jsx)(n.a,{href:"/copacetic/website/v0.1.x/installation",children:"setup instructions"})," or a VSCode ",(0,t.jsx)(n.a,{href:"./contributing.md/#visual-studio-code-development-container",children:"devcontainer"})," environment. This includes:","\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"copa"})," tool ",(0,t.jsx)(n.a,{href:"/copacetic/website/v0.1.x/installation",children:"built & pathed"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://github.com/moby/buildkit/#quick-start",children:"buildkit"})," daemon installed & pathed."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://docs.docker.com/desktop/linux/install/#generic-installation-steps",children:"docker"})," daemon running and CLI installed & pathed."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://aquasecurity.github.io/trivy/latest/getting-started/installation/",children:"trivy CLI"})," installed & pathed."]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,t.jsx)(n.h2,{id:"sample-steps",children:"Sample Steps"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6\n"})}),"\n",(0,t.jsx)(n.p,{children:"You can also see the existing patchable vulnerabilities in table form on the shell with:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6\n\n"})}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsxs)(n.p,{children:["Patch the image using the Trivy report. You will need to start ",(0,t.jsx)(n.code,{children:"buildkitd"})," if it is not already running:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"sudo buildkitd &\nsudo copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched\n"})}),"\n",(0,t.jsxs)(n.p,{children:["Alternatively, you can run ",(0,t.jsx)(n.code,{children:"buildkitd"})," in a container, which allows copa to be run without root access to the local buildkit socket:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'export BUILDKIT_VERSION=v0.11.4\nexport BUILDKIT_PORT=8888\ndocker run \\\n --detach \\\n --rm \\\n --privileged \\\n -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION" \\\n --addr tcp://0.0.0.0:$BUILDKIT_PORT\ncopa patch \\\n -i docker.io/library/nginx:1.21.6 \\\n -r nginx.1.21.6.json \\\n -t 1.21.6-patched \\\n -a tcp://0.0.0.0:$BUILDKIT_PORT\n'})}),"\n",(0,t.jsxs)(n.p,{children:["In either case, ",(0,t.jsx)(n.code,{children:"copa"})," is non-destructive and exports a new image with the specified ",(0,t.jsx)(n.code,{children:"1.21.6-patched"})," label to the local Docker daemon."]}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"NOTE:"})," if you're running this sample against an image from a private registry instead,\nensure that the credentials are configured in the default Docker config.json before running ",(0,t.jsx)(n.code,{children:"copa patch"}),",\nfor example, via ",(0,t.jsx)(n.code,{children:"sudo docker login -u <user> -p <password> <registry>"}),"."]}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Scan the patched image and verify that the vulnerabilities have been patched:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched\n"})}),"\n",(0,t.jsxs)(n.p,{children:["You can also inspect the structure of the patched image with ",(0,t.jsx)(n.code,{children:"docker history"})," to see the new patch layer appended to the image:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'$ docker history docker.io/library/nginx:1.21.6-patched\nIMAGE CREATED CREATED BY SIZE COMMENT\na372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins\u2026 26.1MB buildkit.exporter.image.v0\n<missing> 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0\n<missing> 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0\n<missing> 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0\n<missing> 3 months ago COPY 30-tune-worker-processes.sh /docker-ent\u2026 4.61kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent\u2026 1.04kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker\u2026 1.96kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0\n<missing> 3 months ago RUN /bin/sh -c set -x && addgroup --syst\u2026 61.1MB buildkit.dockerfile.v0\n<missing> 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0\n<missing> 3 months ago LABEL maintainer=NGINX Docker Maintainers <d\u2026 0B buildkit.dockerfile.v0\n<missing> 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B\n<missing> 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55\u2026 80.4MB\n'})}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Run the container to verify that the image has no regressions:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'$ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched\n/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration\n/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh\n10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf\n10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh\n/docker-entrypoint.sh: Configuration complete; ready for start up\n2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method\n2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2\n2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)\n2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2\n2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576\n2022/05/16 18:00:17 [notice] 1#1: start worker processes\n2022/05/16 18:00:17 [notice] 1#1: start worker process 31\n2022/05/16 18:00:17 [notice] 1#1: start worker process 32\n2022/05/16 18:00:17 [notice] 1#1: start worker process 33\n2022/05/16 18:00:17 [notice] 1#1: start worker process 34\n2022/05/16 18:00:17 [notice] 1#1: start worker process 35\n2022/05/16 18:00:17 [notice] 1#1: start worker process 36\n2022/05/16 18:00:17 [notice] 1#1: start worker process 37\n2022/05/16 18:00:17 [notice] 1#1: start worker process 38\n2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received\n'})}),"\n",(0,t.jsxs)(n.p,{children:["You can stop the container by opening a new shell instance and running: ",(0,t.jsx)(n.code,{children:"docker stop nginx-test"})]}),"\n"]}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(d,{...e})}):d(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>r,x:()=>c});var t=i(6540);const s={},o=t.createContext(s);function r(e){const n=t.useContext(o);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),t.createElement(o.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/6173fde8.55493801.js b/website/assets/js/6173fde8.55493801.js deleted file mode 100644 index 69767f5b..00000000 --- a/website/assets/js/6173fde8.55493801.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7451],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>k});var i=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?a(Object(n),!0).forEach((function(t){r(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):a(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,i,r=function(e,t){if(null==e)return{};var n,i,r={},a=Object.keys(e);for(i=0;i<a.length;i++)n=a[i],t.indexOf(n)>=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(i=0;i<a.length;i++)n=a[i],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=i.createContext({}),c=function(e){var t=i.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return i.createElement(l.Provider,{value:t},e.children)},d="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},m=i.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),d=c(n),m=r,k=d["".concat(l,".").concat(m)]||d[m]||u[m]||a;return n?i.createElement(k,o(o({ref:t},p),{},{components:n})):i.createElement(k,o({ref:t},p))}));function k(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,o=new Array(a);o[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[d]="string"==typeof e?e:r,o[1]=s;for(var c=2;c<a;c++)o[c]=n[c];return i.createElement.apply(null,o)}return i.createElement.apply(null,n)}m.displayName="MDXCreateElement"},7308:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>d,frontMatter:()=>a,metadata:()=>s,toc:()=>c});var i=n(7462),r=(n(7294),n(3905));const a={title:"Quick Start"},o=void 0,s={unversionedId:"quick-start",id:"version-v0.1.x/quick-start",title:"Quick Start",description:"This sample illustrates how to patch containers using vulnerability reports with copa.",source:"@site/versioned_docs/version-v0.1.x/quick-start.md",sourceDirName:".",slug:"/quick-start",permalink:"/copacetic/website/v0.1.x/quick-start",draft:!1,tags:[],version:"v0.1.x",frontMatter:{title:"Quick Start"},sidebar:"sidebar",previous:{title:"Installation",permalink:"/copacetic/website/v0.1.x/installation"},next:{title:"Design",permalink:"/copacetic/website/v0.1.x/design"}},l={},c=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Sample Steps",id:"sample-steps",level:2}],p={toc:c};function d(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,i.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"This sample illustrates how to patch containers using vulnerability reports with ",(0,r.kt)("inlineCode",{parentName:"p"},"copa"),"."),(0,r.kt)("h2",{id:"prerequisites"},"Prerequisites"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"An Ubuntu 22.04 VM configured through the ",(0,r.kt)("a",{parentName:"li",href:"/copacetic/website/v0.1.x/installation"},"setup instructions")," or a VSCode ",(0,r.kt)("a",{parentName:"li",href:"/copacetic/website/v0.1.x/contributing/#visual-studio-code-development-container"},"devcontainer")," environment. This includes:",(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"copa")," tool ",(0,r.kt)("a",{parentName:"li",href:"/copacetic/website/v0.1.x/installation"},"built & pathed"),"."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://github.com/moby/buildkit/#quick-start"},"buildkit")," daemon installed & pathed."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://docs.docker.com/desktop/linux/install/#generic-installation-steps"},"docker")," daemon running and CLI installed & pathed."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://aquasecurity.github.io/trivy/latest/getting-started/installation/"},"trivy CLI")," installed & pathed.")))),(0,r.kt)("h2",{id:"sample-steps"},"Sample Steps"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("p",{parentName:"li"},"Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6\n")),(0,r.kt)("p",{parentName:"li"},"You can also see the existing patchable vulnerabilities in table form on the shell with:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6\n\n"))),(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("p",{parentName:"li"},"Patch the image using the Trivy report. You will need to start ",(0,r.kt)("inlineCode",{parentName:"p"},"buildkitd")," if it is not already running:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"sudo buildkitd &\nsudo copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched\n")),(0,r.kt)("p",{parentName:"li"},"Alternatively, you can run ",(0,r.kt)("inlineCode",{parentName:"p"},"buildkitd")," in a container, which allows copa to be run without root access to the local buildkit socket:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'export BUILDKIT_VERSION=v0.11.4\nexport BUILDKIT_PORT=8888\ndocker run \\\n --detach \\\n --rm \\\n --privileged \\\n -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION" \\\n --addr tcp://0.0.0.0:$BUILDKIT_PORT\ncopa patch \\\n -i docker.io/library/nginx:1.21.6 \\\n -r nginx.1.21.6.json \\\n -t 1.21.6-patched \\\n -a tcp://0.0.0.0:$BUILDKIT_PORT\n')),(0,r.kt)("p",{parentName:"li"},"In either case, ",(0,r.kt)("inlineCode",{parentName:"p"},"copa")," is non-destructive and exports a new image with the specified ",(0,r.kt)("inlineCode",{parentName:"p"},"1.21.6-patched")," label to the local Docker daemon."),(0,r.kt)("blockquote",{parentName:"li"},(0,r.kt)("p",{parentName:"blockquote"},(0,r.kt)("strong",{parentName:"p"},"NOTE:")," if you're running this sample against an image from a private registry instead,\nensure that the credentials are configured in the default Docker config.json before running ",(0,r.kt)("inlineCode",{parentName:"p"},"copa patch"),",\nfor example, via ",(0,r.kt)("inlineCode",{parentName:"p"},"sudo docker login -u <user> -p <password> <registry>"),"."))),(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("p",{parentName:"li"},"Scan the patched image and verify that the vulnerabilities have been patched:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched\n")),(0,r.kt)("p",{parentName:"li"},"You can also inspect the structure of the patched image with ",(0,r.kt)("inlineCode",{parentName:"p"},"docker history")," to see the new patch layer appended to the image:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'$ docker history docker.io/library/nginx:1.21.6-patched\nIMAGE CREATED CREATED BY SIZE COMMENT\na372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins\u2026 26.1MB buildkit.exporter.image.v0\n<missing> 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0\n<missing> 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0\n<missing> 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0\n<missing> 3 months ago COPY 30-tune-worker-processes.sh /docker-ent\u2026 4.61kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent\u2026 1.04kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker\u2026 1.96kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0\n<missing> 3 months ago RUN /bin/sh -c set -x && addgroup --syst\u2026 61.1MB buildkit.dockerfile.v0\n<missing> 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0\n<missing> 3 months ago LABEL maintainer=NGINX Docker Maintainers <d\u2026 0B buildkit.dockerfile.v0\n<missing> 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B\n<missing> 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55\u2026 80.4MB\n'))),(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("p",{parentName:"li"},"Run the container to verify that the image has no regressions:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'$ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched\n/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration\n/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh\n10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf\n10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh\n/docker-entrypoint.sh: Configuration complete; ready for start up\n2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method\n2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2\n2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)\n2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2\n2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576\n2022/05/16 18:00:17 [notice] 1#1: start worker processes\n2022/05/16 18:00:17 [notice] 1#1: start worker process 31\n2022/05/16 18:00:17 [notice] 1#1: start worker process 32\n2022/05/16 18:00:17 [notice] 1#1: start worker process 33\n2022/05/16 18:00:17 [notice] 1#1: start worker process 34\n2022/05/16 18:00:17 [notice] 1#1: start worker process 35\n2022/05/16 18:00:17 [notice] 1#1: start worker process 36\n2022/05/16 18:00:17 [notice] 1#1: start worker process 37\n2022/05/16 18:00:17 [notice] 1#1: start worker process 38\n2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received\n')),(0,r.kt)("p",{parentName:"li"},"You can stop the container by opening a new shell instance and running: ",(0,r.kt)("inlineCode",{parentName:"p"},"docker stop nginx-test")))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/683b919f.5b96ddd9.js b/website/assets/js/683b919f.5b96ddd9.js deleted file mode 100644 index 21e1f184..00000000 --- a/website/assets/js/683b919f.5b96ddd9.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2932],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function p(e,t){if(null==e)return{};var n,r,a=function(e,t){if(null==e)return{};var n,r,a={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},s="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,u=p(e,["components","mdxType","originalType","parentName"]),s=l(n),m=a,f=s["".concat(c,".").concat(m)]||s[m]||d[m]||o;return n?r.createElement(f,i(i({ref:t},u),{},{components:n})):r.createElement(f,i({ref:t},u))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=m;var p={};for(var c in t)hasOwnProperty.call(t,c)&&(p[c]=t[c]);p.originalType=e,p[s]="string"==typeof e?e:a,i[1]=p;for(var l=2;l<o;l++)i[l]=n[l];return r.createElement.apply(null,i)}return r.createElement.apply(null,n)}m.displayName="MDXCreateElement"},2835:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>s,frontMatter:()=>o,metadata:()=>p,toc:()=>l});var r=n(7462),a=(n(7294),n(3905));const o={title:"Output"},i=void 0,p={unversionedId:"output",id:"version-v0.6.x/output",title:"Output",description:"Experimental: This feature might change without preserving backwards compatibility.",source:"@site/versioned_docs/version-v0.6.x/output.md",sourceDirName:".",slug:"/output",permalink:"/copacetic/website/output",draft:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Output"},sidebar:"sidebar",previous:{title:"Custom buildkit addresses",permalink:"/copacetic/website/custom-address"},next:{title:"Scanner Plugins",permalink:"/copacetic/website/scanner-plugins"}},c={},l=[{value:"OpenVEX",id:"openvex",level:2}],u={toc:l};function s(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("admonition",{type:"caution"},(0,a.kt)("p",{parentName:"admonition"},"Experimental: This feature might change without preserving backwards compatibility.")),(0,a.kt)("p",null,"Copa optionally outputs a Vulnerability Exploitability eXchange (VEX) file as a result of the patching process to surface the vulnerabilities and packages that were patched."),(0,a.kt)("p",null,"Currently, Copa supports the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/openvex"},"OpenVEX")," format, but it can be extended to support other formats."),(0,a.kt)("h2",{id:"openvex"},"OpenVEX"),(0,a.kt)("p",null,"OpenVEX is an implementation of Vulnerability Exploitability eXchange (VEX) format. For more information, see ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/openvex/spec/"},"OpenVEX specification"),"."),(0,a.kt)("admonition",{type:"tip"},(0,a.kt)("ul",{parentName:"admonition"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"Use ",(0,a.kt)("inlineCode",{parentName:"p"},"COPA_VEX_AUTHOR")," environment variable to set the author of the VEX document. If it's not set, the author will default to ",(0,a.kt)("inlineCode",{parentName:"p"},"Project Copacetic"),".")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"A VEX document must contain at least one VEX statement. If there are no fixed vulnerabilities, Copa will not generate a VEX document.")))),(0,a.kt)("p",null,"To generate a VEX document using OpenVEX, use ",(0,a.kt)("inlineCode",{parentName:"p"},'--format="openvex"')," flag, and use ",(0,a.kt)("inlineCode",{parentName:"p"},"--output")," to specify a file path. For example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},'copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --format="openvex" --output "nginx.1.21.6-vex.json"\n')),(0,a.kt)("p",null,"This will generate a VEX Document that looks like:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-json"},'{\n "@context": "https://openvex.dev/ns",\n "@id": "https://openvex.dev/docs/public/vex-a6c44ec1d79e9dd4190dc01b4ecf7527ebb26bd37c01e32e6efcd203ae00d2a5",\n "author": "Project Copacetic",\n "timestamp": "2023-10-11T00:15:00.114768055Z",\n "version": 1,\n "tooling": "Project Copacetic",\n "statements": [\n {\n "vulnerability": {\n "@id": "CVE-2021-22945"\n },\n "products": [\n {\n "@id": "pkg:oci/docker.io/library/nginx:1.21.6-patched",\n "subcomponents": [\n {\n "@id": "pkg:deb/debian/curl@7.74.0-1.3+deb11u2?arch=amd64"\n },\n {\n "@id": "pkg:deb/debian/libcurl4@7.74.0-1.3+deb11u2?arch=amd64"\n }\n ]\n }\n ],\n "status": "fixed"\n },\n ...\n}\n')))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/683b919f.f74ca5f0.js b/website/assets/js/683b919f.f74ca5f0.js new file mode 100644 index 00000000..6d3c9a51 --- /dev/null +++ b/website/assets/js/683b919f.f74ca5f0.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4521],{65:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>r,contentTitle:()=>a,default:()=>d,frontMatter:()=>s,metadata:()=>c,toc:()=>l});var i=t(4848),o=t(8453);const s={title:"Output"},a=void 0,c={id:"output",title:"Output",description:"Experimental: This feature might change without preserving backwards compatibility.",source:"@site/versioned_docs/version-v0.6.x/output.md",sourceDirName:".",slug:"/output",permalink:"/copacetic/website/output",draft:!1,unlisted:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Output"},sidebar:"sidebar",previous:{title:"Custom buildkit addresses",permalink:"/copacetic/website/custom-address"},next:{title:"Scanner Plugins",permalink:"/copacetic/website/scanner-plugins"}},r={},l=[{value:"OpenVEX",id:"openvex",level:2}];function p(e){const n={a:"a",admonition:"admonition",code:"code",h2:"h2",li:"li",p:"p",pre:"pre",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.admonition,{type:"caution",children:(0,i.jsx)(n.p,{children:"Experimental: This feature might change without preserving backwards compatibility."})}),"\n",(0,i.jsx)(n.p,{children:"Copa optionally outputs a Vulnerability Exploitability eXchange (VEX) file as a result of the patching process to surface the vulnerabilities and packages that were patched."}),"\n",(0,i.jsxs)(n.p,{children:["Currently, Copa supports the ",(0,i.jsx)(n.a,{href:"https://github.com/openvex",children:"OpenVEX"})," format, but it can be extended to support other formats."]}),"\n",(0,i.jsx)(n.h2,{id:"openvex",children:"OpenVEX"}),"\n",(0,i.jsxs)(n.p,{children:["OpenVEX is an implementation of Vulnerability Exploitability eXchange (VEX) format. For more information, see ",(0,i.jsx)(n.a,{href:"https://github.com/openvex/spec/",children:"OpenVEX specification"}),"."]}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["Use ",(0,i.jsx)(n.code,{children:"COPA_VEX_AUTHOR"})," environment variable to set the author of the VEX document. If it's not set, the author will default to ",(0,i.jsx)(n.code,{children:"Project Copacetic"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:"A VEX document must contain at least one VEX statement. If there are no fixed vulnerabilities, Copa will not generate a VEX document."}),"\n"]}),"\n"]})}),"\n",(0,i.jsxs)(n.p,{children:["To generate a VEX document using OpenVEX, use ",(0,i.jsx)(n.code,{children:'--format="openvex"'})," flag, and use ",(0,i.jsx)(n.code,{children:"--output"})," to specify a file path. For example:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --format="openvex" --output "nginx.1.21.6-vex.json"\n'})}),"\n",(0,i.jsx)(n.p,{children:"This will generate a VEX Document that looks like:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-json",children:'{\n "@context": "https://openvex.dev/ns",\n "@id": "https://openvex.dev/docs/public/vex-a6c44ec1d79e9dd4190dc01b4ecf7527ebb26bd37c01e32e6efcd203ae00d2a5",\n "author": "Project Copacetic",\n "timestamp": "2023-10-11T00:15:00.114768055Z",\n "version": 1,\n "tooling": "Project Copacetic",\n "statements": [\n {\n "vulnerability": {\n "@id": "CVE-2021-22945"\n },\n "products": [\n {\n "@id": "pkg:oci/docker.io/library/nginx:1.21.6-patched",\n "subcomponents": [\n {\n "@id": "pkg:deb/debian/curl@7.74.0-1.3+deb11u2?arch=amd64"\n },\n {\n "@id": "pkg:deb/debian/libcurl4@7.74.0-1.3+deb11u2?arch=amd64"\n }\n ]\n }\n ],\n "status": "fixed"\n },\n ...\n}\n'})})]})}function d(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(p,{...e})}):p(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>a,x:()=>c});var i=t(6540);const o={},s=i.createContext(o);function a(e){const n=i.useContext(s);return i.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:a(e.components),i.createElement(s.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/6bd0979b.2824c577.js b/website/assets/js/6bd0979b.2824c577.js deleted file mode 100644 index bb00db9b..00000000 --- a/website/assets/js/6bd0979b.2824c577.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8838],{9358:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"v0.3.x","label":"v0.3.x","banner":"unmaintained","badge":true,"noIndex":false,"className":"docs-version-v0.3.x","isLast":false,"docsSidebars":{"sidebar":[{"type":"link","label":"Introduction","href":"/copacetic/website/v0.3.x/","docId":"introduction"},{"type":"link","label":"Installation","href":"/copacetic/website/v0.3.x/installation","docId":"installation"},{"type":"link","label":"Quick Start","href":"/copacetic/website/v0.3.x/quick-start","docId":"quick-start"},{"type":"link","label":"Design","href":"/copacetic/website/v0.3.x/design","docId":"design"},{"type":"link","label":"FAQ","href":"/copacetic/website/v0.3.x/faq","docId":"faq"},{"type":"link","label":"Contributing","href":"/copacetic/website/v0.3.x/contributing","docId":"contributing"},{"type":"link","label":"Code of Conduct","href":"/copacetic/website/v0.3.x/code-of-conduct","docId":"code-of-conduct"}]},"docs":{"code-of-conduct":{"id":"code-of-conduct","title":"Code of Conduct","description":"Our Pledge","sidebar":"sidebar"},"contributing":{"id":"contributing","title":"Contributing","description":"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.","sidebar":"sidebar"},"design":{"id":"design","title":"Design","description":"Design Tenets","sidebar":"sidebar"},"faq":{"id":"faq","title":"FAQ","description":"What kind of vulnerabilities can Copa patch?","sidebar":"sidebar"},"installation":{"id":"installation","title":"Installation","description":"Homebrew","sidebar":"sidebar"},"introduction":{"id":"introduction","title":"Introduction","description":"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.","sidebar":"sidebar"},"quick-start":{"id":"quick-start","title":"Quick Start","description":"This sample illustrates how to patch containers using vulnerability reports with copa.","sidebar":"sidebar"}}}')}}]); \ No newline at end of file diff --git a/website/assets/js/6bd0979b.ffc56042.js b/website/assets/js/6bd0979b.ffc56042.js new file mode 100644 index 00000000..4a4e24da --- /dev/null +++ b/website/assets/js/6bd0979b.ffc56042.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1786],{9232:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"v0.3.x","label":"v0.3.x","banner":"unmaintained","badge":true,"noIndex":false,"className":"docs-version-v0.3.x","isLast":false,"docsSidebars":{"sidebar":[{"type":"link","label":"Introduction","href":"/copacetic/website/v0.3.x/","docId":"introduction","unlisted":false},{"type":"link","label":"Installation","href":"/copacetic/website/v0.3.x/installation","docId":"installation","unlisted":false},{"type":"link","label":"Quick Start","href":"/copacetic/website/v0.3.x/quick-start","docId":"quick-start","unlisted":false},{"type":"link","label":"Design","href":"/copacetic/website/v0.3.x/design","docId":"design","unlisted":false},{"type":"link","label":"FAQ","href":"/copacetic/website/v0.3.x/faq","docId":"faq","unlisted":false},{"type":"link","label":"Contributing","href":"/copacetic/website/v0.3.x/contributing","docId":"contributing","unlisted":false},{"type":"link","label":"Code of Conduct","href":"/copacetic/website/v0.3.x/code-of-conduct","docId":"code-of-conduct","unlisted":false}]},"docs":{"code-of-conduct":{"id":"code-of-conduct","title":"Code of Conduct","description":"Our Pledge","sidebar":"sidebar"},"contributing":{"id":"contributing","title":"Contributing","description":"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.","sidebar":"sidebar"},"design":{"id":"design","title":"Design","description":"Design Tenets","sidebar":"sidebar"},"faq":{"id":"faq","title":"FAQ","description":"What kind of vulnerabilities can Copa patch?","sidebar":"sidebar"},"installation":{"id":"installation","title":"Installation","description":"Homebrew","sidebar":"sidebar"},"introduction":{"id":"introduction","title":"Introduction","description":"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.","sidebar":"sidebar"},"quick-start":{"id":"quick-start","title":"Quick Start","description":"This sample illustrates how to patch containers using vulnerability reports with copa.","sidebar":"sidebar"}}}')}}]); \ No newline at end of file diff --git a/website/assets/js/72e14192.4b528064.js b/website/assets/js/72e14192.4b528064.js deleted file mode 100644 index 599b30ce..00000000 --- a/website/assets/js/72e14192.4b528064.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7239],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>h});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=a.createContext({}),p=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},c=function(e){var t=p(e.components);return a.createElement(l.Provider,{value:t},e.children)},m="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),m=p(n),d=i,h=m["".concat(l,".").concat(d)]||m[d]||u[d]||r;return n?a.createElement(h,o(o({ref:t},c),{},{components:n})):a.createElement(h,o({ref:t},c))}));function h(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[m]="string"==typeof e?e:i,o[1]=s;for(var p=2;p<r;p++)o[p]=n[p];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}d.displayName="MDXCreateElement"},4181:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>m,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var a=n(7462),i=(n(7294),n(3905));const r={title:"Quick Start"},o=void 0,s={unversionedId:"quick-start",id:"quick-start",title:"Quick Start",description:"This sample illustrates how to patch containers using vulnerability reports with copa.",source:"@site/docs/quick-start.md",sourceDirName:".",slug:"/quick-start",permalink:"/copacetic/website/next/quick-start",draft:!1,tags:[],version:"current",frontMatter:{title:"Quick Start"},sidebar:"sidebar",previous:{title:"Installation",permalink:"/copacetic/website/next/installation"},next:{title:"Tagging Guidelines",permalink:"/copacetic/website/next/best-practices"}},l={},p=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Sample Steps",id:"sample-steps",level:2}],c={toc:p};function m(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"This sample illustrates how to patch containers using vulnerability reports with ",(0,i.kt)("inlineCode",{parentName:"p"},"copa"),"."),(0,i.kt)("h2",{id:"prerequisites"},"Prerequisites"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Linux or macOS configured through the ",(0,i.kt)("a",{parentName:"li",href:"/copacetic/website/next/installation"},"setup instructions"),". This includes:",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"copa")," tool ",(0,i.kt)("a",{parentName:"li",href:"/copacetic/website/next/installation"},"built & pathed"),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://github.com/moby/buildkit/#quick-start"},"buildkit")," daemon installed & pathed. ",(0,i.kt)("a",{parentName:"li",href:"#buildkit-connection-examples"},"Examples"),(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"docker")," daemon runs a buildkit service in-process. If you are using this for your buildkit instance, Docker must have the ",(0,i.kt)("a",{parentName:"li",href:"https://docs.docker.com/storage/containerd/"},"containerd image store feature")," enabled."),(0,i.kt)("li",{parentName:"ul"},"If you are using a buildx instance, or using buildkitd directly, there is no need to enable the containerd image store. However, only images in a remote registry can be patched using these methods."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://docs.docker.com/desktop/linux/install/#generic-installation-steps"},"docker")," daemon running and CLI installed & pathed."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://aquasecurity.github.io/trivy/latest/getting-started/installation/"},"trivy CLI")," installed & pathed.",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"Alternatively, see ",(0,i.kt)("a",{parentName:"li",href:"#scanner-plugins"},"scanner plugins")," for custom scanner support.")))))),(0,i.kt)("h2",{id:"sample-steps"},"Sample Steps"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("p",{parentName:"li"},"Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:"),(0,i.kt)("pre",{parentName:"li"},(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6\n")),(0,i.kt)("p",{parentName:"li"},"You can also see the existing patchable vulnerabilities in table form on the shell with:"),(0,i.kt)("pre",{parentName:"li"},(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6\n"))),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("p",{parentName:"li"},"To patch the image, use the Trivy report and specify a buildkit instance to connect to:"),(0,i.kt)("p",{parentName:"li"},"By default copa will attempt to auto-connect to an instance in order:"),(0,i.kt)("ol",{parentName:"li"},(0,i.kt)("li",{parentName:"ol"},"Default docker buildkit endpoint (requires at least docker v24.0 with ",(0,i.kt)("a",{parentName:"li",href:"https://docs.docker.com/storage/containerd/#enable-containerd-image-store-on-docker-engine"},"containerd image store")," support enabled)"),(0,i.kt)("li",{parentName:"ol"},"Currently selected buildx builder (see: ",(0,i.kt)("inlineCode",{parentName:"li"},"docker buildx --help"),")"),(0,i.kt)("li",{parentName:"ol"},"buildkit daemon at the default address ",(0,i.kt)("inlineCode",{parentName:"li"},"/run/buildkit/buildkitd.sock"))),(0,i.kt)("p",{parentName:"li"},"If an instance doesn't exist or that instance doesn't support all the features copa needs the next will be attempted. Please see ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/next/custom-address"},"custom buildkit addresses")," for more information."),(0,i.kt)("p",{parentName:"li"},"After setting up the buildkit instance, run the following command to patch the image:"),(0,i.kt)("pre",{parentName:"li"},(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"copa patch -r nginx.1.21.6.json -i docker.io/library/nginx:1.21.6\n")),(0,i.kt)("p",{parentName:"li"},"In any of these cases, ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," is non-destructive and exports a new image with the specified ",(0,i.kt)("inlineCode",{parentName:"p"},"1.21.6-patched")," label to the local Docker daemon."),(0,i.kt)("admonition",{parentName:"li",type:"note"},(0,i.kt)("p",{parentName:"admonition"},"If you're running this sample against an image from a private registry instead,ensure that the credentials are configured in the default Docker config.json before running ",(0,i.kt)("inlineCode",{parentName:"p"},"copa patch"),", for example, via ",(0,i.kt)("inlineCode",{parentName:"p"},"docker login -u <user> -p <password> <registry>"),".")),(0,i.kt)("admonition",{parentName:"li",type:"note"},(0,i.kt)("p",{parentName:"admonition"},"If you're scanning and patching an image that is local-only (i.e. built or tagged locally but not pushed to a registry), ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," is limited to using ",(0,i.kt)("inlineCode",{parentName:"p"},"docker"),"'s built-in buildkit service, and must use the ",(0,i.kt)("a",{parentName:"p",href:"https://docs.docker.com/storage/containerd/"},(0,i.kt)("inlineCode",{parentName:"a"},"containerd image store"))," feature. This is because only ",(0,i.kt)("inlineCode",{parentName:"p"},"docker"),"'s built-in buildkit service has access to the docker image store (see ",(0,i.kt)("a",{parentName:"p",href:"#prerequisites"},"Prerequisites")," for more information.)"))),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("p",{parentName:"li"},"Scan the patched image and verify that the vulnerabilities have been patched:"),(0,i.kt)("pre",{parentName:"li"},(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched\n")),(0,i.kt)("p",{parentName:"li"},"You can also inspect the structure of the patched image with ",(0,i.kt)("inlineCode",{parentName:"p"},"docker history")," to see the new patch layer appended to the image:"),(0,i.kt)("pre",{parentName:"li"},(0,i.kt)("code",{parentName:"pre",className:"language-bash"},'$ docker history docker.io/library/nginx:1.21.6-patched\nIMAGE CREATED CREATED BY SIZE COMMENT\n262dacfeb193 About a minute ago mount / from exec sh -c apt install --no-ins\u2026 41.1MB buildkit.exporter.image.v0\n<missing> 20 months ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon\u2026 0B\n<missing> 20 months ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B\n<missing> 20 months ago /bin/sh -c #(nop) EXPOSE 80 0B\n<missing> 20 months ago /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr\u2026 0B\n<missing> 20 months ago /bin/sh -c #(nop) COPY file:09a214a3e07c919a\u2026 16.4kB\n<missing> 20 months ago /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7\u2026 12.3kB\n<missing> 20 months ago /bin/sh -c #(nop) COPY file:0b866ff3fc1ef5b0\u2026 12.3kB\n<missing> 20 months ago /bin/sh -c #(nop) COPY file:65504f71f5855ca0\u2026 8.19kB\n<missing> 20 months ago /bin/sh -c set -x && addgroup --system -\u2026 64.5MB\n<missing> 20 months ago /bin/sh -c #(nop) ENV PKG_RELEASE=1~bullseye 0B\n<missing> 20 months ago /bin/sh -c #(nop) ENV NJS_VERSION=0.7.3 0B\n<missing> 20 months ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.21.6 0B\n<missing> 20 months ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do\u2026 0B\n<missing> 20 months ago /bin/sh -c #(nop) CMD ["bash"] 0B\n<missing> 20 months ago /bin/sh -c #(nop) ADD file:134f25aec8adf83cb\u2026 91.8MB\n'))),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("p",{parentName:"li"},"Run the container to verify that the image has no regressions:"),(0,i.kt)("pre",{parentName:"li"},(0,i.kt)("code",{parentName:"pre",className:"language-bash"},'$ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched\n/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration\n/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh\n10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf\n10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh\n/docker-entrypoint.sh: Configuration complete; ready for start up\n2024/01/22 23:32:54 [notice] 1#1: using the "epoll" event method\n2024/01/22 23:32:54 [notice] 1#1: nginx/1.21.6\n2024/01/22 23:32:54 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)\n2024/01/22 23:32:54 [notice] 1#1: OS: Linux 6.2.0-1018-azure\n2024/01/22 23:32:54 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576\n2024/01/22 23:32:54 [notice] 1#1: start worker processes\n')),(0,i.kt)("p",{parentName:"li"},"You can stop the container by opening a new shell instance and running: ",(0,i.kt)("inlineCode",{parentName:"p"},"docker stop nginx-test")))))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/72e14192.98634abe.js b/website/assets/js/72e14192.98634abe.js new file mode 100644 index 00000000..136d1917 --- /dev/null +++ b/website/assets/js/72e14192.98634abe.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2814],{3744:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>a,contentTitle:()=>r,default:()=>h,frontMatter:()=>o,metadata:()=>c,toc:()=>l});var t=i(4848),s=i(8453);const o={title:"Quick Start"},r=void 0,c={id:"quick-start",title:"Quick Start",description:"This sample illustrates how to patch containers using vulnerability reports with copa.",source:"@site/docs/quick-start.md",sourceDirName:".",slug:"/quick-start",permalink:"/copacetic/website/next/quick-start",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{title:"Quick Start"},sidebar:"sidebar",previous:{title:"Installation",permalink:"/copacetic/website/next/installation"},next:{title:"Tagging Guidelines",permalink:"/copacetic/website/next/best-practices"}},a={},l=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Sample Steps",id:"sample-steps",level:2}];function d(e){const n={a:"a",admonition:"admonition",code:"code",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",ul:"ul",...(0,s.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(n.p,{children:["This sample illustrates how to patch containers using vulnerability reports with ",(0,t.jsx)(n.code,{children:"copa"}),"."]}),"\n",(0,t.jsx)(n.h2,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["Linux or macOS configured through the ",(0,t.jsx)(n.a,{href:"/copacetic/website/next/installation",children:"setup instructions"}),". This includes:","\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"copa"})," tool ",(0,t.jsx)(n.a,{href:"/copacetic/website/next/installation",children:"built & pathed"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://github.com/moby/buildkit/#quick-start",children:"buildkit"})," daemon installed & pathed. ",(0,t.jsx)(n.a,{href:"#buildkit-connection-examples",children:"Examples"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["The ",(0,t.jsx)(n.code,{children:"docker"})," daemon runs a buildkit service in-process. If you are using this for your buildkit instance, Docker must have the ",(0,t.jsx)(n.a,{href:"https://docs.docker.com/storage/containerd/",children:"containerd image store feature"})," enabled."]}),"\n",(0,t.jsx)(n.li,{children:"If you are using a buildx instance, or using buildkitd directly, there is no need to enable the containerd image store. However, only images in a remote registry can be patched using these methods."}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://docs.docker.com/desktop/linux/install/#generic-installation-steps",children:"docker"})," daemon running and CLI installed & pathed."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://aquasecurity.github.io/trivy/latest/getting-started/installation/",children:"trivy CLI"})," installed & pathed.","\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["Alternatively, see ",(0,t.jsx)(n.a,{href:"#scanner-plugins",children:"scanner plugins"})," for custom scanner support."]}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,t.jsx)(n.h2,{id:"sample-steps",children:"Sample Steps"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6\n"})}),"\n",(0,t.jsx)(n.p,{children:"You can also see the existing patchable vulnerabilities in table form on the shell with:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6\n"})}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"To patch the image, use the Trivy report and specify a buildkit instance to connect to:"}),"\n",(0,t.jsx)(n.p,{children:"By default copa will attempt to auto-connect to an instance in order:"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["Default docker buildkit endpoint (requires at least docker v24.0 with ",(0,t.jsx)(n.a,{href:"https://docs.docker.com/storage/containerd/#enable-containerd-image-store-on-docker-engine",children:"containerd image store"})," support enabled)"]}),"\n",(0,t.jsxs)(n.li,{children:["Currently selected buildx builder (see: ",(0,t.jsx)(n.code,{children:"docker buildx --help"}),")"]}),"\n",(0,t.jsxs)(n.li,{children:["buildkit daemon at the default address ",(0,t.jsx)(n.code,{children:"/run/buildkit/buildkitd.sock"})]}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["If an instance doesn't exist or that instance doesn't support all the features copa needs the next will be attempted. Please see ",(0,t.jsx)(n.a,{href:"/copacetic/website/next/custom-address",children:"custom buildkit addresses"})," for more information."]}),"\n",(0,t.jsx)(n.p,{children:"After setting up the buildkit instance, run the following command to patch the image:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"copa patch -r nginx.1.21.6.json -i docker.io/library/nginx:1.21.6\n"})}),"\n",(0,t.jsxs)(n.p,{children:["In any of these cases, ",(0,t.jsx)(n.code,{children:"copa"})," is non-destructive and exports a new image with the specified ",(0,t.jsx)(n.code,{children:"1.21.6-patched"})," label to the local Docker daemon."]}),"\n",(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsxs)(n.p,{children:["If you're running this sample against an image from a private registry instead,ensure that the credentials are configured in the default Docker config.json before running ",(0,t.jsx)(n.code,{children:"copa patch"}),", for example, via ",(0,t.jsx)(n.code,{children:"docker login -u <user> -p <password> <registry>"}),"."]})}),"\n",(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsxs)(n.p,{children:["If you're scanning and patching an image that is local-only (i.e. built or tagged locally but not pushed to a registry), ",(0,t.jsx)(n.code,{children:"copa"})," is limited to using ",(0,t.jsx)(n.code,{children:"docker"}),"'s built-in buildkit service, and must use the ",(0,t.jsx)(n.a,{href:"https://docs.docker.com/storage/containerd/",children:(0,t.jsx)(n.code,{children:"containerd image store"})})," feature. This is because only ",(0,t.jsx)(n.code,{children:"docker"}),"'s built-in buildkit service has access to the docker image store (see ",(0,t.jsx)(n.a,{href:"#prerequisites",children:"Prerequisites"})," for more information.)"]})}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Scan the patched image and verify that the vulnerabilities have been patched:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched\n"})}),"\n",(0,t.jsxs)(n.p,{children:["You can also inspect the structure of the patched image with ",(0,t.jsx)(n.code,{children:"docker history"})," to see the new patch layer appended to the image:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'$ docker history docker.io/library/nginx:1.21.6-patched\nIMAGE CREATED CREATED BY SIZE COMMENT\n262dacfeb193 About a minute ago mount / from exec sh -c apt install --no-ins\u2026 41.1MB buildkit.exporter.image.v0\n<missing> 20 months ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon\u2026 0B\n<missing> 20 months ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B\n<missing> 20 months ago /bin/sh -c #(nop) EXPOSE 80 0B\n<missing> 20 months ago /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr\u2026 0B\n<missing> 20 months ago /bin/sh -c #(nop) COPY file:09a214a3e07c919a\u2026 16.4kB\n<missing> 20 months ago /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7\u2026 12.3kB\n<missing> 20 months ago /bin/sh -c #(nop) COPY file:0b866ff3fc1ef5b0\u2026 12.3kB\n<missing> 20 months ago /bin/sh -c #(nop) COPY file:65504f71f5855ca0\u2026 8.19kB\n<missing> 20 months ago /bin/sh -c set -x && addgroup --system -\u2026 64.5MB\n<missing> 20 months ago /bin/sh -c #(nop) ENV PKG_RELEASE=1~bullseye 0B\n<missing> 20 months ago /bin/sh -c #(nop) ENV NJS_VERSION=0.7.3 0B\n<missing> 20 months ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.21.6 0B\n<missing> 20 months ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do\u2026 0B\n<missing> 20 months ago /bin/sh -c #(nop) CMD ["bash"] 0B\n<missing> 20 months ago /bin/sh -c #(nop) ADD file:134f25aec8adf83cb\u2026 91.8MB\n'})}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Run the container to verify that the image has no regressions:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'$ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched\n/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration\n/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh\n10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf\n10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh\n/docker-entrypoint.sh: Configuration complete; ready for start up\n2024/01/22 23:32:54 [notice] 1#1: using the "epoll" event method\n2024/01/22 23:32:54 [notice] 1#1: nginx/1.21.6\n2024/01/22 23:32:54 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)\n2024/01/22 23:32:54 [notice] 1#1: OS: Linux 6.2.0-1018-azure\n2024/01/22 23:32:54 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576\n2024/01/22 23:32:54 [notice] 1#1: start worker processes\n'})}),"\n",(0,t.jsxs)(n.p,{children:["You can stop the container by opening a new shell instance and running: ",(0,t.jsx)(n.code,{children:"docker stop nginx-test"})]}),"\n"]}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(d,{...e})}):d(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>r,x:()=>c});var t=i(6540);const s={},o=t.createContext(s);function r(e){const n=t.useContext(o);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),t.createElement(o.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/77a9ed43.c56f8af9.js b/website/assets/js/77a9ed43.c56f8af9.js new file mode 100644 index 00000000..defb7ccb --- /dev/null +++ b/website/assets/js/77a9ed43.c56f8af9.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2171],{9378:(e,t,i)=>{i.r(t),i.d(t,{assets:()=>a,contentTitle:()=>r,default:()=>h,frontMatter:()=>n,metadata:()=>o,toc:()=>l});var c=i(4848),s=i(8453);const n={title:"Release Process"},r=void 0,o={id:"release",title:"Release Process",description:"Overview",source:"@site/versioned_docs/version-v0.5.x/release.md",sourceDirName:".",slug:"/release",permalink:"/copacetic/website/v0.5.x/release",draft:!1,unlisted:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Release Process"},sidebar:"sidebar",previous:{title:"Copa Github Action",permalink:"/copacetic/website/v0.5.x/github-action"}},a={},l=[{value:"Overview",id:"overview",level:2},{value:"Publishing",id:"publishing",level:2}];function p(e){const t={a:"a",code:"code",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",...(0,s.R)(),...e.components};return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(t.h2,{id:"overview",children:"Overview"}),"\n",(0,c.jsxs)(t.p,{children:["The release process for Copacetic uses ",(0,c.jsx)(t.a,{href:"https://goreleaser.com/",children:"GoReleaser"}),"."]}),"\n",(0,c.jsx)(t.p,{children:"Once you are ready to cut a new release, checkout the release branch and tag it with the respective version."}),"\n",(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{children:"git checkout <BRANCH NAME>\ngit pull origin <BRANCH NAME>\ngit tag -a <NEW VERSION> -m '<NEW VERSION>'\ngit push origin <NEW VERSION>\n"})}),"\n",(0,c.jsx)(t.h2,{id:"publishing",children:"Publishing"}),"\n",(0,c.jsxs)(t.ol,{children:["\n",(0,c.jsxs)(t.li,{children:["GoReleaser will create a new release, review and edit it at ",(0,c.jsx)(t.a,{href:"https://github.com/project-copacetic/copacetic/releases",children:"https://github.com/project-copacetic/copacetic/releases"})]}),"\n",(0,c.jsxs)(t.li,{children:["Review the respective copa-action image at: ",(0,c.jsx)(t.a,{href:"https://github.com/orgs/project-copacetic/packages/container/package/copa-action",children:"https://github.com/orgs/project-copacetic/packages/container/package/copa-action"})]}),"\n"]})]})}function h(e={}){const{wrapper:t}={...(0,s.R)(),...e.components};return t?(0,c.jsx)(t,{...e,children:(0,c.jsx)(p,{...e})}):p(e)}},8453:(e,t,i)=>{i.d(t,{R:()=>r,x:()=>o});var c=i(6540);const s={},n=c.createContext(s);function r(e){const t=c.useContext(n);return c.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function o(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),c.createElement(n.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/77a9ed43.cdfe4c4f.js b/website/assets/js/77a9ed43.cdfe4c4f.js deleted file mode 100644 index e678eb03..00000000 --- a/website/assets/js/77a9ed43.cdfe4c4f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6380],{3905:(e,t,r)=>{r.d(t,{Zo:()=>s,kt:()=>m});var n=r(7294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?o(Object(r),!0).forEach((function(t){a(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):o(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function c(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r,n,a={},o=Object.keys(e);for(n=0;n<o.length;n++)r=o[n],t.indexOf(r)>=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n<o.length;n++)r=o[n],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var l=n.createContext({}),p=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},s=function(e){var t=p(e.components);return n.createElement(l.Provider,{value:t},e.children)},u="mdxType",v={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},f=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,o=e.originalType,l=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),u=p(r),f=a,m=u["".concat(l,".").concat(f)]||u[f]||v[f]||o;return r?n.createElement(m,i(i({ref:t},s),{},{components:r})):n.createElement(m,i({ref:t},s))}));function m(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=r.length,i=new Array(o);i[0]=f;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[u]="string"==typeof e?e:a,i[1]=c;for(var p=2;p<o;p++)i[p]=r[p];return n.createElement.apply(null,i)}return n.createElement.apply(null,r)}f.displayName="MDXCreateElement"},509:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>c,toc:()=>p});var n=r(7462),a=(r(7294),r(3905));const o={title:"Release Process"},i=void 0,c={unversionedId:"release",id:"version-v0.5.x/release",title:"Release Process",description:"Overview",source:"@site/versioned_docs/version-v0.5.x/release.md",sourceDirName:".",slug:"/release",permalink:"/copacetic/website/v0.5.x/release",draft:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Release Process"},sidebar:"sidebar",previous:{title:"Copa Github Action",permalink:"/copacetic/website/v0.5.x/github-action"}},l={},p=[{value:"Overview",id:"overview",level:2},{value:"Publishing",id:"publishing",level:2}],s={toc:p};function u(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},s,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h2",{id:"overview"},"Overview"),(0,a.kt)("p",null,"The release process for Copacetic uses ",(0,a.kt)("a",{parentName:"p",href:"https://goreleaser.com/"},"GoReleaser"),". "),(0,a.kt)("p",null,"Once you are ready to cut a new release, checkout the release branch and tag it with the respective version."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"```\ngit checkout <BRANCH NAME>\ngit pull origin <BRANCH NAME>\ngit tag -a <NEW VERSION> -m '<NEW VERSION>'\ngit push origin <NEW VERSION>\n```\n")),(0,a.kt)("h2",{id:"publishing"},"Publishing"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"GoReleaser will create a new release, review and edit it at ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/project-copacetic/copacetic/releases"},"https://github.com/project-copacetic/copacetic/releases")),(0,a.kt)("li",{parentName:"ol"},"Review the respective copa-action image at: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/orgs/project-copacetic/packages/container/package/copa-action"},"https://github.com/orgs/project-copacetic/packages/container/package/copa-action"))))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/7d213ce9.26e02b59.js b/website/assets/js/7d213ce9.26e02b59.js deleted file mode 100644 index 507fb42a..00000000 --- a/website/assets/js/7d213ce9.26e02b59.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[240],{3905:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?i(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):i(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,r,a=function(e,t){if(null==e)return{};var n,r,a={},i=Object.keys(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),p=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},s=function(e){var t=p(e.components);return r.createElement(c.Provider,{value:t},e.children)},u="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,c=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),u=p(n),d=a,f=u["".concat(c,".").concat(d)]||u[d]||m[d]||i;return n?r.createElement(f,o(o({ref:t},s),{},{components:n})):r.createElement(f,o({ref:t},s))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=d;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[u]="string"==typeof e?e:a,o[1]=l;for(var p=2;p<i;p++)o[p]=n[p];return r.createElement.apply(null,o)}return r.createElement.apply(null,n)}d.displayName="MDXCreateElement"},1090:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>u,frontMatter:()=>i,metadata:()=>l,toc:()=>p});var r=n(7462),a=(n(7294),n(3905));const i={title:"Installation"},o=void 0,l={unversionedId:"installation",id:"version-v0.6.x/installation",title:"Installation",description:"Homebrew",source:"@site/versioned_docs/version-v0.6.x/installation.md",sourceDirName:".",slug:"/installation",permalink:"/copacetic/website/installation",draft:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Installation"},sidebar:"sidebar",previous:{title:"Introduction",permalink:"/copacetic/website/"},next:{title:"Quick Start",permalink:"/copacetic/website/quick-start"}},c={},p=[{value:"Homebrew",id:"homebrew",level:2},{value:"GitHub",id:"github",level:2},{value:"Development Setup",id:"development-setup",level:2},{value:"Prequisitives",id:"prequisitives",level:3}],s={toc:p};function u(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h2",{id:"homebrew"},"Homebrew"),(0,a.kt)("p",null,"On macOS and Linux, ",(0,a.kt)("inlineCode",{parentName:"p"},"copa")," can be installed via ",(0,a.kt)("a",{parentName:"p",href:"https://brew.sh/"},"Homebrew"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"brew install copa\n")),(0,a.kt)("h2",{id:"github"},"GitHub"),(0,a.kt)("p",null,"You can download the latest and previous versions of ",(0,a.kt)("inlineCode",{parentName:"p"},"copa")," from the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/releases"},"GitHub releases page"),"."),(0,a.kt)("h2",{id:"development-setup"},"Development Setup"),(0,a.kt)("h3",{id:"prequisitives"},"Prequisitives"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://go.dev/doc/install"},"Go")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://docs.docker.com/engine/install/"},"Docker")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://aquasecurity.github.io/trivy/latest/getting-started/installation/"},"Trivy")," (optional as a scanner)")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"git clone https://github.com/project-copacetic/copacetic\ncd copacetic\nmake\n# OPTIONAL: install copa to a pathed folder\nsudo mv dist/linux_amd64/release/copa /usr/local/bin/\n")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/7d213ce9.8520a379.js b/website/assets/js/7d213ce9.8520a379.js new file mode 100644 index 00000000..7add28dd --- /dev/null +++ b/website/assets/js/7d213ce9.8520a379.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3033],{3748:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>p,frontMatter:()=>c,metadata:()=>a,toc:()=>r});var i=n(4848),s=n(8453);const c={title:"Installation"},o=void 0,a={id:"installation",title:"Installation",description:"Homebrew",source:"@site/versioned_docs/version-v0.6.x/installation.md",sourceDirName:".",slug:"/installation",permalink:"/copacetic/website/installation",draft:!1,unlisted:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Installation"},sidebar:"sidebar",previous:{title:"Introduction",permalink:"/copacetic/website/"},next:{title:"Quick Start",permalink:"/copacetic/website/quick-start"}},l={},r=[{value:"Homebrew",id:"homebrew",level:2},{value:"GitHub",id:"github",level:2},{value:"Development Setup",id:"development-setup",level:2},{value:"Prequisitives",id:"prequisitives",level:3}];function d(e){const t={a:"a",code:"code",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(t.h2,{id:"homebrew",children:"Homebrew"}),"\n",(0,i.jsxs)(t.p,{children:["On macOS and Linux, ",(0,i.jsx)(t.code,{children:"copa"})," can be installed via ",(0,i.jsx)(t.a,{href:"https://brew.sh/",children:"Homebrew"}),":"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:"brew install copa\n"})}),"\n",(0,i.jsx)(t.h2,{id:"github",children:"GitHub"}),"\n",(0,i.jsxs)(t.p,{children:["You can download the latest and previous versions of ",(0,i.jsx)(t.code,{children:"copa"})," from the ",(0,i.jsx)(t.a,{href:"https://github.com/project-copacetic/copacetic/releases",children:"GitHub releases page"}),"."]}),"\n",(0,i.jsx)(t.h2,{id:"development-setup",children:"Development Setup"}),"\n",(0,i.jsx)(t.h3,{id:"prequisitives",children:"Prequisitives"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://go.dev/doc/install",children:"Go"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://docs.docker.com/engine/install/",children:"Docker"})}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.a,{href:"https://aquasecurity.github.io/trivy/latest/getting-started/installation/",children:"Trivy"})," (optional as a scanner)"]}),"\n"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:"git clone https://github.com/project-copacetic/copacetic\ncd copacetic\nmake\n# OPTIONAL: install copa to a pathed folder\nsudo mv dist/linux_amd64/release/copa /usr/local/bin/\n"})})]})}function p(e={}){const{wrapper:t}={...(0,s.R)(),...e.components};return t?(0,i.jsx)(t,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>o,x:()=>a});var i=n(6540);const s={},c=i.createContext(s);function o(e){const t=i.useContext(c);return i.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function a(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:o(e.components),i.createElement(c.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/7e72de0f.46d6e17a.js b/website/assets/js/7e72de0f.46d6e17a.js deleted file mode 100644 index 4476a11c..00000000 --- a/website/assets/js/7e72de0f.46d6e17a.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[722],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>h});var o=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function r(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?a(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):a(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,o,i=function(e,t){if(null==e)return{};var n,o,i={},a=Object.keys(e);for(o=0;o<a.length;o++)n=a[o],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o<a.length;o++)n=a[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=o.createContext({}),c=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},p=function(e){var t=c(e.components);return o.createElement(l.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),u=c(n),m=i,h=u["".concat(l,".").concat(m)]||u[m]||d[m]||a;return n?o.createElement(h,r(r({ref:t},p),{},{components:n})):o.createElement(h,r({ref:t},p))}));function h(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,r=new Array(a);r[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[u]="string"==typeof e?e:i,r[1]=s;for(var c=2;c<a;c++)r[c]=n[c];return o.createElement.apply(null,r)}return o.createElement.apply(null,n)}m.displayName="MDXCreateElement"},5301:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>u,frontMatter:()=>a,metadata:()=>s,toc:()=>c});var o=n(7462),i=(n(7294),n(3905));const a={title:"Contributing"},r=void 0,s={unversionedId:"contributing",id:"version-v0.3.x/contributing",title:"Contributing",description:"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.",source:"@site/versioned_docs/version-v0.3.x/contributing.md",sourceDirName:".",slug:"/contributing",permalink:"/copacetic/website/v0.3.x/contributing",draft:!1,tags:[],version:"v0.3.x",frontMatter:{title:"Contributing"},sidebar:"sidebar",previous:{title:"FAQ",permalink:"/copacetic/website/v0.3.x/faq"},next:{title:"Code of Conduct",permalink:"/copacetic/website/v0.3.x/code-of-conduct"}},l={},c=[{value:"Bi-Weekly Community Meeting",id:"bi-weekly-community-meeting",level:2},{value:"Slack",id:"slack",level:2},{value:"Contributing Issues",id:"contributing-issues",level:2},{value:"Contributing Code",id:"contributing-code",level:2},{value:"Getting Started",id:"getting-started",level:3},{value:"Visual Studio Code Development Container",id:"visual-studio-code-development-container",level:3},{value:"Prerequisites",id:"prerequisites",level:4},{value:"Using the dev container",id:"using-the-dev-container",level:4},{value:"Personalizing user settings in a dev container",id:"personalizing-user-settings-in-a-dev-container",level:4},{value:"Tests",id:"tests",level:3},{value:"Pull Requests",id:"pull-requests",level:3},{value:"Developer Certificate of Origin (DCO)",id:"developer-certificate-of-origin-dco",level:2},{value:"I didn't sign my commit, now what?",id:"i-didnt-sign-my-commit-now-what",level:3},{value:"Code of Conduct",id:"code-of-conduct",level:2}],p={toc:c};function u(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,o.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"Welcome! We are very happy to accept community contributions to the project, whether through ",(0,i.kt)("a",{parentName:"p",href:"#contributing-issues"},"filing issues")," or ",(0,i.kt)("a",{parentName:"p",href:"#contributing-code"},"code")," in the form of ",(0,i.kt)("a",{parentName:"p",href:"#pull-requests"},"Pull Requests"),". Please note that by participating in this project, you agree to abide by the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.3.x/code-of-conduct"},"Code of Conduct"),", as well as the terms of the ",(0,i.kt)("a",{parentName:"p",href:"#developer-certificate-of-origin-dco"},"Developer Certificate of Origin"),"."),(0,i.kt)("h2",{id:"bi-weekly-community-meeting"},"Bi-Weekly Community Meeting"),(0,i.kt)("p",null,"A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join ",(0,i.kt)("a",{parentName:"p",href:"https://docs.google.com/document/d/1QdskbeCtgKcdWYHI6EXkLFxyzTCyVT6e8MgB3CaAhWI/edit?usp=sharing"},"here")),(0,i.kt)("h2",{id:"slack"},"Slack"),(0,i.kt)("p",null,"To discuss issues with Copa, features, or development, you can join the ",(0,i.kt)("inlineCode",{parentName:"p"},"#copa")," channel on the ",(0,i.kt)("a",{parentName:"p",href:"https://communityinviter.com/apps/opencontainers/join-the-oci-community"},"OCI Slack"),"."),(0,i.kt)("h2",{id:"contributing-issues"},"Contributing Issues"),(0,i.kt)("p",null,"Before opening any new issues, please search our ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/issues"},"existing GitHub issues")," to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead."),(0,i.kt)("p",null,"When opening an issue, please select the most appropriate template for what you're contributing:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Bug Report:")," If you would like to report the project or tool behaving in unexpected ways."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Documentation Improvement:")," If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Request:")," If you have a feature request, suggestion, or a even a design proposal to review."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Question:")," If you would like to ask the maintainers a question about the project.")),(0,i.kt)("h2",{id:"contributing-code"},"Contributing Code"),(0,i.kt)("h3",{id:"getting-started"},"Getting Started"),(0,i.kt)("p",null,"Follow the instructions to either:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/copacetic/website/v0.3.x/installation"},"Setup your dev environment to build copa"),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"#visual-studio-code-development-container"},"Use the copa development container")," in ",(0,i.kt)("a",{parentName:"li",href:"https://code.visualstudio.com/"},"Visual Studio Code"),".")),(0,i.kt)("p",null,"For an overview of the project components, refer to the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.3.x/design"},"copa design")," document."),(0,i.kt)("h3",{id:"visual-studio-code-development-container"},"Visual Studio Code Development Container"),(0,i.kt)("p",null,(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/"},"VSCode")," supports development in a containerized environment through its ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/docs/remote/containers"},"Remote - Container extension"),". This folder provides a development container which encapsulates the dependencies specified in the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.3.x/installation"},"instructions to build and run copa"),"."),(0,i.kt)("h4",{id:"prerequisites"},"Prerequisites"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://docs.docker.com/get-docker/"},"Docker"),(0,i.kt)("blockquote",{parentName:"li"},(0,i.kt)("p",{parentName:"blockquote"},"For Windows users, enabling ",(0,i.kt)("a",{parentName:"p",href:"https://docs.docker.com/docker-for-windows/wsl/"},"WSL2 back-end integration with Docker")," is recommended."))),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://code.visualstudio.com/"},"Visual Studio Code")),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers"},"Visual Studio Code Remote - Containers extension"))),(0,i.kt)("h4",{id:"using-the-dev-container"},"Using the dev container"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("p",{parentName:"li"},"After you have cloned this repo locally, open the repo folder in VSCode. VSCode will detect the presence of this ",(0,i.kt)("inlineCode",{parentName:"p"},".devcontainer")," subfolder and will prompt you to reopen the project in a container."),(0,i.kt)("p",{parentName:"li"},"Alternatively, you can open the command palette and use the ",(0,i.kt)("inlineCode",{parentName:"p"},"Remote-Containers: Reopen in Container")," command.")),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("p",{parentName:"li"},"Once the container is loaded, open an ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/docs/editor/integrated-terminal"},"integrated terminal")," in VSCode and you can start running the demo instructions."))),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},(0,i.kt)("strong",{parentName:"p"},"\u26a0 If running via Docker Desktop for Windows")),(0,i.kt)("p",{parentName:"blockquote"},"Note that the ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user"},"mounted workspace files appear owned by ",(0,i.kt)("inlineCode",{parentName:"a"},"root"))," in the dev container, which will cause ",(0,i.kt)("inlineCode",{parentName:"p"},"git")," commands to fail with a ",(0,i.kt)("inlineCode",{parentName:"p"},"fatal: detected dubious ownership in a repository")," error due to ",(0,i.kt)("a",{parentName:"p",href:"https://git-scm.com/docs/git-config/2.35.2#Documentation/git-config.txt-safedirectory"},"safe.directory")," checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the ",(0,i.kt)("inlineCode",{parentName:"p"},"vscode")," user:"),(0,i.kt)("pre",{parentName:"blockquote"},(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"sudo chown -R vscode:vscode /workspace/copacetic\n"))),(0,i.kt)("h4",{id:"personalizing-user-settings-in-a-dev-container"},"Personalizing user settings in a dev container"),(0,i.kt)("p",null,"VSCode supports applying your user settings, such as your ",(0,i.kt)("inlineCode",{parentName:"p"},".gitconfig"),", to a dev container through the use of ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories"},"dotfiles repositories"),". This can be done through your own VSCode ",(0,i.kt)("inlineCode",{parentName:"p"},"settings.json")," file without changing the dev container image or configuration."),(0,i.kt)("h3",{id:"tests"},"Tests"),(0,i.kt)("p",null,"Once you can successfully ",(0,i.kt)("inlineCode",{parentName:"p"},"make")," the project, any code contributions should also successfully:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Pass unit tests via ",(0,i.kt)("inlineCode",{parentName:"li"},"make test"),"."),(0,i.kt)("li",{parentName:"ul"},"Lint cleanly via ",(0,i.kt)("inlineCode",{parentName:"li"},"make lint"),".")),(0,i.kt)("p",null,"Pull requests will also be expected to pass the PR functional tests specified by ",(0,i.kt)("inlineCode",{parentName:"p"},".github/workflows/build.yml"),"."),(0,i.kt)("h3",{id:"pull-requests"},"Pull Requests"),(0,i.kt)("p",null,"If you'd like to start contributing code to the project, you can search for ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/labels/good%20first%20issue"},"issues with the ",(0,i.kt)("inlineCode",{parentName:"a"},"good first issue")," label"),". Other kinds of PR contributions we would look for include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Fixes for bugs and other correctness issues."),(0,i.kt)("li",{parentName:"ul"},"Docs and other content improvements (e.g. samples)."),(0,i.kt)("li",{parentName:"ul"},"Extensions to support parsing new scanning report formats."),(0,i.kt)("li",{parentName:"ul"},"Extensions to support patching images based on new distros or using new package managers.")),(0,i.kt)("p",null,"For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Architectural changes (e.g. breaking interfaces or violations of ",(0,i.kt)("a",{parentName:"li",href:"/copacetic/website/v0.3.x/design"},"this project's design tenets"),")."),(0,i.kt)("li",{parentName:"ul"},"Unsolicited features that significantly expand the functional scope of the tool.")),(0,i.kt)("p",null,"Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format"},"Angular commit message format")," for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs."),(0,i.kt)("p",null,"We suggest:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Use the standard header format of ",(0,i.kt)("inlineCode",{parentName:"li"},'"<type>: <short summary>"')," where the ",(0,i.kt)("inlineCode",{parentName:"li"},"<type>")," is one of the following:",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"build:")," Changes that affect the build system or external dependencies"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"ci:")," Changes to the GitHub workflows and configurations"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"docs:")," Documentation only changes"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"feat:")," A new feature"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"fix:")," A bug fix"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"perf:")," A code change that improves performance"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"refactor:")," A code change that neither fixes a bug nor adds a feature"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"test:")," Adding missing tests or correcting existing tests"))),(0,i.kt)("li",{parentName:"ul"},"Use a ",(0,i.kt)("a",{parentName:"li",href:"https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html"},"concise, imperative description")," of the changes included in the ",(0,i.kt)("inlineCode",{parentName:"li"},"<short summary>")," of the header, the body of the PR, and generally in your commit messages."),(0,i.kt)("li",{parentName:"ul"},"Use ",(0,i.kt)("a",{parentName:"li",href:"https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests"},"GitHub keywords")," in the footer of your PR description, such as ",(0,i.kt)("inlineCode",{parentName:"li"},"closes")," to automatically close issues the PR intends to address.")),(0,i.kt)("h2",{id:"developer-certificate-of-origin-dco"},"Developer Certificate of Origin (DCO)"),(0,i.kt)("p",null,"The ",(0,i.kt)("a",{parentName:"p",href:"https://wiki.linuxfoundation.org/dco"},"Developer Certificate of Origin")," (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the ",(0,i.kt)("a",{parentName:"p",href:"https://developercertificate.org/"},"full text of the DCO"),", reformatted for readability:"),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},"By making a contribution to this project, I certify that:"),(0,i.kt)("p",{parentName:"blockquote"},"(a) The contribution was created in whole or in part by me and I\nhave the right to submit it under the open source license\nindicated in the file; or"),(0,i.kt)("p",{parentName:"blockquote"},"(b) The contribution is based upon previous work that, to the best\nof my knowledge, is covered under an appropriate open source\nlicense and I have the right under that license to submit that\nwork with modifications, whether created in whole or in part\nby me, under the same open source license (unless I am\npermitted to submit under a different license), as indicated\nin the file; or"),(0,i.kt)("p",{parentName:"blockquote"},"(c) The contribution was provided directly to me by some other\nperson who certified (a), (b) or (c) and I have not modified\nit."),(0,i.kt)("p",{parentName:"blockquote"},"(d) I understand and agree that this project and the contribution\nare public and that a record of the contribution (including all\npersonal information I submit with it, including my sign-off) is\nmaintained indefinitely and may be redistributed consistent with\nthis project or the open source license(s) involved.")),(0,i.kt)("p",null,"Contributors ",(0,i.kt)("em",{parentName:"p"},"sign-off")," that they adhere to these requirements by adding a ",(0,i.kt)("inlineCode",{parentName:"p"},"Signed-off-by")," line to commit messages."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-text"},"This is my commit message\n\nSigned-off-by: Random J Developer <random@developer.example.org>\n")),(0,i.kt)("p",null,"Git even has a ",(0,i.kt)("inlineCode",{parentName:"p"},"-s")," command line option to append this automatically to your commit message:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"git commit -s -m 'This is my commit message'\n")),(0,i.kt)("p",null,"Pull requests that do not contain a valid ",(0,i.kt)("inlineCode",{parentName:"p"},"Signed-off-by")," line cannot be merged."),(0,i.kt)("h3",{id:"i-didnt-sign-my-commit-now-what"},"I didn't sign my commit, now what?"),(0,i.kt)("p",null,"No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"git switch <branch-name>\ngit commit --amend --no-edit --signoff\ngit push --force-with-lease <remote-name> <branch-name>\n")),(0,i.kt)("h2",{id:"code-of-conduct"},"Code of Conduct"),(0,i.kt)("p",null,"This project has adopted the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.3.x/code-of-conduct"},"Contributor Covenant Code of Conduct"),"."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/7e72de0f.7b9dfc9f.js b/website/assets/js/7e72de0f.7b9dfc9f.js new file mode 100644 index 00000000..f6c6087f --- /dev/null +++ b/website/assets/js/7e72de0f.7b9dfc9f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4519],{4717:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>d,contentTitle:()=>r,default:()=>h,frontMatter:()=>o,metadata:()=>c,toc:()=>a});var i=t(4848),s=t(8453);const o={title:"Contributing"},r=void 0,c={id:"contributing",title:"Contributing",description:"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.",source:"@site/versioned_docs/version-v0.3.x/contributing.md",sourceDirName:".",slug:"/contributing",permalink:"/copacetic/website/v0.3.x/contributing",draft:!1,unlisted:!1,tags:[],version:"v0.3.x",frontMatter:{title:"Contributing"},sidebar:"sidebar",previous:{title:"FAQ",permalink:"/copacetic/website/v0.3.x/faq"},next:{title:"Code of Conduct",permalink:"/copacetic/website/v0.3.x/code-of-conduct"}},d={},a=[{value:"Bi-Weekly Community Meeting",id:"bi-weekly-community-meeting",level:2},{value:"Slack",id:"slack",level:2},{value:"Contributing Issues",id:"contributing-issues",level:2},{value:"Contributing Code",id:"contributing-code",level:2},{value:"Getting Started",id:"getting-started",level:3},{value:"Visual Studio Code Development Container",id:"visual-studio-code-development-container",level:3},{value:"Prerequisites",id:"prerequisites",level:4},{value:"Using the dev container",id:"using-the-dev-container",level:4},{value:"Personalizing user settings in a dev container",id:"personalizing-user-settings-in-a-dev-container",level:4},{value:"Tests",id:"tests",level:3},{value:"Pull Requests",id:"pull-requests",level:3},{value:"Developer Certificate of Origin (DCO)",id:"developer-certificate-of-origin-dco",level:2},{value:"I didn't sign my commit, now what?",id:"i-didnt-sign-my-commit-now-what",level:3},{value:"Code of Conduct",id:"code-of-conduct",level:2}];function l(e){const n={a:"a",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h3:"h3",h4:"h4",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsxs)(n.p,{children:["Welcome! We are very happy to accept community contributions to the project, whether through ",(0,i.jsx)(n.a,{href:"#contributing-issues",children:"filing issues"})," or ",(0,i.jsx)(n.a,{href:"#contributing-code",children:"code"})," in the form of ",(0,i.jsx)(n.a,{href:"#pull-requests",children:"Pull Requests"}),". Please note that by participating in this project, you agree to abide by the ",(0,i.jsx)(n.a,{href:"/copacetic/website/v0.3.x/code-of-conduct",children:"Code of Conduct"}),", as well as the terms of the ",(0,i.jsx)(n.a,{href:"#developer-certificate-of-origin-dco",children:"Developer Certificate of Origin"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"bi-weekly-community-meeting",children:"Bi-Weekly Community Meeting"}),"\n",(0,i.jsxs)(n.p,{children:["A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join ",(0,i.jsx)(n.a,{href:"https://docs.google.com/document/d/1QdskbeCtgKcdWYHI6EXkLFxyzTCyVT6e8MgB3CaAhWI/edit?usp=sharing",children:"here"})]}),"\n",(0,i.jsx)(n.h2,{id:"slack",children:"Slack"}),"\n",(0,i.jsxs)(n.p,{children:["To discuss issues with Copa, features, or development, you can join the ",(0,i.jsx)(n.code,{children:"#copa"})," channel on the ",(0,i.jsx)(n.a,{href:"https://communityinviter.com/apps/opencontainers/join-the-oci-community",children:"OCI Slack"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"contributing-issues",children:"Contributing Issues"}),"\n",(0,i.jsxs)(n.p,{children:["Before opening any new issues, please search our ",(0,i.jsx)(n.a,{href:"https://github.com/project-copacetic/copacetic/issues",children:"existing GitHub issues"})," to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead."]}),"\n",(0,i.jsx)(n.p,{children:"When opening an issue, please select the most appropriate template for what you're contributing:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Bug Report:"})," If you would like to report the project or tool behaving in unexpected ways."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Documentation Improvement:"})," If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Request:"})," If you have a feature request, suggestion, or a even a design proposal to review."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Question:"})," If you would like to ask the maintainers a question about the project."]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"contributing-code",children:"Contributing Code"}),"\n",(0,i.jsx)(n.h3,{id:"getting-started",children:"Getting Started"}),"\n",(0,i.jsx)(n.p,{children:"Follow the instructions to either:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"/copacetic/website/v0.3.x/installation",children:"Setup your dev environment to build copa"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"#visual-studio-code-development-container",children:"Use the copa development container"})," in ",(0,i.jsx)(n.a,{href:"https://code.visualstudio.com/",children:"Visual Studio Code"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["For an overview of the project components, refer to the ",(0,i.jsx)(n.a,{href:"/copacetic/website/v0.3.x/design",children:"copa design"})," document."]}),"\n",(0,i.jsx)(n.h3,{id:"visual-studio-code-development-container",children:"Visual Studio Code Development Container"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"https://code.visualstudio.com/",children:"VSCode"})," supports development in a containerized environment through its ",(0,i.jsx)(n.a,{href:"https://code.visualstudio.com/docs/remote/containers",children:"Remote - Container extension"}),". This folder provides a development container which encapsulates the dependencies specified in the ",(0,i.jsx)(n.a,{href:"/copacetic/website/v0.3.x/installation",children:"instructions to build and run copa"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"https://docs.docker.com/get-docker/",children:"Docker"}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsxs)(n.p,{children:["For Windows users, enabling ",(0,i.jsx)(n.a,{href:"https://docs.docker.com/docker-for-windows/wsl/",children:"WSL2 back-end integration with Docker"})," is recommended."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://code.visualstudio.com/",children:"Visual Studio Code"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers",children:"Visual Studio Code Remote - Containers extension"})}),"\n"]}),"\n",(0,i.jsx)(n.h4,{id:"using-the-dev-container",children:"Using the dev container"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["After you have cloned this repo locally, open the repo folder in VSCode. VSCode will detect the presence of this ",(0,i.jsx)(n.code,{children:".devcontainer"})," subfolder and will prompt you to reopen the project in a container."]}),"\n",(0,i.jsxs)(n.p,{children:["Alternatively, you can open the command palette and use the ",(0,i.jsx)(n.code,{children:"Remote-Containers: Reopen in Container"})," command."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["Once the container is loaded, open an ",(0,i.jsx)(n.a,{href:"https://code.visualstudio.com/docs/editor/integrated-terminal",children:"integrated terminal"})," in VSCode and you can start running the demo instructions."]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"\u26a0 If running via Docker Desktop for Windows"})}),"\n",(0,i.jsxs)(n.p,{children:["Note that the ",(0,i.jsxs)(n.a,{href:"https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user",children:["mounted workspace files appear owned by ",(0,i.jsx)(n.code,{children:"root"})]})," in the dev container, which will cause ",(0,i.jsx)(n.code,{children:"git"})," commands to fail with a ",(0,i.jsx)(n.code,{children:"fatal: detected dubious ownership in a repository"})," error due to ",(0,i.jsx)(n.a,{href:"https://git-scm.com/docs/git-config/2.35.2#Documentation/git-config.txt-safedirectory",children:"safe.directory"})," checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the ",(0,i.jsx)(n.code,{children:"vscode"})," user:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"sudo chown -R vscode:vscode /workspace/copacetic\n"})}),"\n"]}),"\n",(0,i.jsx)(n.h4,{id:"personalizing-user-settings-in-a-dev-container",children:"Personalizing user settings in a dev container"}),"\n",(0,i.jsxs)(n.p,{children:["VSCode supports applying your user settings, such as your ",(0,i.jsx)(n.code,{children:".gitconfig"}),", to a dev container through the use of ",(0,i.jsx)(n.a,{href:"https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories",children:"dotfiles repositories"}),". This can be done through your own VSCode ",(0,i.jsx)(n.code,{children:"settings.json"})," file without changing the dev container image or configuration."]}),"\n",(0,i.jsx)(n.h3,{id:"tests",children:"Tests"}),"\n",(0,i.jsxs)(n.p,{children:["Once you can successfully ",(0,i.jsx)(n.code,{children:"make"})," the project, any code contributions should also successfully:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Pass unit tests via ",(0,i.jsx)(n.code,{children:"make test"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["Lint cleanly via ",(0,i.jsx)(n.code,{children:"make lint"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["Pull requests will also be expected to pass the PR functional tests specified by ",(0,i.jsx)(n.code,{children:".github/workflows/build.yml"}),"."]}),"\n",(0,i.jsx)(n.h3,{id:"pull-requests",children:"Pull Requests"}),"\n",(0,i.jsxs)(n.p,{children:["If you'd like to start contributing code to the project, you can search for ",(0,i.jsxs)(n.a,{href:"https://github.com/project-copacetic/copacetic/labels/good%20first%20issue",children:["issues with the ",(0,i.jsx)(n.code,{children:"good first issue"})," label"]}),". Other kinds of PR contributions we would look for include:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Fixes for bugs and other correctness issues."}),"\n",(0,i.jsx)(n.li,{children:"Docs and other content improvements (e.g. samples)."}),"\n",(0,i.jsx)(n.li,{children:"Extensions to support parsing new scanning report formats."}),"\n",(0,i.jsx)(n.li,{children:"Extensions to support patching images based on new distros or using new package managers."}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Architectural changes (e.g. breaking interfaces or violations of ",(0,i.jsx)(n.a,{href:"/copacetic/website/v0.3.x/design",children:"this project's design tenets"}),")."]}),"\n",(0,i.jsx)(n.li,{children:"Unsolicited features that significantly expand the functional scope of the tool."}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the ",(0,i.jsx)(n.a,{href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format",children:"Angular commit message format"})," for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs."]}),"\n",(0,i.jsx)(n.p,{children:"We suggest:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Use the standard header format of ",(0,i.jsx)(n.code,{children:'"<type>: <short summary>"'})," where the ",(0,i.jsx)(n.code,{children:"<type>"})," is one of the following:","\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"build:"})," Changes that affect the build system or external dependencies"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"ci:"})," Changes to the GitHub workflows and configurations"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"docs:"})," Documentation only changes"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"feat:"})," A new feature"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"fix:"})," A bug fix"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"perf:"})," A code change that improves performance"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"refactor:"})," A code change that neither fixes a bug nor adds a feature"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"test:"})," Adding missing tests or correcting existing tests"]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["Use a ",(0,i.jsx)(n.a,{href:"https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html",children:"concise, imperative description"})," of the changes included in the ",(0,i.jsx)(n.code,{children:"<short summary>"})," of the header, the body of the PR, and generally in your commit messages."]}),"\n",(0,i.jsxs)(n.li,{children:["Use ",(0,i.jsx)(n.a,{href:"https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests",children:"GitHub keywords"})," in the footer of your PR description, such as ",(0,i.jsx)(n.code,{children:"closes"})," to automatically close issues the PR intends to address."]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"developer-certificate-of-origin-dco",children:"Developer Certificate of Origin (DCO)"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.a,{href:"https://wiki.linuxfoundation.org/dco",children:"Developer Certificate of Origin"})," (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the ",(0,i.jsx)(n.a,{href:"https://developercertificate.org/",children:"full text of the DCO"}),", reformatted for readability:"]}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsx)(n.p,{children:"By making a contribution to this project, I certify that:"}),"\n",(0,i.jsx)(n.p,{children:"(a) The contribution was created in whole or in part by me and I\nhave the right to submit it under the open source license\nindicated in the file; or"}),"\n",(0,i.jsx)(n.p,{children:"(b) The contribution is based upon previous work that, to the best\nof my knowledge, is covered under an appropriate open source\nlicense and I have the right under that license to submit that\nwork with modifications, whether created in whole or in part\nby me, under the same open source license (unless I am\npermitted to submit under a different license), as indicated\nin the file; or"}),"\n",(0,i.jsx)(n.p,{children:"(c) The contribution was provided directly to me by some other\nperson who certified (a), (b) or (c) and I have not modified\nit."}),"\n",(0,i.jsx)(n.p,{children:"(d) I understand and agree that this project and the contribution\nare public and that a record of the contribution (including all\npersonal information I submit with it, including my sign-off) is\nmaintained indefinitely and may be redistributed consistent with\nthis project or the open source license(s) involved."}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["Contributors ",(0,i.jsx)(n.em,{children:"sign-off"})," that they adhere to these requirements by adding a ",(0,i.jsx)(n.code,{children:"Signed-off-by"})," line to commit messages."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-text",children:"This is my commit message\n\nSigned-off-by: Random J Developer <random@developer.example.org>\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Git even has a ",(0,i.jsx)(n.code,{children:"-s"})," command line option to append this automatically to your commit message:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"git commit -s -m 'This is my commit message'\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Pull requests that do not contain a valid ",(0,i.jsx)(n.code,{children:"Signed-off-by"})," line cannot be merged."]}),"\n",(0,i.jsx)(n.h3,{id:"i-didnt-sign-my-commit-now-what",children:"I didn't sign my commit, now what?"}),"\n",(0,i.jsx)(n.p,{children:"No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"git switch <branch-name>\ngit commit --amend --no-edit --signoff\ngit push --force-with-lease <remote-name> <branch-name>\n"})}),"\n",(0,i.jsx)(n.h2,{id:"code-of-conduct",children:"Code of Conduct"}),"\n",(0,i.jsxs)(n.p,{children:["This project has adopted the ",(0,i.jsx)(n.a,{href:"/copacetic/website/v0.3.x/code-of-conduct",children:"Contributor Covenant Code of Conduct"}),"."]})]})}function h(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(l,{...e})}):l(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>r,x:()=>c});var i=t(6540);const s={},o=i.createContext(s);function r(e){const n=i.useContext(o);return i.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),i.createElement(o.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/801664dc.a10c85ef.js b/website/assets/js/801664dc.a10c85ef.js new file mode 100644 index 00000000..ba260ae3 --- /dev/null +++ b/website/assets/js/801664dc.a10c85ef.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5819],{2004:(e,t,i)=>{i.r(t),i.d(t,{assets:()=>d,contentTitle:()=>r,default:()=>h,frontMatter:()=>o,metadata:()=>c,toc:()=>a});var n=i(4848),s=i(8453);const o={title:"Contributing"},r=void 0,c={id:"contributing",title:"Contributing",description:"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.",source:"@site/versioned_docs/version-v0.6.x/contributing.md",sourceDirName:".",slug:"/contributing",permalink:"/copacetic/website/contributing",draft:!1,unlisted:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Contributing"},sidebar:"sidebar",previous:{title:"Scanner Plugins",permalink:"/copacetic/website/scanner-plugins"},next:{title:"Code of Conduct",permalink:"/copacetic/website/code-of-conduct"}},d={},a=[{value:"Bi-Weekly Community Meeting",id:"bi-weekly-community-meeting",level:2},{value:"Slack",id:"slack",level:2},{value:"Contributing Issues",id:"contributing-issues",level:2},{value:"Contributing Code",id:"contributing-code",level:2},{value:"Getting Started",id:"getting-started",level:3},{value:"Visual Studio Code Development Container",id:"visual-studio-code-development-container",level:3},{value:"Prerequisites",id:"prerequisites",level:4},{value:"Personalizing user settings in a dev container",id:"personalizing-user-settings-in-a-dev-container",level:4},{value:"Tests",id:"tests",level:3},{value:"Pull Requests",id:"pull-requests",level:3},{value:"Developer Certificate of Origin (DCO)",id:"developer-certificate-of-origin-dco",level:2},{value:"I didn't sign my commit, now what?",id:"i-didnt-sign-my-commit-now-what",level:3},{value:"Code of Conduct",id:"code-of-conduct",level:2}];function l(e){const t={a:"a",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h3:"h3",h4:"h4",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsxs)(t.p,{children:["Welcome! We are very happy to accept community contributions to the project, whether through ",(0,n.jsx)(t.a,{href:"#contributing-issues",children:"filing issues"})," or ",(0,n.jsx)(t.a,{href:"#contributing-code",children:"code"})," in the form of ",(0,n.jsx)(t.a,{href:"#pull-requests",children:"Pull Requests"}),". Please note that by participating in this project, you agree to abide by the ",(0,n.jsx)(t.a,{href:"/copacetic/website/code-of-conduct",children:"Code of Conduct"}),", as well as the terms of the ",(0,n.jsx)(t.a,{href:"#developer-certificate-of-origin-dco",children:"Developer Certificate of Origin"}),"."]}),"\n",(0,n.jsx)(t.h2,{id:"bi-weekly-community-meeting",children:"Bi-Weekly Community Meeting"}),"\n",(0,n.jsxs)(t.p,{children:["A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join ",(0,n.jsx)(t.a,{href:"https://docs.google.com/document/d/1QdskbeCtgKcdWYHI6EXkLFxyzTCyVT6e8MgB3CaAhWI/edit?usp=sharing",children:"here"})]}),"\n",(0,n.jsx)(t.h2,{id:"slack",children:"Slack"}),"\n",(0,n.jsxs)(t.p,{children:["To discuss issues with Copa, features, or development, you can join the ",(0,n.jsx)(t.code,{children:"#copa"})," channel on the ",(0,n.jsx)(t.a,{href:"https://communityinviter.com/apps/opencontainers/join-the-oci-community",children:"OCI Slack"}),"."]}),"\n",(0,n.jsx)(t.h2,{id:"contributing-issues",children:"Contributing Issues"}),"\n",(0,n.jsxs)(t.p,{children:["Before opening any new issues, please search our ",(0,n.jsx)(t.a,{href:"https://github.com/project-copacetic/copacetic/issues",children:"existing GitHub issues"})," to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead."]}),"\n",(0,n.jsx)(t.p,{children:"When opening an issue, please select the most appropriate template for what you're contributing:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"Bug Report:"})," If you would like to report the project or tool behaving in unexpected ways."]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"Documentation Improvement:"})," If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content."]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"Request:"})," If you have a feature request, suggestion, or a even a design proposal to review."]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"Question:"})," If you would like to ask the maintainers a question about the project."]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"contributing-code",children:"Contributing Code"}),"\n",(0,n.jsx)(t.h3,{id:"getting-started",children:"Getting Started"}),"\n",(0,n.jsx)(t.p,{children:"Follow the instructions to either:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.a,{href:"/copacetic/website/installation",children:"Setup your dev environment to build copa"}),"."]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.a,{href:"#visual-studio-code-development-container",children:"Use the copa development container"})," in ",(0,n.jsx)(t.a,{href:"https://code.visualstudio.com/",children:"Visual Studio Code"}),"."]}),"\n"]}),"\n",(0,n.jsxs)(t.p,{children:["For an overview of the project components, refer to the ",(0,n.jsx)(t.a,{href:"/copacetic/website/design",children:"copa design"})," document."]}),"\n",(0,n.jsx)(t.h3,{id:"visual-studio-code-development-container",children:"Visual Studio Code Development Container"}),"\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.a,{href:"https://code.visualstudio.com/",children:"VSCode"})," supports development in a containerized environment through its ",(0,n.jsx)(t.a,{href:"https://code.visualstudio.com/docs/remote/containers",children:"Remote - Container extension"}),". This folder provides a development container which encapsulates the dependencies specified in the ",(0,n.jsx)(t.a,{href:"/copacetic/website/installation",children:"instructions to build and run copa"}),"."]}),"\n",(0,n.jsx)(t.h4,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,n.jsxs)(t.ol,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.a,{href:"https://docs.docker.com/get-docker/",children:"Docker"}),"\n",(0,n.jsxs)(t.blockquote,{children:["\n",(0,n.jsxs)(t.p,{children:["For Windows users, enabling ",(0,n.jsx)(t.a,{href:"https://docs.docker.com/docker-for-windows/wsl/",children:"WSL2 back-end integration with Docker"})," is recommended."]}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.li,{children:(0,n.jsx)(t.a,{href:"https://code.visualstudio.com/",children:"Visual Studio Code"})}),"\n",(0,n.jsx)(t.li,{children:(0,n.jsx)(t.a,{href:"https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers",children:"Visual Studio Code Remote - Containers extension"})}),"\n"]}),"\n",(0,n.jsxs)(t.blockquote,{children:["\n",(0,n.jsx)(t.p,{children:(0,n.jsx)(t.strong,{children:"\u26a0 If running via Docker Desktop for Windows"})}),"\n",(0,n.jsxs)(t.p,{children:["Note that the ",(0,n.jsxs)(t.a,{href:"https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user",children:["mounted workspace files appear owned by ",(0,n.jsx)(t.code,{children:"root"})]})," in the dev container, which will cause ",(0,n.jsx)(t.code,{children:"git"})," commands to fail with a ",(0,n.jsx)(t.code,{children:"fatal: detected dubious ownership in a repository"})," error due to ",(0,n.jsx)(t.a,{href:"https://git-scm.com/docs/git-config/2.35.2#Documentation/git-config.txt-safedirectory",children:"safe.directory"})," checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the ",(0,n.jsx)(t.code,{children:"vscode"})," user:"]}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-bash",children:"sudo chown -R vscode:vscode /workspace/copacetic\n"})}),"\n"]}),"\n",(0,n.jsx)(t.h4,{id:"personalizing-user-settings-in-a-dev-container",children:"Personalizing user settings in a dev container"}),"\n",(0,n.jsxs)(t.p,{children:["VSCode supports applying your user settings, such as your ",(0,n.jsx)(t.code,{children:".gitconfig"}),", to a dev container through the use of ",(0,n.jsx)(t.a,{href:"https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories",children:"dotfiles repositories"}),". This can be done through your own VSCode ",(0,n.jsx)(t.code,{children:"settings.json"})," file without changing the dev container image or configuration."]}),"\n",(0,n.jsx)(t.h3,{id:"tests",children:"Tests"}),"\n",(0,n.jsxs)(t.p,{children:["Once you can successfully ",(0,n.jsx)(t.code,{children:"make"})," the project, any code contributions should also successfully:"]}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["Pass unit tests via ",(0,n.jsx)(t.code,{children:"make test"}),"."]}),"\n",(0,n.jsxs)(t.li,{children:["Lint cleanly via ",(0,n.jsx)(t.code,{children:"make lint"}),"."]}),"\n"]}),"\n",(0,n.jsxs)(t.p,{children:["Pull requests will also be expected to pass the PR functional tests specified by ",(0,n.jsx)(t.code,{children:".github/workflows/build.yml"}),"."]}),"\n",(0,n.jsx)(t.h3,{id:"pull-requests",children:"Pull Requests"}),"\n",(0,n.jsxs)(t.p,{children:["If you'd like to start contributing code to the project, you can search for ",(0,n.jsxs)(t.a,{href:"https://github.com/project-copacetic/copacetic/labels/good%20first%20issue",children:["issues with the ",(0,n.jsx)(t.code,{children:"good first issue"})," label"]}),". Other kinds of PR contributions we would look for include:"]}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"Fixes for bugs and other correctness issues."}),"\n",(0,n.jsx)(t.li,{children:"Docs and other content improvements (e.g. samples)."}),"\n",(0,n.jsx)(t.li,{children:"Extensions to support parsing new scanning report formats."}),"\n",(0,n.jsx)(t.li,{children:"Extensions to support patching images based on new distros or using new package managers."}),"\n"]}),"\n",(0,n.jsx)(t.p,{children:"For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["Architectural changes (e.g. breaking interfaces or violations of ",(0,n.jsx)(t.a,{href:"/copacetic/website/design",children:"this project's design tenets"}),")."]}),"\n",(0,n.jsx)(t.li,{children:"Unsolicited features that significantly expand the functional scope of the tool."}),"\n"]}),"\n",(0,n.jsxs)(t.p,{children:["Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the ",(0,n.jsx)(t.a,{href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format",children:"Angular commit message format"})," for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs."]}),"\n",(0,n.jsx)(t.p,{children:"We suggest:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["Use the standard header format of ",(0,n.jsx)(t.code,{children:'"<type>: <short summary>"'})," where the ",(0,n.jsx)(t.code,{children:"<type>"})," is one of the following:","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"build:"})," Changes that affect the build system or external dependencies"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"ci:"})," Changes to the GitHub workflows and configurations"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"docs:"})," Documentation only changes"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"feat:"})," A new feature"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"fix:"})," A bug fix"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"perf:"})," A code change that improves performance"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"refactor:"})," A code change that neither fixes a bug nor adds a feature"]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"test:"})," Adding missing tests or correcting existing tests"]}),"\n"]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["Use a ",(0,n.jsx)(t.a,{href:"https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html",children:"concise, imperative description"})," of the changes included in the ",(0,n.jsx)(t.code,{children:"<short summary>"})," of the header, the body of the PR, and generally in your commit messages."]}),"\n",(0,n.jsxs)(t.li,{children:["Use ",(0,n.jsx)(t.a,{href:"https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests",children:"GitHub keywords"})," in the footer of your PR description, such as ",(0,n.jsx)(t.code,{children:"closes"})," to automatically close issues the PR intends to address."]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"developer-certificate-of-origin-dco",children:"Developer Certificate of Origin (DCO)"}),"\n",(0,n.jsxs)(t.p,{children:["The ",(0,n.jsx)(t.a,{href:"https://wiki.linuxfoundation.org/dco",children:"Developer Certificate of Origin"})," (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the ",(0,n.jsx)(t.a,{href:"https://developercertificate.org/",children:"full text of the DCO"}),", reformatted for readability:"]}),"\n",(0,n.jsxs)(t.blockquote,{children:["\n",(0,n.jsx)(t.p,{children:"By making a contribution to this project, I certify that:"}),"\n",(0,n.jsx)(t.p,{children:"(a) The contribution was created in whole or in part by me and I\nhave the right to submit it under the open source license\nindicated in the file; or"}),"\n",(0,n.jsx)(t.p,{children:"(b) The contribution is based upon previous work that, to the best\nof my knowledge, is covered under an appropriate open source\nlicense and I have the right under that license to submit that\nwork with modifications, whether created in whole or in part\nby me, under the same open source license (unless I am\npermitted to submit under a different license), as indicated\nin the file; or"}),"\n",(0,n.jsx)(t.p,{children:"(c) The contribution was provided directly to me by some other\nperson who certified (a), (b) or (c) and I have not modified\nit."}),"\n",(0,n.jsx)(t.p,{children:"(d) I understand and agree that this project and the contribution\nare public and that a record of the contribution (including all\npersonal information I submit with it, including my sign-off) is\nmaintained indefinitely and may be redistributed consistent with\nthis project or the open source license(s) involved."}),"\n"]}),"\n",(0,n.jsxs)(t.p,{children:["Contributors ",(0,n.jsx)(t.em,{children:"sign-off"})," that they adhere to these requirements by adding a ",(0,n.jsx)(t.code,{children:"Signed-off-by"})," line to commit messages."]}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-text",children:"This is my commit message\n\nSigned-off-by: Random J Developer <random@developer.example.org>\n"})}),"\n",(0,n.jsxs)(t.p,{children:["Git even has a ",(0,n.jsx)(t.code,{children:"-s"})," command line option to append this automatically to your commit message:"]}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-bash",children:"git commit -s -m 'This is my commit message'\n"})}),"\n",(0,n.jsxs)(t.p,{children:["Pull requests that do not contain a valid ",(0,n.jsx)(t.code,{children:"Signed-off-by"})," line cannot be merged."]}),"\n",(0,n.jsx)(t.h3,{id:"i-didnt-sign-my-commit-now-what",children:"I didn't sign my commit, now what?"}),"\n",(0,n.jsx)(t.p,{children:"No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:"}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-bash",children:"git switch <branch-name>\ngit commit --amend --no-edit --signoff\ngit push --force-with-lease <remote-name> <branch-name>\n"})}),"\n",(0,n.jsx)(t.h2,{id:"code-of-conduct",children:"Code of Conduct"}),"\n",(0,n.jsxs)(t.p,{children:["This project has adopted the ",(0,n.jsx)(t.a,{href:"/copacetic/website/code-of-conduct",children:"Contributor Covenant Code of Conduct"}),"."]})]})}function h(e={}){const{wrapper:t}={...(0,s.R)(),...e.components};return t?(0,n.jsx)(t,{...e,children:(0,n.jsx)(l,{...e})}):l(e)}},8453:(e,t,i)=>{i.d(t,{R:()=>r,x:()=>c});var n=i(6540);const s={},o=n.createContext(s);function r(e){const t=n.useContext(o);return n.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function c(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),n.createElement(o.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/801664dc.e764cf5c.js b/website/assets/js/801664dc.e764cf5c.js deleted file mode 100644 index 4dcf9642..00000000 --- a/website/assets/js/801664dc.e764cf5c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1150],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var o=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function r(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?a(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):a(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,o,i=function(e,t){if(null==e)return{};var n,o,i={},a=Object.keys(e);for(o=0;o<a.length;o++)n=a[o],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o<a.length;o++)n=a[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=o.createContext({}),c=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},u=function(e){var t=c(e.components);return o.createElement(l.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),p=c(n),m=i,h=p["".concat(l,".").concat(m)]||p[m]||d[m]||a;return n?o.createElement(h,r(r({ref:t},u),{},{components:n})):o.createElement(h,r({ref:t},u))}));function h(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,r=new Array(a);r[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[p]="string"==typeof e?e:i,r[1]=s;for(var c=2;c<a;c++)r[c]=n[c];return o.createElement.apply(null,r)}return o.createElement.apply(null,n)}m.displayName="MDXCreateElement"},6903:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>p,frontMatter:()=>a,metadata:()=>s,toc:()=>c});var o=n(7462),i=(n(7294),n(3905));const a={title:"Contributing"},r=void 0,s={unversionedId:"contributing",id:"version-v0.6.x/contributing",title:"Contributing",description:"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.",source:"@site/versioned_docs/version-v0.6.x/contributing.md",sourceDirName:".",slug:"/contributing",permalink:"/copacetic/website/contributing",draft:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Contributing"},sidebar:"sidebar",previous:{title:"Scanner Plugins",permalink:"/copacetic/website/scanner-plugins"},next:{title:"Code of Conduct",permalink:"/copacetic/website/code-of-conduct"}},l={},c=[{value:"Bi-Weekly Community Meeting",id:"bi-weekly-community-meeting",level:2},{value:"Slack",id:"slack",level:2},{value:"Contributing Issues",id:"contributing-issues",level:2},{value:"Contributing Code",id:"contributing-code",level:2},{value:"Getting Started",id:"getting-started",level:3},{value:"Visual Studio Code Development Container",id:"visual-studio-code-development-container",level:3},{value:"Prerequisites",id:"prerequisites",level:4},{value:"Personalizing user settings in a dev container",id:"personalizing-user-settings-in-a-dev-container",level:4},{value:"Tests",id:"tests",level:3},{value:"Pull Requests",id:"pull-requests",level:3},{value:"Developer Certificate of Origin (DCO)",id:"developer-certificate-of-origin-dco",level:2},{value:"I didn't sign my commit, now what?",id:"i-didnt-sign-my-commit-now-what",level:3},{value:"Code of Conduct",id:"code-of-conduct",level:2}],u={toc:c};function p(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,o.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"Welcome! We are very happy to accept community contributions to the project, whether through ",(0,i.kt)("a",{parentName:"p",href:"#contributing-issues"},"filing issues")," or ",(0,i.kt)("a",{parentName:"p",href:"#contributing-code"},"code")," in the form of ",(0,i.kt)("a",{parentName:"p",href:"#pull-requests"},"Pull Requests"),". Please note that by participating in this project, you agree to abide by the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/code-of-conduct"},"Code of Conduct"),", as well as the terms of the ",(0,i.kt)("a",{parentName:"p",href:"#developer-certificate-of-origin-dco"},"Developer Certificate of Origin"),"."),(0,i.kt)("h2",{id:"bi-weekly-community-meeting"},"Bi-Weekly Community Meeting"),(0,i.kt)("p",null,"A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join ",(0,i.kt)("a",{parentName:"p",href:"https://docs.google.com/document/d/1QdskbeCtgKcdWYHI6EXkLFxyzTCyVT6e8MgB3CaAhWI/edit?usp=sharing"},"here")),(0,i.kt)("h2",{id:"slack"},"Slack"),(0,i.kt)("p",null,"To discuss issues with Copa, features, or development, you can join the ",(0,i.kt)("inlineCode",{parentName:"p"},"#copa")," channel on the ",(0,i.kt)("a",{parentName:"p",href:"https://communityinviter.com/apps/opencontainers/join-the-oci-community"},"OCI Slack"),"."),(0,i.kt)("h2",{id:"contributing-issues"},"Contributing Issues"),(0,i.kt)("p",null,"Before opening any new issues, please search our ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/issues"},"existing GitHub issues")," to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead."),(0,i.kt)("p",null,"When opening an issue, please select the most appropriate template for what you're contributing:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Bug Report:")," If you would like to report the project or tool behaving in unexpected ways."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Documentation Improvement:")," If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Request:")," If you have a feature request, suggestion, or a even a design proposal to review."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Question:")," If you would like to ask the maintainers a question about the project.")),(0,i.kt)("h2",{id:"contributing-code"},"Contributing Code"),(0,i.kt)("h3",{id:"getting-started"},"Getting Started"),(0,i.kt)("p",null,"Follow the instructions to either:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/copacetic/website/installation"},"Setup your dev environment to build copa"),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"#visual-studio-code-development-container"},"Use the copa development container")," in ",(0,i.kt)("a",{parentName:"li",href:"https://code.visualstudio.com/"},"Visual Studio Code"),".")),(0,i.kt)("p",null,"For an overview of the project components, refer to the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/design"},"copa design")," document."),(0,i.kt)("h3",{id:"visual-studio-code-development-container"},"Visual Studio Code Development Container"),(0,i.kt)("p",null,(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/"},"VSCode")," supports development in a containerized environment through its ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/docs/remote/containers"},"Remote - Container extension"),". This folder provides a development container which encapsulates the dependencies specified in the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/installation"},"instructions to build and run copa"),"."),(0,i.kt)("h4",{id:"prerequisites"},"Prerequisites"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://docs.docker.com/get-docker/"},"Docker"),(0,i.kt)("blockquote",{parentName:"li"},(0,i.kt)("p",{parentName:"blockquote"},"For Windows users, enabling ",(0,i.kt)("a",{parentName:"p",href:"https://docs.docker.com/docker-for-windows/wsl/"},"WSL2 back-end integration with Docker")," is recommended."))),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://code.visualstudio.com/"},"Visual Studio Code")),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers"},"Visual Studio Code Remote - Containers extension"))),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},(0,i.kt)("strong",{parentName:"p"},"\u26a0 If running via Docker Desktop for Windows")),(0,i.kt)("p",{parentName:"blockquote"},"Note that the ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user"},"mounted workspace files appear owned by ",(0,i.kt)("inlineCode",{parentName:"a"},"root"))," in the dev container, which will cause ",(0,i.kt)("inlineCode",{parentName:"p"},"git")," commands to fail with a ",(0,i.kt)("inlineCode",{parentName:"p"},"fatal: detected dubious ownership in a repository")," error due to ",(0,i.kt)("a",{parentName:"p",href:"https://git-scm.com/docs/git-config/2.35.2#Documentation/git-config.txt-safedirectory"},"safe.directory")," checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the ",(0,i.kt)("inlineCode",{parentName:"p"},"vscode")," user:"),(0,i.kt)("pre",{parentName:"blockquote"},(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"sudo chown -R vscode:vscode /workspace/copacetic\n"))),(0,i.kt)("h4",{id:"personalizing-user-settings-in-a-dev-container"},"Personalizing user settings in a dev container"),(0,i.kt)("p",null,"VSCode supports applying your user settings, such as your ",(0,i.kt)("inlineCode",{parentName:"p"},".gitconfig"),", to a dev container through the use of ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories"},"dotfiles repositories"),". This can be done through your own VSCode ",(0,i.kt)("inlineCode",{parentName:"p"},"settings.json")," file without changing the dev container image or configuration."),(0,i.kt)("h3",{id:"tests"},"Tests"),(0,i.kt)("p",null,"Once you can successfully ",(0,i.kt)("inlineCode",{parentName:"p"},"make")," the project, any code contributions should also successfully:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Pass unit tests via ",(0,i.kt)("inlineCode",{parentName:"li"},"make test"),"."),(0,i.kt)("li",{parentName:"ul"},"Lint cleanly via ",(0,i.kt)("inlineCode",{parentName:"li"},"make lint"),".")),(0,i.kt)("p",null,"Pull requests will also be expected to pass the PR functional tests specified by ",(0,i.kt)("inlineCode",{parentName:"p"},".github/workflows/build.yml"),"."),(0,i.kt)("h3",{id:"pull-requests"},"Pull Requests"),(0,i.kt)("p",null,"If you'd like to start contributing code to the project, you can search for ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/labels/good%20first%20issue"},"issues with the ",(0,i.kt)("inlineCode",{parentName:"a"},"good first issue")," label"),". Other kinds of PR contributions we would look for include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Fixes for bugs and other correctness issues."),(0,i.kt)("li",{parentName:"ul"},"Docs and other content improvements (e.g. samples)."),(0,i.kt)("li",{parentName:"ul"},"Extensions to support parsing new scanning report formats."),(0,i.kt)("li",{parentName:"ul"},"Extensions to support patching images based on new distros or using new package managers.")),(0,i.kt)("p",null,"For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Architectural changes (e.g. breaking interfaces or violations of ",(0,i.kt)("a",{parentName:"li",href:"/copacetic/website/design"},"this project's design tenets"),")."),(0,i.kt)("li",{parentName:"ul"},"Unsolicited features that significantly expand the functional scope of the tool.")),(0,i.kt)("p",null,"Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format"},"Angular commit message format")," for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs."),(0,i.kt)("p",null,"We suggest:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Use the standard header format of ",(0,i.kt)("inlineCode",{parentName:"li"},'"<type>: <short summary>"')," where the ",(0,i.kt)("inlineCode",{parentName:"li"},"<type>")," is one of the following:",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"build:")," Changes that affect the build system or external dependencies"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"ci:")," Changes to the GitHub workflows and configurations"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"docs:")," Documentation only changes"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"feat:")," A new feature"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"fix:")," A bug fix"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"perf:")," A code change that improves performance"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"refactor:")," A code change that neither fixes a bug nor adds a feature"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"test:")," Adding missing tests or correcting existing tests"))),(0,i.kt)("li",{parentName:"ul"},"Use a ",(0,i.kt)("a",{parentName:"li",href:"https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html"},"concise, imperative description")," of the changes included in the ",(0,i.kt)("inlineCode",{parentName:"li"},"<short summary>")," of the header, the body of the PR, and generally in your commit messages."),(0,i.kt)("li",{parentName:"ul"},"Use ",(0,i.kt)("a",{parentName:"li",href:"https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests"},"GitHub keywords")," in the footer of your PR description, such as ",(0,i.kt)("inlineCode",{parentName:"li"},"closes")," to automatically close issues the PR intends to address.")),(0,i.kt)("h2",{id:"developer-certificate-of-origin-dco"},"Developer Certificate of Origin (DCO)"),(0,i.kt)("p",null,"The ",(0,i.kt)("a",{parentName:"p",href:"https://wiki.linuxfoundation.org/dco"},"Developer Certificate of Origin")," (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the ",(0,i.kt)("a",{parentName:"p",href:"https://developercertificate.org/"},"full text of the DCO"),", reformatted for readability:"),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},"By making a contribution to this project, I certify that:"),(0,i.kt)("p",{parentName:"blockquote"},"(a) The contribution was created in whole or in part by me and I\nhave the right to submit it under the open source license\nindicated in the file; or"),(0,i.kt)("p",{parentName:"blockquote"},"(b) The contribution is based upon previous work that, to the best\nof my knowledge, is covered under an appropriate open source\nlicense and I have the right under that license to submit that\nwork with modifications, whether created in whole or in part\nby me, under the same open source license (unless I am\npermitted to submit under a different license), as indicated\nin the file; or"),(0,i.kt)("p",{parentName:"blockquote"},"(c) The contribution was provided directly to me by some other\nperson who certified (a), (b) or (c) and I have not modified\nit."),(0,i.kt)("p",{parentName:"blockquote"},"(d) I understand and agree that this project and the contribution\nare public and that a record of the contribution (including all\npersonal information I submit with it, including my sign-off) is\nmaintained indefinitely and may be redistributed consistent with\nthis project or the open source license(s) involved.")),(0,i.kt)("p",null,"Contributors ",(0,i.kt)("em",{parentName:"p"},"sign-off")," that they adhere to these requirements by adding a ",(0,i.kt)("inlineCode",{parentName:"p"},"Signed-off-by")," line to commit messages."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-text"},"This is my commit message\n\nSigned-off-by: Random J Developer <random@developer.example.org>\n")),(0,i.kt)("p",null,"Git even has a ",(0,i.kt)("inlineCode",{parentName:"p"},"-s")," command line option to append this automatically to your commit message:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"git commit -s -m 'This is my commit message'\n")),(0,i.kt)("p",null,"Pull requests that do not contain a valid ",(0,i.kt)("inlineCode",{parentName:"p"},"Signed-off-by")," line cannot be merged."),(0,i.kt)("h3",{id:"i-didnt-sign-my-commit-now-what"},"I didn't sign my commit, now what?"),(0,i.kt)("p",null,"No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"git switch <branch-name>\ngit commit --amend --no-edit --signoff\ngit push --force-with-lease <remote-name> <branch-name>\n")),(0,i.kt)("h2",{id:"code-of-conduct"},"Code of Conduct"),(0,i.kt)("p",null,"This project has adopted the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/code-of-conduct"},"Contributor Covenant Code of Conduct"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/804a5934.e1bcf12f.js b/website/assets/js/804a5934.e93947f5.js similarity index 63% rename from website/assets/js/804a5934.e1bcf12f.js rename to website/assets/js/804a5934.e93947f5.js index 80be7284..490232b6 100644 --- a/website/assets/js/804a5934.e1bcf12f.js +++ b/website/assets/js/804a5934.e93947f5.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7071],{3769:e=>{e.exports=JSON.parse('{"name":"docusaurus-plugin-content-docs","id":"default"}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1157],{1966:e=>{e.exports=JSON.parse('{"name":"docusaurus-plugin-content-docs","id":"default"}')}}]); \ No newline at end of file diff --git a/website/assets/js/84979900.3a0cf957.js b/website/assets/js/84979900.3a0cf957.js new file mode 100644 index 00000000..cfc1de52 --- /dev/null +++ b/website/assets/js/84979900.3a0cf957.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4269],{1632:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>c,contentTitle:()=>r,default:()=>h,frontMatter:()=>o,metadata:()=>a,toc:()=>l});var t=i(4848),s=i(8453);const o={title:"Quick Start"},r=void 0,a={id:"quick-start",title:"Quick Start",description:"This sample illustrates how to patch containers using vulnerability reports with copa.",source:"@site/versioned_docs/version-v0.6.x/quick-start.md",sourceDirName:".",slug:"/quick-start",permalink:"/copacetic/website/quick-start",draft:!1,unlisted:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Quick Start"},sidebar:"sidebar",previous:{title:"Installation",permalink:"/copacetic/website/installation"},next:{title:"Tagging Guidelines",permalink:"/copacetic/website/best-practices"}},c={},l=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Sample Steps",id:"sample-steps",level:2}];function d(e){const n={a:"a",admonition:"admonition",code:"code",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",ul:"ul",...(0,s.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(n.p,{children:["This sample illustrates how to patch containers using vulnerability reports with ",(0,t.jsx)(n.code,{children:"copa"}),"."]}),"\n",(0,t.jsx)(n.h2,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["Linux or macOS configured through the ",(0,t.jsx)(n.a,{href:"/copacetic/website/installation",children:"setup instructions"}),". This includes:","\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"copa"})," tool ",(0,t.jsx)(n.a,{href:"/copacetic/website/installation",children:"built & pathed"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://github.com/moby/buildkit/#quick-start",children:"buildkit"})," daemon installed & pathed. ",(0,t.jsx)(n.a,{href:"#buildkit-connection-examples",children:"Examples"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["The ",(0,t.jsx)(n.code,{children:"docker"})," daemon runs a buildkit service in-process. If you are using this for your buildkit instance, Docker must have the ",(0,t.jsx)(n.a,{href:"https://docs.docker.com/storage/containerd/",children:"containerd image store feature"})," enabled."]}),"\n",(0,t.jsx)(n.li,{children:"If you are using a buildx instance, or using buildkitd directly, there is no need to enable the containerd image store. However, only images in a remote registry can be patched using these methods."}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://docs.docker.com/desktop/linux/install/#generic-installation-steps",children:"docker"})," daemon running and CLI installed & pathed."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://aquasecurity.github.io/trivy/latest/getting-started/installation/",children:"trivy CLI"})," installed & pathed.","\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["Alternatively, see ",(0,t.jsx)(n.a,{href:"#scanner-plugins",children:"scanner plugins"})," for custom scanner support."]}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,t.jsx)(n.h2,{id:"sample-steps",children:"Sample Steps"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6\n"})}),"\n",(0,t.jsx)(n.p,{children:"You can also see the existing patchable vulnerabilities in table form on the shell with:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6\n"})}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"To patch the image, use the Trivy report and specify a buildkit instance to connect to:"}),"\n",(0,t.jsx)(n.p,{children:"By default copa will attempt to auto-connect to an instance in order:"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["Default docker buildkit endpoint (requires at least docker v24.0 with ",(0,t.jsx)(n.a,{href:"https://docs.docker.com/storage/containerd/#enable-containerd-image-store-on-docker-engine",children:"containerd image store"})," support enabled)"]}),"\n",(0,t.jsxs)(n.li,{children:["Currently selected buildx builder (see: ",(0,t.jsx)(n.code,{children:"docker buildx --help"}),")"]}),"\n",(0,t.jsxs)(n.li,{children:["buildkit daemon at the default address ",(0,t.jsx)(n.code,{children:"/run/buildkit/buildkitd.sock"})]}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["If an instance doesn't exist or that instance doesn't support all the features copa needs the next will be attempted. Please see ",(0,t.jsx)(n.a,{href:"/copacetic/website/custom-address",children:"custom buildkit addresses"})," for more information."]}),"\n",(0,t.jsx)(n.p,{children:"After setting up the buildkit instance, run the following command to patch the image:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"copa patch -r nginx.1.21.6.json -i docker.io/library/nginx:1.21.6\n"})}),"\n",(0,t.jsxs)(n.p,{children:["In any of these cases, ",(0,t.jsx)(n.code,{children:"copa"})," is non-destructive and exports a new image with the specified ",(0,t.jsx)(n.code,{children:"1.21.6-patched"})," label to the local Docker daemon."]}),"\n",(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsxs)(n.p,{children:["If you're running this sample against an image from a private registry instead,ensure that the credentials are configured in the default Docker config.json before running ",(0,t.jsx)(n.code,{children:"copa patch"}),", for example, via ",(0,t.jsx)(n.code,{children:"docker login -u <user> -p <password> <registry>"}),"."]})}),"\n",(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsxs)(n.p,{children:["If you're scanning and patching an image that is local-only (i.e. built or tagged locally but not pushed to a registry), ",(0,t.jsx)(n.code,{children:"copa"})," is limited to using ",(0,t.jsx)(n.code,{children:"docker"}),"'s built-in buildkit service, and must use the ",(0,t.jsx)(n.a,{href:"https://docs.docker.com/storage/containerd/",children:(0,t.jsx)(n.code,{children:"containerd image store"})})," feature. This is because only ",(0,t.jsx)(n.code,{children:"docker"}),"'s built-in buildkit service has access to the docker image store (see ",(0,t.jsx)(n.a,{href:"#prerequisites",children:"Prerequisites"})," for more information.)"]})}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Scan the patched image and verify that the vulnerabilities have been patched:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched\n"})}),"\n",(0,t.jsxs)(n.p,{children:["You can also inspect the structure of the patched image with ",(0,t.jsx)(n.code,{children:"docker history"})," to see the new patch layer appended to the image:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'$ docker history docker.io/library/nginx:1.21.6-patched\nIMAGE CREATED CREATED BY SIZE COMMENT\n262dacfeb193 About a minute ago mount / from exec sh -c apt install --no-ins\u2026 41.1MB buildkit.exporter.image.v0\n<missing> 20 months ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon\u2026 0B\n<missing> 20 months ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B\n<missing> 20 months ago /bin/sh -c #(nop) EXPOSE 80 0B\n<missing> 20 months ago /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr\u2026 0B\n<missing> 20 months ago /bin/sh -c #(nop) COPY file:09a214a3e07c919a\u2026 16.4kB\n<missing> 20 months ago /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7\u2026 12.3kB\n<missing> 20 months ago /bin/sh -c #(nop) COPY file:0b866ff3fc1ef5b0\u2026 12.3kB\n<missing> 20 months ago /bin/sh -c #(nop) COPY file:65504f71f5855ca0\u2026 8.19kB\n<missing> 20 months ago /bin/sh -c set -x && addgroup --system -\u2026 64.5MB\n<missing> 20 months ago /bin/sh -c #(nop) ENV PKG_RELEASE=1~bullseye 0B\n<missing> 20 months ago /bin/sh -c #(nop) ENV NJS_VERSION=0.7.3 0B\n<missing> 20 months ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.21.6 0B\n<missing> 20 months ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do\u2026 0B\n<missing> 20 months ago /bin/sh -c #(nop) CMD ["bash"] 0B\n<missing> 20 months ago /bin/sh -c #(nop) ADD file:134f25aec8adf83cb\u2026 91.8MB\n'})}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Run the container to verify that the image has no regressions:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'$ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched\n/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration\n/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh\n10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf\n10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh\n/docker-entrypoint.sh: Configuration complete; ready for start up\n2024/01/22 23:32:54 [notice] 1#1: using the "epoll" event method\n2024/01/22 23:32:54 [notice] 1#1: nginx/1.21.6\n2024/01/22 23:32:54 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)\n2024/01/22 23:32:54 [notice] 1#1: OS: Linux 6.2.0-1018-azure\n2024/01/22 23:32:54 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576\n2024/01/22 23:32:54 [notice] 1#1: start worker processes\n'})}),"\n",(0,t.jsxs)(n.p,{children:["You can stop the container by opening a new shell instance and running: ",(0,t.jsx)(n.code,{children:"docker stop nginx-test"})]}),"\n"]}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(d,{...e})}):d(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>r,x:()=>a});var t=i(6540);const s={},o=t.createContext(s);function r(e){const n=t.useContext(o);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),t.createElement(o.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/84979900.d30448bf.js b/website/assets/js/84979900.d30448bf.js deleted file mode 100644 index ac22b617..00000000 --- a/website/assets/js/84979900.d30448bf.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4789],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>h});var i=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,i,a=function(e,t){if(null==e)return{};var n,i,a={},r=Object.keys(e);for(i=0;i<r.length;i++)n=r[i],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i<r.length;i++)n=r[i],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=i.createContext({}),p=function(e){var t=i.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},c=function(e){var t=p(e.components);return i.createElement(l.Provider,{value:t},e.children)},m="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},d=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),m=p(n),d=a,h=m["".concat(l,".").concat(d)]||m[d]||u[d]||r;return n?i.createElement(h,o(o({ref:t},c),{},{components:n})):i.createElement(h,o({ref:t},c))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,o=new Array(r);o[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[m]="string"==typeof e?e:a,o[1]=s;for(var p=2;p<r;p++)o[p]=n[p];return i.createElement.apply(null,o)}return i.createElement.apply(null,n)}d.displayName="MDXCreateElement"},4320:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>m,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var i=n(7462),a=(n(7294),n(3905));const r={title:"Quick Start"},o=void 0,s={unversionedId:"quick-start",id:"version-v0.6.x/quick-start",title:"Quick Start",description:"This sample illustrates how to patch containers using vulnerability reports with copa.",source:"@site/versioned_docs/version-v0.6.x/quick-start.md",sourceDirName:".",slug:"/quick-start",permalink:"/copacetic/website/quick-start",draft:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Quick Start"},sidebar:"sidebar",previous:{title:"Installation",permalink:"/copacetic/website/installation"},next:{title:"Tagging Guidelines",permalink:"/copacetic/website/best-practices"}},l={},p=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Sample Steps",id:"sample-steps",level:2}],c={toc:p};function m(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,i.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"This sample illustrates how to patch containers using vulnerability reports with ",(0,a.kt)("inlineCode",{parentName:"p"},"copa"),"."),(0,a.kt)("h2",{id:"prerequisites"},"Prerequisites"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"Linux or macOS configured through the ",(0,a.kt)("a",{parentName:"li",href:"/copacetic/website/installation"},"setup instructions"),". This includes:",(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"copa")," tool ",(0,a.kt)("a",{parentName:"li",href:"/copacetic/website/installation"},"built & pathed"),"."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/moby/buildkit/#quick-start"},"buildkit")," daemon installed & pathed. ",(0,a.kt)("a",{parentName:"li",href:"#buildkit-connection-examples"},"Examples"),(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},"The ",(0,a.kt)("inlineCode",{parentName:"li"},"docker")," daemon runs a buildkit service in-process. If you are using this for your buildkit instance, Docker must have the ",(0,a.kt)("a",{parentName:"li",href:"https://docs.docker.com/storage/containerd/"},"containerd image store feature")," enabled."),(0,a.kt)("li",{parentName:"ul"},"If you are using a buildx instance, or using buildkitd directly, there is no need to enable the containerd image store. However, only images in a remote registry can be patched using these methods."))),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://docs.docker.com/desktop/linux/install/#generic-installation-steps"},"docker")," daemon running and CLI installed & pathed."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://aquasecurity.github.io/trivy/latest/getting-started/installation/"},"trivy CLI")," installed & pathed.",(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},"Alternatively, see ",(0,a.kt)("a",{parentName:"li",href:"#scanner-plugins"},"scanner plugins")," for custom scanner support.")))))),(0,a.kt)("h2",{id:"sample-steps"},"Sample Steps"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6\n")),(0,a.kt)("p",{parentName:"li"},"You can also see the existing patchable vulnerabilities in table form on the shell with:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6\n"))),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"To patch the image, use the Trivy report and specify a buildkit instance to connect to:"),(0,a.kt)("p",{parentName:"li"},"By default copa will attempt to auto-connect to an instance in order:"),(0,a.kt)("ol",{parentName:"li"},(0,a.kt)("li",{parentName:"ol"},"Default docker buildkit endpoint (requires at least docker v24.0 with ",(0,a.kt)("a",{parentName:"li",href:"https://docs.docker.com/storage/containerd/#enable-containerd-image-store-on-docker-engine"},"containerd image store")," support enabled)"),(0,a.kt)("li",{parentName:"ol"},"Currently selected buildx builder (see: ",(0,a.kt)("inlineCode",{parentName:"li"},"docker buildx --help"),")"),(0,a.kt)("li",{parentName:"ol"},"buildkit daemon at the default address ",(0,a.kt)("inlineCode",{parentName:"li"},"/run/buildkit/buildkitd.sock"))),(0,a.kt)("p",{parentName:"li"},"If an instance doesn't exist or that instance doesn't support all the features copa needs the next will be attempted. Please see ",(0,a.kt)("a",{parentName:"p",href:"/copacetic/website/custom-address"},"custom buildkit addresses")," for more information."),(0,a.kt)("p",{parentName:"li"},"After setting up the buildkit instance, run the following command to patch the image:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"copa patch -r nginx.1.21.6.json -i docker.io/library/nginx:1.21.6\n")),(0,a.kt)("p",{parentName:"li"},"In any of these cases, ",(0,a.kt)("inlineCode",{parentName:"p"},"copa")," is non-destructive and exports a new image with the specified ",(0,a.kt)("inlineCode",{parentName:"p"},"1.21.6-patched")," label to the local Docker daemon."),(0,a.kt)("admonition",{parentName:"li",type:"note"},(0,a.kt)("p",{parentName:"admonition"},"If you're running this sample against an image from a private registry instead,ensure that the credentials are configured in the default Docker config.json before running ",(0,a.kt)("inlineCode",{parentName:"p"},"copa patch"),", for example, via ",(0,a.kt)("inlineCode",{parentName:"p"},"docker login -u <user> -p <password> <registry>"),".")),(0,a.kt)("admonition",{parentName:"li",type:"note"},(0,a.kt)("p",{parentName:"admonition"},"If you're scanning and patching an image that is local-only (i.e. built or tagged locally but not pushed to a registry), ",(0,a.kt)("inlineCode",{parentName:"p"},"copa")," is limited to using ",(0,a.kt)("inlineCode",{parentName:"p"},"docker"),"'s built-in buildkit service, and must use the ",(0,a.kt)("a",{parentName:"p",href:"https://docs.docker.com/storage/containerd/"},(0,a.kt)("inlineCode",{parentName:"a"},"containerd image store"))," feature. This is because only ",(0,a.kt)("inlineCode",{parentName:"p"},"docker"),"'s built-in buildkit service has access to the docker image store (see ",(0,a.kt)("a",{parentName:"p",href:"#prerequisites"},"Prerequisites")," for more information.)"))),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"Scan the patched image and verify that the vulnerabilities have been patched:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched\n")),(0,a.kt)("p",{parentName:"li"},"You can also inspect the structure of the patched image with ",(0,a.kt)("inlineCode",{parentName:"p"},"docker history")," to see the new patch layer appended to the image:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre",className:"language-bash"},'$ docker history docker.io/library/nginx:1.21.6-patched\nIMAGE CREATED CREATED BY SIZE COMMENT\n262dacfeb193 About a minute ago mount / from exec sh -c apt install --no-ins\u2026 41.1MB buildkit.exporter.image.v0\n<missing> 20 months ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon\u2026 0B\n<missing> 20 months ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B\n<missing> 20 months ago /bin/sh -c #(nop) EXPOSE 80 0B\n<missing> 20 months ago /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr\u2026 0B\n<missing> 20 months ago /bin/sh -c #(nop) COPY file:09a214a3e07c919a\u2026 16.4kB\n<missing> 20 months ago /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7\u2026 12.3kB\n<missing> 20 months ago /bin/sh -c #(nop) COPY file:0b866ff3fc1ef5b0\u2026 12.3kB\n<missing> 20 months ago /bin/sh -c #(nop) COPY file:65504f71f5855ca0\u2026 8.19kB\n<missing> 20 months ago /bin/sh -c set -x && addgroup --system -\u2026 64.5MB\n<missing> 20 months ago /bin/sh -c #(nop) ENV PKG_RELEASE=1~bullseye 0B\n<missing> 20 months ago /bin/sh -c #(nop) ENV NJS_VERSION=0.7.3 0B\n<missing> 20 months ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.21.6 0B\n<missing> 20 months ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do\u2026 0B\n<missing> 20 months ago /bin/sh -c #(nop) CMD ["bash"] 0B\n<missing> 20 months ago /bin/sh -c #(nop) ADD file:134f25aec8adf83cb\u2026 91.8MB\n'))),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"Run the container to verify that the image has no regressions:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre",className:"language-bash"},'$ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched\n/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration\n/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh\n10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf\n10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh\n/docker-entrypoint.sh: Configuration complete; ready for start up\n2024/01/22 23:32:54 [notice] 1#1: using the "epoll" event method\n2024/01/22 23:32:54 [notice] 1#1: nginx/1.21.6\n2024/01/22 23:32:54 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)\n2024/01/22 23:32:54 [notice] 1#1: OS: Linux 6.2.0-1018-azure\n2024/01/22 23:32:54 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576\n2024/01/22 23:32:54 [notice] 1#1: start worker processes\n')),(0,a.kt)("p",{parentName:"li"},"You can stop the container by opening a new shell instance and running: ",(0,a.kt)("inlineCode",{parentName:"p"},"docker stop nginx-test")))))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/86b9c768.1fcb11fa.js b/website/assets/js/86b9c768.1fcb11fa.js new file mode 100644 index 00000000..b543d580 --- /dev/null +++ b/website/assets/js/86b9c768.1fcb11fa.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8442],{8704:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>r,default:()=>p,frontMatter:()=>s,metadata:()=>a,toc:()=>l});var o=n(4848),i=n(8453);const s={title:"Installation"},r=void 0,a={id:"installation",title:"Installation",description:"Homebrew",source:"@site/versioned_docs/version-v0.2.x/installation.md",sourceDirName:".",slug:"/installation",permalink:"/copacetic/website/v0.2.x/installation",draft:!1,unlisted:!1,tags:[],version:"v0.2.x",frontMatter:{title:"Installation"},sidebar:"sidebar",previous:{title:"Introduction",permalink:"/copacetic/website/v0.2.x/"},next:{title:"Quick Start",permalink:"/copacetic/website/v0.2.x/quick-start"}},c={},l=[{value:"Homebrew",id:"homebrew",level:2},{value:"GitHub",id:"github",level:2},{value:"Development Setup",id:"development-setup",level:2}];function d(e){const t={a:"a",code:"code",h2:"h2",p:"p",pre:"pre",strong:"strong",...(0,i.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(t.h2,{id:"homebrew",children:"Homebrew"}),"\n",(0,o.jsxs)(t.p,{children:["On macOS and Linux, ",(0,o.jsx)(t.code,{children:"copa"})," can be installed via ",(0,o.jsx)(t.a,{href:"https://brew.sh/",children:"Homebrew"}),":"]}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-bash",children:"brew install copa\n"})}),"\n",(0,o.jsx)(t.h2,{id:"github",children:"GitHub"}),"\n",(0,o.jsxs)(t.p,{children:["You can download the latest and previous versions of ",(0,o.jsx)(t.code,{children:"copa"})," from the ",(0,o.jsx)(t.a,{href:"https://github.com/project-copacetic/copacetic/releases",children:"GitHub releases page"}),"."]}),"\n",(0,o.jsx)(t.h2,{id:"development-setup",children:"Development Setup"}),"\n",(0,o.jsxs)(t.p,{children:["The following instructions are for ",(0,o.jsx)(t.strong,{children:"Ubuntu 22.04"})," with the dependency versions supported as part of the ",(0,o.jsx)(t.a,{href:"./contributing.md/#visual-studio-code-development-container",children:"dev container"})," environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead."]}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-bash",children:"git clone https://github.com/project-copacetic/copacetic\ncd copacetic\nmake\n# OPTIONAL: install copa to a pathed folder\nsudo mv dist/linux_amd64/release/copa /usr/local/bin/\n"})})]})}function p(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,o.jsx)(t,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>r,x:()=>a});var o=n(6540);const i={},s=o.createContext(i);function r(e){const t=o.useContext(s);return o.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function a(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:r(e.components),o.createElement(s.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/86b9c768.6a1c76ae.js b/website/assets/js/86b9c768.6a1c76ae.js deleted file mode 100644 index 7d78b34b..00000000 --- a/website/assets/js/86b9c768.6a1c76ae.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[267],{3905:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function c(e,t){if(null==e)return{};var n,r,a=function(e,t){if(null==e)return{};var n,r,a={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=r.createContext({}),p=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},s=function(e){var t=p(e.components);return r.createElement(l.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,l=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),u=p(n),m=a,f=u["".concat(l,".").concat(m)]||u[m]||d[m]||o;return n?r.createElement(f,i(i({ref:t},s),{},{components:n})):r.createElement(f,i({ref:t},s))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=m;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[u]="string"==typeof e?e:a,i[1]=c;for(var p=2;p<o;p++)i[p]=n[p];return r.createElement.apply(null,i)}return r.createElement.apply(null,n)}m.displayName="MDXCreateElement"},401:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>c,toc:()=>p});var r=n(7462),a=(n(7294),n(3905));const o={title:"Installation"},i=void 0,c={unversionedId:"installation",id:"version-v0.2.x/installation",title:"Installation",description:"Homebrew",source:"@site/versioned_docs/version-v0.2.x/installation.md",sourceDirName:".",slug:"/installation",permalink:"/copacetic/website/v0.2.x/installation",draft:!1,tags:[],version:"v0.2.x",frontMatter:{title:"Installation"},sidebar:"sidebar",previous:{title:"Introduction",permalink:"/copacetic/website/v0.2.x/"},next:{title:"Quick Start",permalink:"/copacetic/website/v0.2.x/quick-start"}},l={},p=[{value:"Homebrew",id:"homebrew",level:2},{value:"GitHub",id:"github",level:2},{value:"Development Setup",id:"development-setup",level:2}],s={toc:p};function u(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h2",{id:"homebrew"},"Homebrew"),(0,a.kt)("p",null,"On macOS and Linux, ",(0,a.kt)("inlineCode",{parentName:"p"},"copa")," can be installed via ",(0,a.kt)("a",{parentName:"p",href:"https://brew.sh/"},"Homebrew"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"brew install copa\n")),(0,a.kt)("h2",{id:"github"},"GitHub"),(0,a.kt)("p",null,"You can download the latest and previous versions of ",(0,a.kt)("inlineCode",{parentName:"p"},"copa")," from the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/releases"},"GitHub releases page"),"."),(0,a.kt)("h2",{id:"development-setup"},"Development Setup"),(0,a.kt)("p",null,"The following instructions are for ",(0,a.kt)("strong",{parentName:"p"},"Ubuntu 22.04")," with the dependency versions supported as part of the ",(0,a.kt)("a",{parentName:"p",href:"/copacetic/website/v0.2.x/contributing/#visual-studio-code-development-container"},"dev container")," environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"git clone https://github.com/project-copacetic/copacetic\ncd copacetic\nmake\n# OPTIONAL: install copa to a pathed folder\nsudo mv dist/linux_amd64/release/copa /usr/local/bin/\n")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/89eebbd3.287960d1.js b/website/assets/js/89eebbd3.287960d1.js new file mode 100644 index 00000000..227267a5 --- /dev/null +++ b/website/assets/js/89eebbd3.287960d1.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6683],{7365:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>l});var o=i(4848),t=i(8453);const r={title:"Code of Conduct"},a="Contributor Covenant Code of Conduct",s={id:"code-of-conduct",title:"Code of Conduct",description:"Our Pledge",source:"@site/versioned_docs/version-v0.3.x/code-of-conduct.md",sourceDirName:".",slug:"/code-of-conduct",permalink:"/copacetic/website/v0.3.x/code-of-conduct",draft:!1,unlisted:!1,tags:[],version:"v0.3.x",frontMatter:{title:"Code of Conduct"},sidebar:"sidebar",previous:{title:"Contributing",permalink:"/copacetic/website/v0.3.x/contributing"}},c={},l=[{value:"Our Pledge",id:"our-pledge",level:2},{value:"Our Standards",id:"our-standards",level:2},{value:"Enforcement Responsibilities",id:"enforcement-responsibilities",level:2},{value:"Scope",id:"scope",level:2},{value:"Enforcement",id:"enforcement",level:2},{value:"Enforcement Guidelines",id:"enforcement-guidelines",level:2},{value:"1. Correction",id:"1-correction",level:3},{value:"2. Warning",id:"2-warning",level:3},{value:"3. Temporary Ban",id:"3-temporary-ban",level:3},{value:"4. Permanent Ban",id:"4-permanent-ban",level:3},{value:"Attribution",id:"attribution",level:2}];function d(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",li:"li",p:"p",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.h1,{id:"contributor-covenant-code-of-conduct",children:"Contributor Covenant Code of Conduct"}),"\n",(0,o.jsx)(n.h2,{id:"our-pledge",children:"Our Pledge"}),"\n",(0,o.jsx)(n.p,{children:"We as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation."}),"\n",(0,o.jsx)(n.p,{children:"We pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community."}),"\n",(0,o.jsx)(n.h2,{id:"our-standards",children:"Our Standards"}),"\n",(0,o.jsx)(n.p,{children:"Examples of behavior that contributes to a positive environment for our\ncommunity include:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"Demonstrating empathy and kindness toward other people"}),"\n",(0,o.jsx)(n.li,{children:"Being respectful of differing opinions, viewpoints, and experiences"}),"\n",(0,o.jsx)(n.li,{children:"Giving and gracefully accepting constructive feedback"}),"\n",(0,o.jsx)(n.li,{children:"Accepting responsibility and apologizing to those affected by our mistakes,\nand learning from the experience"}),"\n",(0,o.jsx)(n.li,{children:"Focusing on what is best not just for us as individuals, but for the overall\ncommunity"}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"Examples of unacceptable behavior include:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"The use of sexualized language or imagery, and sexual attention or advances of\nany kind"}),"\n",(0,o.jsx)(n.li,{children:"Trolling, insulting or derogatory comments, and personal or political attacks"}),"\n",(0,o.jsx)(n.li,{children:"Public or private harassment"}),"\n",(0,o.jsx)(n.li,{children:"Publishing others' private information, such as a physical or email address,\nwithout their explicit permission"}),"\n",(0,o.jsx)(n.li,{children:"Other conduct which could reasonably be considered inappropriate in a\nprofessional setting"}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"enforcement-responsibilities",children:"Enforcement Responsibilities"}),"\n",(0,o.jsx)(n.p,{children:"Community leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful."}),"\n",(0,o.jsx)(n.p,{children:"Community leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate."}),"\n",(0,o.jsx)(n.h2,{id:"scope",children:"Scope"}),"\n",(0,o.jsx)(n.p,{children:"This Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event."}),"\n",(0,o.jsx)(n.h2,{id:"enforcement",children:"Enforcement"}),"\n",(0,o.jsxs)(n.p,{children:["Instances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n",(0,o.jsx)(n.code,{children:"project-copacetic@googlegroups.com"}),".\nAll complaints will be reviewed and investigated promptly and fairly."]}),"\n",(0,o.jsx)(n.p,{children:"All community leaders are obligated to respect the privacy and security of the\nreporter of any incident."}),"\n",(0,o.jsx)(n.h2,{id:"enforcement-guidelines",children:"Enforcement Guidelines"}),"\n",(0,o.jsx)(n.p,{children:"Community leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:"}),"\n",(0,o.jsx)(n.h3,{id:"1-correction",children:"1. Correction"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested."]}),"\n",(0,o.jsx)(n.h3,{id:"2-warning",children:"2. Warning"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": A violation through a single incident or series of\nactions."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban."]}),"\n",(0,o.jsx)(n.h3,{id:"3-temporary-ban",children:"3. Temporary Ban"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": A serious violation of community standards, including\nsustained inappropriate behavior."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban."]}),"\n",(0,o.jsx)(n.h3,{id:"4-permanent-ban",children:"4. Permanent Ban"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A permanent ban from any sort of public interaction within the\ncommunity."]}),"\n",(0,o.jsx)(n.h2,{id:"attribution",children:"Attribution"}),"\n",(0,o.jsxs)(n.p,{children:["This Code of Conduct is adapted from the ",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org",children:"Contributor Covenant"}),",\nversion 2.1, available at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html",children:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["Community Impact Guidelines were inspired by\n",(0,o.jsx)(n.a,{href:"https://github.com/mozilla/diversity",children:"Mozilla's code of conduct enforcement ladder"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["For answers to common questions about this code of conduct, see the FAQ at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/faq",children:"https://www.contributor-covenant.org/faq"}),". Translations are available at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/translations",children:"https://www.contributor-covenant.org/translations"}),"."]})]})}function u(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>a,x:()=>s});var o=i(6540);const t={},r=o.createContext(t);function a(e){const n=o.useContext(r);return o.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function s(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:a(e.components),o.createElement(r.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/89eebbd3.f1022e5a.js b/website/assets/js/89eebbd3.f1022e5a.js deleted file mode 100644 index ee37bc65..00000000 --- a/website/assets/js/89eebbd3.f1022e5a.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9925],{3905:(e,n,t)=>{t.d(n,{Zo:()=>p,kt:()=>f});var o=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);n&&(o=o.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,o)}return t}function a(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?r(Object(t),!0).forEach((function(n){i(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):r(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))}))}return e}function l(e,n){if(null==e)return{};var t,o,i=function(e,n){if(null==e)return{};var t,o,i={},r=Object.keys(e);for(o=0;o<r.length;o++)t=r[o],n.indexOf(t)>=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o<r.length;o++)t=r[o],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var c=o.createContext({}),s=function(e){var n=o.useContext(c),t=n;return e&&(t="function"==typeof e?e(n):a(a({},n),e)),t},p=function(e){var n=s(e.components);return o.createElement(c.Provider,{value:n},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var n=e.children;return o.createElement(o.Fragment,{},n)}},m=o.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,c=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=s(t),m=i,f=u["".concat(c,".").concat(m)]||u[m]||d[m]||r;return t?o.createElement(f,a(a({ref:n},p),{},{components:t})):o.createElement(f,a({ref:n},p))}));function f(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,a=new Array(r);a[0]=m;var l={};for(var c in n)hasOwnProperty.call(n,c)&&(l[c]=n[c]);l.originalType=e,l[u]="string"==typeof e?e:i,a[1]=l;for(var s=2;s<r;s++)a[s]=t[s];return o.createElement.apply(null,a)}return o.createElement.apply(null,t)}m.displayName="MDXCreateElement"},8902:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>r,metadata:()=>l,toc:()=>s});var o=t(7462),i=(t(7294),t(3905));const r={title:"Code of Conduct"},a="Contributor Covenant Code of Conduct",l={unversionedId:"code-of-conduct",id:"version-v0.3.x/code-of-conduct",title:"Code of Conduct",description:"Our Pledge",source:"@site/versioned_docs/version-v0.3.x/code-of-conduct.md",sourceDirName:".",slug:"/code-of-conduct",permalink:"/copacetic/website/v0.3.x/code-of-conduct",draft:!1,tags:[],version:"v0.3.x",frontMatter:{title:"Code of Conduct"},sidebar:"sidebar",previous:{title:"Contributing",permalink:"/copacetic/website/v0.3.x/contributing"}},c={},s=[{value:"Our Pledge",id:"our-pledge",level:2},{value:"Our Standards",id:"our-standards",level:2},{value:"Enforcement Responsibilities",id:"enforcement-responsibilities",level:2},{value:"Scope",id:"scope",level:2},{value:"Enforcement",id:"enforcement",level:2},{value:"Enforcement Guidelines",id:"enforcement-guidelines",level:2},{value:"1. Correction",id:"1-correction",level:3},{value:"2. Warning",id:"2-warning",level:3},{value:"3. Temporary Ban",id:"3-temporary-ban",level:3},{value:"4. Permanent Ban",id:"4-permanent-ban",level:3},{value:"Attribution",id:"attribution",level:2}],p={toc:s};function u(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,o.Z)({},p,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"contributor-covenant-code-of-conduct"},"Contributor Covenant Code of Conduct"),(0,i.kt)("h2",{id:"our-pledge"},"Our Pledge"),(0,i.kt)("p",null,"We as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation."),(0,i.kt)("p",null,"We pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community."),(0,i.kt)("h2",{id:"our-standards"},"Our Standards"),(0,i.kt)("p",null,"Examples of behavior that contributes to a positive environment for our\ncommunity include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Demonstrating empathy and kindness toward other people"),(0,i.kt)("li",{parentName:"ul"},"Being respectful of differing opinions, viewpoints, and experiences"),(0,i.kt)("li",{parentName:"ul"},"Giving and gracefully accepting constructive feedback"),(0,i.kt)("li",{parentName:"ul"},"Accepting responsibility and apologizing to those affected by our mistakes,\nand learning from the experience"),(0,i.kt)("li",{parentName:"ul"},"Focusing on what is best not just for us as individuals, but for the overall\ncommunity")),(0,i.kt)("p",null,"Examples of unacceptable behavior include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"The use of sexualized language or imagery, and sexual attention or advances of\nany kind"),(0,i.kt)("li",{parentName:"ul"},"Trolling, insulting or derogatory comments, and personal or political attacks"),(0,i.kt)("li",{parentName:"ul"},"Public or private harassment"),(0,i.kt)("li",{parentName:"ul"},"Publishing others' private information, such as a physical or email address,\nwithout their explicit permission"),(0,i.kt)("li",{parentName:"ul"},"Other conduct which could reasonably be considered inappropriate in a\nprofessional setting")),(0,i.kt)("h2",{id:"enforcement-responsibilities"},"Enforcement Responsibilities"),(0,i.kt)("p",null,"Community leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful."),(0,i.kt)("p",null,"Community leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate."),(0,i.kt)("h2",{id:"scope"},"Scope"),(0,i.kt)("p",null,"This Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event."),(0,i.kt)("h2",{id:"enforcement"},"Enforcement"),(0,i.kt)("p",null,"Instances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n",(0,i.kt)("inlineCode",{parentName:"p"},"project-copacetic@googlegroups.com"),".\nAll complaints will be reviewed and investigated promptly and fairly."),(0,i.kt)("p",null,"All community leaders are obligated to respect the privacy and security of the\nreporter of any incident."),(0,i.kt)("h2",{id:"enforcement-guidelines"},"Enforcement Guidelines"),(0,i.kt)("p",null,"Community leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:"),(0,i.kt)("h3",{id:"1-correction"},"1. Correction"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested."),(0,i.kt)("h3",{id:"2-warning"},"2. Warning"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": A violation through a single incident or series of\nactions."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban."),(0,i.kt)("h3",{id:"3-temporary-ban"},"3. Temporary Ban"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": A serious violation of community standards, including\nsustained inappropriate behavior."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban."),(0,i.kt)("h3",{id:"4-permanent-ban"},"4. Permanent Ban"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A permanent ban from any sort of public interaction within the\ncommunity."),(0,i.kt)("h2",{id:"attribution"},"Attribution"),(0,i.kt)("p",null,"This Code of Conduct is adapted from the ",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org"},"Contributor Covenant"),",\nversion 2.1, available at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"},"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"),"."),(0,i.kt)("p",null,"Community Impact Guidelines were inspired by\n",(0,i.kt)("a",{parentName:"p",href:"https://github.com/mozilla/diversity"},"Mozilla's code of conduct enforcement ladder"),"."),(0,i.kt)("p",null,"For answers to common questions about this code of conduct, see the FAQ at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/faq"},"https://www.contributor-covenant.org/faq"),". Translations are available at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/translations"},"https://www.contributor-covenant.org/translations"),"."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/934782ba.2d85275c.js b/website/assets/js/934782ba.2d85275c.js new file mode 100644 index 00000000..b8028f5e --- /dev/null +++ b/website/assets/js/934782ba.2d85275c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5713],{6602:(e,a,t)=>{t.r(a),t.d(a,{assets:()=>l,contentTitle:()=>c,default:()=>h,frontMatter:()=>o,metadata:()=>s,toc:()=>r});var i=t(4848),n=t(8453);const o={title:"FAQ"},c=void 0,s={id:"faq",title:"FAQ",description:"What kind of vulnerabilities can Copa patch?",source:"@site/versioned_docs/version-v0.2.x/faq.md",sourceDirName:".",slug:"/faq",permalink:"/copacetic/website/v0.2.x/faq",draft:!1,unlisted:!1,tags:[],version:"v0.2.x",frontMatter:{title:"FAQ"},sidebar:"sidebar",previous:{title:"Design",permalink:"/copacetic/website/v0.2.x/design"},next:{title:"Contributing",permalink:"/copacetic/website/v0.2.x/contributing"}},l={},r=[{value:"What kind of vulnerabilities can Copa patch?",id:"what-kind-of-vulnerabilities-can-copa-patch",level:2},{value:"What kind of vulnerabilities can Copa not patch?",id:"what-kind-of-vulnerabilities-can-copa-not-patch",level:2}];function p(e){const a={a:"a",code:"code",h2:"h2",p:"p",...(0,n.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(a.h2,{id:"what-kind-of-vulnerabilities-can-copa-patch",children:"What kind of vulnerabilities can Copa patch?"}),"\n",(0,i.jsxs)(a.p,{children:['Copa is capable of patching "OS level" vulnerabilities. This includes packages (like ',(0,i.jsx)(a.code,{children:"openssl"}),") in the image that are managed by a package manager such as ",(0,i.jsx)(a.code,{children:"apt"})," or ",(0,i.jsx)(a.code,{children:"yum"}),'. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see ',(0,i.jsx)(a.a,{href:"#what-kind-of-vulnerabilities-can-copa-not-patch",children:"below"})," for more details)."]}),"\n",(0,i.jsx)(a.h2,{id:"what-kind-of-vulnerabilities-can-copa-not-patch",children:"What kind of vulnerabilities can Copa not patch?"}),"\n",(0,i.jsxs)(a.p,{children:['Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the ',(0,i.jsx)(a.code,{children:"golang.org/x/net"})," module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level."]}),"\n",(0,i.jsxs)(a.p,{children:["To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like ",(0,i.jsx)(a.code,{children:"http://archive.ubuntu.com/ubuntu/"})," for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications."]})]})}function h(e={}){const{wrapper:a}={...(0,n.R)(),...e.components};return a?(0,i.jsx)(a,{...e,children:(0,i.jsx)(p,{...e})}):p(e)}},8453:(e,a,t)=>{t.d(a,{R:()=>c,x:()=>s});var i=t(6540);const n={},o=i.createContext(n);function c(e){const a=i.useContext(o);return i.useMemo((function(){return"function"==typeof e?e(a):{...a,...e}}),[a,e])}function s(e){let a;return a=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:c(e.components),i.createElement(o.Provider,{value:a},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/934782ba.c1eb8bb2.js b/website/assets/js/934782ba.c1eb8bb2.js deleted file mode 100644 index 020e4967..00000000 --- a/website/assets/js/934782ba.c1eb8bb2.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8163],{3905:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>d});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function c(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=a.createContext({}),p=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},s=function(e){var t=p(e.components);return a.createElement(l.Provider,{value:t},e.children)},u="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},h=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,l=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),u=p(n),h=i,d=u["".concat(l,".").concat(h)]||u[h]||f[h]||r;return n?a.createElement(d,o(o({ref:t},s),{},{components:n})):a.createElement(d,o({ref:t},s))}));function d(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=h;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[u]="string"==typeof e?e:i,o[1]=c;for(var p=2;p<r;p++)o[p]=n[p];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}h.displayName="MDXCreateElement"},315:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>c,toc:()=>p});var a=n(7462),i=(n(7294),n(3905));const r={title:"FAQ"},o=void 0,c={unversionedId:"faq",id:"version-v0.2.x/faq",title:"FAQ",description:"What kind of vulnerabilities can Copa patch?",source:"@site/versioned_docs/version-v0.2.x/faq.md",sourceDirName:".",slug:"/faq",permalink:"/copacetic/website/v0.2.x/faq",draft:!1,tags:[],version:"v0.2.x",frontMatter:{title:"FAQ"},sidebar:"sidebar",previous:{title:"Design",permalink:"/copacetic/website/v0.2.x/design"},next:{title:"Contributing",permalink:"/copacetic/website/v0.2.x/contributing"}},l={},p=[{value:"What kind of vulnerabilities can Copa patch?",id:"what-kind-of-vulnerabilities-can-copa-patch",level:2},{value:"What kind of vulnerabilities can Copa not patch?",id:"what-kind-of-vulnerabilities-can-copa-not-patch",level:2}],s={toc:p};function u(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h2",{id:"what-kind-of-vulnerabilities-can-copa-patch"},"What kind of vulnerabilities can Copa patch?"),(0,i.kt)("p",null,'Copa is capable of patching "OS level" vulnerabilities. This includes packages (like ',(0,i.kt)("inlineCode",{parentName:"p"},"openssl"),") in the image that are managed by a package manager such as ",(0,i.kt)("inlineCode",{parentName:"p"},"apt")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"yum"),'. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see ',(0,i.kt)("a",{parentName:"p",href:"#what-kind-of-vulnerabilities-can-copa-not-patch"},"below")," for more details)."),(0,i.kt)("h2",{id:"what-kind-of-vulnerabilities-can-copa-not-patch"},"What kind of vulnerabilities can Copa not patch?"),(0,i.kt)("p",null,'Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the ',(0,i.kt)("inlineCode",{parentName:"p"},"golang.org/x/net")," module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level."),(0,i.kt)("p",null,"To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like ",(0,i.kt)("inlineCode",{parentName:"p"},"http://archive.ubuntu.com/ubuntu/")," for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/935f2afb.a5a28739.js b/website/assets/js/935f2afb.a5a28739.js new file mode 100644 index 00000000..817cb061 --- /dev/null +++ b/website/assets/js/935f2afb.a5a28739.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8581],{5610:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"current","label":"Next","banner":"unreleased","badge":true,"noIndex":false,"className":"docs-version-current","isLast":false,"docsSidebars":{"sidebar":[{"type":"category","label":"Getting Started","collapsed":false,"items":[{"type":"link","label":"Introduction","href":"/copacetic/website/next/","docId":"introduction","unlisted":false},{"type":"link","label":"Installation","href":"/copacetic/website/next/installation","docId":"installation","unlisted":false},{"type":"link","label":"Quick Start","href":"/copacetic/website/next/quick-start","docId":"quick-start","unlisted":false},{"type":"link","label":"Tagging Guidelines","href":"/copacetic/website/next/best-practices","docId":"best-practices","unlisted":false},{"type":"link","label":"Troubleshooting","href":"/copacetic/website/next/troubleshooting","docId":"troubleshooting","unlisted":false},{"type":"link","label":"FAQ","href":"/copacetic/website/next/faq","docId":"faq","unlisted":false}],"collapsible":true},{"type":"category","label":"Features","collapsed":false,"items":[{"type":"link","label":"Github Action","href":"/copacetic/website/next/github-action","docId":"github-action","unlisted":false},{"type":"link","label":"Custom buildkit addresses","href":"/copacetic/website/next/custom-address","docId":"custom-address","unlisted":false},{"type":"link","label":"Output","href":"/copacetic/website/next/output","docId":"output","unlisted":false},{"type":"link","label":"Scanner Plugins","href":"/copacetic/website/next/scanner-plugins","docId":"scanner-plugins","unlisted":false}],"collapsible":true},{"type":"category","label":"Contributing","collapsed":false,"items":[{"type":"link","label":"Contributing","href":"/copacetic/website/next/contributing","docId":"contributing","unlisted":false},{"type":"link","label":"Code of Conduct","href":"/copacetic/website/next/code-of-conduct","docId":"code-of-conduct","unlisted":false},{"type":"link","label":"Design","href":"/copacetic/website/next/design","docId":"design","unlisted":false},{"type":"link","label":"Development and Testing Tips","href":"/copacetic/website/next/development-tips","docId":"development-tips","unlisted":false},{"type":"link","label":"Maintainer Guidelines","href":"/copacetic/website/next/maintainer-guidelines","docId":"maintainer-guidelines","unlisted":false},{"type":"link","label":"Release Process","href":"/copacetic/website/next/release","docId":"release","unlisted":false}],"collapsible":true}]},"docs":{"best-practices":{"id":"best-practices","title":"Tagging Guidelines","description":"There are some patterns and practices you may want to consider when using Copa to patch images. Remember that these are suggestions that may not fit into your workflow, but we think that staying as close as possible to these practices offers the best experience with Copa.","sidebar":"sidebar"},"code-of-conduct":{"id":"code-of-conduct","title":"Code of Conduct","description":"Our Pledge","sidebar":"sidebar"},"contributing":{"id":"contributing","title":"Contributing","description":"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.","sidebar":"sidebar"},"custom-address":{"id":"custom-address","title":"Custom buildkit addresses","description":"You may need to specify a custom address using the --addr flag. Here are the supported formats:","sidebar":"sidebar"},"design":{"id":"design","title":"Design","description":"Design Tenets","sidebar":"sidebar"},"development-tips":{"id":"development-tips","title":"Development and Testing Tips","description":"This document provides some tips and tricks for devs to better understand what is happening under the hood of copa.","sidebar":"sidebar"},"faq":{"id":"faq","title":"FAQ","description":"What kind of vulnerabilities can Copa patch?","sidebar":"sidebar"},"github-action":{"id":"github-action","title":"Github Action","description":"The Copa Github Action allows you patch vulnerable containers in your GitHub Actions workflows using Copa.","sidebar":"sidebar"},"installation":{"id":"installation","title":"Installation","description":"Homebrew","sidebar":"sidebar"},"introduction":{"id":"introduction","title":"Introduction","description":"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.","sidebar":"sidebar"},"maintainer-guidelines":{"id":"maintainer-guidelines","title":"Maintainer Guidelines","description":"Semantic Release Management","sidebar":"sidebar"},"output":{"id":"output","title":"Output","description":"Experimental: This feature might change without preserving backwards compatibility.","sidebar":"sidebar"},"quick-start":{"id":"quick-start","title":"Quick Start","description":"This sample illustrates how to patch containers using vulnerability reports with copa.","sidebar":"sidebar"},"release":{"id":"release","title":"Release Process","description":"Overview","sidebar":"sidebar"},"scanner-plugins":{"id":"scanner-plugins","title":"Scanner Plugins","description":"By default, copa uses Trivy to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners.","sidebar":"sidebar"},"troubleshooting":{"id":"troubleshooting","title":"Troubleshooting","description":"Filtering Vulnerabilities","sidebar":"sidebar"}}}')}}]); \ No newline at end of file diff --git a/website/assets/js/935f2afb.c1a00716.js b/website/assets/js/935f2afb.c1a00716.js deleted file mode 100644 index 736a9bdd..00000000 --- a/website/assets/js/935f2afb.c1a00716.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[53],{1109:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"current","label":"Next","banner":"unreleased","badge":true,"noIndex":false,"className":"docs-version-current","isLast":false,"docsSidebars":{"sidebar":[{"type":"category","label":"Getting Started","collapsed":false,"items":[{"type":"link","label":"Introduction","href":"/copacetic/website/next/","docId":"introduction"},{"type":"link","label":"Installation","href":"/copacetic/website/next/installation","docId":"installation"},{"type":"link","label":"Quick Start","href":"/copacetic/website/next/quick-start","docId":"quick-start"},{"type":"link","label":"Tagging Guidelines","href":"/copacetic/website/next/best-practices","docId":"best-practices"},{"type":"link","label":"Troubleshooting","href":"/copacetic/website/next/troubleshooting","docId":"troubleshooting"},{"type":"link","label":"FAQ","href":"/copacetic/website/next/faq","docId":"faq"}],"collapsible":true},{"type":"category","label":"Features","collapsed":false,"items":[{"type":"link","label":"Github Action","href":"/copacetic/website/next/github-action","docId":"github-action"},{"type":"link","label":"Custom buildkit addresses","href":"/copacetic/website/next/custom-address","docId":"custom-address"},{"type":"link","label":"Output","href":"/copacetic/website/next/output","docId":"output"},{"type":"link","label":"Scanner Plugins","href":"/copacetic/website/next/scanner-plugins","docId":"scanner-plugins"}],"collapsible":true},{"type":"category","label":"Contributing","collapsed":false,"items":[{"type":"link","label":"Contributing","href":"/copacetic/website/next/contributing","docId":"contributing"},{"type":"link","label":"Code of Conduct","href":"/copacetic/website/next/code-of-conduct","docId":"code-of-conduct"},{"type":"link","label":"Design","href":"/copacetic/website/next/design","docId":"design"},{"type":"link","label":"Development and Testing Tips","href":"/copacetic/website/next/development-tips","docId":"development-tips"},{"type":"link","label":"Maintainer Guidelines","href":"/copacetic/website/next/maintainer-guidelines","docId":"maintainer-guidelines"},{"type":"link","label":"Release Process","href":"/copacetic/website/next/release","docId":"release"}],"collapsible":true}]},"docs":{"best-practices":{"id":"best-practices","title":"Tagging Guidelines","description":"There are some patterns and practices you may want to consider when using Copa to patch images. Remember that these are suggestions that may not fit into your workflow, but we think that staying as close as possible to these practices offers the best experience with Copa.","sidebar":"sidebar"},"code-of-conduct":{"id":"code-of-conduct","title":"Code of Conduct","description":"Our Pledge","sidebar":"sidebar"},"contributing":{"id":"contributing","title":"Contributing","description":"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.","sidebar":"sidebar"},"custom-address":{"id":"custom-address","title":"Custom buildkit addresses","description":"You may need to specify a custom address using the --addr flag. Here are the supported formats:","sidebar":"sidebar"},"design":{"id":"design","title":"Design","description":"Design Tenets","sidebar":"sidebar"},"development-tips":{"id":"development-tips","title":"Development and Testing Tips","description":"This document provides some tips and tricks for devs to better understand what is happening under the hood of copa.","sidebar":"sidebar"},"faq":{"id":"faq","title":"FAQ","description":"What kind of vulnerabilities can Copa patch?","sidebar":"sidebar"},"github-action":{"id":"github-action","title":"Github Action","description":"The Copa Github Action allows you patch vulnerable containers in your GitHub Actions workflows using Copa.","sidebar":"sidebar"},"installation":{"id":"installation","title":"Installation","description":"Homebrew","sidebar":"sidebar"},"introduction":{"id":"introduction","title":"Introduction","description":"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.","sidebar":"sidebar"},"maintainer-guidelines":{"id":"maintainer-guidelines","title":"Maintainer Guidelines","description":"Semantic Release Management","sidebar":"sidebar"},"output":{"id":"output","title":"Output","description":"Experimental: This feature might change without preserving backwards compatibility.","sidebar":"sidebar"},"quick-start":{"id":"quick-start","title":"Quick Start","description":"This sample illustrates how to patch containers using vulnerability reports with copa.","sidebar":"sidebar"},"release":{"id":"release","title":"Release Process","description":"Overview","sidebar":"sidebar"},"scanner-plugins":{"id":"scanner-plugins","title":"Scanner Plugins","description":"By default, copa uses Trivy to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners.","sidebar":"sidebar"},"troubleshooting":{"id":"troubleshooting","title":"Troubleshooting","description":"Filtering Vulnerabilities","sidebar":"sidebar"}}}')}}]); \ No newline at end of file diff --git a/website/assets/js/98016c8b.9efc2921.js b/website/assets/js/98016c8b.9efc2921.js new file mode 100644 index 00000000..7e63372f --- /dev/null +++ b/website/assets/js/98016c8b.9efc2921.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[90],{7847:(e,t,i)=>{i.r(t),i.d(t,{assets:()=>a,contentTitle:()=>r,default:()=>h,frontMatter:()=>s,metadata:()=>o,toc:()=>l});var c=i(4848),n=i(8453);const s={title:"Release Process"},r=void 0,o={id:"release",title:"Release Process",description:"Overview",source:"@site/versioned_docs/version-v0.6.x/release.md",sourceDirName:".",slug:"/release",permalink:"/copacetic/website/release",draft:!1,unlisted:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Release Process"},sidebar:"sidebar",previous:{title:"Maintainer Guidelines",permalink:"/copacetic/website/maintainer-guidelines"}},a={},l=[{value:"Overview",id:"overview",level:2},{value:"Publishing",id:"publishing",level:2}];function p(e){const t={a:"a",code:"code",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",...(0,n.R)(),...e.components};return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(t.h2,{id:"overview",children:"Overview"}),"\n",(0,c.jsxs)(t.p,{children:["The release process for Copacetic uses ",(0,c.jsx)(t.a,{href:"https://goreleaser.com/",children:"GoReleaser"}),"."]}),"\n",(0,c.jsx)(t.p,{children:"Once you are ready to cut a new release, checkout the release branch and tag it with the respective version."}),"\n",(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{children:"git checkout <BRANCH NAME>\ngit pull origin <BRANCH NAME>\ngit tag -a <NEW VERSION> -m '<NEW VERSION>'\ngit push origin <NEW VERSION>\n"})}),"\n",(0,c.jsx)(t.h2,{id:"publishing",children:"Publishing"}),"\n",(0,c.jsxs)(t.ol,{children:["\n",(0,c.jsxs)(t.li,{children:["GoReleaser will create a new release, review and edit it at ",(0,c.jsx)(t.a,{href:"https://github.com/project-copacetic/copacetic/releases",children:"https://github.com/project-copacetic/copacetic/releases"})]}),"\n",(0,c.jsxs)(t.li,{children:["Review the respective copa-action image at: ",(0,c.jsx)(t.a,{href:"https://github.com/orgs/project-copacetic/packages/container/package/copa-action",children:"https://github.com/orgs/project-copacetic/packages/container/package/copa-action"})]}),"\n"]})]})}function h(e={}){const{wrapper:t}={...(0,n.R)(),...e.components};return t?(0,c.jsx)(t,{...e,children:(0,c.jsx)(p,{...e})}):p(e)}},8453:(e,t,i)=>{i.d(t,{R:()=>r,x:()=>o});var c=i(6540);const n={},s=c.createContext(n);function r(e){const t=c.useContext(s);return c.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function o(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:r(e.components),c.createElement(s.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/98016c8b.a456db16.js b/website/assets/js/98016c8b.a456db16.js deleted file mode 100644 index df85c400..00000000 --- a/website/assets/js/98016c8b.a456db16.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1031],{3905:(e,t,r)=>{r.d(t,{Zo:()=>p,kt:()=>m});var n=r(7294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?i(Object(r),!0).forEach((function(t){a(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):i(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function c(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r,n,a={},i=Object.keys(e);for(n=0;n<i.length;n++)r=i[n],t.indexOf(r)>=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n<i.length;n++)r=i[n],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var l=n.createContext({}),s=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},p=function(e){var t=s(e.components);return n.createElement(l.Provider,{value:t},e.children)},u="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},v=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,i=e.originalType,l=e.parentName,p=c(e,["components","mdxType","originalType","parentName"]),u=s(r),v=a,m=u["".concat(l,".").concat(v)]||u[v]||f[v]||i;return r?n.createElement(m,o(o({ref:t},p),{},{components:r})):n.createElement(m,o({ref:t},p))}));function m(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=r.length,o=new Array(i);o[0]=v;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[u]="string"==typeof e?e:a,o[1]=c;for(var s=2;s<i;s++)o[s]=r[s];return n.createElement.apply(null,o)}return n.createElement.apply(null,r)}v.displayName="MDXCreateElement"},5924:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>i,metadata:()=>c,toc:()=>s});var n=r(7462),a=(r(7294),r(3905));const i={title:"Release Process"},o=void 0,c={unversionedId:"release",id:"version-v0.6.x/release",title:"Release Process",description:"Overview",source:"@site/versioned_docs/version-v0.6.x/release.md",sourceDirName:".",slug:"/release",permalink:"/copacetic/website/release",draft:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Release Process"},sidebar:"sidebar",previous:{title:"Maintainer Guidelines",permalink:"/copacetic/website/maintainer-guidelines"}},l={},s=[{value:"Overview",id:"overview",level:2},{value:"Publishing",id:"publishing",level:2}],p={toc:s};function u(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h2",{id:"overview"},"Overview"),(0,a.kt)("p",null,"The release process for Copacetic uses ",(0,a.kt)("a",{parentName:"p",href:"https://goreleaser.com/"},"GoReleaser"),". "),(0,a.kt)("p",null,"Once you are ready to cut a new release, checkout the release branch and tag it with the respective version."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"```\ngit checkout <BRANCH NAME>\ngit pull origin <BRANCH NAME>\ngit tag -a <NEW VERSION> -m '<NEW VERSION>'\ngit push origin <NEW VERSION>\n```\n")),(0,a.kt)("h2",{id:"publishing"},"Publishing"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"GoReleaser will create a new release, review and edit it at ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/project-copacetic/copacetic/releases"},"https://github.com/project-copacetic/copacetic/releases")),(0,a.kt)("li",{parentName:"ol"},"Review the respective copa-action image at: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/orgs/project-copacetic/packages/container/package/copa-action"},"https://github.com/orgs/project-copacetic/packages/container/package/copa-action"))))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/9b66f67b.6e821884.js b/website/assets/js/9b66f67b.6e821884.js new file mode 100644 index 00000000..8b9375c8 --- /dev/null +++ b/website/assets/js/9b66f67b.6e821884.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1837],{9530:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>s,contentTitle:()=>d,default:()=>u,frontMatter:()=>c,metadata:()=>r,toc:()=>l});var t=i(4848),o=i(8453);const c={title:"Custom buildkit addresses"},d=void 0,r={id:"custom-address",title:"Custom buildkit addresses",description:"If you need to specify a custom address using the --addr flag. Here are the supported formats:",source:"@site/versioned_docs/version-v0.6.x/custom-address.md",sourceDirName:".",slug:"/custom-address",permalink:"/copacetic/website/custom-address",draft:!1,unlisted:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Custom buildkit addresses"},sidebar:"sidebar",previous:{title:"Github Action",permalink:"/copacetic/website/github-action"},next:{title:"Output",permalink:"/copacetic/website/output"}},s={},l=[{value:"Buildkit Connection Examples",id:"buildkit-connection-examples",level:2},{value:"Option 1: Connect using defaults",id:"option-1-connect-using-defaults",level:3},{value:"Option 2: Connect to buildx",id:"option-2-connect-to-buildx",level:3},{value:"Option 3: Buildkit in a container",id:"option-3-buildkit-in-a-container",level:3},{value:"Option 4: Buildkit over TCP",id:"option-4-buildkit-over-tcp",level:3}];function a(e){const n={code:"code",em:"em",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",ul:"ul",...(0,o.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(n.p,{children:["If you need to specify a custom address using the ",(0,t.jsx)(n.code,{children:"--addr"})," flag. Here are the supported formats:"]}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"unix:///path/to/buildkit.sock"})," - Connect to buildkit over unix socket."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"tcp://$BUILDKIT_ADDR:$PORT"})," - Connect to buildkit over TCP. (not recommended for security reasons)"]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"docker://<docker connection spec>"})," - Connect to docker, currently only unix sockets are supported, e.g. ",(0,t.jsx)(n.code,{children:"docker://unix:///var/run/docker.sock"})," (or just ",(0,t.jsx)(n.code,{children:"docker://"}),")."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"docker-container://my-buildkit-container"})," - Connect to a buildkitd running in a docker container."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"buildx://my-builder"})," - Connect to a buildx builder (or ",(0,t.jsx)(n.code,{children:"buildx://"})," for the currently selected builder). ",(0,t.jsx)(n.em,{children:"Note: only container-backed buildx instances are currently supported"})]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"nerdctl-container://my-container-name"})," - Similar to ",(0,t.jsx)(n.code,{children:"docker-container"})," but uses ",(0,t.jsx)(n.code,{children:"nerdctl"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"podman-container://my-container-name"})," - Similar to ",(0,t.jsx)(n.code,{children:"docker-container"})," but uses ",(0,t.jsx)(n.code,{children:"podman"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"ssh://myhost"})," - Connect to a buildkit instance over SSH. Format of the host spec should mimic the SSH command."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"kubepod://mypod"})," - Connect to buildkit running in a Kubernetes pod. Can also specify kubectl context and pod namespace (",(0,t.jsx)(n.code,{children:"kubepod://mypod?context=foo&namespace=notdefault"}),")."]}),"\n"]}),"\n",(0,t.jsx)(n.h2,{id:"buildkit-connection-examples",children:"Buildkit Connection Examples"}),"\n",(0,t.jsx)(n.h3,{id:"option-1-connect-using-defaults",children:"Option 1: Connect using defaults"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched\n"})}),"\n",(0,t.jsx)(n.h3,{id:"option-2-connect-to-buildx",children:"Option 2: Connect to buildx"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"docker buildx create --name demo\ncopa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr buildx://demo\n"})}),"\n",(0,t.jsx)(n.h3,{id:"option-3-buildkit-in-a-container",children:"Option 3: Buildkit in a container"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'export BUILDKIT_VERSION=v0.12.4\ndocker run \\\n --detach \\\n --rm \\\n --privileged \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION"\n\ncopa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr docker-container://buildkitd\n'})}),"\n",(0,t.jsx)(n.h3,{id:"option-4-buildkit-over-tcp",children:"Option 4: Buildkit over TCP"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'export BUILDKIT_VERSION=v0.12.4\nexport BUILDKIT_PORT=8888\ndocker run \\\n --detach \\\n --rm \\\n --privileged \\\n -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION" \\\n --addr tcp://0.0.0.0:$BUILDKIT_PORT\n\ncopa patch \\\n -i docker.io/library/nginx:1.21.6 \\\n -r nginx.1.21.6.json \\\n -t 1.21.6-patched \\\n -a tcp://0.0.0.0:$BUILDKIT_PORT\n'})})]})}function u(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(a,{...e})}):a(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>d,x:()=>r});var t=i(6540);const o={},c=t.createContext(o);function d(e){const n=t.useContext(c);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function r(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:d(e.components),t.createElement(c.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/9b66f67b.7d9c27c7.js b/website/assets/js/9b66f67b.7d9c27c7.js deleted file mode 100644 index 36a2e95b..00000000 --- a/website/assets/js/9b66f67b.7d9c27c7.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6022],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>k});var i=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function a(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){r(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,i,r=function(e,t){if(null==e)return{};var n,i,r={},o=Object.keys(e);for(i=0;i<o.length;i++)n=o[i],t.indexOf(n)>=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i<o.length;i++)n=o[i],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var c=i.createContext({}),d=function(e){var t=i.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},p=function(e){var t=d(e.components);return i.createElement(c.Provider,{value:t},e.children)},u="mdxType",s={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},m=i.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,c=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=d(n),m=r,k=u["".concat(c,".").concat(m)]||u[m]||s[m]||o;return n?i.createElement(k,a(a({ref:t},p),{},{components:n})):i.createElement(k,a({ref:t},p))}));function k(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,a=new Array(o);a[0]=m;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[u]="string"==typeof e?e:r,a[1]=l;for(var d=2;d<o;d++)a[d]=n[d];return i.createElement.apply(null,a)}return i.createElement.apply(null,n)}m.displayName="MDXCreateElement"},8810:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>o,metadata:()=>l,toc:()=>d});var i=n(7462),r=(n(7294),n(3905));const o={title:"Custom buildkit addresses"},a=void 0,l={unversionedId:"custom-address",id:"version-v0.6.x/custom-address",title:"Custom buildkit addresses",description:"If you need to specify a custom address using the --addr flag. Here are the supported formats:",source:"@site/versioned_docs/version-v0.6.x/custom-address.md",sourceDirName:".",slug:"/custom-address",permalink:"/copacetic/website/custom-address",draft:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Custom buildkit addresses"},sidebar:"sidebar",previous:{title:"Github Action",permalink:"/copacetic/website/github-action"},next:{title:"Output",permalink:"/copacetic/website/output"}},c={},d=[{value:"Buildkit Connection Examples",id:"buildkit-connection-examples",level:2},{value:"Option 1: Connect using defaults",id:"option-1-connect-using-defaults",level:3},{value:"Option 2: Connect to buildx",id:"option-2-connect-to-buildx",level:3},{value:"Option 3: Buildkit in a container",id:"option-3-buildkit-in-a-container",level:3},{value:"Option 4: Buildkit over TCP",id:"option-4-buildkit-over-tcp",level:3}],p={toc:d};function u(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,i.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"If you need to specify a custom address using the ",(0,r.kt)("inlineCode",{parentName:"p"},"--addr")," flag. Here are the supported formats:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"unix:///path/to/buildkit.sock")," - Connect to buildkit over unix socket."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"tcp://$BUILDKIT_ADDR:$PORT")," - Connect to buildkit over TCP. (not recommended for security reasons)"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"docker://<docker connection spec>")," - Connect to docker, currently only unix sockets are supported, e.g. ",(0,r.kt)("inlineCode",{parentName:"li"},"docker://unix:///var/run/docker.sock")," (or just ",(0,r.kt)("inlineCode",{parentName:"li"},"docker://"),")."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"docker-container://my-buildkit-container")," - Connect to a buildkitd running in a docker container."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"buildx://my-builder")," - Connect to a buildx builder (or ",(0,r.kt)("inlineCode",{parentName:"li"},"buildx://")," for the currently selected builder). ",(0,r.kt)("em",{parentName:"li"},"Note: only container-backed buildx instances are currently supported")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"nerdctl-container://my-container-name")," - Similar to ",(0,r.kt)("inlineCode",{parentName:"li"},"docker-container")," but uses ",(0,r.kt)("inlineCode",{parentName:"li"},"nerdctl"),"."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"podman-container://my-container-name")," - Similar to ",(0,r.kt)("inlineCode",{parentName:"li"},"docker-container")," but uses ",(0,r.kt)("inlineCode",{parentName:"li"},"podman"),"."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"ssh://myhost")," - Connect to a buildkit instance over SSH. Format of the host spec should mimic the SSH command."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"kubepod://mypod")," - Connect to buildkit running in a Kubernetes pod. Can also specify kubectl context and pod namespace (",(0,r.kt)("inlineCode",{parentName:"li"},"kubepod://mypod?context=foo&namespace=notdefault"),").")),(0,r.kt)("h2",{id:"buildkit-connection-examples"},"Buildkit Connection Examples"),(0,r.kt)("h3",{id:"option-1-connect-using-defaults"},"Option 1: Connect using defaults"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched\n")),(0,r.kt)("h3",{id:"option-2-connect-to-buildx"},"Option 2: Connect to buildx"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"docker buildx create --name demo\ncopa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr buildx://demo\n")),(0,r.kt)("h3",{id:"option-3-buildkit-in-a-container"},"Option 3: Buildkit in a container"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'export BUILDKIT_VERSION=v0.12.4\ndocker run \\\n --detach \\\n --rm \\\n --privileged \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION"\n\ncopa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr docker-container://buildkitd\n')),(0,r.kt)("h3",{id:"option-4-buildkit-over-tcp"},"Option 4: Buildkit over TCP"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'export BUILDKIT_VERSION=v0.12.4\nexport BUILDKIT_PORT=8888\ndocker run \\\n --detach \\\n --rm \\\n --privileged \\\n -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION" \\\n --addr tcp://0.0.0.0:$BUILDKIT_PORT\n\ncopa patch \\\n -i docker.io/library/nginx:1.21.6 \\\n -r nginx.1.21.6.json \\\n -t 1.21.6-patched \\\n -a tcp://0.0.0.0:$BUILDKIT_PORT\n')))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/9d9f8394.4bdf9bac.js b/website/assets/js/9d9f8394.4bdf9bac.js deleted file mode 100644 index d71c102a..00000000 --- a/website/assets/js/9d9f8394.4bdf9bac.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9360],{3905:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>b});var r=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function a(e,t){if(null==e)return{};var n,r,i=function(e,t){if(null==e)return{};var n,r,i={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var c=r.createContext({}),u=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},s=function(e){var t=u(e.components);return r.createElement(c.Provider,{value:t},e.children)},p="mdxType",g={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,c=e.parentName,s=a(e,["components","mdxType","originalType","parentName"]),p=u(n),f=i,b=p["".concat(c,".").concat(f)]||p[f]||g[f]||o;return n?r.createElement(b,l(l({ref:t},s),{},{components:n})):r.createElement(b,l({ref:t},s))}));function b(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,l=new Array(o);l[0]=f;var a={};for(var c in t)hasOwnProperty.call(t,c)&&(a[c]=t[c]);a.originalType=e,a[p]="string"==typeof e?e:i,l[1]=a;for(var u=2;u<o;u++)l[u]=n[u];return r.createElement.apply(null,l)}return r.createElement.apply(null,n)}f.displayName="MDXCreateElement"},9222:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>l,default:()=>p,frontMatter:()=>o,metadata:()=>a,toc:()=>u});var r=n(7462),i=(n(7294),n(3905));const o={title:"Troubleshooting"},l=void 0,a={unversionedId:"troubleshooting",id:"troubleshooting",title:"Troubleshooting",description:"Filtering Vulnerabilities",source:"@site/docs/troubleshooting.md",sourceDirName:".",slug:"/troubleshooting",permalink:"/copacetic/website/next/troubleshooting",draft:!1,tags:[],version:"current",frontMatter:{title:"Troubleshooting"},sidebar:"sidebar",previous:{title:"Tagging Guidelines",permalink:"/copacetic/website/next/best-practices"},next:{title:"FAQ",permalink:"/copacetic/website/next/faq"}},c={},u=[{value:"Filtering Vulnerabilities",id:"filtering-vulnerabilities",level:2},{value:"Rego Policy",id:"rego-policy",level:3},{value:"Ignore File",id:"ignore-file",level:3}],s={toc:u};function p(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,r.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h2",{id:"filtering-vulnerabilities"},"Filtering Vulnerabilities"),(0,i.kt)("p",null,"You might want to filter/ignore some of the vulnerabilities while patching. To do so, you need to first filter those undesired vulnerabilities from your scanner output."),(0,i.kt)("p",null,"For Trivy, vulnerabilities can be filtered by the following 2 ways:"),(0,i.kt)("h3",{id:"rego-policy"},"Rego Policy"),(0,i.kt)("p",null,"An example rego file which demonstrates how to ignore certain Vulnerability IDs or Package Names:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},'$ cat trivy_ignore.rego\n\npackage trivy\n\nimport data.lib.trivy\n\ndefault ignore = false\n\n\n# Ignore the following Vulnerability IDs\nignore_vulnerability_ids := {\n "CVE-2018-14618"\n}\n# Ignore the following Package Names\nignore_pkgs := {"bash", "vim"}\n\n\n# For ignoring vulnID\nignore {\n input.VulnerabilityID == ignore_vulnerability_ids[_]\n}\n# For ignoring pkgName\nignore {\n input.PkgName == ignore_pkgs[_]\n}\n\n')),(0,i.kt)("p",null,"After adding the above rego file, run the image scan with the ",(0,i.kt)("inlineCode",{parentName:"p"},"--ignore-policy")," flag followed by the file name to ignore them while scanning:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --ignore-policy trivy_ignore.rego ruby:2.4.0\n")),(0,i.kt)("p",null,'In the above example, the vulnerability "CVE-2018-14618" and the packages "bash" & "vim" are ignored while scanning, and hence patching the image.'),(0,i.kt)("h3",{id:"ignore-file"},"Ignore File"),(0,i.kt)("p",null,"Use a ",(0,i.kt)("inlineCode",{parentName:"p"},".trivyignore")," file to list all the vulnerabilities you want to ignore."),(0,i.kt)("p",null,"Example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"$ cat .trivyignore\n\n# Accept the risk\nCVE-2018-14618\n")),(0,i.kt)("p",null,"In the above example, the vulnerability CVE-2018-14618 is ignored while scanning, and hence while patching the image."),(0,i.kt)("p",null,"For a more detailed explanation on how to ignore certain vulnerabilities with Trivy, please refer to the official documentation ",(0,i.kt)("a",{parentName:"p",href:"https://aquasecurity.github.io/trivy/v0.44/docs/configuration/filtering/"},"here"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/9d9f8394.6a5641e3.js b/website/assets/js/9d9f8394.6a5641e3.js new file mode 100644 index 00000000..7791e9f3 --- /dev/null +++ b/website/assets/js/9d9f8394.6a5641e3.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9013],{5520:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>s,contentTitle:()=>l,default:()=>h,frontMatter:()=>o,metadata:()=>a,toc:()=>c});var t=i(4848),r=i(8453);const o={title:"Troubleshooting"},l=void 0,a={id:"troubleshooting",title:"Troubleshooting",description:"Filtering Vulnerabilities",source:"@site/docs/troubleshooting.md",sourceDirName:".",slug:"/troubleshooting",permalink:"/copacetic/website/next/troubleshooting",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{title:"Troubleshooting"},sidebar:"sidebar",previous:{title:"Tagging Guidelines",permalink:"/copacetic/website/next/best-practices"},next:{title:"FAQ",permalink:"/copacetic/website/next/faq"}},s={},c=[{value:"Filtering Vulnerabilities",id:"filtering-vulnerabilities",level:2},{value:"Rego Policy",id:"rego-policy",level:3},{value:"Ignore File",id:"ignore-file",level:3}];function g(e){const n={a:"a",code:"code",h2:"h2",h3:"h3",p:"p",pre:"pre",...(0,r.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h2,{id:"filtering-vulnerabilities",children:"Filtering Vulnerabilities"}),"\n",(0,t.jsx)(n.p,{children:"You might want to filter/ignore some of the vulnerabilities while patching. To do so, you need to first filter those undesired vulnerabilities from your scanner output."}),"\n",(0,t.jsx)(n.p,{children:"For Trivy, vulnerabilities can be filtered by the following 2 ways:"}),"\n",(0,t.jsx)(n.h3,{id:"rego-policy",children:"Rego Policy"}),"\n",(0,t.jsx)(n.p,{children:"An example rego file which demonstrates how to ignore certain Vulnerability IDs or Package Names:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'$ cat trivy_ignore.rego\n\npackage trivy\n\nimport data.lib.trivy\n\ndefault ignore = false\n\n\n# Ignore the following Vulnerability IDs\nignore_vulnerability_ids := {\n "CVE-2018-14618"\n}\n# Ignore the following Package Names\nignore_pkgs := {"bash", "vim"}\n\n\n# For ignoring vulnID\nignore {\n input.VulnerabilityID == ignore_vulnerability_ids[_]\n}\n# For ignoring pkgName\nignore {\n\tinput.PkgName == ignore_pkgs[_]\n}\n\n'})}),"\n",(0,t.jsxs)(n.p,{children:["After adding the above rego file, run the image scan with the ",(0,t.jsx)(n.code,{children:"--ignore-policy"})," flag followed by the file name to ignore them while scanning:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --ignore-policy trivy_ignore.rego ruby:2.4.0\n"})}),"\n",(0,t.jsx)(n.p,{children:'In the above example, the vulnerability "CVE-2018-14618" and the packages "bash" & "vim" are ignored while scanning, and hence patching the image.'}),"\n",(0,t.jsx)(n.h3,{id:"ignore-file",children:"Ignore File"}),"\n",(0,t.jsxs)(n.p,{children:["Use a ",(0,t.jsx)(n.code,{children:".trivyignore"})," file to list all the vulnerabilities you want to ignore."]}),"\n",(0,t.jsx)(n.p,{children:"Example:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"$ cat .trivyignore\n\n# Accept the risk\nCVE-2018-14618\n"})}),"\n",(0,t.jsx)(n.p,{children:"In the above example, the vulnerability CVE-2018-14618 is ignored while scanning, and hence while patching the image."}),"\n",(0,t.jsxs)(n.p,{children:["For a more detailed explanation on how to ignore certain vulnerabilities with Trivy, please refer to the official documentation ",(0,t.jsx)(n.a,{href:"https://aquasecurity.github.io/trivy/v0.44/docs/configuration/filtering/",children:"here"}),"."]})]})}function h(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(g,{...e})}):g(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>l,x:()=>a});var t=i(6540);const r={},o=t.createContext(r);function l(e){const n=t.useContext(o);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:l(e.components),t.createElement(o.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/9e350ec0.ad02c27c.js b/website/assets/js/9e350ec0.ad02c27c.js new file mode 100644 index 00000000..0b48dc5f --- /dev/null +++ b/website/assets/js/9e350ec0.ad02c27c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3817],{3014:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>d,contentTitle:()=>c,default:()=>h,frontMatter:()=>o,metadata:()=>r,toc:()=>l});var t=i(4848),s=i(8453);const o={title:"Quick Start"},c=void 0,r={id:"quick-start",title:"Quick Start",description:"This sample illustrates how to patch containers using vulnerability reports with copa.",source:"@site/versioned_docs/version-v0.4.x/quick-start.md",sourceDirName:".",slug:"/quick-start",permalink:"/copacetic/website/v0.4.x/quick-start",draft:!1,unlisted:!1,tags:[],version:"v0.4.x",frontMatter:{title:"Quick Start"},sidebar:"sidebar",previous:{title:"Installation",permalink:"/copacetic/website/v0.4.x/installation"},next:{title:"Troubleshooting",permalink:"/copacetic/website/v0.4.x/troubleshooting"}},d={},l=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Sample Steps",id:"sample-steps",level:2},{value:"Buildkit Connection Examples",id:"buildkit-connection-examples",level:4}];function a(e){const n={a:"a",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h4:"h4",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(n.p,{children:["This sample illustrates how to patch containers using vulnerability reports with ",(0,t.jsx)(n.code,{children:"copa"}),"."]}),"\n",(0,t.jsx)(n.h2,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["An Ubuntu 22.04 VM configured through the ",(0,t.jsx)(n.a,{href:"/copacetic/website/v0.4.x/installation",children:"setup instructions"}),". This includes:","\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"copa"})," tool ",(0,t.jsx)(n.a,{href:"/copacetic/website/v0.4.x/installation",children:"built & pathed"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://github.com/moby/buildkit/#quick-start",children:"buildkit"})," daemon installed & pathed. ",(0,t.jsx)(n.a,{href:"#buildkit-connection-examples",children:"Examples"})]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://docs.docker.com/desktop/linux/install/#generic-installation-steps",children:"docker"})," daemon running and CLI installed & pathed."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://aquasecurity.github.io/trivy/latest/getting-started/installation/",children:"trivy CLI"})," installed & pathed."]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,t.jsx)(n.h2,{id:"sample-steps",children:"Sample Steps"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Download the target container to scan and patch:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"docker pull mcr.microsoft.com/oss/nginx/nginx:1.21.6\n"})}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json mcr.microsoft.com/oss/nginx/nginx:1.21.6\n"})}),"\n",(0,t.jsx)(n.p,{children:"You can also see the existing patchable vulnerabilities in table form on the shell with:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed mcr.microsoft.com/oss/nginx/nginx:1.21.6\n\n"})}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"To patch the image, use the Trivy report and specify a buildkit instance to connect to:"}),"\n",(0,t.jsx)(n.p,{children:"By default copa will attempt to auto-connect to an instance in order:"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["Default docker buildkit endpoint (requires at least docker v24.0 with ",(0,t.jsx)(n.a,{href:"https://docs.docker.com/storage/containerd/#enable-containerd-image-store-on-docker-engine",children:"containerd snapshotter"})," support enabled)"]}),"\n",(0,t.jsxs)(n.li,{children:["Currently selected buildx builder (see: ",(0,t.jsx)(n.code,{children:"docker buildx --help"}),")"]}),"\n",(0,t.jsxs)(n.li,{children:["buildkit daemon at the default address ",(0,t.jsx)(n.code,{children:"/run/buildkit/buildkitd.sock"})]}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["If an instance doesn't exist or that instance doesn't support all the features copa needs the next will be attempted.\nYou may need to specify a custom address using the ",(0,t.jsx)(n.code,{children:"--addr"})," flag. Here are the supported formats:"]}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"unix:///path/to/buildkit.sock"})," - Connect to buildkit over unix socket."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"tcp://$BUILDKIT_ADDR:$PORT"})," - Connec to buildkit over TCP. (not recommended for security reasons)"]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"docker://<docker connection spec>"})," - Connect to docker, currently only unix sockets are supported, e.g. ",(0,t.jsx)(n.code,{children:"docker://unix:///var/run/docker.sock"})," (or just ",(0,t.jsx)(n.code,{children:"docker://"}),")."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"docker-container://my-buildkit-container"})," - Connect to a buildkitd running in a docker container."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"buildx://my-builder"})," - Connect to a buildx builder (or ",(0,t.jsx)(n.code,{children:"buildx://"})," for the currently selected builder). ",(0,t.jsx)(n.em,{children:"Note: only container-backed buildx instances are currently supported"})]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"nerdctl-container://my-container-name"})," - Similar to ",(0,t.jsx)(n.code,{children:"docker-container"})," but uses ",(0,t.jsx)(n.code,{children:"nerdctl"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"podman-container://my-container-name"})," - Similar to ",(0,t.jsx)(n.code,{children:"docker-container"})," but uses ",(0,t.jsx)(n.code,{children:"podman"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"ssh://myhost"})," - Connect to a buildkit instance over SSH. Format of the host spec should mimic the SSH command."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"kubepod://mypod"})," - Connect to buildkit running in a Kubernetes pod. Can also specify kubectl context and pod namespace (",(0,t.jsx)(n.code,{children:"kubepod://mypod?context=foo&namespace=notdefault"}),")."]}),"\n"]}),"\n",(0,t.jsx)(n.h4,{id:"buildkit-connection-examples",children:"Buildkit Connection Examples"}),"\n",(0,t.jsx)(n.p,{children:"Example: Connect using defaults:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"copa patch -i mcr.microsoft.com/oss/nginx/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched\n"})}),"\n",(0,t.jsx)(n.p,{children:"Example: Connect to buildx"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:"docker buildx create --name demo\ncopa patch -i mcr.microsoft.com/oss/nginx/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr buildx://demo\n"})}),"\n",(0,t.jsx)(n.p,{children:"Example: Buildkit in a container"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:'export BUILDKIT_VERSION=v0.12.0\ndocker run \\\n --detach \\\n --rm \\\n --privileged \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION"\n\ncopa patch -i mcr.microsoft.com/oss/nginx/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr docker-container://buildkitd\n'})}),"\n",(0,t.jsx)(n.p,{children:"Example: Buildkit over TCP"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:'export BUILDKIT_VERSION=v0.12.0\n export BUILDKIT_PORT=8888\n docker run \\\n --detach \\\n --rm \\\n --privileged \\\n -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION" \\\n --addr tcp://0.0.0.0:$BUILDKIT_PORT\n copa patch \\\n -i mcr.microsoft.com/oss/nginx/nginx:1.21.6 \\\n -r nginx.1.21.6.json \\\n -t 1.21.6-patched \\\n -a tcp://0.0.0.0:$BUILDKIT_PORT \n'})}),"\n",(0,t.jsxs)(n.p,{children:["In either case, ",(0,t.jsx)(n.code,{children:"copa"})," is non-destructive and exports a new image with the specified ",(0,t.jsx)(n.code,{children:"1.21.6-patched"})," label to the local Docker daemon."]}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"NOTE:"})," if you're running this sample against an image from a private registry instead,\nensure that the credentials are configured in the default Docker config.json before running ",(0,t.jsx)(n.code,{children:"copa patch"}),",\nfor example, via ",(0,t.jsx)(n.code,{children:"sudo docker login -u <user> -p <password> <registry>"}),"."]}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Scan the patched image and verify that the vulnerabilities have been patched:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed mcr.microsoft.com/oss/nginx/nginx:1.21.6-patched\n"})}),"\n",(0,t.jsxs)(n.p,{children:["You can also inspect the structure of the patched image with ",(0,t.jsx)(n.code,{children:"docker history"})," to see the new patch layer appended to the image:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'$ docker history mcr.microsoft.com/oss/nginx/nginx:1.21.6-patched\nIMAGE CREATED CREATED BY SIZE COMMENT\na372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins\u2026 26.1MB buildkit.exporter.image.v0\n<missing> 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0\n<missing> 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0\n<missing> 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0\n<missing> 3 months ago COPY 30-tune-worker-processes.sh /docker-ent\u2026 4.61kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent\u2026 1.04kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker\u2026 1.96kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0\n<missing> 3 months ago RUN /bin/sh -c set -x && addgroup --syst\u2026 61.1MB buildkit.dockerfile.v0\n<missing> 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0\n<missing> 3 months ago LABEL maintainer=NGINX Docker Maintainers <d\u2026 0B buildkit.dockerfile.v0\n<missing> 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B\n<missing> 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55\u2026 80.4MB\n'})}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Run the container to verify that the image has no regressions:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'$ docker run -it --rm --name nginx-test mcr.microsoft.com/oss/nginx/nginx:1.21.6-patched\n/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration\n/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh\n10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf\n10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh\n/docker-entrypoint.sh: Configuration complete; ready for start up\n2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method\n2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2\n2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)\n2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2\n2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576\n2022/05/16 18:00:17 [notice] 1#1: start worker processes\n2022/05/16 18:00:17 [notice] 1#1: start worker process 31\n2022/05/16 18:00:17 [notice] 1#1: start worker process 32\n2022/05/16 18:00:17 [notice] 1#1: start worker process 33\n2022/05/16 18:00:17 [notice] 1#1: start worker process 34\n2022/05/16 18:00:17 [notice] 1#1: start worker process 35\n2022/05/16 18:00:17 [notice] 1#1: start worker process 36\n2022/05/16 18:00:17 [notice] 1#1: start worker process 37\n2022/05/16 18:00:17 [notice] 1#1: start worker process 38\n2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received\n'})}),"\n",(0,t.jsxs)(n.p,{children:["You can stop the container by opening a new shell instance and running: ",(0,t.jsx)(n.code,{children:"docker stop nginx-test"})]}),"\n"]}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(a,{...e})}):a(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>c,x:()=>r});var t=i(6540);const s={},o=t.createContext(s);function c(e){const n=t.useContext(o);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function r(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:c(e.components),t.createElement(o.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/9e350ec0.c4af5b3c.js b/website/assets/js/9e350ec0.c4af5b3c.js deleted file mode 100644 index 98f7eda6..00000000 --- a/website/assets/js/9e350ec0.c4af5b3c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7262],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>k});var i=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,i,a=function(e,t){if(null==e)return{};var n,i,a={},r=Object.keys(e);for(i=0;i<r.length;i++)n=r[i],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i<r.length;i++)n=r[i],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=i.createContext({}),c=function(e){var t=i.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return i.createElement(s.Provider,{value:t},e.children)},d="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},u=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),d=c(n),u=a,k=d["".concat(s,".").concat(u)]||d[u]||m[u]||r;return n?i.createElement(k,o(o({ref:t},p),{},{components:n})):i.createElement(k,o({ref:t},p))}));function k(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,o=new Array(r);o[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[d]="string"==typeof e?e:a,o[1]=l;for(var c=2;c<r;c++)o[c]=n[c];return i.createElement.apply(null,o)}return i.createElement.apply(null,n)}u.displayName="MDXCreateElement"},6937:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>d,frontMatter:()=>r,metadata:()=>l,toc:()=>c});var i=n(7462),a=(n(7294),n(3905));const r={title:"Quick Start"},o=void 0,l={unversionedId:"quick-start",id:"version-v0.4.x/quick-start",title:"Quick Start",description:"This sample illustrates how to patch containers using vulnerability reports with copa.",source:"@site/versioned_docs/version-v0.4.x/quick-start.md",sourceDirName:".",slug:"/quick-start",permalink:"/copacetic/website/v0.4.x/quick-start",draft:!1,tags:[],version:"v0.4.x",frontMatter:{title:"Quick Start"},sidebar:"sidebar",previous:{title:"Installation",permalink:"/copacetic/website/v0.4.x/installation"},next:{title:"Troubleshooting",permalink:"/copacetic/website/v0.4.x/troubleshooting"}},s={},c=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Sample Steps",id:"sample-steps",level:2}],p={toc:c};function d(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,i.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"This sample illustrates how to patch containers using vulnerability reports with ",(0,a.kt)("inlineCode",{parentName:"p"},"copa"),"."),(0,a.kt)("h2",{id:"prerequisites"},"Prerequisites"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"An Ubuntu 22.04 VM configured through the ",(0,a.kt)("a",{parentName:"li",href:"/copacetic/website/v0.4.x/installation"},"setup instructions"),". This includes:",(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"copa")," tool ",(0,a.kt)("a",{parentName:"li",href:"/copacetic/website/v0.4.x/installation"},"built & pathed"),"."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/moby/buildkit/#quick-start"},"buildkit")," daemon installed & pathed. ",(0,a.kt)("a",{parentName:"li",href:"#buildkit-connection-examples"},"Examples")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://docs.docker.com/desktop/linux/install/#generic-installation-steps"},"docker")," daemon running and CLI installed & pathed."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://aquasecurity.github.io/trivy/latest/getting-started/installation/"},"trivy CLI")," installed & pathed.")))),(0,a.kt)("h2",{id:"sample-steps"},"Sample Steps"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"Download the target container to scan and patch:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"docker pull mcr.microsoft.com/oss/nginx/nginx:1.21.6\n"))),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json mcr.microsoft.com/oss/nginx/nginx:1.21.6\n")),(0,a.kt)("p",{parentName:"li"},"You can also see the existing patchable vulnerabilities in table form on the shell with:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed mcr.microsoft.com/oss/nginx/nginx:1.21.6\n\n"))),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"To patch the image, use the Trivy report and specify a buildkit instance to connect to:"),(0,a.kt)("p",{parentName:"li"},"By default copa will attempt to auto-connect to an instance in order:"),(0,a.kt)("ol",{parentName:"li"},(0,a.kt)("li",{parentName:"ol"},"Default docker buildkit endpoint (requires at least docker v24.0 with ",(0,a.kt)("a",{parentName:"li",href:"https://docs.docker.com/storage/containerd/#enable-containerd-image-store-on-docker-engine"},"containerd snapshotter")," support enabled)"),(0,a.kt)("li",{parentName:"ol"},"Currently selected buildx builder (see: ",(0,a.kt)("inlineCode",{parentName:"li"},"docker buildx --help"),")"),(0,a.kt)("li",{parentName:"ol"},"buildkit daemon at the default address ",(0,a.kt)("inlineCode",{parentName:"li"},"/run/buildkit/buildkitd.sock"))),(0,a.kt)("p",{parentName:"li"},"If an instance doesn't exist or that instance doesn't support all the features copa needs the next will be attempted.\nYou may need to specify a custom address using the ",(0,a.kt)("inlineCode",{parentName:"p"},"--addr")," flag. Here are the supported formats:"),(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"unix:///path/to/buildkit.sock")," - Connect to buildkit over unix socket."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"tcp://$BUILDKIT_ADDR:$PORT")," - Connec to buildkit over TCP. (not recommended for security reasons)"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"docker://<docker connection spec>")," - Connect to docker, currently only unix sockets are supported, e.g. ",(0,a.kt)("inlineCode",{parentName:"li"},"docker://unix:///var/run/docker.sock")," (or just ",(0,a.kt)("inlineCode",{parentName:"li"},"docker://"),")."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"docker-container://my-buildkit-container")," - Connect to a buildkitd running in a docker container."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"buildx://my-builder")," - Connect to a buildx builder (or ",(0,a.kt)("inlineCode",{parentName:"li"},"buildx://")," for the currently selected builder). ",(0,a.kt)("em",{parentName:"li"},"Note: only container-backed buildx instances are currently supported")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"nerdctl-container://my-container-name")," - Similar to ",(0,a.kt)("inlineCode",{parentName:"li"},"docker-container")," but uses ",(0,a.kt)("inlineCode",{parentName:"li"},"nerdctl"),"."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"podman-container://my-container-name")," - Similar to ",(0,a.kt)("inlineCode",{parentName:"li"},"docker-container")," but uses ",(0,a.kt)("inlineCode",{parentName:"li"},"podman"),"."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"ssh://myhost")," - Connect to a buildkit instance over SSH. Format of the host spec should mimic the SSH command."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"kubepod://mypod")," - Connect to buildkit running in a Kubernetes pod. Can also specify kubectl context and pod namespace (",(0,a.kt)("inlineCode",{parentName:"li"},"kubepod://mypod?context=foo&namespace=notdefault"),").")),(0,a.kt)("h4",{parentName:"li",id:"buildkit-connection-examples"},"Buildkit Connection Examples"),(0,a.kt)("p",{parentName:"li"},"Example: Connect using defaults: "),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"copa patch -i mcr.microsoft.com/oss/nginx/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched\n")),(0,a.kt)("p",{parentName:"li"},"Example: Connect to buildx"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre"},"docker buildx create --name demo\ncopa patch -i mcr.microsoft.com/oss/nginx/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr buildx://demo\n")),(0,a.kt)("p",{parentName:"li"},"Example: Buildkit in a container"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre"},'export BUILDKIT_VERSION=v0.12.0\ndocker run \\\n --detach \\\n --rm \\\n --privileged \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION"\n\ncopa patch -i mcr.microsoft.com/oss/nginx/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr docker-container://buildkitd\n')),(0,a.kt)("p",{parentName:"li"},"Example: Buildkit over TCP"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre"},'export BUILDKIT_VERSION=v0.12.0\n export BUILDKIT_PORT=8888\n docker run \\\n --detach \\\n --rm \\\n --privileged \\\n -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION" \\\n --addr tcp://0.0.0.0:$BUILDKIT_PORT\n copa patch \\\n -i mcr.microsoft.com/oss/nginx/nginx:1.21.6 \\\n -r nginx.1.21.6.json \\\n -t 1.21.6-patched \\\n -a tcp://0.0.0.0:$BUILDKIT_PORT \n')),(0,a.kt)("p",{parentName:"li"},"In either case, ",(0,a.kt)("inlineCode",{parentName:"p"},"copa")," is non-destructive and exports a new image with the specified ",(0,a.kt)("inlineCode",{parentName:"p"},"1.21.6-patched")," label to the local Docker daemon."),(0,a.kt)("blockquote",{parentName:"li"},(0,a.kt)("p",{parentName:"blockquote"},(0,a.kt)("strong",{parentName:"p"},"NOTE:")," if you're running this sample against an image from a private registry instead,\nensure that the credentials are configured in the default Docker config.json before running ",(0,a.kt)("inlineCode",{parentName:"p"},"copa patch"),",\nfor example, via ",(0,a.kt)("inlineCode",{parentName:"p"},"sudo docker login -u <user> -p <password> <registry>"),"."))),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"Scan the patched image and verify that the vulnerabilities have been patched:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed mcr.microsoft.com/oss/nginx/nginx:1.21.6-patched\n")),(0,a.kt)("p",{parentName:"li"},"You can also inspect the structure of the patched image with ",(0,a.kt)("inlineCode",{parentName:"p"},"docker history")," to see the new patch layer appended to the image:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre",className:"language-bash"},'$ docker history mcr.microsoft.com/oss/nginx/nginx:1.21.6-patched\nIMAGE CREATED CREATED BY SIZE COMMENT\na372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins\u2026 26.1MB buildkit.exporter.image.v0\n<missing> 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0\n<missing> 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0\n<missing> 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0\n<missing> 3 months ago COPY 30-tune-worker-processes.sh /docker-ent\u2026 4.61kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent\u2026 1.04kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker\u2026 1.96kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0\n<missing> 3 months ago RUN /bin/sh -c set -x && addgroup --syst\u2026 61.1MB buildkit.dockerfile.v0\n<missing> 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0\n<missing> 3 months ago LABEL maintainer=NGINX Docker Maintainers <d\u2026 0B buildkit.dockerfile.v0\n<missing> 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B\n<missing> 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55\u2026 80.4MB\n'))),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"Run the container to verify that the image has no regressions:"),(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre",className:"language-bash"},'$ docker run -it --rm --name nginx-test mcr.microsoft.com/oss/nginx/nginx:1.21.6-patched\n/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration\n/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh\n10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf\n10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh\n/docker-entrypoint.sh: Configuration complete; ready for start up\n2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method\n2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2\n2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)\n2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2\n2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576\n2022/05/16 18:00:17 [notice] 1#1: start worker processes\n2022/05/16 18:00:17 [notice] 1#1: start worker process 31\n2022/05/16 18:00:17 [notice] 1#1: start worker process 32\n2022/05/16 18:00:17 [notice] 1#1: start worker process 33\n2022/05/16 18:00:17 [notice] 1#1: start worker process 34\n2022/05/16 18:00:17 [notice] 1#1: start worker process 35\n2022/05/16 18:00:17 [notice] 1#1: start worker process 36\n2022/05/16 18:00:17 [notice] 1#1: start worker process 37\n2022/05/16 18:00:17 [notice] 1#1: start worker process 38\n2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received\n')),(0,a.kt)("p",{parentName:"li"},"You can stop the container by opening a new shell instance and running: ",(0,a.kt)("inlineCode",{parentName:"p"},"docker stop nginx-test")))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/9f27d5ef.f416e8f9.js b/website/assets/js/9f27d5ef.f416e8f9.js deleted file mode 100644 index 7e01cb67..00000000 --- a/website/assets/js/9f27d5ef.f416e8f9.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6927],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function p(e,t){if(null==e)return{};var n,r,a=function(e,t){if(null==e)return{};var n,r,a={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},s="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,u=p(e,["components","mdxType","originalType","parentName"]),s=l(n),d=a,f=s["".concat(c,".").concat(d)]||s[d]||m[d]||o;return n?r.createElement(f,i(i({ref:t},u),{},{components:n})):r.createElement(f,i({ref:t},u))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=d;var p={};for(var c in t)hasOwnProperty.call(t,c)&&(p[c]=t[c]);p.originalType=e,p[s]="string"==typeof e?e:a,i[1]=p;for(var l=2;l<o;l++)i[l]=n[l];return r.createElement.apply(null,i)}return r.createElement.apply(null,n)}d.displayName="MDXCreateElement"},9936:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>s,frontMatter:()=>o,metadata:()=>p,toc:()=>l});var r=n(7462),a=(n(7294),n(3905));const o={title:"Output"},i=void 0,p={unversionedId:"output",id:"version-v0.5.x/output",title:"Output",description:"Experimental: This feature might change without preserving backwards compatibility.",source:"@site/versioned_docs/version-v0.5.x/output.md",sourceDirName:".",slug:"/output",permalink:"/copacetic/website/v0.5.x/output",draft:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Output"},sidebar:"sidebar",previous:{title:"Quick Start",permalink:"/copacetic/website/v0.5.x/quick-start"},next:{title:"Troubleshooting",permalink:"/copacetic/website/v0.5.x/troubleshooting"}},c={},l=[{value:"OpenVEX",id:"openvex",level:2}],u={toc:l};function s(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("admonition",{type:"caution"},(0,a.kt)("p",{parentName:"admonition"},"Experimental: This feature might change without preserving backwards compatibility.")),(0,a.kt)("p",null,"Copa optionally outputs a Vulnerability Exploitability eXchange (VEX) file as a result of the patching process to surface the vulnerabilities and packages that were patched."),(0,a.kt)("p",null,"Currently, Copa supports the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/openvex"},"OpenVEX")," format, but it can be extended to support other formats."),(0,a.kt)("h2",{id:"openvex"},"OpenVEX"),(0,a.kt)("p",null,"OpenVEX is an implementation of Vulnerability Exploitability eXchange (VEX) format. For more information, see ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/openvex/spec/"},"OpenVEX specification"),"."),(0,a.kt)("admonition",{type:"tip"},(0,a.kt)("ul",{parentName:"admonition"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"Use ",(0,a.kt)("inlineCode",{parentName:"p"},"COPA_VEX_AUTHOR")," environment variable to set the author of the VEX document. If it's not set, the author will default to ",(0,a.kt)("inlineCode",{parentName:"p"},"Project Copacetic"),".")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"A VEX document must contain at least one VEX statement. If there are no fixed vulnerabilities, Copa will not generate a VEX document.")))),(0,a.kt)("p",null,"To generate a VEX document using OpenVEX, use ",(0,a.kt)("inlineCode",{parentName:"p"},'--format="openvex"')," flag, and use ",(0,a.kt)("inlineCode",{parentName:"p"},"--output")," to specify a file path. For example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},'copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --format="openvex" --output "nginx.1.21.6-vex.json"\n')),(0,a.kt)("p",null,"This will generate a VEX Document that looks like:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-json"},'{\n "@context": "https://openvex.dev/ns",\n "@id": "https://openvex.dev/docs/public/vex-a6c44ec1d79e9dd4190dc01b4ecf7527ebb26bd37c01e32e6efcd203ae00d2a5",\n "author": "Project Copacetic",\n "timestamp": "2023-10-11T00:15:00.114768055Z",\n "version": 1,\n "tooling": "Project Copacetic",\n "statements": [\n {\n "vulnerability": {\n "@id": "CVE-2021-22945"\n },\n "products": [\n {\n "@id": "pkg:oci/docker.io/library/nginx:1.21.6-patched",\n "subcomponents": [\n {\n "@id": "pkg:deb/debian/curl@7.74.0-1.3+deb11u2?arch=amd64"\n },\n {\n "@id": "pkg:deb/debian/libcurl4@7.74.0-1.3+deb11u2?arch=amd64"\n }\n ]\n }\n ],\n "status": "fixed"\n },\n ...\n}\n')))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/9f27d5ef.f6738d54.js b/website/assets/js/9f27d5ef.f6738d54.js new file mode 100644 index 00000000..ff188236 --- /dev/null +++ b/website/assets/js/9f27d5ef.f6738d54.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9727],{6242:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>d,frontMatter:()=>a,metadata:()=>r,toc:()=>l});var i=n(4848),o=n(8453);const a={title:"Output"},s=void 0,r={id:"output",title:"Output",description:"Experimental: This feature might change without preserving backwards compatibility.",source:"@site/versioned_docs/version-v0.5.x/output.md",sourceDirName:".",slug:"/output",permalink:"/copacetic/website/v0.5.x/output",draft:!1,unlisted:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Output"},sidebar:"sidebar",previous:{title:"Quick Start",permalink:"/copacetic/website/v0.5.x/quick-start"},next:{title:"Troubleshooting",permalink:"/copacetic/website/v0.5.x/troubleshooting"}},c={},l=[{value:"OpenVEX",id:"openvex",level:2}];function p(e){const t={a:"a",admonition:"admonition",code:"code",h2:"h2",li:"li",p:"p",pre:"pre",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(t.admonition,{type:"caution",children:(0,i.jsx)(t.p,{children:"Experimental: This feature might change without preserving backwards compatibility."})}),"\n",(0,i.jsx)(t.p,{children:"Copa optionally outputs a Vulnerability Exploitability eXchange (VEX) file as a result of the patching process to surface the vulnerabilities and packages that were patched."}),"\n",(0,i.jsxs)(t.p,{children:["Currently, Copa supports the ",(0,i.jsx)(t.a,{href:"https://github.com/openvex",children:"OpenVEX"})," format, but it can be extended to support other formats."]}),"\n",(0,i.jsx)(t.h2,{id:"openvex",children:"OpenVEX"}),"\n",(0,i.jsxs)(t.p,{children:["OpenVEX is an implementation of Vulnerability Exploitability eXchange (VEX) format. For more information, see ",(0,i.jsx)(t.a,{href:"https://github.com/openvex/spec/",children:"OpenVEX specification"}),"."]}),"\n",(0,i.jsx)(t.admonition,{type:"tip",children:(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:["Use ",(0,i.jsx)(t.code,{children:"COPA_VEX_AUTHOR"})," environment variable to set the author of the VEX document. If it's not set, the author will default to ",(0,i.jsx)(t.code,{children:"Project Copacetic"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsx)(t.p,{children:"A VEX document must contain at least one VEX statement. If there are no fixed vulnerabilities, Copa will not generate a VEX document."}),"\n"]}),"\n"]})}),"\n",(0,i.jsxs)(t.p,{children:["To generate a VEX document using OpenVEX, use ",(0,i.jsx)(t.code,{children:'--format="openvex"'})," flag, and use ",(0,i.jsx)(t.code,{children:"--output"})," to specify a file path. For example:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:'copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --format="openvex" --output "nginx.1.21.6-vex.json"\n'})}),"\n",(0,i.jsx)(t.p,{children:"This will generate a VEX Document that looks like:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",children:'{\n "@context": "https://openvex.dev/ns",\n "@id": "https://openvex.dev/docs/public/vex-a6c44ec1d79e9dd4190dc01b4ecf7527ebb26bd37c01e32e6efcd203ae00d2a5",\n "author": "Project Copacetic",\n "timestamp": "2023-10-11T00:15:00.114768055Z",\n "version": 1,\n "tooling": "Project Copacetic",\n "statements": [\n {\n "vulnerability": {\n "@id": "CVE-2021-22945"\n },\n "products": [\n {\n "@id": "pkg:oci/docker.io/library/nginx:1.21.6-patched",\n "subcomponents": [\n {\n "@id": "pkg:deb/debian/curl@7.74.0-1.3+deb11u2?arch=amd64"\n },\n {\n "@id": "pkg:deb/debian/libcurl4@7.74.0-1.3+deb11u2?arch=amd64"\n }\n ]\n }\n ],\n "status": "fixed"\n },\n ...\n}\n'})})]})}function d(e={}){const{wrapper:t}={...(0,o.R)(),...e.components};return t?(0,i.jsx)(t,{...e,children:(0,i.jsx)(p,{...e})}):p(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>s,x:()=>r});var i=n(6540);const o={},a=i.createContext(o);function s(e){const t=i.useContext(a);return i.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function r(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:s(e.components),i.createElement(a.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/9f65b58a.3374a93f.js b/website/assets/js/9f65b58a.3374a93f.js deleted file mode 100644 index 1b691699..00000000 --- a/website/assets/js/9f65b58a.3374a93f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3480],{3905:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>u});var i=a(7294);function n(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,i)}return a}function o(e){for(var t=1;t<arguments.length;t++){var a=null!=arguments[t]?arguments[t]:{};t%2?r(Object(a),!0).forEach((function(t){n(e,t,a[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):r(Object(a)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))}))}return e}function s(e,t){if(null==e)return{};var a,i,n=function(e,t){if(null==e)return{};var a,i,n={},r=Object.keys(e);for(i=0;i<r.length;i++)a=r[i],t.indexOf(a)>=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i<r.length;i++)a=r[i],t.indexOf(a)>=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var l=i.createContext({}),p=function(e){var t=i.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},c=function(e){var t=p(e.components);return i.createElement(l.Provider,{value:t},e.children)},d="mdxType",g={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},h=i.forwardRef((function(e,t){var a=e.components,n=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=p(a),h=n,u=d["".concat(l,".").concat(h)]||d[h]||g[h]||r;return a?i.createElement(u,o(o({ref:t},c),{},{components:a})):i.createElement(u,o({ref:t},c))}));function u(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var r=a.length,o=new Array(r);o[0]=h;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[d]="string"==typeof e?e:n,o[1]=s;for(var p=2;p<r;p++)o[p]=a[p];return i.createElement.apply(null,o)}return i.createElement.apply(null,a)}h.displayName="MDXCreateElement"},5177:(e,t,a)=>{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>d,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var i=a(7462),n=(a(7294),a(3905));const r={title:"Design"},o=void 0,s={unversionedId:"design",id:"version-v0.3.x/design",title:"Design",description:"Design Tenets",source:"@site/versioned_docs/version-v0.3.x/design.md",sourceDirName:".",slug:"/design",permalink:"/copacetic/website/v0.3.x/design",draft:!1,tags:[],version:"v0.3.x",frontMatter:{title:"Design"},sidebar:"sidebar",previous:{title:"Quick Start",permalink:"/copacetic/website/v0.3.x/quick-start"},next:{title:"FAQ",permalink:"/copacetic/website/v0.3.x/faq"}},l={},p=[{value:"Design Tenets",id:"design-tenets",level:2},{value:"Design Reasoning",id:"design-reasoning",level:2},{value:"Architecture",id:"architecture",level:2},{value:"Implementation",id:"implementation",level:2},{value:"Tradeoffs",id:"tradeoffs",level:2}],c={toc:p};function d(e){let{components:t,...a}=e;return(0,n.kt)("wrapper",(0,i.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h2",{id:"design-tenets"},"Design Tenets"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update.")," This is a raison d\u2019etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to work with the existing ecosystem of container images.")," The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to allow parties other than the image authors to address container vulnerabilities.")," Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to do one thing well and be composable with other tools and processes.")," Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane."))),(0,n.kt)("h2",{id:"design-reasoning"},"Design Reasoning"),(0,n.kt)("p",null,"The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/google/go-containerregistry/blob/main/cmd/crane/rebase.md"},(0,n.kt)("inlineCode",{parentName:"a"},"crane rebase")),":"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},"Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an ",(0,n.kt)("inlineCode",{parentName:"p"},"apt install")," during image creation will overwrite the dpkg ",(0,n.kt)("inlineCode",{parentName:"p"},"status")," file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners."),(0,n.kt)("p",{parentName:"li"},"To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},"Rebasing also requires that the user knows ",(0,n.kt)("em",{parentName:"p"},"a priori")," what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets."),(0,n.kt)("p",{parentName:"li"},"While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose."),(0,n.kt)("p",{parentName:"li"},"The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well."))),(0,n.kt)("h2",{id:"architecture"},"Architecture"),(0,n.kt)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),(0,n.kt)("p",null,"The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"The data schema of various vulnerability scanners producing the input vulnerability report."),(0,n.kt)("li",{parentName:"ul"},"The state management of various package managers and process for applying patches appropriately through them.")),(0,n.kt)("p",null,"Effectively, ",(0,n.kt)("inlineCode",{parentName:"p"},"copa patch")," can be considered a command that bridges an extensible ",(0,n.kt)("inlineCode",{parentName:"p"},"Parse")," action with an extensible ",(0,n.kt)("inlineCode",{parentName:"p"},"Apply")," action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-go"},"type UpdatePackage struct {\n Name string\n Version string\n}\n\ntype UpdateManifest struct {\n OSType string\n OSVersion string\n Arch string\n Updates []UpdatePackage\n}\n\ntype ScanReportParser interface {\n Parse(reportPath string) (*UpdateManifest, error)\n}\n\ntype PackageManager interface {\n Apply(imagePath string, report *UpdateManifest) error\n}\n")),(0,n.kt)("h2",{id:"implementation"},"Implementation"),(0,n.kt)("img",{title:"buildkit graph execution",src:"/copacetic/website/img/graph-execution.png"}),(0,n.kt)("p",null,(0,n.kt)("inlineCode",{parentName:"p"},"copa")," is a pseudo-frontend to ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit"},"buildkit")," implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit/tree/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4#exploring-llb"},"LLB graphs")," for buildkit to execute:"),(0,n.kt)("ol",null,(0,n.kt)("li",{parentName:"ol"},"Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions."))),(0,n.kt)("li",{parentName:"ol"},"Actions to fetch and deploy tools that can be injected into the target image to perform the patching.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches."),(0,n.kt)("li",{parentName:"ul"},"In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image."),(0,n.kt)("li",{parentName:"ul"},"Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well."))),(0,n.kt)("li",{parentName:"ol"},"Actions to deploy the required patch packages to the target image.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"copa")," integrates with buildkit at the API level because it uses the ",(0,n.kt)("a",{parentName:"li",href:"https://github.com/moby/buildkit/blob/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4/docs/merge%2Bdiff.md"},"diff and merge")," graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.")))),(0,n.kt)("h2",{id:"tradeoffs"},"Tradeoffs"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa."),(0,n.kt)("li",{parentName:"ul"},"copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this."))),(0,n.kt)("li",{parentName:"ul"},"No support for windows containers given the dependency on buildkit.")))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/9f65b58a.37deb826.js b/website/assets/js/9f65b58a.37deb826.js new file mode 100644 index 00000000..6f599148 --- /dev/null +++ b/website/assets/js/9f65b58a.37deb826.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3379],{8715:(e,t,i)=>{i.r(t),i.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>h,frontMatter:()=>s,metadata:()=>r,toc:()=>l});var n=i(4848),a=i(8453);const s={title:"Design"},o=void 0,r={id:"design",title:"Design",description:"Design Tenets",source:"@site/versioned_docs/version-v0.3.x/design.md",sourceDirName:".",slug:"/design",permalink:"/copacetic/website/v0.3.x/design",draft:!1,unlisted:!1,tags:[],version:"v0.3.x",frontMatter:{title:"Design"},sidebar:"sidebar",previous:{title:"Quick Start",permalink:"/copacetic/website/v0.3.x/quick-start"},next:{title:"FAQ",permalink:"/copacetic/website/v0.3.x/faq"}},c={},l=[{value:"Design Tenets",id:"design-tenets",level:2},{value:"Design Reasoning",id:"design-reasoning",level:2},{value:"Architecture",id:"architecture",level:2},{value:"Implementation",id:"implementation",level:2},{value:"Tradeoffs",id:"tradeoffs",level:2}];function d(e){const t={a:"a",code:"code",em:"em",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(t.h2,{id:"design-tenets",children:"Design Tenets"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update."})," This is a raison d\u2019etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to work with the existing ecosystem of container images."})," The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to allow parties other than the image authors to address container vulnerabilities."})," Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to do one thing well and be composable with other tools and processes."})," Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane."]}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"design-reasoning",children:"Design Reasoning"}),"\n",(0,n.jsxs)(t.p,{children:["The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental ",(0,n.jsx)(t.a,{href:"https://github.com/google/go-containerregistry/blob/main/cmd/crane/rebase.md",children:(0,n.jsx)(t.code,{children:"crane rebase"})}),":"]}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:["Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an ",(0,n.jsx)(t.code,{children:"apt install"})," during image creation will overwrite the dpkg ",(0,n.jsx)(t.code,{children:"status"})," file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners."]}),"\n",(0,n.jsx)(t.p,{children:"To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on."}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:["Rebasing also requires that the user knows ",(0,n.jsx)(t.em,{children:"a priori"})," what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets."]}),"\n",(0,n.jsx)(t.p,{children:"While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose."}),"\n",(0,n.jsx)(t.p,{children:"The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well."}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"architecture",children:"Architecture"}),"\n",(0,n.jsx)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),"\n",(0,n.jsx)(t.p,{children:"The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"The data schema of various vulnerability scanners producing the input vulnerability report."}),"\n",(0,n.jsx)(t.li,{children:"The state management of various package managers and process for applying patches appropriately through them."}),"\n"]}),"\n",(0,n.jsxs)(t.p,{children:["Effectively, ",(0,n.jsx)(t.code,{children:"copa patch"})," can be considered a command that bridges an extensible ",(0,n.jsx)(t.code,{children:"Parse"})," action with an extensible ",(0,n.jsx)(t.code,{children:"Apply"})," action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:"]}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-go",children:"type UpdatePackage struct {\n Name string\n Version string\n}\n\ntype UpdateManifest struct {\n OSType string\n OSVersion string\n Arch string\n Updates []UpdatePackage\n}\n\ntype ScanReportParser interface {\n Parse(reportPath string) (*UpdateManifest, error)\n}\n\ntype PackageManager interface {\n Apply(imagePath string, report *UpdateManifest) error\n}\n"})}),"\n",(0,n.jsx)(t.h2,{id:"implementation",children:"Implementation"}),"\n",(0,n.jsx)("img",{title:"buildkit graph execution",src:"/copacetic/website/img/graph-execution.png"}),"\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.code,{children:"copa"})," is a pseudo-frontend to ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit",children:"buildkit"})," implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit/tree/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4#exploring-llb",children:"LLB graphs"})," for buildkit to execute:"]}),"\n",(0,n.jsxs)(t.ol,{children:["\n",(0,n.jsxs)(t.li,{children:["Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions."}),"\n"]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["Actions to fetch and deploy tools that can be injected into the target image to perform the patching.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches."}),"\n",(0,n.jsx)(t.li,{children:"In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image."}),"\n",(0,n.jsx)(t.li,{children:"Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well."}),"\n"]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["Actions to deploy the required patch packages to the target image.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.code,{children:"copa"})," integrates with buildkit at the API level because it uses the ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit/blob/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4/docs/merge%2Bdiff.md",children:"diff and merge"})," graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches."]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"tradeoffs",children:"Tradeoffs"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa."}),"\n",(0,n.jsx)(t.li,{children:"copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this."}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.li,{children:"No support for windows containers given the dependency on buildkit."}),"\n"]})]})}function h(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,n.jsx)(t,{...e,children:(0,n.jsx)(d,{...e})}):d(e)}},8453:(e,t,i)=>{i.d(t,{R:()=>o,x:()=>r});var n=i(6540);const a={},s=n.createContext(a);function o(e){const t=n.useContext(s);return n.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function r(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),n.createElement(s.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/a09c2993.2acef99e.js b/website/assets/js/a09c2993.2acef99e.js new file mode 100644 index 00000000..e7a779d9 --- /dev/null +++ b/website/assets/js/a09c2993.2acef99e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5899],{1456:(e,i,n)=>{n.r(i),n.d(i,{assets:()=>c,contentTitle:()=>r,default:()=>h,frontMatter:()=>s,metadata:()=>o,toc:()=>l});var t=n(4848),a=n(8453);const s={title:"Introduction",slug:"/"},r="Project Copacetic: Directly patch container image vulnerabilities",o={id:"introduction",title:"Introduction",description:"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.",source:"@site/docs/introduction.md",sourceDirName:".",slug:"/",permalink:"/copacetic/website/next/",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{title:"Introduction",slug:"/"},sidebar:"sidebar",next:{title:"Installation",permalink:"/copacetic/website/next/installation"}},c={},l=[{value:"Why?",id:"why",level:2},{value:"How?",id:"how",level:2}];function d(e){const i={a:"a",code:"code",em:"em",h1:"h1",h2:"h2",li:"li",ol:"ol",p:"p",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i.h1,{id:"project-copacetic-directly-patch-container-image-vulnerabilities",children:"Project Copacetic: Directly patch container image vulnerabilities"}),"\n",(0,t.jsxs)(i.p,{children:[(0,t.jsx)(i.code,{children:"copa"})," is a CLI tool written in ",(0,t.jsx)(i.a,{href:"https://golang.org",children:"Go"})," and based on ",(0,t.jsx)(i.a,{href:"https://github.com/moby/buildkit",children:"buildkit"})," that can be used to directly patch container images given the vulnerability scanning results from popular tools like ",(0,t.jsx)(i.a,{href:"https://github.com/aquasecurity/trivy",children:"Trivy"}),"."]}),"\n",(0,t.jsx)(i.h2,{id:"why",children:"Why?"}),"\n",(0,t.jsxs)(i.p,{children:["We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between ",(0,t.jsx)(i.a,{href:"https://www.bleepingcomputer.com/news/security/hackers-scan-for-vulnerabilities-within-15-minutes-of-disclosure/",children:"vulnerability disclosure and active exploitation continues to narrow"}),", there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:"]}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option"}),"\n",(0,t.jsx)(i.li,{children:"found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs."}),"\n"]}),"\n",(0,t.jsx)("img",{title:"direct image patching",src:"/copacetic/website/img/direct-image-patching.png"}),"\n",(0,t.jsxs)(i.p,{children:["In addition to filling the operational gap not met by left-shift security practices and tools, the ability of ",(0,t.jsx)(i.code,{children:"copa"})," to patch a container without requiring a rebuild of the container image provides other benefits:"]}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Allows users other than the image publishers to also patch container images, such as DevSecOps engineers."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image."}),"\n"]}),"\n",(0,t.jsx)(i.h2,{id:"how",children:"How?"}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"copa"})," tool is an extensible engine that:"]}),"\n",(0,t.jsxs)(i.ol,{children:["\n",(0,t.jsx)(i.li,{children:"Parses the needed update packages from the container image\u2019s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats."}),"\n",(0,t.jsx)(i.li,{children:"Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers."}),"\n",(0,t.jsx)(i.li,{children:"Applies the resulting update binaries to the container image using buildkit."}),"\n"]}),"\n",(0,t.jsx)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),"\n",(0,t.jsx)(i.p,{children:"This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:"}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:[(0,t.jsxs)(i.strong,{children:["Copa supports patching ",(0,t.jsx)(i.em,{children:"existing"})," container images"]}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Devs don't need to build their images using specific tools or modify them in some way just to support container patching."}),"\n"]}),"\n"]}),"\n",(0,t.jsx)(i.li,{children:(0,t.jsxs)(i.strong,{children:["Copa supports containers without package managers ",(0,t.jsx)(i.em,{children:"including"})," distroless containers"]})}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Copa works with the existing vulnerability scanning and mitigation ecosystems"}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today."}),"\n",(0,t.jsx)(i.li,{children:"Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports."}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image"}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today."}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.p,{children:["For more details, refer to the ",(0,t.jsx)(i.a,{href:"/copacetic/website/next/design",children:"copa design"})," documentation."]})]})}function h(e={}){const{wrapper:i}={...(0,a.R)(),...e.components};return i?(0,t.jsx)(i,{...e,children:(0,t.jsx)(d,{...e})}):d(e)}},8453:(e,i,n)=>{n.d(i,{R:()=>r,x:()=>o});var t=n(6540);const a={},s=t.createContext(a);function r(e){const i=t.useContext(s);return t.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function o(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:r(e.components),t.createElement(s.Provider,{value:i},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/a09c2993.5da876e3.js b/website/assets/js/a09c2993.5da876e3.js deleted file mode 100644 index 3a6c886c..00000000 --- a/website/assets/js/a09c2993.5da876e3.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4128],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>g});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),c=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(s.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=c(n),m=i,g=u["".concat(s,".").concat(m)]||u[m]||d[m]||r;return n?a.createElement(g,o(o({ref:t},p),{},{components:n})):a.createElement(g,o({ref:t},p))}));function g(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[u]="string"==typeof e?e:i,o[1]=l;for(var c=2;c<r;c++)o[c]=n[c];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}m.displayName="MDXCreateElement"},8495:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>l,toc:()=>c});var a=n(7462),i=(n(7294),n(3905));const r={title:"Introduction",slug:"/"},o="Project Copacetic: Directly patch container image vulnerabilities",l={unversionedId:"introduction",id:"introduction",title:"Introduction",description:"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.",source:"@site/docs/introduction.md",sourceDirName:".",slug:"/",permalink:"/copacetic/website/next/",draft:!1,tags:[],version:"current",frontMatter:{title:"Introduction",slug:"/"},sidebar:"sidebar",next:{title:"Installation",permalink:"/copacetic/website/next/installation"}},s={},c=[{value:"Why?",id:"why",level:2},{value:"How?",id:"how",level:2}],p={toc:c};function u(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"project-copacetic-directly-patch-container-image-vulnerabilities"},"Project Copacetic: Directly patch container image vulnerabilities"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"copa")," is a CLI tool written in ",(0,i.kt)("a",{parentName:"p",href:"https://golang.org"},"Go")," and based on ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit"},"buildkit")," that can be used to directly patch container images given the vulnerability scanning results from popular tools like ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/aquasecurity/trivy"},"Trivy"),"."),(0,i.kt)("h2",{id:"why"},"Why?"),(0,i.kt)("p",null,"We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between ",(0,i.kt)("a",{parentName:"p",href:"https://www.bleepingcomputer.com/news/security/hackers-scan-for-vulnerabilities-within-15-minutes-of-disclosure/"},"vulnerability disclosure and active exploitation continues to narrow"),", there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option"),(0,i.kt)("li",{parentName:"ul"},"found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.")),(0,i.kt)("img",{title:"direct image patching",src:"/copacetic/website/img/direct-image-patching.png"}),(0,i.kt)("p",null,"In addition to filling the operational gap not met by left-shift security practices and tools, the ability of ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," to patch a container without requiring a rebuild of the container image provides other benefits:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Allows users other than the image publishers to also patch container images, such as DevSecOps engineers."),(0,i.kt)("li",{parentName:"ul"},"Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching."),(0,i.kt)("li",{parentName:"ul"},"Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild."),(0,i.kt)("li",{parentName:"ul"},"Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.")),(0,i.kt)("h2",{id:"how"},"How?"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," tool is an extensible engine that:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Parses the needed update packages from the container image\u2019s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats."),(0,i.kt)("li",{parentName:"ol"},"Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers."),(0,i.kt)("li",{parentName:"ol"},"Applies the resulting update binaries to the container image using buildkit.")),(0,i.kt)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),(0,i.kt)("p",null,"This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa supports patching ",(0,i.kt)("em",{parentName:"strong"},"existing")," container images"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"Devs don't need to build their images using specific tools or modify them in some way just to support container patching."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa supports containers without package managers ",(0,i.kt)("em",{parentName:"strong"},"including")," distroless containers")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa works with the existing vulnerability scanning and mitigation ecosystems"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today."),(0,i.kt)("li",{parentName:"ul"},"Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.")))),(0,i.kt)("p",null,"For more details, refer to the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/next/design"},"copa design")," documentation."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/a7bd4aaa.b82ee0df.js b/website/assets/js/a7bd4aaa.b82ee0df.js new file mode 100644 index 00000000..9aa99771 --- /dev/null +++ b/website/assets/js/a7bd4aaa.b82ee0df.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7098],{4532:(e,n,s)=>{s.r(n),s.d(n,{default:()=>x});s(6540);var r=s(1003),t=s(2967),o=s(2252),i=s(2831),c=s(1463),u=s(4848);function a(e){const{version:n}=e;return(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(c.A,{version:n.version,tag:(0,t.tU)(n.pluginId,n.version)}),(0,u.jsx)(r.be,{children:n.noIndex&&(0,u.jsx)("meta",{name:"robots",content:"noindex, nofollow"})})]})}function l(e){const{version:n,route:s}=e;return(0,u.jsx)(r.e3,{className:n.className,children:(0,u.jsx)(o.n,{version:n,children:(0,i.v)(s.routes)})})}function x(e){return(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(a,{...e}),(0,u.jsx)(l,{...e})]})}}}]); \ No newline at end of file diff --git a/website/assets/js/a94703ab.2668bc5b.js b/website/assets/js/a94703ab.2668bc5b.js new file mode 100644 index 00000000..3b329078 --- /dev/null +++ b/website/assets/js/a94703ab.2668bc5b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9048],{2559:(e,t,n)=>{n.r(t),n.d(t,{default:()=>be});var a=n(6540),o=n(4164),i=n(1003),s=n(7559),l=n(1754),r=n(6588),c=n(1312),d=n(3104),u=n(5062);const m={backToTopButton:"backToTopButton_sjWU",backToTopButtonShow:"backToTopButtonShow_xfvO"};var b=n(4848);function h(){const{shown:e,scrollToTop:t}=function(e){let{threshold:t}=e;const[n,o]=(0,a.useState)(!1),i=(0,a.useRef)(!1),{startScroll:s,cancelScroll:l}=(0,d.gk)();return(0,d.Mq)(((e,n)=>{let{scrollY:a}=e;const s=n?.scrollY;s&&(i.current?i.current=!1:a>=s?(l(),o(!1)):a<t?o(!1):a+window.innerHeight<document.documentElement.scrollHeight&&o(!0))})),(0,u.$)((e=>{e.location.hash&&(i.current=!0,o(!1))})),{shown:n,scrollToTop:()=>s(0)}}({threshold:300});return(0,b.jsx)("button",{"aria-label":(0,c.T)({id:"theme.BackToTopButton.buttonAriaLabel",message:"Scroll back to top",description:"The ARIA label for the back to top button"}),className:(0,o.A)("clean-btn",s.G.common.backToTopButton,m.backToTopButton,e&&m.backToTopButtonShow),type:"button",onClick:t})}var p=n(3109),x=n(6347),j=n(4581),f=n(6342),v=n(3465);function _(e){return(0,b.jsx)("svg",{width:"20",height:"20","aria-hidden":"true",...e,children:(0,b.jsxs)("g",{fill:"#7a7a7a",children:[(0,b.jsx)("path",{d:"M9.992 10.023c0 .2-.062.399-.172.547l-4.996 7.492a.982.982 0 01-.828.454H1c-.55 0-1-.453-1-1 0-.2.059-.403.168-.551l4.629-6.942L.168 3.078A.939.939 0 010 2.528c0-.548.45-.997 1-.997h2.996c.352 0 .649.18.828.45L9.82 9.472c.11.148.172.347.172.55zm0 0"}),(0,b.jsx)("path",{d:"M19.98 10.023c0 .2-.058.399-.168.547l-4.996 7.492a.987.987 0 01-.828.454h-3c-.547 0-.996-.453-.996-1 0-.2.059-.403.168-.551l4.625-6.942-4.625-6.945a.939.939 0 01-.168-.55 1 1 0 01.996-.997h3c.348 0 .649.18.828.45l4.996 7.492c.11.148.168.347.168.55zm0 0"})]})})}const A={collapseSidebarButton:"collapseSidebarButton_PEFL",collapseSidebarButtonIcon:"collapseSidebarButtonIcon_kv0_"};function g(e){let{onClick:t}=e;return(0,b.jsx)("button",{type:"button",title:(0,c.T)({id:"theme.docs.sidebar.collapseButtonTitle",message:"Collapse sidebar",description:"The title attribute for collapse button of doc sidebar"}),"aria-label":(0,c.T)({id:"theme.docs.sidebar.collapseButtonAriaLabel",message:"Collapse sidebar",description:"The title attribute for collapse button of doc sidebar"}),className:(0,o.A)("button button--secondary button--outline",A.collapseSidebarButton),onClick:t,children:(0,b.jsx)(_,{className:A.collapseSidebarButtonIcon})})}var k=n(5041),C=n(9532);const S=Symbol("EmptyContext"),T=a.createContext(S);function N(e){let{children:t}=e;const[n,o]=(0,a.useState)(null),i=(0,a.useMemo)((()=>({expandedItem:n,setExpandedItem:o})),[n]);return(0,b.jsx)(T.Provider,{value:i,children:t})}var I=n(1422),B=n(9169),y=n(8774),w=n(2303);function L(e){let{collapsed:t,categoryLabel:n,onClick:a}=e;return(0,b.jsx)("button",{"aria-label":t?(0,c.T)({id:"theme.DocSidebarItem.expandCategoryAriaLabel",message:"Expand sidebar category '{label}'",description:"The ARIA label to expand the sidebar category"},{label:n}):(0,c.T)({id:"theme.DocSidebarItem.collapseCategoryAriaLabel",message:"Collapse sidebar category '{label}'",description:"The ARIA label to collapse the sidebar category"},{label:n}),"aria-expanded":!t,type:"button",className:"clean-btn menu__caret",onClick:a})}function E(e){let{item:t,onItemClick:n,activePath:i,level:r,index:c,...d}=e;const{items:u,label:m,collapsible:h,className:p,href:x}=t,{docs:{sidebar:{autoCollapseCategories:j}}}=(0,f.p)(),v=function(e){const t=(0,w.A)();return(0,a.useMemo)((()=>e.href&&!e.linkUnlisted?e.href:!t&&e.collapsible?(0,l.Nr)(e):void 0),[e,t])}(t),_=(0,l.w8)(t,i),A=(0,B.ys)(x,i),{collapsed:g,setCollapsed:k}=(0,I.u)({initialState:()=>!!h&&(!_&&t.collapsed)}),{expandedItem:N,setExpandedItem:E}=function(){const e=(0,a.useContext)(T);if(e===S)throw new C.dV("DocSidebarItemsExpandedStateProvider");return e}(),M=function(e){void 0===e&&(e=!g),E(e?null:c),k(e)};return function(e){let{isActive:t,collapsed:n,updateCollapsed:o}=e;const i=(0,C.ZC)(t);(0,a.useEffect)((()=>{t&&!i&&n&&o(!1)}),[t,i,n,o])}({isActive:_,collapsed:g,updateCollapsed:M}),(0,a.useEffect)((()=>{h&&null!=N&&N!==c&&j&&k(!0)}),[h,N,c,k,j]),(0,b.jsxs)("li",{className:(0,o.A)(s.G.docs.docSidebarItemCategory,s.G.docs.docSidebarItemCategoryLevel(r),"menu__list-item",{"menu__list-item--collapsed":g},p),children:[(0,b.jsxs)("div",{className:(0,o.A)("menu__list-item-collapsible",{"menu__list-item-collapsible--active":A}),children:[(0,b.jsx)(y.A,{className:(0,o.A)("menu__link",{"menu__link--sublist":h,"menu__link--sublist-caret":!x&&h,"menu__link--active":_}),onClick:h?e=>{n?.(t),x?M(!1):(e.preventDefault(),M())}:()=>{n?.(t)},"aria-current":A?"page":void 0,role:h&&!x?"button":void 0,"aria-expanded":h&&!x?!g:void 0,href:h?v??"#":v,...d,children:m}),x&&h&&(0,b.jsx)(L,{collapsed:g,categoryLabel:m,onClick:e=>{e.preventDefault(),M()}})]}),(0,b.jsx)(I.N,{lazy:!0,as:"ul",className:"menu__list",collapsed:g,children:(0,b.jsx)(U,{items:u,tabIndex:g?-1:0,onItemClick:n,activePath:i,level:r+1})})]})}var M=n(6654),H=n(3186);const G={menuExternalLink:"menuExternalLink_NmtK"};function W(e){let{item:t,onItemClick:n,activePath:a,level:i,index:r,...c}=e;const{href:d,label:u,className:m,autoAddBaseUrl:h}=t,p=(0,l.w8)(t,a),x=(0,M.A)(d);return(0,b.jsx)("li",{className:(0,o.A)(s.G.docs.docSidebarItemLink,s.G.docs.docSidebarItemLinkLevel(i),"menu__list-item",m),children:(0,b.jsxs)(y.A,{className:(0,o.A)("menu__link",!x&&G.menuExternalLink,{"menu__link--active":p}),autoAddBaseUrl:h,"aria-current":p?"page":void 0,to:d,...x&&{onClick:n?()=>n(t):void 0},...c,children:[u,!x&&(0,b.jsx)(H.A,{})]})},u)}const P={menuHtmlItem:"menuHtmlItem_M9Kj"};function R(e){let{item:t,level:n,index:a}=e;const{value:i,defaultStyle:l,className:r}=t;return(0,b.jsx)("li",{className:(0,o.A)(s.G.docs.docSidebarItemLink,s.G.docs.docSidebarItemLinkLevel(n),l&&[P.menuHtmlItem,"menu__list-item"],r),dangerouslySetInnerHTML:{__html:i}},a)}function D(e){let{item:t,...n}=e;switch(t.type){case"category":return(0,b.jsx)(E,{item:t,...n});case"html":return(0,b.jsx)(R,{item:t,...n});default:return(0,b.jsx)(W,{item:t,...n})}}function F(e){let{items:t,...n}=e;const a=(0,l.Y)(t,n.activePath);return(0,b.jsx)(N,{children:a.map(((e,t)=>(0,b.jsx)(D,{item:e,index:t,...n},t)))})}const U=(0,a.memo)(F),V={menu:"menu_SIkG",menuWithAnnouncementBar:"menuWithAnnouncementBar_GW3s"};function Y(e){let{path:t,sidebar:n,className:i}=e;const l=function(){const{isActive:e}=(0,k.Mj)(),[t,n]=(0,a.useState)(e);return(0,d.Mq)((t=>{let{scrollY:a}=t;e&&n(0===a)}),[e]),e&&t}();return(0,b.jsx)("nav",{"aria-label":(0,c.T)({id:"theme.docs.sidebar.navAriaLabel",message:"Docs sidebar",description:"The ARIA label for the sidebar navigation"}),className:(0,o.A)("menu thin-scrollbar",V.menu,l&&V.menuWithAnnouncementBar,i),children:(0,b.jsx)("ul",{className:(0,o.A)(s.G.docs.docSidebarMenu,"menu__list"),children:(0,b.jsx)(U,{items:n,activePath:t,level:1})})})}const K="sidebar_njMd",z="sidebarWithHideableNavbar_wUlq",q="sidebarHidden_VK0M",O="sidebarLogo_isFc";function J(e){let{path:t,sidebar:n,onCollapse:a,isHidden:i}=e;const{navbar:{hideOnScroll:s},docs:{sidebar:{hideable:l}}}=(0,f.p)();return(0,b.jsxs)("div",{className:(0,o.A)(K,s&&z,i&&q),children:[s&&(0,b.jsx)(v.A,{tabIndex:-1,className:O}),(0,b.jsx)(Y,{path:t,sidebar:n}),l&&(0,b.jsx)(g,{onClick:a})]})}const Q=a.memo(J);var X=n(5600),Z=n(9876);const $=e=>{let{sidebar:t,path:n}=e;const a=(0,Z.M)();return(0,b.jsx)("ul",{className:(0,o.A)(s.G.docs.docSidebarMenu,"menu__list"),children:(0,b.jsx)(U,{items:t,activePath:n,onItemClick:e=>{"category"===e.type&&e.href&&a.toggle(),"link"===e.type&&a.toggle()},level:1})})};function ee(e){return(0,b.jsx)(X.GX,{component:$,props:e})}const te=a.memo(ee);function ne(e){const t=(0,j.l)(),n="desktop"===t||"ssr"===t,a="mobile"===t;return(0,b.jsxs)(b.Fragment,{children:[n&&(0,b.jsx)(Q,{...e}),a&&(0,b.jsx)(te,{...e})]})}const ae={expandButton:"expandButton_TmdG",expandButtonIcon:"expandButtonIcon_i1dp"};function oe(e){let{toggleSidebar:t}=e;return(0,b.jsx)("div",{className:ae.expandButton,title:(0,c.T)({id:"theme.docs.sidebar.expandButtonTitle",message:"Expand sidebar",description:"The ARIA label and title attribute for expand button of doc sidebar"}),"aria-label":(0,c.T)({id:"theme.docs.sidebar.expandButtonAriaLabel",message:"Expand sidebar",description:"The ARIA label and title attribute for expand button of doc sidebar"}),tabIndex:0,role:"button",onKeyDown:t,onClick:t,children:(0,b.jsx)(_,{className:ae.expandButtonIcon})})}const ie={docSidebarContainer:"docSidebarContainer_YfHR",docSidebarContainerHidden:"docSidebarContainerHidden_DPk8",sidebarViewport:"sidebarViewport_aRkj"};function se(e){let{children:t}=e;const n=(0,r.t)();return(0,b.jsx)(a.Fragment,{children:t},n?.name??"noSidebar")}function le(e){let{sidebar:t,hiddenSidebarContainer:n,setHiddenSidebarContainer:i}=e;const{pathname:l}=(0,x.zy)(),[r,c]=(0,a.useState)(!1),d=(0,a.useCallback)((()=>{r&&c(!1),!r&&(0,p.O)()&&c(!0),i((e=>!e))}),[i,r]);return(0,b.jsx)("aside",{className:(0,o.A)(s.G.docs.docSidebarContainer,ie.docSidebarContainer,n&&ie.docSidebarContainerHidden),onTransitionEnd:e=>{e.currentTarget.classList.contains(ie.docSidebarContainer)&&n&&c(!0)},children:(0,b.jsx)(se,{children:(0,b.jsxs)("div",{className:(0,o.A)(ie.sidebarViewport,r&&ie.sidebarViewportHidden),children:[(0,b.jsx)(ne,{sidebar:t,path:l,onCollapse:d,isHidden:r}),r&&(0,b.jsx)(oe,{toggleSidebar:d})]})})})}const re={docMainContainer:"docMainContainer_TBSr",docMainContainerEnhanced:"docMainContainerEnhanced_lQrH",docItemWrapperEnhanced:"docItemWrapperEnhanced_JWYK"};function ce(e){let{hiddenSidebarContainer:t,children:n}=e;const a=(0,r.t)();return(0,b.jsx)("main",{className:(0,o.A)(re.docMainContainer,(t||!a)&&re.docMainContainerEnhanced),children:(0,b.jsx)("div",{className:(0,o.A)("container padding-top--md padding-bottom--lg",re.docItemWrapper,t&&re.docItemWrapperEnhanced),children:n})})}const de={docRoot:"docRoot_UBD9",docsWrapper:"docsWrapper_hBAB"};function ue(e){let{children:t}=e;const n=(0,r.t)(),[o,i]=(0,a.useState)(!1);return(0,b.jsxs)("div",{className:de.docsWrapper,children:[(0,b.jsx)(h,{}),(0,b.jsxs)("div",{className:de.docRoot,children:[n&&(0,b.jsx)(le,{sidebar:n.items,hiddenSidebarContainer:o,setHiddenSidebarContainer:i}),(0,b.jsx)(ce,{hiddenSidebarContainer:o,children:t})]})]})}var me=n(3363);function be(e){const t=(0,l.B5)(e);if(!t)return(0,b.jsx)(me.A,{});const{docElement:n,sidebarName:a,sidebarItems:c}=t;return(0,b.jsx)(i.e3,{className:(0,o.A)(s.G.page.docsDocPage),children:(0,b.jsx)(r.V,{name:a,items:c,children:(0,b.jsx)(ue,{children:n})})})}},3363:(e,t,n)=>{n.d(t,{A:()=>l});n(6540);var a=n(4164),o=n(1312),i=n(1107),s=n(4848);function l(e){let{className:t}=e;return(0,s.jsx)("main",{className:(0,a.A)("container margin-vert--xl",t),children:(0,s.jsx)("div",{className:"row",children:(0,s.jsxs)("div",{className:"col col--6 col--offset-3",children:[(0,s.jsx)(i.A,{as:"h1",className:"hero__title",children:(0,s.jsx)(o.A,{id:"theme.NotFound.title",description:"The title of the 404 page",children:"Page Not Found"})}),(0,s.jsx)("p",{children:(0,s.jsx)(o.A,{id:"theme.NotFound.p1",description:"The first paragraph of the 404 page",children:"We could not find what you were looking for."})}),(0,s.jsx)("p",{children:(0,s.jsx)(o.A,{id:"theme.NotFound.p2",description:"The 2nd paragraph of the 404 page",children:"Please contact the owner of the site that linked you to the original URL and let them know their link is broken."})})]})})})}}}]); \ No newline at end of file diff --git a/website/assets/js/a95b810a.3050b258.js b/website/assets/js/a95b810a.3050b258.js deleted file mode 100644 index b38060eb..00000000 --- a/website/assets/js/a95b810a.3050b258.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5692],{3905:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>u});var i=a(7294);function n(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,i)}return a}function o(e){for(var t=1;t<arguments.length;t++){var a=null!=arguments[t]?arguments[t]:{};t%2?r(Object(a),!0).forEach((function(t){n(e,t,a[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):r(Object(a)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))}))}return e}function s(e,t){if(null==e)return{};var a,i,n=function(e,t){if(null==e)return{};var a,i,n={},r=Object.keys(e);for(i=0;i<r.length;i++)a=r[i],t.indexOf(a)>=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i<r.length;i++)a=r[i],t.indexOf(a)>=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var l=i.createContext({}),p=function(e){var t=i.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},c=function(e){var t=p(e.components);return i.createElement(l.Provider,{value:t},e.children)},d="mdxType",g={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},h=i.forwardRef((function(e,t){var a=e.components,n=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=p(a),h=n,u=d["".concat(l,".").concat(h)]||d[h]||g[h]||r;return a?i.createElement(u,o(o({ref:t},c),{},{components:a})):i.createElement(u,o({ref:t},c))}));function u(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var r=a.length,o=new Array(r);o[0]=h;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[d]="string"==typeof e?e:n,o[1]=s;for(var p=2;p<r;p++)o[p]=a[p];return i.createElement.apply(null,o)}return i.createElement.apply(null,a)}h.displayName="MDXCreateElement"},5949:(e,t,a)=>{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>d,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var i=a(7462),n=(a(7294),a(3905));const r={title:"Design"},o=void 0,s={unversionedId:"design",id:"version-v0.6.x/design",title:"Design",description:"Design Tenets",source:"@site/versioned_docs/version-v0.6.x/design.md",sourceDirName:".",slug:"/design",permalink:"/copacetic/website/design",draft:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Design"},sidebar:"sidebar",previous:{title:"Code of Conduct",permalink:"/copacetic/website/code-of-conduct"},next:{title:"Development and Testing Tips",permalink:"/copacetic/website/development-tips"}},l={},p=[{value:"Design Tenets",id:"design-tenets",level:2},{value:"Design Reasoning",id:"design-reasoning",level:2},{value:"Architecture",id:"architecture",level:2},{value:"Implementation",id:"implementation",level:2},{value:"Tradeoffs",id:"tradeoffs",level:2}],c={toc:p};function d(e){let{components:t,...a}=e;return(0,n.kt)("wrapper",(0,i.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h2",{id:"design-tenets"},"Design Tenets"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update.")," This is a raison d\u2019etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to work with the existing ecosystem of container images.")," The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to allow parties other than the image authors to address container vulnerabilities.")," Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to do one thing well and be composable with other tools and processes.")," Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane."))),(0,n.kt)("h2",{id:"design-reasoning"},"Design Reasoning"),(0,n.kt)("p",null,"The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/google/go-containerregistry/blob/main/cmd/crane/rebase.md"},(0,n.kt)("inlineCode",{parentName:"a"},"crane rebase")),":"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},"Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an ",(0,n.kt)("inlineCode",{parentName:"p"},"apt install")," during image creation will overwrite the dpkg ",(0,n.kt)("inlineCode",{parentName:"p"},"status")," file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners."),(0,n.kt)("p",{parentName:"li"},"To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},"Rebasing also requires that the user knows ",(0,n.kt)("em",{parentName:"p"},"a priori")," what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets."),(0,n.kt)("p",{parentName:"li"},"While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose."),(0,n.kt)("p",{parentName:"li"},"The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well."))),(0,n.kt)("h2",{id:"architecture"},"Architecture"),(0,n.kt)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),(0,n.kt)("p",null,"The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"The data schema of various vulnerability scanners producing the input vulnerability report."),(0,n.kt)("li",{parentName:"ul"},"The state management of various package managers and process for applying patches appropriately through them.")),(0,n.kt)("p",null,"Effectively, ",(0,n.kt)("inlineCode",{parentName:"p"},"copa patch")," can be considered a command that bridges an extensible ",(0,n.kt)("inlineCode",{parentName:"p"},"Parse")," action with an extensible ",(0,n.kt)("inlineCode",{parentName:"p"},"Apply")," action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-go"},"type UpdatePackage struct {\n Name string\n Version string\n}\n\ntype UpdateManifest struct {\n OSType string\n OSVersion string\n Arch string\n Updates []UpdatePackage\n}\n\ntype ScanReportParser interface {\n Parse(reportPath string) (*UpdateManifest, error)\n}\n\ntype PackageManager interface {\n Apply(imagePath string, report *UpdateManifest) error\n}\n")),(0,n.kt)("h2",{id:"implementation"},"Implementation"),(0,n.kt)("img",{title:"buildkit graph execution",src:"/copacetic/website/img/graph-execution.png"}),(0,n.kt)("p",null,(0,n.kt)("inlineCode",{parentName:"p"},"copa")," is a pseudo-frontend to ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit"},"buildkit")," implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit/tree/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4#exploring-llb"},"LLB graphs")," for buildkit to execute:"),(0,n.kt)("ol",null,(0,n.kt)("li",{parentName:"ol"},"Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions."))),(0,n.kt)("li",{parentName:"ol"},"Actions to fetch and deploy tools that can be injected into the target image to perform the patching.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches."),(0,n.kt)("li",{parentName:"ul"},"In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image."),(0,n.kt)("li",{parentName:"ul"},"Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well."))),(0,n.kt)("li",{parentName:"ol"},"Actions to deploy the required patch packages to the target image.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"copa")," integrates with buildkit at the API level because it uses the ",(0,n.kt)("a",{parentName:"li",href:"https://github.com/moby/buildkit/blob/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4/docs/merge%2Bdiff.md"},"diff and merge")," graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.")))),(0,n.kt)("h2",{id:"tradeoffs"},"Tradeoffs"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa."),(0,n.kt)("li",{parentName:"ul"},"copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this."))),(0,n.kt)("li",{parentName:"ul"},"No support for windows containers given the dependency on buildkit.")))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/a95b810a.dac0b4cb.js b/website/assets/js/a95b810a.dac0b4cb.js new file mode 100644 index 00000000..9f071141 --- /dev/null +++ b/website/assets/js/a95b810a.dac0b4cb.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6524],{5970:(e,t,i)=>{i.r(t),i.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>h,frontMatter:()=>s,metadata:()=>r,toc:()=>l});var n=i(4848),a=i(8453);const s={title:"Design"},o=void 0,r={id:"design",title:"Design",description:"Design Tenets",source:"@site/versioned_docs/version-v0.6.x/design.md",sourceDirName:".",slug:"/design",permalink:"/copacetic/website/design",draft:!1,unlisted:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Design"},sidebar:"sidebar",previous:{title:"Code of Conduct",permalink:"/copacetic/website/code-of-conduct"},next:{title:"Development and Testing Tips",permalink:"/copacetic/website/development-tips"}},c={},l=[{value:"Design Tenets",id:"design-tenets",level:2},{value:"Design Reasoning",id:"design-reasoning",level:2},{value:"Architecture",id:"architecture",level:2},{value:"Implementation",id:"implementation",level:2},{value:"Tradeoffs",id:"tradeoffs",level:2}];function d(e){const t={a:"a",code:"code",em:"em",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(t.h2,{id:"design-tenets",children:"Design Tenets"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update."})," This is a raison d\u2019etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to work with the existing ecosystem of container images."})," The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to allow parties other than the image authors to address container vulnerabilities."})," Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to do one thing well and be composable with other tools and processes."})," Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane."]}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"design-reasoning",children:"Design Reasoning"}),"\n",(0,n.jsxs)(t.p,{children:["The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental ",(0,n.jsx)(t.a,{href:"https://github.com/google/go-containerregistry/blob/main/cmd/crane/rebase.md",children:(0,n.jsx)(t.code,{children:"crane rebase"})}),":"]}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:["Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an ",(0,n.jsx)(t.code,{children:"apt install"})," during image creation will overwrite the dpkg ",(0,n.jsx)(t.code,{children:"status"})," file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners."]}),"\n",(0,n.jsx)(t.p,{children:"To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on."}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:["Rebasing also requires that the user knows ",(0,n.jsx)(t.em,{children:"a priori"})," what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets."]}),"\n",(0,n.jsx)(t.p,{children:"While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose."}),"\n",(0,n.jsx)(t.p,{children:"The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well."}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"architecture",children:"Architecture"}),"\n",(0,n.jsx)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),"\n",(0,n.jsx)(t.p,{children:"The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"The data schema of various vulnerability scanners producing the input vulnerability report."}),"\n",(0,n.jsx)(t.li,{children:"The state management of various package managers and process for applying patches appropriately through them."}),"\n"]}),"\n",(0,n.jsxs)(t.p,{children:["Effectively, ",(0,n.jsx)(t.code,{children:"copa patch"})," can be considered a command that bridges an extensible ",(0,n.jsx)(t.code,{children:"Parse"})," action with an extensible ",(0,n.jsx)(t.code,{children:"Apply"})," action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:"]}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-go",children:"type UpdatePackage struct {\n Name string\n Version string\n}\n\ntype UpdateManifest struct {\n OSType string\n OSVersion string\n Arch string\n Updates []UpdatePackage\n}\n\ntype ScanReportParser interface {\n Parse(reportPath string) (*UpdateManifest, error)\n}\n\ntype PackageManager interface {\n Apply(imagePath string, report *UpdateManifest) error\n}\n"})}),"\n",(0,n.jsx)(t.h2,{id:"implementation",children:"Implementation"}),"\n",(0,n.jsx)("img",{title:"buildkit graph execution",src:"/copacetic/website/img/graph-execution.png"}),"\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.code,{children:"copa"})," is a pseudo-frontend to ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit",children:"buildkit"})," implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit/tree/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4#exploring-llb",children:"LLB graphs"})," for buildkit to execute:"]}),"\n",(0,n.jsxs)(t.ol,{children:["\n",(0,n.jsxs)(t.li,{children:["Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions."}),"\n"]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["Actions to fetch and deploy tools that can be injected into the target image to perform the patching.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches."}),"\n",(0,n.jsx)(t.li,{children:"In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image."}),"\n",(0,n.jsx)(t.li,{children:"Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well."}),"\n"]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["Actions to deploy the required patch packages to the target image.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.code,{children:"copa"})," integrates with buildkit at the API level because it uses the ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit/blob/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4/docs/merge%2Bdiff.md",children:"diff and merge"})," graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches."]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"tradeoffs",children:"Tradeoffs"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa."}),"\n",(0,n.jsx)(t.li,{children:"copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this."}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.li,{children:"No support for windows containers given the dependency on buildkit."}),"\n"]})]})}function h(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,n.jsx)(t,{...e,children:(0,n.jsx)(d,{...e})}):d(e)}},8453:(e,t,i)=>{i.d(t,{R:()=>o,x:()=>r});var n=i(6540);const a={},s=n.createContext(a);function o(e){const t=n.useContext(s);return n.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function r(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),n.createElement(s.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/b407a98a.2829481b.js b/website/assets/js/b407a98a.2829481b.js new file mode 100644 index 00000000..87d70964 --- /dev/null +++ b/website/assets/js/b407a98a.2829481b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8409],{5218:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>l});var o=i(4848),t=i(8453);const r={title:"Code of Conduct"},a="Contributor Covenant Code of Conduct",s={id:"code-of-conduct",title:"Code of Conduct",description:"Our Pledge",source:"@site/versioned_docs/version-v0.2.x/code-of-conduct.md",sourceDirName:".",slug:"/code-of-conduct",permalink:"/copacetic/website/v0.2.x/code-of-conduct",draft:!1,unlisted:!1,tags:[],version:"v0.2.x",frontMatter:{title:"Code of Conduct"},sidebar:"sidebar",previous:{title:"Contributing",permalink:"/copacetic/website/v0.2.x/contributing"}},c={},l=[{value:"Our Pledge",id:"our-pledge",level:2},{value:"Our Standards",id:"our-standards",level:2},{value:"Enforcement Responsibilities",id:"enforcement-responsibilities",level:2},{value:"Scope",id:"scope",level:2},{value:"Enforcement",id:"enforcement",level:2},{value:"Enforcement Guidelines",id:"enforcement-guidelines",level:2},{value:"1. Correction",id:"1-correction",level:3},{value:"2. Warning",id:"2-warning",level:3},{value:"3. Temporary Ban",id:"3-temporary-ban",level:3},{value:"4. Permanent Ban",id:"4-permanent-ban",level:3},{value:"Attribution",id:"attribution",level:2}];function d(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",li:"li",p:"p",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.h1,{id:"contributor-covenant-code-of-conduct",children:"Contributor Covenant Code of Conduct"}),"\n",(0,o.jsx)(n.h2,{id:"our-pledge",children:"Our Pledge"}),"\n",(0,o.jsx)(n.p,{children:"We as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation."}),"\n",(0,o.jsx)(n.p,{children:"We pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community."}),"\n",(0,o.jsx)(n.h2,{id:"our-standards",children:"Our Standards"}),"\n",(0,o.jsx)(n.p,{children:"Examples of behavior that contributes to a positive environment for our\ncommunity include:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"Demonstrating empathy and kindness toward other people"}),"\n",(0,o.jsx)(n.li,{children:"Being respectful of differing opinions, viewpoints, and experiences"}),"\n",(0,o.jsx)(n.li,{children:"Giving and gracefully accepting constructive feedback"}),"\n",(0,o.jsx)(n.li,{children:"Accepting responsibility and apologizing to those affected by our mistakes,\nand learning from the experience"}),"\n",(0,o.jsx)(n.li,{children:"Focusing on what is best not just for us as individuals, but for the overall\ncommunity"}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"Examples of unacceptable behavior include:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"The use of sexualized language or imagery, and sexual attention or advances of\nany kind"}),"\n",(0,o.jsx)(n.li,{children:"Trolling, insulting or derogatory comments, and personal or political attacks"}),"\n",(0,o.jsx)(n.li,{children:"Public or private harassment"}),"\n",(0,o.jsx)(n.li,{children:"Publishing others' private information, such as a physical or email address,\nwithout their explicit permission"}),"\n",(0,o.jsx)(n.li,{children:"Other conduct which could reasonably be considered inappropriate in a\nprofessional setting"}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"enforcement-responsibilities",children:"Enforcement Responsibilities"}),"\n",(0,o.jsx)(n.p,{children:"Community leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful."}),"\n",(0,o.jsx)(n.p,{children:"Community leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate."}),"\n",(0,o.jsx)(n.h2,{id:"scope",children:"Scope"}),"\n",(0,o.jsx)(n.p,{children:"This Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event."}),"\n",(0,o.jsx)(n.h2,{id:"enforcement",children:"Enforcement"}),"\n",(0,o.jsxs)(n.p,{children:["Instances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n",(0,o.jsx)(n.code,{children:"project-copacetic@googlegroups.com"}),".\nAll complaints will be reviewed and investigated promptly and fairly."]}),"\n",(0,o.jsx)(n.p,{children:"All community leaders are obligated to respect the privacy and security of the\nreporter of any incident."}),"\n",(0,o.jsx)(n.h2,{id:"enforcement-guidelines",children:"Enforcement Guidelines"}),"\n",(0,o.jsx)(n.p,{children:"Community leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:"}),"\n",(0,o.jsx)(n.h3,{id:"1-correction",children:"1. Correction"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested."]}),"\n",(0,o.jsx)(n.h3,{id:"2-warning",children:"2. Warning"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": A violation through a single incident or series of\nactions."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban."]}),"\n",(0,o.jsx)(n.h3,{id:"3-temporary-ban",children:"3. Temporary Ban"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": A serious violation of community standards, including\nsustained inappropriate behavior."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban."]}),"\n",(0,o.jsx)(n.h3,{id:"4-permanent-ban",children:"4. Permanent Ban"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A permanent ban from any sort of public interaction within the\ncommunity."]}),"\n",(0,o.jsx)(n.h2,{id:"attribution",children:"Attribution"}),"\n",(0,o.jsxs)(n.p,{children:["This Code of Conduct is adapted from the ",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org",children:"Contributor Covenant"}),",\nversion 2.1, available at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html",children:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["Community Impact Guidelines were inspired by\n",(0,o.jsx)(n.a,{href:"https://github.com/mozilla/diversity",children:"Mozilla's code of conduct enforcement ladder"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["For answers to common questions about this code of conduct, see the FAQ at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/faq",children:"https://www.contributor-covenant.org/faq"}),". Translations are available at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/translations",children:"https://www.contributor-covenant.org/translations"}),"."]})]})}function u(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>a,x:()=>s});var o=i(6540);const t={},r=o.createContext(t);function a(e){const n=o.useContext(r);return o.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function s(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:a(e.components),o.createElement(r.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/b407a98a.7eeb3207.js b/website/assets/js/b407a98a.7eeb3207.js deleted file mode 100644 index ca978e0d..00000000 --- a/website/assets/js/b407a98a.7eeb3207.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4925],{3905:(e,n,t)=>{t.d(n,{Zo:()=>p,kt:()=>f});var o=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);n&&(o=o.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,o)}return t}function a(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?r(Object(t),!0).forEach((function(n){i(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):r(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))}))}return e}function l(e,n){if(null==e)return{};var t,o,i=function(e,n){if(null==e)return{};var t,o,i={},r=Object.keys(e);for(o=0;o<r.length;o++)t=r[o],n.indexOf(t)>=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o<r.length;o++)t=r[o],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var c=o.createContext({}),s=function(e){var n=o.useContext(c),t=n;return e&&(t="function"==typeof e?e(n):a(a({},n),e)),t},p=function(e){var n=s(e.components);return o.createElement(c.Provider,{value:n},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var n=e.children;return o.createElement(o.Fragment,{},n)}},m=o.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,c=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=s(t),m=i,f=u["".concat(c,".").concat(m)]||u[m]||d[m]||r;return t?o.createElement(f,a(a({ref:n},p),{},{components:t})):o.createElement(f,a({ref:n},p))}));function f(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,a=new Array(r);a[0]=m;var l={};for(var c in n)hasOwnProperty.call(n,c)&&(l[c]=n[c]);l.originalType=e,l[u]="string"==typeof e?e:i,a[1]=l;for(var s=2;s<r;s++)a[s]=t[s];return o.createElement.apply(null,a)}return o.createElement.apply(null,t)}m.displayName="MDXCreateElement"},1615:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>r,metadata:()=>l,toc:()=>s});var o=t(7462),i=(t(7294),t(3905));const r={title:"Code of Conduct"},a="Contributor Covenant Code of Conduct",l={unversionedId:"code-of-conduct",id:"version-v0.2.x/code-of-conduct",title:"Code of Conduct",description:"Our Pledge",source:"@site/versioned_docs/version-v0.2.x/code-of-conduct.md",sourceDirName:".",slug:"/code-of-conduct",permalink:"/copacetic/website/v0.2.x/code-of-conduct",draft:!1,tags:[],version:"v0.2.x",frontMatter:{title:"Code of Conduct"},sidebar:"sidebar",previous:{title:"Contributing",permalink:"/copacetic/website/v0.2.x/contributing"}},c={},s=[{value:"Our Pledge",id:"our-pledge",level:2},{value:"Our Standards",id:"our-standards",level:2},{value:"Enforcement Responsibilities",id:"enforcement-responsibilities",level:2},{value:"Scope",id:"scope",level:2},{value:"Enforcement",id:"enforcement",level:2},{value:"Enforcement Guidelines",id:"enforcement-guidelines",level:2},{value:"1. Correction",id:"1-correction",level:3},{value:"2. Warning",id:"2-warning",level:3},{value:"3. Temporary Ban",id:"3-temporary-ban",level:3},{value:"4. Permanent Ban",id:"4-permanent-ban",level:3},{value:"Attribution",id:"attribution",level:2}],p={toc:s};function u(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,o.Z)({},p,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"contributor-covenant-code-of-conduct"},"Contributor Covenant Code of Conduct"),(0,i.kt)("h2",{id:"our-pledge"},"Our Pledge"),(0,i.kt)("p",null,"We as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation."),(0,i.kt)("p",null,"We pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community."),(0,i.kt)("h2",{id:"our-standards"},"Our Standards"),(0,i.kt)("p",null,"Examples of behavior that contributes to a positive environment for our\ncommunity include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Demonstrating empathy and kindness toward other people"),(0,i.kt)("li",{parentName:"ul"},"Being respectful of differing opinions, viewpoints, and experiences"),(0,i.kt)("li",{parentName:"ul"},"Giving and gracefully accepting constructive feedback"),(0,i.kt)("li",{parentName:"ul"},"Accepting responsibility and apologizing to those affected by our mistakes,\nand learning from the experience"),(0,i.kt)("li",{parentName:"ul"},"Focusing on what is best not just for us as individuals, but for the overall\ncommunity")),(0,i.kt)("p",null,"Examples of unacceptable behavior include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"The use of sexualized language or imagery, and sexual attention or advances of\nany kind"),(0,i.kt)("li",{parentName:"ul"},"Trolling, insulting or derogatory comments, and personal or political attacks"),(0,i.kt)("li",{parentName:"ul"},"Public or private harassment"),(0,i.kt)("li",{parentName:"ul"},"Publishing others' private information, such as a physical or email address,\nwithout their explicit permission"),(0,i.kt)("li",{parentName:"ul"},"Other conduct which could reasonably be considered inappropriate in a\nprofessional setting")),(0,i.kt)("h2",{id:"enforcement-responsibilities"},"Enforcement Responsibilities"),(0,i.kt)("p",null,"Community leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful."),(0,i.kt)("p",null,"Community leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate."),(0,i.kt)("h2",{id:"scope"},"Scope"),(0,i.kt)("p",null,"This Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event."),(0,i.kt)("h2",{id:"enforcement"},"Enforcement"),(0,i.kt)("p",null,"Instances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n",(0,i.kt)("inlineCode",{parentName:"p"},"project-copacetic@googlegroups.com"),".\nAll complaints will be reviewed and investigated promptly and fairly."),(0,i.kt)("p",null,"All community leaders are obligated to respect the privacy and security of the\nreporter of any incident."),(0,i.kt)("h2",{id:"enforcement-guidelines"},"Enforcement Guidelines"),(0,i.kt)("p",null,"Community leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:"),(0,i.kt)("h3",{id:"1-correction"},"1. Correction"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested."),(0,i.kt)("h3",{id:"2-warning"},"2. Warning"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": A violation through a single incident or series of\nactions."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban."),(0,i.kt)("h3",{id:"3-temporary-ban"},"3. Temporary Ban"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": A serious violation of community standards, including\nsustained inappropriate behavior."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban."),(0,i.kt)("h3",{id:"4-permanent-ban"},"4. Permanent Ban"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A permanent ban from any sort of public interaction within the\ncommunity."),(0,i.kt)("h2",{id:"attribution"},"Attribution"),(0,i.kt)("p",null,"This Code of Conduct is adapted from the ",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org"},"Contributor Covenant"),",\nversion 2.1, available at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"},"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"),"."),(0,i.kt)("p",null,"Community Impact Guidelines were inspired by\n",(0,i.kt)("a",{parentName:"p",href:"https://github.com/mozilla/diversity"},"Mozilla's code of conduct enforcement ladder"),"."),(0,i.kt)("p",null,"For answers to common questions about this code of conduct, see the FAQ at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/faq"},"https://www.contributor-covenant.org/faq"),". Translations are available at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/translations"},"https://www.contributor-covenant.org/translations"),"."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/b5d5e16b.0cb143ee.js b/website/assets/js/b5d5e16b.0cb143ee.js deleted file mode 100644 index f4c007d6..00000000 --- a/website/assets/js/b5d5e16b.0cb143ee.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7059],{3905:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function c(e,t){if(null==e)return{};var n,r,a=function(e,t){if(null==e)return{};var n,r,a={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=r.createContext({}),p=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},s=function(e){var t=p(e.components);return r.createElement(l.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,l=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),u=p(n),m=a,f=u["".concat(l,".").concat(m)]||u[m]||d[m]||o;return n?r.createElement(f,i(i({ref:t},s),{},{components:n})):r.createElement(f,i({ref:t},s))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=m;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[u]="string"==typeof e?e:a,i[1]=c;for(var p=2;p<o;p++)i[p]=n[p];return r.createElement.apply(null,i)}return r.createElement.apply(null,n)}m.displayName="MDXCreateElement"},7264:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>c,toc:()=>p});var r=n(7462),a=(n(7294),n(3905));const o={title:"Installation"},i=void 0,c={unversionedId:"installation",id:"version-v0.3.x/installation",title:"Installation",description:"Homebrew",source:"@site/versioned_docs/version-v0.3.x/installation.md",sourceDirName:".",slug:"/installation",permalink:"/copacetic/website/v0.3.x/installation",draft:!1,tags:[],version:"v0.3.x",frontMatter:{title:"Installation"},sidebar:"sidebar",previous:{title:"Introduction",permalink:"/copacetic/website/v0.3.x/"},next:{title:"Quick Start",permalink:"/copacetic/website/v0.3.x/quick-start"}},l={},p=[{value:"Homebrew",id:"homebrew",level:2},{value:"GitHub",id:"github",level:2},{value:"Development Setup",id:"development-setup",level:2}],s={toc:p};function u(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h2",{id:"homebrew"},"Homebrew"),(0,a.kt)("p",null,"On macOS and Linux, ",(0,a.kt)("inlineCode",{parentName:"p"},"copa")," can be installed via ",(0,a.kt)("a",{parentName:"p",href:"https://brew.sh/"},"Homebrew"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"brew install copa\n")),(0,a.kt)("h2",{id:"github"},"GitHub"),(0,a.kt)("p",null,"You can download the latest and previous versions of ",(0,a.kt)("inlineCode",{parentName:"p"},"copa")," from the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/releases"},"GitHub releases page"),"."),(0,a.kt)("h2",{id:"development-setup"},"Development Setup"),(0,a.kt)("p",null,"The following instructions are for ",(0,a.kt)("strong",{parentName:"p"},"Ubuntu 22.04")," with the dependency versions supported as part of the ",(0,a.kt)("a",{parentName:"p",href:"/copacetic/website/v0.3.x/contributing/#visual-studio-code-development-container"},"dev container")," environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"git clone https://github.com/project-copacetic/copacetic\ncd copacetic\nmake\n# OPTIONAL: install copa to a pathed folder\nsudo mv dist/linux_amd64/release/copa /usr/local/bin/\n")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/b5d5e16b.8d66db9d.js b/website/assets/js/b5d5e16b.8d66db9d.js new file mode 100644 index 00000000..25981e86 --- /dev/null +++ b/website/assets/js/b5d5e16b.8d66db9d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7231],{137:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>r,default:()=>p,frontMatter:()=>s,metadata:()=>a,toc:()=>l});var o=n(4848),i=n(8453);const s={title:"Installation"},r=void 0,a={id:"installation",title:"Installation",description:"Homebrew",source:"@site/versioned_docs/version-v0.3.x/installation.md",sourceDirName:".",slug:"/installation",permalink:"/copacetic/website/v0.3.x/installation",draft:!1,unlisted:!1,tags:[],version:"v0.3.x",frontMatter:{title:"Installation"},sidebar:"sidebar",previous:{title:"Introduction",permalink:"/copacetic/website/v0.3.x/"},next:{title:"Quick Start",permalink:"/copacetic/website/v0.3.x/quick-start"}},c={},l=[{value:"Homebrew",id:"homebrew",level:2},{value:"GitHub",id:"github",level:2},{value:"Development Setup",id:"development-setup",level:2}];function d(e){const t={a:"a",code:"code",h2:"h2",p:"p",pre:"pre",strong:"strong",...(0,i.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(t.h2,{id:"homebrew",children:"Homebrew"}),"\n",(0,o.jsxs)(t.p,{children:["On macOS and Linux, ",(0,o.jsx)(t.code,{children:"copa"})," can be installed via ",(0,o.jsx)(t.a,{href:"https://brew.sh/",children:"Homebrew"}),":"]}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-bash",children:"brew install copa\n"})}),"\n",(0,o.jsx)(t.h2,{id:"github",children:"GitHub"}),"\n",(0,o.jsxs)(t.p,{children:["You can download the latest and previous versions of ",(0,o.jsx)(t.code,{children:"copa"})," from the ",(0,o.jsx)(t.a,{href:"https://github.com/project-copacetic/copacetic/releases",children:"GitHub releases page"}),"."]}),"\n",(0,o.jsx)(t.h2,{id:"development-setup",children:"Development Setup"}),"\n",(0,o.jsxs)(t.p,{children:["The following instructions are for ",(0,o.jsx)(t.strong,{children:"Ubuntu 22.04"})," with the dependency versions supported as part of the ",(0,o.jsx)(t.a,{href:"./contributing.md/#visual-studio-code-development-container",children:"dev container"})," environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead."]}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-bash",children:"git clone https://github.com/project-copacetic/copacetic\ncd copacetic\nmake\n# OPTIONAL: install copa to a pathed folder\nsudo mv dist/linux_amd64/release/copa /usr/local/bin/\n"})})]})}function p(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,o.jsx)(t,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>r,x:()=>a});var o=n(6540);const i={},s=o.createContext(i);function r(e){const t=o.useContext(s);return o.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function a(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:r(e.components),o.createElement(s.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/b9421f89.2697740e.js b/website/assets/js/b9421f89.2697740e.js deleted file mode 100644 index 6d8c387c..00000000 --- a/website/assets/js/b9421f89.2697740e.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7114],{3905:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>d});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function c(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=a.createContext({}),p=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},s=function(e){var t=p(e.components);return a.createElement(l.Provider,{value:t},e.children)},u="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},h=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,l=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),u=p(n),h=i,d=u["".concat(l,".").concat(h)]||u[h]||f[h]||r;return n?a.createElement(d,o(o({ref:t},s),{},{components:n})):a.createElement(d,o({ref:t},s))}));function d(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=h;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[u]="string"==typeof e?e:i,o[1]=c;for(var p=2;p<r;p++)o[p]=n[p];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}h.displayName="MDXCreateElement"},550:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>c,toc:()=>p});var a=n(7462),i=(n(7294),n(3905));const r={title:"FAQ"},o=void 0,c={unversionedId:"faq",id:"version-v0.4.x/faq",title:"FAQ",description:"What kind of vulnerabilities can Copa patch?",source:"@site/versioned_docs/version-v0.4.x/faq.md",sourceDirName:".",slug:"/faq",permalink:"/copacetic/website/v0.4.x/faq",draft:!1,tags:[],version:"v0.4.x",frontMatter:{title:"FAQ"},sidebar:"sidebar",previous:{title:"Design",permalink:"/copacetic/website/v0.4.x/design"},next:{title:"Contributing",permalink:"/copacetic/website/v0.4.x/contributing"}},l={},p=[{value:"What kind of vulnerabilities can Copa patch?",id:"what-kind-of-vulnerabilities-can-copa-patch",level:2},{value:"What kind of vulnerabilities can Copa not patch?",id:"what-kind-of-vulnerabilities-can-copa-not-patch",level:2}],s={toc:p};function u(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h2",{id:"what-kind-of-vulnerabilities-can-copa-patch"},"What kind of vulnerabilities can Copa patch?"),(0,i.kt)("p",null,'Copa is capable of patching "OS level" vulnerabilities. This includes packages (like ',(0,i.kt)("inlineCode",{parentName:"p"},"openssl"),") in the image that are managed by a package manager such as ",(0,i.kt)("inlineCode",{parentName:"p"},"apt")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"yum"),'. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see ',(0,i.kt)("a",{parentName:"p",href:"#what-kind-of-vulnerabilities-can-copa-not-patch"},"below")," for more details)."),(0,i.kt)("h2",{id:"what-kind-of-vulnerabilities-can-copa-not-patch"},"What kind of vulnerabilities can Copa not patch?"),(0,i.kt)("p",null,'Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the ',(0,i.kt)("inlineCode",{parentName:"p"},"golang.org/x/net")," module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level."),(0,i.kt)("p",null,"To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like ",(0,i.kt)("inlineCode",{parentName:"p"},"http://archive.ubuntu.com/ubuntu/")," for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/b9421f89.6ce1c578.js b/website/assets/js/b9421f89.6ce1c578.js new file mode 100644 index 00000000..5a9107d6 --- /dev/null +++ b/website/assets/js/b9421f89.6ce1c578.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3978],{7192:(e,a,t)=>{t.r(a),t.d(a,{assets:()=>l,contentTitle:()=>c,default:()=>h,frontMatter:()=>o,metadata:()=>s,toc:()=>r});var i=t(4848),n=t(8453);const o={title:"FAQ"},c=void 0,s={id:"faq",title:"FAQ",description:"What kind of vulnerabilities can Copa patch?",source:"@site/versioned_docs/version-v0.4.x/faq.md",sourceDirName:".",slug:"/faq",permalink:"/copacetic/website/v0.4.x/faq",draft:!1,unlisted:!1,tags:[],version:"v0.4.x",frontMatter:{title:"FAQ"},sidebar:"sidebar",previous:{title:"Design",permalink:"/copacetic/website/v0.4.x/design"},next:{title:"Contributing",permalink:"/copacetic/website/v0.4.x/contributing"}},l={},r=[{value:"What kind of vulnerabilities can Copa patch?",id:"what-kind-of-vulnerabilities-can-copa-patch",level:2},{value:"What kind of vulnerabilities can Copa not patch?",id:"what-kind-of-vulnerabilities-can-copa-not-patch",level:2}];function p(e){const a={a:"a",code:"code",h2:"h2",p:"p",...(0,n.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(a.h2,{id:"what-kind-of-vulnerabilities-can-copa-patch",children:"What kind of vulnerabilities can Copa patch?"}),"\n",(0,i.jsxs)(a.p,{children:['Copa is capable of patching "OS level" vulnerabilities. This includes packages (like ',(0,i.jsx)(a.code,{children:"openssl"}),") in the image that are managed by a package manager such as ",(0,i.jsx)(a.code,{children:"apt"})," or ",(0,i.jsx)(a.code,{children:"yum"}),'. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see ',(0,i.jsx)(a.a,{href:"#what-kind-of-vulnerabilities-can-copa-not-patch",children:"below"})," for more details)."]}),"\n",(0,i.jsx)(a.h2,{id:"what-kind-of-vulnerabilities-can-copa-not-patch",children:"What kind of vulnerabilities can Copa not patch?"}),"\n",(0,i.jsxs)(a.p,{children:['Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the ',(0,i.jsx)(a.code,{children:"golang.org/x/net"})," module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level."]}),"\n",(0,i.jsxs)(a.p,{children:["To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like ",(0,i.jsx)(a.code,{children:"http://archive.ubuntu.com/ubuntu/"})," for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications."]})]})}function h(e={}){const{wrapper:a}={...(0,n.R)(),...e.components};return a?(0,i.jsx)(a,{...e,children:(0,i.jsx)(p,{...e})}):p(e)}},8453:(e,a,t)=>{t.d(a,{R:()=>c,x:()=>s});var i=t(6540);const n={},o=i.createContext(n);function c(e){const a=i.useContext(o);return i.useMemo((function(){return"function"==typeof e?e(a):{...a,...e}}),[a,e])}function s(e){let a;return a=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:c(e.components),i.createElement(o.Provider,{value:a},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/baa9408d.07d2dc63.js b/website/assets/js/baa9408d.07d2dc63.js new file mode 100644 index 00000000..da4c0c7e --- /dev/null +++ b/website/assets/js/baa9408d.07d2dc63.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9222],{7750:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>s,default:()=>h,frontMatter:()=>o,metadata:()=>r,toc:()=>c});var i=n(4848),a=n(8453);const o={},s="Development and Testing Tips",r={id:"development-tips",title:"Development and Testing Tips",description:"This document provides some tips and tricks for devs to better understand what is happening under the hood of copa.",source:"@site/docs/development-tips.md",sourceDirName:".",slug:"/development-tips",permalink:"/copacetic/website/next/development-tips",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{},sidebar:"sidebar",previous:{title:"Design",permalink:"/copacetic/website/next/design"},next:{title:"Maintainer Guidelines",permalink:"/copacetic/website/next/maintainer-guidelines"}},d={},c=[{value:"Use the <code>--debug</code> flag with <code>copa patch</code>",id:"use-the---debug-flag-with-copa-patch",level:2},{value:"Verify the intermediate stages of building a patched image",id:"verify-the-intermediate-stages-of-building-a-patched-image",level:2},{value:"Inspect a Docker image",id:"inspect-a-docker-image",level:2},{value:"Use <code>docker</code> to inspect the metadata of the image",id:"use-docker-to-inspect-the-metadata-of-the-image",level:3},{value:"Use <code>dive</code> to inspect the filesystem differences at each layer of the image",id:"use-dive-to-inspect-the-filesystem-differences-at-each-layer-of-the-image",level:3},{value:"Extract individual files from the image to inspect them",id:"extract-individual-files-from-the-image-to-inspect-them",level:3},{value:"Use <code>crane</code> to manipulate the image or extract the image filesystem",id:"use-crane-to-manipulate-the-image-or-extract-the-image-filesystem",level:3},{value:"Run scripts interactively in an image",id:"run-scripts-interactively-in-an-image",level:2},{value:"Dump the LLB Graph",id:"dump-the-llb-graph",level:2}];function l(e){const t={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",ul:"ul",...(0,a.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(t.h1,{id:"development-and-testing-tips",children:"Development and Testing Tips"}),"\n",(0,i.jsxs)(t.p,{children:["This document provides some tips and tricks for devs to better understand what is happening under the hood of ",(0,i.jsx)(t.code,{children:"copa"}),"."]}),"\n",(0,i.jsxs)(t.p,{children:["Much of the functionality of ",(0,i.jsx)(t.code,{children:"copa"})," is implemented through the use of the ",(0,i.jsx)(t.a,{href:"https://docs.docker.com/build/buildkit/",children:"BuildKit"})," library, and in particular, direct building a ",(0,i.jsx)(t.a,{href:"https://github.com/moby/buildkit#exploring-llb",children:"Low-Level Build (LLB)"})," intermediate representation. Most patching operations are implemented as a series of LLB stages that form a Directed Acyclic Graph (DAG) to produce the final patched image, and we'll walk through some ways to deal with the opaque nature of each operation in that graph which can otherwise make it difficult to debug or test ",(0,i.jsx)(t.code,{children:"copa"}),"."]}),"\n",(0,i.jsxs)(t.h2,{id:"use-the---debug-flag-with-copa-patch",children:["Use the ",(0,i.jsx)(t.code,{children:"--debug"})," flag with ",(0,i.jsx)(t.code,{children:"copa patch"})]}),"\n",(0,i.jsxs)(t.p,{children:["It's always useful to know that code on the ",(0,i.jsx)(t.code,{children:"copa"})," side is behaving as expected first before diving into the weeds of its interactions with BuildKit. The ",(0,i.jsx)(t.code,{children:"--debug"})," flag will do two useful things when enabled:"]}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:["Log debug state to stdout with the ",(0,i.jsx)(t.code,{children:"DEBU"})," tag, including useful information such as the type of image it expects to be operating on, the list of updates and their versions it expects to apply, and any detailed errors."]}),"\n",(0,i.jsxs)(t.li,{children:["Leave the working folder in place so that you can inspect the contents of the working files ",(0,i.jsx)(t.code,{children:"copa"})," writes for its own use during the patching process."]}),"\n"]}),"\n",(0,i.jsxs)(t.p,{children:["For example, if you run ",(0,i.jsx)(t.code,{children:"copa patch"})," with the ",(0,i.jsx)(t.code,{children:"--debug"})," flag, you'll see something like the following output:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:"$ copa patch -i <image> -r <report> --debug\nDEBU[0000] updates to apply: ...\n...\nWARN[0000] --debug specified, working folder at /var/folders/fx/nbhd5jln1qq3t405hz_hl4000000gn/T/copa-806164554 needs to be manually cleaned up \n"})}),"\n",(0,i.jsxs)(t.p,{children:["The folder specified defaults to the system temp folder unless the ",(0,i.jsx)(t.code,{children:"--working-folder"})," option was specified, and you can delete it with ",(0,i.jsx)(t.code,{children:"rm -r <folder>"})," when you're done. The working folder will usually contain the ",(0,i.jsx)(t.code,{children:"copa-out"})," directory which contains files depending on the ",(0,i.jsx)(t.code,{children:"pkgmgr"})," implementation, such as the probed package state or post-patching package state file for the package manager. Searching for ",(0,i.jsx)(t.code,{children:"SolveToLocal()"})," invocations in the ",(0,i.jsx)(t.code,{children:"copa"})," codebase will show you where these files are written."]}),"\n",(0,i.jsx)(t.h2,{id:"verify-the-intermediate-stages-of-building-a-patched-image",children:"Verify the intermediate stages of building a patched image"}),"\n",(0,i.jsxs)(t.p,{children:["It's often useful to be able to inspect what the output of an intermediate LLB stage would look like after it has executed, and you can perform an analog to ",(0,i.jsx)(t.code,{children:"printf"})," debugging by solving the LLB stage to a Docker image and then inspecting the resulting image:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:'// DEBUG: Solve the LLB stage to a Docker image.\nif err := buildkit.SolveToDocker(ctx, dm.config.Client, &<llb.Stage>, dm.config.ConfigData, dm.config.ImageName+"-<llb.Stage suffix>"); err != nil {\n return nil, err\n}\n'})}),"\n",(0,i.jsxs)(t.p,{children:["For example, if you want to see what the resulting Docker image looks like at the ",(0,i.jsx)(t.code,{children:"busyBoxApplied"})," stage, you can add the ",(0,i.jsx)(t.code,{children:"buildkit.SolveToDocker"})," call to the end of the ",(0,i.jsx)(t.code,{children:"busyBoxApplied"})," stage as follows. The result will be a Docker image with the ",(0,i.jsx)(t.code,{children:"-busyBoxApplied"})," suffix to the tag that you can inspect with the ",(0,i.jsx)(t.code,{children:"docker"})," CLI or ",(0,i.jsx)(t.code,{children:"dive"})," tool:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:'busyBoxApplied := dm.config.ImageState.File(llb.Copy(toolImage, "/bin/busybox", "/bin/busybox"))\nif err := buildkit.SolveToDocker(ctx, dm.config.Client, &busyBoxApplied, dm.config.ConfigData, dm.config.ImageName+"-busyBoxApplied"); err != nil {\n return nil, err\n}\n'})}),"\n",(0,i.jsx)(t.h2,{id:"inspect-a-docker-image",children:"Inspect a Docker image"}),"\n",(0,i.jsxs)(t.h3,{id:"use-docker-to-inspect-the-metadata-of-the-image",children:["Use ",(0,i.jsx)(t.code,{children:"docker"})," to inspect the metadata of the image"]}),"\n",(0,i.jsxs)(t.p,{children:["For a quick check of a Docker image, the built in ",(0,i.jsx)(t.code,{children:"docker"})," CLI commands can be useful:"]}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.a,{href:"https://docs.docker.com/engine/reference/commandline/inspect/",children:(0,i.jsx)(t.code,{children:"docker inspect"})})," can show you the metadata for the image and verifying that the patching process generally preserves the metadata of the original image."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.a,{href:"https://docs.docker.com/engine/reference/commandline/history/",children:(0,i.jsx)(t.code,{children:"docker history"})})," can give you a quick overview of the layers in the image, and with the ",(0,i.jsx)(t.code,{children:"--no-trunc"})," flag, can provide the commands that were run to create each layer."]}),"\n"]}),"\n",(0,i.jsxs)(t.h3,{id:"use-dive-to-inspect-the-filesystem-differences-at-each-layer-of-the-image",children:["Use ",(0,i.jsx)(t.code,{children:"dive"})," to inspect the filesystem differences at each layer of the image"]}),"\n",(0,i.jsxs)(t.p,{children:["Instructions for installing and using the ",(0,i.jsx)(t.code,{children:"dive"})," CLI tool are at ",(0,i.jsx)(t.a,{href:"https://github.com/wagoodman/dive",children:"https://github.com/wagoodman/dive"}),"."]}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"dive"})," provides a simple interface for walking the layers of the image and inspecting the files that were added or changed at each layer with your arrow keys."]}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"Tab"})," will toggle between navigating the layers and the files in the layer."]}),"\n",(0,i.jsxs)(t.li,{children:["Filtering out unmodified files with ",(0,i.jsx)(t.code,{children:"Ctrl+U"})," while in files view will effectively show you the file diff introduced by that layer."]}),"\n"]}),"\n",(0,i.jsx)(t.p,{children:"In particular, if you are adding or changing any of the patching functionality, the diff view of the files in the image can be useful to verify that the expected files have actually been written to the target image."}),"\n",(0,i.jsx)(t.h3,{id:"extract-individual-files-from-the-image-to-inspect-them",children:"Extract individual files from the image to inspect them"}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"dive"})," won't let you read the contents of the files in the image though; to do that, you can use the ",(0,i.jsx)(t.code,{children:"docker cp"})," command to copy the files out of the image to a local folder. Note that ",(0,i.jsx)(t.code,{children:"docker cp"})," only works with containers and not just container images, so you will need to create a container from the image and then copy the files out of the container:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:"id=$(docker create <image name>:<tag>)\ndocker cp $id:<filepath> <destination path>\ndocker rm -v $id\n"})}),"\n",(0,i.jsxs)(t.h3,{id:"use-crane-to-manipulate-the-image-or-extract-the-image-filesystem",children:["Use ",(0,i.jsx)(t.code,{children:"crane"})," to manipulate the image or extract the image filesystem"]}),"\n",(0,i.jsxs)(t.p,{children:["Sometimes it's useful to be able to manipulate the image in ways that ",(0,i.jsx)(t.code,{children:"docker"})," or ",(0,i.jsx)(t.code,{children:"dive"})," don't support, such as extracting the entire image filesystem to a local folder. ",(0,i.jsx)(t.a,{href:"https://github.com/google/go-containerregistry/tree/main/cmd/crane",children:(0,i.jsx)(t.code,{children:"crane"})})," can be useful for this and also provides ",(0,i.jsx)(t.a,{href:"https://github.com/google/go-containerregistry/blob/main/cmd/crane/doc/crane.md",children:"many other convenient utilities"})," for working with container images."]}),"\n",(0,i.jsxs)(t.p,{children:["For instance, to extract the filesystem of the image to a local folder, you can use ",(0,i.jsx)(t.code,{children:"crane export"}),":"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:"crane export <image name>:<tag> - | tar -xvf -\n"})}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"crane"})," is a very flexible tool designed to work well with pipes to existing shell tools. For example, you can also use ",(0,i.jsx)(t.code,{children:"crane"})," to do a full diff between two images as well:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:" diff \\\n <(crane export image:tag - | tar -tvf - | sort) \\\n <(crane export image:tag-patched - | tar -tvf - | sort)\n"})}),"\n",(0,i.jsx)(t.h2,{id:"run-scripts-interactively-in-an-image",children:"Run scripts interactively in an image"}),"\n",(0,i.jsxs)(t.p,{children:["Some of the LLB stages effectively run shell scripts defined by ",(0,i.jsx)(t.code,{children:"copa"})," in the image, and sometimes these need to be debugged or modified. The easiest way to do this is usually just solving the ",(0,i.jsx)(t.code,{children:"llb.Stage"})," of interest to Docker and then running the image interactively with ",(0,i.jsx)(t.code,{children:"docker run"}),":"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:"docker run --rm -it --entrypoint sh <image name>:<tag>-<llb.Stage suffix>\n"})}),"\n",(0,i.jsxs)(t.p,{children:["One thing to note is that the scripts embedded in the ",(0,i.jsx)(t.code,{children:".go"})," files will often have an additional layer of character escapes to make them valid Go strings, so you may need to unescape them before running them interactively."]}),"\n",(0,i.jsx)(t.h2,{id:"dump-the-llb-graph",children:"Dump the LLB Graph"}),"\n",(0,i.jsxs)(t.p,{children:["Ultimately, ",(0,i.jsx)(t.code,{children:"copa"})," is just a tool for building a BuildKit LLB graph, and you may need to understand if the LLB graph being constructed is reasonable or expected. It's helpful to have a basic understanding of how BuildKit and the operations commonly used by ",(0,i.jsx)(t.code,{children:"copa"})," here, so it's good to be familiar with some key resources here:"]}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://pkg.go.dev/github.com/moby/buildkit#section-readme",children:"BuildKit pkg.go.dev README"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://github.com/moby/buildkit/tree/master/docs/dev",children:"BuildKit Developer Docs"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://www.docker.com/blog/mergediff-building-dags-more-efficiently-and-elegantly/",children:"Merge+Diff: Building DAGs More Efficiently and Elegantly"})}),"\n"]}),"\n",(0,i.jsxs)(t.p,{children:["The LLB graph up to any ",(0,i.jsx)(t.code,{children:"llb.Stage"})," can be written out by marshalling it to a ",(0,i.jsx)(t.code,{children:"Definition"})," and enumerating each of the operations to output. Using the ",(0,i.jsx)(t.code,{children:"buildctl"})," implementation of ",(0,i.jsx)(t.a,{href:"https://github.com/moby/buildkit/blob/master/cmd/buildctl/debug/dumpllb.go#L30",children:"dumpLLB"})," as a reference, you can write a function to output the LLB graph as JSON nodes to stdout:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:'import "github.com/moby/buildkit/solver/pb"\n\n// Definition of the LLB graph node to display, modify as desired.\n// This version is what the buildctl tool uses.\ntype llbOp struct {\n Op pb.Op\n Digest digest.Digest\n OpMetadata pb.OpMetadata\n}\n\nfunc outputLLBGraph(ctx context.Context, llbState *llb.State) error {\n // Marshal the llb.State to a LLB definition.\n def, err := llbState.Marshal(ctx)\n if err != nil {\n log.Errorf("Marshal to LLB failed with %s", err)\n return err\n }\n\n // Format each operation node in the LLB definition into a struct.\n var ops []llbOp\n for _, dt := range def.Def {\n var op pb.Op\n if err := (&op).Unmarshal(dt); err != nil {\n return errors.Wrap(err, "failed to parse op")\n }\n hash := digest.FromBytes(dt)\n ent := llbOp{Op: op, Digest: hash, OpMetadata: def.Metadata[hash]}\n ops = append(ops, ent)\n }\n\n // Output the LLB graph as JSON nodes to stdout.\n // Modify as desired to output to file or other formats.\n enc := json.NewEncoder(os.Stdout)\n for _, op := range ops {\n if err := enc.Encode(op); err != nil {\n return err\n }\n }\n return nil\n}\n\n// Within the function (e.g. with the llb.State where you want to dump the LLB graph:\n...\n // DEBUG: dump the LLB graph to stdout\n if err := outputLLBGraph(ctx, &merged); err != nil {\n return nil, err\n }\n...\n'})}),"\n",(0,i.jsxs)(t.p,{children:["For the definition of an LLB vertex (an ",(0,i.jsx)(t.code,{children:"Op"})," node struct enumerated by the code snippet above), refer to ",(0,i.jsx)(t.a,{href:"https://github.com/moby/buildkit/blob/master/solver/pb/ops.proto",children:"https://github.com/moby/buildkit/blob/master/solver/pb/ops.proto"}),"."]}),"\n",(0,i.jsxs)(t.p,{children:["Following the edges between the LLB nodes is a matter of following the resulting ",(0,i.jsx)(t.code,{children:"Digest"})," value for the node to where it is consumed as one of the ",(0,i.jsx)(t.code,{children:"Op.inputs"})," in another node. For example, a pretty-printed version of a LLB graph in json format focusing on a few key nodes might look like:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",children:'// Initial target image source node\n{\n "Op": {\n "Op": {\n "source": {\n "identifier": "docker-image://mcr.microsoft.com/oss/open-policy-agent/opa:0.46.0"\n }\n },\n "platform": {\n "Architecture": "amd64",\n "OS": "linux"\n },\n "constraints": {}\n },\n "Digest": "sha256:a86ddb9065d07c67dc838e11a81ff54020531c4ca2d85fb20574088222da8b30",\n "OpMetadata": {\n "caps": {\n "source.image": true\n }\n }\n}\n\n// ..\n// Skipping intermediate graph nodes\n// ...\n\n// Diffing out the manifest updates layer\n{\n "Op": {\n "inputs": [\n {\n "digest": "sha256:cbc31a96266caa8cd5ced38a1f8e97de9f13fafb23dbe9e342125569cd4d5018",\n "index": 0\n },\n {\n "digest": "sha256:9f798b2e38e054aadf1ee66c7eb7230c65be324c26d8739a3d5fa2d5da90e5de",\n "index": 0\n }\n ],\n "Op": {\n "diff": {\n "lower": {\n "input": 0\n },\n "upper": {\n "input": 1\n }\n }\n },\n "constraints": {}\n },\n "Digest": "sha256:f337f99144ab75fee8593ec6531caa9ebace06aaca07614778b7c0ca5c816135",\n "OpMetadata": {\n "caps": {\n "diffop": true\n }\n }\n}\n\n// Merging all the target image with the patch layer and the manifest updates layer\n{\n "Op": {\n "inputs": [\n {\n "digest": "sha256:a86ddb9065d07c67dc838e11a81ff54020531c4ca2d85fb20574088222da8b30",\n "index": 0\n },\n {\n "digest": "sha256:1c3ad84c0de7e1384d727f6168db3f1f8fb632c0086760aff1786a7e89562d13",\n "index": 0\n },\n {\n "digest": "sha256:f337f99144ab75fee8593ec6531caa9ebace06aaca07614778b7c0ca5c816135",\n "index": 0\n }\n ],\n "Op": {\n "merge": {\n "inputs": [\n {\n "input": 0\n },\n {\n "input": 1\n },\n {\n "input": 2\n }\n ]\n }\n },\n "constraints": {}\n },\n "Digest": "sha256:e6a4086e2caf03c8814fc5388dd7d2e45420e1c5281e0df0d3db375d3f00358a",\n "OpMetadata": {\n "caps": {\n "mergeop": true\n }\n }\n}\n\n//...\n\n'})})]})}function h(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,i.jsx)(t,{...e,children:(0,i.jsx)(l,{...e})}):l(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>s,x:()=>r});var i=n(6540);const a={},o=i.createContext(a);function s(e){const t=i.useContext(o);return i.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function r(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:s(e.components),i.createElement(o.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/baa9408d.c3109501.js b/website/assets/js/baa9408d.c3109501.js deleted file mode 100644 index 7eb869cc..00000000 --- a/website/assets/js/baa9408d.c3109501.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6571],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>m});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function r(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},o=Object.keys(e);for(a=0;a<o.length;a++)n=o[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a<o.length;a++)n=o[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var p=a.createContext({}),s=function(e){var t=a.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},d=function(e){var t=s(e.components);return a.createElement(p.Provider,{value:t},e.children)},c="mdxType",h={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,p=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),c=s(n),u=i,m=c["".concat(p,".").concat(u)]||c[u]||h[u]||o;return n?a.createElement(m,r(r({ref:t},d),{},{components:n})):a.createElement(m,r({ref:t},d))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,r=new Array(o);r[0]=u;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l[c]="string"==typeof e?e:i,r[1]=l;for(var s=2;s<o;s++)r[s]=n[s];return a.createElement.apply(null,r)}return a.createElement.apply(null,n)}u.displayName="MDXCreateElement"},6118:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>r,default:()=>c,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var a=n(7462),i=(n(7294),n(3905));const o={},r="Development and Testing Tips",l={unversionedId:"development-tips",id:"development-tips",title:"Development and Testing Tips",description:"This document provides some tips and tricks for devs to better understand what is happening under the hood of copa.",source:"@site/docs/development-tips.md",sourceDirName:".",slug:"/development-tips",permalink:"/copacetic/website/next/development-tips",draft:!1,tags:[],version:"current",frontMatter:{},sidebar:"sidebar",previous:{title:"Design",permalink:"/copacetic/website/next/design"},next:{title:"Maintainer Guidelines",permalink:"/copacetic/website/next/maintainer-guidelines"}},p={},s=[{value:"Use the <code>--debug</code> flag with <code>copa patch</code>",id:"use-the---debug-flag-with-copa-patch",level:2},{value:"Verify the intermediate stages of building a patched image",id:"verify-the-intermediate-stages-of-building-a-patched-image",level:2},{value:"Inspect a Docker image",id:"inspect-a-docker-image",level:2},{value:"Use <code>docker</code> to inspect the metadata of the image",id:"use-docker-to-inspect-the-metadata-of-the-image",level:3},{value:"Use <code>dive</code> to inspect the filesystem differences at each layer of the image",id:"use-dive-to-inspect-the-filesystem-differences-at-each-layer-of-the-image",level:3},{value:"Extract individual files from the image to inspect them",id:"extract-individual-files-from-the-image-to-inspect-them",level:3},{value:"Use <code>crane</code> to manipulate the image or extract the image filesystem",id:"use-crane-to-manipulate-the-image-or-extract-the-image-filesystem",level:3},{value:"Run scripts interactively in an image",id:"run-scripts-interactively-in-an-image",level:2},{value:"Dump the LLB Graph",id:"dump-the-llb-graph",level:2}],d={toc:s};function c(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"development-and-testing-tips"},"Development and Testing Tips"),(0,i.kt)("p",null,"This document provides some tips and tricks for devs to better understand what is happening under the hood of ",(0,i.kt)("inlineCode",{parentName:"p"},"copa"),"."),(0,i.kt)("p",null,"Much of the functionality of ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," is implemented through the use of the ",(0,i.kt)("a",{parentName:"p",href:"https://docs.docker.com/build/buildkit/"},"BuildKit")," library, and in particular, direct building a ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit#exploring-llb"},"Low-Level Build (LLB)")," intermediate representation. Most patching operations are implemented as a series of LLB stages that form a Directed Acyclic Graph (DAG) to produce the final patched image, and we'll walk through some ways to deal with the opaque nature of each operation in that graph which can otherwise make it difficult to debug or test ",(0,i.kt)("inlineCode",{parentName:"p"},"copa"),"."),(0,i.kt)("h2",{id:"use-the---debug-flag-with-copa-patch"},"Use the ",(0,i.kt)("inlineCode",{parentName:"h2"},"--debug")," flag with ",(0,i.kt)("inlineCode",{parentName:"h2"},"copa patch")),(0,i.kt)("p",null,"It's always useful to know that code on the ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," side is behaving as expected first before diving into the weeds of its interactions with BuildKit. The ",(0,i.kt)("inlineCode",{parentName:"p"},"--debug")," flag will do two useful things when enabled:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Log debug state to stdout with the ",(0,i.kt)("inlineCode",{parentName:"li"},"DEBU")," tag, including useful information such as the type of image it expects to be operating on, the list of updates and their versions it expects to apply, and any detailed errors."),(0,i.kt)("li",{parentName:"ul"},"Leave the working folder in place so that you can inspect the contents of the working files ",(0,i.kt)("inlineCode",{parentName:"li"},"copa")," writes for its own use during the patching process.")),(0,i.kt)("p",null,"For example, if you run ",(0,i.kt)("inlineCode",{parentName:"p"},"copa patch")," with the ",(0,i.kt)("inlineCode",{parentName:"p"},"--debug")," flag, you'll see something like the following output:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"$ copa patch -i <image> -r <report> --debug\nDEBU[0000] updates to apply: ...\n...\nWARN[0000] --debug specified, working folder at /var/folders/fx/nbhd5jln1qq3t405hz_hl4000000gn/T/copa-806164554 needs to be manually cleaned up \n")),(0,i.kt)("p",null,"The folder specified defaults to the system temp folder unless the ",(0,i.kt)("inlineCode",{parentName:"p"},"--working-folder")," option was specified, and you can delete it with ",(0,i.kt)("inlineCode",{parentName:"p"},"rm -r <folder>")," when you're done. The working folder will usually contain the ",(0,i.kt)("inlineCode",{parentName:"p"},"copa-out")," directory which contains files depending on the ",(0,i.kt)("inlineCode",{parentName:"p"},"pkgmgr")," implementation, such as the probed package state or post-patching package state file for the package manager. Searching for ",(0,i.kt)("inlineCode",{parentName:"p"},"SolveToLocal()")," invocations in the ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," codebase will show you where these files are written."),(0,i.kt)("h2",{id:"verify-the-intermediate-stages-of-building-a-patched-image"},"Verify the intermediate stages of building a patched image"),(0,i.kt)("p",null,"It's often useful to be able to inspect what the output of an intermediate LLB stage would look like after it has executed, and you can perform an analog to ",(0,i.kt)("inlineCode",{parentName:"p"},"printf")," debugging by solving the LLB stage to a Docker image and then inspecting the resulting image:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-go"},'// DEBUG: Solve the LLB stage to a Docker image.\nif err := buildkit.SolveToDocker(ctx, dm.config.Client, &<llb.Stage>, dm.config.ConfigData, dm.config.ImageName+"-<llb.Stage suffix>"); err != nil {\n return nil, err\n}\n')),(0,i.kt)("p",null,"For example, if you want to see what the resulting Docker image looks like at the ",(0,i.kt)("inlineCode",{parentName:"p"},"busyBoxApplied")," stage, you can add the ",(0,i.kt)("inlineCode",{parentName:"p"},"buildkit.SolveToDocker")," call to the end of the ",(0,i.kt)("inlineCode",{parentName:"p"},"busyBoxApplied")," stage as follows. The result will be a Docker image with the ",(0,i.kt)("inlineCode",{parentName:"p"},"-busyBoxApplied")," suffix to the tag that you can inspect with the ",(0,i.kt)("inlineCode",{parentName:"p"},"docker")," CLI or ",(0,i.kt)("inlineCode",{parentName:"p"},"dive")," tool:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-go"},'busyBoxApplied := dm.config.ImageState.File(llb.Copy(toolImage, "/bin/busybox", "/bin/busybox"))\nif err := buildkit.SolveToDocker(ctx, dm.config.Client, &busyBoxApplied, dm.config.ConfigData, dm.config.ImageName+"-busyBoxApplied"); err != nil {\n return nil, err\n}\n')),(0,i.kt)("h2",{id:"inspect-a-docker-image"},"Inspect a Docker image"),(0,i.kt)("h3",{id:"use-docker-to-inspect-the-metadata-of-the-image"},"Use ",(0,i.kt)("inlineCode",{parentName:"h3"},"docker")," to inspect the metadata of the image"),(0,i.kt)("p",null,"For a quick check of a Docker image, the built in ",(0,i.kt)("inlineCode",{parentName:"p"},"docker")," CLI commands can be useful:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://docs.docker.com/engine/reference/commandline/inspect/"},(0,i.kt)("inlineCode",{parentName:"a"},"docker inspect"))," can show you the metadata for the image and verifying that the patching process generally preserves the metadata of the original image."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://docs.docker.com/engine/reference/commandline/history/"},(0,i.kt)("inlineCode",{parentName:"a"},"docker history"))," can give you a quick overview of the layers in the image, and with the ",(0,i.kt)("inlineCode",{parentName:"li"},"--no-trunc")," flag, can provide the commands that were run to create each layer.")),(0,i.kt)("h3",{id:"use-dive-to-inspect-the-filesystem-differences-at-each-layer-of-the-image"},"Use ",(0,i.kt)("inlineCode",{parentName:"h3"},"dive")," to inspect the filesystem differences at each layer of the image"),(0,i.kt)("p",null,"Instructions for installing and using the ",(0,i.kt)("inlineCode",{parentName:"p"},"dive")," CLI tool are at ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/wagoodman/dive"},"https://github.com/wagoodman/dive"),"."),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"dive")," provides a simple interface for walking the layers of the image and inspecting the files that were added or changed at each layer with your arrow keys."),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"Tab")," will toggle between navigating the layers and the files in the layer."),(0,i.kt)("li",{parentName:"ul"},"Filtering out unmodified files with ",(0,i.kt)("inlineCode",{parentName:"li"},"Ctrl+U")," while in files view will effectively show you the file diff introduced by that layer.")),(0,i.kt)("p",null,"In particular, if you are adding or changing any of the patching functionality, the diff view of the files in the image can be useful to verify that the expected files have actually been written to the target image."),(0,i.kt)("h3",{id:"extract-individual-files-from-the-image-to-inspect-them"},"Extract individual files from the image to inspect them"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"dive")," won't let you read the contents of the files in the image though; to do that, you can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"docker cp")," command to copy the files out of the image to a local folder. Note that ",(0,i.kt)("inlineCode",{parentName:"p"},"docker cp")," only works with containers and not just container images, so you will need to create a container from the image and then copy the files out of the container:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"id=$(docker create <image name>:<tag>)\ndocker cp $id:<filepath> <destination path>\ndocker rm -v $id\n")),(0,i.kt)("h3",{id:"use-crane-to-manipulate-the-image-or-extract-the-image-filesystem"},"Use ",(0,i.kt)("inlineCode",{parentName:"h3"},"crane")," to manipulate the image or extract the image filesystem"),(0,i.kt)("p",null,"Sometimes it's useful to be able to manipulate the image in ways that ",(0,i.kt)("inlineCode",{parentName:"p"},"docker")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"dive")," don't support, such as extracting the entire image filesystem to a local folder. ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/google/go-containerregistry/tree/main/cmd/crane"},(0,i.kt)("inlineCode",{parentName:"a"},"crane"))," can be useful for this and also provides ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/google/go-containerregistry/blob/main/cmd/crane/doc/crane.md"},"many other convenient utilities")," for working with container images."),(0,i.kt)("p",null,"For instance, to extract the filesystem of the image to a local folder, you can use ",(0,i.kt)("inlineCode",{parentName:"p"},"crane export"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"crane export <image name>:<tag> - | tar -xvf -\n")),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"crane")," is a very flexible tool designed to work well with pipes to existing shell tools. For example, you can also use ",(0,i.kt)("inlineCode",{parentName:"p"},"crane")," to do a full diff between two images as well:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"}," diff \\\n <(crane export image:tag - | tar -tvf - | sort) \\\n <(crane export image:tag-patched - | tar -tvf - | sort)\n")),(0,i.kt)("h2",{id:"run-scripts-interactively-in-an-image"},"Run scripts interactively in an image"),(0,i.kt)("p",null,"Some of the LLB stages effectively run shell scripts defined by ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," in the image, and sometimes these need to be debugged or modified. The easiest way to do this is usually just solving the ",(0,i.kt)("inlineCode",{parentName:"p"},"llb.Stage")," of interest to Docker and then running the image interactively with ",(0,i.kt)("inlineCode",{parentName:"p"},"docker run"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"docker run --rm -it --entrypoint sh <image name>:<tag>-<llb.Stage suffix>\n")),(0,i.kt)("p",null,"One thing to note is that the scripts embedded in the ",(0,i.kt)("inlineCode",{parentName:"p"},".go")," files will often have an additional layer of character escapes to make them valid Go strings, so you may need to unescape them before running them interactively."),(0,i.kt)("h2",{id:"dump-the-llb-graph"},"Dump the LLB Graph"),(0,i.kt)("p",null,"Ultimately, ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," is just a tool for building a BuildKit LLB graph, and you may need to understand if the LLB graph being constructed is reasonable or expected. It's helpful to have a basic understanding of how BuildKit and the operations commonly used by ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," here, so it's good to be familiar with some key resources here:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://pkg.go.dev/github.com/moby/buildkit#section-readme"},"BuildKit pkg.go.dev README")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://github.com/moby/buildkit/tree/master/docs/dev"},"BuildKit Developer Docs")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://www.docker.com/blog/mergediff-building-dags-more-efficiently-and-elegantly/"},"Merge+Diff: Building DAGs More Efficiently and Elegantly"))),(0,i.kt)("p",null,"The LLB graph up to any ",(0,i.kt)("inlineCode",{parentName:"p"},"llb.Stage")," can be written out by marshalling it to a ",(0,i.kt)("inlineCode",{parentName:"p"},"Definition")," and enumerating each of the operations to output. Using the ",(0,i.kt)("inlineCode",{parentName:"p"},"buildctl")," implementation of ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit/blob/master/cmd/buildctl/debug/dumpllb.go#L30"},"dumpLLB")," as a reference, you can write a function to output the LLB graph as JSON nodes to stdout:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-go"},'import "github.com/moby/buildkit/solver/pb"\n\n// Definition of the LLB graph node to display, modify as desired.\n// This version is what the buildctl tool uses.\ntype llbOp struct {\n Op pb.Op\n Digest digest.Digest\n OpMetadata pb.OpMetadata\n}\n\nfunc outputLLBGraph(ctx context.Context, llbState *llb.State) error {\n // Marshal the llb.State to a LLB definition.\n def, err := llbState.Marshal(ctx)\n if err != nil {\n log.Errorf("Marshal to LLB failed with %s", err)\n return err\n }\n\n // Format each operation node in the LLB definition into a struct.\n var ops []llbOp\n for _, dt := range def.Def {\n var op pb.Op\n if err := (&op).Unmarshal(dt); err != nil {\n return errors.Wrap(err, "failed to parse op")\n }\n hash := digest.FromBytes(dt)\n ent := llbOp{Op: op, Digest: hash, OpMetadata: def.Metadata[hash]}\n ops = append(ops, ent)\n }\n\n // Output the LLB graph as JSON nodes to stdout.\n // Modify as desired to output to file or other formats.\n enc := json.NewEncoder(os.Stdout)\n for _, op := range ops {\n if err := enc.Encode(op); err != nil {\n return err\n }\n }\n return nil\n}\n\n// Within the function (e.g. with the llb.State where you want to dump the LLB graph:\n...\n // DEBUG: dump the LLB graph to stdout\n if err := outputLLBGraph(ctx, &merged); err != nil {\n return nil, err\n }\n...\n')),(0,i.kt)("p",null,"For the definition of an LLB vertex (an ",(0,i.kt)("inlineCode",{parentName:"p"},"Op")," node struct enumerated by the code snippet above), refer to ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit/blob/master/solver/pb/ops.proto"},"https://github.com/moby/buildkit/blob/master/solver/pb/ops.proto"),". "),(0,i.kt)("p",null,"Following the edges between the LLB nodes is a matter of following the resulting ",(0,i.kt)("inlineCode",{parentName:"p"},"Digest")," value for the node to where it is consumed as one of the ",(0,i.kt)("inlineCode",{parentName:"p"},"Op.inputs")," in another node. For example, a pretty-printed version of a LLB graph in json format focusing on a few key nodes might look like:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'// Initial target image source node\n{\n "Op": {\n "Op": {\n "source": {\n "identifier": "docker-image://mcr.microsoft.com/oss/open-policy-agent/opa:0.46.0"\n }\n },\n "platform": {\n "Architecture": "amd64",\n "OS": "linux"\n },\n "constraints": {}\n },\n "Digest": "sha256:a86ddb9065d07c67dc838e11a81ff54020531c4ca2d85fb20574088222da8b30",\n "OpMetadata": {\n "caps": {\n "source.image": true\n }\n }\n}\n\n// ..\n// Skipping intermediate graph nodes\n// ...\n\n// Diffing out the manifest updates layer\n{\n "Op": {\n "inputs": [\n {\n "digest": "sha256:cbc31a96266caa8cd5ced38a1f8e97de9f13fafb23dbe9e342125569cd4d5018",\n "index": 0\n },\n {\n "digest": "sha256:9f798b2e38e054aadf1ee66c7eb7230c65be324c26d8739a3d5fa2d5da90e5de",\n "index": 0\n }\n ],\n "Op": {\n "diff": {\n "lower": {\n "input": 0\n },\n "upper": {\n "input": 1\n }\n }\n },\n "constraints": {}\n },\n "Digest": "sha256:f337f99144ab75fee8593ec6531caa9ebace06aaca07614778b7c0ca5c816135",\n "OpMetadata": {\n "caps": {\n "diffop": true\n }\n }\n}\n\n// Merging all the target image with the patch layer and the manifest updates layer\n{\n "Op": {\n "inputs": [\n {\n "digest": "sha256:a86ddb9065d07c67dc838e11a81ff54020531c4ca2d85fb20574088222da8b30",\n "index": 0\n },\n {\n "digest": "sha256:1c3ad84c0de7e1384d727f6168db3f1f8fb632c0086760aff1786a7e89562d13",\n "index": 0\n },\n {\n "digest": "sha256:f337f99144ab75fee8593ec6531caa9ebace06aaca07614778b7c0ca5c816135",\n "index": 0\n }\n ],\n "Op": {\n "merge": {\n "inputs": [\n {\n "input": 0\n },\n {\n "input": 1\n },\n {\n "input": 2\n }\n ]\n }\n },\n "constraints": {}\n },\n "Digest": "sha256:e6a4086e2caf03c8814fc5388dd7d2e45420e1c5281e0df0d3db375d3f00358a",\n "OpMetadata": {\n "caps": {\n "mergeop": true\n }\n }\n}\n\n//...\n\n')))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/bab6f5ab.0b153f31.js b/website/assets/js/bab6f5ab.0b153f31.js new file mode 100644 index 00000000..7b030e9e --- /dev/null +++ b/website/assets/js/bab6f5ab.0b153f31.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9162],{2363:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>l});var o=i(4848),t=i(8453);const r={title:"Code of Conduct"},a="Contributor Covenant Code of Conduct",s={id:"code-of-conduct",title:"Code of Conduct",description:"Our Pledge",source:"@site/versioned_docs/version-v0.1.x/code-of-conduct.md",sourceDirName:".",slug:"/code-of-conduct",permalink:"/copacetic/website/v0.1.x/code-of-conduct",draft:!1,unlisted:!1,tags:[],version:"v0.1.x",frontMatter:{title:"Code of Conduct"},sidebar:"sidebar",previous:{title:"Contributing",permalink:"/copacetic/website/v0.1.x/contributing"}},c={},l=[{value:"Our Pledge",id:"our-pledge",level:2},{value:"Our Standards",id:"our-standards",level:2},{value:"Enforcement Responsibilities",id:"enforcement-responsibilities",level:2},{value:"Scope",id:"scope",level:2},{value:"Enforcement",id:"enforcement",level:2},{value:"Enforcement Guidelines",id:"enforcement-guidelines",level:2},{value:"1. Correction",id:"1-correction",level:3},{value:"2. Warning",id:"2-warning",level:3},{value:"3. Temporary Ban",id:"3-temporary-ban",level:3},{value:"4. Permanent Ban",id:"4-permanent-ban",level:3},{value:"Attribution",id:"attribution",level:2}];function d(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",li:"li",p:"p",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.h1,{id:"contributor-covenant-code-of-conduct",children:"Contributor Covenant Code of Conduct"}),"\n",(0,o.jsx)(n.h2,{id:"our-pledge",children:"Our Pledge"}),"\n",(0,o.jsx)(n.p,{children:"We as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation."}),"\n",(0,o.jsx)(n.p,{children:"We pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community."}),"\n",(0,o.jsx)(n.h2,{id:"our-standards",children:"Our Standards"}),"\n",(0,o.jsx)(n.p,{children:"Examples of behavior that contributes to a positive environment for our\ncommunity include:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"Demonstrating empathy and kindness toward other people"}),"\n",(0,o.jsx)(n.li,{children:"Being respectful of differing opinions, viewpoints, and experiences"}),"\n",(0,o.jsx)(n.li,{children:"Giving and gracefully accepting constructive feedback"}),"\n",(0,o.jsx)(n.li,{children:"Accepting responsibility and apologizing to those affected by our mistakes,\nand learning from the experience"}),"\n",(0,o.jsx)(n.li,{children:"Focusing on what is best not just for us as individuals, but for the overall\ncommunity"}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"Examples of unacceptable behavior include:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"The use of sexualized language or imagery, and sexual attention or advances of\nany kind"}),"\n",(0,o.jsx)(n.li,{children:"Trolling, insulting or derogatory comments, and personal or political attacks"}),"\n",(0,o.jsx)(n.li,{children:"Public or private harassment"}),"\n",(0,o.jsx)(n.li,{children:"Publishing others' private information, such as a physical or email address,\nwithout their explicit permission"}),"\n",(0,o.jsx)(n.li,{children:"Other conduct which could reasonably be considered inappropriate in a\nprofessional setting"}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"enforcement-responsibilities",children:"Enforcement Responsibilities"}),"\n",(0,o.jsx)(n.p,{children:"Community leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful."}),"\n",(0,o.jsx)(n.p,{children:"Community leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate."}),"\n",(0,o.jsx)(n.h2,{id:"scope",children:"Scope"}),"\n",(0,o.jsx)(n.p,{children:"This Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event."}),"\n",(0,o.jsx)(n.h2,{id:"enforcement",children:"Enforcement"}),"\n",(0,o.jsxs)(n.p,{children:["Instances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n",(0,o.jsx)(n.code,{children:"project-copacetic@googlegroups.com"}),".\nAll complaints will be reviewed and investigated promptly and fairly."]}),"\n",(0,o.jsx)(n.p,{children:"All community leaders are obligated to respect the privacy and security of the\nreporter of any incident."}),"\n",(0,o.jsx)(n.h2,{id:"enforcement-guidelines",children:"Enforcement Guidelines"}),"\n",(0,o.jsx)(n.p,{children:"Community leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:"}),"\n",(0,o.jsx)(n.h3,{id:"1-correction",children:"1. Correction"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested."]}),"\n",(0,o.jsx)(n.h3,{id:"2-warning",children:"2. Warning"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": A violation through a single incident or series of\nactions."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban."]}),"\n",(0,o.jsx)(n.h3,{id:"3-temporary-ban",children:"3. Temporary Ban"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": A serious violation of community standards, including\nsustained inappropriate behavior."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban."]}),"\n",(0,o.jsx)(n.h3,{id:"4-permanent-ban",children:"4. Permanent Ban"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A permanent ban from any sort of public interaction within the\ncommunity."]}),"\n",(0,o.jsx)(n.h2,{id:"attribution",children:"Attribution"}),"\n",(0,o.jsxs)(n.p,{children:["This Code of Conduct is adapted from the ",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org",children:"Contributor Covenant"}),",\nversion 2.1, available at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html",children:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["Community Impact Guidelines were inspired by\n",(0,o.jsx)(n.a,{href:"https://github.com/mozilla/diversity",children:"Mozilla's code of conduct enforcement ladder"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["For answers to common questions about this code of conduct, see the FAQ at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/faq",children:"https://www.contributor-covenant.org/faq"}),". Translations are available at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/translations",children:"https://www.contributor-covenant.org/translations"}),"."]})]})}function u(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>a,x:()=>s});var o=i(6540);const t={},r=o.createContext(t);function a(e){const n=o.useContext(r);return o.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function s(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:a(e.components),o.createElement(r.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/bab6f5ab.1dcf3cff.js b/website/assets/js/bab6f5ab.1dcf3cff.js deleted file mode 100644 index 2f28c6e9..00000000 --- a/website/assets/js/bab6f5ab.1dcf3cff.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3200],{3905:(e,n,t)=>{t.d(n,{Zo:()=>p,kt:()=>f});var o=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);n&&(o=o.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,o)}return t}function a(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?r(Object(t),!0).forEach((function(n){i(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):r(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))}))}return e}function l(e,n){if(null==e)return{};var t,o,i=function(e,n){if(null==e)return{};var t,o,i={},r=Object.keys(e);for(o=0;o<r.length;o++)t=r[o],n.indexOf(t)>=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o<r.length;o++)t=r[o],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var c=o.createContext({}),s=function(e){var n=o.useContext(c),t=n;return e&&(t="function"==typeof e?e(n):a(a({},n),e)),t},p=function(e){var n=s(e.components);return o.createElement(c.Provider,{value:n},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var n=e.children;return o.createElement(o.Fragment,{},n)}},m=o.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,c=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=s(t),m=i,f=u["".concat(c,".").concat(m)]||u[m]||d[m]||r;return t?o.createElement(f,a(a({ref:n},p),{},{components:t})):o.createElement(f,a({ref:n},p))}));function f(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,a=new Array(r);a[0]=m;var l={};for(var c in n)hasOwnProperty.call(n,c)&&(l[c]=n[c]);l.originalType=e,l[u]="string"==typeof e?e:i,a[1]=l;for(var s=2;s<r;s++)a[s]=t[s];return o.createElement.apply(null,a)}return o.createElement.apply(null,t)}m.displayName="MDXCreateElement"},7216:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>r,metadata:()=>l,toc:()=>s});var o=t(7462),i=(t(7294),t(3905));const r={title:"Code of Conduct"},a="Contributor Covenant Code of Conduct",l={unversionedId:"code-of-conduct",id:"version-v0.1.x/code-of-conduct",title:"Code of Conduct",description:"Our Pledge",source:"@site/versioned_docs/version-v0.1.x/code-of-conduct.md",sourceDirName:".",slug:"/code-of-conduct",permalink:"/copacetic/website/v0.1.x/code-of-conduct",draft:!1,tags:[],version:"v0.1.x",frontMatter:{title:"Code of Conduct"},sidebar:"sidebar",previous:{title:"Contributing",permalink:"/copacetic/website/v0.1.x/contributing"}},c={},s=[{value:"Our Pledge",id:"our-pledge",level:2},{value:"Our Standards",id:"our-standards",level:2},{value:"Enforcement Responsibilities",id:"enforcement-responsibilities",level:2},{value:"Scope",id:"scope",level:2},{value:"Enforcement",id:"enforcement",level:2},{value:"Enforcement Guidelines",id:"enforcement-guidelines",level:2},{value:"1. Correction",id:"1-correction",level:3},{value:"2. Warning",id:"2-warning",level:3},{value:"3. Temporary Ban",id:"3-temporary-ban",level:3},{value:"4. Permanent Ban",id:"4-permanent-ban",level:3},{value:"Attribution",id:"attribution",level:2}],p={toc:s};function u(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,o.Z)({},p,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"contributor-covenant-code-of-conduct"},"Contributor Covenant Code of Conduct"),(0,i.kt)("h2",{id:"our-pledge"},"Our Pledge"),(0,i.kt)("p",null,"We as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation."),(0,i.kt)("p",null,"We pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community."),(0,i.kt)("h2",{id:"our-standards"},"Our Standards"),(0,i.kt)("p",null,"Examples of behavior that contributes to a positive environment for our\ncommunity include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Demonstrating empathy and kindness toward other people"),(0,i.kt)("li",{parentName:"ul"},"Being respectful of differing opinions, viewpoints, and experiences"),(0,i.kt)("li",{parentName:"ul"},"Giving and gracefully accepting constructive feedback"),(0,i.kt)("li",{parentName:"ul"},"Accepting responsibility and apologizing to those affected by our mistakes,\nand learning from the experience"),(0,i.kt)("li",{parentName:"ul"},"Focusing on what is best not just for us as individuals, but for the overall\ncommunity")),(0,i.kt)("p",null,"Examples of unacceptable behavior include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"The use of sexualized language or imagery, and sexual attention or advances of\nany kind"),(0,i.kt)("li",{parentName:"ul"},"Trolling, insulting or derogatory comments, and personal or political attacks"),(0,i.kt)("li",{parentName:"ul"},"Public or private harassment"),(0,i.kt)("li",{parentName:"ul"},"Publishing others' private information, such as a physical or email address,\nwithout their explicit permission"),(0,i.kt)("li",{parentName:"ul"},"Other conduct which could reasonably be considered inappropriate in a\nprofessional setting")),(0,i.kt)("h2",{id:"enforcement-responsibilities"},"Enforcement Responsibilities"),(0,i.kt)("p",null,"Community leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful."),(0,i.kt)("p",null,"Community leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate."),(0,i.kt)("h2",{id:"scope"},"Scope"),(0,i.kt)("p",null,"This Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event."),(0,i.kt)("h2",{id:"enforcement"},"Enforcement"),(0,i.kt)("p",null,"Instances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n",(0,i.kt)("inlineCode",{parentName:"p"},"project-copacetic@googlegroups.com"),".\nAll complaints will be reviewed and investigated promptly and fairly."),(0,i.kt)("p",null,"All community leaders are obligated to respect the privacy and security of the\nreporter of any incident."),(0,i.kt)("h2",{id:"enforcement-guidelines"},"Enforcement Guidelines"),(0,i.kt)("p",null,"Community leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:"),(0,i.kt)("h3",{id:"1-correction"},"1. Correction"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested."),(0,i.kt)("h3",{id:"2-warning"},"2. Warning"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": A violation through a single incident or series of\nactions."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban."),(0,i.kt)("h3",{id:"3-temporary-ban"},"3. Temporary Ban"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": A serious violation of community standards, including\nsustained inappropriate behavior."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban."),(0,i.kt)("h3",{id:"4-permanent-ban"},"4. Permanent Ban"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A permanent ban from any sort of public interaction within the\ncommunity."),(0,i.kt)("h2",{id:"attribution"},"Attribution"),(0,i.kt)("p",null,"This Code of Conduct is adapted from the ",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org"},"Contributor Covenant"),",\nversion 2.1, available at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"},"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"),"."),(0,i.kt)("p",null,"Community Impact Guidelines were inspired by\n",(0,i.kt)("a",{parentName:"p",href:"https://github.com/mozilla/diversity"},"Mozilla's code of conduct enforcement ladder"),"."),(0,i.kt)("p",null,"For answers to common questions about this code of conduct, see the FAQ at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/faq"},"https://www.contributor-covenant.org/faq"),". Translations are available at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/translations"},"https://www.contributor-covenant.org/translations"),"."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/bfa18532.76e61599.js b/website/assets/js/bfa18532.76e61599.js new file mode 100644 index 00000000..ec4b4b27 --- /dev/null +++ b/website/assets/js/bfa18532.76e61599.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8399],{4781:(n,e,t)=>{t.r(e),t.d(e,{assets:()=>c,contentTitle:()=>r,default:()=>u,frontMatter:()=>s,metadata:()=>o,toc:()=>l});var a=t(4848),i=t(8453);const s={title:"Scanner Plugins"},r="Motivation",o={id:"scanner-plugins",title:"Scanner Plugins",description:"By default, copa uses Trivy to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners.",source:"@site/docs/scanner-plugins.md",sourceDirName:".",slug:"/scanner-plugins",permalink:"/copacetic/website/next/scanner-plugins",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{title:"Scanner Plugins"},sidebar:"sidebar",previous:{title:"Output",permalink:"/copacetic/website/next/output"},next:{title:"Contributing",permalink:"/copacetic/website/next/contributing"}},c={},l=[{value:"v1alpha1",id:"v1alpha1",level:2}];function d(n){const e={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",li:"li",p:"p",pre:"pre",ul:"ul",...(0,i.R)(),...n.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(e.h1,{id:"motivation",children:"Motivation"}),"\n",(0,a.jsxs)(e.p,{children:["By default, ",(0,a.jsx)(e.code,{children:"copa"})," uses ",(0,a.jsx)(e.a,{href:"https://github.com/aquasecurity/trivy",children:"Trivy"})," to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners."]}),"\n",(0,a.jsxs)(e.p,{children:["Starting with v0.5.0 and later, ",(0,a.jsx)(e.code,{children:"copa"})," offers extensibility to support different vulnerability scanners. Plugin architecture allows users to use the vulnerability scanner of their choice to patch container images without having to modify ",(0,a.jsx)(e.code,{children:"copa"}),"'s core codebase."]}),"\n",(0,a.jsx)(e.h1,{id:"usage",children:"Usage"}),"\n",(0,a.jsxs)(e.p,{children:["Scanner plugin binaries must be in ",(0,a.jsx)(e.code,{children:"$PATH"}),", and should be prefixed with ",(0,a.jsx)(e.code,{children:"copa-"})," and have executable permissions. Copa will automatically detect and use the scanner plugin if it is in ",(0,a.jsx)(e.code,{children:"$PATH"}),"."]}),"\n",(0,a.jsxs)(e.p,{children:["For example, if you have a scanner plugin binary called ",(0,a.jsx)(e.code,{children:"copa-foo"})," in ",(0,a.jsx)(e.code,{children:"$PATH"}),", you can run ",(0,a.jsx)(e.code,{children:"copa"})," with the following command:"]}),"\n",(0,a.jsx)(e.pre,{children:(0,a.jsx)(e.code,{className:"language-bash",children:"copa patch --scanner foo --image $IMAGE ...\n"})}),"\n",(0,a.jsx)(e.h1,{id:"scanner-plugins-from-the-community",children:"Scanner Plugins from the Community"}),"\n",(0,a.jsx)(e.p,{children:"If you have built a scanner plugin and would like to add it to this list, please submit a PR to update this section with your plugin."}),"\n",(0,a.jsx)(e.admonition,{type:"note",children:(0,a.jsx)(e.p,{children:"If you have any issues with a specific plugin, please open an issue in the applicable plugin's repository."})}),"\n",(0,a.jsxs)(e.ul,{children:["\n",(0,a.jsxs)(e.li,{children:["Grype: ",(0,a.jsx)(e.a,{href:"https://github.com/anubhav06/copa-grype",children:"https://github.com/anubhav06/copa-grype"})]}),"\n"]}),"\n",(0,a.jsx)(e.h1,{id:"writing-a-scanner-plugin",children:"Writing a Scanner Plugin"}),"\n",(0,a.jsxs)(e.p,{children:["Please see instructions at ",(0,a.jsx)(e.a,{href:"https://github.com/project-copacetic/scanner-plugin-template",children:"Scanner Plugin Template"})," for a template to get started with writing a scanner plugin."]}),"\n",(0,a.jsx)(e.h1,{id:"scanner-plugin-interface",children:"Scanner Plugin Interface"}),"\n",(0,a.jsx)(e.admonition,{type:"note",children:(0,a.jsxs)(e.p,{children:[(0,a.jsx)(e.code,{children:"alpha"})," versions of the API are not guarenteed to be backwards compatible. Once the API graduates to ",(0,a.jsx)(e.code,{children:"beta"})," and ",(0,a.jsx)(e.code,{children:"stable"}),", it will be backwards compatible."]})}),"\n",(0,a.jsx)(e.p,{children:"Scanner plugins must implement the following interface:"}),"\n",(0,a.jsx)(e.h2,{id:"v1alpha1",children:"v1alpha1"}),"\n",(0,a.jsx)(e.pre,{children:(0,a.jsx)(e.code,{className:"language-go",children:'type UpdateManifest struct {\n // API version of the interface (e.g. v1alpha1)\n APIVersion string `json:"apiVersion"`\n // Metadata contains information about the OS and config\n Metadata Metadata `json:"metadata"`\n // Updates is a list of UpdatePackage that contains information about the package updates\n Updates UpdatePackages `json:"updates"`\n}\n\n// UpdatePackages is a list of UpdatePackage\ntype UpdatePackages []UpdatePackage\n\n// Metadata contains information about the OS and config\ntype Metadata struct {\n OS OS `json:"os"`\n Config Config `json:"config"`\n}\n\ntype OS struct {\n // OS Type (e.g. debian, alpine, etc.)\n Type string `json:"type"`\n // OS Version (e.g. 11.3)\n Version string `json:"version"`\n}\n\n// Config contains information about the config\ntype Config struct {\n // OS Architecture (e.g. amd64, arm64)\n Arch string `json:"arch"`\n}\n\n// UpdatePackage contains information about the package update\ntype UpdatePackage struct {\n // Package name\n Name string `json:"name"`\n // Installed version\n InstalledVersion string `json:"installedVersion"`\n // Fixed version\n FixedVersion string `json:"fixedVersion"`\n // Vulnerability ID\n VulnerabilityID string `json:"vulnerabilityID"`\n}\n'})}),"\n",(0,a.jsx)(e.p,{children:"From the above, we can see that the plugin must return a JSON object via standard out with the following fields. For example:"}),"\n",(0,a.jsx)(e.pre,{children:(0,a.jsx)(e.code,{className:"language-json",children:'{\n "apiVersion": "v1alpha1",\n "metadata": {\n "os": {\n "type": "debian",\n "version": "11.3",\n },\n "config": {\n "arch": "amd64"\n }\n },\n "updates": [\n {\n "name": "libcurl4",\n "installedVersion": "7.74.0-1.3+deb11u1",\n "fixedVersion": "7.74.0-1.3+deb11u2",\n "vulnerabilityID": "CVE-2021-22945"\n }\n ]\n}\n'})})]})}function u(n={}){const{wrapper:e}={...(0,i.R)(),...n.components};return e?(0,a.jsx)(e,{...n,children:(0,a.jsx)(d,{...n})}):d(n)}},8453:(n,e,t)=>{t.d(e,{R:()=>r,x:()=>o});var a=t(6540);const i={},s=a.createContext(i);function r(n){const e=a.useContext(s);return a.useMemo((function(){return"function"==typeof n?n(e):{...e,...n}}),[e,n])}function o(n){let e;return e=n.disableParentContext?"function"==typeof n.components?n.components(i):n.components||i:r(n.components),a.createElement(s.Provider,{value:e},n.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/bfa18532.f8fc985e.js b/website/assets/js/bfa18532.f8fc985e.js deleted file mode 100644 index c7309bf6..00000000 --- a/website/assets/js/bfa18532.f8fc985e.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3665],{3905:(e,n,t)=>{t.d(n,{Zo:()=>c,kt:()=>g});var a=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function o(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?r(Object(t),!0).forEach((function(n){i(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):r(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))}))}return e}function s(e,n){if(null==e)return{};var t,a,i=function(e,n){if(null==e)return{};var t,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)t=r[a],n.indexOf(t)>=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)t=r[a],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var l=a.createContext({}),p=function(e){var n=a.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},c=function(e){var n=p(e.components);return a.createElement(l.Provider,{value:n},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},m=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),u=p(t),m=i,g=u["".concat(l,".").concat(m)]||u[m]||d[m]||r;return t?a.createElement(g,o(o({ref:n},c),{},{components:t})):a.createElement(g,o({ref:n},c))}));function g(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,o=new Array(r);o[0]=m;var s={};for(var l in n)hasOwnProperty.call(n,l)&&(s[l]=n[l]);s.originalType=e,s[u]="string"==typeof e?e:i,o[1]=s;for(var p=2;p<r;p++)o[p]=t[p];return a.createElement.apply(null,o)}return a.createElement.apply(null,t)}m.displayName="MDXCreateElement"},9771:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var a=t(7462),i=(t(7294),t(3905));const r={title:"Scanner Plugins"},o="Motivation",s={unversionedId:"scanner-plugins",id:"scanner-plugins",title:"Scanner Plugins",description:"By default, copa uses Trivy to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners.",source:"@site/docs/scanner-plugins.md",sourceDirName:".",slug:"/scanner-plugins",permalink:"/copacetic/website/next/scanner-plugins",draft:!1,tags:[],version:"current",frontMatter:{title:"Scanner Plugins"},sidebar:"sidebar",previous:{title:"Output",permalink:"/copacetic/website/next/output"},next:{title:"Contributing",permalink:"/copacetic/website/next/contributing"}},l={},p=[{value:"v1alpha1",id:"v1alpha1",level:2}],c={toc:p};function u(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,a.Z)({},c,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"motivation"},"Motivation"),(0,i.kt)("p",null,"By default, ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," uses ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/aquasecurity/trivy"},"Trivy")," to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners."),(0,i.kt)("p",null,"Starting with v0.5.0 and later, ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," offers extensibility to support different vulnerability scanners. Plugin architecture allows users to use the vulnerability scanner of their choice to patch container images without having to modify ",(0,i.kt)("inlineCode",{parentName:"p"},"copa"),"'s core codebase."),(0,i.kt)("h1",{id:"usage"},"Usage"),(0,i.kt)("p",null,"Scanner plugin binaries must be in ",(0,i.kt)("inlineCode",{parentName:"p"},"$PATH"),", and should be prefixed with ",(0,i.kt)("inlineCode",{parentName:"p"},"copa-")," and have executable permissions. Copa will automatically detect and use the scanner plugin if it is in ",(0,i.kt)("inlineCode",{parentName:"p"},"$PATH"),"."),(0,i.kt)("p",null,"For example, if you have a scanner plugin binary called ",(0,i.kt)("inlineCode",{parentName:"p"},"copa-foo")," in ",(0,i.kt)("inlineCode",{parentName:"p"},"$PATH"),", you can run ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," with the following command:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"copa patch --scanner foo --image $IMAGE ...\n")),(0,i.kt)("h1",{id:"scanner-plugins-from-the-community"},"Scanner Plugins from the Community"),(0,i.kt)("p",null,"If you have built a scanner plugin and would like to add it to this list, please submit a PR to update this section with your plugin."),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},"If you have any issues with a specific plugin, please open an issue in the applicable plugin's repository.")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Grype: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/anubhav06/copa-grype"},"https://github.com/anubhav06/copa-grype"))),(0,i.kt)("h1",{id:"writing-a-scanner-plugin"},"Writing a Scanner Plugin"),(0,i.kt)("p",null,"Please see instructions at ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/scanner-plugin-template"},"Scanner Plugin Template")," for a template to get started with writing a scanner plugin."),(0,i.kt)("h1",{id:"scanner-plugin-interface"},"Scanner Plugin Interface"),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},(0,i.kt)("inlineCode",{parentName:"p"},"alpha")," versions of the API are not guarenteed to be backwards compatible. Once the API graduates to ",(0,i.kt)("inlineCode",{parentName:"p"},"beta")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"stable"),", it will be backwards compatible.")),(0,i.kt)("p",null,"Scanner plugins must implement the following interface:"),(0,i.kt)("h2",{id:"v1alpha1"},"v1alpha1"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-go"},'type UpdateManifest struct {\n // API version of the interface (e.g. v1alpha1)\n APIVersion string `json:"apiVersion"`\n // Metadata contains information about the OS and config\n Metadata Metadata `json:"metadata"`\n // Updates is a list of UpdatePackage that contains information about the package updates\n Updates UpdatePackages `json:"updates"`\n}\n\n// UpdatePackages is a list of UpdatePackage\ntype UpdatePackages []UpdatePackage\n\n// Metadata contains information about the OS and config\ntype Metadata struct {\n OS OS `json:"os"`\n Config Config `json:"config"`\n}\n\ntype OS struct {\n // OS Type (e.g. debian, alpine, etc.)\n Type string `json:"type"`\n // OS Version (e.g. 11.3)\n Version string `json:"version"`\n}\n\n// Config contains information about the config\ntype Config struct {\n // OS Architecture (e.g. amd64, arm64)\n Arch string `json:"arch"`\n}\n\n// UpdatePackage contains information about the package update\ntype UpdatePackage struct {\n // Package name\n Name string `json:"name"`\n // Installed version\n InstalledVersion string `json:"installedVersion"`\n // Fixed version\n FixedVersion string `json:"fixedVersion"`\n // Vulnerability ID\n VulnerabilityID string `json:"vulnerabilityID"`\n}\n')),(0,i.kt)("p",null,"From the above, we can see that the plugin must return a JSON object via standard out with the following fields. For example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'{\n "apiVersion": "v1alpha1",\n "metadata": {\n "os": {\n "type": "debian",\n "version": "11.3",\n },\n "config": {\n "arch": "amd64"\n }\n },\n "updates": [\n {\n "name": "libcurl4",\n "installedVersion": "7.74.0-1.3+deb11u1",\n "fixedVersion": "7.74.0-1.3+deb11u2",\n "vulnerabilityID": "CVE-2021-22945"\n }\n ]\n}\n')))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/c12dc9fd.1489a453.js b/website/assets/js/c12dc9fd.1489a453.js new file mode 100644 index 00000000..13406c1e --- /dev/null +++ b/website/assets/js/c12dc9fd.1489a453.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7391],{4916:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>l});var o=i(4848),t=i(8453);const r={title:"Code of Conduct"},a="Contributor Covenant Code of Conduct",s={id:"code-of-conduct",title:"Code of Conduct",description:"Our Pledge",source:"@site/versioned_docs/version-v0.4.x/code-of-conduct.md",sourceDirName:".",slug:"/code-of-conduct",permalink:"/copacetic/website/v0.4.x/code-of-conduct",draft:!1,unlisted:!1,tags:[],version:"v0.4.x",frontMatter:{title:"Code of Conduct"},sidebar:"sidebar",previous:{title:"Contributing",permalink:"/copacetic/website/v0.4.x/contributing"},next:{title:"Copa Github Action",permalink:"/copacetic/website/v0.4.x/github-action"}},c={},l=[{value:"Our Pledge",id:"our-pledge",level:2},{value:"Our Standards",id:"our-standards",level:2},{value:"Enforcement Responsibilities",id:"enforcement-responsibilities",level:2},{value:"Scope",id:"scope",level:2},{value:"Enforcement",id:"enforcement",level:2},{value:"Enforcement Guidelines",id:"enforcement-guidelines",level:2},{value:"1. Correction",id:"1-correction",level:3},{value:"2. Warning",id:"2-warning",level:3},{value:"3. Temporary Ban",id:"3-temporary-ban",level:3},{value:"4. Permanent Ban",id:"4-permanent-ban",level:3},{value:"Attribution",id:"attribution",level:2}];function d(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",li:"li",p:"p",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.h1,{id:"contributor-covenant-code-of-conduct",children:"Contributor Covenant Code of Conduct"}),"\n",(0,o.jsx)(n.h2,{id:"our-pledge",children:"Our Pledge"}),"\n",(0,o.jsx)(n.p,{children:"We as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation."}),"\n",(0,o.jsx)(n.p,{children:"We pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community."}),"\n",(0,o.jsx)(n.h2,{id:"our-standards",children:"Our Standards"}),"\n",(0,o.jsx)(n.p,{children:"Examples of behavior that contributes to a positive environment for our\ncommunity include:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"Demonstrating empathy and kindness toward other people"}),"\n",(0,o.jsx)(n.li,{children:"Being respectful of differing opinions, viewpoints, and experiences"}),"\n",(0,o.jsx)(n.li,{children:"Giving and gracefully accepting constructive feedback"}),"\n",(0,o.jsx)(n.li,{children:"Accepting responsibility and apologizing to those affected by our mistakes,\nand learning from the experience"}),"\n",(0,o.jsx)(n.li,{children:"Focusing on what is best not just for us as individuals, but for the overall\ncommunity"}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"Examples of unacceptable behavior include:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"The use of sexualized language or imagery, and sexual attention or advances of\nany kind"}),"\n",(0,o.jsx)(n.li,{children:"Trolling, insulting or derogatory comments, and personal or political attacks"}),"\n",(0,o.jsx)(n.li,{children:"Public or private harassment"}),"\n",(0,o.jsx)(n.li,{children:"Publishing others' private information, such as a physical or email address,\nwithout their explicit permission"}),"\n",(0,o.jsx)(n.li,{children:"Other conduct which could reasonably be considered inappropriate in a\nprofessional setting"}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"enforcement-responsibilities",children:"Enforcement Responsibilities"}),"\n",(0,o.jsx)(n.p,{children:"Community leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful."}),"\n",(0,o.jsx)(n.p,{children:"Community leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate."}),"\n",(0,o.jsx)(n.h2,{id:"scope",children:"Scope"}),"\n",(0,o.jsx)(n.p,{children:"This Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event."}),"\n",(0,o.jsx)(n.h2,{id:"enforcement",children:"Enforcement"}),"\n",(0,o.jsxs)(n.p,{children:["Instances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n",(0,o.jsx)(n.code,{children:"project-copacetic@googlegroups.com"}),".\nAll complaints will be reviewed and investigated promptly and fairly."]}),"\n",(0,o.jsx)(n.p,{children:"All community leaders are obligated to respect the privacy and security of the\nreporter of any incident."}),"\n",(0,o.jsx)(n.h2,{id:"enforcement-guidelines",children:"Enforcement Guidelines"}),"\n",(0,o.jsx)(n.p,{children:"Community leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:"}),"\n",(0,o.jsx)(n.h3,{id:"1-correction",children:"1. Correction"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested."]}),"\n",(0,o.jsx)(n.h3,{id:"2-warning",children:"2. Warning"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": A violation through a single incident or series of\nactions."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban."]}),"\n",(0,o.jsx)(n.h3,{id:"3-temporary-ban",children:"3. Temporary Ban"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": A serious violation of community standards, including\nsustained inappropriate behavior."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban."]}),"\n",(0,o.jsx)(n.h3,{id:"4-permanent-ban",children:"4. Permanent Ban"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A permanent ban from any sort of public interaction within the\ncommunity."]}),"\n",(0,o.jsx)(n.h2,{id:"attribution",children:"Attribution"}),"\n",(0,o.jsxs)(n.p,{children:["This Code of Conduct is adapted from the ",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org",children:"Contributor Covenant"}),",\nversion 2.1, available at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html",children:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["Community Impact Guidelines were inspired by\n",(0,o.jsx)(n.a,{href:"https://github.com/mozilla/diversity",children:"Mozilla's code of conduct enforcement ladder"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["For answers to common questions about this code of conduct, see the FAQ at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/faq",children:"https://www.contributor-covenant.org/faq"}),". Translations are available at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/translations",children:"https://www.contributor-covenant.org/translations"}),"."]})]})}function u(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>a,x:()=>s});var o=i(6540);const t={},r=o.createContext(t);function a(e){const n=o.useContext(r);return o.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function s(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:a(e.components),o.createElement(r.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/c12dc9fd.2e134505.js b/website/assets/js/c12dc9fd.2e134505.js deleted file mode 100644 index 908fd61f..00000000 --- a/website/assets/js/c12dc9fd.2e134505.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6325],{3905:(e,n,t)=>{t.d(n,{Zo:()=>p,kt:()=>f});var o=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);n&&(o=o.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,o)}return t}function a(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?r(Object(t),!0).forEach((function(n){i(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):r(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))}))}return e}function l(e,n){if(null==e)return{};var t,o,i=function(e,n){if(null==e)return{};var t,o,i={},r=Object.keys(e);for(o=0;o<r.length;o++)t=r[o],n.indexOf(t)>=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o<r.length;o++)t=r[o],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var c=o.createContext({}),s=function(e){var n=o.useContext(c),t=n;return e&&(t="function"==typeof e?e(n):a(a({},n),e)),t},p=function(e){var n=s(e.components);return o.createElement(c.Provider,{value:n},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var n=e.children;return o.createElement(o.Fragment,{},n)}},m=o.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,c=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=s(t),m=i,f=u["".concat(c,".").concat(m)]||u[m]||d[m]||r;return t?o.createElement(f,a(a({ref:n},p),{},{components:t})):o.createElement(f,a({ref:n},p))}));function f(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,a=new Array(r);a[0]=m;var l={};for(var c in n)hasOwnProperty.call(n,c)&&(l[c]=n[c]);l.originalType=e,l[u]="string"==typeof e?e:i,a[1]=l;for(var s=2;s<r;s++)a[s]=t[s];return o.createElement.apply(null,a)}return o.createElement.apply(null,t)}m.displayName="MDXCreateElement"},4606:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>r,metadata:()=>l,toc:()=>s});var o=t(7462),i=(t(7294),t(3905));const r={title:"Code of Conduct"},a="Contributor Covenant Code of Conduct",l={unversionedId:"code-of-conduct",id:"version-v0.4.x/code-of-conduct",title:"Code of Conduct",description:"Our Pledge",source:"@site/versioned_docs/version-v0.4.x/code-of-conduct.md",sourceDirName:".",slug:"/code-of-conduct",permalink:"/copacetic/website/v0.4.x/code-of-conduct",draft:!1,tags:[],version:"v0.4.x",frontMatter:{title:"Code of Conduct"},sidebar:"sidebar",previous:{title:"Contributing",permalink:"/copacetic/website/v0.4.x/contributing"},next:{title:"Copa Github Action",permalink:"/copacetic/website/v0.4.x/github-action"}},c={},s=[{value:"Our Pledge",id:"our-pledge",level:2},{value:"Our Standards",id:"our-standards",level:2},{value:"Enforcement Responsibilities",id:"enforcement-responsibilities",level:2},{value:"Scope",id:"scope",level:2},{value:"Enforcement",id:"enforcement",level:2},{value:"Enforcement Guidelines",id:"enforcement-guidelines",level:2},{value:"1. Correction",id:"1-correction",level:3},{value:"2. Warning",id:"2-warning",level:3},{value:"3. Temporary Ban",id:"3-temporary-ban",level:3},{value:"4. Permanent Ban",id:"4-permanent-ban",level:3},{value:"Attribution",id:"attribution",level:2}],p={toc:s};function u(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,o.Z)({},p,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"contributor-covenant-code-of-conduct"},"Contributor Covenant Code of Conduct"),(0,i.kt)("h2",{id:"our-pledge"},"Our Pledge"),(0,i.kt)("p",null,"We as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation."),(0,i.kt)("p",null,"We pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community."),(0,i.kt)("h2",{id:"our-standards"},"Our Standards"),(0,i.kt)("p",null,"Examples of behavior that contributes to a positive environment for our\ncommunity include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Demonstrating empathy and kindness toward other people"),(0,i.kt)("li",{parentName:"ul"},"Being respectful of differing opinions, viewpoints, and experiences"),(0,i.kt)("li",{parentName:"ul"},"Giving and gracefully accepting constructive feedback"),(0,i.kt)("li",{parentName:"ul"},"Accepting responsibility and apologizing to those affected by our mistakes,\nand learning from the experience"),(0,i.kt)("li",{parentName:"ul"},"Focusing on what is best not just for us as individuals, but for the overall\ncommunity")),(0,i.kt)("p",null,"Examples of unacceptable behavior include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"The use of sexualized language or imagery, and sexual attention or advances of\nany kind"),(0,i.kt)("li",{parentName:"ul"},"Trolling, insulting or derogatory comments, and personal or political attacks"),(0,i.kt)("li",{parentName:"ul"},"Public or private harassment"),(0,i.kt)("li",{parentName:"ul"},"Publishing others' private information, such as a physical or email address,\nwithout their explicit permission"),(0,i.kt)("li",{parentName:"ul"},"Other conduct which could reasonably be considered inappropriate in a\nprofessional setting")),(0,i.kt)("h2",{id:"enforcement-responsibilities"},"Enforcement Responsibilities"),(0,i.kt)("p",null,"Community leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful."),(0,i.kt)("p",null,"Community leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate."),(0,i.kt)("h2",{id:"scope"},"Scope"),(0,i.kt)("p",null,"This Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event."),(0,i.kt)("h2",{id:"enforcement"},"Enforcement"),(0,i.kt)("p",null,"Instances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n",(0,i.kt)("inlineCode",{parentName:"p"},"project-copacetic@googlegroups.com"),".\nAll complaints will be reviewed and investigated promptly and fairly."),(0,i.kt)("p",null,"All community leaders are obligated to respect the privacy and security of the\nreporter of any incident."),(0,i.kt)("h2",{id:"enforcement-guidelines"},"Enforcement Guidelines"),(0,i.kt)("p",null,"Community leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:"),(0,i.kt)("h3",{id:"1-correction"},"1. Correction"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested."),(0,i.kt)("h3",{id:"2-warning"},"2. Warning"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": A violation through a single incident or series of\nactions."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban."),(0,i.kt)("h3",{id:"3-temporary-ban"},"3. Temporary Ban"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": A serious violation of community standards, including\nsustained inappropriate behavior."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban."),(0,i.kt)("h3",{id:"4-permanent-ban"},"4. Permanent Ban"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A permanent ban from any sort of public interaction within the\ncommunity."),(0,i.kt)("h2",{id:"attribution"},"Attribution"),(0,i.kt)("p",null,"This Code of Conduct is adapted from the ",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org"},"Contributor Covenant"),",\nversion 2.1, available at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"},"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"),"."),(0,i.kt)("p",null,"Community Impact Guidelines were inspired by\n",(0,i.kt)("a",{parentName:"p",href:"https://github.com/mozilla/diversity"},"Mozilla's code of conduct enforcement ladder"),"."),(0,i.kt)("p",null,"For answers to common questions about this code of conduct, see the FAQ at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/faq"},"https://www.contributor-covenant.org/faq"),". Translations are available at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/translations"},"https://www.contributor-covenant.org/translations"),"."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/c13ba925.cfea7e03.js b/website/assets/js/c13ba925.cfea7e03.js deleted file mode 100644 index 535db082..00000000 --- a/website/assets/js/c13ba925.cfea7e03.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7845],{3905:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>u});var i=a(7294);function n(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,i)}return a}function o(e){for(var t=1;t<arguments.length;t++){var a=null!=arguments[t]?arguments[t]:{};t%2?r(Object(a),!0).forEach((function(t){n(e,t,a[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):r(Object(a)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))}))}return e}function s(e,t){if(null==e)return{};var a,i,n=function(e,t){if(null==e)return{};var a,i,n={},r=Object.keys(e);for(i=0;i<r.length;i++)a=r[i],t.indexOf(a)>=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i<r.length;i++)a=r[i],t.indexOf(a)>=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var l=i.createContext({}),p=function(e){var t=i.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},c=function(e){var t=p(e.components);return i.createElement(l.Provider,{value:t},e.children)},d="mdxType",g={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},h=i.forwardRef((function(e,t){var a=e.components,n=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=p(a),h=n,u=d["".concat(l,".").concat(h)]||d[h]||g[h]||r;return a?i.createElement(u,o(o({ref:t},c),{},{components:a})):i.createElement(u,o({ref:t},c))}));function u(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var r=a.length,o=new Array(r);o[0]=h;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[d]="string"==typeof e?e:n,o[1]=s;for(var p=2;p<r;p++)o[p]=a[p];return i.createElement.apply(null,o)}return i.createElement.apply(null,a)}h.displayName="MDXCreateElement"},460:(e,t,a)=>{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>d,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var i=a(7462),n=(a(7294),a(3905));const r={title:"Design"},o=void 0,s={unversionedId:"design",id:"version-v0.4.x/design",title:"Design",description:"Design Tenets",source:"@site/versioned_docs/version-v0.4.x/design.md",sourceDirName:".",slug:"/design",permalink:"/copacetic/website/v0.4.x/design",draft:!1,tags:[],version:"v0.4.x",frontMatter:{title:"Design"},sidebar:"sidebar",previous:{title:"Troubleshooting",permalink:"/copacetic/website/v0.4.x/troubleshooting"},next:{title:"FAQ",permalink:"/copacetic/website/v0.4.x/faq"}},l={},p=[{value:"Design Tenets",id:"design-tenets",level:2},{value:"Design Reasoning",id:"design-reasoning",level:2},{value:"Architecture",id:"architecture",level:2},{value:"Implementation",id:"implementation",level:2},{value:"Tradeoffs",id:"tradeoffs",level:2}],c={toc:p};function d(e){let{components:t,...a}=e;return(0,n.kt)("wrapper",(0,i.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h2",{id:"design-tenets"},"Design Tenets"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update.")," This is a raison d\u2019etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to work with the existing ecosystem of container images.")," The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to allow parties other than the image authors to address container vulnerabilities.")," Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},(0,n.kt)("strong",{parentName:"p"},"Copa is intended to do one thing well and be composable with other tools and processes.")," Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane."))),(0,n.kt)("h2",{id:"design-reasoning"},"Design Reasoning"),(0,n.kt)("p",null,"The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/google/go-containerregistry/blob/main/cmd/crane/rebase.md"},(0,n.kt)("inlineCode",{parentName:"a"},"crane rebase")),":"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},"Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an ",(0,n.kt)("inlineCode",{parentName:"p"},"apt install")," during image creation will overwrite the dpkg ",(0,n.kt)("inlineCode",{parentName:"p"},"status")," file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners."),(0,n.kt)("p",{parentName:"li"},"To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("p",{parentName:"li"},"Rebasing also requires that the user knows ",(0,n.kt)("em",{parentName:"p"},"a priori")," what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets."),(0,n.kt)("p",{parentName:"li"},"While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose."),(0,n.kt)("p",{parentName:"li"},"The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well."))),(0,n.kt)("h2",{id:"architecture"},"Architecture"),(0,n.kt)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),(0,n.kt)("p",null,"The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"The data schema of various vulnerability scanners producing the input vulnerability report."),(0,n.kt)("li",{parentName:"ul"},"The state management of various package managers and process for applying patches appropriately through them.")),(0,n.kt)("p",null,"Effectively, ",(0,n.kt)("inlineCode",{parentName:"p"},"copa patch")," can be considered a command that bridges an extensible ",(0,n.kt)("inlineCode",{parentName:"p"},"Parse")," action with an extensible ",(0,n.kt)("inlineCode",{parentName:"p"},"Apply")," action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-go"},"type UpdatePackage struct {\n Name string\n Version string\n}\n\ntype UpdateManifest struct {\n OSType string\n OSVersion string\n Arch string\n Updates []UpdatePackage\n}\n\ntype ScanReportParser interface {\n Parse(reportPath string) (*UpdateManifest, error)\n}\n\ntype PackageManager interface {\n Apply(imagePath string, report *UpdateManifest) error\n}\n")),(0,n.kt)("h2",{id:"implementation"},"Implementation"),(0,n.kt)("img",{title:"buildkit graph execution",src:"/copacetic/website/img/graph-execution.png"}),(0,n.kt)("p",null,(0,n.kt)("inlineCode",{parentName:"p"},"copa")," is a pseudo-frontend to ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit"},"buildkit")," implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit/tree/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4#exploring-llb"},"LLB graphs")," for buildkit to execute:"),(0,n.kt)("ol",null,(0,n.kt)("li",{parentName:"ol"},"Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions."))),(0,n.kt)("li",{parentName:"ol"},"Actions to fetch and deploy tools that can be injected into the target image to perform the patching.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches."),(0,n.kt)("li",{parentName:"ul"},"In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image."),(0,n.kt)("li",{parentName:"ul"},"Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well."))),(0,n.kt)("li",{parentName:"ol"},"Actions to deploy the required patch packages to the target image.",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"copa")," integrates with buildkit at the API level because it uses the ",(0,n.kt)("a",{parentName:"li",href:"https://github.com/moby/buildkit/blob/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4/docs/merge%2Bdiff.md"},"diff and merge")," graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.")))),(0,n.kt)("h2",{id:"tradeoffs"},"Tradeoffs"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa."),(0,n.kt)("li",{parentName:"ul"},"copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this."))),(0,n.kt)("li",{parentName:"ul"},"No support for windows containers given the dependency on buildkit.")))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/c13ba925.e5b48032.js b/website/assets/js/c13ba925.e5b48032.js new file mode 100644 index 00000000..5d75f0d2 --- /dev/null +++ b/website/assets/js/c13ba925.e5b48032.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[621],{5496:(e,t,i)=>{i.r(t),i.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>h,frontMatter:()=>s,metadata:()=>r,toc:()=>l});var n=i(4848),a=i(8453);const s={title:"Design"},o=void 0,r={id:"design",title:"Design",description:"Design Tenets",source:"@site/versioned_docs/version-v0.4.x/design.md",sourceDirName:".",slug:"/design",permalink:"/copacetic/website/v0.4.x/design",draft:!1,unlisted:!1,tags:[],version:"v0.4.x",frontMatter:{title:"Design"},sidebar:"sidebar",previous:{title:"Troubleshooting",permalink:"/copacetic/website/v0.4.x/troubleshooting"},next:{title:"FAQ",permalink:"/copacetic/website/v0.4.x/faq"}},c={},l=[{value:"Design Tenets",id:"design-tenets",level:2},{value:"Design Reasoning",id:"design-reasoning",level:2},{value:"Architecture",id:"architecture",level:2},{value:"Implementation",id:"implementation",level:2},{value:"Tradeoffs",id:"tradeoffs",level:2}];function d(e){const t={a:"a",code:"code",em:"em",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(t.h2,{id:"design-tenets",children:"Design Tenets"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update."})," This is a raison d\u2019etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to work with the existing ecosystem of container images."})," The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to allow parties other than the image authors to address container vulnerabilities."})," Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully."]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Copa is intended to do one thing well and be composable with other tools and processes."})," Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane."]}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"design-reasoning",children:"Design Reasoning"}),"\n",(0,n.jsxs)(t.p,{children:["The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental ",(0,n.jsx)(t.a,{href:"https://github.com/google/go-containerregistry/blob/main/cmd/crane/rebase.md",children:(0,n.jsx)(t.code,{children:"crane rebase"})}),":"]}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:["Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an ",(0,n.jsx)(t.code,{children:"apt install"})," during image creation will overwrite the dpkg ",(0,n.jsx)(t.code,{children:"status"})," file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners."]}),"\n",(0,n.jsx)(t.p,{children:"To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on."}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["\n",(0,n.jsxs)(t.p,{children:["Rebasing also requires that the user knows ",(0,n.jsx)(t.em,{children:"a priori"})," what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets."]}),"\n",(0,n.jsx)(t.p,{children:"While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose."}),"\n",(0,n.jsx)(t.p,{children:"The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well."}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"architecture",children:"Architecture"}),"\n",(0,n.jsx)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),"\n",(0,n.jsx)(t.p,{children:"The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"The data schema of various vulnerability scanners producing the input vulnerability report."}),"\n",(0,n.jsx)(t.li,{children:"The state management of various package managers and process for applying patches appropriately through them."}),"\n"]}),"\n",(0,n.jsxs)(t.p,{children:["Effectively, ",(0,n.jsx)(t.code,{children:"copa patch"})," can be considered a command that bridges an extensible ",(0,n.jsx)(t.code,{children:"Parse"})," action with an extensible ",(0,n.jsx)(t.code,{children:"Apply"})," action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:"]}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-go",children:"type UpdatePackage struct {\n Name string\n Version string\n}\n\ntype UpdateManifest struct {\n OSType string\n OSVersion string\n Arch string\n Updates []UpdatePackage\n}\n\ntype ScanReportParser interface {\n Parse(reportPath string) (*UpdateManifest, error)\n}\n\ntype PackageManager interface {\n Apply(imagePath string, report *UpdateManifest) error\n}\n"})}),"\n",(0,n.jsx)(t.h2,{id:"implementation",children:"Implementation"}),"\n",(0,n.jsx)("img",{title:"buildkit graph execution",src:"/copacetic/website/img/graph-execution.png"}),"\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.code,{children:"copa"})," is a pseudo-frontend to ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit",children:"buildkit"})," implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit/tree/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4#exploring-llb",children:"LLB graphs"})," for buildkit to execute:"]}),"\n",(0,n.jsxs)(t.ol,{children:["\n",(0,n.jsxs)(t.li,{children:["Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions."}),"\n"]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["Actions to fetch and deploy tools that can be injected into the target image to perform the patching.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches."}),"\n",(0,n.jsx)(t.li,{children:"In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image."}),"\n",(0,n.jsx)(t.li,{children:"Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well."}),"\n"]}),"\n"]}),"\n",(0,n.jsxs)(t.li,{children:["Actions to deploy the required patch packages to the target image.","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.code,{children:"copa"})," integrates with buildkit at the API level because it uses the ",(0,n.jsx)(t.a,{href:"https://github.com/moby/buildkit/blob/99f6199fa6f0c34dbb3acfa57e00b7189a6a79d4/docs/merge%2Bdiff.md",children:"diff and merge"})," graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches."]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"tradeoffs",children:"Tradeoffs"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:","\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsx)(t.li,{children:"By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa."}),"\n",(0,n.jsx)(t.li,{children:"copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this."}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(t.li,{children:"No support for windows containers given the dependency on buildkit."}),"\n"]})]})}function h(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,n.jsx)(t,{...e,children:(0,n.jsx)(d,{...e})}):d(e)}},8453:(e,t,i)=>{i.d(t,{R:()=>o,x:()=>r});var n=i(6540);const a={},s=n.createContext(a);function o(e){const t=n.useContext(s);return n.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function r(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),n.createElement(s.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/c5934ffc.0e15e6c8.js b/website/assets/js/c5934ffc.0e15e6c8.js deleted file mode 100644 index 5e8b80c7..00000000 --- a/website/assets/js/c5934ffc.0e15e6c8.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6705],{9452:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"v0.1.x","label":"v0.1.x","banner":"unmaintained","badge":true,"noIndex":false,"className":"docs-version-v0.1.x","isLast":false,"docsSidebars":{"sidebar":[{"type":"link","label":"Introduction","href":"/copacetic/website/v0.1.x/","docId":"introduction"},{"type":"link","label":"Installation","href":"/copacetic/website/v0.1.x/installation","docId":"installation"},{"type":"link","label":"Quick Start","href":"/copacetic/website/v0.1.x/quick-start","docId":"quick-start"},{"type":"link","label":"Design","href":"/copacetic/website/v0.1.x/design","docId":"design"},{"type":"link","label":"FAQ","href":"/copacetic/website/v0.1.x/faq","docId":"faq"},{"type":"link","label":"Contributing","href":"/copacetic/website/v0.1.x/contributing","docId":"contributing"},{"type":"link","label":"Code of Conduct","href":"/copacetic/website/v0.1.x/code-of-conduct","docId":"code-of-conduct"}]},"docs":{"code-of-conduct":{"id":"code-of-conduct","title":"Code of Conduct","description":"Our Pledge","sidebar":"sidebar"},"contributing":{"id":"contributing","title":"Contributing","description":"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.","sidebar":"sidebar"},"design":{"id":"design","title":"Design","description":"Design Tenets","sidebar":"sidebar"},"faq":{"id":"faq","title":"FAQ","description":"What kind of vulnerabilities can Copa patch?","sidebar":"sidebar"},"installation":{"id":"installation","title":"Installation","description":"Homebrew","sidebar":"sidebar"},"introduction":{"id":"introduction","title":"Introduction","description":"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.","sidebar":"sidebar"},"quick-start":{"id":"quick-start","title":"Quick Start","description":"This sample illustrates how to patch containers using vulnerability reports with copa.","sidebar":"sidebar"}}}')}}]); \ No newline at end of file diff --git a/website/assets/js/c5934ffc.658c31dc.js b/website/assets/js/c5934ffc.658c31dc.js new file mode 100644 index 00000000..506daff6 --- /dev/null +++ b/website/assets/js/c5934ffc.658c31dc.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2728],{9564:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"v0.1.x","label":"v0.1.x","banner":"unmaintained","badge":true,"noIndex":false,"className":"docs-version-v0.1.x","isLast":false,"docsSidebars":{"sidebar":[{"type":"link","label":"Introduction","href":"/copacetic/website/v0.1.x/","docId":"introduction","unlisted":false},{"type":"link","label":"Installation","href":"/copacetic/website/v0.1.x/installation","docId":"installation","unlisted":false},{"type":"link","label":"Quick Start","href":"/copacetic/website/v0.1.x/quick-start","docId":"quick-start","unlisted":false},{"type":"link","label":"Design","href":"/copacetic/website/v0.1.x/design","docId":"design","unlisted":false},{"type":"link","label":"FAQ","href":"/copacetic/website/v0.1.x/faq","docId":"faq","unlisted":false},{"type":"link","label":"Contributing","href":"/copacetic/website/v0.1.x/contributing","docId":"contributing","unlisted":false},{"type":"link","label":"Code of Conduct","href":"/copacetic/website/v0.1.x/code-of-conduct","docId":"code-of-conduct","unlisted":false}]},"docs":{"code-of-conduct":{"id":"code-of-conduct","title":"Code of Conduct","description":"Our Pledge","sidebar":"sidebar"},"contributing":{"id":"contributing","title":"Contributing","description":"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.","sidebar":"sidebar"},"design":{"id":"design","title":"Design","description":"Design Tenets","sidebar":"sidebar"},"faq":{"id":"faq","title":"FAQ","description":"What kind of vulnerabilities can Copa patch?","sidebar":"sidebar"},"installation":{"id":"installation","title":"Installation","description":"Homebrew","sidebar":"sidebar"},"introduction":{"id":"introduction","title":"Introduction","description":"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.","sidebar":"sidebar"},"quick-start":{"id":"quick-start","title":"Quick Start","description":"This sample illustrates how to patch containers using vulnerability reports with copa.","sidebar":"sidebar"}}}')}}]); \ No newline at end of file diff --git a/website/assets/js/c66bbf8a.356f0a06.js b/website/assets/js/c66bbf8a.356f0a06.js deleted file mode 100644 index f64d71c3..00000000 --- a/website/assets/js/c66bbf8a.356f0a06.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[568],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>g});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),c=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(s.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=c(n),m=i,g=u["".concat(s,".").concat(m)]||u[m]||d[m]||r;return n?a.createElement(g,o(o({ref:t},p),{},{components:n})):a.createElement(g,o({ref:t},p))}));function g(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[u]="string"==typeof e?e:i,o[1]=l;for(var c=2;c<r;c++)o[c]=n[c];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}m.displayName="MDXCreateElement"},789:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>l,toc:()=>c});var a=n(7462),i=(n(7294),n(3905));const r={title:"Introduction",slug:"/"},o="Project Copacetic: Directly patch container image vulnerabilities",l={unversionedId:"introduction",id:"version-v0.5.x/introduction",title:"Introduction",description:"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.",source:"@site/versioned_docs/version-v0.5.x/introduction.md",sourceDirName:".",slug:"/",permalink:"/copacetic/website/v0.5.x/",draft:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Introduction",slug:"/"},sidebar:"sidebar",next:{title:"Installation",permalink:"/copacetic/website/v0.5.x/installation"}},s={},c=[{value:"Why?",id:"why",level:2},{value:"How?",id:"how",level:2}],p={toc:c};function u(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"project-copacetic-directly-patch-container-image-vulnerabilities"},"Project Copacetic: Directly patch container image vulnerabilities"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"copa")," is a CLI tool written in ",(0,i.kt)("a",{parentName:"p",href:"https://golang.org"},"Go")," and based on ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit"},"buildkit")," that can be used to directly patch container images given the vulnerability scanning results from popular tools like ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/aquasecurity/trivy"},"Trivy"),"."),(0,i.kt)("h2",{id:"why"},"Why?"),(0,i.kt)("p",null,"We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between ",(0,i.kt)("a",{parentName:"p",href:"https://www.bleepingcomputer.com/news/security/hackers-scan-for-vulnerabilities-within-15-minutes-of-disclosure/"},"vulnerability disclosure and active exploitation continues to narrow"),", there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option"),(0,i.kt)("li",{parentName:"ul"},"found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.")),(0,i.kt)("img",{title:"direct image patching",src:"/copacetic/website/img/direct-image-patching.png"}),(0,i.kt)("p",null,"In addition to filling the operational gap not met by left-shift security practices and tools, the ability of ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," to patch a container without requiring a rebuild of the container image provides other benefits:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Allows users other than the image publishers to also patch container images, such as DevSecOps engineers."),(0,i.kt)("li",{parentName:"ul"},"Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching."),(0,i.kt)("li",{parentName:"ul"},"Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild."),(0,i.kt)("li",{parentName:"ul"},"Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.")),(0,i.kt)("h2",{id:"how"},"How?"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," tool is an extensible engine that:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Parses the needed update packages from the container image\u2019s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats."),(0,i.kt)("li",{parentName:"ol"},"Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers."),(0,i.kt)("li",{parentName:"ol"},"Applies the resulting update binaries to the container image using buildkit.")),(0,i.kt)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),(0,i.kt)("p",null,"This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa supports patching ",(0,i.kt)("em",{parentName:"strong"},"existing")," container images"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"Devs don't need to build their images using specific tools or modify them in some way just to support container patching."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa works with the existing vulnerability scanning and mitigation ecosystems"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today."),(0,i.kt)("li",{parentName:"ul"},"Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.")))),(0,i.kt)("p",null,"For more details, refer to the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.5.x/design"},"copa design")," documentation."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/c66bbf8a.9d86a839.js b/website/assets/js/c66bbf8a.9d86a839.js new file mode 100644 index 00000000..dfbc2713 --- /dev/null +++ b/website/assets/js/c66bbf8a.9d86a839.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6613],{7811:(e,i,n)=>{n.r(i),n.d(i,{assets:()=>c,contentTitle:()=>o,default:()=>h,frontMatter:()=>s,metadata:()=>r,toc:()=>l});var t=n(4848),a=n(8453);const s={title:"Introduction",slug:"/"},o="Project Copacetic: Directly patch container image vulnerabilities",r={id:"introduction",title:"Introduction",description:"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.",source:"@site/versioned_docs/version-v0.5.x/introduction.md",sourceDirName:".",slug:"/",permalink:"/copacetic/website/v0.5.x/",draft:!1,unlisted:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Introduction",slug:"/"},sidebar:"sidebar",next:{title:"Installation",permalink:"/copacetic/website/v0.5.x/installation"}},c={},l=[{value:"Why?",id:"why",level:2},{value:"How?",id:"how",level:2}];function d(e){const i={a:"a",code:"code",em:"em",h1:"h1",h2:"h2",li:"li",ol:"ol",p:"p",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i.h1,{id:"project-copacetic-directly-patch-container-image-vulnerabilities",children:"Project Copacetic: Directly patch container image vulnerabilities"}),"\n",(0,t.jsxs)(i.p,{children:[(0,t.jsx)(i.code,{children:"copa"})," is a CLI tool written in ",(0,t.jsx)(i.a,{href:"https://golang.org",children:"Go"})," and based on ",(0,t.jsx)(i.a,{href:"https://github.com/moby/buildkit",children:"buildkit"})," that can be used to directly patch container images given the vulnerability scanning results from popular tools like ",(0,t.jsx)(i.a,{href:"https://github.com/aquasecurity/trivy",children:"Trivy"}),"."]}),"\n",(0,t.jsx)(i.h2,{id:"why",children:"Why?"}),"\n",(0,t.jsxs)(i.p,{children:["We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between ",(0,t.jsx)(i.a,{href:"https://www.bleepingcomputer.com/news/security/hackers-scan-for-vulnerabilities-within-15-minutes-of-disclosure/",children:"vulnerability disclosure and active exploitation continues to narrow"}),", there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:"]}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option"}),"\n",(0,t.jsx)(i.li,{children:"found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs."}),"\n"]}),"\n",(0,t.jsx)("img",{title:"direct image patching",src:"/copacetic/website/img/direct-image-patching.png"}),"\n",(0,t.jsxs)(i.p,{children:["In addition to filling the operational gap not met by left-shift security practices and tools, the ability of ",(0,t.jsx)(i.code,{children:"copa"})," to patch a container without requiring a rebuild of the container image provides other benefits:"]}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Allows users other than the image publishers to also patch container images, such as DevSecOps engineers."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image."}),"\n"]}),"\n",(0,t.jsx)(i.h2,{id:"how",children:"How?"}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"copa"})," tool is an extensible engine that:"]}),"\n",(0,t.jsxs)(i.ol,{children:["\n",(0,t.jsx)(i.li,{children:"Parses the needed update packages from the container image\u2019s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats."}),"\n",(0,t.jsx)(i.li,{children:"Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers."}),"\n",(0,t.jsx)(i.li,{children:"Applies the resulting update binaries to the container image using buildkit."}),"\n"]}),"\n",(0,t.jsx)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),"\n",(0,t.jsx)(i.p,{children:"This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:"}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:[(0,t.jsxs)(i.strong,{children:["Copa supports patching ",(0,t.jsx)(i.em,{children:"existing"})," container images"]}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Devs don't need to build their images using specific tools or modify them in some way just to support container patching."}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Copa works with the existing vulnerability scanning and mitigation ecosystems"}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today."}),"\n",(0,t.jsx)(i.li,{children:"Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports."}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image"}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today."}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.p,{children:["For more details, refer to the ",(0,t.jsx)(i.a,{href:"/copacetic/website/v0.5.x/design",children:"copa design"})," documentation."]})]})}function h(e={}){const{wrapper:i}={...(0,a.R)(),...e.components};return i?(0,t.jsx)(i,{...e,children:(0,t.jsx)(d,{...e})}):d(e)}},8453:(e,i,n)=>{n.d(i,{R:()=>o,x:()=>r});var t=n(6540);const a={},s=t.createContext(a);function o(e){const i=t.useContext(s);return t.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function r(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),t.createElement(s.Provider,{value:i},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/c698fe77.2ced926f.js b/website/assets/js/c698fe77.2ced926f.js deleted file mode 100644 index 0b67bd97..00000000 --- a/website/assets/js/c698fe77.2ced926f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4993],{6283:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"v0.4.x","label":"v0.4.x","banner":"unmaintained","badge":true,"noIndex":false,"className":"docs-version-v0.4.x","isLast":false,"docsSidebars":{"sidebar":[{"type":"link","label":"Introduction","href":"/copacetic/website/v0.4.x/","docId":"introduction"},{"type":"link","label":"Installation","href":"/copacetic/website/v0.4.x/installation","docId":"installation"},{"type":"link","label":"Quick Start","href":"/copacetic/website/v0.4.x/quick-start","docId":"quick-start"},{"type":"link","label":"Troubleshooting","href":"/copacetic/website/v0.4.x/troubleshooting","docId":"troubleshooting"},{"type":"link","label":"Design","href":"/copacetic/website/v0.4.x/design","docId":"design"},{"type":"link","label":"FAQ","href":"/copacetic/website/v0.4.x/faq","docId":"faq"},{"type":"link","label":"Contributing","href":"/copacetic/website/v0.4.x/contributing","docId":"contributing"},{"type":"link","label":"Code of Conduct","href":"/copacetic/website/v0.4.x/code-of-conduct","docId":"code-of-conduct"},{"type":"link","label":"Copa Github Action","href":"/copacetic/website/v0.4.x/github-action","docId":"github-action"},{"type":"link","label":"Release Process","href":"/copacetic/website/v0.4.x/release","docId":"release"}]},"docs":{"code-of-conduct":{"id":"code-of-conduct","title":"Code of Conduct","description":"Our Pledge","sidebar":"sidebar"},"contributing":{"id":"contributing","title":"Contributing","description":"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.","sidebar":"sidebar"},"design":{"id":"design","title":"Design","description":"Design Tenets","sidebar":"sidebar"},"faq":{"id":"faq","title":"FAQ","description":"What kind of vulnerabilities can Copa patch?","sidebar":"sidebar"},"github-action":{"id":"github-action","title":"Copa Github Action","description":"Overview","sidebar":"sidebar"},"installation":{"id":"installation","title":"Installation","description":"Homebrew","sidebar":"sidebar"},"introduction":{"id":"introduction","title":"Introduction","description":"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.","sidebar":"sidebar"},"quick-start":{"id":"quick-start","title":"Quick Start","description":"This sample illustrates how to patch containers using vulnerability reports with copa.","sidebar":"sidebar"},"release":{"id":"release","title":"Release Process","description":"Overview","sidebar":"sidebar"},"troubleshooting":{"id":"troubleshooting","title":"Troubleshooting","description":"Filtering Vulnerabilities","sidebar":"sidebar"}}}')}}]); \ No newline at end of file diff --git a/website/assets/js/c698fe77.8fa65e9b.js b/website/assets/js/c698fe77.8fa65e9b.js new file mode 100644 index 00000000..8e9f5b7a --- /dev/null +++ b/website/assets/js/c698fe77.8fa65e9b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8222],{3046:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"v0.4.x","label":"v0.4.x","banner":"unmaintained","badge":true,"noIndex":false,"className":"docs-version-v0.4.x","isLast":false,"docsSidebars":{"sidebar":[{"type":"link","label":"Introduction","href":"/copacetic/website/v0.4.x/","docId":"introduction","unlisted":false},{"type":"link","label":"Installation","href":"/copacetic/website/v0.4.x/installation","docId":"installation","unlisted":false},{"type":"link","label":"Quick Start","href":"/copacetic/website/v0.4.x/quick-start","docId":"quick-start","unlisted":false},{"type":"link","label":"Troubleshooting","href":"/copacetic/website/v0.4.x/troubleshooting","docId":"troubleshooting","unlisted":false},{"type":"link","label":"Design","href":"/copacetic/website/v0.4.x/design","docId":"design","unlisted":false},{"type":"link","label":"FAQ","href":"/copacetic/website/v0.4.x/faq","docId":"faq","unlisted":false},{"type":"link","label":"Contributing","href":"/copacetic/website/v0.4.x/contributing","docId":"contributing","unlisted":false},{"type":"link","label":"Code of Conduct","href":"/copacetic/website/v0.4.x/code-of-conduct","docId":"code-of-conduct","unlisted":false},{"type":"link","label":"Copa Github Action","href":"/copacetic/website/v0.4.x/github-action","docId":"github-action","unlisted":false},{"type":"link","label":"Release Process","href":"/copacetic/website/v0.4.x/release","docId":"release","unlisted":false}]},"docs":{"code-of-conduct":{"id":"code-of-conduct","title":"Code of Conduct","description":"Our Pledge","sidebar":"sidebar"},"contributing":{"id":"contributing","title":"Contributing","description":"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.","sidebar":"sidebar"},"design":{"id":"design","title":"Design","description":"Design Tenets","sidebar":"sidebar"},"faq":{"id":"faq","title":"FAQ","description":"What kind of vulnerabilities can Copa patch?","sidebar":"sidebar"},"github-action":{"id":"github-action","title":"Copa Github Action","description":"Overview","sidebar":"sidebar"},"installation":{"id":"installation","title":"Installation","description":"Homebrew","sidebar":"sidebar"},"introduction":{"id":"introduction","title":"Introduction","description":"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.","sidebar":"sidebar"},"quick-start":{"id":"quick-start","title":"Quick Start","description":"This sample illustrates how to patch containers using vulnerability reports with copa.","sidebar":"sidebar"},"release":{"id":"release","title":"Release Process","description":"Overview","sidebar":"sidebar"},"troubleshooting":{"id":"troubleshooting","title":"Troubleshooting","description":"Filtering Vulnerabilities","sidebar":"sidebar"}}}')}}]); \ No newline at end of file diff --git a/website/assets/js/c73303db.5731fdf2.js b/website/assets/js/c73303db.5731fdf2.js new file mode 100644 index 00000000..d2a54232 --- /dev/null +++ b/website/assets/js/c73303db.5731fdf2.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4914],{3927:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>r,default:()=>p,frontMatter:()=>s,metadata:()=>a,toc:()=>l});var o=n(4848),i=n(8453);const s={title:"Installation"},r=void 0,a={id:"installation",title:"Installation",description:"Homebrew",source:"@site/versioned_docs/version-v0.5.x/installation.md",sourceDirName:".",slug:"/installation",permalink:"/copacetic/website/v0.5.x/installation",draft:!1,unlisted:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Installation"},sidebar:"sidebar",previous:{title:"Introduction",permalink:"/copacetic/website/v0.5.x/"},next:{title:"Quick Start",permalink:"/copacetic/website/v0.5.x/quick-start"}},c={},l=[{value:"Homebrew",id:"homebrew",level:2},{value:"GitHub",id:"github",level:2},{value:"Development Setup",id:"development-setup",level:2}];function d(e){const t={a:"a",code:"code",h2:"h2",p:"p",pre:"pre",strong:"strong",...(0,i.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(t.h2,{id:"homebrew",children:"Homebrew"}),"\n",(0,o.jsxs)(t.p,{children:["On macOS and Linux, ",(0,o.jsx)(t.code,{children:"copa"})," can be installed via ",(0,o.jsx)(t.a,{href:"https://brew.sh/",children:"Homebrew"}),":"]}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-bash",children:"brew install copa\n"})}),"\n",(0,o.jsx)(t.h2,{id:"github",children:"GitHub"}),"\n",(0,o.jsxs)(t.p,{children:["You can download the latest and previous versions of ",(0,o.jsx)(t.code,{children:"copa"})," from the ",(0,o.jsx)(t.a,{href:"https://github.com/project-copacetic/copacetic/releases",children:"GitHub releases page"}),"."]}),"\n",(0,o.jsx)(t.h2,{id:"development-setup",children:"Development Setup"}),"\n",(0,o.jsxs)(t.p,{children:["The following instructions are for ",(0,o.jsx)(t.strong,{children:"Ubuntu 22.04"})," with the dependency versions supported as part of the ",(0,o.jsx)(t.a,{href:"./contributing.md/#visual-studio-code-development-container",children:"dev container"})," environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead."]}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-bash",children:"git clone https://github.com/project-copacetic/copacetic\ncd copacetic\nmake\n# OPTIONAL: install copa to a pathed folder\nsudo mv dist/linux_amd64/release/copa /usr/local/bin/\n"})})]})}function p(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,o.jsx)(t,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>r,x:()=>a});var o=n(6540);const i={},s=o.createContext(i);function r(e){const t=o.useContext(s);return o.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function a(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:r(e.components),o.createElement(s.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/c73303db.a8986184.js b/website/assets/js/c73303db.a8986184.js deleted file mode 100644 index 5d29aaf7..00000000 --- a/website/assets/js/c73303db.a8986184.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5082],{3905:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function c(e,t){if(null==e)return{};var n,r,a=function(e,t){if(null==e)return{};var n,r,a={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=r.createContext({}),p=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},s=function(e){var t=p(e.components);return r.createElement(l.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,l=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),u=p(n),m=a,f=u["".concat(l,".").concat(m)]||u[m]||d[m]||o;return n?r.createElement(f,i(i({ref:t},s),{},{components:n})):r.createElement(f,i({ref:t},s))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=m;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[u]="string"==typeof e?e:a,i[1]=c;for(var p=2;p<o;p++)i[p]=n[p];return r.createElement.apply(null,i)}return r.createElement.apply(null,n)}m.displayName="MDXCreateElement"},2992:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>c,toc:()=>p});var r=n(7462),a=(n(7294),n(3905));const o={title:"Installation"},i=void 0,c={unversionedId:"installation",id:"version-v0.5.x/installation",title:"Installation",description:"Homebrew",source:"@site/versioned_docs/version-v0.5.x/installation.md",sourceDirName:".",slug:"/installation",permalink:"/copacetic/website/v0.5.x/installation",draft:!1,tags:[],version:"v0.5.x",frontMatter:{title:"Installation"},sidebar:"sidebar",previous:{title:"Introduction",permalink:"/copacetic/website/v0.5.x/"},next:{title:"Quick Start",permalink:"/copacetic/website/v0.5.x/quick-start"}},l={},p=[{value:"Homebrew",id:"homebrew",level:2},{value:"GitHub",id:"github",level:2},{value:"Development Setup",id:"development-setup",level:2}],s={toc:p};function u(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h2",{id:"homebrew"},"Homebrew"),(0,a.kt)("p",null,"On macOS and Linux, ",(0,a.kt)("inlineCode",{parentName:"p"},"copa")," can be installed via ",(0,a.kt)("a",{parentName:"p",href:"https://brew.sh/"},"Homebrew"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"brew install copa\n")),(0,a.kt)("h2",{id:"github"},"GitHub"),(0,a.kt)("p",null,"You can download the latest and previous versions of ",(0,a.kt)("inlineCode",{parentName:"p"},"copa")," from the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/releases"},"GitHub releases page"),"."),(0,a.kt)("h2",{id:"development-setup"},"Development Setup"),(0,a.kt)("p",null,"The following instructions are for ",(0,a.kt)("strong",{parentName:"p"},"Ubuntu 22.04")," with the dependency versions supported as part of the ",(0,a.kt)("a",{parentName:"p",href:"/copacetic/website/v0.5.x/contributing/#visual-studio-code-development-container"},"dev container")," environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"git clone https://github.com/project-copacetic/copacetic\ncd copacetic\nmake\n# OPTIONAL: install copa to a pathed folder\nsudo mv dist/linux_amd64/release/copa /usr/local/bin/\n")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/d2339658.3f5cadcb.js b/website/assets/js/d2339658.3f5cadcb.js deleted file mode 100644 index b5b823b9..00000000 --- a/website/assets/js/d2339658.3f5cadcb.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8644],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>g});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),c=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(s.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=c(n),m=i,g=u["".concat(s,".").concat(m)]||u[m]||d[m]||r;return n?a.createElement(g,o(o({ref:t},p),{},{components:n})):a.createElement(g,o({ref:t},p))}));function g(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[u]="string"==typeof e?e:i,o[1]=l;for(var c=2;c<r;c++)o[c]=n[c];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}m.displayName="MDXCreateElement"},7316:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>l,toc:()=>c});var a=n(7462),i=(n(7294),n(3905));const r={title:"Introduction",slug:"/"},o="Project Copacetic: Directly patch container image vulnerabilities",l={unversionedId:"introduction",id:"version-v0.1.x/introduction",title:"Introduction",description:"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.",source:"@site/versioned_docs/version-v0.1.x/introduction.md",sourceDirName:".",slug:"/",permalink:"/copacetic/website/v0.1.x/",draft:!1,tags:[],version:"v0.1.x",frontMatter:{title:"Introduction",slug:"/"},sidebar:"sidebar",next:{title:"Installation",permalink:"/copacetic/website/v0.1.x/installation"}},s={},c=[{value:"Why?",id:"why",level:2},{value:"How?",id:"how",level:2}],p={toc:c};function u(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"project-copacetic-directly-patch-container-image-vulnerabilities"},"Project Copacetic: Directly patch container image vulnerabilities"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"copa")," is a CLI tool written in ",(0,i.kt)("a",{parentName:"p",href:"https://golang.org"},"Go")," and based on ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit"},"buildkit")," that can be used to directly patch container images given the vulnerability scanning results from popular tools like ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/aquasecurity/trivy"},"Trivy"),"."),(0,i.kt)("h2",{id:"why"},"Why?"),(0,i.kt)("p",null,"We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between ",(0,i.kt)("a",{parentName:"p",href:"https://www.bleepingcomputer.com/news/security/hackers-scan-for-vulnerabilities-within-15-minutes-of-disclosure/"},"vulnerability disclosure and active exploitation continues to narrow"),", there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option"),(0,i.kt)("li",{parentName:"ul"},"found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.")),(0,i.kt)("img",{title:"direct image patching",src:"/copacetic/website/img/direct-image-patching.png"}),(0,i.kt)("p",null,"In addition to filling the operational gap not met by left-shift security practices and tools, the ability of ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," to patch a container without requiring a rebuild of the container image provides other benefits:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Allows users other than the image publishers to also patch container images, such as DevSecOps engineers."),(0,i.kt)("li",{parentName:"ul"},"Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching."),(0,i.kt)("li",{parentName:"ul"},"Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild."),(0,i.kt)("li",{parentName:"ul"},"Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.")),(0,i.kt)("h2",{id:"how"},"How?"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," tool is an extensible engine that:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Parses the needed update packages from the container image\u2019s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats."),(0,i.kt)("li",{parentName:"ol"},"Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers."),(0,i.kt)("li",{parentName:"ol"},"Applies the resulting update binaries to the container image using buildkit.")),(0,i.kt)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),(0,i.kt)("p",null,"This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa supports patching ",(0,i.kt)("em",{parentName:"strong"},"existing")," container images"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"Devs don't need to build their images using specific tools or modify them in some way just to support container patching."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa works with the existing vulnerability scanning and mitigation ecosystems"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today."),(0,i.kt)("li",{parentName:"ul"},"Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.")))),(0,i.kt)("p",null,"For more details, refer to the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.1.x/design"},"copa design")," documentation."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/d2339658.5e176ea4.js b/website/assets/js/d2339658.5e176ea4.js new file mode 100644 index 00000000..6f556c99 --- /dev/null +++ b/website/assets/js/d2339658.5e176ea4.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1909],{6791:(e,i,n)=>{n.r(i),n.d(i,{assets:()=>c,contentTitle:()=>o,default:()=>h,frontMatter:()=>s,metadata:()=>r,toc:()=>l});var t=n(4848),a=n(8453);const s={title:"Introduction",slug:"/"},o="Project Copacetic: Directly patch container image vulnerabilities",r={id:"introduction",title:"Introduction",description:"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.",source:"@site/versioned_docs/version-v0.1.x/introduction.md",sourceDirName:".",slug:"/",permalink:"/copacetic/website/v0.1.x/",draft:!1,unlisted:!1,tags:[],version:"v0.1.x",frontMatter:{title:"Introduction",slug:"/"},sidebar:"sidebar",next:{title:"Installation",permalink:"/copacetic/website/v0.1.x/installation"}},c={},l=[{value:"Why?",id:"why",level:2},{value:"How?",id:"how",level:2}];function d(e){const i={a:"a",code:"code",em:"em",h1:"h1",h2:"h2",li:"li",ol:"ol",p:"p",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i.h1,{id:"project-copacetic-directly-patch-container-image-vulnerabilities",children:"Project Copacetic: Directly patch container image vulnerabilities"}),"\n",(0,t.jsxs)(i.p,{children:[(0,t.jsx)(i.code,{children:"copa"})," is a CLI tool written in ",(0,t.jsx)(i.a,{href:"https://golang.org",children:"Go"})," and based on ",(0,t.jsx)(i.a,{href:"https://github.com/moby/buildkit",children:"buildkit"})," that can be used to directly patch container images given the vulnerability scanning results from popular tools like ",(0,t.jsx)(i.a,{href:"https://github.com/aquasecurity/trivy",children:"Trivy"}),"."]}),"\n",(0,t.jsx)(i.h2,{id:"why",children:"Why?"}),"\n",(0,t.jsxs)(i.p,{children:["We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between ",(0,t.jsx)(i.a,{href:"https://www.bleepingcomputer.com/news/security/hackers-scan-for-vulnerabilities-within-15-minutes-of-disclosure/",children:"vulnerability disclosure and active exploitation continues to narrow"}),", there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:"]}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option"}),"\n",(0,t.jsx)(i.li,{children:"found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs."}),"\n"]}),"\n",(0,t.jsx)("img",{title:"direct image patching",src:"/copacetic/website/img/direct-image-patching.png"}),"\n",(0,t.jsxs)(i.p,{children:["In addition to filling the operational gap not met by left-shift security practices and tools, the ability of ",(0,t.jsx)(i.code,{children:"copa"})," to patch a container without requiring a rebuild of the container image provides other benefits:"]}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Allows users other than the image publishers to also patch container images, such as DevSecOps engineers."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image."}),"\n"]}),"\n",(0,t.jsx)(i.h2,{id:"how",children:"How?"}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"copa"})," tool is an extensible engine that:"]}),"\n",(0,t.jsxs)(i.ol,{children:["\n",(0,t.jsx)(i.li,{children:"Parses the needed update packages from the container image\u2019s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats."}),"\n",(0,t.jsx)(i.li,{children:"Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers."}),"\n",(0,t.jsx)(i.li,{children:"Applies the resulting update binaries to the container image using buildkit."}),"\n"]}),"\n",(0,t.jsx)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),"\n",(0,t.jsx)(i.p,{children:"This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:"}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:[(0,t.jsxs)(i.strong,{children:["Copa supports patching ",(0,t.jsx)(i.em,{children:"existing"})," container images"]}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Devs don't need to build their images using specific tools or modify them in some way just to support container patching."}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Copa works with the existing vulnerability scanning and mitigation ecosystems"}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today."}),"\n",(0,t.jsx)(i.li,{children:"Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports."}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image"}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today."}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.p,{children:["For more details, refer to the ",(0,t.jsx)(i.a,{href:"/copacetic/website/v0.1.x/design",children:"copa design"})," documentation."]})]})}function h(e={}){const{wrapper:i}={...(0,a.R)(),...e.components};return i?(0,t.jsx)(i,{...e,children:(0,t.jsx)(d,{...e})}):d(e)}},8453:(e,i,n)=>{n.d(i,{R:()=>o,x:()=>r});var t=n(6540);const a={},s=t.createContext(a);function o(e){const i=t.useContext(s);return t.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function r(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),t.createElement(s.Provider,{value:i},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/d832efb0.23e6d38e.js b/website/assets/js/d832efb0.23e6d38e.js deleted file mode 100644 index 53991da0..00000000 --- a/website/assets/js/d832efb0.23e6d38e.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9070],{3905:(e,n,t)=>{t.d(n,{Zo:()=>c,kt:()=>g});var a=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function o(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?r(Object(t),!0).forEach((function(n){i(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):r(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))}))}return e}function s(e,n){if(null==e)return{};var t,a,i=function(e,n){if(null==e)return{};var t,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)t=r[a],n.indexOf(t)>=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)t=r[a],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var l=a.createContext({}),p=function(e){var n=a.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},c=function(e){var n=p(e.components);return a.createElement(l.Provider,{value:n},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},m=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),u=p(t),m=i,g=u["".concat(l,".").concat(m)]||u[m]||d[m]||r;return t?a.createElement(g,o(o({ref:n},c),{},{components:t})):a.createElement(g,o({ref:n},c))}));function g(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,o=new Array(r);o[0]=m;var s={};for(var l in n)hasOwnProperty.call(n,l)&&(s[l]=n[l]);s.originalType=e,s[u]="string"==typeof e?e:i,o[1]=s;for(var p=2;p<r;p++)o[p]=t[p];return a.createElement.apply(null,o)}return a.createElement.apply(null,t)}m.displayName="MDXCreateElement"},4293:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>p});var a=t(7462),i=(t(7294),t(3905));const r={title:"Scanner Plugins"},o="Motivation",s={unversionedId:"scanner-plugins",id:"version-v0.6.x/scanner-plugins",title:"Scanner Plugins",description:"By default, copa uses Trivy to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners.",source:"@site/versioned_docs/version-v0.6.x/scanner-plugins.md",sourceDirName:".",slug:"/scanner-plugins",permalink:"/copacetic/website/scanner-plugins",draft:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Scanner Plugins"},sidebar:"sidebar",previous:{title:"Output",permalink:"/copacetic/website/output"},next:{title:"Contributing",permalink:"/copacetic/website/contributing"}},l={},p=[{value:"v1alpha1",id:"v1alpha1",level:2}],c={toc:p};function u(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,a.Z)({},c,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"motivation"},"Motivation"),(0,i.kt)("p",null,"By default, ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," uses ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/aquasecurity/trivy"},"Trivy")," to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners."),(0,i.kt)("p",null,"Starting with v0.5.0 and later, ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," offers extensibility to support different vulnerability scanners. Plugin architecture allows users to use the vulnerability scanner of their choice to patch container images without having to modify ",(0,i.kt)("inlineCode",{parentName:"p"},"copa"),"'s core codebase."),(0,i.kt)("h1",{id:"usage"},"Usage"),(0,i.kt)("p",null,"Scanner plugin binaries must be in ",(0,i.kt)("inlineCode",{parentName:"p"},"$PATH"),", and should be prefixed with ",(0,i.kt)("inlineCode",{parentName:"p"},"copa-")," and have executable permissions. Copa will automatically detect and use the scanner plugin if it is in ",(0,i.kt)("inlineCode",{parentName:"p"},"$PATH"),"."),(0,i.kt)("p",null,"For example, if you have a scanner plugin binary called ",(0,i.kt)("inlineCode",{parentName:"p"},"copa-foo")," in ",(0,i.kt)("inlineCode",{parentName:"p"},"$PATH"),", you can run ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," with the following command:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"copa patch --scanner foo --image $IMAGE ...\n")),(0,i.kt)("h1",{id:"scanner-plugins-from-the-community"},"Scanner Plugins from the Community"),(0,i.kt)("p",null,"If you have built a scanner plugin and would like to add it to this list, please submit a PR to update this section with your plugin."),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},"If you have any issues with a specific plugin, please open an issue in the applicable plugin's repository.")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Grype: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/anubhav06/copa-grype"},"https://github.com/anubhav06/copa-grype"))),(0,i.kt)("h1",{id:"writing-a-scanner-plugin"},"Writing a Scanner Plugin"),(0,i.kt)("p",null,"Please see instructions at ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/scanner-plugin-template"},"Scanner Plugin Template")," for a template to get started with writing a scanner plugin."),(0,i.kt)("h1",{id:"scanner-plugin-interface"},"Scanner Plugin Interface"),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},(0,i.kt)("inlineCode",{parentName:"p"},"alpha")," versions of the API are not guarenteed to be backwards compatible. Once the API graduates to ",(0,i.kt)("inlineCode",{parentName:"p"},"beta")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"stable"),", it will be backwards compatible.")),(0,i.kt)("p",null,"Scanner plugins must implement the following interface:"),(0,i.kt)("h2",{id:"v1alpha1"},"v1alpha1"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-go"},'type UpdateManifest struct {\n // API version of the interface (e.g. v1alpha1)\n APIVersion string `json:"apiVersion"`\n // Metadata contains information about the OS and config\n Metadata Metadata `json:"metadata"`\n // Updates is a list of UpdatePackage that contains information about the package updates\n Updates UpdatePackages `json:"updates"`\n}\n\n// UpdatePackages is a list of UpdatePackage\ntype UpdatePackages []UpdatePackage\n\n// Metadata contains information about the OS and config\ntype Metadata struct {\n OS OS `json:"os"`\n Config Config `json:"config"`\n}\n\ntype OS struct {\n // OS Type (e.g. debian, alpine, etc.)\n Type string `json:"type"`\n // OS Version (e.g. 11.3)\n Version string `json:"version"`\n}\n\n// Config contains information about the config\ntype Config struct {\n // OS Architecture (e.g. amd64, arm64)\n Arch string `json:"arch"`\n}\n\n// UpdatePackage contains information about the package update\ntype UpdatePackage struct {\n // Package name\n Name string `json:"name"`\n // Installed version\n InstalledVersion string `json:"installedVersion"`\n // Fixed version\n FixedVersion string `json:"fixedVersion"`\n // Vulnerability ID\n VulnerabilityID string `json:"vulnerabilityID"`\n}\n')),(0,i.kt)("p",null,"From the above, we can see that the plugin must return a JSON object via standard out with the following fields. For example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'{\n "apiVersion": "v1alpha1",\n "metadata": {\n "os": {\n "type": "debian",\n "version": "11.3",\n },\n "config": {\n "arch": "amd64"\n }\n },\n "updates": [\n {\n "name": "libcurl4",\n "installedVersion": "7.74.0-1.3+deb11u1",\n "fixedVersion": "7.74.0-1.3+deb11u2",\n "vulnerabilityID": "CVE-2021-22945"\n }\n ]\n}\n')))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/d832efb0.70cfe285.js b/website/assets/js/d832efb0.70cfe285.js new file mode 100644 index 00000000..fe414102 --- /dev/null +++ b/website/assets/js/d832efb0.70cfe285.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2375],{2285:(n,e,t)=>{t.r(e),t.d(e,{assets:()=>c,contentTitle:()=>r,default:()=>u,frontMatter:()=>s,metadata:()=>o,toc:()=>l});var i=t(4848),a=t(8453);const s={title:"Scanner Plugins"},r="Motivation",o={id:"scanner-plugins",title:"Scanner Plugins",description:"By default, copa uses Trivy to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners.",source:"@site/versioned_docs/version-v0.6.x/scanner-plugins.md",sourceDirName:".",slug:"/scanner-plugins",permalink:"/copacetic/website/scanner-plugins",draft:!1,unlisted:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Scanner Plugins"},sidebar:"sidebar",previous:{title:"Output",permalink:"/copacetic/website/output"},next:{title:"Contributing",permalink:"/copacetic/website/contributing"}},c={},l=[{value:"v1alpha1",id:"v1alpha1",level:2}];function d(n){const e={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",li:"li",p:"p",pre:"pre",ul:"ul",...(0,a.R)(),...n.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(e.h1,{id:"motivation",children:"Motivation"}),"\n",(0,i.jsxs)(e.p,{children:["By default, ",(0,i.jsx)(e.code,{children:"copa"})," uses ",(0,i.jsx)(e.a,{href:"https://github.com/aquasecurity/trivy",children:"Trivy"})," to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners."]}),"\n",(0,i.jsxs)(e.p,{children:["Starting with v0.5.0 and later, ",(0,i.jsx)(e.code,{children:"copa"})," offers extensibility to support different vulnerability scanners. Plugin architecture allows users to use the vulnerability scanner of their choice to patch container images without having to modify ",(0,i.jsx)(e.code,{children:"copa"}),"'s core codebase."]}),"\n",(0,i.jsx)(e.h1,{id:"usage",children:"Usage"}),"\n",(0,i.jsxs)(e.p,{children:["Scanner plugin binaries must be in ",(0,i.jsx)(e.code,{children:"$PATH"}),", and should be prefixed with ",(0,i.jsx)(e.code,{children:"copa-"})," and have executable permissions. Copa will automatically detect and use the scanner plugin if it is in ",(0,i.jsx)(e.code,{children:"$PATH"}),"."]}),"\n",(0,i.jsxs)(e.p,{children:["For example, if you have a scanner plugin binary called ",(0,i.jsx)(e.code,{children:"copa-foo"})," in ",(0,i.jsx)(e.code,{children:"$PATH"}),", you can run ",(0,i.jsx)(e.code,{children:"copa"})," with the following command:"]}),"\n",(0,i.jsx)(e.pre,{children:(0,i.jsx)(e.code,{className:"language-bash",children:"copa patch --scanner foo --image $IMAGE ...\n"})}),"\n",(0,i.jsx)(e.h1,{id:"scanner-plugins-from-the-community",children:"Scanner Plugins from the Community"}),"\n",(0,i.jsx)(e.p,{children:"If you have built a scanner plugin and would like to add it to this list, please submit a PR to update this section with your plugin."}),"\n",(0,i.jsx)(e.admonition,{type:"note",children:(0,i.jsx)(e.p,{children:"If you have any issues with a specific plugin, please open an issue in the applicable plugin's repository."})}),"\n",(0,i.jsxs)(e.ul,{children:["\n",(0,i.jsxs)(e.li,{children:["Grype: ",(0,i.jsx)(e.a,{href:"https://github.com/anubhav06/copa-grype",children:"https://github.com/anubhav06/copa-grype"})]}),"\n"]}),"\n",(0,i.jsx)(e.h1,{id:"writing-a-scanner-plugin",children:"Writing a Scanner Plugin"}),"\n",(0,i.jsxs)(e.p,{children:["Please see instructions at ",(0,i.jsx)(e.a,{href:"https://github.com/project-copacetic/scanner-plugin-template",children:"Scanner Plugin Template"})," for a template to get started with writing a scanner plugin."]}),"\n",(0,i.jsx)(e.h1,{id:"scanner-plugin-interface",children:"Scanner Plugin Interface"}),"\n",(0,i.jsx)(e.admonition,{type:"note",children:(0,i.jsxs)(e.p,{children:[(0,i.jsx)(e.code,{children:"alpha"})," versions of the API are not guarenteed to be backwards compatible. Once the API graduates to ",(0,i.jsx)(e.code,{children:"beta"})," and ",(0,i.jsx)(e.code,{children:"stable"}),", it will be backwards compatible."]})}),"\n",(0,i.jsx)(e.p,{children:"Scanner plugins must implement the following interface:"}),"\n",(0,i.jsx)(e.h2,{id:"v1alpha1",children:"v1alpha1"}),"\n",(0,i.jsx)(e.pre,{children:(0,i.jsx)(e.code,{className:"language-go",children:'type UpdateManifest struct {\n // API version of the interface (e.g. v1alpha1)\n APIVersion string `json:"apiVersion"`\n // Metadata contains information about the OS and config\n Metadata Metadata `json:"metadata"`\n // Updates is a list of UpdatePackage that contains information about the package updates\n Updates UpdatePackages `json:"updates"`\n}\n\n// UpdatePackages is a list of UpdatePackage\ntype UpdatePackages []UpdatePackage\n\n// Metadata contains information about the OS and config\ntype Metadata struct {\n OS OS `json:"os"`\n Config Config `json:"config"`\n}\n\ntype OS struct {\n // OS Type (e.g. debian, alpine, etc.)\n Type string `json:"type"`\n // OS Version (e.g. 11.3)\n Version string `json:"version"`\n}\n\n// Config contains information about the config\ntype Config struct {\n // OS Architecture (e.g. amd64, arm64)\n Arch string `json:"arch"`\n}\n\n// UpdatePackage contains information about the package update\ntype UpdatePackage struct {\n // Package name\n Name string `json:"name"`\n // Installed version\n InstalledVersion string `json:"installedVersion"`\n // Fixed version\n FixedVersion string `json:"fixedVersion"`\n // Vulnerability ID\n VulnerabilityID string `json:"vulnerabilityID"`\n}\n'})}),"\n",(0,i.jsx)(e.p,{children:"From the above, we can see that the plugin must return a JSON object via standard out with the following fields. For example:"}),"\n",(0,i.jsx)(e.pre,{children:(0,i.jsx)(e.code,{className:"language-json",children:'{\n "apiVersion": "v1alpha1",\n "metadata": {\n "os": {\n "type": "debian",\n "version": "11.3",\n },\n "config": {\n "arch": "amd64"\n }\n },\n "updates": [\n {\n "name": "libcurl4",\n "installedVersion": "7.74.0-1.3+deb11u1",\n "fixedVersion": "7.74.0-1.3+deb11u2",\n "vulnerabilityID": "CVE-2021-22945"\n }\n ]\n}\n'})})]})}function u(n={}){const{wrapper:e}={...(0,a.R)(),...n.components};return e?(0,i.jsx)(e,{...n,children:(0,i.jsx)(d,{...n})}):d(n)}},8453:(n,e,t)=>{t.d(e,{R:()=>r,x:()=>o});var i=t(6540);const a={},s=i.createContext(a);function r(n){const e=i.useContext(s);return i.useMemo((function(){return"function"==typeof n?n(e):{...e,...n}}),[e,n])}function o(n){let e;return e=n.disableParentContext?"function"==typeof n.components?n.components(a):n.components||a:r(n.components),i.createElement(s.Provider,{value:e},n.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/d9461b6c.63467998.js b/website/assets/js/d9461b6c.63467998.js new file mode 100644 index 00000000..e3b57562 --- /dev/null +++ b/website/assets/js/d9461b6c.63467998.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1268],{308:(e,i,n)=>{n.r(i),n.d(i,{assets:()=>c,contentTitle:()=>o,default:()=>h,frontMatter:()=>s,metadata:()=>r,toc:()=>l});var t=n(4848),a=n(8453);const s={title:"Introduction",slug:"/"},o="Project Copacetic: Directly patch container image vulnerabilities",r={id:"introduction",title:"Introduction",description:"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.",source:"@site/versioned_docs/version-v0.2.x/introduction.md",sourceDirName:".",slug:"/",permalink:"/copacetic/website/v0.2.x/",draft:!1,unlisted:!1,tags:[],version:"v0.2.x",frontMatter:{title:"Introduction",slug:"/"},sidebar:"sidebar",next:{title:"Installation",permalink:"/copacetic/website/v0.2.x/installation"}},c={},l=[{value:"Why?",id:"why",level:2},{value:"How?",id:"how",level:2}];function d(e){const i={a:"a",code:"code",em:"em",h1:"h1",h2:"h2",li:"li",ol:"ol",p:"p",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i.h1,{id:"project-copacetic-directly-patch-container-image-vulnerabilities",children:"Project Copacetic: Directly patch container image vulnerabilities"}),"\n",(0,t.jsxs)(i.p,{children:[(0,t.jsx)(i.code,{children:"copa"})," is a CLI tool written in ",(0,t.jsx)(i.a,{href:"https://golang.org",children:"Go"})," and based on ",(0,t.jsx)(i.a,{href:"https://github.com/moby/buildkit",children:"buildkit"})," that can be used to directly patch container images given the vulnerability scanning results from popular tools like ",(0,t.jsx)(i.a,{href:"https://github.com/aquasecurity/trivy",children:"Trivy"}),"."]}),"\n",(0,t.jsx)(i.h2,{id:"why",children:"Why?"}),"\n",(0,t.jsxs)(i.p,{children:["We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between ",(0,t.jsx)(i.a,{href:"https://www.bleepingcomputer.com/news/security/hackers-scan-for-vulnerabilities-within-15-minutes-of-disclosure/",children:"vulnerability disclosure and active exploitation continues to narrow"}),", there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:"]}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option"}),"\n",(0,t.jsx)(i.li,{children:"found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs."}),"\n"]}),"\n",(0,t.jsx)("img",{title:"direct image patching",src:"/copacetic/website/img/direct-image-patching.png"}),"\n",(0,t.jsxs)(i.p,{children:["In addition to filling the operational gap not met by left-shift security practices and tools, the ability of ",(0,t.jsx)(i.code,{children:"copa"})," to patch a container without requiring a rebuild of the container image provides other benefits:"]}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Allows users other than the image publishers to also patch container images, such as DevSecOps engineers."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild."}),"\n",(0,t.jsx)(i.li,{children:"Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image."}),"\n"]}),"\n",(0,t.jsx)(i.h2,{id:"how",children:"How?"}),"\n",(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"copa"})," tool is an extensible engine that:"]}),"\n",(0,t.jsxs)(i.ol,{children:["\n",(0,t.jsx)(i.li,{children:"Parses the needed update packages from the container image\u2019s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats."}),"\n",(0,t.jsx)(i.li,{children:"Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers."}),"\n",(0,t.jsx)(i.li,{children:"Applies the resulting update binaries to the container image using buildkit."}),"\n"]}),"\n",(0,t.jsx)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),"\n",(0,t.jsx)(i.p,{children:"This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:"}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:[(0,t.jsxs)(i.strong,{children:["Copa supports patching ",(0,t.jsx)(i.em,{children:"existing"})," container images"]}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Devs don't need to build their images using specific tools or modify them in some way just to support container patching."}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Copa works with the existing vulnerability scanning and mitigation ecosystems"}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today."}),"\n",(0,t.jsx)(i.li,{children:"Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports."}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image"}),".","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today."}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.p,{children:["For more details, refer to the ",(0,t.jsx)(i.a,{href:"/copacetic/website/v0.2.x/design",children:"copa design"})," documentation."]})]})}function h(e={}){const{wrapper:i}={...(0,a.R)(),...e.components};return i?(0,t.jsx)(i,{...e,children:(0,t.jsx)(d,{...e})}):d(e)}},8453:(e,i,n)=>{n.d(i,{R:()=>o,x:()=>r});var t=n(6540);const a={},s=t.createContext(a);function o(e){const i=t.useContext(s);return t.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function r(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),t.createElement(s.Provider,{value:i},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/d9461b6c.cfb2e633.js b/website/assets/js/d9461b6c.cfb2e633.js deleted file mode 100644 index 1c832547..00000000 --- a/website/assets/js/d9461b6c.cfb2e633.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>g});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),c=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(s.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=c(n),m=i,g=u["".concat(s,".").concat(m)]||u[m]||d[m]||r;return n?a.createElement(g,o(o({ref:t},p),{},{components:n})):a.createElement(g,o({ref:t},p))}));function g(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[u]="string"==typeof e?e:i,o[1]=l;for(var c=2;c<r;c++)o[c]=n[c];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}m.displayName="MDXCreateElement"},5814:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>l,toc:()=>c});var a=n(7462),i=(n(7294),n(3905));const r={title:"Introduction",slug:"/"},o="Project Copacetic: Directly patch container image vulnerabilities",l={unversionedId:"introduction",id:"version-v0.2.x/introduction",title:"Introduction",description:"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.",source:"@site/versioned_docs/version-v0.2.x/introduction.md",sourceDirName:".",slug:"/",permalink:"/copacetic/website/v0.2.x/",draft:!1,tags:[],version:"v0.2.x",frontMatter:{title:"Introduction",slug:"/"},sidebar:"sidebar",next:{title:"Installation",permalink:"/copacetic/website/v0.2.x/installation"}},s={},c=[{value:"Why?",id:"why",level:2},{value:"How?",id:"how",level:2}],p={toc:c};function u(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"project-copacetic-directly-patch-container-image-vulnerabilities"},"Project Copacetic: Directly patch container image vulnerabilities"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"copa")," is a CLI tool written in ",(0,i.kt)("a",{parentName:"p",href:"https://golang.org"},"Go")," and based on ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/moby/buildkit"},"buildkit")," that can be used to directly patch container images given the vulnerability scanning results from popular tools like ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/aquasecurity/trivy"},"Trivy"),"."),(0,i.kt)("h2",{id:"why"},"Why?"),(0,i.kt)("p",null,"We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between ",(0,i.kt)("a",{parentName:"p",href:"https://www.bleepingcomputer.com/news/security/hackers-scan-for-vulnerabilities-within-15-minutes-of-disclosure/"},"vulnerability disclosure and active exploitation continues to narrow"),", there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option"),(0,i.kt)("li",{parentName:"ul"},"found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.")),(0,i.kt)("img",{title:"direct image patching",src:"/copacetic/website/img/direct-image-patching.png"}),(0,i.kt)("p",null,"In addition to filling the operational gap not met by left-shift security practices and tools, the ability of ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," to patch a container without requiring a rebuild of the container image provides other benefits:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Allows users other than the image publishers to also patch container images, such as DevSecOps engineers."),(0,i.kt)("li",{parentName:"ul"},"Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching."),(0,i.kt)("li",{parentName:"ul"},"Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild."),(0,i.kt)("li",{parentName:"ul"},"Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.")),(0,i.kt)("h2",{id:"how"},"How?"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"copa")," tool is an extensible engine that:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Parses the needed update packages from the container image\u2019s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats."),(0,i.kt)("li",{parentName:"ol"},"Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers."),(0,i.kt)("li",{parentName:"ol"},"Applies the resulting update binaries to the container image using buildkit.")),(0,i.kt)("img",{title:"report-driven vulnerability patching",src:"/copacetic/website/img/vulnerability-patch.png"}),(0,i.kt)("p",null,"This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa supports patching ",(0,i.kt)("em",{parentName:"strong"},"existing")," container images"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"Devs don't need to build their images using specific tools or modify them in some way just to support container patching."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa works with the existing vulnerability scanning and mitigation ecosystems"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today."),(0,i.kt)("li",{parentName:"ul"},"Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image"),".",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.")))),(0,i.kt)("p",null,"For more details, refer to the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.2.x/design"},"copa design")," documentation."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/dea0f9ea.c17bbf7a.js b/website/assets/js/dea0f9ea.c17bbf7a.js new file mode 100644 index 00000000..fc46e64f --- /dev/null +++ b/website/assets/js/dea0f9ea.c17bbf7a.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9228],{9350:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>l});var o=i(4848),t=i(8453);const r={title:"Code of Conduct"},a="Contributor Covenant Code of Conduct",s={id:"code-of-conduct",title:"Code of Conduct",description:"Our Pledge",source:"@site/docs/code-of-conduct.md",sourceDirName:".",slug:"/code-of-conduct",permalink:"/copacetic/website/next/code-of-conduct",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{title:"Code of Conduct"},sidebar:"sidebar",previous:{title:"Contributing",permalink:"/copacetic/website/next/contributing"},next:{title:"Design",permalink:"/copacetic/website/next/design"}},c={},l=[{value:"Our Pledge",id:"our-pledge",level:2},{value:"Our Standards",id:"our-standards",level:2},{value:"Enforcement Responsibilities",id:"enforcement-responsibilities",level:2},{value:"Scope",id:"scope",level:2},{value:"Enforcement",id:"enforcement",level:2},{value:"Enforcement Guidelines",id:"enforcement-guidelines",level:2},{value:"1. Correction",id:"1-correction",level:3},{value:"2. Warning",id:"2-warning",level:3},{value:"3. Temporary Ban",id:"3-temporary-ban",level:3},{value:"4. Permanent Ban",id:"4-permanent-ban",level:3},{value:"Attribution",id:"attribution",level:2}];function d(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",li:"li",p:"p",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.h1,{id:"contributor-covenant-code-of-conduct",children:"Contributor Covenant Code of Conduct"}),"\n",(0,o.jsx)(n.h2,{id:"our-pledge",children:"Our Pledge"}),"\n",(0,o.jsx)(n.p,{children:"We as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation."}),"\n",(0,o.jsx)(n.p,{children:"We pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community."}),"\n",(0,o.jsx)(n.h2,{id:"our-standards",children:"Our Standards"}),"\n",(0,o.jsx)(n.p,{children:"Examples of behavior that contributes to a positive environment for our\ncommunity include:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"Demonstrating empathy and kindness toward other people"}),"\n",(0,o.jsx)(n.li,{children:"Being respectful of differing opinions, viewpoints, and experiences"}),"\n",(0,o.jsx)(n.li,{children:"Giving and gracefully accepting constructive feedback"}),"\n",(0,o.jsx)(n.li,{children:"Accepting responsibility and apologizing to those affected by our mistakes,\nand learning from the experience"}),"\n",(0,o.jsx)(n.li,{children:"Focusing on what is best not just for us as individuals, but for the overall\ncommunity"}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"Examples of unacceptable behavior include:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"The use of sexualized language or imagery, and sexual attention or advances of\nany kind"}),"\n",(0,o.jsx)(n.li,{children:"Trolling, insulting or derogatory comments, and personal or political attacks"}),"\n",(0,o.jsx)(n.li,{children:"Public or private harassment"}),"\n",(0,o.jsx)(n.li,{children:"Publishing others' private information, such as a physical or email address,\nwithout their explicit permission"}),"\n",(0,o.jsx)(n.li,{children:"Other conduct which could reasonably be considered inappropriate in a\nprofessional setting"}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"enforcement-responsibilities",children:"Enforcement Responsibilities"}),"\n",(0,o.jsx)(n.p,{children:"Community leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful."}),"\n",(0,o.jsx)(n.p,{children:"Community leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate."}),"\n",(0,o.jsx)(n.h2,{id:"scope",children:"Scope"}),"\n",(0,o.jsx)(n.p,{children:"This Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event."}),"\n",(0,o.jsx)(n.h2,{id:"enforcement",children:"Enforcement"}),"\n",(0,o.jsxs)(n.p,{children:["Instances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n",(0,o.jsx)(n.code,{children:"project-copacetic@googlegroups.com"}),".\nAll complaints will be reviewed and investigated promptly and fairly."]}),"\n",(0,o.jsx)(n.p,{children:"All community leaders are obligated to respect the privacy and security of the\nreporter of any incident."}),"\n",(0,o.jsx)(n.h2,{id:"enforcement-guidelines",children:"Enforcement Guidelines"}),"\n",(0,o.jsx)(n.p,{children:"Community leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:"}),"\n",(0,o.jsx)(n.h3,{id:"1-correction",children:"1. Correction"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested."]}),"\n",(0,o.jsx)(n.h3,{id:"2-warning",children:"2. Warning"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": A violation through a single incident or series of\nactions."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban."]}),"\n",(0,o.jsx)(n.h3,{id:"3-temporary-ban",children:"3. Temporary Ban"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": A serious violation of community standards, including\nsustained inappropriate behavior."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban."]}),"\n",(0,o.jsx)(n.h3,{id:"4-permanent-ban",children:"4. Permanent Ban"}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Community Impact"}),": Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals."]}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Consequence"}),": A permanent ban from any sort of public interaction within the\ncommunity."]}),"\n",(0,o.jsx)(n.h2,{id:"attribution",children:"Attribution"}),"\n",(0,o.jsxs)(n.p,{children:["This Code of Conduct is adapted from the ",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org",children:"Contributor Covenant"}),",\nversion 2.1, available at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html",children:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["Community Impact Guidelines were inspired by\n",(0,o.jsx)(n.a,{href:"https://github.com/mozilla/diversity",children:"Mozilla's code of conduct enforcement ladder"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["For answers to common questions about this code of conduct, see the FAQ at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/faq",children:"https://www.contributor-covenant.org/faq"}),". Translations are available at\n",(0,o.jsx)(n.a,{href:"https://www.contributor-covenant.org/translations",children:"https://www.contributor-covenant.org/translations"}),"."]})]})}function u(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>a,x:()=>s});var o=i(6540);const t={},r=o.createContext(t);function a(e){const n=o.useContext(r);return o.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function s(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:a(e.components),o.createElement(r.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/dea0f9ea.d742361f.js b/website/assets/js/dea0f9ea.d742361f.js deleted file mode 100644 index 41508805..00000000 --- a/website/assets/js/dea0f9ea.d742361f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6352],{3905:(e,n,t)=>{t.d(n,{Zo:()=>p,kt:()=>f});var o=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);n&&(o=o.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,o)}return t}function a(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?r(Object(t),!0).forEach((function(n){i(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):r(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))}))}return e}function l(e,n){if(null==e)return{};var t,o,i=function(e,n){if(null==e)return{};var t,o,i={},r=Object.keys(e);for(o=0;o<r.length;o++)t=r[o],n.indexOf(t)>=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o<r.length;o++)t=r[o],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var c=o.createContext({}),s=function(e){var n=o.useContext(c),t=n;return e&&(t="function"==typeof e?e(n):a(a({},n),e)),t},p=function(e){var n=s(e.components);return o.createElement(c.Provider,{value:n},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var n=e.children;return o.createElement(o.Fragment,{},n)}},m=o.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,c=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=s(t),m=i,f=u["".concat(c,".").concat(m)]||u[m]||d[m]||r;return t?o.createElement(f,a(a({ref:n},p),{},{components:t})):o.createElement(f,a({ref:n},p))}));function f(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,a=new Array(r);a[0]=m;var l={};for(var c in n)hasOwnProperty.call(n,c)&&(l[c]=n[c]);l.originalType=e,l[u]="string"==typeof e?e:i,a[1]=l;for(var s=2;s<r;s++)a[s]=t[s];return o.createElement.apply(null,a)}return o.createElement.apply(null,t)}m.displayName="MDXCreateElement"},6853:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>r,metadata:()=>l,toc:()=>s});var o=t(7462),i=(t(7294),t(3905));const r={title:"Code of Conduct"},a="Contributor Covenant Code of Conduct",l={unversionedId:"code-of-conduct",id:"code-of-conduct",title:"Code of Conduct",description:"Our Pledge",source:"@site/docs/code-of-conduct.md",sourceDirName:".",slug:"/code-of-conduct",permalink:"/copacetic/website/next/code-of-conduct",draft:!1,tags:[],version:"current",frontMatter:{title:"Code of Conduct"},sidebar:"sidebar",previous:{title:"Contributing",permalink:"/copacetic/website/next/contributing"},next:{title:"Design",permalink:"/copacetic/website/next/design"}},c={},s=[{value:"Our Pledge",id:"our-pledge",level:2},{value:"Our Standards",id:"our-standards",level:2},{value:"Enforcement Responsibilities",id:"enforcement-responsibilities",level:2},{value:"Scope",id:"scope",level:2},{value:"Enforcement",id:"enforcement",level:2},{value:"Enforcement Guidelines",id:"enforcement-guidelines",level:2},{value:"1. Correction",id:"1-correction",level:3},{value:"2. Warning",id:"2-warning",level:3},{value:"3. Temporary Ban",id:"3-temporary-ban",level:3},{value:"4. Permanent Ban",id:"4-permanent-ban",level:3},{value:"Attribution",id:"attribution",level:2}],p={toc:s};function u(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,o.Z)({},p,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"contributor-covenant-code-of-conduct"},"Contributor Covenant Code of Conduct"),(0,i.kt)("h2",{id:"our-pledge"},"Our Pledge"),(0,i.kt)("p",null,"We as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation."),(0,i.kt)("p",null,"We pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community."),(0,i.kt)("h2",{id:"our-standards"},"Our Standards"),(0,i.kt)("p",null,"Examples of behavior that contributes to a positive environment for our\ncommunity include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Demonstrating empathy and kindness toward other people"),(0,i.kt)("li",{parentName:"ul"},"Being respectful of differing opinions, viewpoints, and experiences"),(0,i.kt)("li",{parentName:"ul"},"Giving and gracefully accepting constructive feedback"),(0,i.kt)("li",{parentName:"ul"},"Accepting responsibility and apologizing to those affected by our mistakes,\nand learning from the experience"),(0,i.kt)("li",{parentName:"ul"},"Focusing on what is best not just for us as individuals, but for the overall\ncommunity")),(0,i.kt)("p",null,"Examples of unacceptable behavior include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"The use of sexualized language or imagery, and sexual attention or advances of\nany kind"),(0,i.kt)("li",{parentName:"ul"},"Trolling, insulting or derogatory comments, and personal or political attacks"),(0,i.kt)("li",{parentName:"ul"},"Public or private harassment"),(0,i.kt)("li",{parentName:"ul"},"Publishing others' private information, such as a physical or email address,\nwithout their explicit permission"),(0,i.kt)("li",{parentName:"ul"},"Other conduct which could reasonably be considered inappropriate in a\nprofessional setting")),(0,i.kt)("h2",{id:"enforcement-responsibilities"},"Enforcement Responsibilities"),(0,i.kt)("p",null,"Community leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful."),(0,i.kt)("p",null,"Community leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate."),(0,i.kt)("h2",{id:"scope"},"Scope"),(0,i.kt)("p",null,"This Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event."),(0,i.kt)("h2",{id:"enforcement"},"Enforcement"),(0,i.kt)("p",null,"Instances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n",(0,i.kt)("inlineCode",{parentName:"p"},"project-copacetic@googlegroups.com"),".\nAll complaints will be reviewed and investigated promptly and fairly."),(0,i.kt)("p",null,"All community leaders are obligated to respect the privacy and security of the\nreporter of any incident."),(0,i.kt)("h2",{id:"enforcement-guidelines"},"Enforcement Guidelines"),(0,i.kt)("p",null,"Community leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:"),(0,i.kt)("h3",{id:"1-correction"},"1. Correction"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested."),(0,i.kt)("h3",{id:"2-warning"},"2. Warning"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": A violation through a single incident or series of\nactions."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban."),(0,i.kt)("h3",{id:"3-temporary-ban"},"3. Temporary Ban"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": A serious violation of community standards, including\nsustained inappropriate behavior."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban."),(0,i.kt)("h3",{id:"4-permanent-ban"},"4. Permanent Ban"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Community Impact"),": Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Consequence"),": A permanent ban from any sort of public interaction within the\ncommunity."),(0,i.kt)("h2",{id:"attribution"},"Attribution"),(0,i.kt)("p",null,"This Code of Conduct is adapted from the ",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org"},"Contributor Covenant"),",\nversion 2.1, available at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"},"https://www.contributor-covenant.org/version/2/1/code_of_conduct.html"),"."),(0,i.kt)("p",null,"Community Impact Guidelines were inspired by\n",(0,i.kt)("a",{parentName:"p",href:"https://github.com/mozilla/diversity"},"Mozilla's code of conduct enforcement ladder"),"."),(0,i.kt)("p",null,"For answers to common questions about this code of conduct, see the FAQ at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/faq"},"https://www.contributor-covenant.org/faq"),". Translations are available at\n",(0,i.kt)("a",{parentName:"p",href:"https://www.contributor-covenant.org/translations"},"https://www.contributor-covenant.org/translations"),"."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/e4d3cddf.23b82d37.js b/website/assets/js/e4d3cddf.23b82d37.js new file mode 100644 index 00000000..aaa468f3 --- /dev/null +++ b/website/assets/js/e4d3cddf.23b82d37.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[864],{6461:(e,t,i)=>{i.r(t),i.d(t,{assets:()=>r,contentTitle:()=>o,default:()=>l,frontMatter:()=>s,metadata:()=>c,toc:()=>h});var n=i(4848),a=i(8453);const s={title:"Tagging Guidelines"},o=void 0,c={id:"best-practices",title:"Tagging Guidelines",description:"There are some patterns and practices you may want to consider when using Copa to patch images. Remember that these are suggestions that may not fit into your workflow, but we think that staying as close as possible to these practices offers the best experience with Copa.",source:"@site/docs/best-practices.md",sourceDirName:".",slug:"/best-practices",permalink:"/copacetic/website/next/best-practices",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{title:"Tagging Guidelines"},sidebar:"sidebar",previous:{title:"Quick Start",permalink:"/copacetic/website/next/quick-start"},next:{title:"Troubleshooting",permalink:"/copacetic/website/next/troubleshooting"}},r={},h=[{value:"Patch from Unmodified image",id:"patch-from-unmodified-image",level:2},{value:"Tagging",id:"tagging",level:2},{value:"Static Incremental Tags",id:"static-incremental-tags",level:3},{value:"Dynamic Tags",id:"dynamic-tags",level:3}];function d(e){const t={a:"a",code:"code",h2:"h2",h3:"h3",p:"p",...(0,a.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(t.p,{children:"There are some patterns and practices you may want to consider when using Copa to patch images. Remember that these are suggestions that may not fit into your workflow, but we think that staying as close as possible to these practices offers the best experience with Copa."}),"\n",(0,n.jsx)(t.h2,{id:"patch-from-unmodified-image",children:"Patch from Unmodified image"}),"\n",(0,n.jsxs)(t.p,{children:["When patching vulnerabilities in an image, it helps to always work from the initial unmodified image. For example, say you have an image tagged ",(0,n.jsx)(t.code,{children:"nginx:1.24.0"})," that contains a vulnerability. You run Copa to patch the image and produce a new image tagged ",(0,n.jsx)(t.code,{children:"nginx:1.24.0-1"}),". Then if another vulnerability shows up in your ",(0,n.jsx)(t.code,{children:"nginx:1.24.0-1"})," image, you should again patch from the unmodified ",(0,n.jsx)(t.code,{children:"nginx:1.24.0"})," image. This will help prevent the buildup of patch layers (",(0,n.jsx)(t.a,{href:"https://github.com/project-copacetic/copacetic/issues/389",children:"discarding subsequent patch layers"})," is a potential future enhancement)."]}),"\n",(0,n.jsx)(t.h2,{id:"tagging",children:"Tagging"}),"\n",(0,n.jsx)(t.p,{children:"There are a couple possible patterns that you could follow when tagging patched images."}),"\n",(0,n.jsx)(t.h3,{id:"static-incremental-tags",children:"Static Incremental Tags"}),"\n",(0,n.jsxs)(t.p,{children:["The first approach you could take is incrementing a number you append to the end of an image tag. For example, if you have an image tagged ",(0,n.jsx)(t.code,{children:"nginx:1.24.0"}),", following patches would be tagged as ",(0,n.jsx)(t.code,{children:"nginx:1.24.0-1"}),", ",(0,n.jsx)(t.code,{children:"nginx:1.24.0-2"}),", ",(0,n.jsx)(t.code,{children:"nginx:1.24.0-3"}),", and so on."]}),"\n",(0,n.jsx)(t.p,{children:"With this pattern you are always explicitly aware of the patch state of the image you are using. The downside is that dependabot is currently unable bump to patched images from unmodified images or bump from one patched image to the next."}),"\n",(0,n.jsx)(t.h3,{id:"dynamic-tags",children:"Dynamic Tags"}),"\n",(0,n.jsxs)(t.p,{children:["Another option is a static tag that is continually reused as new patches are applied. For example, you could have an initial unmodified image that you've tagged ",(0,n.jsx)(t.code,{children:"nginx:1.24.0-0"})," (in this case the ",(0,n.jsx)(t.code,{children:"-0"})," at the end helps identify the base unpatched image). All following patched images are then tagged as ",(0,n.jsx)(t.code,{children:"nginx:1.24.0"}),". You then know that the one tagged image always has the latest patches applied."]}),"\n",(0,n.jsxs)(t.p,{children:["This method makes it easy to continually consume the latest patched version of an image, but does contain some tradeoffs. First is that without pinning, image digests could change causing unpredictable behavior. Secondly, if an ",(0,n.jsx)(t.code,{children:"ImagePullPolicy"})," is set to ",(0,n.jsx)(t.code,{children:"IfNotPresent"}),", newly patched images would not be pulled since the tag hasn't changed."]})]})}function l(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,n.jsx)(t,{...e,children:(0,n.jsx)(d,{...e})}):d(e)}},8453:(e,t,i)=>{i.d(t,{R:()=>o,x:()=>c});var n=i(6540);const a={},s=n.createContext(a);function o(e){const t=n.useContext(s);return n.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function c(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),n.createElement(s.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/e4d3cddf.34856fe1.js b/website/assets/js/e4d3cddf.34856fe1.js deleted file mode 100644 index c0f0fe0e..00000000 --- a/website/assets/js/e4d3cddf.34856fe1.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8202],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>d});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var c=a.createContext({}),l=function(e){var t=a.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=l(e.components);return a.createElement(c.Provider,{value:t},e.children)},u="mdxType",g={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,c=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),u=l(n),m=i,d=u["".concat(c,".").concat(m)]||u[m]||g[m]||r;return n?a.createElement(d,o(o({ref:t},p),{},{components:n})):a.createElement(d,o({ref:t},p))}));function d(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=m;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s[u]="string"==typeof e?e:i,o[1]=s;for(var l=2;l<r;l++)o[l]=n[l];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}m.displayName="MDXCreateElement"},3010:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>l});var a=n(7462),i=(n(7294),n(3905));const r={title:"Tagging Guidelines"},o=void 0,s={unversionedId:"best-practices",id:"best-practices",title:"Tagging Guidelines",description:"There are some patterns and practices you may want to consider when using Copa to patch images. Remember that these are suggestions that may not fit into your workflow, but we think that staying as close as possible to these practices offers the best experience with Copa.",source:"@site/docs/best-practices.md",sourceDirName:".",slug:"/best-practices",permalink:"/copacetic/website/next/best-practices",draft:!1,tags:[],version:"current",frontMatter:{title:"Tagging Guidelines"},sidebar:"sidebar",previous:{title:"Quick Start",permalink:"/copacetic/website/next/quick-start"},next:{title:"Troubleshooting",permalink:"/copacetic/website/next/troubleshooting"}},c={},l=[{value:"Patch from Unmodified image",id:"patch-from-unmodified-image",level:2},{value:"Tagging",id:"tagging",level:2},{value:"Static Incremental Tags",id:"static-incremental-tags",level:3},{value:"Dynamic Tags",id:"dynamic-tags",level:3}],p={toc:l};function u(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"There are some patterns and practices you may want to consider when using Copa to patch images. Remember that these are suggestions that may not fit into your workflow, but we think that staying as close as possible to these practices offers the best experience with Copa."),(0,i.kt)("h2",{id:"patch-from-unmodified-image"},"Patch from Unmodified image"),(0,i.kt)("p",null,"When patching vulnerabilities in an image, it helps to always work from the initial unmodified image. For example, say you have an image tagged ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0")," that contains a vulnerability. You run Copa to patch the image and produce a new image tagged ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0-1"),". Then if another vulnerability shows up in your ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0-1")," image, you should again patch from the unmodified ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0")," image. This will help prevent the buildup of patch layers (",(0,i.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/issues/389"},"discarding subsequent patch layers")," is a potential future enhancement)."),(0,i.kt)("h2",{id:"tagging"},"Tagging"),(0,i.kt)("p",null,"There are a couple possible patterns that you could follow when tagging patched images."),(0,i.kt)("h3",{id:"static-incremental-tags"},"Static Incremental Tags"),(0,i.kt)("p",null,"The first approach you could take is incrementing a number you append to the end of an image tag. For example, if you have an image tagged ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0"),", following patches would be tagged as ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0-1"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0-2"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0-3"),", and so on."),(0,i.kt)("p",null,"With this pattern you are always explicitly aware of the patch state of the image you are using. The downside is that dependabot is currently unable bump to patched images from unmodified images or bump from one patched image to the next."),(0,i.kt)("h3",{id:"dynamic-tags"},"Dynamic Tags"),(0,i.kt)("p",null,"Another option is a static tag that is continually reused as new patches are applied. For example, you could have an initial unmodified image that you've tagged ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0-0")," (in this case the ",(0,i.kt)("inlineCode",{parentName:"p"},"-0")," at the end helps identify the base unpatched image). All following patched images are then tagged as ",(0,i.kt)("inlineCode",{parentName:"p"},"nginx:1.24.0"),". You then know that the one tagged image always has the latest patches applied."),(0,i.kt)("p",null,"This method makes it easy to continually consume the latest patched version of an image, but does contain some tradeoffs. First is that without pinning, image digests could change causing unpredictable behavior. Secondly, if an ",(0,i.kt)("inlineCode",{parentName:"p"},"ImagePullPolicy")," is set to ",(0,i.kt)("inlineCode",{parentName:"p"},"IfNotPresent"),", newly patched images would not be pulled since the tag hasn't changed."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/e8c0720c.4b83c6ce.js b/website/assets/js/e8c0720c.4b83c6ce.js new file mode 100644 index 00000000..70c9357d --- /dev/null +++ b/website/assets/js/e8c0720c.4b83c6ce.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6229],{8209:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>r,contentTitle:()=>a,default:()=>d,frontMatter:()=>s,metadata:()=>c,toc:()=>l});var i=n(4848),o=n(8453);const s={title:"Output"},a=void 0,c={id:"output",title:"Output",description:"Experimental: This feature might change without preserving backwards compatibility.",source:"@site/docs/output.md",sourceDirName:".",slug:"/output",permalink:"/copacetic/website/next/output",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{title:"Output"},sidebar:"sidebar",previous:{title:"Custom buildkit addresses",permalink:"/copacetic/website/next/custom-address"},next:{title:"Scanner Plugins",permalink:"/copacetic/website/next/scanner-plugins"}},r={},l=[{value:"OpenVEX",id:"openvex",level:2}];function p(e){const t={a:"a",admonition:"admonition",code:"code",h2:"h2",li:"li",p:"p",pre:"pre",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(t.admonition,{type:"caution",children:(0,i.jsx)(t.p,{children:"Experimental: This feature might change without preserving backwards compatibility."})}),"\n",(0,i.jsx)(t.p,{children:"Copa optionally outputs a Vulnerability Exploitability eXchange (VEX) file as a result of the patching process to surface the vulnerabilities and packages that were patched."}),"\n",(0,i.jsxs)(t.p,{children:["Currently, Copa supports the ",(0,i.jsx)(t.a,{href:"https://github.com/openvex",children:"OpenVEX"})," format, but it can be extended to support other formats."]}),"\n",(0,i.jsx)(t.h2,{id:"openvex",children:"OpenVEX"}),"\n",(0,i.jsxs)(t.p,{children:["OpenVEX is an implementation of Vulnerability Exploitability eXchange (VEX) format. For more information, see ",(0,i.jsx)(t.a,{href:"https://github.com/openvex/spec/",children:"OpenVEX specification"}),"."]}),"\n",(0,i.jsx)(t.admonition,{type:"tip",children:(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:["Use ",(0,i.jsx)(t.code,{children:"COPA_VEX_AUTHOR"})," environment variable to set the author of the VEX document. If it's not set, the author will default to ",(0,i.jsx)(t.code,{children:"Project Copacetic"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsx)(t.p,{children:"A VEX document must contain at least one VEX statement. If there are no fixed vulnerabilities, Copa will not generate a VEX document."}),"\n"]}),"\n"]})}),"\n",(0,i.jsxs)(t.p,{children:["To generate a VEX document using OpenVEX, use ",(0,i.jsx)(t.code,{children:'--format="openvex"'})," flag, and use ",(0,i.jsx)(t.code,{children:"--output"})," to specify a file path. For example:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-bash",children:'copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --format="openvex" --output "nginx.1.21.6-vex.json"\n'})}),"\n",(0,i.jsx)(t.p,{children:"This will generate a VEX Document that looks like:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",children:'{\n "@context": "https://openvex.dev/ns",\n "@id": "https://openvex.dev/docs/public/vex-a6c44ec1d79e9dd4190dc01b4ecf7527ebb26bd37c01e32e6efcd203ae00d2a5",\n "author": "Project Copacetic",\n "timestamp": "2023-10-11T00:15:00.114768055Z",\n "version": 1,\n "tooling": "Project Copacetic",\n "statements": [\n {\n "vulnerability": {\n "@id": "CVE-2021-22945"\n },\n "products": [\n {\n "@id": "pkg:oci/docker.io/library/nginx:1.21.6-patched",\n "subcomponents": [\n {\n "@id": "pkg:deb/debian/curl@7.74.0-1.3+deb11u2?arch=amd64"\n },\n {\n "@id": "pkg:deb/debian/libcurl4@7.74.0-1.3+deb11u2?arch=amd64"\n }\n ]\n }\n ],\n "status": "fixed"\n },\n ...\n}\n'})})]})}function d(e={}){const{wrapper:t}={...(0,o.R)(),...e.components};return t?(0,i.jsx)(t,{...e,children:(0,i.jsx)(p,{...e})}):p(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>a,x:()=>c});var i=n(6540);const o={},s=i.createContext(o);function a(e){const t=i.useContext(s);return i.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function c(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:a(e.components),i.createElement(s.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/e8c0720c.d9edccdb.js b/website/assets/js/e8c0720c.d9edccdb.js deleted file mode 100644 index d2a56eae..00000000 --- a/website/assets/js/e8c0720c.d9edccdb.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[3296],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function p(e,t){if(null==e)return{};var n,r,a=function(e,t){if(null==e)return{};var n,r,a={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},s="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,u=p(e,["components","mdxType","originalType","parentName"]),s=l(n),m=a,f=s["".concat(c,".").concat(m)]||s[m]||d[m]||o;return n?r.createElement(f,i(i({ref:t},u),{},{components:n})):r.createElement(f,i({ref:t},u))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=m;var p={};for(var c in t)hasOwnProperty.call(t,c)&&(p[c]=t[c]);p.originalType=e,p[s]="string"==typeof e?e:a,i[1]=p;for(var l=2;l<o;l++)i[l]=n[l];return r.createElement.apply(null,i)}return r.createElement.apply(null,n)}m.displayName="MDXCreateElement"},4376:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>s,frontMatter:()=>o,metadata:()=>p,toc:()=>l});var r=n(7462),a=(n(7294),n(3905));const o={title:"Output"},i=void 0,p={unversionedId:"output",id:"output",title:"Output",description:"Experimental: This feature might change without preserving backwards compatibility.",source:"@site/docs/output.md",sourceDirName:".",slug:"/output",permalink:"/copacetic/website/next/output",draft:!1,tags:[],version:"current",frontMatter:{title:"Output"},sidebar:"sidebar",previous:{title:"Custom buildkit addresses",permalink:"/copacetic/website/next/custom-address"},next:{title:"Scanner Plugins",permalink:"/copacetic/website/next/scanner-plugins"}},c={},l=[{value:"OpenVEX",id:"openvex",level:2}],u={toc:l};function s(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("admonition",{type:"caution"},(0,a.kt)("p",{parentName:"admonition"},"Experimental: This feature might change without preserving backwards compatibility.")),(0,a.kt)("p",null,"Copa optionally outputs a Vulnerability Exploitability eXchange (VEX) file as a result of the patching process to surface the vulnerabilities and packages that were patched."),(0,a.kt)("p",null,"Currently, Copa supports the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/openvex"},"OpenVEX")," format, but it can be extended to support other formats."),(0,a.kt)("h2",{id:"openvex"},"OpenVEX"),(0,a.kt)("p",null,"OpenVEX is an implementation of Vulnerability Exploitability eXchange (VEX) format. For more information, see ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/openvex/spec/"},"OpenVEX specification"),"."),(0,a.kt)("admonition",{type:"tip"},(0,a.kt)("ul",{parentName:"admonition"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"Use ",(0,a.kt)("inlineCode",{parentName:"p"},"COPA_VEX_AUTHOR")," environment variable to set the author of the VEX document. If it's not set, the author will default to ",(0,a.kt)("inlineCode",{parentName:"p"},"Project Copacetic"),".")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"A VEX document must contain at least one VEX statement. If there are no fixed vulnerabilities, Copa will not generate a VEX document.")))),(0,a.kt)("p",null,"To generate a VEX document using OpenVEX, use ",(0,a.kt)("inlineCode",{parentName:"p"},'--format="openvex"')," flag, and use ",(0,a.kt)("inlineCode",{parentName:"p"},"--output")," to specify a file path. For example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},'copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --format="openvex" --output "nginx.1.21.6-vex.json"\n')),(0,a.kt)("p",null,"This will generate a VEX Document that looks like:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-json"},'{\n "@context": "https://openvex.dev/ns",\n "@id": "https://openvex.dev/docs/public/vex-a6c44ec1d79e9dd4190dc01b4ecf7527ebb26bd37c01e32e6efcd203ae00d2a5",\n "author": "Project Copacetic",\n "timestamp": "2023-10-11T00:15:00.114768055Z",\n "version": 1,\n "tooling": "Project Copacetic",\n "statements": [\n {\n "vulnerability": {\n "@id": "CVE-2021-22945"\n },\n "products": [\n {\n "@id": "pkg:oci/docker.io/library/nginx:1.21.6-patched",\n "subcomponents": [\n {\n "@id": "pkg:deb/debian/curl@7.74.0-1.3+deb11u2?arch=amd64"\n },\n {\n "@id": "pkg:deb/debian/libcurl4@7.74.0-1.3+deb11u2?arch=amd64"\n }\n ]\n }\n ],\n "status": "fixed"\n },\n ...\n}\n')))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/e99d2929.85b262dd.js b/website/assets/js/e99d2929.85b262dd.js deleted file mode 100644 index 5fcf34cb..00000000 --- a/website/assets/js/e99d2929.85b262dd.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2972],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>b});var r=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function a(e,t){if(null==e)return{};var n,r,i=function(e,t){if(null==e)return{};var n,r,i={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var c=r.createContext({}),s=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(c.Provider,{value:t},e.children)},p="mdxType",g={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,c=e.parentName,u=a(e,["components","mdxType","originalType","parentName"]),p=s(n),f=i,b=p["".concat(c,".").concat(f)]||p[f]||g[f]||o;return n?r.createElement(b,l(l({ref:t},u),{},{components:n})):r.createElement(b,l({ref:t},u))}));function b(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,l=new Array(o);l[0]=f;var a={};for(var c in t)hasOwnProperty.call(t,c)&&(a[c]=t[c]);a.originalType=e,a[p]="string"==typeof e?e:i,l[1]=a;for(var s=2;s<o;s++)l[s]=n[s];return r.createElement.apply(null,l)}return r.createElement.apply(null,n)}f.displayName="MDXCreateElement"},1413:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>l,default:()=>p,frontMatter:()=>o,metadata:()=>a,toc:()=>s});var r=n(7462),i=(n(7294),n(3905));const o={title:"Troubleshooting"},l=void 0,a={unversionedId:"troubleshooting",id:"version-v0.6.x/troubleshooting",title:"Troubleshooting",description:"Filtering Vulnerabilities",source:"@site/versioned_docs/version-v0.6.x/troubleshooting.md",sourceDirName:".",slug:"/troubleshooting",permalink:"/copacetic/website/troubleshooting",draft:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Troubleshooting"},sidebar:"sidebar",previous:{title:"Tagging Guidelines",permalink:"/copacetic/website/best-practices"},next:{title:"FAQ",permalink:"/copacetic/website/faq"}},c={},s=[{value:"Filtering Vulnerabilities",id:"filtering-vulnerabilities",level:2},{value:"Rego Policy",id:"rego-policy",level:3},{value:"Ignore File",id:"ignore-file",level:3}],u={toc:s};function p(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h2",{id:"filtering-vulnerabilities"},"Filtering Vulnerabilities"),(0,i.kt)("p",null,"You might want to filter/ignore some of the vulnerabilities while patching. To do so, you need to first filter those undesired vulnerabilities from your scanner output."),(0,i.kt)("p",null,"For Trivy, vulnerabilities can be filtered by the following 2 ways:"),(0,i.kt)("h3",{id:"rego-policy"},"Rego Policy"),(0,i.kt)("p",null,"An example rego file which demonstrates how to ignore certain Vulnerability IDs or Package Names:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},'$ cat trivy_ignore.rego\n\npackage trivy\n\nimport data.lib.trivy\n\ndefault ignore = false\n\n\n# Ignore the following Vulnerability IDs\nignore_vulnerability_ids := {\n "CVE-2018-14618"\n}\n# Ignore the following Package Names\nignore_pkgs := {"bash", "vim"}\n\n\n# For ignoring vulnID\nignore {\n input.VulnerabilityID == ignore_vulnerability_ids[_]\n}\n# For ignoring pkgName\nignore {\n input.PkgName == ignore_pkgs[_]\n}\n\n')),(0,i.kt)("p",null,"After adding the above rego file, run the image scan with the ",(0,i.kt)("inlineCode",{parentName:"p"},"--ignore-policy")," flag followed by the file name to ignore them while scanning:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --ignore-policy trivy_ignore.rego ruby:2.4.0\n")),(0,i.kt)("p",null,'In the above example, the vulnerability "CVE-2018-14618" and the packages "bash" & "vim" are ignored while scanning, and hence patching the image.'),(0,i.kt)("h3",{id:"ignore-file"},"Ignore File"),(0,i.kt)("p",null,"Use a ",(0,i.kt)("inlineCode",{parentName:"p"},".trivyignore")," file to list all the vulnerabilities you want to ignore."),(0,i.kt)("p",null,"Example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"$ cat .trivyignore\n\n# Accept the risk\nCVE-2018-14618\n")),(0,i.kt)("p",null,"In the above example, the vulnerability CVE-2018-14618 is ignored while scanning, and hence while patching the image."),(0,i.kt)("p",null,"For a more detailed explanation on how to ignore certain vulnerabilities with Trivy, please refer to the official documentation ",(0,i.kt)("a",{parentName:"p",href:"https://aquasecurity.github.io/trivy/v0.44/docs/configuration/filtering/"},"here"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/e99d2929.a259dc08.js b/website/assets/js/e99d2929.a259dc08.js new file mode 100644 index 00000000..9d054a62 --- /dev/null +++ b/website/assets/js/e99d2929.a259dc08.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[782],{9632:(e,i,n)=>{n.r(i),n.d(i,{assets:()=>s,contentTitle:()=>l,default:()=>h,frontMatter:()=>o,metadata:()=>a,toc:()=>c});var t=n(4848),r=n(8453);const o={title:"Troubleshooting"},l=void 0,a={id:"troubleshooting",title:"Troubleshooting",description:"Filtering Vulnerabilities",source:"@site/versioned_docs/version-v0.6.x/troubleshooting.md",sourceDirName:".",slug:"/troubleshooting",permalink:"/copacetic/website/troubleshooting",draft:!1,unlisted:!1,tags:[],version:"v0.6.x",frontMatter:{title:"Troubleshooting"},sidebar:"sidebar",previous:{title:"Tagging Guidelines",permalink:"/copacetic/website/best-practices"},next:{title:"FAQ",permalink:"/copacetic/website/faq"}},s={},c=[{value:"Filtering Vulnerabilities",id:"filtering-vulnerabilities",level:2},{value:"Rego Policy",id:"rego-policy",level:3},{value:"Ignore File",id:"ignore-file",level:3}];function g(e){const i={a:"a",code:"code",h2:"h2",h3:"h3",p:"p",pre:"pre",...(0,r.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i.h2,{id:"filtering-vulnerabilities",children:"Filtering Vulnerabilities"}),"\n",(0,t.jsx)(i.p,{children:"You might want to filter/ignore some of the vulnerabilities while patching. To do so, you need to first filter those undesired vulnerabilities from your scanner output."}),"\n",(0,t.jsx)(i.p,{children:"For Trivy, vulnerabilities can be filtered by the following 2 ways:"}),"\n",(0,t.jsx)(i.h3,{id:"rego-policy",children:"Rego Policy"}),"\n",(0,t.jsx)(i.p,{children:"An example rego file which demonstrates how to ignore certain Vulnerability IDs or Package Names:"}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-bash",children:'$ cat trivy_ignore.rego\n\npackage trivy\n\nimport data.lib.trivy\n\ndefault ignore = false\n\n\n# Ignore the following Vulnerability IDs\nignore_vulnerability_ids := {\n "CVE-2018-14618"\n}\n# Ignore the following Package Names\nignore_pkgs := {"bash", "vim"}\n\n\n# For ignoring vulnID\nignore {\n input.VulnerabilityID == ignore_vulnerability_ids[_]\n}\n# For ignoring pkgName\nignore {\n\tinput.PkgName == ignore_pkgs[_]\n}\n\n'})}),"\n",(0,t.jsxs)(i.p,{children:["After adding the above rego file, run the image scan with the ",(0,t.jsx)(i.code,{children:"--ignore-policy"})," flag followed by the file name to ignore them while scanning:"]}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-bash",children:"trivy image --ignore-policy trivy_ignore.rego ruby:2.4.0\n"})}),"\n",(0,t.jsx)(i.p,{children:'In the above example, the vulnerability "CVE-2018-14618" and the packages "bash" & "vim" are ignored while scanning, and hence patching the image.'}),"\n",(0,t.jsx)(i.h3,{id:"ignore-file",children:"Ignore File"}),"\n",(0,t.jsxs)(i.p,{children:["Use a ",(0,t.jsx)(i.code,{children:".trivyignore"})," file to list all the vulnerabilities you want to ignore."]}),"\n",(0,t.jsx)(i.p,{children:"Example:"}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-bash",children:"$ cat .trivyignore\n\n# Accept the risk\nCVE-2018-14618\n"})}),"\n",(0,t.jsx)(i.p,{children:"In the above example, the vulnerability CVE-2018-14618 is ignored while scanning, and hence while patching the image."}),"\n",(0,t.jsxs)(i.p,{children:["For a more detailed explanation on how to ignore certain vulnerabilities with Trivy, please refer to the official documentation ",(0,t.jsx)(i.a,{href:"https://aquasecurity.github.io/trivy/v0.44/docs/configuration/filtering/",children:"here"}),"."]})]})}function h(e={}){const{wrapper:i}={...(0,r.R)(),...e.components};return i?(0,t.jsx)(i,{...e,children:(0,t.jsx)(g,{...e})}):g(e)}},8453:(e,i,n)=>{n.d(i,{R:()=>l,x:()=>a});var t=n(6540);const r={},o=t.createContext(r);function l(e){const i=t.useContext(o);return t.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function a(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:l(e.components),t.createElement(o.Provider,{value:i},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/ea9eabff.6d2cdf3d.js b/website/assets/js/ea9eabff.6d2cdf3d.js deleted file mode 100644 index 93b9ed68..00000000 --- a/website/assets/js/ea9eabff.6d2cdf3d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7835],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>h});var o=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function r(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?a(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):a(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,o,i=function(e,t){if(null==e)return{};var n,o,i={},a=Object.keys(e);for(o=0;o<a.length;o++)n=a[o],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o<a.length;o++)n=a[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=o.createContext({}),c=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},p=function(e){var t=c(e.components);return o.createElement(l.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),u=c(n),m=i,h=u["".concat(l,".").concat(m)]||u[m]||d[m]||a;return n?o.createElement(h,r(r({ref:t},p),{},{components:n})):o.createElement(h,r({ref:t},p))}));function h(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,r=new Array(a);r[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[u]="string"==typeof e?e:i,r[1]=s;for(var c=2;c<a;c++)r[c]=n[c];return o.createElement.apply(null,r)}return o.createElement.apply(null,n)}m.displayName="MDXCreateElement"},1008:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>u,frontMatter:()=>a,metadata:()=>s,toc:()=>c});var o=n(7462),i=(n(7294),n(3905));const a={title:"Contributing"},r=void 0,s={unversionedId:"contributing",id:"version-v0.1.x/contributing",title:"Contributing",description:"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.",source:"@site/versioned_docs/version-v0.1.x/contributing.md",sourceDirName:".",slug:"/contributing",permalink:"/copacetic/website/v0.1.x/contributing",draft:!1,tags:[],version:"v0.1.x",frontMatter:{title:"Contributing"},sidebar:"sidebar",previous:{title:"FAQ",permalink:"/copacetic/website/v0.1.x/faq"},next:{title:"Code of Conduct",permalink:"/copacetic/website/v0.1.x/code-of-conduct"}},l={},c=[{value:"Contributing Issues",id:"contributing-issues",level:2},{value:"Contributing Code",id:"contributing-code",level:2},{value:"Getting Started",id:"getting-started",level:3},{value:"Visual Studio Code Development Container",id:"visual-studio-code-development-container",level:3},{value:"Prerequisites",id:"prerequisites",level:4},{value:"Using the dev container",id:"using-the-dev-container",level:4},{value:"Personalizing user settings in a dev container",id:"personalizing-user-settings-in-a-dev-container",level:4},{value:"Tests",id:"tests",level:3},{value:"Pull Requests",id:"pull-requests",level:3},{value:"Developer Certificate of Origin (DCO)",id:"developer-certificate-of-origin-dco",level:2},{value:"I didn't sign my commit, now what?",id:"i-didnt-sign-my-commit-now-what",level:3},{value:"Code of Conduct",id:"code-of-conduct",level:2}],p={toc:c};function u(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,o.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"Welcome! We are very happy to accept community contributions to the project, whether through ",(0,i.kt)("a",{parentName:"p",href:"#contributing-issues"},"filing issues")," or ",(0,i.kt)("a",{parentName:"p",href:"#contributing-code"},"code")," in the form of ",(0,i.kt)("a",{parentName:"p",href:"#pull-requests"},"Pull Requests"),". Please note that by participating in this project, you agree to abide by the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.1.x/code-of-conduct"},"Code of Conduct"),", as well as the terms of the ",(0,i.kt)("a",{parentName:"p",href:"#developer-certificate-of-origin-dco"},"Developer Certificate of Origin"),"."),(0,i.kt)("h2",{id:"contributing-issues"},"Contributing Issues"),(0,i.kt)("p",null,"Before opening any new issues, please search our ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/issues"},"existing GitHub issues")," to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead."),(0,i.kt)("p",null,"When opening an issue, please select the most appropriate template for what you're contributing:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Bug Report:")," If you would like to report the project or tool behaving in unexpected ways."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Documentation Improvement:")," If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Request:")," If you have a feature request, suggestion, or a even a design proposal to review."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Question:")," If you would like to ask the maintainers a question about the project.")),(0,i.kt)("h2",{id:"contributing-code"},"Contributing Code"),(0,i.kt)("h3",{id:"getting-started"},"Getting Started"),(0,i.kt)("p",null,"Follow the instructions to either:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/copacetic/website/v0.1.x/installation"},"Setup your dev environment to build copa"),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"#visual-studio-code-development-container"},"Use the copa development container")," in ",(0,i.kt)("a",{parentName:"li",href:"https://code.visualstudio.com/"},"Visual Studio Code"),".")),(0,i.kt)("p",null,"For an overview of the project components, refer to the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.1.x/design"},"copa design")," document."),(0,i.kt)("h3",{id:"visual-studio-code-development-container"},"Visual Studio Code Development Container"),(0,i.kt)("p",null,(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/"},"VSCode")," supports development in a containerized environment through its ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/docs/remote/containers"},"Remote - Container extension"),". This folder provides a development container which encapsulates the dependencies specified in the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.1.x/installation"},"instructions to build and run copa"),"."),(0,i.kt)("h4",{id:"prerequisites"},"Prerequisites"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://docs.docker.com/get-docker/"},"Docker"),(0,i.kt)("blockquote",{parentName:"li"},(0,i.kt)("p",{parentName:"blockquote"},"For Windows users, enabling ",(0,i.kt)("a",{parentName:"p",href:"https://docs.docker.com/docker-for-windows/wsl/"},"WSL2 back-end integration with Docker")," is recommended."))),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://code.visualstudio.com/"},"Visual Studio Code")),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers"},"Visual Studio Code Remote - Containers extension"))),(0,i.kt)("h4",{id:"using-the-dev-container"},"Using the dev container"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("p",{parentName:"li"},"After you have cloned this repo locally, open the repo folder in VSCode. VSCode will detect the presence of this ",(0,i.kt)("inlineCode",{parentName:"p"},".devcontainer")," subfolder and will prompt you to reopen the project in a container."),(0,i.kt)("p",{parentName:"li"},"Alternatively, you can open the command palette and use the ",(0,i.kt)("inlineCode",{parentName:"p"},"Remote-Containers: Reopen in Container")," command.")),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("p",{parentName:"li"},"Once the container is loaded, open an ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/docs/editor/integrated-terminal"},"integrated terminal")," in VSCode and you can start running the demo instructions."))),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},(0,i.kt)("strong",{parentName:"p"},"\u26a0 If running via Docker Desktop for Windows")),(0,i.kt)("p",{parentName:"blockquote"},"Note that the ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user"},"mounted workspace files appear owned by ",(0,i.kt)("inlineCode",{parentName:"a"},"root"))," in the dev container, which will cause ",(0,i.kt)("inlineCode",{parentName:"p"},"git")," commands to fail with a ",(0,i.kt)("inlineCode",{parentName:"p"},"fatal: detected dubious ownership in a repository")," error due to ",(0,i.kt)("a",{parentName:"p",href:"https://git-scm.com/docs/git-config/2.35.2#Documentation/git-config.txt-safedirectory"},"safe.directory")," checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the ",(0,i.kt)("inlineCode",{parentName:"p"},"vscode")," user:"),(0,i.kt)("pre",{parentName:"blockquote"},(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"sudo chown -R vscode:vscode /workspace/copacetic\n"))),(0,i.kt)("h4",{id:"personalizing-user-settings-in-a-dev-container"},"Personalizing user settings in a dev container"),(0,i.kt)("p",null,"VSCode supports applying your user settings, such as your ",(0,i.kt)("inlineCode",{parentName:"p"},".gitconfig"),", to a dev container through the use of ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories"},"dotfiles repositories"),". This can be done through your own VSCode ",(0,i.kt)("inlineCode",{parentName:"p"},"settings.json")," file without changing the dev container image or configuration."),(0,i.kt)("h3",{id:"tests"},"Tests"),(0,i.kt)("p",null,"Once you can successfully ",(0,i.kt)("inlineCode",{parentName:"p"},"make")," the project, any code contributions should also successfully:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Pass unit tests via ",(0,i.kt)("inlineCode",{parentName:"li"},"make test"),"."),(0,i.kt)("li",{parentName:"ul"},"Lint cleanly via ",(0,i.kt)("inlineCode",{parentName:"li"},"make lint"),".")),(0,i.kt)("p",null,"Pull requests will also be expected to pass the PR functional tests specified by ",(0,i.kt)("inlineCode",{parentName:"p"},".github/workflows/build.yml"),"."),(0,i.kt)("h3",{id:"pull-requests"},"Pull Requests"),(0,i.kt)("p",null,"If you'd like to start contributing code to the project, you can search for ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/labels/good%20first%20issue"},"issues with the ",(0,i.kt)("inlineCode",{parentName:"a"},"good first issue")," label"),". Other kinds of PR contributions we would look for include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Fixes for bugs and other correctness issues."),(0,i.kt)("li",{parentName:"ul"},"Docs and other content improvements (e.g. samples)."),(0,i.kt)("li",{parentName:"ul"},"Extensions to support parsing new scanning report formats."),(0,i.kt)("li",{parentName:"ul"},"Extensions to support patching images based on new distros or using new package managers.")),(0,i.kt)("p",null,"For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Architectural changes (e.g. breaking interfaces or violations of ",(0,i.kt)("a",{parentName:"li",href:"/copacetic/website/v0.1.x/design"},"this project's design tenets"),")."),(0,i.kt)("li",{parentName:"ul"},"Unsolicited features that significantly expand the functional scope of the tool.")),(0,i.kt)("p",null,"Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format"},"Angular commit message format")," for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs."),(0,i.kt)("p",null,"We suggest:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Use the standard header format of ",(0,i.kt)("inlineCode",{parentName:"li"},'"<type>: <short summary>"')," where the ",(0,i.kt)("inlineCode",{parentName:"li"},"<type>")," is one of the following:",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"build:")," Changes that affect the build system or external dependencies"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"ci:")," Changes to the GitHub workflows and configurations"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"docs:")," Documentation only changes"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"feat:")," A new feature"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"fix:")," A bug fix"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"perf:")," A code change that improves performance"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"refactor:")," A code change that neither fixes a bug nor adds a feature"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"test:")," Adding missing tests or correcting existing tests"))),(0,i.kt)("li",{parentName:"ul"},"Use a ",(0,i.kt)("a",{parentName:"li",href:"https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html"},"concise, imperative description")," of the changes included in the ",(0,i.kt)("inlineCode",{parentName:"li"},"<short summary>")," of the header, the body of the PR, and generally in your commit messages."),(0,i.kt)("li",{parentName:"ul"},"Use ",(0,i.kt)("a",{parentName:"li",href:"https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests"},"GitHub keywords")," in the footer of your PR description, such as ",(0,i.kt)("inlineCode",{parentName:"li"},"closes")," to automatically close issues the PR intends to address.")),(0,i.kt)("h2",{id:"developer-certificate-of-origin-dco"},"Developer Certificate of Origin (DCO)"),(0,i.kt)("p",null,"The ",(0,i.kt)("a",{parentName:"p",href:"https://wiki.linuxfoundation.org/dco"},"Developer Certificate of Origin")," (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the ",(0,i.kt)("a",{parentName:"p",href:"https://developercertificate.org/"},"full text of the DCO"),", reformatted for readability:"),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},"By making a contribution to this project, I certify that:"),(0,i.kt)("p",{parentName:"blockquote"},"(a) The contribution was created in whole or in part by me and I\nhave the right to submit it under the open source license\nindicated in the file; or"),(0,i.kt)("p",{parentName:"blockquote"},"(b) The contribution is based upon previous work that, to the best\nof my knowledge, is covered under an appropriate open source\nlicense and I have the right under that license to submit that\nwork with modifications, whether created in whole or in part\nby me, under the same open source license (unless I am\npermitted to submit under a different license), as indicated\nin the file; or"),(0,i.kt)("p",{parentName:"blockquote"},"(c) The contribution was provided directly to me by some other\nperson who certified (a), (b) or (c) and I have not modified\nit."),(0,i.kt)("p",{parentName:"blockquote"},"(d) I understand and agree that this project and the contribution\nare public and that a record of the contribution (including all\npersonal information I submit with it, including my sign-off) is\nmaintained indefinitely and may be redistributed consistent with\nthis project or the open source license(s) involved.")),(0,i.kt)("p",null,"Contributors ",(0,i.kt)("em",{parentName:"p"},"sign-off")," that they adhere to these requirements by adding a ",(0,i.kt)("inlineCode",{parentName:"p"},"Signed-off-by")," line to commit messages."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-text"},"This is my commit message\n\nSigned-off-by: Random J Developer <random@developer.example.org>\n")),(0,i.kt)("p",null,"Git even has a ",(0,i.kt)("inlineCode",{parentName:"p"},"-s")," command line option to append this automatically to your commit message:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"git commit -s -m 'This is my commit message'\n")),(0,i.kt)("p",null,"Pull requests that do not contain a valid ",(0,i.kt)("inlineCode",{parentName:"p"},"Signed-off-by")," line cannot be merged."),(0,i.kt)("h3",{id:"i-didnt-sign-my-commit-now-what"},"I didn't sign my commit, now what?"),(0,i.kt)("p",null,"No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"git switch <branch-name>\ngit commit --amend --no-edit --signoff\ngit push --force-with-lease <remote-name> <branch-name>\n")),(0,i.kt)("h2",{id:"code-of-conduct"},"Code of Conduct"),(0,i.kt)("p",null,"This project has adopted the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.1.x/code-of-conduct"},"Contributor Covenant Code of Conduct"),"."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/ea9eabff.7a3cec22.js b/website/assets/js/ea9eabff.7a3cec22.js new file mode 100644 index 00000000..3d7096a5 --- /dev/null +++ b/website/assets/js/ea9eabff.7a3cec22.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9590],{7979:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>d,contentTitle:()=>r,default:()=>h,frontMatter:()=>o,metadata:()=>c,toc:()=>a});var i=t(4848),s=t(8453);const o={title:"Contributing"},r=void 0,c={id:"contributing",title:"Contributing",description:"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.",source:"@site/versioned_docs/version-v0.1.x/contributing.md",sourceDirName:".",slug:"/contributing",permalink:"/copacetic/website/v0.1.x/contributing",draft:!1,unlisted:!1,tags:[],version:"v0.1.x",frontMatter:{title:"Contributing"},sidebar:"sidebar",previous:{title:"FAQ",permalink:"/copacetic/website/v0.1.x/faq"},next:{title:"Code of Conduct",permalink:"/copacetic/website/v0.1.x/code-of-conduct"}},d={},a=[{value:"Contributing Issues",id:"contributing-issues",level:2},{value:"Contributing Code",id:"contributing-code",level:2},{value:"Getting Started",id:"getting-started",level:3},{value:"Visual Studio Code Development Container",id:"visual-studio-code-development-container",level:3},{value:"Prerequisites",id:"prerequisites",level:4},{value:"Using the dev container",id:"using-the-dev-container",level:4},{value:"Personalizing user settings in a dev container",id:"personalizing-user-settings-in-a-dev-container",level:4},{value:"Tests",id:"tests",level:3},{value:"Pull Requests",id:"pull-requests",level:3},{value:"Developer Certificate of Origin (DCO)",id:"developer-certificate-of-origin-dco",level:2},{value:"I didn't sign my commit, now what?",id:"i-didnt-sign-my-commit-now-what",level:3},{value:"Code of Conduct",id:"code-of-conduct",level:2}];function l(e){const n={a:"a",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h3:"h3",h4:"h4",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsxs)(n.p,{children:["Welcome! We are very happy to accept community contributions to the project, whether through ",(0,i.jsx)(n.a,{href:"#contributing-issues",children:"filing issues"})," or ",(0,i.jsx)(n.a,{href:"#contributing-code",children:"code"})," in the form of ",(0,i.jsx)(n.a,{href:"#pull-requests",children:"Pull Requests"}),". Please note that by participating in this project, you agree to abide by the ",(0,i.jsx)(n.a,{href:"/copacetic/website/v0.1.x/code-of-conduct",children:"Code of Conduct"}),", as well as the terms of the ",(0,i.jsx)(n.a,{href:"#developer-certificate-of-origin-dco",children:"Developer Certificate of Origin"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"contributing-issues",children:"Contributing Issues"}),"\n",(0,i.jsxs)(n.p,{children:["Before opening any new issues, please search our ",(0,i.jsx)(n.a,{href:"https://github.com/project-copacetic/copacetic/issues",children:"existing GitHub issues"})," to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead."]}),"\n",(0,i.jsx)(n.p,{children:"When opening an issue, please select the most appropriate template for what you're contributing:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Bug Report:"})," If you would like to report the project or tool behaving in unexpected ways."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Documentation Improvement:"})," If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Request:"})," If you have a feature request, suggestion, or a even a design proposal to review."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Question:"})," If you would like to ask the maintainers a question about the project."]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"contributing-code",children:"Contributing Code"}),"\n",(0,i.jsx)(n.h3,{id:"getting-started",children:"Getting Started"}),"\n",(0,i.jsx)(n.p,{children:"Follow the instructions to either:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"/copacetic/website/v0.1.x/installation",children:"Setup your dev environment to build copa"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"#visual-studio-code-development-container",children:"Use the copa development container"})," in ",(0,i.jsx)(n.a,{href:"https://code.visualstudio.com/",children:"Visual Studio Code"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["For an overview of the project components, refer to the ",(0,i.jsx)(n.a,{href:"/copacetic/website/v0.1.x/design",children:"copa design"})," document."]}),"\n",(0,i.jsx)(n.h3,{id:"visual-studio-code-development-container",children:"Visual Studio Code Development Container"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"https://code.visualstudio.com/",children:"VSCode"})," supports development in a containerized environment through its ",(0,i.jsx)(n.a,{href:"https://code.visualstudio.com/docs/remote/containers",children:"Remote - Container extension"}),". This folder provides a development container which encapsulates the dependencies specified in the ",(0,i.jsx)(n.a,{href:"/copacetic/website/v0.1.x/installation",children:"instructions to build and run copa"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"https://docs.docker.com/get-docker/",children:"Docker"}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsxs)(n.p,{children:["For Windows users, enabling ",(0,i.jsx)(n.a,{href:"https://docs.docker.com/docker-for-windows/wsl/",children:"WSL2 back-end integration with Docker"})," is recommended."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://code.visualstudio.com/",children:"Visual Studio Code"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers",children:"Visual Studio Code Remote - Containers extension"})}),"\n"]}),"\n",(0,i.jsx)(n.h4,{id:"using-the-dev-container",children:"Using the dev container"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["After you have cloned this repo locally, open the repo folder in VSCode. VSCode will detect the presence of this ",(0,i.jsx)(n.code,{children:".devcontainer"})," subfolder and will prompt you to reopen the project in a container."]}),"\n",(0,i.jsxs)(n.p,{children:["Alternatively, you can open the command palette and use the ",(0,i.jsx)(n.code,{children:"Remote-Containers: Reopen in Container"})," command."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["Once the container is loaded, open an ",(0,i.jsx)(n.a,{href:"https://code.visualstudio.com/docs/editor/integrated-terminal",children:"integrated terminal"})," in VSCode and you can start running the demo instructions."]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"\u26a0 If running via Docker Desktop for Windows"})}),"\n",(0,i.jsxs)(n.p,{children:["Note that the ",(0,i.jsxs)(n.a,{href:"https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user",children:["mounted workspace files appear owned by ",(0,i.jsx)(n.code,{children:"root"})]})," in the dev container, which will cause ",(0,i.jsx)(n.code,{children:"git"})," commands to fail with a ",(0,i.jsx)(n.code,{children:"fatal: detected dubious ownership in a repository"})," error due to ",(0,i.jsx)(n.a,{href:"https://git-scm.com/docs/git-config/2.35.2#Documentation/git-config.txt-safedirectory",children:"safe.directory"})," checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the ",(0,i.jsx)(n.code,{children:"vscode"})," user:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"sudo chown -R vscode:vscode /workspace/copacetic\n"})}),"\n"]}),"\n",(0,i.jsx)(n.h4,{id:"personalizing-user-settings-in-a-dev-container",children:"Personalizing user settings in a dev container"}),"\n",(0,i.jsxs)(n.p,{children:["VSCode supports applying your user settings, such as your ",(0,i.jsx)(n.code,{children:".gitconfig"}),", to a dev container through the use of ",(0,i.jsx)(n.a,{href:"https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories",children:"dotfiles repositories"}),". This can be done through your own VSCode ",(0,i.jsx)(n.code,{children:"settings.json"})," file without changing the dev container image or configuration."]}),"\n",(0,i.jsx)(n.h3,{id:"tests",children:"Tests"}),"\n",(0,i.jsxs)(n.p,{children:["Once you can successfully ",(0,i.jsx)(n.code,{children:"make"})," the project, any code contributions should also successfully:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Pass unit tests via ",(0,i.jsx)(n.code,{children:"make test"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["Lint cleanly via ",(0,i.jsx)(n.code,{children:"make lint"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["Pull requests will also be expected to pass the PR functional tests specified by ",(0,i.jsx)(n.code,{children:".github/workflows/build.yml"}),"."]}),"\n",(0,i.jsx)(n.h3,{id:"pull-requests",children:"Pull Requests"}),"\n",(0,i.jsxs)(n.p,{children:["If you'd like to start contributing code to the project, you can search for ",(0,i.jsxs)(n.a,{href:"https://github.com/project-copacetic/copacetic/labels/good%20first%20issue",children:["issues with the ",(0,i.jsx)(n.code,{children:"good first issue"})," label"]}),". Other kinds of PR contributions we would look for include:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Fixes for bugs and other correctness issues."}),"\n",(0,i.jsx)(n.li,{children:"Docs and other content improvements (e.g. samples)."}),"\n",(0,i.jsx)(n.li,{children:"Extensions to support parsing new scanning report formats."}),"\n",(0,i.jsx)(n.li,{children:"Extensions to support patching images based on new distros or using new package managers."}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Architectural changes (e.g. breaking interfaces or violations of ",(0,i.jsx)(n.a,{href:"/copacetic/website/v0.1.x/design",children:"this project's design tenets"}),")."]}),"\n",(0,i.jsx)(n.li,{children:"Unsolicited features that significantly expand the functional scope of the tool."}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the ",(0,i.jsx)(n.a,{href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format",children:"Angular commit message format"})," for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs."]}),"\n",(0,i.jsx)(n.p,{children:"We suggest:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Use the standard header format of ",(0,i.jsx)(n.code,{children:'"<type>: <short summary>"'})," where the ",(0,i.jsx)(n.code,{children:"<type>"})," is one of the following:","\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"build:"})," Changes that affect the build system or external dependencies"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"ci:"})," Changes to the GitHub workflows and configurations"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"docs:"})," Documentation only changes"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"feat:"})," A new feature"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"fix:"})," A bug fix"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"perf:"})," A code change that improves performance"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"refactor:"})," A code change that neither fixes a bug nor adds a feature"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"test:"})," Adding missing tests or correcting existing tests"]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["Use a ",(0,i.jsx)(n.a,{href:"https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html",children:"concise, imperative description"})," of the changes included in the ",(0,i.jsx)(n.code,{children:"<short summary>"})," of the header, the body of the PR, and generally in your commit messages."]}),"\n",(0,i.jsxs)(n.li,{children:["Use ",(0,i.jsx)(n.a,{href:"https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests",children:"GitHub keywords"})," in the footer of your PR description, such as ",(0,i.jsx)(n.code,{children:"closes"})," to automatically close issues the PR intends to address."]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"developer-certificate-of-origin-dco",children:"Developer Certificate of Origin (DCO)"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.a,{href:"https://wiki.linuxfoundation.org/dco",children:"Developer Certificate of Origin"})," (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the ",(0,i.jsx)(n.a,{href:"https://developercertificate.org/",children:"full text of the DCO"}),", reformatted for readability:"]}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsx)(n.p,{children:"By making a contribution to this project, I certify that:"}),"\n",(0,i.jsx)(n.p,{children:"(a) The contribution was created in whole or in part by me and I\nhave the right to submit it under the open source license\nindicated in the file; or"}),"\n",(0,i.jsx)(n.p,{children:"(b) The contribution is based upon previous work that, to the best\nof my knowledge, is covered under an appropriate open source\nlicense and I have the right under that license to submit that\nwork with modifications, whether created in whole or in part\nby me, under the same open source license (unless I am\npermitted to submit under a different license), as indicated\nin the file; or"}),"\n",(0,i.jsx)(n.p,{children:"(c) The contribution was provided directly to me by some other\nperson who certified (a), (b) or (c) and I have not modified\nit."}),"\n",(0,i.jsx)(n.p,{children:"(d) I understand and agree that this project and the contribution\nare public and that a record of the contribution (including all\npersonal information I submit with it, including my sign-off) is\nmaintained indefinitely and may be redistributed consistent with\nthis project or the open source license(s) involved."}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["Contributors ",(0,i.jsx)(n.em,{children:"sign-off"})," that they adhere to these requirements by adding a ",(0,i.jsx)(n.code,{children:"Signed-off-by"})," line to commit messages."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-text",children:"This is my commit message\n\nSigned-off-by: Random J Developer <random@developer.example.org>\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Git even has a ",(0,i.jsx)(n.code,{children:"-s"})," command line option to append this automatically to your commit message:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"git commit -s -m 'This is my commit message'\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Pull requests that do not contain a valid ",(0,i.jsx)(n.code,{children:"Signed-off-by"})," line cannot be merged."]}),"\n",(0,i.jsx)(n.h3,{id:"i-didnt-sign-my-commit-now-what",children:"I didn't sign my commit, now what?"}),"\n",(0,i.jsx)(n.p,{children:"No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"git switch <branch-name>\ngit commit --amend --no-edit --signoff\ngit push --force-with-lease <remote-name> <branch-name>\n"})}),"\n",(0,i.jsx)(n.h2,{id:"code-of-conduct",children:"Code of Conduct"}),"\n",(0,i.jsxs)(n.p,{children:["This project has adopted the ",(0,i.jsx)(n.a,{href:"/copacetic/website/v0.1.x/code-of-conduct",children:"Contributor Covenant Code of Conduct"}),"."]})]})}function h(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(l,{...e})}):l(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>r,x:()=>c});var i=t(6540);const s={},o=i.createContext(s);function r(e){const n=i.useContext(o);return i.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),i.createElement(o.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/efdb11b6.1df4609f.js b/website/assets/js/efdb11b6.1df4609f.js deleted file mode 100644 index cbcf1d79..00000000 --- a/website/assets/js/efdb11b6.1df4609f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4111],{7064:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"v0.5.x","label":"v0.5.x","banner":"unmaintained","badge":true,"noIndex":false,"className":"docs-version-v0.5.x","isLast":false,"docsSidebars":{"sidebar":[{"type":"link","label":"Introduction","href":"/copacetic/website/v0.5.x/","docId":"introduction"},{"type":"link","label":"Installation","href":"/copacetic/website/v0.5.x/installation","docId":"installation"},{"type":"link","label":"Quick Start","href":"/copacetic/website/v0.5.x/quick-start","docId":"quick-start"},{"type":"link","label":"Output","href":"/copacetic/website/v0.5.x/output","docId":"output"},{"type":"link","label":"Troubleshooting","href":"/copacetic/website/v0.5.x/troubleshooting","docId":"troubleshooting"},{"type":"link","label":"Design","href":"/copacetic/website/v0.5.x/design","docId":"design"},{"type":"link","label":"FAQ","href":"/copacetic/website/v0.5.x/faq","docId":"faq"},{"type":"link","label":"Scanner Plugins","href":"/copacetic/website/v0.5.x/scanner-plugins","docId":"scanner-plugins"},{"type":"link","label":"Contributing","href":"/copacetic/website/v0.5.x/contributing","docId":"contributing"},{"type":"link","label":"Code of Conduct","href":"/copacetic/website/v0.5.x/code-of-conduct","docId":"code-of-conduct"},{"type":"link","label":"Copa Github Action","href":"/copacetic/website/v0.5.x/github-action","docId":"github-action"},{"type":"link","label":"Release Process","href":"/copacetic/website/v0.5.x/release","docId":"release"}]},"docs":{"code-of-conduct":{"id":"code-of-conduct","title":"Code of Conduct","description":"Our Pledge","sidebar":"sidebar"},"contributing":{"id":"contributing","title":"Contributing","description":"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.","sidebar":"sidebar"},"design":{"id":"design","title":"Design","description":"Design Tenets","sidebar":"sidebar"},"faq":{"id":"faq","title":"FAQ","description":"What kind of vulnerabilities can Copa patch?","sidebar":"sidebar"},"github-action":{"id":"github-action","title":"Copa Github Action","description":"Overview","sidebar":"sidebar"},"installation":{"id":"installation","title":"Installation","description":"Homebrew","sidebar":"sidebar"},"introduction":{"id":"introduction","title":"Introduction","description":"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.","sidebar":"sidebar"},"output":{"id":"output","title":"Output","description":"Experimental: This feature might change without preserving backwards compatibility.","sidebar":"sidebar"},"quick-start":{"id":"quick-start","title":"Quick Start","description":"This sample illustrates how to patch containers using vulnerability reports with copa.","sidebar":"sidebar"},"release":{"id":"release","title":"Release Process","description":"Overview","sidebar":"sidebar"},"scanner-plugins":{"id":"scanner-plugins","title":"Scanner Plugins","description":"By default, copa uses Trivy to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners.","sidebar":"sidebar"},"troubleshooting":{"id":"troubleshooting","title":"Troubleshooting","description":"Filtering Vulnerabilities","sidebar":"sidebar"}}}')}}]); \ No newline at end of file diff --git a/website/assets/js/efdb11b6.2663fe04.js b/website/assets/js/efdb11b6.2663fe04.js new file mode 100644 index 00000000..e8b624d9 --- /dev/null +++ b/website/assets/js/efdb11b6.2663fe04.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4586],{3412:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"v0.5.x","label":"v0.5.x","banner":"unmaintained","badge":true,"noIndex":false,"className":"docs-version-v0.5.x","isLast":false,"docsSidebars":{"sidebar":[{"type":"link","label":"Introduction","href":"/copacetic/website/v0.5.x/","docId":"introduction","unlisted":false},{"type":"link","label":"Installation","href":"/copacetic/website/v0.5.x/installation","docId":"installation","unlisted":false},{"type":"link","label":"Quick Start","href":"/copacetic/website/v0.5.x/quick-start","docId":"quick-start","unlisted":false},{"type":"link","label":"Output","href":"/copacetic/website/v0.5.x/output","docId":"output","unlisted":false},{"type":"link","label":"Troubleshooting","href":"/copacetic/website/v0.5.x/troubleshooting","docId":"troubleshooting","unlisted":false},{"type":"link","label":"Design","href":"/copacetic/website/v0.5.x/design","docId":"design","unlisted":false},{"type":"link","label":"FAQ","href":"/copacetic/website/v0.5.x/faq","docId":"faq","unlisted":false},{"type":"link","label":"Scanner Plugins","href":"/copacetic/website/v0.5.x/scanner-plugins","docId":"scanner-plugins","unlisted":false},{"type":"link","label":"Contributing","href":"/copacetic/website/v0.5.x/contributing","docId":"contributing","unlisted":false},{"type":"link","label":"Code of Conduct","href":"/copacetic/website/v0.5.x/code-of-conduct","docId":"code-of-conduct","unlisted":false},{"type":"link","label":"Copa Github Action","href":"/copacetic/website/v0.5.x/github-action","docId":"github-action","unlisted":false},{"type":"link","label":"Release Process","href":"/copacetic/website/v0.5.x/release","docId":"release","unlisted":false}]},"docs":{"code-of-conduct":{"id":"code-of-conduct","title":"Code of Conduct","description":"Our Pledge","sidebar":"sidebar"},"contributing":{"id":"contributing","title":"Contributing","description":"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.","sidebar":"sidebar"},"design":{"id":"design","title":"Design","description":"Design Tenets","sidebar":"sidebar"},"faq":{"id":"faq","title":"FAQ","description":"What kind of vulnerabilities can Copa patch?","sidebar":"sidebar"},"github-action":{"id":"github-action","title":"Copa Github Action","description":"Overview","sidebar":"sidebar"},"installation":{"id":"installation","title":"Installation","description":"Homebrew","sidebar":"sidebar"},"introduction":{"id":"introduction","title":"Introduction","description":"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.","sidebar":"sidebar"},"output":{"id":"output","title":"Output","description":"Experimental: This feature might change without preserving backwards compatibility.","sidebar":"sidebar"},"quick-start":{"id":"quick-start","title":"Quick Start","description":"This sample illustrates how to patch containers using vulnerability reports with copa.","sidebar":"sidebar"},"release":{"id":"release","title":"Release Process","description":"Overview","sidebar":"sidebar"},"scanner-plugins":{"id":"scanner-plugins","title":"Scanner Plugins","description":"By default, copa uses Trivy to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners.","sidebar":"sidebar"},"troubleshooting":{"id":"troubleshooting","title":"Troubleshooting","description":"Filtering Vulnerabilities","sidebar":"sidebar"}}}')}}]); \ No newline at end of file diff --git a/website/assets/js/f3bd9382.b63c2bc3.js b/website/assets/js/f3bd9382.b63c2bc3.js new file mode 100644 index 00000000..88f92e1a --- /dev/null +++ b/website/assets/js/f3bd9382.b63c2bc3.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2898],{8389:(e,t,i)=>{i.r(t),i.d(t,{assets:()=>a,contentTitle:()=>r,default:()=>h,frontMatter:()=>n,metadata:()=>o,toc:()=>l});var c=i(4848),s=i(8453);const n={title:"Release Process"},r=void 0,o={id:"release",title:"Release Process",description:"Overview",source:"@site/versioned_docs/version-v0.4.x/release.md",sourceDirName:".",slug:"/release",permalink:"/copacetic/website/v0.4.x/release",draft:!1,unlisted:!1,tags:[],version:"v0.4.x",frontMatter:{title:"Release Process"},sidebar:"sidebar",previous:{title:"Copa Github Action",permalink:"/copacetic/website/v0.4.x/github-action"}},a={},l=[{value:"Overview",id:"overview",level:2},{value:"Publishing",id:"publishing",level:2}];function p(e){const t={a:"a",code:"code",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",...(0,s.R)(),...e.components};return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(t.h2,{id:"overview",children:"Overview"}),"\n",(0,c.jsxs)(t.p,{children:["The release process for Copacetic uses ",(0,c.jsx)(t.a,{href:"https://goreleaser.com/",children:"GoReleaser"}),"."]}),"\n",(0,c.jsx)(t.p,{children:"Once you are ready to cut a new release, checkout the release branch and tag it with the respective version."}),"\n",(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{children:"git checkout <BRANCH NAME>\ngit pull origin <BRANCH NAME>\ngit tag -a <NEW VERSION> -m '<NEW VERSION>'\ngit push origin <NEW VERSION>\n"})}),"\n",(0,c.jsx)(t.h2,{id:"publishing",children:"Publishing"}),"\n",(0,c.jsxs)(t.ol,{children:["\n",(0,c.jsxs)(t.li,{children:["GoReleaser will create a new release, review and edit it at ",(0,c.jsx)(t.a,{href:"https://github.com/project-copacetic/copacetic/releases",children:"https://github.com/project-copacetic/copacetic/releases"})]}),"\n",(0,c.jsxs)(t.li,{children:["Review the respective copa-action image at: ",(0,c.jsx)(t.a,{href:"https://github.com/orgs/project-copacetic/packages/container/package/copa-action",children:"https://github.com/orgs/project-copacetic/packages/container/package/copa-action"})]}),"\n"]})]})}function h(e={}){const{wrapper:t}={...(0,s.R)(),...e.components};return t?(0,c.jsx)(t,{...e,children:(0,c.jsx)(p,{...e})}):p(e)}},8453:(e,t,i)=>{i.d(t,{R:()=>r,x:()=>o});var c=i(6540);const s={},n=c.createContext(s);function r(e){const t=c.useContext(n);return c.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function o(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),c.createElement(n.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/f3bd9382.cfea1d0a.js b/website/assets/js/f3bd9382.cfea1d0a.js deleted file mode 100644 index 308bfdc9..00000000 --- a/website/assets/js/f3bd9382.cfea1d0a.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[494],{3905:(e,t,r)=>{r.d(t,{Zo:()=>s,kt:()=>m});var n=r(7294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?o(Object(r),!0).forEach((function(t){a(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):o(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function c(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r,n,a={},o=Object.keys(e);for(n=0;n<o.length;n++)r=o[n],t.indexOf(r)>=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n<o.length;n++)r=o[n],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var l=n.createContext({}),p=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},s=function(e){var t=p(e.components);return n.createElement(l.Provider,{value:t},e.children)},u="mdxType",v={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},f=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,o=e.originalType,l=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),u=p(r),f=a,m=u["".concat(l,".").concat(f)]||u[f]||v[f]||o;return r?n.createElement(m,i(i({ref:t},s),{},{components:r})):n.createElement(m,i({ref:t},s))}));function m(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=r.length,i=new Array(o);i[0]=f;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[u]="string"==typeof e?e:a,i[1]=c;for(var p=2;p<o;p++)i[p]=r[p];return n.createElement.apply(null,i)}return n.createElement.apply(null,r)}f.displayName="MDXCreateElement"},8034:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>c,toc:()=>p});var n=r(7462),a=(r(7294),r(3905));const o={title:"Release Process"},i=void 0,c={unversionedId:"release",id:"version-v0.4.x/release",title:"Release Process",description:"Overview",source:"@site/versioned_docs/version-v0.4.x/release.md",sourceDirName:".",slug:"/release",permalink:"/copacetic/website/v0.4.x/release",draft:!1,tags:[],version:"v0.4.x",frontMatter:{title:"Release Process"},sidebar:"sidebar",previous:{title:"Copa Github Action",permalink:"/copacetic/website/v0.4.x/github-action"}},l={},p=[{value:"Overview",id:"overview",level:2},{value:"Publishing",id:"publishing",level:2}],s={toc:p};function u(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},s,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h2",{id:"overview"},"Overview"),(0,a.kt)("p",null,"The release process for Copacetic uses ",(0,a.kt)("a",{parentName:"p",href:"https://goreleaser.com/"},"GoReleaser"),". "),(0,a.kt)("p",null,"Once you are ready to cut a new release, checkout the release branch and tag it with the respective version."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"```\ngit checkout <BRANCH NAME>\ngit pull origin <BRANCH NAME>\ngit tag -a <NEW VERSION> -m '<NEW VERSION>'\ngit push origin <NEW VERSION>\n```\n")),(0,a.kt)("h2",{id:"publishing"},"Publishing"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"GoReleaser will create a new release, review and edit it at ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/project-copacetic/copacetic/releases"},"https://github.com/project-copacetic/copacetic/releases")),(0,a.kt)("li",{parentName:"ol"},"Review the respective copa-action image at: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/orgs/project-copacetic/packages/container/package/copa-action"},"https://github.com/orgs/project-copacetic/packages/container/package/copa-action"))))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/f9459e94.d4932d2b.js b/website/assets/js/f9459e94.d4932d2b.js deleted file mode 100644 index f6739eb5..00000000 --- a/website/assets/js/f9459e94.d4932d2b.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1220],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>k});var i=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?a(Object(n),!0).forEach((function(t){r(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):a(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,i,r=function(e,t){if(null==e)return{};var n,i,r={},a=Object.keys(e);for(i=0;i<a.length;i++)n=a[i],t.indexOf(n)>=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(i=0;i<a.length;i++)n=a[i],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=i.createContext({}),c=function(e){var t=i.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return i.createElement(l.Provider,{value:t},e.children)},d="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},m=i.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),d=c(n),m=r,k=d["".concat(l,".").concat(m)]||d[m]||u[m]||a;return n?i.createElement(k,o(o({ref:t},p),{},{components:n})):i.createElement(k,o({ref:t},p))}));function k(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,o=new Array(a);o[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[d]="string"==typeof e?e:r,o[1]=s;for(var c=2;c<a;c++)o[c]=n[c];return i.createElement.apply(null,o)}return i.createElement.apply(null,n)}m.displayName="MDXCreateElement"},5616:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>d,frontMatter:()=>a,metadata:()=>s,toc:()=>c});var i=n(7462),r=(n(7294),n(3905));const a={title:"Quick Start"},o=void 0,s={unversionedId:"quick-start",id:"version-v0.3.x/quick-start",title:"Quick Start",description:"This sample illustrates how to patch containers using vulnerability reports with copa.",source:"@site/versioned_docs/version-v0.3.x/quick-start.md",sourceDirName:".",slug:"/quick-start",permalink:"/copacetic/website/v0.3.x/quick-start",draft:!1,tags:[],version:"v0.3.x",frontMatter:{title:"Quick Start"},sidebar:"sidebar",previous:{title:"Installation",permalink:"/copacetic/website/v0.3.x/installation"},next:{title:"Design",permalink:"/copacetic/website/v0.3.x/design"}},l={},c=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Sample Steps",id:"sample-steps",level:2}],p={toc:c};function d(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,i.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"This sample illustrates how to patch containers using vulnerability reports with ",(0,r.kt)("inlineCode",{parentName:"p"},"copa"),"."),(0,r.kt)("h2",{id:"prerequisites"},"Prerequisites"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"An Ubuntu 22.04 VM configured through the ",(0,r.kt)("a",{parentName:"li",href:"/copacetic/website/v0.3.x/installation"},"setup instructions")," or a VSCode ",(0,r.kt)("a",{parentName:"li",href:"/copacetic/website/v0.3.x/contributing/#visual-studio-code-development-container"},"devcontainer")," environment. This includes:",(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"copa")," tool ",(0,r.kt)("a",{parentName:"li",href:"/copacetic/website/v0.3.x/installation"},"built & pathed"),"."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://github.com/moby/buildkit/#quick-start"},"buildkit")," daemon installed & pathed."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://docs.docker.com/desktop/linux/install/#generic-installation-steps"},"docker")," daemon running and CLI installed & pathed."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://aquasecurity.github.io/trivy/latest/getting-started/installation/"},"trivy CLI")," installed & pathed.")))),(0,r.kt)("h2",{id:"sample-steps"},"Sample Steps"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("p",{parentName:"li"},"Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6\n")),(0,r.kt)("p",{parentName:"li"},"You can also see the existing patchable vulnerabilities in table form on the shell with:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6\n\n"))),(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("p",{parentName:"li"},"Patch the image using the Trivy report. You will need to start ",(0,r.kt)("inlineCode",{parentName:"p"},"buildkitd")," if it is not already running:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"sudo buildkitd &\nsudo copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched\n")),(0,r.kt)("p",{parentName:"li"},"Alternatively, you can run ",(0,r.kt)("inlineCode",{parentName:"p"},"buildkitd")," in a container, which allows copa to be run without root access to the local buildkit socket:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'export BUILDKIT_VERSION=v0.11.4\nexport BUILDKIT_PORT=8888\ndocker run \\\n --detach \\\n --rm \\\n --privileged \\\n -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION" \\\n --addr tcp://0.0.0.0:$BUILDKIT_PORT\ncopa patch \\\n -i docker.io/library/nginx:1.21.6 \\\n -r nginx.1.21.6.json \\\n -t 1.21.6-patched \\\n -a tcp://0.0.0.0:$BUILDKIT_PORT\n')),(0,r.kt)("p",{parentName:"li"},"In either case, ",(0,r.kt)("inlineCode",{parentName:"p"},"copa")," is non-destructive and exports a new image with the specified ",(0,r.kt)("inlineCode",{parentName:"p"},"1.21.6-patched")," label to the local Docker daemon."),(0,r.kt)("blockquote",{parentName:"li"},(0,r.kt)("p",{parentName:"blockquote"},(0,r.kt)("strong",{parentName:"p"},"NOTE:")," if you're running this sample against an image from a private registry instead,\nensure that the credentials are configured in the default Docker config.json before running ",(0,r.kt)("inlineCode",{parentName:"p"},"copa patch"),",\nfor example, via ",(0,r.kt)("inlineCode",{parentName:"p"},"sudo docker login -u <user> -p <password> <registry>"),"."))),(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("p",{parentName:"li"},"Scan the patched image and verify that the vulnerabilities have been patched:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched\n")),(0,r.kt)("p",{parentName:"li"},"You can also inspect the structure of the patched image with ",(0,r.kt)("inlineCode",{parentName:"p"},"docker history")," to see the new patch layer appended to the image:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'$ docker history docker.io/library/nginx:1.21.6-patched\nIMAGE CREATED CREATED BY SIZE COMMENT\na372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins\u2026 26.1MB buildkit.exporter.image.v0\n<missing> 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0\n<missing> 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0\n<missing> 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0\n<missing> 3 months ago COPY 30-tune-worker-processes.sh /docker-ent\u2026 4.61kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent\u2026 1.04kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker\u2026 1.96kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0\n<missing> 3 months ago RUN /bin/sh -c set -x && addgroup --syst\u2026 61.1MB buildkit.dockerfile.v0\n<missing> 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0\n<missing> 3 months ago LABEL maintainer=NGINX Docker Maintainers <d\u2026 0B buildkit.dockerfile.v0\n<missing> 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B\n<missing> 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55\u2026 80.4MB\n'))),(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("p",{parentName:"li"},"Run the container to verify that the image has no regressions:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'$ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched\n/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration\n/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh\n10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf\n10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh\n/docker-entrypoint.sh: Configuration complete; ready for start up\n2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method\n2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2\n2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)\n2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2\n2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576\n2022/05/16 18:00:17 [notice] 1#1: start worker processes\n2022/05/16 18:00:17 [notice] 1#1: start worker process 31\n2022/05/16 18:00:17 [notice] 1#1: start worker process 32\n2022/05/16 18:00:17 [notice] 1#1: start worker process 33\n2022/05/16 18:00:17 [notice] 1#1: start worker process 34\n2022/05/16 18:00:17 [notice] 1#1: start worker process 35\n2022/05/16 18:00:17 [notice] 1#1: start worker process 36\n2022/05/16 18:00:17 [notice] 1#1: start worker process 37\n2022/05/16 18:00:17 [notice] 1#1: start worker process 38\n2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received\n')),(0,r.kt)("p",{parentName:"li"},"You can stop the container by opening a new shell instance and running: ",(0,r.kt)("inlineCode",{parentName:"p"},"docker stop nginx-test")))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/f9459e94.d7263fc2.js b/website/assets/js/f9459e94.d7263fc2.js new file mode 100644 index 00000000..6f8e5b0c --- /dev/null +++ b/website/assets/js/f9459e94.d7263fc2.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[7508],{9379:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>a,contentTitle:()=>r,default:()=>h,frontMatter:()=>o,metadata:()=>c,toc:()=>l});var t=i(4848),s=i(8453);const o={title:"Quick Start"},r=void 0,c={id:"quick-start",title:"Quick Start",description:"This sample illustrates how to patch containers using vulnerability reports with copa.",source:"@site/versioned_docs/version-v0.3.x/quick-start.md",sourceDirName:".",slug:"/quick-start",permalink:"/copacetic/website/v0.3.x/quick-start",draft:!1,unlisted:!1,tags:[],version:"v0.3.x",frontMatter:{title:"Quick Start"},sidebar:"sidebar",previous:{title:"Installation",permalink:"/copacetic/website/v0.3.x/installation"},next:{title:"Design",permalink:"/copacetic/website/v0.3.x/design"}},a={},l=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Sample Steps",id:"sample-steps",level:2}];function d(e){const n={a:"a",blockquote:"blockquote",code:"code",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(n.p,{children:["This sample illustrates how to patch containers using vulnerability reports with ",(0,t.jsx)(n.code,{children:"copa"}),"."]}),"\n",(0,t.jsx)(n.h2,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["An Ubuntu 22.04 VM configured through the ",(0,t.jsx)(n.a,{href:"/copacetic/website/v0.3.x/installation",children:"setup instructions"})," or a VSCode ",(0,t.jsx)(n.a,{href:"./contributing.md/#visual-studio-code-development-container",children:"devcontainer"})," environment. This includes:","\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"copa"})," tool ",(0,t.jsx)(n.a,{href:"/copacetic/website/v0.3.x/installation",children:"built & pathed"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://github.com/moby/buildkit/#quick-start",children:"buildkit"})," daemon installed & pathed."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://docs.docker.com/desktop/linux/install/#generic-installation-steps",children:"docker"})," daemon running and CLI installed & pathed."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"https://aquasecurity.github.io/trivy/latest/getting-started/installation/",children:"trivy CLI"})," installed & pathed."]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,t.jsx)(n.h2,{id:"sample-steps",children:"Sample Steps"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6\n"})}),"\n",(0,t.jsx)(n.p,{children:"You can also see the existing patchable vulnerabilities in table form on the shell with:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6\n\n"})}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsxs)(n.p,{children:["Patch the image using the Trivy report. You will need to start ",(0,t.jsx)(n.code,{children:"buildkitd"})," if it is not already running:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"sudo buildkitd &\nsudo copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched\n"})}),"\n",(0,t.jsxs)(n.p,{children:["Alternatively, you can run ",(0,t.jsx)(n.code,{children:"buildkitd"})," in a container, which allows copa to be run without root access to the local buildkit socket:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'export BUILDKIT_VERSION=v0.11.4\nexport BUILDKIT_PORT=8888\ndocker run \\\n --detach \\\n --rm \\\n --privileged \\\n -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \\\n --name buildkitd \\\n --entrypoint buildkitd \\\n "moby/buildkit:$BUILDKIT_VERSION" \\\n --addr tcp://0.0.0.0:$BUILDKIT_PORT\ncopa patch \\\n -i docker.io/library/nginx:1.21.6 \\\n -r nginx.1.21.6.json \\\n -t 1.21.6-patched \\\n -a tcp://0.0.0.0:$BUILDKIT_PORT\n'})}),"\n",(0,t.jsxs)(n.p,{children:["In either case, ",(0,t.jsx)(n.code,{children:"copa"})," is non-destructive and exports a new image with the specified ",(0,t.jsx)(n.code,{children:"1.21.6-patched"})," label to the local Docker daemon."]}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"NOTE:"})," if you're running this sample against an image from a private registry instead,\nensure that the credentials are configured in the default Docker config.json before running ",(0,t.jsx)(n.code,{children:"copa patch"}),",\nfor example, via ",(0,t.jsx)(n.code,{children:"sudo docker login -u <user> -p <password> <registry>"}),"."]}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Scan the patched image and verify that the vulnerabilities have been patched:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched\n"})}),"\n",(0,t.jsxs)(n.p,{children:["You can also inspect the structure of the patched image with ",(0,t.jsx)(n.code,{children:"docker history"})," to see the new patch layer appended to the image:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'$ docker history docker.io/library/nginx:1.21.6-patched\nIMAGE CREATED CREATED BY SIZE COMMENT\na372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins\u2026 26.1MB buildkit.exporter.image.v0\n<missing> 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0\n<missing> 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0\n<missing> 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0\n<missing> 3 months ago COPY 30-tune-worker-processes.sh /docker-ent\u2026 4.61kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent\u2026 1.04kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker\u2026 1.96kB buildkit.dockerfile.v0\n<missing> 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0\n<missing> 3 months ago RUN /bin/sh -c set -x && addgroup --syst\u2026 61.1MB buildkit.dockerfile.v0\n<missing> 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0\n<missing> 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0\n<missing> 3 months ago LABEL maintainer=NGINX Docker Maintainers <d\u2026 0B buildkit.dockerfile.v0\n<missing> 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B\n<missing> 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55\u2026 80.4MB\n'})}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Run the container to verify that the image has no regressions:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:'$ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched\n/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration\n/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh\n10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf\n10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh\n/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh\n/docker-entrypoint.sh: Configuration complete; ready for start up\n2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method\n2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2\n2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)\n2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2\n2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576\n2022/05/16 18:00:17 [notice] 1#1: start worker processes\n2022/05/16 18:00:17 [notice] 1#1: start worker process 31\n2022/05/16 18:00:17 [notice] 1#1: start worker process 32\n2022/05/16 18:00:17 [notice] 1#1: start worker process 33\n2022/05/16 18:00:17 [notice] 1#1: start worker process 34\n2022/05/16 18:00:17 [notice] 1#1: start worker process 35\n2022/05/16 18:00:17 [notice] 1#1: start worker process 36\n2022/05/16 18:00:17 [notice] 1#1: start worker process 37\n2022/05/16 18:00:17 [notice] 1#1: start worker process 38\n2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received\n2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received\n'})}),"\n",(0,t.jsxs)(n.p,{children:["You can stop the container by opening a new shell instance and running: ",(0,t.jsx)(n.code,{children:"docker stop nginx-test"})]}),"\n"]}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(d,{...e})}):d(e)}},8453:(e,n,i)=>{i.d(n,{R:()=>r,x:()=>c});var t=i(6540);const s={},o=t.createContext(s);function r(e){const n=t.useContext(o);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),t.createElement(o.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/fa2b770f.2b296f82.js b/website/assets/js/fa2b770f.2b296f82.js new file mode 100644 index 00000000..47443e6a --- /dev/null +++ b/website/assets/js/fa2b770f.2b296f82.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[5368],{2847:(e,i,a)=>{a.r(i),a.d(i,{assets:()=>r,contentTitle:()=>c,default:()=>d,frontMatter:()=>o,metadata:()=>s,toc:()=>l});var n=a(4848),t=a(8453);const o={title:"FAQ"},c=void 0,s={id:"faq",title:"FAQ",description:"What kind of vulnerabilities can Copa patch?",source:"@site/versioned_docs/version-v0.5.x/faq.md",sourceDirName:".",slug:"/faq",permalink:"/copacetic/website/v0.5.x/faq",draft:!1,unlisted:!1,tags:[],version:"v0.5.x",frontMatter:{title:"FAQ"},sidebar:"sidebar",previous:{title:"Design",permalink:"/copacetic/website/v0.5.x/design"},next:{title:"Scanner Plugins",permalink:"/copacetic/website/v0.5.x/scanner-plugins"}},r={},l=[{value:"What kind of vulnerabilities can Copa patch?",id:"what-kind-of-vulnerabilities-can-copa-patch",level:2},{value:"What kind of vulnerabilities can Copa not patch?",id:"what-kind-of-vulnerabilities-can-copa-not-patch",level:2},{value:"Can I replace the package repositories in the image with my own?",id:"can-i-replace-the-package-repositories-in-the-image-with-my-own",level:2}];function p(e){const i={a:"a",admonition:"admonition",blockquote:"blockquote",code:"code",h2:"h2",p:"p",pre:"pre",...(0,t.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(i.h2,{id:"what-kind-of-vulnerabilities-can-copa-patch",children:"What kind of vulnerabilities can Copa patch?"}),"\n",(0,n.jsxs)(i.p,{children:['Copa is capable of patching "OS level" vulnerabilities. This includes packages (like ',(0,n.jsx)(i.code,{children:"openssl"}),") in the image that are managed by a package manager such as ",(0,n.jsx)(i.code,{children:"apt"})," or ",(0,n.jsx)(i.code,{children:"yum"}),'. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see ',(0,n.jsx)(i.a,{href:"#what-kind-of-vulnerabilities-can-copa-not-patch",children:"below"})," for more details)."]}),"\n",(0,n.jsx)(i.h2,{id:"what-kind-of-vulnerabilities-can-copa-not-patch",children:"What kind of vulnerabilities can Copa not patch?"}),"\n",(0,n.jsxs)(i.p,{children:['Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the ',(0,n.jsx)(i.code,{children:"golang.org/x/net"})," module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level."]}),"\n",(0,n.jsxs)(i.p,{children:["To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like ",(0,n.jsx)(i.code,{children:"http://archive.ubuntu.com/ubuntu/"})," for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications."]}),"\n",(0,n.jsx)(i.h2,{id:"can-i-replace-the-package-repositories-in-the-image-with-my-own",children:"Can I replace the package repositories in the image with my own?"}),"\n",(0,n.jsx)(i.admonition,{type:"caution",children:(0,n.jsx)(i.p,{children:"Experimental: This feature might change without preserving backwards compatibility."})}),"\n",(0,n.jsxs)(i.p,{children:["Copa does not support replacing the repositories in the package managers with alternatives. Images must already use the intended package repositories. For example, for debian, updating ",(0,n.jsx)(i.code,{children:"/etc/apt/sources.list"})," from ",(0,n.jsx)(i.code,{children:"http://archive.ubuntu.com/ubuntu/"})," to a mirror, such as ",(0,n.jsx)(i.code,{children:"https://mirrors.wikimedia.org/ubuntu/"}),"."]}),"\n",(0,n.jsxs)(i.p,{children:["If you need the tooling image to use a different package repository, you can create a source policy to define a replacement image and/or pin to a digest. For example, the following source policy replaces ",(0,n.jsx)(i.code,{children:"docker.io/library/debian:11-slim"})," image with ",(0,n.jsx)(i.code,{children:"foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5"}),":"]}),"\n",(0,n.jsx)(i.pre,{children:(0,n.jsx)(i.code,{className:"language-shell",children:'cat <<EOF > source-policy.json\n{\n "rules": [\n {\n "action": "CONVERT",\n "selector": {\n "identifier": "docker-image://docker.io/library/debian:11-slim"\n },\n "updates": {\n "identifier": "docker-image://foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5"\n }\n }\n ]\n}\nEOF\n\nexport EXPERIMENTAL_BUILDKIT_SOURCE_POLICY=source-policy.json\n'})}),"\n",(0,n.jsxs)(i.blockquote,{children:["\n",(0,n.jsxs)(i.p,{children:["Tooling image for Debian-based images are ",(0,n.jsx)(i.code,{children:"docker.io/library/debian:11-slim"})," and RPM-based repos are ",(0,n.jsx)(i.code,{children:"mcr.microsoft.com/cbl-mariner/base/core:2.0"}),"."]}),"\n"]}),"\n",(0,n.jsxs)(i.p,{children:["For more information on source policies, see ",(0,n.jsx)(i.a,{href:"https://docs.docker.com/build/building/env-vars/#experimental_buildkit_source_policy",children:"Buildkit Source Policies"}),"."]})]})}function d(e={}){const{wrapper:i}={...(0,t.R)(),...e.components};return i?(0,n.jsx)(i,{...e,children:(0,n.jsx)(p,{...e})}):p(e)}},8453:(e,i,a)=>{a.d(i,{R:()=>c,x:()=>s});var n=a(6540);const t={},o=n.createContext(t);function c(e){const i=n.useContext(o);return n.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function s(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:c(e.components),n.createElement(o.Provider,{value:i},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/fa2b770f.684fae56.js b/website/assets/js/fa2b770f.684fae56.js deleted file mode 100644 index a565a78c..00000000 --- a/website/assets/js/fa2b770f.684fae56.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[6148],{3905:(e,t,a)=>{a.d(t,{Zo:()=>s,kt:()=>h});var n=a(7294);function i(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function o(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function r(e){for(var t=1;t<arguments.length;t++){var a=null!=arguments[t]?arguments[t]:{};t%2?o(Object(a),!0).forEach((function(t){i(e,t,a[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):o(Object(a)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))}))}return e}function l(e,t){if(null==e)return{};var a,n,i=function(e,t){if(null==e)return{};var a,n,i={},o=Object.keys(e);for(n=0;n<o.length;n++)a=o[n],t.indexOf(a)>=0||(i[a]=e[a]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n<o.length;n++)a=o[n],t.indexOf(a)>=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(i[a]=e[a])}return i}var c=n.createContext({}),p=function(e){var t=n.useContext(c),a=t;return e&&(a="function"==typeof e?e(t):r(r({},t),e)),a},s=function(e){var t=p(e.components);return n.createElement(c.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var a=e.components,i=e.mdxType,o=e.originalType,c=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),u=p(a),m=i,h=u["".concat(c,".").concat(m)]||u[m]||d[m]||o;return a?n.createElement(h,r(r({ref:t},s),{},{components:a})):n.createElement(h,r({ref:t},s))}));function h(e,t){var a=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=a.length,r=new Array(o);r[0]=m;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[u]="string"==typeof e?e:i,r[1]=l;for(var p=2;p<o;p++)r[p]=a[p];return n.createElement.apply(null,r)}return n.createElement.apply(null,a)}m.displayName="MDXCreateElement"},9974:(e,t,a)=>{a.r(t),a.d(t,{assets:()=>c,contentTitle:()=>r,default:()=>u,frontMatter:()=>o,metadata:()=>l,toc:()=>p});var n=a(7462),i=(a(7294),a(3905));const o={title:"FAQ"},r=void 0,l={unversionedId:"faq",id:"version-v0.5.x/faq",title:"FAQ",description:"What kind of vulnerabilities can Copa patch?",source:"@site/versioned_docs/version-v0.5.x/faq.md",sourceDirName:".",slug:"/faq",permalink:"/copacetic/website/v0.5.x/faq",draft:!1,tags:[],version:"v0.5.x",frontMatter:{title:"FAQ"},sidebar:"sidebar",previous:{title:"Design",permalink:"/copacetic/website/v0.5.x/design"},next:{title:"Scanner Plugins",permalink:"/copacetic/website/v0.5.x/scanner-plugins"}},c={},p=[{value:"What kind of vulnerabilities can Copa patch?",id:"what-kind-of-vulnerabilities-can-copa-patch",level:2},{value:"What kind of vulnerabilities can Copa not patch?",id:"what-kind-of-vulnerabilities-can-copa-not-patch",level:2},{value:"Can I replace the package repositories in the image with my own?",id:"can-i-replace-the-package-repositories-in-the-image-with-my-own",level:2}],s={toc:p};function u(e){let{components:t,...a}=e;return(0,i.kt)("wrapper",(0,n.Z)({},s,a,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h2",{id:"what-kind-of-vulnerabilities-can-copa-patch"},"What kind of vulnerabilities can Copa patch?"),(0,i.kt)("p",null,'Copa is capable of patching "OS level" vulnerabilities. This includes packages (like ',(0,i.kt)("inlineCode",{parentName:"p"},"openssl"),") in the image that are managed by a package manager such as ",(0,i.kt)("inlineCode",{parentName:"p"},"apt")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"yum"),'. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see ',(0,i.kt)("a",{parentName:"p",href:"#what-kind-of-vulnerabilities-can-copa-not-patch"},"below")," for more details)."),(0,i.kt)("h2",{id:"what-kind-of-vulnerabilities-can-copa-not-patch"},"What kind of vulnerabilities can Copa not patch?"),(0,i.kt)("p",null,'Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the ',(0,i.kt)("inlineCode",{parentName:"p"},"golang.org/x/net")," module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level."),(0,i.kt)("p",null,"To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like ",(0,i.kt)("inlineCode",{parentName:"p"},"http://archive.ubuntu.com/ubuntu/")," for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications."),(0,i.kt)("h2",{id:"can-i-replace-the-package-repositories-in-the-image-with-my-own"},"Can I replace the package repositories in the image with my own?"),(0,i.kt)("admonition",{type:"caution"},(0,i.kt)("p",{parentName:"admonition"},"Experimental: This feature might change without preserving backwards compatibility.")),(0,i.kt)("p",null,"Copa does not support replacing the repositories in the package managers with alternatives. Images must already use the intended package repositories. For example, for debian, updating ",(0,i.kt)("inlineCode",{parentName:"p"},"/etc/apt/sources.list")," from ",(0,i.kt)("inlineCode",{parentName:"p"},"http://archive.ubuntu.com/ubuntu/")," to a mirror, such as ",(0,i.kt)("inlineCode",{parentName:"p"},"https://mirrors.wikimedia.org/ubuntu/"),"."),(0,i.kt)("p",null,"If you need the tooling image to use a different package repository, you can create a source policy to define a replacement image and/or pin to a digest. For example, the following source policy replaces ",(0,i.kt)("inlineCode",{parentName:"p"},"docker.io/library/debian:11-slim")," image with ",(0,i.kt)("inlineCode",{parentName:"p"},"foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-shell"},'cat <<EOF > source-policy.json\n{\n "rules": [\n {\n "action": "CONVERT",\n "selector": {\n "identifier": "docker-image://docker.io/library/debian:11-slim"\n },\n "updates": {\n "identifier": "docker-image://foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5"\n }\n }\n ]\n}\nEOF\n\nexport EXPERIMENTAL_BUILDKIT_SOURCE_POLICY=source-policy.json\n')),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},"Tooling image for Debian-based images are ",(0,i.kt)("inlineCode",{parentName:"p"},"docker.io/library/debian:11-slim")," and RPM-based repos are ",(0,i.kt)("inlineCode",{parentName:"p"},"mcr.microsoft.com/cbl-mariner/base/core:2.0"),".")),(0,i.kt)("p",null,"For more information on source policies, see ",(0,i.kt)("a",{parentName:"p",href:"https://docs.docker.com/build/building/env-vars/#experimental_buildkit_source_policy"},"Buildkit Source Policies"),"."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/fcc13998.5e4b93fa.js b/website/assets/js/fcc13998.5e4b93fa.js deleted file mode 100644 index e5cce9ed..00000000 --- a/website/assets/js/fcc13998.5e4b93fa.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1087],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>h});var o=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function r(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?a(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):a(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,o,i=function(e,t){if(null==e)return{};var n,o,i={},a=Object.keys(e);for(o=0;o<a.length;o++)n=a[o],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o<a.length;o++)n=a[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=o.createContext({}),c=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},p=function(e){var t=c(e.components);return o.createElement(l.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),u=c(n),m=i,h=u["".concat(l,".").concat(m)]||u[m]||d[m]||a;return n?o.createElement(h,r(r({ref:t},p),{},{components:n})):o.createElement(h,r({ref:t},p))}));function h(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,r=new Array(a);r[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[u]="string"==typeof e?e:i,r[1]=s;for(var c=2;c<a;c++)r[c]=n[c];return o.createElement.apply(null,r)}return o.createElement.apply(null,n)}m.displayName="MDXCreateElement"},7534:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>u,frontMatter:()=>a,metadata:()=>s,toc:()=>c});var o=n(7462),i=(n(7294),n(3905));const a={title:"Contributing"},r=void 0,s={unversionedId:"contributing",id:"version-v0.2.x/contributing",title:"Contributing",description:"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.",source:"@site/versioned_docs/version-v0.2.x/contributing.md",sourceDirName:".",slug:"/contributing",permalink:"/copacetic/website/v0.2.x/contributing",draft:!1,tags:[],version:"v0.2.x",frontMatter:{title:"Contributing"},sidebar:"sidebar",previous:{title:"FAQ",permalink:"/copacetic/website/v0.2.x/faq"},next:{title:"Code of Conduct",permalink:"/copacetic/website/v0.2.x/code-of-conduct"}},l={},c=[{value:"Contributing Issues",id:"contributing-issues",level:2},{value:"Contributing Code",id:"contributing-code",level:2},{value:"Getting Started",id:"getting-started",level:3},{value:"Visual Studio Code Development Container",id:"visual-studio-code-development-container",level:3},{value:"Prerequisites",id:"prerequisites",level:4},{value:"Using the dev container",id:"using-the-dev-container",level:4},{value:"Personalizing user settings in a dev container",id:"personalizing-user-settings-in-a-dev-container",level:4},{value:"Tests",id:"tests",level:3},{value:"Pull Requests",id:"pull-requests",level:3},{value:"Developer Certificate of Origin (DCO)",id:"developer-certificate-of-origin-dco",level:2},{value:"I didn't sign my commit, now what?",id:"i-didnt-sign-my-commit-now-what",level:3},{value:"Code of Conduct",id:"code-of-conduct",level:2}],p={toc:c};function u(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,o.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"Welcome! We are very happy to accept community contributions to the project, whether through ",(0,i.kt)("a",{parentName:"p",href:"#contributing-issues"},"filing issues")," or ",(0,i.kt)("a",{parentName:"p",href:"#contributing-code"},"code")," in the form of ",(0,i.kt)("a",{parentName:"p",href:"#pull-requests"},"Pull Requests"),". Please note that by participating in this project, you agree to abide by the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.2.x/code-of-conduct"},"Code of Conduct"),", as well as the terms of the ",(0,i.kt)("a",{parentName:"p",href:"#developer-certificate-of-origin-dco"},"Developer Certificate of Origin"),"."),(0,i.kt)("h2",{id:"contributing-issues"},"Contributing Issues"),(0,i.kt)("p",null,"Before opening any new issues, please search our ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/issues"},"existing GitHub issues")," to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead."),(0,i.kt)("p",null,"When opening an issue, please select the most appropriate template for what you're contributing:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Bug Report:")," If you would like to report the project or tool behaving in unexpected ways."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Documentation Improvement:")," If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Request:")," If you have a feature request, suggestion, or a even a design proposal to review."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Question:")," If you would like to ask the maintainers a question about the project.")),(0,i.kt)("h2",{id:"contributing-code"},"Contributing Code"),(0,i.kt)("h3",{id:"getting-started"},"Getting Started"),(0,i.kt)("p",null,"Follow the instructions to either:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/copacetic/website/v0.2.x/installation"},"Setup your dev environment to build copa"),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"#visual-studio-code-development-container"},"Use the copa development container")," in ",(0,i.kt)("a",{parentName:"li",href:"https://code.visualstudio.com/"},"Visual Studio Code"),".")),(0,i.kt)("p",null,"For an overview of the project components, refer to the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.2.x/design"},"copa design")," document."),(0,i.kt)("h3",{id:"visual-studio-code-development-container"},"Visual Studio Code Development Container"),(0,i.kt)("p",null,(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/"},"VSCode")," supports development in a containerized environment through its ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/docs/remote/containers"},"Remote - Container extension"),". This folder provides a development container which encapsulates the dependencies specified in the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.2.x/installation"},"instructions to build and run copa"),"."),(0,i.kt)("h4",{id:"prerequisites"},"Prerequisites"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://docs.docker.com/get-docker/"},"Docker"),(0,i.kt)("blockquote",{parentName:"li"},(0,i.kt)("p",{parentName:"blockquote"},"For Windows users, enabling ",(0,i.kt)("a",{parentName:"p",href:"https://docs.docker.com/docker-for-windows/wsl/"},"WSL2 back-end integration with Docker")," is recommended."))),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://code.visualstudio.com/"},"Visual Studio Code")),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers"},"Visual Studio Code Remote - Containers extension"))),(0,i.kt)("h4",{id:"using-the-dev-container"},"Using the dev container"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("p",{parentName:"li"},"After you have cloned this repo locally, open the repo folder in VSCode. VSCode will detect the presence of this ",(0,i.kt)("inlineCode",{parentName:"p"},".devcontainer")," subfolder and will prompt you to reopen the project in a container."),(0,i.kt)("p",{parentName:"li"},"Alternatively, you can open the command palette and use the ",(0,i.kt)("inlineCode",{parentName:"p"},"Remote-Containers: Reopen in Container")," command.")),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("p",{parentName:"li"},"Once the container is loaded, open an ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/docs/editor/integrated-terminal"},"integrated terminal")," in VSCode and you can start running the demo instructions."))),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},(0,i.kt)("strong",{parentName:"p"},"\u26a0 If running via Docker Desktop for Windows")),(0,i.kt)("p",{parentName:"blockquote"},"Note that the ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user"},"mounted workspace files appear owned by ",(0,i.kt)("inlineCode",{parentName:"a"},"root"))," in the dev container, which will cause ",(0,i.kt)("inlineCode",{parentName:"p"},"git")," commands to fail with a ",(0,i.kt)("inlineCode",{parentName:"p"},"fatal: detected dubious ownership in a repository")," error due to ",(0,i.kt)("a",{parentName:"p",href:"https://git-scm.com/docs/git-config/2.35.2#Documentation/git-config.txt-safedirectory"},"safe.directory")," checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the ",(0,i.kt)("inlineCode",{parentName:"p"},"vscode")," user:"),(0,i.kt)("pre",{parentName:"blockquote"},(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"sudo chown -R vscode:vscode /workspace/copacetic\n"))),(0,i.kt)("h4",{id:"personalizing-user-settings-in-a-dev-container"},"Personalizing user settings in a dev container"),(0,i.kt)("p",null,"VSCode supports applying your user settings, such as your ",(0,i.kt)("inlineCode",{parentName:"p"},".gitconfig"),", to a dev container through the use of ",(0,i.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories"},"dotfiles repositories"),". This can be done through your own VSCode ",(0,i.kt)("inlineCode",{parentName:"p"},"settings.json")," file without changing the dev container image or configuration."),(0,i.kt)("h3",{id:"tests"},"Tests"),(0,i.kt)("p",null,"Once you can successfully ",(0,i.kt)("inlineCode",{parentName:"p"},"make")," the project, any code contributions should also successfully:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Pass unit tests via ",(0,i.kt)("inlineCode",{parentName:"li"},"make test"),"."),(0,i.kt)("li",{parentName:"ul"},"Lint cleanly via ",(0,i.kt)("inlineCode",{parentName:"li"},"make lint"),".")),(0,i.kt)("p",null,"Pull requests will also be expected to pass the PR functional tests specified by ",(0,i.kt)("inlineCode",{parentName:"p"},".github/workflows/build.yml"),"."),(0,i.kt)("h3",{id:"pull-requests"},"Pull Requests"),(0,i.kt)("p",null,"If you'd like to start contributing code to the project, you can search for ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/project-copacetic/copacetic/labels/good%20first%20issue"},"issues with the ",(0,i.kt)("inlineCode",{parentName:"a"},"good first issue")," label"),". Other kinds of PR contributions we would look for include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Fixes for bugs and other correctness issues."),(0,i.kt)("li",{parentName:"ul"},"Docs and other content improvements (e.g. samples)."),(0,i.kt)("li",{parentName:"ul"},"Extensions to support parsing new scanning report formats."),(0,i.kt)("li",{parentName:"ul"},"Extensions to support patching images based on new distros or using new package managers.")),(0,i.kt)("p",null,"For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Architectural changes (e.g. breaking interfaces or violations of ",(0,i.kt)("a",{parentName:"li",href:"/copacetic/website/v0.2.x/design"},"this project's design tenets"),")."),(0,i.kt)("li",{parentName:"ul"},"Unsolicited features that significantly expand the functional scope of the tool.")),(0,i.kt)("p",null,"Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format"},"Angular commit message format")," for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs."),(0,i.kt)("p",null,"We suggest:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Use the standard header format of ",(0,i.kt)("inlineCode",{parentName:"li"},'"<type>: <short summary>"')," where the ",(0,i.kt)("inlineCode",{parentName:"li"},"<type>")," is one of the following:",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"build:")," Changes that affect the build system or external dependencies"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"ci:")," Changes to the GitHub workflows and configurations"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"docs:")," Documentation only changes"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"feat:")," A new feature"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"fix:")," A bug fix"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"perf:")," A code change that improves performance"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"refactor:")," A code change that neither fixes a bug nor adds a feature"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"test:")," Adding missing tests or correcting existing tests"))),(0,i.kt)("li",{parentName:"ul"},"Use a ",(0,i.kt)("a",{parentName:"li",href:"https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html"},"concise, imperative description")," of the changes included in the ",(0,i.kt)("inlineCode",{parentName:"li"},"<short summary>")," of the header, the body of the PR, and generally in your commit messages."),(0,i.kt)("li",{parentName:"ul"},"Use ",(0,i.kt)("a",{parentName:"li",href:"https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests"},"GitHub keywords")," in the footer of your PR description, such as ",(0,i.kt)("inlineCode",{parentName:"li"},"closes")," to automatically close issues the PR intends to address.")),(0,i.kt)("h2",{id:"developer-certificate-of-origin-dco"},"Developer Certificate of Origin (DCO)"),(0,i.kt)("p",null,"The ",(0,i.kt)("a",{parentName:"p",href:"https://wiki.linuxfoundation.org/dco"},"Developer Certificate of Origin")," (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the ",(0,i.kt)("a",{parentName:"p",href:"https://developercertificate.org/"},"full text of the DCO"),", reformatted for readability:"),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},"By making a contribution to this project, I certify that:"),(0,i.kt)("p",{parentName:"blockquote"},"(a) The contribution was created in whole or in part by me and I\nhave the right to submit it under the open source license\nindicated in the file; or"),(0,i.kt)("p",{parentName:"blockquote"},"(b) The contribution is based upon previous work that, to the best\nof my knowledge, is covered under an appropriate open source\nlicense and I have the right under that license to submit that\nwork with modifications, whether created in whole or in part\nby me, under the same open source license (unless I am\npermitted to submit under a different license), as indicated\nin the file; or"),(0,i.kt)("p",{parentName:"blockquote"},"(c) The contribution was provided directly to me by some other\nperson who certified (a), (b) or (c) and I have not modified\nit."),(0,i.kt)("p",{parentName:"blockquote"},"(d) I understand and agree that this project and the contribution\nare public and that a record of the contribution (including all\npersonal information I submit with it, including my sign-off) is\nmaintained indefinitely and may be redistributed consistent with\nthis project or the open source license(s) involved.")),(0,i.kt)("p",null,"Contributors ",(0,i.kt)("em",{parentName:"p"},"sign-off")," that they adhere to these requirements by adding a ",(0,i.kt)("inlineCode",{parentName:"p"},"Signed-off-by")," line to commit messages."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-text"},"This is my commit message\n\nSigned-off-by: Random J Developer <random@developer.example.org>\n")),(0,i.kt)("p",null,"Git even has a ",(0,i.kt)("inlineCode",{parentName:"p"},"-s")," command line option to append this automatically to your commit message:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"git commit -s -m 'This is my commit message'\n")),(0,i.kt)("p",null,"Pull requests that do not contain a valid ",(0,i.kt)("inlineCode",{parentName:"p"},"Signed-off-by")," line cannot be merged."),(0,i.kt)("h3",{id:"i-didnt-sign-my-commit-now-what"},"I didn't sign my commit, now what?"),(0,i.kt)("p",null,"No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"git switch <branch-name>\ngit commit --amend --no-edit --signoff\ngit push --force-with-lease <remote-name> <branch-name>\n")),(0,i.kt)("h2",{id:"code-of-conduct"},"Code of Conduct"),(0,i.kt)("p",null,"This project has adopted the ",(0,i.kt)("a",{parentName:"p",href:"/copacetic/website/v0.2.x/code-of-conduct"},"Contributor Covenant Code of Conduct"),"."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/website/assets/js/fcc13998.bf8a3793.js b/website/assets/js/fcc13998.bf8a3793.js new file mode 100644 index 00000000..7da6a18a --- /dev/null +++ b/website/assets/js/fcc13998.bf8a3793.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9077],{6080:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>d,contentTitle:()=>r,default:()=>h,frontMatter:()=>o,metadata:()=>c,toc:()=>a});var i=t(4848),s=t(8453);const o={title:"Contributing"},r=void 0,c={id:"contributing",title:"Contributing",description:"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.",source:"@site/versioned_docs/version-v0.2.x/contributing.md",sourceDirName:".",slug:"/contributing",permalink:"/copacetic/website/v0.2.x/contributing",draft:!1,unlisted:!1,tags:[],version:"v0.2.x",frontMatter:{title:"Contributing"},sidebar:"sidebar",previous:{title:"FAQ",permalink:"/copacetic/website/v0.2.x/faq"},next:{title:"Code of Conduct",permalink:"/copacetic/website/v0.2.x/code-of-conduct"}},d={},a=[{value:"Contributing Issues",id:"contributing-issues",level:2},{value:"Contributing Code",id:"contributing-code",level:2},{value:"Getting Started",id:"getting-started",level:3},{value:"Visual Studio Code Development Container",id:"visual-studio-code-development-container",level:3},{value:"Prerequisites",id:"prerequisites",level:4},{value:"Using the dev container",id:"using-the-dev-container",level:4},{value:"Personalizing user settings in a dev container",id:"personalizing-user-settings-in-a-dev-container",level:4},{value:"Tests",id:"tests",level:3},{value:"Pull Requests",id:"pull-requests",level:3},{value:"Developer Certificate of Origin (DCO)",id:"developer-certificate-of-origin-dco",level:2},{value:"I didn't sign my commit, now what?",id:"i-didnt-sign-my-commit-now-what",level:3},{value:"Code of Conduct",id:"code-of-conduct",level:2}];function l(e){const n={a:"a",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h3:"h3",h4:"h4",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsxs)(n.p,{children:["Welcome! We are very happy to accept community contributions to the project, whether through ",(0,i.jsx)(n.a,{href:"#contributing-issues",children:"filing issues"})," or ",(0,i.jsx)(n.a,{href:"#contributing-code",children:"code"})," in the form of ",(0,i.jsx)(n.a,{href:"#pull-requests",children:"Pull Requests"}),". Please note that by participating in this project, you agree to abide by the ",(0,i.jsx)(n.a,{href:"/copacetic/website/v0.2.x/code-of-conduct",children:"Code of Conduct"}),", as well as the terms of the ",(0,i.jsx)(n.a,{href:"#developer-certificate-of-origin-dco",children:"Developer Certificate of Origin"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"contributing-issues",children:"Contributing Issues"}),"\n",(0,i.jsxs)(n.p,{children:["Before opening any new issues, please search our ",(0,i.jsx)(n.a,{href:"https://github.com/project-copacetic/copacetic/issues",children:"existing GitHub issues"})," to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead."]}),"\n",(0,i.jsx)(n.p,{children:"When opening an issue, please select the most appropriate template for what you're contributing:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Bug Report:"})," If you would like to report the project or tool behaving in unexpected ways."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Documentation Improvement:"})," If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Request:"})," If you have a feature request, suggestion, or a even a design proposal to review."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Question:"})," If you would like to ask the maintainers a question about the project."]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"contributing-code",children:"Contributing Code"}),"\n",(0,i.jsx)(n.h3,{id:"getting-started",children:"Getting Started"}),"\n",(0,i.jsx)(n.p,{children:"Follow the instructions to either:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"/copacetic/website/v0.2.x/installation",children:"Setup your dev environment to build copa"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"#visual-studio-code-development-container",children:"Use the copa development container"})," in ",(0,i.jsx)(n.a,{href:"https://code.visualstudio.com/",children:"Visual Studio Code"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["For an overview of the project components, refer to the ",(0,i.jsx)(n.a,{href:"/copacetic/website/v0.2.x/design",children:"copa design"})," document."]}),"\n",(0,i.jsx)(n.h3,{id:"visual-studio-code-development-container",children:"Visual Studio Code Development Container"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"https://code.visualstudio.com/",children:"VSCode"})," supports development in a containerized environment through its ",(0,i.jsx)(n.a,{href:"https://code.visualstudio.com/docs/remote/containers",children:"Remote - Container extension"}),". This folder provides a development container which encapsulates the dependencies specified in the ",(0,i.jsx)(n.a,{href:"/copacetic/website/v0.2.x/installation",children:"instructions to build and run copa"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"https://docs.docker.com/get-docker/",children:"Docker"}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsxs)(n.p,{children:["For Windows users, enabling ",(0,i.jsx)(n.a,{href:"https://docs.docker.com/docker-for-windows/wsl/",children:"WSL2 back-end integration with Docker"})," is recommended."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://code.visualstudio.com/",children:"Visual Studio Code"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers",children:"Visual Studio Code Remote - Containers extension"})}),"\n"]}),"\n",(0,i.jsx)(n.h4,{id:"using-the-dev-container",children:"Using the dev container"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["After you have cloned this repo locally, open the repo folder in VSCode. VSCode will detect the presence of this ",(0,i.jsx)(n.code,{children:".devcontainer"})," subfolder and will prompt you to reopen the project in a container."]}),"\n",(0,i.jsxs)(n.p,{children:["Alternatively, you can open the command palette and use the ",(0,i.jsx)(n.code,{children:"Remote-Containers: Reopen in Container"})," command."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["Once the container is loaded, open an ",(0,i.jsx)(n.a,{href:"https://code.visualstudio.com/docs/editor/integrated-terminal",children:"integrated terminal"})," in VSCode and you can start running the demo instructions."]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"\u26a0 If running via Docker Desktop for Windows"})}),"\n",(0,i.jsxs)(n.p,{children:["Note that the ",(0,i.jsxs)(n.a,{href:"https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user",children:["mounted workspace files appear owned by ",(0,i.jsx)(n.code,{children:"root"})]})," in the dev container, which will cause ",(0,i.jsx)(n.code,{children:"git"})," commands to fail with a ",(0,i.jsx)(n.code,{children:"fatal: detected dubious ownership in a repository"})," error due to ",(0,i.jsx)(n.a,{href:"https://git-scm.com/docs/git-config/2.35.2#Documentation/git-config.txt-safedirectory",children:"safe.directory"})," checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the ",(0,i.jsx)(n.code,{children:"vscode"})," user:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"sudo chown -R vscode:vscode /workspace/copacetic\n"})}),"\n"]}),"\n",(0,i.jsx)(n.h4,{id:"personalizing-user-settings-in-a-dev-container",children:"Personalizing user settings in a dev container"}),"\n",(0,i.jsxs)(n.p,{children:["VSCode supports applying your user settings, such as your ",(0,i.jsx)(n.code,{children:".gitconfig"}),", to a dev container through the use of ",(0,i.jsx)(n.a,{href:"https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories",children:"dotfiles repositories"}),". This can be done through your own VSCode ",(0,i.jsx)(n.code,{children:"settings.json"})," file without changing the dev container image or configuration."]}),"\n",(0,i.jsx)(n.h3,{id:"tests",children:"Tests"}),"\n",(0,i.jsxs)(n.p,{children:["Once you can successfully ",(0,i.jsx)(n.code,{children:"make"})," the project, any code contributions should also successfully:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Pass unit tests via ",(0,i.jsx)(n.code,{children:"make test"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["Lint cleanly via ",(0,i.jsx)(n.code,{children:"make lint"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["Pull requests will also be expected to pass the PR functional tests specified by ",(0,i.jsx)(n.code,{children:".github/workflows/build.yml"}),"."]}),"\n",(0,i.jsx)(n.h3,{id:"pull-requests",children:"Pull Requests"}),"\n",(0,i.jsxs)(n.p,{children:["If you'd like to start contributing code to the project, you can search for ",(0,i.jsxs)(n.a,{href:"https://github.com/project-copacetic/copacetic/labels/good%20first%20issue",children:["issues with the ",(0,i.jsx)(n.code,{children:"good first issue"})," label"]}),". Other kinds of PR contributions we would look for include:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Fixes for bugs and other correctness issues."}),"\n",(0,i.jsx)(n.li,{children:"Docs and other content improvements (e.g. samples)."}),"\n",(0,i.jsx)(n.li,{children:"Extensions to support parsing new scanning report formats."}),"\n",(0,i.jsx)(n.li,{children:"Extensions to support patching images based on new distros or using new package managers."}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Architectural changes (e.g. breaking interfaces or violations of ",(0,i.jsx)(n.a,{href:"/copacetic/website/v0.2.x/design",children:"this project's design tenets"}),")."]}),"\n",(0,i.jsx)(n.li,{children:"Unsolicited features that significantly expand the functional scope of the tool."}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the ",(0,i.jsx)(n.a,{href:"https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format",children:"Angular commit message format"})," for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs."]}),"\n",(0,i.jsx)(n.p,{children:"We suggest:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Use the standard header format of ",(0,i.jsx)(n.code,{children:'"<type>: <short summary>"'})," where the ",(0,i.jsx)(n.code,{children:"<type>"})," is one of the following:","\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"build:"})," Changes that affect the build system or external dependencies"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"ci:"})," Changes to the GitHub workflows and configurations"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"docs:"})," Documentation only changes"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"feat:"})," A new feature"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"fix:"})," A bug fix"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"perf:"})," A code change that improves performance"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"refactor:"})," A code change that neither fixes a bug nor adds a feature"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"test:"})," Adding missing tests or correcting existing tests"]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["Use a ",(0,i.jsx)(n.a,{href:"https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html",children:"concise, imperative description"})," of the changes included in the ",(0,i.jsx)(n.code,{children:"<short summary>"})," of the header, the body of the PR, and generally in your commit messages."]}),"\n",(0,i.jsxs)(n.li,{children:["Use ",(0,i.jsx)(n.a,{href:"https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests",children:"GitHub keywords"})," in the footer of your PR description, such as ",(0,i.jsx)(n.code,{children:"closes"})," to automatically close issues the PR intends to address."]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"developer-certificate-of-origin-dco",children:"Developer Certificate of Origin (DCO)"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.a,{href:"https://wiki.linuxfoundation.org/dco",children:"Developer Certificate of Origin"})," (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the ",(0,i.jsx)(n.a,{href:"https://developercertificate.org/",children:"full text of the DCO"}),", reformatted for readability:"]}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsx)(n.p,{children:"By making a contribution to this project, I certify that:"}),"\n",(0,i.jsx)(n.p,{children:"(a) The contribution was created in whole or in part by me and I\nhave the right to submit it under the open source license\nindicated in the file; or"}),"\n",(0,i.jsx)(n.p,{children:"(b) The contribution is based upon previous work that, to the best\nof my knowledge, is covered under an appropriate open source\nlicense and I have the right under that license to submit that\nwork with modifications, whether created in whole or in part\nby me, under the same open source license (unless I am\npermitted to submit under a different license), as indicated\nin the file; or"}),"\n",(0,i.jsx)(n.p,{children:"(c) The contribution was provided directly to me by some other\nperson who certified (a), (b) or (c) and I have not modified\nit."}),"\n",(0,i.jsx)(n.p,{children:"(d) I understand and agree that this project and the contribution\nare public and that a record of the contribution (including all\npersonal information I submit with it, including my sign-off) is\nmaintained indefinitely and may be redistributed consistent with\nthis project or the open source license(s) involved."}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["Contributors ",(0,i.jsx)(n.em,{children:"sign-off"})," that they adhere to these requirements by adding a ",(0,i.jsx)(n.code,{children:"Signed-off-by"})," line to commit messages."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-text",children:"This is my commit message\n\nSigned-off-by: Random J Developer <random@developer.example.org>\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Git even has a ",(0,i.jsx)(n.code,{children:"-s"})," command line option to append this automatically to your commit message:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"git commit -s -m 'This is my commit message'\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Pull requests that do not contain a valid ",(0,i.jsx)(n.code,{children:"Signed-off-by"})," line cannot be merged."]}),"\n",(0,i.jsx)(n.h3,{id:"i-didnt-sign-my-commit-now-what",children:"I didn't sign my commit, now what?"}),"\n",(0,i.jsx)(n.p,{children:"No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"git switch <branch-name>\ngit commit --amend --no-edit --signoff\ngit push --force-with-lease <remote-name> <branch-name>\n"})}),"\n",(0,i.jsx)(n.h2,{id:"code-of-conduct",children:"Code of Conduct"}),"\n",(0,i.jsxs)(n.p,{children:["This project has adopted the ",(0,i.jsx)(n.a,{href:"/copacetic/website/v0.2.x/code-of-conduct",children:"Contributor Covenant Code of Conduct"}),"."]})]})}function h(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(l,{...e})}):l(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>r,x:()=>c});var i=t(6540);const s={},o=i.createContext(s);function r(e){const n=i.useContext(o);return i.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),i.createElement(o.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/website/assets/js/ff86e818.6052e0ec.js b/website/assets/js/ff86e818.6052e0ec.js deleted file mode 100644 index 18298d97..00000000 --- a/website/assets/js/ff86e818.6052e0ec.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[601],{1822:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"v0.6.x","label":"v0.6.x","banner":null,"badge":true,"noIndex":false,"className":"docs-version-v0.6.x","isLast":true,"docsSidebars":{"sidebar":[{"type":"category","label":"Getting Started","collapsed":false,"items":[{"type":"link","label":"Introduction","href":"/copacetic/website/","docId":"introduction"},{"type":"link","label":"Installation","href":"/copacetic/website/installation","docId":"installation"},{"type":"link","label":"Quick Start","href":"/copacetic/website/quick-start","docId":"quick-start"},{"type":"link","label":"Tagging Guidelines","href":"/copacetic/website/best-practices","docId":"best-practices"},{"type":"link","label":"Troubleshooting","href":"/copacetic/website/troubleshooting","docId":"troubleshooting"},{"type":"link","label":"FAQ","href":"/copacetic/website/faq","docId":"faq"}],"collapsible":true},{"type":"category","label":"Features","collapsed":false,"items":[{"type":"link","label":"Github Action","href":"/copacetic/website/github-action","docId":"github-action"},{"type":"link","label":"Custom buildkit addresses","href":"/copacetic/website/custom-address","docId":"custom-address"},{"type":"link","label":"Output","href":"/copacetic/website/output","docId":"output"},{"type":"link","label":"Scanner Plugins","href":"/copacetic/website/scanner-plugins","docId":"scanner-plugins"}],"collapsible":true},{"type":"category","label":"Contributing","collapsed":false,"items":[{"type":"link","label":"Contributing","href":"/copacetic/website/contributing","docId":"contributing"},{"type":"link","label":"Code of Conduct","href":"/copacetic/website/code-of-conduct","docId":"code-of-conduct"},{"type":"link","label":"Design","href":"/copacetic/website/design","docId":"design"},{"type":"link","label":"Development and Testing Tips","href":"/copacetic/website/development-tips","docId":"development-tips"},{"type":"link","label":"Maintainer Guidelines","href":"/copacetic/website/maintainer-guidelines","docId":"maintainer-guidelines"},{"type":"link","label":"Release Process","href":"/copacetic/website/release","docId":"release"}],"collapsible":true}]},"docs":{"best-practices":{"id":"best-practices","title":"Tagging Guidelines","description":"There are some patterns and practices you may want to consider when using Copa to patch images. Remember that these are suggestions that may not fit into your workflow, but we think that staying as close as possible to these practices offers the best experience with Copa.","sidebar":"sidebar"},"code-of-conduct":{"id":"code-of-conduct","title":"Code of Conduct","description":"Our Pledge","sidebar":"sidebar"},"contributing":{"id":"contributing","title":"Contributing","description":"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.","sidebar":"sidebar"},"custom-address":{"id":"custom-address","title":"Custom buildkit addresses","description":"If you need to specify a custom address using the --addr flag. Here are the supported formats:","sidebar":"sidebar"},"design":{"id":"design","title":"Design","description":"Design Tenets","sidebar":"sidebar"},"development-tips":{"id":"development-tips","title":"Development and Testing Tips","description":"This document provides some tips and tricks for devs to better understand what is happening under the hood of copa.","sidebar":"sidebar"},"faq":{"id":"faq","title":"FAQ","description":"What kind of vulnerabilities can Copa patch?","sidebar":"sidebar"},"github-action":{"id":"github-action","title":"Github Action","description":"The Copa Github Action allows you patch vulnerable containers in your GitHub Actions workflows using Copa.","sidebar":"sidebar"},"installation":{"id":"installation","title":"Installation","description":"Homebrew","sidebar":"sidebar"},"introduction":{"id":"introduction","title":"Introduction","description":"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.","sidebar":"sidebar"},"maintainer-guidelines":{"id":"maintainer-guidelines","title":"Maintainer Guidelines","description":"Semantic Release Management","sidebar":"sidebar"},"output":{"id":"output","title":"Output","description":"Experimental: This feature might change without preserving backwards compatibility.","sidebar":"sidebar"},"quick-start":{"id":"quick-start","title":"Quick Start","description":"This sample illustrates how to patch containers using vulnerability reports with copa.","sidebar":"sidebar"},"release":{"id":"release","title":"Release Process","description":"Overview","sidebar":"sidebar"},"scanner-plugins":{"id":"scanner-plugins","title":"Scanner Plugins","description":"By default, copa uses Trivy to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners.","sidebar":"sidebar"},"troubleshooting":{"id":"troubleshooting","title":"Troubleshooting","description":"Filtering Vulnerabilities","sidebar":"sidebar"}}}')}}]); \ No newline at end of file diff --git a/website/assets/js/ff86e818.93b13f6e.js b/website/assets/js/ff86e818.93b13f6e.js new file mode 100644 index 00000000..d129b355 --- /dev/null +++ b/website/assets/js/ff86e818.93b13f6e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8293],{8441:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"v0.6.x","label":"v0.6.x","banner":null,"badge":true,"noIndex":false,"className":"docs-version-v0.6.x","isLast":true,"docsSidebars":{"sidebar":[{"type":"category","label":"Getting Started","collapsed":false,"items":[{"type":"link","label":"Introduction","href":"/copacetic/website/","docId":"introduction","unlisted":false},{"type":"link","label":"Installation","href":"/copacetic/website/installation","docId":"installation","unlisted":false},{"type":"link","label":"Quick Start","href":"/copacetic/website/quick-start","docId":"quick-start","unlisted":false},{"type":"link","label":"Tagging Guidelines","href":"/copacetic/website/best-practices","docId":"best-practices","unlisted":false},{"type":"link","label":"Troubleshooting","href":"/copacetic/website/troubleshooting","docId":"troubleshooting","unlisted":false},{"type":"link","label":"FAQ","href":"/copacetic/website/faq","docId":"faq","unlisted":false}],"collapsible":true},{"type":"category","label":"Features","collapsed":false,"items":[{"type":"link","label":"Github Action","href":"/copacetic/website/github-action","docId":"github-action","unlisted":false},{"type":"link","label":"Custom buildkit addresses","href":"/copacetic/website/custom-address","docId":"custom-address","unlisted":false},{"type":"link","label":"Output","href":"/copacetic/website/output","docId":"output","unlisted":false},{"type":"link","label":"Scanner Plugins","href":"/copacetic/website/scanner-plugins","docId":"scanner-plugins","unlisted":false}],"collapsible":true},{"type":"category","label":"Contributing","collapsed":false,"items":[{"type":"link","label":"Contributing","href":"/copacetic/website/contributing","docId":"contributing","unlisted":false},{"type":"link","label":"Code of Conduct","href":"/copacetic/website/code-of-conduct","docId":"code-of-conduct","unlisted":false},{"type":"link","label":"Design","href":"/copacetic/website/design","docId":"design","unlisted":false},{"type":"link","label":"Development and Testing Tips","href":"/copacetic/website/development-tips","docId":"development-tips","unlisted":false},{"type":"link","label":"Maintainer Guidelines","href":"/copacetic/website/maintainer-guidelines","docId":"maintainer-guidelines","unlisted":false},{"type":"link","label":"Release Process","href":"/copacetic/website/release","docId":"release","unlisted":false}],"collapsible":true}]},"docs":{"best-practices":{"id":"best-practices","title":"Tagging Guidelines","description":"There are some patterns and practices you may want to consider when using Copa to patch images. Remember that these are suggestions that may not fit into your workflow, but we think that staying as close as possible to these practices offers the best experience with Copa.","sidebar":"sidebar"},"code-of-conduct":{"id":"code-of-conduct","title":"Code of Conduct","description":"Our Pledge","sidebar":"sidebar"},"contributing":{"id":"contributing","title":"Contributing","description":"Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.","sidebar":"sidebar"},"custom-address":{"id":"custom-address","title":"Custom buildkit addresses","description":"If you need to specify a custom address using the --addr flag. Here are the supported formats:","sidebar":"sidebar"},"design":{"id":"design","title":"Design","description":"Design Tenets","sidebar":"sidebar"},"development-tips":{"id":"development-tips","title":"Development and Testing Tips","description":"This document provides some tips and tricks for devs to better understand what is happening under the hood of copa.","sidebar":"sidebar"},"faq":{"id":"faq","title":"FAQ","description":"What kind of vulnerabilities can Copa patch?","sidebar":"sidebar"},"github-action":{"id":"github-action","title":"Github Action","description":"The Copa Github Action allows you patch vulnerable containers in your GitHub Actions workflows using Copa.","sidebar":"sidebar"},"installation":{"id":"installation","title":"Installation","description":"Homebrew","sidebar":"sidebar"},"introduction":{"id":"introduction","title":"Introduction","description":"copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.","sidebar":"sidebar"},"maintainer-guidelines":{"id":"maintainer-guidelines","title":"Maintainer Guidelines","description":"Semantic Release Management","sidebar":"sidebar"},"output":{"id":"output","title":"Output","description":"Experimental: This feature might change without preserving backwards compatibility.","sidebar":"sidebar"},"quick-start":{"id":"quick-start","title":"Quick Start","description":"This sample illustrates how to patch containers using vulnerability reports with copa.","sidebar":"sidebar"},"release":{"id":"release","title":"Release Process","description":"Overview","sidebar":"sidebar"},"scanner-plugins":{"id":"scanner-plugins","title":"Scanner Plugins","description":"By default, copa uses Trivy to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners.","sidebar":"sidebar"},"troubleshooting":{"id":"troubleshooting","title":"Troubleshooting","description":"Filtering Vulnerabilities","sidebar":"sidebar"}}}')}}]); \ No newline at end of file diff --git a/website/assets/js/main.991a83b1.js b/website/assets/js/main.991a83b1.js deleted file mode 100644 index b0f3ddf4..00000000 --- a/website/assets/js/main.991a83b1.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! For license information please see main.991a83b1.js.LICENSE.txt */ -(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[179],{4334:(e,t,n)=>{"use strict";function r(e){var t,n,a="";if("string"==typeof e||"number"==typeof e)a+=e;else if("object"==typeof e)if(Array.isArray(e))for(t=0;t<e.length;t++)e[t]&&(n=r(e[t]))&&(a&&(a+=" "),a+=n);else for(t in e)e[t]&&(a&&(a+=" "),a+=t);return a}n.d(t,{Z:()=>a});const a=function(){for(var e,t,n=0,a="";n<arguments.length;)(e=arguments[n++])&&(t=r(e))&&(a&&(a+=" "),a+=t);return a}},1205:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=function(){var e=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,n={},r={util:{encode:function e(t){return t instanceof a?new a(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/</g,"<").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).slice(8,-1)},objId:function(e){return e.__id||Object.defineProperty(e,"__id",{value:++t}),e.__id},clone:function e(t,n){var a,o;switch(n=n||{},r.util.type(t)){case"Object":if(o=r.util.objId(t),n[o])return n[o];for(var i in a={},n[o]=a,t)t.hasOwnProperty(i)&&(a[i]=e(t[i],n));return a;case"Array":return o=r.util.objId(t),n[o]?n[o]:(a=[],n[o]=a,t.forEach((function(t,r){a[r]=e(t,n)})),a);default:return t}},getLanguage:function(t){for(;t;){var n=e.exec(t.className);if(n)return n[1].toLowerCase();t=t.parentElement}return"none"},setLanguage:function(t,n){t.className=t.className.replace(RegExp(e,"gi"),""),t.classList.add("language-"+n)},isActive:function(e,t,n){for(var r="no-"+t;e;){var a=e.classList;if(a.contains(t))return!0;if(a.contains(r))return!1;e=e.parentElement}return!!n}},languages:{plain:n,plaintext:n,text:n,txt:n,extend:function(e,t){var n=r.util.clone(r.languages[e]);for(var a in t)n[a]=t[a];return n},insertBefore:function(e,t,n,a){var o=(a=a||r.languages)[e],i={};for(var l in o)if(o.hasOwnProperty(l)){if(l==t)for(var s in n)n.hasOwnProperty(s)&&(i[s]=n[s]);n.hasOwnProperty(l)||(i[l]=o[l])}var c=a[e];return a[e]=i,r.languages.DFS(r.languages,(function(t,n){n===c&&t!=e&&(this[t]=i)})),i},DFS:function e(t,n,a,o){o=o||{};var i=r.util.objId;for(var l in t)if(t.hasOwnProperty(l)){n.call(t,l,t[l],a||l);var s=t[l],c=r.util.type(s);"Object"!==c||o[i(s)]?"Array"!==c||o[i(s)]||(o[i(s)]=!0,e(s,n,l,o)):(o[i(s)]=!0,e(s,n,null,o))}}},plugins:{},highlight:function(e,t,n){var o={code:e,grammar:t,language:n};return r.hooks.run("before-tokenize",o),o.tokens=r.tokenize(o.code,o.grammar),r.hooks.run("after-tokenize",o),a.stringify(r.util.encode(o.tokens),o.language)},tokenize:function(e,t){var n=t.rest;if(n){for(var r in n)t[r]=n[r];delete t.rest}var a=new l;return s(a,a.head,e),i(e,a,t,a.head,0),function(e){var t=[],n=e.head.next;for(;n!==e.tail;)t.push(n.value),n=n.next;return t}(a)},hooks:{all:{},add:function(e,t){var n=r.hooks.all;n[e]=n[e]||[],n[e].push(t)},run:function(e,t){var n=r.hooks.all[e];if(n&&n.length)for(var a,o=0;a=n[o++];)a(t)}},Token:a};function a(e,t,n,r){this.type=e,this.content=t,this.alias=n,this.length=0|(r||"").length}function o(e,t,n,r){e.lastIndex=t;var a=e.exec(n);if(a&&r&&a[1]){var o=a[1].length;a.index+=o,a[0]=a[0].slice(o)}return a}function i(e,t,n,l,u,d){for(var p in n)if(n.hasOwnProperty(p)&&n[p]){var f=n[p];f=Array.isArray(f)?f:[f];for(var m=0;m<f.length;++m){if(d&&d.cause==p+","+m)return;var h=f[m],g=h.inside,b=!!h.lookbehind,v=!!h.greedy,y=h.alias;if(v&&!h.pattern.global){var w=h.pattern.toString().match(/[imsuy]*$/)[0];h.pattern=RegExp(h.pattern.source,w+"g")}for(var k=h.pattern||h,E=l.next,x=u;E!==t.tail&&!(d&&x>=d.reach);x+=E.value.length,E=E.next){var S=E.value;if(t.length>e.length)return;if(!(S instanceof a)){var _,C=1;if(v){if(!(_=o(k,x,e,b))||_.index>=e.length)break;var T=_.index,A=_.index+_[0].length,L=x;for(L+=E.value.length;T>=L;)L+=(E=E.next).value.length;if(x=L-=E.value.length,E.value instanceof a)continue;for(var R=E;R!==t.tail&&(L<A||"string"==typeof R.value);R=R.next)C++,L+=R.value.length;C--,S=e.slice(x,L),_.index-=x}else if(!(_=o(k,0,S,b)))continue;T=_.index;var N=_[0],O=S.slice(0,T),P=S.slice(T+N.length),I=x+S.length;d&&I>d.reach&&(d.reach=I);var D=E.prev;if(O&&(D=s(t,D,O),x+=O.length),c(t,D,C),E=s(t,D,new a(p,g?r.tokenize(N,g):N,y,N)),P&&s(t,E,P),C>1){var M={cause:p+","+m,reach:I};i(e,t,n,E.prev,x,M),d&&M.reach>d.reach&&(d.reach=M.reach)}}}}}}function l(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function s(e,t,n){var r=t.next,a={value:n,prev:t,next:r};return t.next=a,r.prev=a,e.length++,a}function c(e,t,n){for(var r=t.next,a=0;a<n&&r!==e.tail;a++)r=r.next;t.next=r,r.prev=t,e.length-=a}return a.stringify=function e(t,n){if("string"==typeof t)return t;if(Array.isArray(t)){var a="";return t.forEach((function(t){a+=e(t,n)})),a}var o={type:t.type,content:e(t.content,n),tag:"span",classes:["token",t.type],attributes:{},language:n},i=t.alias;i&&(Array.isArray(i)?Array.prototype.push.apply(o.classes,i):o.classes.push(i)),r.hooks.run("wrap",o);var l="";for(var s in o.attributes)l+=" "+s+'="'+(o.attributes[s]||"").replace(/"/g,""")+'"';return"<"+o.tag+' class="'+o.classes.join(" ")+'"'+l+">"+o.content+"</"+o.tag+">"},r}(),a=r;r.default=r,a.languages.markup={comment:{pattern:/<!--(?:(?!<!--)[\s\S])*?-->/,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/<!DOCTYPE(?:[^>"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|<!--(?:[^-]|-(?!->))*-->)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^<!|>$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},a.languages.markup.tag.inside["attr-value"].inside.entity=a.languages.markup.entity,a.languages.markup.doctype.inside["internal-subset"].inside=a.languages.markup,a.hooks.add("wrap",(function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))})),Object.defineProperty(a.languages.markup.tag,"addInlined",{value:function(e,t){var n={};n["language-"+t]={pattern:/(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i,lookbehind:!0,inside:a.languages[t]},n.cdata=/^<!\[CDATA\[|\]\]>$/i;var r={"included-cdata":{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,inside:n}};r["language-"+t]={pattern:/[\s\S]+/,inside:a.languages[t]};var o={};o[e]={pattern:RegExp(/(<__[^>]*>)(?:<!\[CDATA\[(?:[^\]]|\](?!\]>))*\]\]>|(?!<!\[CDATA\[)[\s\S])*?(?=<\/__>)/.source.replace(/__/g,(function(){return e})),"i"),lookbehind:!0,greedy:!0,inside:r},a.languages.insertBefore("markup","cdata",o)}}),Object.defineProperty(a.languages.markup.tag,"addAttribute",{value:function(e,t){a.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[t,"language-"+t],inside:a.languages[t]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),a.languages.html=a.languages.markup,a.languages.mathml=a.languages.markup,a.languages.svg=a.languages.markup,a.languages.xml=a.languages.extend("markup",{}),a.languages.ssml=a.languages.xml,a.languages.atom=a.languages.xml,a.languages.rss=a.languages.xml,function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",n={pattern:/(^(["']?)\w+\2)[ \t]+\S.*/,lookbehind:!0,alias:"punctuation",inside:null},r={bash:n,environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b[\w-]+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:r},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0,inside:{bash:n}},{pattern:/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,lookbehind:!0,greedy:!0,inside:r},{pattern:/(^|[^$\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\$'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,inside:{entity:r.entity}}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:r.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}},n.inside=e.languages.bash;for(var a=["comment","function-name","for-or-select","assign-left","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],o=r.variable[1].inside,i=0;i<a.length;i++)o[a[i]]=e.languages.bash[a[i]];e.languages.shell=e.languages.bash}(a),a.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},a.languages.c=a.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/,lookbehind:!0},keyword:/\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/,function:/\b[a-z_]\w*(?=\s*\()/i,number:/(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/}),a.languages.insertBefore("c","string",{char:{pattern:/'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/,greedy:!0}}),a.languages.insertBefore("c","string",{macro:{pattern:/(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},a.languages.c.string],char:a.languages.c.char,comment:a.languages.c.comment,"macro-name":[{pattern:/(^#\s*define\s+)\w+\b(?!\()/i,lookbehind:!0},{pattern:/(^#\s*define\s+)\w+\b(?=\()/i,lookbehind:!0,alias:"function"}],directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:a.languages.c}}}}),a.languages.insertBefore("c","function",{constant:/\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/}),delete a.languages.c.boolean,function(e){var t=/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,n=/\b(?!<keyword>)\w+(?:\s*\.\s*\w+)*\b/.source.replace(/<keyword>/g,(function(){return t.source}));e.languages.cpp=e.languages.extend("c",{"class-name":[{pattern:RegExp(/(\b(?:class|concept|enum|struct|typename)\s+)(?!<keyword>)\w+/.source.replace(/<keyword>/g,(function(){return t.source}))),lookbehind:!0},/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,/\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/],keyword:t,number:{pattern:/(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,greedy:!0},operator:/>>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,boolean:/\b(?:false|true)\b/}),e.languages.insertBefore("cpp","string",{module:{pattern:RegExp(/(\b(?:import|module)\s+)/.source+"(?:"+/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source+"|"+/<mod-name>(?:\s*:\s*<mod-name>)?|:\s*<mod-name>/.source.replace(/<mod-name>/g,(function(){return n}))+")"),lookbehind:!0,greedy:!0,inside:{string:/^[<"][\s\S]+/,operator:/:/,punctuation:/\./}},"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}}),e.languages.insertBefore("cpp","keyword",{"generic-function":{pattern:/\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i,inside:{function:/^\w+/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:e.languages.cpp}}}}),e.languages.insertBefore("cpp","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}}),e.languages.insertBefore("cpp","class-name",{"base-clause":{pattern:/(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:e.languages.extend("cpp",{})}}),e.languages.insertBefore("inside","double-colon",{"class-name":/\b[a-z_]\w*\b(?!\s*::)/i},e.languages.cpp["base-clause"])}(a),function(e){var t=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-](?:[^;{\s]|\s+(?![\s{]))*(?:;|(?=\s*\{))/,inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+t.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+t.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+t.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:t,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),n.tag.addAttribute("style","css"))}(a),function(e){var t,n=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;e.languages.css.selector={pattern:e.languages.css.selector.pattern,lookbehind:!0,inside:t={"pseudo-element":/:(?:after|before|first-letter|first-line|selection)|::[-\w]+/,"pseudo-class":/:[-\w]+/,class:/\.[-\w]+/,id:/#[-\w]+/,attribute:{pattern:RegExp("\\[(?:[^[\\]\"']|"+n.source+")*\\]"),greedy:!0,inside:{punctuation:/^\[|\]$/,"case-sensitivity":{pattern:/(\s)[si]$/i,lookbehind:!0,alias:"keyword"},namespace:{pattern:/^(\s*)(?:(?!\s)[-*\w\xA0-\uFFFF])*\|(?!=)/,lookbehind:!0,inside:{punctuation:/\|$/}},"attr-name":{pattern:/^(\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+/,lookbehind:!0},"attr-value":[n,{pattern:/(=\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+(?=\s*$)/,lookbehind:!0}],operator:/[|~*^$]?=/}},"n-th":[{pattern:/(\(\s*)[+-]?\d*[\dn](?:\s*[+-]\s*\d+)?(?=\s*\))/,lookbehind:!0,inside:{number:/[\dn]+/,operator:/[+-]/}},{pattern:/(\(\s*)(?:even|odd)(?=\s*\))/i,lookbehind:!0}],combinator:/>|\+|~|\|\|/,punctuation:/[(),]/}},e.languages.css.atrule.inside["selector-function-argument"].inside=t,e.languages.insertBefore("css","property",{variable:{pattern:/(^|[^-\w\xA0-\uFFFF])--(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*/i,lookbehind:!0}});var r={pattern:/(\b\d+)(?:%|[a-z]+(?![\w-]))/,lookbehind:!0},a={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0};e.languages.insertBefore("css","function",{operator:{pattern:/(\s)[+\-*\/](?=\s)/,lookbehind:!0},hexcode:{pattern:/\B#[\da-f]{3,8}\b/i,alias:"color"},color:[{pattern:/(^|[^\w-])(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)(?![\w-])/i,lookbehind:!0},{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:r,number:a,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:r,number:a})}(a),a.languages.javascript=a.languages.extend("clike",{"class-name":[a.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),a.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,a.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:a.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:a.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:a.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:a.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:a.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),a.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:a.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),a.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),a.languages.markup&&(a.languages.markup.tag.addInlined("script","javascript"),a.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),a.languages.js=a.languages.javascript,function(e){var t=/#(?!\{).+/,n={pattern:/#\{[^}]+\}/,alias:"variable"};e.languages.coffeescript=e.languages.extend("javascript",{comment:t,string:[{pattern:/'(?:\\[\s\S]|[^\\'])*'/,greedy:!0},{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0,inside:{interpolation:n}}],keyword:/\b(?:and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/,"class-member":{pattern:/@(?!\d)\w+/,alias:"variable"}}),e.languages.insertBefore("coffeescript","comment",{"multiline-comment":{pattern:/###[\s\S]+?###/,alias:"comment"},"block-regex":{pattern:/\/{3}[\s\S]*?\/{3}/,alias:"regex",inside:{comment:t,interpolation:n}}}),e.languages.insertBefore("coffeescript","string",{"inline-javascript":{pattern:/`(?:\\[\s\S]|[^\\`])*`/,inside:{delimiter:{pattern:/^`|`$/,alias:"punctuation"},script:{pattern:/[\s\S]+/,alias:"language-javascript",inside:e.languages.javascript}}},"multiline-string":[{pattern:/'''[\s\S]*?'''/,greedy:!0,alias:"string"},{pattern:/"""[\s\S]*?"""/,greedy:!0,alias:"string",inside:{interpolation:n}}]}),e.languages.insertBefore("coffeescript","keyword",{property:/(?!\d)\w+(?=\s*:(?!:))/}),delete e.languages.coffeescript["template-string"],e.languages.coffee=e.languages.coffeescript}(a),function(e){var t=/[*&][^\s[\]{},]+/,n=/!(?:<[\w\-%#;/?:@&=+$,.!~*'()[\]]+>|(?:[a-zA-Z\d-]*!)?[\w\-%#;/?:@&=+$.~*'()]+)?/,r="(?:"+n.source+"(?:[ \t]+"+t.source+")?|"+t.source+"(?:[ \t]+"+n.source+")?)",a=/(?:[^\s\x00-\x08\x0e-\x1f!"#%&'*,\-:>?@[\]`{|}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]|[?:-]<PLAIN>)(?:[ \t]*(?:(?![#:])<PLAIN>|:<PLAIN>))*/.source.replace(/<PLAIN>/g,(function(){return/[^\s\x00-\x08\x0e-\x1f,[\]{}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]/.source})),o=/"(?:[^"\\\r\n]|\\.)*"|'(?:[^'\\\r\n]|\\.)*'/.source;function i(e,t){t=(t||"").replace(/m/g,"")+"m";var n=/([:\-,[{]\s*(?:\s<<prop>>[ \t]+)?)(?:<<value>>)(?=[ \t]*(?:$|,|\]|\}|(?:[\r\n]\s*)?#))/.source.replace(/<<prop>>/g,(function(){return r})).replace(/<<value>>/g,(function(){return e}));return RegExp(n,t)}e.languages.yaml={scalar:{pattern:RegExp(/([\-:]\s*(?:\s<<prop>>[ \t]+)?[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)\S[^\r\n]*(?:\2[^\r\n]+)*)/.source.replace(/<<prop>>/g,(function(){return r}))),lookbehind:!0,alias:"string"},comment:/#.*/,key:{pattern:RegExp(/((?:^|[:\-,[{\r\n?])[ \t]*(?:<<prop>>[ \t]+)?)<<key>>(?=\s*:\s)/.source.replace(/<<prop>>/g,(function(){return r})).replace(/<<key>>/g,(function(){return"(?:"+a+"|"+o+")"}))),lookbehind:!0,greedy:!0,alias:"atrule"},directive:{pattern:/(^[ \t]*)%.+/m,lookbehind:!0,alias:"important"},datetime:{pattern:i(/\d{4}-\d\d?-\d\d?(?:[tT]|[ \t]+)\d\d?:\d{2}:\d{2}(?:\.\d*)?(?:[ \t]*(?:Z|[-+]\d\d?(?::\d{2})?))?|\d{4}-\d{2}-\d{2}|\d\d?:\d{2}(?::\d{2}(?:\.\d*)?)?/.source),lookbehind:!0,alias:"number"},boolean:{pattern:i(/false|true/.source,"i"),lookbehind:!0,alias:"important"},null:{pattern:i(/null|~/.source,"i"),lookbehind:!0,alias:"important"},string:{pattern:i(o),lookbehind:!0,greedy:!0},number:{pattern:i(/[+-]?(?:0x[\da-f]+|0o[0-7]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?|\.inf|\.nan)/.source,"i"),lookbehind:!0},tag:n,important:t,punctuation:/---|[:[\]{}\-,|>?]|\.\.\./},e.languages.yml=e.languages.yaml}(a),function(e){var t=/(?:\\.|[^\\\n\r]|(?:\n|\r\n?)(?![\r\n]))/.source;function n(e){return e=e.replace(/<inner>/g,(function(){return t})),RegExp(/((?:^|[^\\])(?:\\{2})*)/.source+"(?:"+e+")")}var r=/(?:\\.|``(?:[^`\r\n]|`(?!`))+``|`[^`\r\n]+`|[^\\|\r\n`])+/.source,a=/\|?__(?:\|__)+\|?(?:(?:\n|\r\n?)|(?![\s\S]))/.source.replace(/__/g,(function(){return r})),o=/\|?[ \t]*:?-{3,}:?[ \t]*(?:\|[ \t]*:?-{3,}:?[ \t]*)+\|?(?:\n|\r\n?)/.source;e.languages.markdown=e.languages.extend("markup",{}),e.languages.insertBefore("markdown","prolog",{"front-matter-block":{pattern:/(^(?:\s*[\r\n])?)---(?!.)[\s\S]*?[\r\n]---(?!.)/,lookbehind:!0,greedy:!0,inside:{punctuation:/^---|---$/,"front-matter":{pattern:/\S+(?:\s+\S+)*/,alias:["yaml","language-yaml"],inside:e.languages.yaml}}},blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},table:{pattern:RegExp("^"+a+o+"(?:"+a+")*","m"),inside:{"table-data-rows":{pattern:RegExp("^("+a+o+")(?:"+a+")*$"),lookbehind:!0,inside:{"table-data":{pattern:RegExp(r),inside:e.languages.markdown},punctuation:/\|/}},"table-line":{pattern:RegExp("^("+a+")"+o+"$"),lookbehind:!0,inside:{punctuation:/\||:?-{3,}:?/}},"table-header-row":{pattern:RegExp("^"+a+"$"),inside:{"table-header":{pattern:RegExp(r),alias:"important",inside:e.languages.markdown},punctuation:/\|/}}}},code:[{pattern:/((?:^|\n)[ \t]*\n|(?:^|\r\n?)[ \t]*\r\n?)(?: {4}|\t).+(?:(?:\n|\r\n?)(?: {4}|\t).+)*/,lookbehind:!0,alias:"keyword"},{pattern:/^```[\s\S]*?^```$/m,greedy:!0,inside:{"code-block":{pattern:/^(```.*(?:\n|\r\n?))[\s\S]+?(?=(?:\n|\r\n?)^```$)/m,lookbehind:!0},"code-language":{pattern:/^(```).+/,lookbehind:!0},punctuation:/```/}}],title:[{pattern:/\S.*(?:\n|\r\n?)(?:==+|--+)(?=[ \t]*$)/m,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:n(/\b__(?:(?!_)<inner>|_(?:(?!_)<inner>)+_)+__\b|\*\*(?:(?!\*)<inner>|\*(?:(?!\*)<inner>)+\*)+\*\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^..)[\s\S]+(?=..$)/,lookbehind:!0,inside:{}},punctuation:/\*\*|__/}},italic:{pattern:n(/\b_(?:(?!_)<inner>|__(?:(?!_)<inner>)+__)+_\b|\*(?:(?!\*)<inner>|\*\*(?:(?!\*)<inner>)+\*\*)+\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^.)[\s\S]+(?=.$)/,lookbehind:!0,inside:{}},punctuation:/[*_]/}},strike:{pattern:n(/(~~?)(?:(?!~)<inner>)+\2/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^~~?)[\s\S]+(?=\1$)/,lookbehind:!0,inside:{}},punctuation:/~~?/}},"code-snippet":{pattern:/(^|[^\\`])(?:``[^`\r\n]+(?:`[^`\r\n]+)*``(?!`)|`[^`\r\n]+`(?!`))/,lookbehind:!0,greedy:!0,alias:["code","keyword"]},url:{pattern:n(/!?\[(?:(?!\])<inner>)+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)|[ \t]?\[(?:(?!\])<inner>)+\])/.source),lookbehind:!0,greedy:!0,inside:{operator:/^!/,content:{pattern:/(^\[)[^\]]+(?=\])/,lookbehind:!0,inside:{}},variable:{pattern:/(^\][ \t]?\[)[^\]]+(?=\]$)/,lookbehind:!0},url:{pattern:/(^\]\()[^\s)]+/,lookbehind:!0},string:{pattern:/(^[ \t]+)"(?:\\.|[^"\\])*"(?=\)$)/,lookbehind:!0}}}}),["url","bold","italic","strike"].forEach((function(t){["url","bold","italic","strike","code-snippet"].forEach((function(n){t!==n&&(e.languages.markdown[t].inside.content.inside[n]=e.languages.markdown[n])}))})),e.hooks.add("after-tokenize",(function(e){"markdown"!==e.language&&"md"!==e.language||function e(t){if(t&&"string"!=typeof t)for(var n=0,r=t.length;n<r;n++){var a=t[n];if("code"===a.type){var o=a.content[1],i=a.content[3];if(o&&i&&"code-language"===o.type&&"code-block"===i.type&&"string"==typeof o.content){var l=o.content.replace(/\b#/g,"sharp").replace(/\b\+\+/g,"pp"),s="language-"+(l=(/[a-z][\w-]*/i.exec(l)||[""])[0].toLowerCase());i.alias?"string"==typeof i.alias?i.alias=[i.alias,s]:i.alias.push(s):i.alias=[s]}}else e(a.content)}}(e.tokens)})),e.hooks.add("wrap",(function(t){if("code-block"===t.type){for(var n="",r=0,a=t.classes.length;r<a;r++){var o=t.classes[r],c=/language-(.+)/.exec(o);if(c){n=c[1];break}}var u,d=e.languages[n];if(d)t.content=e.highlight((u=t.content,u.replace(i,"").replace(/&(\w{1,8}|#x?[\da-f]{1,8});/gi,(function(e,t){var n;if("#"===(t=t.toLowerCase())[0])return n="x"===t[1]?parseInt(t.slice(2),16):Number(t.slice(1)),s(n);var r=l[t];return r||e}))),d,n);else if(n&&"none"!==n&&e.plugins.autoloader){var p="md-"+(new Date).valueOf()+"-"+Math.floor(1e16*Math.random());t.attributes.id=p,e.plugins.autoloader.loadLanguages(n,(function(){var t=document.getElementById(p);t&&(t.innerHTML=e.highlight(t.textContent,e.languages[n],n))}))}}}));var i=RegExp(e.languages.markup.tag.pattern.source,"gi"),l={amp:"&",lt:"<",gt:">",quot:'"'},s=String.fromCodePoint||String.fromCharCode;e.languages.md=e.languages.markdown}(a),a.languages.graphql={comment:/#.*/,description:{pattern:/(?:"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*")(?=\s*[a-z_])/i,greedy:!0,alias:"string",inside:{"language-markdown":{pattern:/(^"(?:"")?)(?!\1)[\s\S]+(?=\1$)/,lookbehind:!0,inside:a.languages.markdown}}},string:{pattern:/"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*"/,greedy:!0},number:/(?:\B-|\b)\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,boolean:/\b(?:false|true)\b/,variable:/\$[a-z_]\w*/i,directive:{pattern:/@[a-z_]\w*/i,alias:"function"},"attr-name":{pattern:/\b[a-z_]\w*(?=\s*(?:\((?:[^()"]|"(?:\\.|[^\\"\r\n])*")*\))?:)/i,greedy:!0},"atom-input":{pattern:/\b[A-Z]\w*Input\b/,alias:"class-name"},scalar:/\b(?:Boolean|Float|ID|Int|String)\b/,constant:/\b[A-Z][A-Z_\d]*\b/,"class-name":{pattern:/(\b(?:enum|implements|interface|on|scalar|type|union)\s+|&\s*|:\s*|\[)[A-Z_]\w*/,lookbehind:!0},fragment:{pattern:/(\bfragment\s+|\.{3}\s*(?!on\b))[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-mutation":{pattern:/(\bmutation\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-query":{pattern:/(\bquery\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},keyword:/\b(?:directive|enum|extend|fragment|implements|input|interface|mutation|on|query|repeatable|scalar|schema|subscription|type|union)\b/,operator:/[!=|&]|\.{3}/,"property-query":/\w+(?=\s*\()/,object:/\w+(?=\s*\{)/,punctuation:/[!(){}\[\]:=,]/,property:/\w+/},a.hooks.add("after-tokenize",(function(e){if("graphql"===e.language)for(var t=e.tokens.filter((function(e){return"string"!=typeof e&&"comment"!==e.type&&"scalar"!==e.type})),n=0;n<t.length;){var r=t[n++];if("keyword"===r.type&&"mutation"===r.content){var a=[];if(d(["definition-mutation","punctuation"])&&"("===u(1).content){n+=2;var o=p(/^\($/,/^\)$/);if(-1===o)continue;for(;n<o;n++){var i=u(0);"variable"===i.type&&(f(i,"variable-input"),a.push(i.content))}n=o+1}if(d(["punctuation","property-query"])&&"{"===u(0).content&&(n++,f(u(0),"property-mutation"),a.length>0)){var l=p(/^\{$/,/^\}$/);if(-1===l)continue;for(var s=n;s<l;s++){var c=t[s];"variable"===c.type&&a.indexOf(c.content)>=0&&f(c,"variable-input")}}}}function u(e){return t[n+e]}function d(e,t){t=t||0;for(var n=0;n<e.length;n++){var r=u(n+t);if(!r||r.type!==e[n])return!1}return!0}function p(e,r){for(var a=1,o=n;o<t.length;o++){var i=t[o],l=i.content;if("punctuation"===i.type&&"string"==typeof l)if(e.test(l))a++;else if(r.test(l)&&0===--a)return o}return-1}function f(e,t){var n=e.alias;n?Array.isArray(n)||(e.alias=n=[n]):e.alias=n=[],n.push(t)}})),a.languages.sql={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:--|\/\/|#).*)/,lookbehind:!0},variable:[{pattern:/@(["'`])(?:\\[\s\S]|(?!\1)[^\\])+\1/,greedy:!0},/@[\w.$]+/],string:{pattern:/(^|[^@\\])("|')(?:\\[\s\S]|(?!\2)[^\\]|\2\2)*\2/,greedy:!0,lookbehind:!0},identifier:{pattern:/(^|[^@\\])`(?:\\[\s\S]|[^`\\]|``)*`/,greedy:!0,lookbehind:!0,inside:{punctuation:/^`|`$/}},function:/\b(?:AVG|COUNT|FIRST|FORMAT|LAST|LCASE|LEN|MAX|MID|MIN|MOD|NOW|ROUND|SUM|UCASE)(?=\s*\()/i,keyword:/\b(?:ACTION|ADD|AFTER|ALGORITHM|ALL|ALTER|ANALYZE|ANY|APPLY|AS|ASC|AUTHORIZATION|AUTO_INCREMENT|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADED?|CASE|CHAIN|CHAR(?:ACTER|SET)?|CHECK(?:POINT)?|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMNS?|COMMENT|COMMIT(?:TED)?|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS(?:TABLE)?|CONTINUE|CONVERT|CREATE|CROSS|CURRENT(?:_DATE|_TIME|_TIMESTAMP|_USER)?|CURSOR|CYCLE|DATA(?:BASES?)?|DATE(?:TIME)?|DAY|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DELIMITERS?|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DROP|DUMMY|DUMP(?:FILE)?|DUPLICATE|ELSE(?:IF)?|ENABLE|ENCLOSED|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPED?|EXCEPT|EXEC(?:UTE)?|EXISTS|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR(?: EACH ROW)?|FORCE|FOREIGN|FREETEXT(?:TABLE)?|FROM|FULL|FUNCTION|GEOMETRY(?:COLLECTION)?|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|HOUR|IDENTITY(?:COL|_INSERT)?|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|INVOKER|ISOLATION|ITERATE|JOIN|KEYS?|KILL|LANGUAGE|LAST|LEAVE|LEFT|LEVEL|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONG(?:BLOB|TEXT)|LOOP|MATCH(?:ED)?|MEDIUM(?:BLOB|INT|TEXT)|MERGE|MIDDLEINT|MINUTE|MODE|MODIFIES|MODIFY|MONTH|MULTI(?:LINESTRING|POINT|POLYGON)|NATIONAL|NATURAL|NCHAR|NEXT|NO|NONCLUSTERED|NULLIF|NUMERIC|OFF?|OFFSETS?|ON|OPEN(?:DATASOURCE|QUERY|ROWSET)?|OPTIMIZE|OPTION(?:ALLY)?|ORDER|OUT(?:ER|FILE)?|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREPARE|PREV|PRIMARY|PRINT|PRIVILEGES|PROC(?:EDURE)?|PUBLIC|PURGE|QUICK|RAISERROR|READS?|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEAT(?:ABLE)?|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESTORE|RESTRICT|RETURN(?:ING|S)?|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROW(?:COUNT|GUIDCOL|S)?|RTREE|RULE|SAVE(?:POINT)?|SCHEMA|SECOND|SELECT|SERIAL(?:IZABLE)?|SESSION(?:_USER)?|SET(?:USER)?|SHARE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|SQL|START(?:ING)?|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLES?|TABLESPACE|TEMP(?:ORARY|TABLE)?|TERMINATED|TEXT(?:SIZE)?|THEN|TIME(?:STAMP)?|TINY(?:BLOB|INT|TEXT)|TOP?|TRAN(?:SACTIONS?)?|TRIGGER|TRUNCATE|TSEQUAL|TYPES?|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNIQUE|UNLOCK|UNPIVOT|UNSIGNED|UPDATE(?:TEXT)?|USAGE|USE|USER|USING|VALUES?|VAR(?:BINARY|CHAR|CHARACTER|YING)|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH(?: ROLLUP|IN)?|WORK|WRITE(?:TEXT)?|YEAR)\b/i,boolean:/\b(?:FALSE|NULL|TRUE)\b/i,number:/\b0x[\da-f]+\b|\b\d+(?:\.\d*)?|\B\.\d+\b/i,operator:/[-+*\/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|DIV|ILIKE|IN|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/},function(e){var t=e.languages.javascript["template-string"],n=t.pattern.source,r=t.inside.interpolation,a=r.inside["interpolation-punctuation"],o=r.pattern.source;function i(t,r){if(e.languages[t])return{pattern:RegExp("((?:"+r+")\\s*)"+n),lookbehind:!0,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},"embedded-code":{pattern:/[\s\S]+/,alias:t}}}}function l(e,t){return"___"+t.toUpperCase()+"_"+e+"___"}function s(t,n,r){var a={code:t,grammar:n,language:r};return e.hooks.run("before-tokenize",a),a.tokens=e.tokenize(a.code,a.grammar),e.hooks.run("after-tokenize",a),a.tokens}function c(t){var n={};n["interpolation-punctuation"]=a;var o=e.tokenize(t,n);if(3===o.length){var i=[1,1];i.push.apply(i,s(o[1],e.languages.javascript,"javascript")),o.splice.apply(o,i)}return new e.Token("interpolation",o,r.alias,t)}function u(t,n,r){var a=e.tokenize(t,{interpolation:{pattern:RegExp(o),lookbehind:!0}}),i=0,u={},d=s(a.map((function(e){if("string"==typeof e)return e;for(var n,a=e.content;-1!==t.indexOf(n=l(i++,r)););return u[n]=a,n})).join(""),n,r),p=Object.keys(u);return i=0,function e(t){for(var n=0;n<t.length;n++){if(i>=p.length)return;var r=t[n];if("string"==typeof r||"string"==typeof r.content){var a=p[i],o="string"==typeof r?r:r.content,l=o.indexOf(a);if(-1!==l){++i;var s=o.substring(0,l),d=c(u[a]),f=o.substring(l+a.length),m=[];if(s&&m.push(s),m.push(d),f){var h=[f];e(h),m.push.apply(m,h)}"string"==typeof r?(t.splice.apply(t,[n,1].concat(m)),n+=m.length-1):r.content=m}}else{var g=r.content;Array.isArray(g)?e(g):e([g])}}}(d),new e.Token(r,d,"language-"+r,t)}e.languages.javascript["template-string"]=[i("css",/\b(?:styled(?:\([^)]*\))?(?:\s*\.\s*\w+(?:\([^)]*\))*)*|css(?:\s*\.\s*(?:global|resolve))?|createGlobalStyle|keyframes)/.source),i("html",/\bhtml|\.\s*(?:inner|outer)HTML\s*\+?=/.source),i("svg",/\bsvg/.source),i("markdown",/\b(?:markdown|md)/.source),i("graphql",/\b(?:gql|graphql(?:\s*\.\s*experimental)?)/.source),i("sql",/\bsql/.source),t].filter(Boolean);var d={javascript:!0,js:!0,typescript:!0,ts:!0,jsx:!0,tsx:!0};function p(e){return"string"==typeof e?e:Array.isArray(e)?e.map(p).join(""):p(e.content)}e.hooks.add("after-tokenize",(function(t){t.language in d&&function t(n){for(var r=0,a=n.length;r<a;r++){var o=n[r];if("string"!=typeof o){var i=o.content;if(Array.isArray(i))if("template-string"===o.type){var l=i[1];if(3===i.length&&"string"!=typeof l&&"embedded-code"===l.type){var s=p(l),c=l.alias,d=Array.isArray(c)?c[0]:c,f=e.languages[d];if(!f)continue;i[1]=u(s,f,d)}}else t(i);else"string"!=typeof i&&t([i])}}}(t.tokens)}))}(a),function(e){e.languages.typescript=e.languages.extend("javascript",{"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|type)\s+)(?!keyof\b)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?:\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\b/}),e.languages.typescript.keyword.push(/\b(?:abstract|declare|is|keyof|readonly|require)\b/,/\b(?:asserts|infer|interface|module|namespace|type)\b(?=\s*(?:[{_$a-zA-Z\xA0-\uFFFF]|$))/,/\btype\b(?=\s*(?:[\{*]|$))/),delete e.languages.typescript.parameter,delete e.languages.typescript["literal-property"];var t=e.languages.extend("typescript",{});delete t["class-name"],e.languages.typescript["class-name"].inside=t,e.languages.insertBefore("typescript","function",{decorator:{pattern:/@[$\w\xA0-\uFFFF]+/,inside:{at:{pattern:/^@/,alias:"operator"},function:/^[\s\S]+/}},"generic-function":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:t}}}}),e.languages.ts=e.languages.typescript}(a),function(e){function t(e,t){return RegExp(e.replace(/<ID>/g,(function(){return/(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/.source})),t)}e.languages.insertBefore("javascript","function-variable",{"method-variable":{pattern:RegExp("(\\.\\s*)"+e.languages.javascript["function-variable"].pattern.source),lookbehind:!0,alias:["function-variable","method","function","property-access"]}}),e.languages.insertBefore("javascript","function",{method:{pattern:RegExp("(\\.\\s*)"+e.languages.javascript.function.source),lookbehind:!0,alias:["function","property-access"]}}),e.languages.insertBefore("javascript","constant",{"known-class-name":[{pattern:/\b(?:(?:Float(?:32|64)|(?:Int|Uint)(?:8|16|32)|Uint8Clamped)?Array|ArrayBuffer|BigInt|Boolean|DataView|Date|Error|Function|Intl|JSON|(?:Weak)?(?:Map|Set)|Math|Number|Object|Promise|Proxy|Reflect|RegExp|String|Symbol|WebAssembly)\b/,alias:"class-name"},{pattern:/\b(?:[A-Z]\w*)Error\b/,alias:"class-name"}]}),e.languages.insertBefore("javascript","keyword",{imports:{pattern:t(/(\bimport\b\s*)(?:<ID>(?:\s*,\s*(?:\*\s*as\s+<ID>|\{[^{}]*\}))?|\*\s*as\s+<ID>|\{[^{}]*\})(?=\s*\bfrom\b)/.source),lookbehind:!0,inside:e.languages.javascript},exports:{pattern:t(/(\bexport\b\s*)(?:\*(?:\s*as\s+<ID>)?(?=\s*\bfrom\b)|\{[^{}]*\})/.source),lookbehind:!0,inside:e.languages.javascript}}),e.languages.javascript.keyword.unshift({pattern:/\b(?:as|default|export|from|import)\b/,alias:"module"},{pattern:/\b(?:await|break|catch|continue|do|else|finally|for|if|return|switch|throw|try|while|yield)\b/,alias:"control-flow"},{pattern:/\bnull\b/,alias:["null","nil"]},{pattern:/\bundefined\b/,alias:"nil"}),e.languages.insertBefore("javascript","operator",{spread:{pattern:/\.{3}/,alias:"operator"},arrow:{pattern:/=>/,alias:"operator"}}),e.languages.insertBefore("javascript","punctuation",{"property-access":{pattern:t(/(\.\s*)#?<ID>/.source),lookbehind:!0},"maybe-class-name":{pattern:/(^|[^$\w\xA0-\uFFFF])[A-Z][$\w\xA0-\uFFFF]+/,lookbehind:!0},dom:{pattern:/\b(?:document|(?:local|session)Storage|location|navigator|performance|window)\b/,alias:"variable"},console:{pattern:/\bconsole(?=\s*\.)/,alias:"class-name"}});for(var n=["function","function-variable","method","method-variable","property-access"],r=0;r<n.length;r++){var a=n[r],o=e.languages.javascript[a];"RegExp"===e.util.type(o)&&(o=e.languages.javascript[a]={pattern:o});var i=o.inside||{};o.inside=i,i["maybe-class-name"]=/^[A-Z][\s\S]*/}}(a),function(e){var t=e.util.clone(e.languages.javascript),n=/(?:\s|\/\/.*(?!.)|\/\*(?:[^*]|\*(?!\/))\*\/)/.source,r=/(?:\{(?:\{(?:\{[^{}]*\}|[^{}])*\}|[^{}])*\})/.source,a=/(?:\{<S>*\.{3}(?:[^{}]|<BRACES>)*\})/.source;function o(e,t){return e=e.replace(/<S>/g,(function(){return n})).replace(/<BRACES>/g,(function(){return r})).replace(/<SPREAD>/g,(function(){return a})),RegExp(e,t)}a=o(a).source,e.languages.jsx=e.languages.extend("markup",t),e.languages.jsx.tag.pattern=o(/<\/?(?:[\w.:-]+(?:<S>+(?:[\w.:$-]+(?:=(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s{'"/>=]+|<BRACES>))?|<SPREAD>))*<S>*\/?)?>/.source),e.languages.jsx.tag.inside.tag.pattern=/^<\/?[^\s>\/]*/,e.languages.jsx.tag.inside["attr-value"].pattern=/=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/,e.languages.jsx.tag.inside.tag.inside["class-name"]=/^[A-Z]\w*(?:\.[A-Z]\w*)*$/,e.languages.jsx.tag.inside.comment=t.comment,e.languages.insertBefore("inside","attr-name",{spread:{pattern:o(/<SPREAD>/.source),inside:e.languages.jsx}},e.languages.jsx.tag),e.languages.insertBefore("inside","special-attr",{script:{pattern:o(/=<BRACES>/.source),alias:"language-javascript",inside:{"script-punctuation":{pattern:/^=(?=\{)/,alias:"punctuation"},rest:e.languages.jsx}}},e.languages.jsx.tag);var i=function(e){return e?"string"==typeof e?e:"string"==typeof e.content?e.content:e.content.map(i).join(""):""},l=function(t){for(var n=[],r=0;r<t.length;r++){var a=t[r],o=!1;if("string"!=typeof a&&("tag"===a.type&&a.content[0]&&"tag"===a.content[0].type?"</"===a.content[0].content[0].content?n.length>0&&n[n.length-1].tagName===i(a.content[0].content[1])&&n.pop():"/>"===a.content[a.content.length-1].content||n.push({tagName:i(a.content[0].content[1]),openedBraces:0}):n.length>0&&"punctuation"===a.type&&"{"===a.content?n[n.length-1].openedBraces++:n.length>0&&n[n.length-1].openedBraces>0&&"punctuation"===a.type&&"}"===a.content?n[n.length-1].openedBraces--:o=!0),(o||"string"==typeof a)&&n.length>0&&0===n[n.length-1].openedBraces){var s=i(a);r<t.length-1&&("string"==typeof t[r+1]||"plain-text"===t[r+1].type)&&(s+=i(t[r+1]),t.splice(r+1,1)),r>0&&("string"==typeof t[r-1]||"plain-text"===t[r-1].type)&&(s=i(t[r-1])+s,t.splice(r-1,1),r--),t[r]=new e.Token("plain-text",s,null,s)}a.content&&"string"!=typeof a.content&&l(a.content)}};e.hooks.add("after-tokenize",(function(e){"jsx"!==e.language&&"tsx"!==e.language||l(e.tokens)}))}(a),function(e){e.languages.diff={coord:[/^(?:\*{3}|-{3}|\+{3}).*$/m,/^@@.*@@$/m,/^\d.*$/m]};var t={"deleted-sign":"-","deleted-arrow":"<","inserted-sign":"+","inserted-arrow":">",unchanged:" ",diff:"!"};Object.keys(t).forEach((function(n){var r=t[n],a=[];/^\w+$/.test(n)||a.push(/\w+/.exec(n)[0]),"diff"===n&&a.push("bold"),e.languages.diff[n]={pattern:RegExp("^(?:["+r+"].*(?:\r\n?|\n|(?![\\s\\S])))+","m"),alias:a,inside:{line:{pattern:/(.)(?=[\s\S]).*(?:\r\n?|\n)?/,lookbehind:!0},prefix:{pattern:/[\s\S]/,alias:/\w+/.exec(n)[0]}}}})),Object.defineProperty(e.languages.diff,"PREFIXES",{value:t})}(a),a.languages.git={comment:/^#.*/m,deleted:/^[-\u2013].*/m,inserted:/^\+.*/m,string:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,command:{pattern:/^.*\$ git .*$/m,inside:{parameter:/\s--?\w+/}},coord:/^@@.*@@$/m,"commit-sha1":/^commit \w{40}$/m},a.languages.go=a.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"|`[^`]*`/,lookbehind:!0,greedy:!0},keyword:/\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/,boolean:/\b(?:_|false|iota|nil|true)\b/,number:[/\b0(?:b[01_]+|o[0-7_]+)i?\b/i,/\b0x(?:[a-f\d_]+(?:\.[a-f\d_]*)?|\.[a-f\d_]+)(?:p[+-]?\d+(?:_\d+)*)?i?(?!\w)/i,/(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?[\d_]+)?i?(?!\w)/i],operator:/[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,builtin:/\b(?:append|bool|byte|cap|close|complex|complex(?:64|128)|copy|delete|error|float(?:32|64)|u?int(?:8|16|32|64)?|imag|len|make|new|panic|print(?:ln)?|real|recover|rune|string|uintptr)\b/}),a.languages.insertBefore("go","string",{char:{pattern:/'(?:\\.|[^'\\\r\n]){0,10}'/,greedy:!0}}),delete a.languages.go["class-name"],function(e){function t(e,t){return"___"+e.toUpperCase()+t+"___"}Object.defineProperties(e.languages["markup-templating"]={},{buildPlaceholders:{value:function(n,r,a,o){if(n.language===r){var i=n.tokenStack=[];n.code=n.code.replace(a,(function(e){if("function"==typeof o&&!o(e))return e;for(var a,l=i.length;-1!==n.code.indexOf(a=t(r,l));)++l;return i[l]=e,a})),n.grammar=e.languages.markup}}},tokenizePlaceholders:{value:function(n,r){if(n.language===r&&n.tokenStack){n.grammar=e.languages[r];var a=0,o=Object.keys(n.tokenStack);!function i(l){for(var s=0;s<l.length&&!(a>=o.length);s++){var c=l[s];if("string"==typeof c||c.content&&"string"==typeof c.content){var u=o[a],d=n.tokenStack[u],p="string"==typeof c?c:c.content,f=t(r,u),m=p.indexOf(f);if(m>-1){++a;var h=p.substring(0,m),g=new e.Token(r,e.tokenize(d,n.grammar),"language-"+r,d),b=p.substring(m+f.length),v=[];h&&v.push.apply(v,i([h])),v.push(g),b&&v.push.apply(v,i([b])),"string"==typeof c?l.splice.apply(l,[s,1].concat(v)):c.content=v}}else c.content&&i(c.content)}return l}(n.tokens)}}}})}(a),function(e){e.languages.handlebars={comment:/\{\{![\s\S]*?\}\}/,delimiter:{pattern:/^\{\{\{?|\}\}\}?$/,alias:"punctuation"},string:/(["'])(?:\\.|(?!\1)[^\\\r\n])*\1/,number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee][+-]?\d+)?/,boolean:/\b(?:false|true)\b/,block:{pattern:/^(\s*(?:~\s*)?)[#\/]\S+?(?=\s*(?:~\s*)?$|\s)/,lookbehind:!0,alias:"keyword"},brackets:{pattern:/\[[^\]]+\]/,inside:{punctuation:/\[|\]/,variable:/[\s\S]+/}},punctuation:/[!"#%&':()*+,.\/;<=>@\[\\\]^`{|}~]/,variable:/[^!"#%&'()*+,\/;<=>@\[\\\]^`{|}~\s]+/},e.hooks.add("before-tokenize",(function(t){e.languages["markup-templating"].buildPlaceholders(t,"handlebars",/\{\{\{[\s\S]+?\}\}\}|\{\{[\s\S]+?\}\}/g)})),e.hooks.add("after-tokenize",(function(t){e.languages["markup-templating"].tokenizePlaceholders(t,"handlebars")})),e.languages.hbs=e.languages.handlebars}(a),a.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},a.languages.webmanifest=a.languages.json,a.languages.less=a.languages.extend("css",{comment:[/\/\*[\s\S]*?\*\//,{pattern:/(^|[^\\])\/\/.*/,lookbehind:!0}],atrule:{pattern:/@[\w-](?:\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{punctuation:/[:()]/}},selector:{pattern:/(?:@\{[\w-]+\}|[^{};\s@])(?:@\{[\w-]+\}|\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};@\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{variable:/@+[\w-]+/}},property:/(?:@\{[\w-]+\}|[\w-])+(?:\+_?)?(?=\s*:)/,operator:/[+\-*\/]/}),a.languages.insertBefore("less","property",{variable:[{pattern:/@[\w-]+\s*:/,inside:{punctuation:/:/}},/@@?[\w-]+/],"mixin-usage":{pattern:/([{;]\s*)[.#](?!\d)[\w-].*?(?=[(;])/,lookbehind:!0,alias:"function"}}),a.languages.makefile={comment:{pattern:/(^|[^\\])#(?:\\(?:\r\n|[\s\S])|[^\\\r\n])*/,lookbehind:!0},string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"builtin-target":{pattern:/\.[A-Z][^:#=\s]+(?=\s*:(?!=))/,alias:"builtin"},target:{pattern:/^(?:[^:=\s]|[ \t]+(?![\s:]))+(?=\s*:(?!=))/m,alias:"symbol",inside:{variable:/\$+(?:(?!\$)[^(){}:#=\s]+|(?=[({]))/}},variable:/\$+(?:(?!\$)[^(){}:#=\s]+|\([@*%<^+?][DF]\)|(?=[({]))/,keyword:/-include\b|\b(?:define|else|endef|endif|export|ifn?def|ifn?eq|include|override|private|sinclude|undefine|unexport|vpath)\b/,function:{pattern:/(\()(?:abspath|addsuffix|and|basename|call|dir|error|eval|file|filter(?:-out)?|findstring|firstword|flavor|foreach|guile|if|info|join|lastword|load|notdir|or|origin|patsubst|realpath|shell|sort|strip|subst|suffix|value|warning|wildcard|word(?:list|s)?)(?=[ \t])/,lookbehind:!0},operator:/(?:::|[?:+!])?=|[|@]/,punctuation:/[:;(){}]/},a.languages.objectivec=a.languages.extend("c",{string:{pattern:/@?"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},keyword:/\b(?:asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|in|inline|int|long|register|return|self|short|signed|sizeof|static|struct|super|switch|typedef|typeof|union|unsigned|void|volatile|while)\b|(?:@interface|@end|@implementation|@protocol|@class|@public|@protected|@private|@property|@try|@catch|@finally|@throw|@synthesize|@dynamic|@selector)\b/,operator:/-[->]?|\+\+?|!=?|<<?=?|>>?=?|==?|&&?|\|\|?|[~^%?*\/@]/}),delete a.languages.objectivec["class-name"],a.languages.objc=a.languages.objectivec,a.languages.ocaml={comment:{pattern:/\(\*[\s\S]*?\*\)/,greedy:!0},char:{pattern:/'(?:[^\\\r\n']|\\(?:.|[ox]?[0-9a-f]{1,3}))'/i,greedy:!0},string:[{pattern:/"(?:\\(?:[\s\S]|\r\n)|[^\\\r\n"])*"/,greedy:!0},{pattern:/\{([a-z_]*)\|[\s\S]*?\|\1\}/,greedy:!0}],number:[/\b(?:0b[01][01_]*|0o[0-7][0-7_]*)\b/i,/\b0x[a-f0-9][a-f0-9_]*(?:\.[a-f0-9_]*)?(?:p[+-]?\d[\d_]*)?(?!\w)/i,/\b\d[\d_]*(?:\.[\d_]*)?(?:e[+-]?\d[\d_]*)?(?!\w)/i],directive:{pattern:/\B#\w+/,alias:"property"},label:{pattern:/\B~\w+/,alias:"property"},"type-variable":{pattern:/\B'\w+/,alias:"function"},variant:{pattern:/`\w+/,alias:"symbol"},keyword:/\b(?:as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|match|method|module|mutable|new|nonrec|object|of|open|private|rec|sig|struct|then|to|try|type|val|value|virtual|when|where|while|with)\b/,boolean:/\b(?:false|true)\b/,"operator-like-punctuation":{pattern:/\[[<>|]|[>|]\]|\{<|>\}/,alias:"punctuation"},operator:/\.[.~]|:[=>]|[=<>@^|&+\-*\/$%!?~][!$%&*+\-.\/:<=>?@^|~]*|\b(?:and|asr|land|lor|lsl|lsr|lxor|mod|or)\b/,punctuation:/;;|::|[(){}\[\].,:;#]|\b_\b/},a.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},a.languages.python["string-interpolation"].inside.interpolation.inside.rest=a.languages.python,a.languages.py=a.languages.python,a.languages.reason=a.languages.extend("clike",{string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^\\\r\n"])*"/,greedy:!0},"class-name":/\b[A-Z]\w*/,keyword:/\b(?:and|as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|method|module|mutable|new|nonrec|object|of|open|or|private|rec|sig|struct|switch|then|to|try|type|val|virtual|when|while|with)\b/,operator:/\.{3}|:[:=]|\|>|->|=(?:==?|>)?|<=?|>=?|[|^?'#!~`]|[+\-*\/]\.?|\b(?:asr|land|lor|lsl|lsr|lxor|mod)\b/}),a.languages.insertBefore("reason","class-name",{char:{pattern:/'(?:\\x[\da-f]{2}|\\o[0-3][0-7][0-7]|\\\d{3}|\\.|[^'\\\r\n])'/,greedy:!0},constructor:/\b[A-Z]\w*\b(?!\s*\.)/,label:{pattern:/\b[a-z]\w*(?=::)/,alias:"symbol"}}),delete a.languages.reason.function,function(e){e.languages.sass=e.languages.extend("css",{comment:{pattern:/^([ \t]*)\/[\/*].*(?:(?:\r?\n|\r)\1[ \t].+)*/m,lookbehind:!0,greedy:!0}}),e.languages.insertBefore("sass","atrule",{"atrule-line":{pattern:/^(?:[ \t]*)[@+=].+/m,greedy:!0,inside:{atrule:/(?:@[\w-]+|[+=])/}}}),delete e.languages.sass.atrule;var t=/\$[-\w]+|#\{\$[-\w]+\}/,n=[/[+*\/%]|[=!]=|<=?|>=?|\b(?:and|not|or)\b/,{pattern:/(\s)-(?=\s)/,lookbehind:!0}];e.languages.insertBefore("sass","property",{"variable-line":{pattern:/^[ \t]*\$.+/m,greedy:!0,inside:{punctuation:/:/,variable:t,operator:n}},"property-line":{pattern:/^[ \t]*(?:[^:\s]+ *:.*|:[^:\s].*)/m,greedy:!0,inside:{property:[/[^:\s]+(?=\s*:)/,{pattern:/(:)[^:\s]+/,lookbehind:!0}],punctuation:/:/,variable:t,operator:n,important:e.languages.sass.important}}}),delete e.languages.sass.property,delete e.languages.sass.important,e.languages.insertBefore("sass","punctuation",{selector:{pattern:/^([ \t]*)\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*(?:,(?:\r?\n|\r)\1[ \t]+\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*)*/m,lookbehind:!0,greedy:!0}})}(a),a.languages.scss=a.languages.extend("css",{comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},atrule:{pattern:/@[\w-](?:\([^()]+\)|[^()\s]|\s+(?!\s))*?(?=\s+[{;])/,inside:{rule:/@[\w-]+/}},url:/(?:[-a-z]+-)?url(?=\()/i,selector:{pattern:/(?=\S)[^@;{}()]?(?:[^@;{}()\s]|\s+(?!\s)|#\{\$[-\w]+\})+(?=\s*\{(?:\}|\s|[^}][^:{}]*[:{][^}]))/,inside:{parent:{pattern:/&/,alias:"important"},placeholder:/%[-\w]+/,variable:/\$[-\w]+|#\{\$[-\w]+\}/}},property:{pattern:/(?:[-\w]|\$[-\w]|#\{\$[-\w]+\})+(?=\s*:)/,inside:{variable:/\$[-\w]+|#\{\$[-\w]+\}/}}}),a.languages.insertBefore("scss","atrule",{keyword:[/@(?:content|debug|each|else(?: if)?|extend|for|forward|function|if|import|include|mixin|return|use|warn|while)\b/i,{pattern:/( )(?:from|through)(?= )/,lookbehind:!0}]}),a.languages.insertBefore("scss","important",{variable:/\$[-\w]+|#\{\$[-\w]+\}/}),a.languages.insertBefore("scss","function",{"module-modifier":{pattern:/\b(?:as|hide|show|with)\b/i,alias:"keyword"},placeholder:{pattern:/%[-\w]+/,alias:"selector"},statement:{pattern:/\B!(?:default|optional)\b/i,alias:"keyword"},boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"},operator:{pattern:/(\s)(?:[-+*\/%]|[=!]=|<=?|>=?|and|not|or)(?=\s)/,lookbehind:!0}}),a.languages.scss.atrule.inside.rest=a.languages.scss,function(e){var t={pattern:/(\b\d+)(?:%|[a-z]+)/,lookbehind:!0},n={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0},r={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},url:{pattern:/\burl\((["']?).*?\1\)/i,greedy:!0},string:{pattern:/("|')(?:(?!\1)[^\\\r\n]|\\(?:\r\n|[\s\S]))*\1/,greedy:!0},interpolation:null,func:null,important:/\B!(?:important|optional)\b/i,keyword:{pattern:/(^|\s+)(?:(?:else|for|if|return|unless)(?=\s|$)|@[\w-]+)/,lookbehind:!0},hexcode:/#[\da-f]{3,6}/i,color:[/\b(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)\b/i,{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:t,number:n,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:t,boolean:/\b(?:false|true)\b/,operator:[/~|[+!\/%<>?=]=?|[-:]=|\*[*=]?|\.{2,3}|&&|\|\||\B-\B|\b(?:and|in|is(?: a| defined| not|nt)?|not|or)\b/],number:n,punctuation:/[{}()\[\];:,]/};r.interpolation={pattern:/\{[^\r\n}:]+\}/,alias:"variable",inside:{delimiter:{pattern:/^\{|\}$/,alias:"punctuation"},rest:r}},r.func={pattern:/[\w-]+\([^)]*\).*/,inside:{function:/^[^(]+/,rest:r}},e.languages.stylus={"atrule-declaration":{pattern:/(^[ \t]*)@.+/m,lookbehind:!0,inside:{atrule:/^@[\w-]+/,rest:r}},"variable-declaration":{pattern:/(^[ \t]*)[\w$-]+\s*.?=[ \t]*(?:\{[^{}]*\}|\S.*|$)/m,lookbehind:!0,inside:{variable:/^\S+/,rest:r}},statement:{pattern:/(^[ \t]*)(?:else|for|if|return|unless)[ \t].+/m,lookbehind:!0,inside:{keyword:/^\S+/,rest:r}},"property-declaration":{pattern:/((?:^|\{)([ \t]*))(?:[\w-]|\{[^}\r\n]+\})+(?:\s*:\s*|[ \t]+)(?!\s)[^{\r\n]*(?:;|[^{\r\n,]$(?!(?:\r?\n|\r)(?:\{|\2[ \t])))/m,lookbehind:!0,inside:{property:{pattern:/^[^\s:]+/,inside:{interpolation:r.interpolation}},rest:r}},selector:{pattern:/(^[ \t]*)(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)(?:(?:\r?\n|\r)(?:\1(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)))*(?:,$|\{|(?=(?:\r?\n|\r)(?:\{|\1[ \t])))/m,lookbehind:!0,inside:{interpolation:r.interpolation,comment:r.comment,punctuation:/[{},]/}},func:r.func,string:r.string,comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0,greedy:!0},interpolation:r.interpolation,punctuation:/[{}()\[\];:.]/}}(a),function(e){var t=e.util.clone(e.languages.typescript);e.languages.tsx=e.languages.extend("jsx",t),delete e.languages.tsx.parameter,delete e.languages.tsx["literal-property"];var n=e.languages.tsx.tag;n.pattern=RegExp(/(^|[^\w$]|(?=<\/))/.source+"(?:"+n.pattern.source+")",n.pattern.flags),n.lookbehind=!0}(a),a.languages.wasm={comment:[/\(;[\s\S]*?;\)/,{pattern:/;;.*/,greedy:!0}],string:{pattern:/"(?:\\[\s\S]|[^"\\])*"/,greedy:!0},keyword:[{pattern:/\b(?:align|offset)=/,inside:{operator:/=/}},{pattern:/\b(?:(?:f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|neg?|nearest|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|sqrt|store(?:8|16|32)?|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))?|memory\.(?:grow|size))\b/,inside:{punctuation:/\./}},/\b(?:anyfunc|block|br(?:_if|_table)?|call(?:_indirect)?|data|drop|elem|else|end|export|func|get_(?:global|local)|global|if|import|local|loop|memory|module|mut|nop|offset|param|result|return|select|set_(?:global|local)|start|table|tee_local|then|type|unreachable)\b/],variable:/\$[\w!#$%&'*+\-./:<=>?@\\^`|~]+/,number:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/,punctuation:/[()]/};const o=a},7459:(e,t,n)=>{"use strict";function r(e){var t,n,a="";if("string"==typeof e||"number"==typeof e)a+=e;else if("object"==typeof e)if(Array.isArray(e))for(t=0;t<e.length;t++)e[t]&&(n=r(e[t]))&&(a&&(a+=" "),a+=n);else for(t in e)e[t]&&(a&&(a+=" "),a+=t);return a}n.d(t,{Z:()=>a});const a=function(){for(var e,t,n=0,a="";n<arguments.length;)(e=arguments[n++])&&(t=r(e))&&(a&&(a+=" "),a+=t);return a}},723:(e,t,n)=>{"use strict";n.d(t,{Z:()=>f});var r=n(7294),a=n(7462),o=n(8356),i=n.n(o),l=n(6887);const s={"040fbf97":[()=>n.e(1201).then(n.bind(n,8189)),"@site/versioned_docs/version-v0.4.x/troubleshooting.md",8189],"0480b142":[()=>n.e(836).then(n.bind(n,3584)),"@site/docs/faq.md",3584],"053b5658":[()=>n.e(335).then(n.bind(n,9412)),"@site/versioned_docs/version-v0.1.x/faq.md",9412],"05a474a1":[()=>n.e(4073).then(n.bind(n,5390)),"@site/docs/release.md",5390],"0a7bb60d":[()=>n.e(9691).then(n.bind(n,5850)),"@site/versioned_docs/version-v0.1.x/design.md",5850],"0aeccac2":[()=>n.e(414).then(n.bind(n,6512)),"@site/docs/design.md",6512],"0c380bb3":[()=>n.e(6948).then(n.bind(n,3066)),"@site/versioned_docs/version-v0.2.x/quick-start.md",3066],"0d9a188d":[()=>n.e(1865).then(n.bind(n,7970)),"@site/versioned_docs/version-v0.5.x/contributing.md",7970],"11d58a17":[()=>n.e(154).then(n.bind(n,9971)),"@site/versioned_docs/version-v0.5.x/code-of-conduct.md",9971],"12042f32":[()=>n.e(1802).then(n.bind(n,7492)),"@site/versioned_docs/version-v0.6.x/faq.md",7492],"14aa7e32":[()=>n.e(5897).then(n.bind(n,5582)),"@site/docs/github-action.md",5582],"14b4e536":[()=>n.e(3186).then(n.bind(n,1984)),"@site/versioned_docs/version-v0.6.x/github-action.md",1984],17896441:[()=>Promise.all([n.e(532),n.e(7918)]).then(n.bind(n,8300)),"@theme/DocItem",8300],"17e02f37":[()=>n.e(8).then(n.bind(n,4109)),"@site/versioned_docs/version-v0.6.x/introduction.md",4109],"1b7d20b2":[()=>n.e(2676).then(n.bind(n,9303)),"@site/versioned_docs/version-v0.4.x/github-action.md",9303],"1be78505":[()=>Promise.all([n.e(532),n.e(9514)]).then(n.bind(n,9963)),"@theme/DocPage",9963],"2133a534":[()=>n.e(5737).then(n.bind(n,5389)),"@site/versioned_docs/version-v0.1.x/installation.md",5389],"22539a87":[()=>n.e(5964).then(n.bind(n,9684)),"@site/versioned_docs/version-v0.4.x/introduction.md",9684],"22dce6f4":[()=>n.e(7252).then(n.bind(n,2014)),"@site/versioned_docs/version-v0.5.x/design.md",2014],"27cf52aa":[()=>n.e(138).then(n.bind(n,5579)),"@site/docs/custom-address.md",5579],"282f11b9":[()=>n.e(2072).then(n.bind(n,2352)),"@site/docs/maintainer-guidelines.md",2352],"299b5310":[()=>n.e(5145).then(n.t.bind(n,5369,19)),"~docs/default/version-v-0-2-x-metadata-prop-6dd.json",5369],"2a636ef6":[()=>n.e(2702).then(n.bind(n,4820)),"@site/versioned_docs/version-v0.3.x/faq.md",4820],"34f2f592":[()=>n.e(9269).then(n.bind(n,5374)),"@site/versioned_docs/version-v0.5.x/quick-start.md",5374],"3b8c55ea":[()=>n.e(3217).then(n.bind(n,9803)),"@site/docs/installation.md",9803],"3fcb412e":[()=>n.e(3242).then(n.bind(n,1450)),"@site/versioned_docs/version-v0.4.x/installation.md",1450],"4468d236":[()=>n.e(6198).then(n.bind(n,540)),"@site/versioned_docs/version-v0.6.x/maintainer-guidelines.md",540],"48ae5635":[()=>n.e(9212).then(n.bind(n,6954)),"@site/versioned_docs/version-v0.4.x/contributing.md",6954],"499c1190":[()=>n.e(3891).then(n.bind(n,5837)),"@site/versioned_docs/version-v0.6.x/best-practices.md",5837],"4cb9f763":[()=>n.e(2088).then(n.bind(n,3656)),"@site/versioned_docs/version-v0.2.x/design.md",3656],"4d54d076":[()=>n.e(7080).then(n.bind(n,1933)),"@site/docs/contributing.md",1933],"4d7fa969":[()=>n.e(9848).then(n.bind(n,8703)),"@site/versioned_docs/version-v0.5.x/troubleshooting.md",8703],"4e9dbf89":[()=>n.e(2747).then(n.bind(n,684)),"@site/versioned_docs/version-v0.6.x/development-tips.md",684],"512645f2":[()=>n.e(2279).then(n.bind(n,233)),"@site/versioned_docs/version-v0.5.x/github-action.md",233],"57b2432c":[()=>n.e(8765).then(n.bind(n,3358)),"@site/versioned_docs/version-v0.3.x/introduction.md",3358],"5c8ee200":[()=>n.e(1057).then(n.bind(n,4633)),"@site/versioned_docs/version-v0.5.x/scanner-plugins.md",4633],"5fc6e4d2":[()=>n.e(3602).then(n.bind(n,9675)),"@site/versioned_docs/version-v0.6.x/code-of-conduct.md",9675],"6173fde8":[()=>n.e(7451).then(n.bind(n,7308)),"@site/versioned_docs/version-v0.1.x/quick-start.md",7308],"683b919f":[()=>n.e(2932).then(n.bind(n,2835)),"@site/versioned_docs/version-v0.6.x/output.md",2835],"6bd0979b":[()=>n.e(8838).then(n.t.bind(n,9358,19)),"~docs/default/version-v-0-3-x-metadata-prop-338.json",9358],"72e14192":[()=>n.e(7239).then(n.bind(n,4181)),"@site/docs/quick-start.md",4181],"77a9ed43":[()=>n.e(6380).then(n.bind(n,509)),"@site/versioned_docs/version-v0.5.x/release.md",509],"7d213ce9":[()=>n.e(240).then(n.bind(n,1090)),"@site/versioned_docs/version-v0.6.x/installation.md",1090],"7e72de0f":[()=>n.e(722).then(n.bind(n,5301)),"@site/versioned_docs/version-v0.3.x/contributing.md",5301],"801664dc":[()=>n.e(1150).then(n.bind(n,6903)),"@site/versioned_docs/version-v0.6.x/contributing.md",6903],"804a5934":[()=>n.e(7071).then(n.t.bind(n,3769,19)),"/home/runner/work/copacetic/copacetic/website/.docusaurus/docusaurus-plugin-content-docs/default/plugin-route-context-module-100.json",3769],84979900:[()=>n.e(4789).then(n.bind(n,4320)),"@site/versioned_docs/version-v0.6.x/quick-start.md",4320],"86b9c768":[()=>n.e(267).then(n.bind(n,401)),"@site/versioned_docs/version-v0.2.x/installation.md",401],"89eebbd3":[()=>n.e(9925).then(n.bind(n,8902)),"@site/versioned_docs/version-v0.3.x/code-of-conduct.md",8902],"934782ba":[()=>n.e(8163).then(n.bind(n,315)),"@site/versioned_docs/version-v0.2.x/faq.md",315],"935f2afb":[()=>n.e(53).then(n.t.bind(n,1109,19)),"~docs/default/version-current-metadata-prop-751.json",1109],"98016c8b":[()=>n.e(1031).then(n.bind(n,5924)),"@site/versioned_docs/version-v0.6.x/release.md",5924],"9b66f67b":[()=>n.e(6022).then(n.bind(n,8810)),"@site/versioned_docs/version-v0.6.x/custom-address.md",8810],"9d9f8394":[()=>n.e(9360).then(n.bind(n,9222)),"@site/docs/troubleshooting.md",9222],"9e350ec0":[()=>n.e(7262).then(n.bind(n,6937)),"@site/versioned_docs/version-v0.4.x/quick-start.md",6937],"9f27d5ef":[()=>n.e(6927).then(n.bind(n,9936)),"@site/versioned_docs/version-v0.5.x/output.md",9936],"9f65b58a":[()=>n.e(3480).then(n.bind(n,5177)),"@site/versioned_docs/version-v0.3.x/design.md",5177],a09c2993:[()=>n.e(4128).then(n.bind(n,8495)),"@site/docs/introduction.md",8495],a95b810a:[()=>n.e(5692).then(n.bind(n,5949)),"@site/versioned_docs/version-v0.6.x/design.md",5949],b407a98a:[()=>n.e(4925).then(n.bind(n,1615)),"@site/versioned_docs/version-v0.2.x/code-of-conduct.md",1615],b5d5e16b:[()=>n.e(7059).then(n.bind(n,7264)),"@site/versioned_docs/version-v0.3.x/installation.md",7264],b9421f89:[()=>n.e(7114).then(n.bind(n,550)),"@site/versioned_docs/version-v0.4.x/faq.md",550],baa9408d:[()=>n.e(6571).then(n.bind(n,6118)),"@site/docs/development-tips.md",6118],bab6f5ab:[()=>n.e(3200).then(n.bind(n,7216)),"@site/versioned_docs/version-v0.1.x/code-of-conduct.md",7216],bfa18532:[()=>n.e(3665).then(n.bind(n,9771)),"@site/docs/scanner-plugins.md",9771],c12dc9fd:[()=>n.e(6325).then(n.bind(n,4606)),"@site/versioned_docs/version-v0.4.x/code-of-conduct.md",4606],c13ba925:[()=>n.e(7845).then(n.bind(n,460)),"@site/versioned_docs/version-v0.4.x/design.md",460],c5934ffc:[()=>n.e(6705).then(n.t.bind(n,9452,19)),"~docs/default/version-v-0-1-x-metadata-prop-912.json",9452],c66bbf8a:[()=>n.e(568).then(n.bind(n,789)),"@site/versioned_docs/version-v0.5.x/introduction.md",789],c698fe77:[()=>n.e(4993).then(n.t.bind(n,6283,19)),"~docs/default/version-v-0-4-x-metadata-prop-1ae.json",6283],c73303db:[()=>n.e(5082).then(n.bind(n,2992)),"@site/versioned_docs/version-v0.5.x/installation.md",2992],d2339658:[()=>n.e(8644).then(n.bind(n,7316)),"@site/versioned_docs/version-v0.1.x/introduction.md",7316],d832efb0:[()=>n.e(9070).then(n.bind(n,4293)),"@site/versioned_docs/version-v0.6.x/scanner-plugins.md",4293],d9461b6c:[()=>n.e(4).then(n.bind(n,5814)),"@site/versioned_docs/version-v0.2.x/introduction.md",5814],dea0f9ea:[()=>n.e(6352).then(n.bind(n,6853)),"@site/docs/code-of-conduct.md",6853],e4d3cddf:[()=>n.e(8202).then(n.bind(n,3010)),"@site/docs/best-practices.md",3010],e8c0720c:[()=>n.e(3296).then(n.bind(n,4376)),"@site/docs/output.md",4376],e99d2929:[()=>n.e(2972).then(n.bind(n,1413)),"@site/versioned_docs/version-v0.6.x/troubleshooting.md",1413],ea9eabff:[()=>n.e(7835).then(n.bind(n,1008)),"@site/versioned_docs/version-v0.1.x/contributing.md",1008],efdb11b6:[()=>n.e(4111).then(n.t.bind(n,7064,19)),"~docs/default/version-v-0-5-x-metadata-prop-c47.json",7064],f3bd9382:[()=>n.e(494).then(n.bind(n,8034)),"@site/versioned_docs/version-v0.4.x/release.md",8034],f9459e94:[()=>n.e(1220).then(n.bind(n,5616)),"@site/versioned_docs/version-v0.3.x/quick-start.md",5616],fa2b770f:[()=>n.e(6148).then(n.bind(n,9974)),"@site/versioned_docs/version-v0.5.x/faq.md",9974],fcc13998:[()=>n.e(1087).then(n.bind(n,7534)),"@site/versioned_docs/version-v0.2.x/contributing.md",7534],ff86e818:[()=>n.e(601).then(n.t.bind(n,1822,19)),"~docs/default/version-v-0-6-x-metadata-prop-c58.json",1822]};function c(e){let{error:t,retry:n,pastDelay:a}=e;return t?r.createElement("div",{style:{textAlign:"center",color:"#fff",backgroundColor:"#fa383e",borderColor:"#fa383e",borderStyle:"solid",borderRadius:"0.25rem",borderWidth:"1px",boxSizing:"border-box",display:"block",padding:"1rem",flex:"0 0 50%",marginLeft:"25%",marginRight:"25%",marginTop:"5rem",maxWidth:"50%",width:"100%"}},r.createElement("p",null,String(t)),r.createElement("div",null,r.createElement("button",{type:"button",onClick:n},"Retry"))):a?r.createElement("div",{style:{display:"flex",justifyContent:"center",alignItems:"center",height:"100vh"}},r.createElement("svg",{id:"loader",style:{width:128,height:110,position:"absolute",top:"calc(100vh - 64%)"},viewBox:"0 0 45 45",xmlns:"http://www.w3.org/2000/svg",stroke:"#61dafb"},r.createElement("g",{fill:"none",fillRule:"evenodd",transform:"translate(1 1)",strokeWidth:"2"},r.createElement("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0"},r.createElement("animate",{attributeName:"r",begin:"1.5s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-opacity",begin:"1.5s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-width",begin:"1.5s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})),r.createElement("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0"},r.createElement("animate",{attributeName:"r",begin:"3s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-opacity",begin:"3s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-width",begin:"3s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})),r.createElement("circle",{cx:"22",cy:"22",r:"8"},r.createElement("animate",{attributeName:"r",begin:"0s",dur:"1.5s",values:"6;1;2;3;4;5;6",calcMode:"linear",repeatCount:"indefinite"}))))):null}var u=n(9670),d=n(226);function p(e,t){if("*"===e)return i()({loading:c,loader:()=>n.e(4972).then(n.bind(n,4972)),modules:["@theme/NotFound"],webpack:()=>[4972],render(e,t){const n=e.default;return r.createElement(d.z,{value:{plugin:{name:"native",id:"default"}}},r.createElement(n,t))}});const o=l[`${e}-${t}`],p={},f=[],m=[],h=(0,u.Z)(o);return Object.entries(h).forEach((e=>{let[t,n]=e;const r=s[n];r&&(p[t]=r[0],f.push(r[1]),m.push(r[2]))})),i().Map({loading:c,loader:p,modules:f,webpack:()=>m,render(t,n){const i=JSON.parse(JSON.stringify(o));Object.entries(t).forEach((t=>{let[n,r]=t;const a=r.default;if(!a)throw new Error(`The page component at ${e} doesn't have a default export. This makes it impossible to render anything. Consider default-exporting a React component.`);"object"!=typeof a&&"function"!=typeof a||Object.keys(r).filter((e=>"default"!==e)).forEach((e=>{a[e]=r[e]}));let o=i;const l=n.split(".");l.slice(0,-1).forEach((e=>{o=o[e]})),o[l[l.length-1]]=a}));const l=i.__comp;delete i.__comp;const s=i.__context;return delete i.__context,r.createElement(d.z,{value:s},r.createElement(l,(0,a.Z)({},i,n)))}})}const f=[{path:"/copacetic/website/next",component:p("/copacetic/website/next","d7c"),routes:[{path:"/copacetic/website/next",component:p("/copacetic/website/next","e13"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/best-practices",component:p("/copacetic/website/next/best-practices","d9c"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/code-of-conduct",component:p("/copacetic/website/next/code-of-conduct","c0e"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/contributing",component:p("/copacetic/website/next/contributing","08c"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/custom-address",component:p("/copacetic/website/next/custom-address","437"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/design",component:p("/copacetic/website/next/design","623"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/development-tips",component:p("/copacetic/website/next/development-tips","868"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/faq",component:p("/copacetic/website/next/faq","93b"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/github-action",component:p("/copacetic/website/next/github-action","5b0"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/installation",component:p("/copacetic/website/next/installation","896"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/maintainer-guidelines",component:p("/copacetic/website/next/maintainer-guidelines","619"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/output",component:p("/copacetic/website/next/output","f6d"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/quick-start",component:p("/copacetic/website/next/quick-start","d17"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/release",component:p("/copacetic/website/next/release","d24"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/scanner-plugins",component:p("/copacetic/website/next/scanner-plugins","390"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/troubleshooting",component:p("/copacetic/website/next/troubleshooting","eda"),exact:!0,sidebar:"sidebar"}]},{path:"/copacetic/website/v0.1.x",component:p("/copacetic/website/v0.1.x","063"),routes:[{path:"/copacetic/website/v0.1.x",component:p("/copacetic/website/v0.1.x","a63"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.1.x/code-of-conduct",component:p("/copacetic/website/v0.1.x/code-of-conduct","88f"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.1.x/contributing",component:p("/copacetic/website/v0.1.x/contributing","64c"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.1.x/design",component:p("/copacetic/website/v0.1.x/design","85e"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.1.x/faq",component:p("/copacetic/website/v0.1.x/faq","162"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.1.x/installation",component:p("/copacetic/website/v0.1.x/installation","e86"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.1.x/quick-start",component:p("/copacetic/website/v0.1.x/quick-start","b29"),exact:!0,sidebar:"sidebar"}]},{path:"/copacetic/website/v0.2.x",component:p("/copacetic/website/v0.2.x","e48"),routes:[{path:"/copacetic/website/v0.2.x",component:p("/copacetic/website/v0.2.x","881"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.2.x/code-of-conduct",component:p("/copacetic/website/v0.2.x/code-of-conduct","f68"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.2.x/contributing",component:p("/copacetic/website/v0.2.x/contributing","ee0"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.2.x/design",component:p("/copacetic/website/v0.2.x/design","f57"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.2.x/faq",component:p("/copacetic/website/v0.2.x/faq","49f"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.2.x/installation",component:p("/copacetic/website/v0.2.x/installation","874"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.2.x/quick-start",component:p("/copacetic/website/v0.2.x/quick-start","5f6"),exact:!0,sidebar:"sidebar"}]},{path:"/copacetic/website/v0.3.x",component:p("/copacetic/website/v0.3.x","ec1"),routes:[{path:"/copacetic/website/v0.3.x",component:p("/copacetic/website/v0.3.x","265"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.3.x/code-of-conduct",component:p("/copacetic/website/v0.3.x/code-of-conduct","729"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.3.x/contributing",component:p("/copacetic/website/v0.3.x/contributing","47d"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.3.x/design",component:p("/copacetic/website/v0.3.x/design","7b5"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.3.x/faq",component:p("/copacetic/website/v0.3.x/faq","8a7"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.3.x/installation",component:p("/copacetic/website/v0.3.x/installation","c8d"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.3.x/quick-start",component:p("/copacetic/website/v0.3.x/quick-start","0e0"),exact:!0,sidebar:"sidebar"}]},{path:"/copacetic/website/v0.4.x",component:p("/copacetic/website/v0.4.x","c9b"),routes:[{path:"/copacetic/website/v0.4.x",component:p("/copacetic/website/v0.4.x","c77"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.4.x/code-of-conduct",component:p("/copacetic/website/v0.4.x/code-of-conduct","f71"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.4.x/contributing",component:p("/copacetic/website/v0.4.x/contributing","cf7"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.4.x/design",component:p("/copacetic/website/v0.4.x/design","800"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.4.x/faq",component:p("/copacetic/website/v0.4.x/faq","951"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.4.x/github-action",component:p("/copacetic/website/v0.4.x/github-action","608"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.4.x/installation",component:p("/copacetic/website/v0.4.x/installation","f2c"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.4.x/quick-start",component:p("/copacetic/website/v0.4.x/quick-start","d0f"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.4.x/release",component:p("/copacetic/website/v0.4.x/release","b08"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.4.x/troubleshooting",component:p("/copacetic/website/v0.4.x/troubleshooting","61e"),exact:!0,sidebar:"sidebar"}]},{path:"/copacetic/website/v0.5.x",component:p("/copacetic/website/v0.5.x","429"),routes:[{path:"/copacetic/website/v0.5.x",component:p("/copacetic/website/v0.5.x","de3"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/code-of-conduct",component:p("/copacetic/website/v0.5.x/code-of-conduct","369"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/contributing",component:p("/copacetic/website/v0.5.x/contributing","22c"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/design",component:p("/copacetic/website/v0.5.x/design","87e"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/faq",component:p("/copacetic/website/v0.5.x/faq","b07"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/github-action",component:p("/copacetic/website/v0.5.x/github-action","5c5"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/installation",component:p("/copacetic/website/v0.5.x/installation","8b9"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/output",component:p("/copacetic/website/v0.5.x/output","9d0"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/quick-start",component:p("/copacetic/website/v0.5.x/quick-start","e12"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/release",component:p("/copacetic/website/v0.5.x/release","b3f"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/scanner-plugins",component:p("/copacetic/website/v0.5.x/scanner-plugins","a63"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/troubleshooting",component:p("/copacetic/website/v0.5.x/troubleshooting","3ce"),exact:!0,sidebar:"sidebar"}]},{path:"/copacetic/website/",component:p("/copacetic/website/","581"),routes:[{path:"/copacetic/website/",component:p("/copacetic/website/","c19"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/best-practices",component:p("/copacetic/website/best-practices","79d"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/code-of-conduct",component:p("/copacetic/website/code-of-conduct","833"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/contributing",component:p("/copacetic/website/contributing","d35"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/custom-address",component:p("/copacetic/website/custom-address","c6b"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/design",component:p("/copacetic/website/design","b0d"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/development-tips",component:p("/copacetic/website/development-tips","18c"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/faq",component:p("/copacetic/website/faq","b74"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/github-action",component:p("/copacetic/website/github-action","8d1"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/installation",component:p("/copacetic/website/installation","90f"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/maintainer-guidelines",component:p("/copacetic/website/maintainer-guidelines","28d"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/output",component:p("/copacetic/website/output","924"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/quick-start",component:p("/copacetic/website/quick-start","c5e"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/release",component:p("/copacetic/website/release","06e"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/scanner-plugins",component:p("/copacetic/website/scanner-plugins","822"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/troubleshooting",component:p("/copacetic/website/troubleshooting","b63"),exact:!0,sidebar:"sidebar"}]},{path:"*",component:p("*")}]},8934:(e,t,n)=>{"use strict";n.d(t,{_:()=>a,t:()=>o});var r=n(7294);const a=r.createContext(!1);function o(e){let{children:t}=e;const[n,o]=(0,r.useState)(!1);return(0,r.useEffect)((()=>{o(!0)}),[]),r.createElement(a.Provider,{value:n},t)}},7221:(e,t,n)=>{"use strict";var r=n(7294),a=n(3935),o=n(3727),i=n(405),l=n(412);const s=[n(6657),n(2497),n(3310),n(8320),n(2295)];var c=n(723),u=n(6550),d=n(8790);function p(e){let{children:t}=e;return r.createElement(r.Fragment,null,t)}var f=n(7462),m=n(5742),h=n(2263),g=n(4996),b=n(6668),v=n(1944),y=n(4711),w=n(9727),k=n(3320),E=n(8780),x=n(197);function S(){const{i18n:{defaultLocale:e,localeConfigs:t}}=(0,h.Z)(),n=(0,y.l)();return r.createElement(m.Z,null,Object.entries(t).map((e=>{let[t,{htmlLang:a}]=e;return r.createElement("link",{key:t,rel:"alternate",href:n.createUrl({locale:t,fullyQualified:!0}),hrefLang:a})})),r.createElement("link",{rel:"alternate",href:n.createUrl({locale:e,fullyQualified:!0}),hrefLang:"x-default"}))}function _(e){let{permalink:t}=e;const{siteConfig:{url:n}}=(0,h.Z)(),a=function(){const{siteConfig:{url:e,baseUrl:t,trailingSlash:n}}=(0,h.Z)(),{pathname:r}=(0,u.TH)();return e+(0,E.applyTrailingSlash)((0,g.Z)(r),{trailingSlash:n,baseUrl:t})}(),o=t?`${n}${t}`:a;return r.createElement(m.Z,null,r.createElement("meta",{property:"og:url",content:o}),r.createElement("link",{rel:"canonical",href:o}))}function C(){const{i18n:{currentLocale:e}}=(0,h.Z)(),{metadata:t,image:n}=(0,b.L)();return r.createElement(r.Fragment,null,r.createElement(m.Z,null,r.createElement("meta",{name:"twitter:card",content:"summary_large_image"}),r.createElement("body",{className:w.h})),n&&r.createElement(v.d,{image:n}),r.createElement(_,null),r.createElement(S,null),r.createElement(x.Z,{tag:k.HX,locale:e}),r.createElement(m.Z,null,t.map(((e,t)=>r.createElement("meta",(0,f.Z)({key:t},e))))))}const T=new Map;function A(e){if(T.has(e.pathname))return{...e,pathname:T.get(e.pathname)};if((0,d.f)(c.Z,e.pathname).some((e=>{let{route:t}=e;return!0===t.exact})))return T.set(e.pathname,e.pathname),e;const t=e.pathname.trim().replace(/(?:\/index)?\.html$/,"")||"/";return T.set(e.pathname,t),{...e,pathname:t}}var L=n(8934),R=n(8940);function N(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r<t;r++)n[r-1]=arguments[r];const a=s.map((t=>(t.default?.[e]??t[e])?.(...n)));return()=>a.forEach((e=>e?.()))}const O=function(e){let{children:t,location:n,previousLocation:a}=e;return(0,r.useLayoutEffect)((()=>{a!==n&&(!function(e){let{location:t,previousLocation:n}=e;if(!n)return;const r=t.pathname===n.pathname,a=t.hash===n.hash,o=t.search===n.search;if(r&&a&&!o)return;const{hash:i}=t;if(i){const e=decodeURIComponent(i.substring(1));document.getElementById(e)?.scrollIntoView()}else window.scrollTo(0,0)}({location:n,previousLocation:a}),N("onRouteDidUpdate",{previousLocation:a,location:n}))}),[a,n]),t};function P(e){const t=Array.from(new Set([e,decodeURI(e)])).map((e=>(0,d.f)(c.Z,e))).flat();return Promise.all(t.map((e=>e.route.component.preload?.())))}class I extends r.Component{previousLocation;routeUpdateCleanupCb;constructor(e){super(e),this.previousLocation=null,this.routeUpdateCleanupCb=l.Z.canUseDOM?N("onRouteUpdate",{previousLocation:null,location:this.props.location}):()=>{},this.state={nextRouteHasLoaded:!0}}shouldComponentUpdate(e,t){if(e.location===this.props.location)return t.nextRouteHasLoaded;const n=e.location;return this.previousLocation=this.props.location,this.setState({nextRouteHasLoaded:!1}),this.routeUpdateCleanupCb=N("onRouteUpdate",{previousLocation:this.previousLocation,location:n}),P(n.pathname).then((()=>{this.routeUpdateCleanupCb(),this.setState({nextRouteHasLoaded:!0})})).catch((e=>{console.warn(e),window.location.reload()})),!1}render(){const{children:e,location:t}=this.props;return r.createElement(O,{previousLocation:this.previousLocation,location:t},r.createElement(u.AW,{location:t,render:()=>e}))}}const D=I,M="__docusaurus-base-url-issue-banner-container",F="__docusaurus-base-url-issue-banner-suggestion-container",B="__DOCUSAURUS_INSERT_BASEURL_BANNER";function j(e){return`\nwindow['${B}'] = true;\n\ndocument.addEventListener('DOMContentLoaded', maybeInsertBanner);\n\nfunction maybeInsertBanner() {\n var shouldInsert = window['${B}'];\n shouldInsert && insertBanner();\n}\n\nfunction insertBanner() {\n var bannerContainer = document.getElementById('${M}');\n if (!bannerContainer) {\n return;\n }\n var bannerHtml = ${JSON.stringify(function(e){return`\n<div id="__docusaurus-base-url-issue-banner" style="border: thick solid red; background-color: rgb(255, 230, 179); margin: 20px; padding: 20px; font-size: 20px;">\n <p style="font-weight: bold; font-size: 30px;">Your Docusaurus site did not load properly.</p>\n <p>A very common reason is a wrong site <a href="https://docusaurus.io/docs/docusaurus.config.js/#baseUrl" style="font-weight: bold;">baseUrl configuration</a>.</p>\n <p>Current configured baseUrl = <span style="font-weight: bold; color: red;">${e}</span> ${"/"===e?" (default value)":""}</p>\n <p>We suggest trying baseUrl = <span id="${F}" style="font-weight: bold; color: green;"></span></p>\n</div>\n`}(e)).replace(/</g,"\\<")};\n bannerContainer.innerHTML = bannerHtml;\n var suggestionContainer = document.getElementById('${F}');\n var actualHomePagePath = window.location.pathname;\n var suggestedBaseUrl = actualHomePagePath.substr(-1) === '/'\n ? actualHomePagePath\n : actualHomePagePath + '/';\n suggestionContainer.innerHTML = suggestedBaseUrl;\n}\n`}function z(){const{siteConfig:{baseUrl:e}}=(0,h.Z)();return(0,r.useLayoutEffect)((()=>{window[B]=!1}),[]),r.createElement(r.Fragment,null,!l.Z.canUseDOM&&r.createElement(m.Z,null,r.createElement("script",null,j(e))),r.createElement("div",{id:M}))}function U(){const{siteConfig:{baseUrl:e,baseUrlIssueBanner:t}}=(0,h.Z)(),{pathname:n}=(0,u.TH)();return t&&n===e?r.createElement(z,null):null}function q(){const{siteConfig:{favicon:e,title:t,noIndex:n},i18n:{currentLocale:a,localeConfigs:o}}=(0,h.Z)(),i=(0,g.Z)(e),{htmlLang:l,direction:s}=o[a];return r.createElement(m.Z,null,r.createElement("html",{lang:l,dir:s}),r.createElement("title",null,t),r.createElement("meta",{property:"og:title",content:t}),r.createElement("meta",{name:"viewport",content:"width=device-width, initial-scale=1.0"}),n&&r.createElement("meta",{name:"robots",content:"noindex, nofollow"}),e&&r.createElement("link",{rel:"icon",href:i}))}var $=n(4763),H=n(2389);function G(){const e=(0,H.Z)();return r.createElement(m.Z,null,r.createElement("html",{"data-has-hydrated":e}))}function Z(){const e=(0,d.H)(c.Z),t=(0,u.TH)();return r.createElement($.Z,null,r.createElement(R.M,null,r.createElement(L.t,null,r.createElement(p,null,r.createElement(q,null),r.createElement(C,null),r.createElement(U,null),r.createElement(D,{location:A(t)},e)),r.createElement(G,null))))}var V=n(6887);const W=function(e){try{return document.createElement("link").relList.supports(e)}catch{return!1}}("prefetch")?function(e){return new Promise(((t,n)=>{if("undefined"==typeof document)return void n();const r=document.createElement("link");r.setAttribute("rel","prefetch"),r.setAttribute("href",e),r.onload=()=>t(),r.onerror=()=>n();(document.getElementsByTagName("head")[0]??document.getElementsByName("script")[0]?.parentNode)?.appendChild(r)}))}:function(e){return new Promise(((t,n)=>{const r=new XMLHttpRequest;r.open("GET",e,!0),r.withCredentials=!0,r.onload=()=>{200===r.status?t():n()},r.send(null)}))};var Y=n(9670);const K=new Set,Q=new Set,X=()=>navigator.connection?.effectiveType.includes("2g")||navigator.connection?.saveData,J={prefetch(e){if(!(e=>!X()&&!Q.has(e)&&!K.has(e))(e))return!1;K.add(e);const t=(0,d.f)(c.Z,e).flatMap((e=>{return t=e.route.path,Object.entries(V).filter((e=>{let[n]=e;return n.replace(/-[^-]+$/,"")===t})).flatMap((e=>{let[,t]=e;return Object.values((0,Y.Z)(t))}));var t}));return Promise.all(t.map((e=>{const t=n.gca(e);return t&&!t.includes("undefined")?W(t).catch((()=>{})):Promise.resolve()})))},preload:e=>!!(e=>!X()&&!Q.has(e))(e)&&(Q.add(e),P(e))},ee=Object.freeze(J);if(l.Z.canUseDOM){window.docusaurus=ee;const e=a.hydrate;P(window.location.pathname).then((()=>{e(r.createElement(i.B6,null,r.createElement(o.VK,null,r.createElement(Z,null))),document.getElementById("__docusaurus"))}))}},8940:(e,t,n)=>{"use strict";n.d(t,{_:()=>u,M:()=>d});var r=n(7294),a=n(6809);const o=JSON.parse('{"docusaurus-plugin-google-gtag":{"default":{"trackingID":["G-3RC20QPKNS"],"anonymizeIP":true,"id":"default"}},"docusaurus-plugin-content-docs":{"default":{"path":"/copacetic/website/","versions":[{"name":"current","label":"Next","isLast":false,"path":"/copacetic/website/next","mainDocId":"introduction","docs":[{"id":"best-practices","path":"/copacetic/website/next/best-practices","sidebar":"sidebar"},{"id":"code-of-conduct","path":"/copacetic/website/next/code-of-conduct","sidebar":"sidebar"},{"id":"contributing","path":"/copacetic/website/next/contributing","sidebar":"sidebar"},{"id":"custom-address","path":"/copacetic/website/next/custom-address","sidebar":"sidebar"},{"id":"design","path":"/copacetic/website/next/design","sidebar":"sidebar"},{"id":"development-tips","path":"/copacetic/website/next/development-tips","sidebar":"sidebar"},{"id":"faq","path":"/copacetic/website/next/faq","sidebar":"sidebar"},{"id":"github-action","path":"/copacetic/website/next/github-action","sidebar":"sidebar"},{"id":"installation","path":"/copacetic/website/next/installation","sidebar":"sidebar"},{"id":"introduction","path":"/copacetic/website/next/","sidebar":"sidebar"},{"id":"maintainer-guidelines","path":"/copacetic/website/next/maintainer-guidelines","sidebar":"sidebar"},{"id":"output","path":"/copacetic/website/next/output","sidebar":"sidebar"},{"id":"quick-start","path":"/copacetic/website/next/quick-start","sidebar":"sidebar"},{"id":"release","path":"/copacetic/website/next/release","sidebar":"sidebar"},{"id":"scanner-plugins","path":"/copacetic/website/next/scanner-plugins","sidebar":"sidebar"},{"id":"troubleshooting","path":"/copacetic/website/next/troubleshooting","sidebar":"sidebar"}],"draftIds":[],"sidebars":{"sidebar":{"link":{"path":"/copacetic/website/next/","label":"introduction"}}}},{"name":"v0.6.x","label":"v0.6.x","isLast":true,"path":"/copacetic/website/","mainDocId":"introduction","docs":[{"id":"best-practices","path":"/copacetic/website/best-practices","sidebar":"sidebar"},{"id":"code-of-conduct","path":"/copacetic/website/code-of-conduct","sidebar":"sidebar"},{"id":"contributing","path":"/copacetic/website/contributing","sidebar":"sidebar"},{"id":"custom-address","path":"/copacetic/website/custom-address","sidebar":"sidebar"},{"id":"design","path":"/copacetic/website/design","sidebar":"sidebar"},{"id":"development-tips","path":"/copacetic/website/development-tips","sidebar":"sidebar"},{"id":"faq","path":"/copacetic/website/faq","sidebar":"sidebar"},{"id":"github-action","path":"/copacetic/website/github-action","sidebar":"sidebar"},{"id":"installation","path":"/copacetic/website/installation","sidebar":"sidebar"},{"id":"introduction","path":"/copacetic/website/","sidebar":"sidebar"},{"id":"maintainer-guidelines","path":"/copacetic/website/maintainer-guidelines","sidebar":"sidebar"},{"id":"output","path":"/copacetic/website/output","sidebar":"sidebar"},{"id":"quick-start","path":"/copacetic/website/quick-start","sidebar":"sidebar"},{"id":"release","path":"/copacetic/website/release","sidebar":"sidebar"},{"id":"scanner-plugins","path":"/copacetic/website/scanner-plugins","sidebar":"sidebar"},{"id":"troubleshooting","path":"/copacetic/website/troubleshooting","sidebar":"sidebar"}],"draftIds":[],"sidebars":{"sidebar":{"link":{"path":"/copacetic/website/","label":"introduction"}}}},{"name":"v0.5.x","label":"v0.5.x","isLast":false,"path":"/copacetic/website/v0.5.x","mainDocId":"introduction","docs":[{"id":"code-of-conduct","path":"/copacetic/website/v0.5.x/code-of-conduct","sidebar":"sidebar"},{"id":"contributing","path":"/copacetic/website/v0.5.x/contributing","sidebar":"sidebar"},{"id":"design","path":"/copacetic/website/v0.5.x/design","sidebar":"sidebar"},{"id":"faq","path":"/copacetic/website/v0.5.x/faq","sidebar":"sidebar"},{"id":"github-action","path":"/copacetic/website/v0.5.x/github-action","sidebar":"sidebar"},{"id":"installation","path":"/copacetic/website/v0.5.x/installation","sidebar":"sidebar"},{"id":"introduction","path":"/copacetic/website/v0.5.x/","sidebar":"sidebar"},{"id":"output","path":"/copacetic/website/v0.5.x/output","sidebar":"sidebar"},{"id":"quick-start","path":"/copacetic/website/v0.5.x/quick-start","sidebar":"sidebar"},{"id":"release","path":"/copacetic/website/v0.5.x/release","sidebar":"sidebar"},{"id":"scanner-plugins","path":"/copacetic/website/v0.5.x/scanner-plugins","sidebar":"sidebar"},{"id":"troubleshooting","path":"/copacetic/website/v0.5.x/troubleshooting","sidebar":"sidebar"}],"draftIds":[],"sidebars":{"sidebar":{"link":{"path":"/copacetic/website/v0.5.x/","label":"introduction"}}}},{"name":"v0.4.x","label":"v0.4.x","isLast":false,"path":"/copacetic/website/v0.4.x","mainDocId":"introduction","docs":[{"id":"code-of-conduct","path":"/copacetic/website/v0.4.x/code-of-conduct","sidebar":"sidebar"},{"id":"contributing","path":"/copacetic/website/v0.4.x/contributing","sidebar":"sidebar"},{"id":"design","path":"/copacetic/website/v0.4.x/design","sidebar":"sidebar"},{"id":"faq","path":"/copacetic/website/v0.4.x/faq","sidebar":"sidebar"},{"id":"github-action","path":"/copacetic/website/v0.4.x/github-action","sidebar":"sidebar"},{"id":"installation","path":"/copacetic/website/v0.4.x/installation","sidebar":"sidebar"},{"id":"introduction","path":"/copacetic/website/v0.4.x/","sidebar":"sidebar"},{"id":"quick-start","path":"/copacetic/website/v0.4.x/quick-start","sidebar":"sidebar"},{"id":"release","path":"/copacetic/website/v0.4.x/release","sidebar":"sidebar"},{"id":"troubleshooting","path":"/copacetic/website/v0.4.x/troubleshooting","sidebar":"sidebar"}],"draftIds":[],"sidebars":{"sidebar":{"link":{"path":"/copacetic/website/v0.4.x/","label":"introduction"}}}},{"name":"v0.3.x","label":"v0.3.x","isLast":false,"path":"/copacetic/website/v0.3.x","mainDocId":"introduction","docs":[{"id":"code-of-conduct","path":"/copacetic/website/v0.3.x/code-of-conduct","sidebar":"sidebar"},{"id":"contributing","path":"/copacetic/website/v0.3.x/contributing","sidebar":"sidebar"},{"id":"design","path":"/copacetic/website/v0.3.x/design","sidebar":"sidebar"},{"id":"faq","path":"/copacetic/website/v0.3.x/faq","sidebar":"sidebar"},{"id":"installation","path":"/copacetic/website/v0.3.x/installation","sidebar":"sidebar"},{"id":"introduction","path":"/copacetic/website/v0.3.x/","sidebar":"sidebar"},{"id":"quick-start","path":"/copacetic/website/v0.3.x/quick-start","sidebar":"sidebar"}],"draftIds":[],"sidebars":{"sidebar":{"link":{"path":"/copacetic/website/v0.3.x/","label":"introduction"}}}},{"name":"v0.2.x","label":"v0.2.x","isLast":false,"path":"/copacetic/website/v0.2.x","mainDocId":"introduction","docs":[{"id":"code-of-conduct","path":"/copacetic/website/v0.2.x/code-of-conduct","sidebar":"sidebar"},{"id":"contributing","path":"/copacetic/website/v0.2.x/contributing","sidebar":"sidebar"},{"id":"design","path":"/copacetic/website/v0.2.x/design","sidebar":"sidebar"},{"id":"faq","path":"/copacetic/website/v0.2.x/faq","sidebar":"sidebar"},{"id":"installation","path":"/copacetic/website/v0.2.x/installation","sidebar":"sidebar"},{"id":"introduction","path":"/copacetic/website/v0.2.x/","sidebar":"sidebar"},{"id":"quick-start","path":"/copacetic/website/v0.2.x/quick-start","sidebar":"sidebar"}],"draftIds":[],"sidebars":{"sidebar":{"link":{"path":"/copacetic/website/v0.2.x/","label":"introduction"}}}},{"name":"v0.1.x","label":"v0.1.x","isLast":false,"path":"/copacetic/website/v0.1.x","mainDocId":"introduction","docs":[{"id":"code-of-conduct","path":"/copacetic/website/v0.1.x/code-of-conduct","sidebar":"sidebar"},{"id":"contributing","path":"/copacetic/website/v0.1.x/contributing","sidebar":"sidebar"},{"id":"design","path":"/copacetic/website/v0.1.x/design","sidebar":"sidebar"},{"id":"faq","path":"/copacetic/website/v0.1.x/faq","sidebar":"sidebar"},{"id":"installation","path":"/copacetic/website/v0.1.x/installation","sidebar":"sidebar"},{"id":"introduction","path":"/copacetic/website/v0.1.x/","sidebar":"sidebar"},{"id":"quick-start","path":"/copacetic/website/v0.1.x/quick-start","sidebar":"sidebar"}],"draftIds":[],"sidebars":{"sidebar":{"link":{"path":"/copacetic/website/v0.1.x/","label":"introduction"}}}}],"breadcrumbs":true}}}'),i=JSON.parse('{"defaultLocale":"en","locales":["en"],"path":"i18n","currentLocale":"en","localeConfigs":{"en":{"label":"English","direction":"ltr","htmlLang":"en","calendar":"gregory","path":"en"}}}');var l=n(7529);const s=JSON.parse('{"docusaurusVersion":"2.4.3","siteVersion":"0.0.0","pluginVersions":{"docusaurus-plugin-content-docs":{"type":"package","name":"@docusaurus/plugin-content-docs","version":"2.4.3"},"docusaurus-plugin-content-pages":{"type":"package","name":"@docusaurus/plugin-content-pages","version":"2.4.3"},"docusaurus-plugin-google-gtag":{"type":"package","name":"@docusaurus/plugin-google-gtag","version":"2.4.3"},"docusaurus-plugin-sitemap":{"type":"package","name":"@docusaurus/plugin-sitemap","version":"2.4.3"},"docusaurus-theme-classic":{"type":"package","name":"@docusaurus/theme-classic","version":"2.4.3"}}}'),c={siteConfig:a.Z,siteMetadata:s,globalData:o,i18n:i,codeTranslations:l},u=r.createContext(c);function d(e){let{children:t}=e;return r.createElement(u.Provider,{value:c},t)}},4763:(e,t,n)=>{"use strict";n.d(t,{Z:()=>p});var r=n(7294),a=n(412),o=n(5742),i=n(8780),l=n(7961);function s(e){let{error:t,tryAgain:n}=e;return r.createElement("div",{style:{display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"flex-start",minHeight:"100vh",width:"100%",maxWidth:"80ch",fontSize:"20px",margin:"0 auto",padding:"1rem"}},r.createElement("h1",{style:{fontSize:"3rem"}},"This page crashed"),r.createElement("button",{type:"button",onClick:n,style:{margin:"1rem 0",fontSize:"2rem",cursor:"pointer",borderRadius:20,padding:"1rem"}},"Try again"),r.createElement(c,{error:t}))}function c(e){let{error:t}=e;const n=(0,i.getErrorCausalChain)(t).map((e=>e.message)).join("\n\nCause:\n");return r.createElement("p",{style:{whiteSpace:"pre-wrap"}},n)}function u(e){let{error:t,tryAgain:n}=e;return r.createElement(p,{fallback:()=>r.createElement(s,{error:t,tryAgain:n})},r.createElement(o.Z,null,r.createElement("title",null,"Page Error")),r.createElement(l.Z,null,r.createElement(s,{error:t,tryAgain:n})))}const d=e=>r.createElement(u,e);class p extends r.Component{constructor(e){super(e),this.state={error:null}}componentDidCatch(e){a.Z.canUseDOM&&this.setState({error:e})}render(){const{children:e}=this.props,{error:t}=this.state;if(t){const e={error:t,tryAgain:()=>this.setState({error:null})};return(this.props.fallback??d)(e)}return e??null}}},412:(e,t,n)=>{"use strict";n.d(t,{Z:()=>a});const r="undefined"!=typeof window&&"document"in window&&"createElement"in window.document,a={canUseDOM:r,canUseEventListeners:r&&("addEventListener"in window||"attachEvent"in window),canUseIntersectionObserver:r&&"IntersectionObserver"in window,canUseViewport:r&&"screen"in window}},5742:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(405);function o(e){return r.createElement(a.ql,e)}},9960:(e,t,n)=>{"use strict";n.d(t,{Z:()=>f});var r=n(7462),a=n(7294),o=n(3727),i=n(8780),l=n(2263),s=n(3919),c=n(412);const u=a.createContext({collectLink:()=>{}});var d=n(4996);function p(e,t){let{isNavLink:n,to:p,href:f,activeClassName:m,isActive:h,"data-noBrokenLinkCheck":g,autoAddBaseUrl:b=!0,...v}=e;const{siteConfig:{trailingSlash:y,baseUrl:w}}=(0,l.Z)(),{withBaseUrl:k}=(0,d.C)(),E=(0,a.useContext)(u),x=(0,a.useRef)(null);(0,a.useImperativeHandle)(t,(()=>x.current));const S=p||f;const _=(0,s.Z)(S),C=S?.replace("pathname://","");let T=void 0!==C?(A=C,b&&(e=>e.startsWith("/"))(A)?k(A):A):void 0;var A;T&&_&&(T=(0,i.applyTrailingSlash)(T,{trailingSlash:y,baseUrl:w}));const L=(0,a.useRef)(!1),R=n?o.OL:o.rU,N=c.Z.canUseIntersectionObserver,O=(0,a.useRef)(),P=()=>{L.current||null==T||(window.docusaurus.preload(T),L.current=!0)};(0,a.useEffect)((()=>(!N&&_&&null!=T&&window.docusaurus.prefetch(T),()=>{N&&O.current&&O.current.disconnect()})),[O,T,N,_]);const I=T?.startsWith("#")??!1,D=!T||!_||I;return D||g||E.collectLink(T),D?a.createElement("a",(0,r.Z)({ref:x,href:T},S&&!_&&{target:"_blank",rel:"noopener noreferrer"},v)):a.createElement(R,(0,r.Z)({},v,{onMouseEnter:P,onTouchStart:P,innerRef:e=>{x.current=e,N&&e&&_&&(O.current=new window.IntersectionObserver((t=>{t.forEach((t=>{e===t.target&&(t.isIntersecting||t.intersectionRatio>0)&&(O.current.unobserve(e),O.current.disconnect(),null!=T&&window.docusaurus.prefetch(T))}))})),O.current.observe(e))},to:T},n&&{isActive:h,activeClassName:m}))}const f=a.forwardRef(p)},1875:(e,t,n)=>{"use strict";n.d(t,{Z:()=>r});const r=()=>null},5999:(e,t,n)=>{"use strict";n.d(t,{Z:()=>s,I:()=>l});var r=n(7294);function a(e,t){const n=e.split(/(\{\w+\})/).map(((e,n)=>{if(n%2==1){const n=t?.[e.slice(1,-1)];if(void 0!==n)return n}return e}));return n.some((e=>(0,r.isValidElement)(e)))?n.map(((e,t)=>(0,r.isValidElement)(e)?r.cloneElement(e,{key:t}):e)).filter((e=>""!==e)):n.join("")}var o=n(7529);function i(e){let{id:t,message:n}=e;if(void 0===t&&void 0===n)throw new Error("Docusaurus translation declarations must have at least a translation id or a default translation message");return o[t??n]??n??t}function l(e,t){let{message:n,id:r}=e;return a(i({message:n,id:r}),t)}function s(e){let{children:t,id:n,values:o}=e;if(t&&"string"!=typeof t)throw console.warn("Illegal <Translate> children",t),new Error("The Docusaurus <Translate> component only accept simple string values");const l=i({message:t,id:n});return r.createElement(r.Fragment,null,a(l,o))}},9935:(e,t,n)=>{"use strict";n.d(t,{m:()=>r});const r="default"},3919:(e,t,n)=>{"use strict";function r(e){return/^(?:\w*:|\/\/)/.test(e)}function a(e){return void 0!==e&&!r(e)}n.d(t,{Z:()=>a,b:()=>r})},4996:(e,t,n)=>{"use strict";n.d(t,{C:()=>i,Z:()=>l});var r=n(7294),a=n(2263),o=n(3919);function i(){const{siteConfig:{baseUrl:e,url:t}}=(0,a.Z)(),n=(0,r.useCallback)(((n,r)=>function(e,t,n,r){let{forcePrependBaseUrl:a=!1,absolute:i=!1}=void 0===r?{}:r;if(!n||n.startsWith("#")||(0,o.b)(n))return n;if(a)return t+n.replace(/^\//,"");if(n===t.replace(/\/$/,""))return t;const l=n.startsWith(t)?n:t+n.replace(/^\//,"");return i?e+l:l}(t,e,n,r)),[t,e]);return{withBaseUrl:n}}function l(e,t){void 0===t&&(t={});const{withBaseUrl:n}=i();return n(e,t)}},2263:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(8940);function o(){return(0,r.useContext)(a._)}},2389:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(8934);function o(){return(0,r.useContext)(a._)}},9670:(e,t,n)=>{"use strict";n.d(t,{Z:()=>r});function r(e){const t={};return function e(n,r){Object.entries(n).forEach((n=>{let[a,o]=n;const i=r?`${r}.${a}`:a;var l;"object"==typeof(l=o)&&l&&Object.keys(l).length>0?e(o,i):t[i]=o}))}(e),t}},226:(e,t,n)=>{"use strict";n.d(t,{_:()=>a,z:()=>o});var r=n(7294);const a=r.createContext(null);function o(e){let{children:t,value:n}=e;const o=r.useContext(a),i=(0,r.useMemo)((()=>function(e){let{parent:t,value:n}=e;if(!t){if(!n)throw new Error("Unexpected: no Docusaurus route context found");if(!("plugin"in n))throw new Error("Unexpected: Docusaurus topmost route context has no `plugin` attribute");return n}const r={...t.data,...n?.data};return{plugin:t.plugin,data:r}}({parent:o,value:n})),[o,n]);return r.createElement(a.Provider,{value:i},t)}},143:(e,t,n)=>{"use strict";n.d(t,{Iw:()=>h,gA:()=>p,_r:()=>u,Jo:()=>g,zh:()=>d,yW:()=>m,gB:()=>f});var r=n(6550),a=n(2263),o=n(9935);function i(e,t){void 0===t&&(t={});const n=function(){const{globalData:e}=(0,a.Z)();return e}()[e];if(!n&&t.failfast)throw new Error(`Docusaurus plugin global data not found for "${e}" plugin.`);return n}const l=e=>e.versions.find((e=>e.isLast));function s(e,t){const n=function(e,t){const n=l(e);return[...e.versions.filter((e=>e!==n)),n].find((e=>!!(0,r.LX)(t,{path:e.path,exact:!1,strict:!1})))}(e,t),a=n?.docs.find((e=>!!(0,r.LX)(t,{path:e.path,exact:!0,strict:!1})));return{activeVersion:n,activeDoc:a,alternateDocVersions:a?function(t){const n={};return e.versions.forEach((e=>{e.docs.forEach((r=>{r.id===t&&(n[e.name]=r)}))})),n}(a.id):{}}}const c={},u=()=>i("docusaurus-plugin-content-docs")??c,d=e=>function(e,t,n){void 0===t&&(t=o.m),void 0===n&&(n={});const r=i(e)?.[t];if(!r&&n.failfast)throw new Error(`Docusaurus plugin global data not found for "${e}" plugin with id "${t}".`);return r}("docusaurus-plugin-content-docs",e,{failfast:!0});function p(e){void 0===e&&(e={});const t=u(),{pathname:n}=(0,r.TH)();return function(e,t,n){void 0===n&&(n={});const a=Object.entries(e).sort(((e,t)=>t[1].path.localeCompare(e[1].path))).find((e=>{let[,n]=e;return!!(0,r.LX)(t,{path:n.path,exact:!1,strict:!1})})),o=a?{pluginId:a[0],pluginData:a[1]}:void 0;if(!o&&n.failfast)throw new Error(`Can't find active docs plugin for "${t}" pathname, while it was expected to be found. Maybe you tried to use a docs feature that can only be used on a docs-related page? Existing docs plugin paths are: ${Object.values(e).map((e=>e.path)).join(", ")}`);return o}(t,n,e)}function f(e){return d(e).versions}function m(e){const t=d(e);return l(t)}function h(e){const t=d(e),{pathname:n}=(0,r.TH)();return s(t,n)}function g(e){const t=d(e),{pathname:n}=(0,r.TH)();return function(e,t){const n=l(e);return{latestDocSuggestion:s(e,t).alternateDocVersions[n.name],latestVersionSuggestion:n}}(t,n)}},6657:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r={onRouteDidUpdate(e){let{location:t,previousLocation:n}=e;!n||t.pathname===n.pathname&&t.search===n.search&&t.hash===n.hash||setTimeout((()=>{window.gtag("event","page_view",{page_title:document.title,page_location:window.location.href,page_path:t.pathname+t.search+t.hash})}))}}},8320:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(4865),a=n.n(r);a().configure({showSpinner:!1});const o={onRouteUpdate(e){let{location:t,previousLocation:n}=e;if(n&&t.pathname!==n.pathname){const e=window.setTimeout((()=>{a().start()}),200);return()=>window.clearTimeout(e)}},onRouteDidUpdate(){a().done()}}},3310:(e,t,n)=>{"use strict";n.r(t);var r=n(1205),a=n(6809);!function(e){const{themeConfig:{prism:t}}=a.Z,{additionalLanguages:r}=t;globalThis.Prism=e,r.forEach((e=>{n(6726)(`./prism-${e}`)})),delete globalThis.Prism}(r.Z)},9471:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294);const a="iconExternalLink_nPIU";function o(e){let{width:t=13.5,height:n=13.5}=e;return r.createElement("svg",{width:t,height:n,"aria-hidden":"true",viewBox:"0 0 24 24",className:a},r.createElement("path",{fill:"currentColor",d:"M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"}))}},7961:(e,t,n)=>{"use strict";n.d(t,{Z:()=>mt});var r=n(7294),a=n(4334),o=n(4763),i=n(1944),l=n(7462),s=n(6550),c=n(5999),u=n(5936);const d="__docusaurus_skipToContent_fallback";function p(e){e.setAttribute("tabindex","-1"),e.focus(),e.removeAttribute("tabindex")}function f(){const e=(0,r.useRef)(null),{action:t}=(0,s.k6)(),n=(0,r.useCallback)((e=>{e.preventDefault();const t=document.querySelector("main:first-of-type")??document.getElementById(d);t&&p(t)}),[]);return(0,u.S)((n=>{let{location:r}=n;e.current&&!r.hash&&"PUSH"===t&&p(e.current)})),{containerRef:e,onClick:n}}const m=(0,c.I)({id:"theme.common.skipToMainContent",description:"The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation",message:"Skip to main content"});function h(e){const t=e.children??m,{containerRef:n,onClick:a}=f();return r.createElement("div",{ref:n,role:"region","aria-label":m},r.createElement("a",(0,l.Z)({},e,{href:`#${d}`,onClick:a}),t))}var g=n(5281),b=n(9727);const v="skipToContent_fXgn";function y(){return r.createElement(h,{className:v})}var w=n(6668),k=n(9689);function E(e){let{width:t=21,height:n=21,color:a="currentColor",strokeWidth:o=1.2,className:i,...s}=e;return r.createElement("svg",(0,l.Z)({viewBox:"0 0 15 15",width:t,height:n},s),r.createElement("g",{stroke:a,strokeWidth:o},r.createElement("path",{d:"M.75.75l13.5 13.5M14.25.75L.75 14.25"})))}const x="closeButton_CVFx";function S(e){return r.createElement("button",(0,l.Z)({type:"button","aria-label":(0,c.I)({id:"theme.AnnouncementBar.closeButtonAriaLabel",message:"Close",description:"The ARIA label for close button of announcement bar"})},e,{className:(0,a.Z)("clean-btn close",x,e.className)}),r.createElement(E,{width:14,height:14,strokeWidth:3.1}))}const _="content_knG7";function C(e){const{announcementBar:t}=(0,w.L)(),{content:n}=t;return r.createElement("div",(0,l.Z)({},e,{className:(0,a.Z)(_,e.className),dangerouslySetInnerHTML:{__html:n}}))}const T="announcementBar_mb4j",A="announcementBarPlaceholder_vyr4",L="announcementBarClose_gvF7",R="announcementBarContent_xLdY";function N(){const{announcementBar:e}=(0,w.L)(),{isActive:t,close:n}=(0,k.nT)();if(!t)return null;const{backgroundColor:a,textColor:o,isCloseable:i}=e;return r.createElement("div",{className:T,style:{backgroundColor:a,color:o},role:"banner"},i&&r.createElement("div",{className:A}),r.createElement(C,{className:R}),i&&r.createElement(S,{onClick:n,className:L}))}var O=n(2961),P=n(2466);var I=n(902),D=n(3102);const M=r.createContext(null);function F(e){let{children:t}=e;const n=function(){const e=(0,O.e)(),t=(0,D.HY)(),[n,a]=(0,r.useState)(!1),o=null!==t.component,i=(0,I.D9)(o);return(0,r.useEffect)((()=>{o&&!i&&a(!0)}),[o,i]),(0,r.useEffect)((()=>{o?e.shown||a(!0):a(!1)}),[e.shown,o]),(0,r.useMemo)((()=>[n,a]),[n])}();return r.createElement(M.Provider,{value:n},t)}function B(e){if(e.component){const t=e.component;return r.createElement(t,e.props)}}function j(){const e=(0,r.useContext)(M);if(!e)throw new I.i6("NavbarSecondaryMenuDisplayProvider");const[t,n]=e,a=(0,r.useCallback)((()=>n(!1)),[n]),o=(0,D.HY)();return(0,r.useMemo)((()=>({shown:t,hide:a,content:B(o)})),[a,o,t])}function z(e){let{header:t,primaryMenu:n,secondaryMenu:o}=e;const{shown:i}=j();return r.createElement("div",{className:"navbar-sidebar"},t,r.createElement("div",{className:(0,a.Z)("navbar-sidebar__items",{"navbar-sidebar__items--show-secondary":i})},r.createElement("div",{className:"navbar-sidebar__item menu"},n),r.createElement("div",{className:"navbar-sidebar__item menu"},o)))}var U=n(2949),q=n(2389);function $(e){return r.createElement("svg",(0,l.Z)({viewBox:"0 0 24 24",width:24,height:24},e),r.createElement("path",{fill:"currentColor",d:"M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"}))}function H(e){return r.createElement("svg",(0,l.Z)({viewBox:"0 0 24 24",width:24,height:24},e),r.createElement("path",{fill:"currentColor",d:"M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"}))}const G={toggle:"toggle_vylO",toggleButton:"toggleButton_gllP",darkToggleIcon:"darkToggleIcon_wfgR",lightToggleIcon:"lightToggleIcon_pyhR",toggleButtonDisabled:"toggleButtonDisabled_aARS"};function Z(e){let{className:t,buttonClassName:n,value:o,onChange:i}=e;const l=(0,q.Z)(),s=(0,c.I)({message:"Switch between dark and light mode (currently {mode})",id:"theme.colorToggle.ariaLabel",description:"The ARIA label for the navbar color mode toggle"},{mode:"dark"===o?(0,c.I)({message:"dark mode",id:"theme.colorToggle.ariaLabel.mode.dark",description:"The name for the dark color mode"}):(0,c.I)({message:"light mode",id:"theme.colorToggle.ariaLabel.mode.light",description:"The name for the light color mode"})});return r.createElement("div",{className:(0,a.Z)(G.toggle,t)},r.createElement("button",{className:(0,a.Z)("clean-btn",G.toggleButton,!l&&G.toggleButtonDisabled,n),type:"button",onClick:()=>i("dark"===o?"light":"dark"),disabled:!l,title:s,"aria-label":s,"aria-live":"polite"},r.createElement($,{className:(0,a.Z)(G.toggleIcon,G.lightToggleIcon)}),r.createElement(H,{className:(0,a.Z)(G.toggleIcon,G.darkToggleIcon)})))}const V=r.memo(Z),W="darkNavbarColorModeToggle_X3D1";function Y(e){let{className:t}=e;const n=(0,w.L)().navbar.style,a=(0,w.L)().colorMode.disableSwitch,{colorMode:o,setColorMode:i}=(0,U.I)();return a?null:r.createElement(V,{className:t,buttonClassName:"dark"===n?W:void 0,value:o,onChange:i})}var K=n(1327);function Q(){return r.createElement(K.Z,{className:"navbar__brand",imageClassName:"navbar__logo",titleClassName:"navbar__title text--truncate"})}function X(){const e=(0,O.e)();return r.createElement("button",{type:"button","aria-label":(0,c.I)({id:"theme.docs.sidebar.closeSidebarButtonAriaLabel",message:"Close navigation bar",description:"The ARIA label for close button of mobile sidebar"}),className:"clean-btn navbar-sidebar__close",onClick:()=>e.toggle()},r.createElement(E,{color:"var(--ifm-color-emphasis-600)"}))}function J(){return r.createElement("div",{className:"navbar-sidebar__brand"},r.createElement(Q,null),r.createElement(Y,{className:"margin-right--md"}),r.createElement(X,null))}var ee=n(9960),te=n(4996),ne=n(3919);function re(e,t){return void 0!==e&&void 0!==t&&new RegExp(e,"gi").test(t)}var ae=n(9471);function oe(e){let{activeBasePath:t,activeBaseRegex:n,to:a,href:o,label:i,html:s,isDropdownLink:c,prependBaseUrlToHref:u,...d}=e;const p=(0,te.Z)(a),f=(0,te.Z)(t),m=(0,te.Z)(o,{forcePrependBaseUrl:!0}),h=i&&o&&!(0,ne.Z)(o),g=s?{dangerouslySetInnerHTML:{__html:s}}:{children:r.createElement(r.Fragment,null,i,h&&r.createElement(ae.Z,c&&{width:12,height:12}))};return o?r.createElement(ee.Z,(0,l.Z)({href:u?m:o},d,g)):r.createElement(ee.Z,(0,l.Z)({to:p,isNavLink:!0},(t||n)&&{isActive:(e,t)=>n?re(n,t.pathname):t.pathname.startsWith(f)},d,g))}function ie(e){let{className:t,isDropdownItem:n=!1,...o}=e;const i=r.createElement(oe,(0,l.Z)({className:(0,a.Z)(n?"dropdown__link":"navbar__item navbar__link",t),isDropdownLink:n},o));return n?r.createElement("li",null,i):i}function le(e){let{className:t,isDropdownItem:n,...o}=e;return r.createElement("li",{className:"menu__list-item"},r.createElement(oe,(0,l.Z)({className:(0,a.Z)("menu__link",t)},o)))}function se(e){let{mobile:t=!1,position:n,...a}=e;const o=t?le:ie;return r.createElement(o,(0,l.Z)({},a,{activeClassName:a.activeClassName??(t?"menu__link--active":"navbar__link--active")}))}var ce=n(6043),ue=n(8596),de=n(2263);function pe(e,t){return e.some((e=>function(e,t){return!!(0,ue.Mg)(e.to,t)||!!re(e.activeBaseRegex,t)||!(!e.activeBasePath||!t.startsWith(e.activeBasePath))}(e,t)))}function fe(e){let{items:t,position:n,className:o,onClick:i,...s}=e;const c=(0,r.useRef)(null),[u,d]=(0,r.useState)(!1);return(0,r.useEffect)((()=>{const e=e=>{c.current&&!c.current.contains(e.target)&&d(!1)};return document.addEventListener("mousedown",e),document.addEventListener("touchstart",e),document.addEventListener("focusin",e),()=>{document.removeEventListener("mousedown",e),document.removeEventListener("touchstart",e),document.removeEventListener("focusin",e)}}),[c]),r.createElement("div",{ref:c,className:(0,a.Z)("navbar__item","dropdown","dropdown--hoverable",{"dropdown--right":"right"===n,"dropdown--show":u})},r.createElement(oe,(0,l.Z)({"aria-haspopup":"true","aria-expanded":u,role:"button",href:s.to?void 0:"#",className:(0,a.Z)("navbar__link",o)},s,{onClick:s.to?void 0:e=>e.preventDefault(),onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),d(!u))}}),s.children??s.label),r.createElement("ul",{className:"dropdown__menu"},t.map(((e,t)=>r.createElement(Te,(0,l.Z)({isDropdownItem:!0,activeClassName:"dropdown__link--active"},e,{key:t}))))))}function me(e){let{items:t,className:n,position:o,onClick:i,...c}=e;const u=function(){const{siteConfig:{baseUrl:e}}=(0,de.Z)(),{pathname:t}=(0,s.TH)();return t.replace(e,"/")}(),d=pe(t,u),{collapsed:p,toggleCollapsed:f,setCollapsed:m}=(0,ce.u)({initialState:()=>!d});return(0,r.useEffect)((()=>{d&&m(!d)}),[u,d,m]),r.createElement("li",{className:(0,a.Z)("menu__list-item",{"menu__list-item--collapsed":p})},r.createElement(oe,(0,l.Z)({role:"button",className:(0,a.Z)("menu__link menu__link--sublist menu__link--sublist-caret",n)},c,{onClick:e=>{e.preventDefault(),f()}}),c.children??c.label),r.createElement(ce.z,{lazy:!0,as:"ul",className:"menu__list",collapsed:p},t.map(((e,t)=>r.createElement(Te,(0,l.Z)({mobile:!0,isDropdownItem:!0,onClick:i,activeClassName:"menu__link--active"},e,{key:t}))))))}function he(e){let{mobile:t=!1,...n}=e;const a=t?me:fe;return r.createElement(a,n)}var ge=n(4711);function be(e){let{width:t=20,height:n=20,...a}=e;return r.createElement("svg",(0,l.Z)({viewBox:"0 0 24 24",width:t,height:n,"aria-hidden":!0},a),r.createElement("path",{fill:"currentColor",d:"M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"}))}const ve="iconLanguage_nlXk";var ye=n(1875);const we="searchBox_ZlJk";function ke(e){let{children:t,className:n}=e;return r.createElement("div",{className:(0,a.Z)(n,we)},t)}var Ee=n(143),xe=n(2802);var Se=n(373);const _e=e=>e.docs.find((t=>t.id===e.mainDocId));const Ce={default:se,localeDropdown:function(e){let{mobile:t,dropdownItemsBefore:n,dropdownItemsAfter:a,...o}=e;const{i18n:{currentLocale:i,locales:u,localeConfigs:d}}=(0,de.Z)(),p=(0,ge.l)(),{search:f,hash:m}=(0,s.TH)(),h=[...n,...u.map((e=>{const n=`${`pathname://${p.createUrl({locale:e,fullyQualified:!1})}`}${f}${m}`;return{label:d[e].label,lang:d[e].htmlLang,to:n,target:"_self",autoAddBaseUrl:!1,className:e===i?t?"menu__link--active":"dropdown__link--active":""}})),...a],g=t?(0,c.I)({message:"Languages",id:"theme.navbar.mobileLanguageDropdown.label",description:"The label for the mobile language switcher dropdown"}):d[i].label;return r.createElement(he,(0,l.Z)({},o,{mobile:t,label:r.createElement(r.Fragment,null,r.createElement(be,{className:ve}),g),items:h}))},search:function(e){let{mobile:t,className:n}=e;return t?null:r.createElement(ke,{className:n},r.createElement(ye.Z,null))},dropdown:he,html:function(e){let{value:t,className:n,mobile:o=!1,isDropdownItem:i=!1}=e;const l=i?"li":"div";return r.createElement(l,{className:(0,a.Z)({navbar__item:!o&&!i,"menu__list-item":o},n),dangerouslySetInnerHTML:{__html:t}})},doc:function(e){let{docId:t,label:n,docsPluginId:a,...o}=e;const{activeDoc:i}=(0,Ee.Iw)(a),s=(0,xe.vY)(t,a);return null===s?null:r.createElement(se,(0,l.Z)({exact:!0},o,{isActive:()=>i?.path===s.path||!!i?.sidebar&&i.sidebar===s.sidebar,label:n??s.id,to:s.path}))},docSidebar:function(e){let{sidebarId:t,label:n,docsPluginId:a,...o}=e;const{activeDoc:i}=(0,Ee.Iw)(a),s=(0,xe.oz)(t,a).link;if(!s)throw new Error(`DocSidebarNavbarItem: Sidebar with ID "${t}" doesn't have anything to be linked to.`);return r.createElement(se,(0,l.Z)({exact:!0},o,{isActive:()=>i?.sidebar===t,label:n??s.label,to:s.path}))},docsVersion:function(e){let{label:t,to:n,docsPluginId:a,...o}=e;const i=(0,xe.lO)(a)[0],s=t??i.label,c=n??(e=>e.docs.find((t=>t.id===e.mainDocId)))(i).path;return r.createElement(se,(0,l.Z)({},o,{label:s,to:c}))},docsVersionDropdown:function(e){let{mobile:t,docsPluginId:n,dropdownActiveClassDisabled:a,dropdownItemsBefore:o,dropdownItemsAfter:i,...u}=e;const{search:d,hash:p}=(0,s.TH)(),f=(0,Ee.Iw)(n),m=(0,Ee.gB)(n),{savePreferredVersionName:h}=(0,Se.J)(n),g=[...o,...m.map((e=>{const t=f.alternateDocVersions[e.name]??_e(e);return{label:e.label,to:`${t.path}${d}${p}`,isActive:()=>e===f.activeVersion,onClick:()=>h(e.name)}})),...i],b=(0,xe.lO)(n)[0],v=t&&g.length>1?(0,c.I)({id:"theme.navbar.mobileVersionsDropdown.label",message:"Versions",description:"The label for the navbar versions dropdown on mobile view"}):b.label,y=t&&g.length>1?void 0:_e(b).path;return g.length<=1?r.createElement(se,(0,l.Z)({},u,{mobile:t,label:v,to:y,isActive:a?()=>!1:void 0})):r.createElement(he,(0,l.Z)({},u,{mobile:t,label:v,to:y,items:g,isActive:a?()=>!1:void 0}))}};function Te(e){let{type:t,...n}=e;const a=function(e,t){return e&&"default"!==e?e:"items"in t?"dropdown":"default"}(t,n),o=Ce[a];if(!o)throw new Error(`No NavbarItem component found for type "${t}".`);return r.createElement(o,n)}function Ae(){const e=(0,O.e)(),t=(0,w.L)().navbar.items;return r.createElement("ul",{className:"menu__list"},t.map(((t,n)=>r.createElement(Te,(0,l.Z)({mobile:!0},t,{onClick:()=>e.toggle(),key:n})))))}function Le(e){return r.createElement("button",(0,l.Z)({},e,{type:"button",className:"clean-btn navbar-sidebar__back"}),r.createElement(c.Z,{id:"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel",description:"The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)"},"\u2190 Back to main menu"))}function Re(){const e=0===(0,w.L)().navbar.items.length,t=j();return r.createElement(r.Fragment,null,!e&&r.createElement(Le,{onClick:()=>t.hide()}),t.content)}function Ne(){const e=(0,O.e)();var t;return void 0===(t=e.shown)&&(t=!0),(0,r.useEffect)((()=>(document.body.style.overflow=t?"hidden":"visible",()=>{document.body.style.overflow="visible"})),[t]),e.shouldRender?r.createElement(z,{header:r.createElement(J,null),primaryMenu:r.createElement(Ae,null),secondaryMenu:r.createElement(Re,null)}):null}const Oe="navbarHideable_m1mJ",Pe="navbarHidden_jGov";function Ie(e){return r.createElement("div",(0,l.Z)({role:"presentation"},e,{className:(0,a.Z)("navbar-sidebar__backdrop",e.className)}))}function De(e){let{children:t}=e;const{navbar:{hideOnScroll:n,style:o}}=(0,w.L)(),i=(0,O.e)(),{navbarRef:l,isNavbarVisible:s}=function(e){const[t,n]=(0,r.useState)(e),a=(0,r.useRef)(!1),o=(0,r.useRef)(0),i=(0,r.useCallback)((e=>{null!==e&&(o.current=e.getBoundingClientRect().height)}),[]);return(0,P.RF)(((t,r)=>{let{scrollY:i}=t;if(!e)return;if(i<o.current)return void n(!0);if(a.current)return void(a.current=!1);const l=r?.scrollY,s=document.documentElement.scrollHeight-o.current,c=window.innerHeight;l&&i>=l?n(!1):i+c<s&&n(!0)})),(0,u.S)((t=>{if(!e)return;const r=t.location.hash;if(r?document.getElementById(r.substring(1)):void 0)return a.current=!0,void n(!1);n(!0)})),{navbarRef:i,isNavbarVisible:t}}(n);return r.createElement("nav",{ref:l,"aria-label":(0,c.I)({id:"theme.NavBar.navAriaLabel",message:"Main",description:"The ARIA label for the main navigation"}),className:(0,a.Z)("navbar","navbar--fixed-top",n&&[Oe,!s&&Pe],{"navbar--dark":"dark"===o,"navbar--primary":"primary"===o,"navbar-sidebar--show":i.shown})},t,r.createElement(Ie,{onClick:i.toggle}),r.createElement(Ne,null))}var Me=n(8780);const Fe="errorBoundaryError_a6uf";function Be(e){return r.createElement("button",(0,l.Z)({type:"button"},e),r.createElement(c.Z,{id:"theme.ErrorPageContent.tryAgain",description:"The label of the button to try again rendering when the React error boundary captures an error"},"Try again"))}function je(e){let{error:t}=e;const n=(0,Me.getErrorCausalChain)(t).map((e=>e.message)).join("\n\nCause:\n");return r.createElement("p",{className:Fe},n)}class ze extends r.Component{componentDidCatch(e,t){throw this.props.onError(e,t)}render(){return this.props.children}}function Ue(e){let{width:t=30,height:n=30,className:a,...o}=e;return r.createElement("svg",(0,l.Z)({className:a,width:t,height:n,viewBox:"0 0 30 30","aria-hidden":"true"},o),r.createElement("path",{stroke:"currentColor",strokeLinecap:"round",strokeMiterlimit:"10",strokeWidth:"2",d:"M4 7h22M4 15h22M4 23h22"}))}function qe(){const{toggle:e,shown:t}=(0,O.e)();return r.createElement("button",{onClick:e,"aria-label":(0,c.I)({id:"theme.docs.sidebar.toggleSidebarButtonAriaLabel",message:"Toggle navigation bar",description:"The ARIA label for hamburger menu button of mobile navigation"}),"aria-expanded":t,className:"navbar__toggle clean-btn",type:"button"},r.createElement(Ue,null))}const $e="colorModeToggle_DEke";function He(e){let{items:t}=e;return r.createElement(r.Fragment,null,t.map(((e,t)=>r.createElement(ze,{key:t,onError:t=>new Error(`A theme navbar item failed to render.\nPlease double-check the following navbar item (themeConfig.navbar.items) of your Docusaurus config:\n${JSON.stringify(e,null,2)}`,{cause:t})},r.createElement(Te,e)))))}function Ge(e){let{left:t,right:n}=e;return r.createElement("div",{className:"navbar__inner"},r.createElement("div",{className:"navbar__items"},t),r.createElement("div",{className:"navbar__items navbar__items--right"},n))}function Ze(){const e=(0,O.e)(),t=(0,w.L)().navbar.items,[n,a]=function(e){function t(e){return"left"===(e.position??"right")}return[e.filter(t),e.filter((e=>!t(e)))]}(t),o=t.find((e=>"search"===e.type));return r.createElement(Ge,{left:r.createElement(r.Fragment,null,!e.disabled&&r.createElement(qe,null),r.createElement(Q,null),r.createElement(He,{items:n})),right:r.createElement(r.Fragment,null,r.createElement(He,{items:a}),r.createElement(Y,{className:$e}),!o&&r.createElement(ke,null,r.createElement(ye.Z,null)))})}function Ve(){return r.createElement(De,null,r.createElement(Ze,null))}function We(e){let{item:t}=e;const{to:n,href:a,label:o,prependBaseUrlToHref:i,...s}=t,c=(0,te.Z)(n),u=(0,te.Z)(a,{forcePrependBaseUrl:!0});return r.createElement(ee.Z,(0,l.Z)({className:"footer__link-item"},a?{href:i?u:a}:{to:c},s),o,a&&!(0,ne.Z)(a)&&r.createElement(ae.Z,null))}function Ye(e){let{item:t}=e;return t.html?r.createElement("li",{className:"footer__item",dangerouslySetInnerHTML:{__html:t.html}}):r.createElement("li",{key:t.href??t.to,className:"footer__item"},r.createElement(We,{item:t}))}function Ke(e){let{column:t}=e;return r.createElement("div",{className:"col footer__col"},r.createElement("div",{className:"footer__title"},t.title),r.createElement("ul",{className:"footer__items clean-list"},t.items.map(((e,t)=>r.createElement(Ye,{key:t,item:e})))))}function Qe(e){let{columns:t}=e;return r.createElement("div",{className:"row footer__links"},t.map(((e,t)=>r.createElement(Ke,{key:t,column:e}))))}function Xe(){return r.createElement("span",{className:"footer__link-separator"},"\xb7")}function Je(e){let{item:t}=e;return t.html?r.createElement("span",{className:"footer__link-item",dangerouslySetInnerHTML:{__html:t.html}}):r.createElement(We,{item:t})}function et(e){let{links:t}=e;return r.createElement("div",{className:"footer__links text--center"},r.createElement("div",{className:"footer__links"},t.map(((e,n)=>r.createElement(r.Fragment,{key:n},r.createElement(Je,{item:e}),t.length!==n+1&&r.createElement(Xe,null))))))}function tt(e){let{links:t}=e;return function(e){return"title"in e[0]}(t)?r.createElement(Qe,{columns:t}):r.createElement(et,{links:t})}var nt=n(941);const rt="footerLogoLink_BH7S";function at(e){let{logo:t}=e;const{withBaseUrl:n}=(0,te.C)(),o={light:n(t.src),dark:n(t.srcDark??t.src)};return r.createElement(nt.Z,{className:(0,a.Z)("footer__logo",t.className),alt:t.alt,sources:o,width:t.width,height:t.height,style:t.style})}function ot(e){let{logo:t}=e;return t.href?r.createElement(ee.Z,{href:t.href,className:rt,target:t.target},r.createElement(at,{logo:t})):r.createElement(at,{logo:t})}function it(e){let{copyright:t}=e;return r.createElement("div",{className:"footer__copyright",dangerouslySetInnerHTML:{__html:t}})}function lt(e){let{style:t,links:n,logo:o,copyright:i}=e;return r.createElement("footer",{className:(0,a.Z)("footer",{"footer--dark":"dark"===t})},r.createElement("div",{className:"container container-fluid"},n,(o||i)&&r.createElement("div",{className:"footer__bottom text--center"},o&&r.createElement("div",{className:"margin-bottom--sm"},o),i)))}function st(){const{footer:e}=(0,w.L)();if(!e)return null;const{copyright:t,links:n,logo:a,style:o}=e;return r.createElement(lt,{style:o,links:n&&n.length>0&&r.createElement(tt,{links:n}),logo:a&&r.createElement(ot,{logo:a}),copyright:t&&r.createElement(it,{copyright:t})})}const ct=r.memo(st),ut=(0,I.Qc)([U.S,k.pl,P.OC,Se.L5,i.VC,function(e){let{children:t}=e;return r.createElement(D.n2,null,r.createElement(O.M,null,r.createElement(F,null,t)))}]);function dt(e){let{children:t}=e;return r.createElement(ut,null,t)}function pt(e){let{error:t,tryAgain:n}=e;return r.createElement("main",{className:"container margin-vert--xl"},r.createElement("div",{className:"row"},r.createElement("div",{className:"col col--6 col--offset-3"},r.createElement("h1",{className:"hero__title"},r.createElement(c.Z,{id:"theme.ErrorPageContent.title",description:"The title of the fallback page when the page crashed"},"This page crashed.")),r.createElement("div",{className:"margin-vert--lg"},r.createElement(Be,{onClick:n,className:"button button--primary shadow--lw"})),r.createElement("hr",null),r.createElement("div",{className:"margin-vert--md"},r.createElement(je,{error:t})))))}const ft="mainWrapper_z2l0";function mt(e){const{children:t,noFooter:n,wrapperClassName:l,title:s,description:c}=e;return(0,b.t)(),r.createElement(dt,null,r.createElement(i.d,{title:s,description:c}),r.createElement(y,null),r.createElement(N,null),r.createElement(Ve,null),r.createElement("div",{id:d,className:(0,a.Z)(g.k.wrapper.main,ft,l)},r.createElement(o.Z,{fallback:e=>r.createElement(pt,e)},t)),!n&&r.createElement(ct,null))}},1327:(e,t,n)=>{"use strict";n.d(t,{Z:()=>d});var r=n(7462),a=n(7294),o=n(9960),i=n(4996),l=n(2263),s=n(6668),c=n(941);function u(e){let{logo:t,alt:n,imageClassName:r}=e;const o={light:(0,i.Z)(t.src),dark:(0,i.Z)(t.srcDark||t.src)},l=a.createElement(c.Z,{className:t.className,sources:o,height:t.height,width:t.width,alt:n,style:t.style});return r?a.createElement("div",{className:r},l):l}function d(e){const{siteConfig:{title:t}}=(0,l.Z)(),{navbar:{title:n,logo:c}}=(0,s.L)(),{imageClassName:d,titleClassName:p,...f}=e,m=(0,i.Z)(c?.href||"/"),h=n?"":t,g=c?.alt??h;return a.createElement(o.Z,(0,r.Z)({to:m},f,c?.target&&{target:c.target}),c&&a.createElement(u,{logo:c,alt:g,imageClassName:d}),null!=n&&a.createElement("b",{className:p},n))}},197:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(5742);function o(e){let{locale:t,version:n,tag:o}=e;const i=t;return r.createElement(a.Z,null,t&&r.createElement("meta",{name:"docusaurus_locale",content:t}),n&&r.createElement("meta",{name:"docusaurus_version",content:n}),o&&r.createElement("meta",{name:"docusaurus_tag",content:o}),i&&r.createElement("meta",{name:"docsearch:language",content:i}),n&&r.createElement("meta",{name:"docsearch:version",content:n}),o&&r.createElement("meta",{name:"docsearch:docusaurus_tag",content:o}))}},941:(e,t,n)=>{"use strict";n.d(t,{Z:()=>c});var r=n(7462),a=n(7294),o=n(4334),i=n(2389),l=n(2949);const s={themedImage:"themedImage_ToTc","themedImage--light":"themedImage--light_HNdA","themedImage--dark":"themedImage--dark_i4oU"};function c(e){const t=(0,i.Z)(),{colorMode:n}=(0,l.I)(),{sources:c,className:u,alt:d,...p}=e,f=t?"dark"===n?["dark"]:["light"]:["light","dark"];return a.createElement(a.Fragment,null,f.map((e=>a.createElement("img",(0,r.Z)({key:e,src:c[e],alt:d,className:(0,o.Z)(s.themedImage,s[`themedImage--${e}`],u)},p)))))}},6043:(e,t,n)=>{"use strict";n.d(t,{u:()=>l,z:()=>h});var r=n(7462),a=n(7294),o=n(412),i=n(1442);function l(e){let{initialState:t}=e;const[n,r]=(0,a.useState)(t??!1),o=(0,a.useCallback)((()=>{r((e=>!e))}),[]);return{collapsed:n,setCollapsed:r,toggleCollapsed:o}}const s={display:"none",overflow:"hidden",height:"0px"},c={display:"block",overflow:"visible",height:"auto"};function u(e,t){const n=t?s:c;e.style.display=n.display,e.style.overflow=n.overflow,e.style.height=n.height}function d(e){let{collapsibleRef:t,collapsed:n,animation:r}=e;const o=(0,a.useRef)(!1);(0,a.useEffect)((()=>{const e=t.current;function a(){const t=e.scrollHeight,n=r?.duration??function(e){if((0,i.n)())return 1;const t=e/36;return Math.round(10*(4+15*t**.25+t/5))}(t);return{transition:`height ${n}ms ${r?.easing??"ease-in-out"}`,height:`${t}px`}}function l(){const t=a();e.style.transition=t.transition,e.style.height=t.height}if(!o.current)return u(e,n),void(o.current=!0);return e.style.willChange="height",function(){const t=requestAnimationFrame((()=>{n?(l(),requestAnimationFrame((()=>{e.style.height=s.height,e.style.overflow=s.overflow}))):(e.style.display="block",requestAnimationFrame((()=>{l()})))}));return()=>cancelAnimationFrame(t)}()}),[t,n,r])}function p(e){if(!o.Z.canUseDOM)return e?s:c}function f(e){let{as:t="div",collapsed:n,children:r,animation:o,onCollapseTransitionEnd:i,className:l,disableSSRStyle:s}=e;const c=(0,a.useRef)(null);return d({collapsibleRef:c,collapsed:n,animation:o}),a.createElement(t,{ref:c,style:s?void 0:p(n),onTransitionEnd:e=>{"height"===e.propertyName&&(u(c.current,n),i?.(n))},className:l},r)}function m(e){let{collapsed:t,...n}=e;const[o,i]=(0,a.useState)(!t),[l,s]=(0,a.useState)(t);return(0,a.useLayoutEffect)((()=>{t||i(!0)}),[t]),(0,a.useLayoutEffect)((()=>{o&&s(t)}),[o,t]),o?a.createElement(f,(0,r.Z)({},n,{collapsed:l})):null}function h(e){let{lazy:t,...n}=e;const r=t?m:f;return a.createElement(r,n)}},9689:(e,t,n)=>{"use strict";n.d(t,{nT:()=>m,pl:()=>f});var r=n(7294),a=n(2389),o=n(12),i=n(902),l=n(6668);const s=(0,o.WA)("docusaurus.announcement.dismiss"),c=(0,o.WA)("docusaurus.announcement.id"),u=()=>"true"===s.get(),d=e=>s.set(String(e)),p=r.createContext(null);function f(e){let{children:t}=e;const n=function(){const{announcementBar:e}=(0,l.L)(),t=(0,a.Z)(),[n,o]=(0,r.useState)((()=>!!t&&u()));(0,r.useEffect)((()=>{o(u())}),[]);const i=(0,r.useCallback)((()=>{d(!0),o(!0)}),[]);return(0,r.useEffect)((()=>{if(!e)return;const{id:t}=e;let n=c.get();"annoucement-bar"===n&&(n="announcement-bar");const r=t!==n;c.set(t),r&&d(!1),!r&&u()||o(!1)}),[e]),(0,r.useMemo)((()=>({isActive:!!e&&!n,close:i})),[e,n,i])}();return r.createElement(p.Provider,{value:n},t)}function m(){const e=(0,r.useContext)(p);if(!e)throw new i.i6("AnnouncementBarProvider");return e}},2949:(e,t,n)=>{"use strict";n.d(t,{I:()=>g,S:()=>h});var r=n(7294),a=n(412),o=n(902),i=n(12),l=n(6668);const s=r.createContext(void 0),c="theme",u=(0,i.WA)(c),d="light",p="dark",f=e=>e===p?p:d;function m(){const{colorMode:{defaultMode:e,disableSwitch:t,respectPrefersColorScheme:n}}=(0,l.L)(),[o,i]=(0,r.useState)((e=>a.Z.canUseDOM?f(document.documentElement.getAttribute("data-theme")):f(e))(e));(0,r.useEffect)((()=>{t&&u.del()}),[t]);const s=(0,r.useCallback)((function(t,r){void 0===r&&(r={});const{persist:a=!0}=r;t?(i(t),a&&(e=>{u.set(f(e))})(t)):(i(n?window.matchMedia("(prefers-color-scheme: dark)").matches?p:d:e),u.del())}),[n,e]);(0,r.useEffect)((()=>{document.documentElement.setAttribute("data-theme",f(o))}),[o]),(0,r.useEffect)((()=>{if(t)return;const e=e=>{if(e.key!==c)return;const t=u.get();null!==t&&s(f(t))};return window.addEventListener("storage",e),()=>window.removeEventListener("storage",e)}),[t,s]);const m=(0,r.useRef)(!1);return(0,r.useEffect)((()=>{if(t&&!n)return;const e=window.matchMedia("(prefers-color-scheme: dark)"),r=()=>{window.matchMedia("print").matches||m.current?m.current=window.matchMedia("print").matches:s(null)};return e.addListener(r),()=>e.removeListener(r)}),[s,t,n]),(0,r.useMemo)((()=>({colorMode:o,setColorMode:s,get isDarkTheme(){return o===p},setLightTheme(){s(d)},setDarkTheme(){s(p)}})),[o,s])}function h(e){let{children:t}=e;const n=m();return r.createElement(s.Provider,{value:n},t)}function g(){const e=(0,r.useContext)(s);if(null==e)throw new o.i6("ColorModeProvider","Please see https://docusaurus.io/docs/api/themes/configuration#use-color-mode.");return e}},373:(e,t,n)=>{"use strict";n.d(t,{J:()=>y,L5:()=>b});var r=n(7294),a=n(143),o=n(9935),i=n(6668),l=n(2802),s=n(902),c=n(12);const u=e=>`docs-preferred-version-${e}`,d=(e,t,n)=>{(0,c.WA)(u(e),{persistence:t}).set(n)},p=(e,t)=>(0,c.WA)(u(e),{persistence:t}).get(),f=(e,t)=>{(0,c.WA)(u(e),{persistence:t}).del()};const m=r.createContext(null);function h(){const e=(0,a._r)(),t=(0,i.L)().docs.versionPersistence,n=(0,r.useMemo)((()=>Object.keys(e)),[e]),[o,l]=(0,r.useState)((()=>(e=>Object.fromEntries(e.map((e=>[e,{preferredVersionName:null}]))))(n)));(0,r.useEffect)((()=>{l(function(e){let{pluginIds:t,versionPersistence:n,allDocsData:r}=e;function a(e){const t=p(e,n);return r[e].versions.some((e=>e.name===t))?{preferredVersionName:t}:(f(e,n),{preferredVersionName:null})}return Object.fromEntries(t.map((e=>[e,a(e)])))}({allDocsData:e,versionPersistence:t,pluginIds:n}))}),[e,t,n]);return[o,(0,r.useMemo)((()=>({savePreferredVersion:function(e,n){d(e,t,n),l((t=>({...t,[e]:{preferredVersionName:n}})))}})),[t])]}function g(e){let{children:t}=e;const n=h();return r.createElement(m.Provider,{value:n},t)}function b(e){let{children:t}=e;return l.cE?r.createElement(g,null,t):r.createElement(r.Fragment,null,t)}function v(){const e=(0,r.useContext)(m);if(!e)throw new s.i6("DocsPreferredVersionContextProvider");return e}function y(e){void 0===e&&(e=o.m);const t=(0,a.zh)(e),[n,i]=v(),{preferredVersionName:l}=n[e];return{preferredVersion:t.versions.find((e=>e.name===l))??null,savePreferredVersionName:(0,r.useCallback)((t=>{i.savePreferredVersion(e,t)}),[i,e])}}},1116:(e,t,n)=>{"use strict";n.d(t,{V:()=>s,b:()=>l});var r=n(7294),a=n(902);const o=Symbol("EmptyContext"),i=r.createContext(o);function l(e){let{children:t,name:n,items:a}=e;const o=(0,r.useMemo)((()=>n&&a?{name:n,items:a}:null),[n,a]);return r.createElement(i.Provider,{value:o},t)}function s(){const e=(0,r.useContext)(i);if(e===o)throw new a.i6("DocsSidebarProvider");return e}},2961:(e,t,n)=>{"use strict";n.d(t,{M:()=>p,e:()=>f});var r=n(7294),a=n(3102),o=n(7524),i=n(6550),l=(n(1688),n(902));function s(e){!function(e){const t=(0,i.k6)(),n=(0,l.zX)(e);(0,r.useEffect)((()=>t.block(((e,t)=>n(e,t)))),[t,n])}(((t,n)=>{if("POP"===n)return e(t,n)}))}var c=n(6668);const u=r.createContext(void 0);function d(){const e=function(){const e=(0,a.HY)(),{items:t}=(0,c.L)().navbar;return 0===t.length&&!e.component}(),t=(0,o.i)(),n=!e&&"mobile"===t,[i,l]=(0,r.useState)(!1);s((()=>{if(i)return l(!1),!1}));const u=(0,r.useCallback)((()=>{l((e=>!e))}),[]);return(0,r.useEffect)((()=>{"desktop"===t&&l(!1)}),[t]),(0,r.useMemo)((()=>({disabled:e,shouldRender:n,toggle:u,shown:i})),[e,n,u,i])}function p(e){let{children:t}=e;const n=d();return r.createElement(u.Provider,{value:n},t)}function f(){const e=r.useContext(u);if(void 0===e)throw new l.i6("NavbarMobileSidebarProvider");return e}},3102:(e,t,n)=>{"use strict";n.d(t,{HY:()=>l,Zo:()=>s,n2:()=>i});var r=n(7294),a=n(902);const o=r.createContext(null);function i(e){let{children:t}=e;const n=(0,r.useState)({component:null,props:null});return r.createElement(o.Provider,{value:n},t)}function l(){const e=(0,r.useContext)(o);if(!e)throw new a.i6("NavbarSecondaryMenuContentProvider");return e[0]}function s(e){let{component:t,props:n}=e;const i=(0,r.useContext)(o);if(!i)throw new a.i6("NavbarSecondaryMenuContentProvider");const[,l]=i,s=(0,a.Ql)(n);return(0,r.useEffect)((()=>{l({component:t,props:s})}),[l,t,s]),(0,r.useEffect)((()=>()=>l({component:null,props:null})),[l]),null}},9727:(e,t,n)=>{"use strict";n.d(t,{h:()=>a,t:()=>o});var r=n(7294);const a="navigation-with-keyboard";function o(){(0,r.useEffect)((()=>{function e(e){"keydown"===e.type&&"Tab"===e.key&&document.body.classList.add(a),"mousedown"===e.type&&document.body.classList.remove(a)}return document.addEventListener("keydown",e),document.addEventListener("mousedown",e),()=>{document.body.classList.remove(a),document.removeEventListener("keydown",e),document.removeEventListener("mousedown",e)}}),[])}},7524:(e,t,n)=>{"use strict";n.d(t,{i:()=>c});var r=n(7294),a=n(412);const o="desktop",i="mobile",l="ssr";function s(){return a.Z.canUseDOM?window.innerWidth>996?o:i:l}function c(){const[e,t]=(0,r.useState)((()=>s()));return(0,r.useEffect)((()=>{function e(){t(s())}return window.addEventListener("resize",e),()=>{window.removeEventListener("resize",e),clearTimeout(undefined)}}),[]),e}},5281:(e,t,n)=>{"use strict";n.d(t,{k:()=>r});const r={page:{blogListPage:"blog-list-page",blogPostPage:"blog-post-page",blogTagsListPage:"blog-tags-list-page",blogTagPostListPage:"blog-tags-post-list-page",docsDocPage:"docs-doc-page",docsTagsListPage:"docs-tags-list-page",docsTagDocListPage:"docs-tags-doc-list-page",mdxPage:"mdx-page"},wrapper:{main:"main-wrapper",blogPages:"blog-wrapper",docsPages:"docs-wrapper",mdxPages:"mdx-wrapper"},common:{editThisPage:"theme-edit-this-page",lastUpdated:"theme-last-updated",backToTopButton:"theme-back-to-top-button",codeBlock:"theme-code-block",admonition:"theme-admonition",admonitionType:e=>`theme-admonition-${e}`},layout:{},docs:{docVersionBanner:"theme-doc-version-banner",docVersionBadge:"theme-doc-version-badge",docBreadcrumbs:"theme-doc-breadcrumbs",docMarkdown:"theme-doc-markdown",docTocMobile:"theme-doc-toc-mobile",docTocDesktop:"theme-doc-toc-desktop",docFooter:"theme-doc-footer",docFooterTagsRow:"theme-doc-footer-tags-row",docFooterEditMetaRow:"theme-doc-footer-edit-meta-row",docSidebarContainer:"theme-doc-sidebar-container",docSidebarMenu:"theme-doc-sidebar-menu",docSidebarItemCategory:"theme-doc-sidebar-item-category",docSidebarItemLink:"theme-doc-sidebar-item-link",docSidebarItemCategoryLevel:e=>`theme-doc-sidebar-item-category-level-${e}`,docSidebarItemLinkLevel:e=>`theme-doc-sidebar-item-link-level-${e}`},blog:{}}},1442:(e,t,n)=>{"use strict";function r(){return window.matchMedia("(prefers-reduced-motion: reduce)").matches}n.d(t,{n:()=>r})},2802:(e,t,n)=>{"use strict";n.d(t,{Wl:()=>p,_F:()=>m,cE:()=>d,hI:()=>w,lO:()=>b,vY:()=>y,oz:()=>v,s1:()=>g});var r=n(7294),a=n(6550),o=n(8790),i=n(143),l=n(373),s=n(1116);function c(e){return Array.from(new Set(e))}var u=n(8596);const d=!!i._r;function p(e){if(e.href)return e.href;for(const t of e.items){if("link"===t.type)return t.href;if("category"===t.type){const e=p(t);if(e)return e}}}const f=(e,t)=>void 0!==e&&(0,u.Mg)(e,t);function m(e,t){return"link"===e.type?f(e.href,t):"category"===e.type&&(f(e.href,t)||((e,t)=>e.some((e=>m(e,t))))(e.items,t))}function h(e){let{sidebarItems:t,pathname:n,onlyCategories:r=!1}=e;const a=[];return function e(t){for(const o of t)if("category"===o.type&&((0,u.Mg)(o.href,n)||e(o.items))||"link"===o.type&&(0,u.Mg)(o.href,n)){return r&&"category"!==o.type||a.unshift(o),!0}return!1}(t),a}function g(){const e=(0,s.V)(),{pathname:t}=(0,a.TH)(),n=(0,i.gA)()?.pluginData.breadcrumbs;return!1!==n&&e?h({sidebarItems:e.items,pathname:t}):null}function b(e){const{activeVersion:t}=(0,i.Iw)(e),{preferredVersion:n}=(0,l.J)(e),a=(0,i.yW)(e);return(0,r.useMemo)((()=>c([t,n,a].filter(Boolean))),[t,n,a])}function v(e,t){const n=b(t);return(0,r.useMemo)((()=>{const t=n.flatMap((e=>e.sidebars?Object.entries(e.sidebars):[])),r=t.find((t=>t[0]===e));if(!r)throw new Error(`Can't find any sidebar with id "${e}" in version${n.length>1?"s":""} ${n.map((e=>e.name)).join(", ")}".\nAvailable sidebar ids are:\n- ${t.map((e=>e[0])).join("\n- ")}`);return r[1]}),[e,n])}function y(e,t){const n=b(t);return(0,r.useMemo)((()=>{const t=n.flatMap((e=>e.docs)),r=t.find((t=>t.id===e));if(!r){if(n.flatMap((e=>e.draftIds)).includes(e))return null;throw new Error(`Couldn't find any doc with id "${e}" in version${n.length>1?"s":""} "${n.map((e=>e.name)).join(", ")}".\nAvailable doc ids are:\n- ${c(t.map((e=>e.id))).join("\n- ")}`)}return r}),[e,n])}function w(e){let{route:t,versionMetadata:n}=e;const r=(0,a.TH)(),i=t.routes,l=i.find((e=>(0,a.LX)(r.pathname,e)));if(!l)return null;const s=l.sidebar,c=s?n.docsSidebars[s]:void 0;return{docElement:(0,o.H)(i),sidebarName:s,sidebarItems:c}}},1944:(e,t,n)=>{"use strict";n.d(t,{FG:()=>p,d:()=>u,VC:()=>f});var r=n(7294),a=n(7459),o=n(5742),i=n(226);function l(){const e=r.useContext(i._);if(!e)throw new Error("Unexpected: no Docusaurus route context found");return e}var s=n(4996),c=n(2263);function u(e){let{title:t,description:n,keywords:a,image:i,children:l}=e;const u=function(e){const{siteConfig:t}=(0,c.Z)(),{title:n,titleDelimiter:r}=t;return e?.trim().length?`${e.trim()} ${r} ${n}`:n}(t),{withBaseUrl:d}=(0,s.C)(),p=i?d(i,{absolute:!0}):void 0;return r.createElement(o.Z,null,t&&r.createElement("title",null,u),t&&r.createElement("meta",{property:"og:title",content:u}),n&&r.createElement("meta",{name:"description",content:n}),n&&r.createElement("meta",{property:"og:description",content:n}),a&&r.createElement("meta",{name:"keywords",content:Array.isArray(a)?a.join(","):a}),p&&r.createElement("meta",{property:"og:image",content:p}),p&&r.createElement("meta",{name:"twitter:image",content:p}),l)}const d=r.createContext(void 0);function p(e){let{className:t,children:n}=e;const i=r.useContext(d),l=(0,a.Z)(i,t);return r.createElement(d.Provider,{value:l},r.createElement(o.Z,null,r.createElement("html",{className:l})),n)}function f(e){let{children:t}=e;const n=l(),o=`plugin-${n.plugin.name.replace(/docusaurus-(?:plugin|theme)-(?:content-)?/gi,"")}`;const i=`plugin-id-${n.plugin.id}`;return r.createElement(p,{className:(0,a.Z)(o,i)},t)}},902:(e,t,n)=>{"use strict";n.d(t,{D9:()=>i,Qc:()=>c,Ql:()=>s,i6:()=>l,zX:()=>o});var r=n(7294);const a=n(412).Z.canUseDOM?r.useLayoutEffect:r.useEffect;function o(e){const t=(0,r.useRef)(e);return a((()=>{t.current=e}),[e]),(0,r.useCallback)((function(){return t.current(...arguments)}),[])}function i(e){const t=(0,r.useRef)();return a((()=>{t.current=e})),t.current}class l extends Error{constructor(e,t){super(),this.name="ReactContextError",this.message=`Hook ${this.stack?.split("\n")[1]?.match(/at (?:\w+\.)?(?<name>\w+)/)?.groups.name??""} is called outside the <${e}>. ${t??""}`}}function s(e){const t=Object.entries(e);return t.sort(((e,t)=>e[0].localeCompare(t[0]))),(0,r.useMemo)((()=>e),t.flat())}function c(e){return t=>{let{children:n}=t;return r.createElement(r.Fragment,null,e.reduceRight(((e,t)=>r.createElement(t,null,e)),n))}}},8596:(e,t,n)=>{"use strict";n.d(t,{Mg:()=>i,Ns:()=>l});var r=n(7294),a=n(723),o=n(2263);function i(e,t){const n=e=>(!e||e.endsWith("/")?e:`${e}/`)?.toLowerCase();return n(e)===n(t)}function l(){const{baseUrl:e}=(0,o.Z)().siteConfig;return(0,r.useMemo)((()=>function(e){let{baseUrl:t,routes:n}=e;function r(e){return e.path===t&&!0===e.exact}function a(e){return e.path===t&&!e.exact}return function e(t){if(0===t.length)return;return t.find(r)||e(t.filter(a).flatMap((e=>e.routes??[])))}(n)}({routes:a.Z,baseUrl:e})),[e])}},2466:(e,t,n)=>{"use strict";n.d(t,{Ct:()=>p,OC:()=>s,RF:()=>d});var r=n(7294),a=n(412),o=n(2389),i=n(902);const l=r.createContext(void 0);function s(e){let{children:t}=e;const n=function(){const e=(0,r.useRef)(!0);return(0,r.useMemo)((()=>({scrollEventsEnabledRef:e,enableScrollEvents:()=>{e.current=!0},disableScrollEvents:()=>{e.current=!1}})),[])}();return r.createElement(l.Provider,{value:n},t)}function c(){const e=(0,r.useContext)(l);if(null==e)throw new i.i6("ScrollControllerProvider");return e}const u=()=>a.Z.canUseDOM?{scrollX:window.pageXOffset,scrollY:window.pageYOffset}:null;function d(e,t){void 0===t&&(t=[]);const{scrollEventsEnabledRef:n}=c(),a=(0,r.useRef)(u()),o=(0,i.zX)(e);(0,r.useEffect)((()=>{const e=()=>{if(!n.current)return;const e=u();o(e,a.current),a.current=e},t={passive:!0};return e(),window.addEventListener("scroll",e,t),()=>window.removeEventListener("scroll",e,t)}),[o,n,...t])}function p(){const e=(0,r.useRef)(null),t=(0,o.Z)()&&"smooth"===getComputedStyle(document.documentElement).scrollBehavior;return{startScroll:n=>{e.current=t?function(e){return window.scrollTo({top:e,behavior:"smooth"}),()=>{}}(n):function(e){let t=null;const n=document.documentElement.scrollTop>e;return function r(){const a=document.documentElement.scrollTop;(n&&a>e||!n&&a<e)&&(t=requestAnimationFrame(r),window.scrollTo(0,Math.floor(.85*(a-e))+e))}(),()=>t&&cancelAnimationFrame(t)}(n)},cancelScroll:()=>e.current?.()}}},3320:(e,t,n)=>{"use strict";n.d(t,{HX:()=>r,os:()=>a});n(2263);const r="default";function a(e,t){return`docs-${e}-${t}`}},12:(e,t,n)=>{"use strict";n.d(t,{WA:()=>s});n(7294),n(1688);const r="localStorage";function a(e){let{key:t,oldValue:n,newValue:r,storage:a}=e;if(n===r)return;const o=document.createEvent("StorageEvent");o.initStorageEvent("storage",!1,!1,t,n,r,window.location.href,a),window.dispatchEvent(o)}function o(e){if(void 0===e&&(e=r),"undefined"==typeof window)throw new Error("Browser storage is not available on Node.js/Docusaurus SSR process.");if("none"===e)return null;try{return window[e]}catch(n){return t=n,i||(console.warn("Docusaurus browser storage is not available.\nPossible reasons: running Docusaurus in an iframe, in an incognito browser session, or using too strict browser privacy settings.",t),i=!0),null}var t}let i=!1;const l={get:()=>null,set:()=>{},del:()=>{},listen:()=>()=>{}};function s(e,t){if("undefined"==typeof window)return function(e){function t(){throw new Error(`Illegal storage API usage for storage key "${e}".\nDocusaurus storage APIs are not supposed to be called on the server-rendering process.\nPlease only call storage APIs in effects and event handlers.`)}return{get:t,set:t,del:t,listen:t}}(e);const n=o(t?.persistence);return null===n?l:{get:()=>{try{return n.getItem(e)}catch(t){return console.error(`Docusaurus storage error, can't get key=${e}`,t),null}},set:t=>{try{const r=n.getItem(e);n.setItem(e,t),a({key:e,oldValue:r,newValue:t,storage:n})}catch(r){console.error(`Docusaurus storage error, can't set ${e}=${t}`,r)}},del:()=>{try{const t=n.getItem(e);n.removeItem(e),a({key:e,oldValue:t,newValue:null,storage:n})}catch(t){console.error(`Docusaurus storage error, can't delete key=${e}`,t)}},listen:t=>{try{const r=r=>{r.storageArea===n&&r.key===e&&t(r)};return window.addEventListener("storage",r),()=>window.removeEventListener("storage",r)}catch(r){return console.error(`Docusaurus storage error, can't listen for changes of key=${e}`,r),()=>{}}}}}},4711:(e,t,n)=>{"use strict";n.d(t,{l:()=>i});var r=n(2263),a=n(6550),o=n(8780);function i(){const{siteConfig:{baseUrl:e,url:t,trailingSlash:n},i18n:{defaultLocale:i,currentLocale:l}}=(0,r.Z)(),{pathname:s}=(0,a.TH)(),c=(0,o.applyTrailingSlash)(s,{trailingSlash:n,baseUrl:e}),u=l===i?e:e.replace(`/${l}/`,"/"),d=c.replace(e,"");return{createUrl:function(e){let{locale:n,fullyQualified:r}=e;return`${r?t:""}${function(e){return e===i?`${u}`:`${u}${e}/`}(n)}${d}`}}}},5936:(e,t,n)=>{"use strict";n.d(t,{S:()=>i});var r=n(7294),a=n(6550),o=n(902);function i(e){const t=(0,a.TH)(),n=(0,o.D9)(t),i=(0,o.zX)(e);(0,r.useEffect)((()=>{n&&t!==n&&i({location:t,previousLocation:n})}),[i,t,n])}},6668:(e,t,n)=>{"use strict";n.d(t,{L:()=>a});var r=n(2263);function a(){return(0,r.Z)().siteConfig.themeConfig}},8802:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){const{trailingSlash:n,baseUrl:r}=t;if(e.startsWith("#"))return e;if(void 0===n)return e;const[a]=e.split(/[#?]/),o="/"===a||a===r?a:(i=a,n?function(e){return e.endsWith("/")?e:`${e}/`}(i):function(e){return e.endsWith("/")?e.slice(0,-1):e}(i));var i;return e.replace(a,o)}},4143:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getErrorCausalChain=void 0,t.getErrorCausalChain=function e(t){return t.cause?[t,...e(t.cause)]:[t]}},8780:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.getErrorCausalChain=t.applyTrailingSlash=t.blogPostContainerID=void 0,t.blogPostContainerID="__blog-post-container";var a=n(8802);Object.defineProperty(t,"applyTrailingSlash",{enumerable:!0,get:function(){return r(a).default}});var o=n(4143);Object.defineProperty(t,"getErrorCausalChain",{enumerable:!0,get:function(){return o.getErrorCausalChain}})},9318:(e,t,n)=>{"use strict";n.d(t,{lX:()=>w,q_:()=>C,ob:()=>f,PP:()=>A,Ep:()=>p});var r=n(7462);function a(e){return"/"===e.charAt(0)}function o(e,t){for(var n=t,r=n+1,a=e.length;r<a;n+=1,r+=1)e[n]=e[r];e.pop()}const i=function(e,t){void 0===t&&(t="");var n,r=e&&e.split("/")||[],i=t&&t.split("/")||[],l=e&&a(e),s=t&&a(t),c=l||s;if(e&&a(e)?i=r:r.length&&(i.pop(),i=i.concat(r)),!i.length)return"/";if(i.length){var u=i[i.length-1];n="."===u||".."===u||""===u}else n=!1;for(var d=0,p=i.length;p>=0;p--){var f=i[p];"."===f?o(i,p):".."===f?(o(i,p),d++):d&&(o(i,p),d--)}if(!c)for(;d--;d)i.unshift("..");!c||""===i[0]||i[0]&&a(i[0])||i.unshift("");var m=i.join("/");return n&&"/"!==m.substr(-1)&&(m+="/"),m};var l=n(8776);function s(e){return"/"===e.charAt(0)?e:"/"+e}function c(e){return"/"===e.charAt(0)?e.substr(1):e}function u(e,t){return function(e,t){return 0===e.toLowerCase().indexOf(t.toLowerCase())&&-1!=="/?#".indexOf(e.charAt(t.length))}(e,t)?e.substr(t.length):e}function d(e){return"/"===e.charAt(e.length-1)?e.slice(0,-1):e}function p(e){var t=e.pathname,n=e.search,r=e.hash,a=t||"/";return n&&"?"!==n&&(a+="?"===n.charAt(0)?n:"?"+n),r&&"#"!==r&&(a+="#"===r.charAt(0)?r:"#"+r),a}function f(e,t,n,a){var o;"string"==typeof e?(o=function(e){var t=e||"/",n="",r="",a=t.indexOf("#");-1!==a&&(r=t.substr(a),t=t.substr(0,a));var o=t.indexOf("?");return-1!==o&&(n=t.substr(o),t=t.substr(0,o)),{pathname:t,search:"?"===n?"":n,hash:"#"===r?"":r}}(e),o.state=t):(void 0===(o=(0,r.Z)({},e)).pathname&&(o.pathname=""),o.search?"?"!==o.search.charAt(0)&&(o.search="?"+o.search):o.search="",o.hash?"#"!==o.hash.charAt(0)&&(o.hash="#"+o.hash):o.hash="",void 0!==t&&void 0===o.state&&(o.state=t));try{o.pathname=decodeURI(o.pathname)}catch(l){throw l instanceof URIError?new URIError('Pathname "'+o.pathname+'" could not be decoded. This is likely caused by an invalid percent-encoding.'):l}return n&&(o.key=n),a?o.pathname?"/"!==o.pathname.charAt(0)&&(o.pathname=i(o.pathname,a.pathname)):o.pathname=a.pathname:o.pathname||(o.pathname="/"),o}function m(){var e=null;var t=[];return{setPrompt:function(t){return e=t,function(){e===t&&(e=null)}},confirmTransitionTo:function(t,n,r,a){if(null!=e){var o="function"==typeof e?e(t,n):e;"string"==typeof o?"function"==typeof r?r(o,a):a(!0):a(!1!==o)}else a(!0)},appendListener:function(e){var n=!0;function r(){n&&e.apply(void 0,arguments)}return t.push(r),function(){n=!1,t=t.filter((function(e){return e!==r}))}},notifyListeners:function(){for(var e=arguments.length,n=new Array(e),r=0;r<e;r++)n[r]=arguments[r];t.forEach((function(e){return e.apply(void 0,n)}))}}}var h=!("undefined"==typeof window||!window.document||!window.document.createElement);function g(e,t){t(window.confirm(e))}var b="popstate",v="hashchange";function y(){try{return window.history.state||{}}catch(e){return{}}}function w(e){void 0===e&&(e={}),h||(0,l.Z)(!1);var t,n=window.history,a=(-1===(t=window.navigator.userAgent).indexOf("Android 2.")&&-1===t.indexOf("Android 4.0")||-1===t.indexOf("Mobile Safari")||-1!==t.indexOf("Chrome")||-1!==t.indexOf("Windows Phone"))&&window.history&&"pushState"in window.history,o=!(-1===window.navigator.userAgent.indexOf("Trident")),i=e,c=i.forceRefresh,w=void 0!==c&&c,k=i.getUserConfirmation,E=void 0===k?g:k,x=i.keyLength,S=void 0===x?6:x,_=e.basename?d(s(e.basename)):"";function C(e){var t=e||{},n=t.key,r=t.state,a=window.location,o=a.pathname+a.search+a.hash;return _&&(o=u(o,_)),f(o,r,n)}function T(){return Math.random().toString(36).substr(2,S)}var A=m();function L(e){(0,r.Z)(U,e),U.length=n.length,A.notifyListeners(U.location,U.action)}function R(e){(function(e){return void 0===e.state&&-1===navigator.userAgent.indexOf("CriOS")})(e)||P(C(e.state))}function N(){P(C(y()))}var O=!1;function P(e){if(O)O=!1,L();else{A.confirmTransitionTo(e,"POP",E,(function(t){t?L({action:"POP",location:e}):function(e){var t=U.location,n=D.indexOf(t.key);-1===n&&(n=0);var r=D.indexOf(e.key);-1===r&&(r=0);var a=n-r;a&&(O=!0,F(a))}(e)}))}}var I=C(y()),D=[I.key];function M(e){return _+p(e)}function F(e){n.go(e)}var B=0;function j(e){1===(B+=e)&&1===e?(window.addEventListener(b,R),o&&window.addEventListener(v,N)):0===B&&(window.removeEventListener(b,R),o&&window.removeEventListener(v,N))}var z=!1;var U={length:n.length,action:"POP",location:I,createHref:M,push:function(e,t){var r="PUSH",o=f(e,t,T(),U.location);A.confirmTransitionTo(o,r,E,(function(e){if(e){var t=M(o),i=o.key,l=o.state;if(a)if(n.pushState({key:i,state:l},null,t),w)window.location.href=t;else{var s=D.indexOf(U.location.key),c=D.slice(0,s+1);c.push(o.key),D=c,L({action:r,location:o})}else window.location.href=t}}))},replace:function(e,t){var r="REPLACE",o=f(e,t,T(),U.location);A.confirmTransitionTo(o,r,E,(function(e){if(e){var t=M(o),i=o.key,l=o.state;if(a)if(n.replaceState({key:i,state:l},null,t),w)window.location.replace(t);else{var s=D.indexOf(U.location.key);-1!==s&&(D[s]=o.key),L({action:r,location:o})}else window.location.replace(t)}}))},go:F,goBack:function(){F(-1)},goForward:function(){F(1)},block:function(e){void 0===e&&(e=!1);var t=A.setPrompt(e);return z||(j(1),z=!0),function(){return z&&(z=!1,j(-1)),t()}},listen:function(e){var t=A.appendListener(e);return j(1),function(){j(-1),t()}}};return U}var k="hashchange",E={hashbang:{encodePath:function(e){return"!"===e.charAt(0)?e:"!/"+c(e)},decodePath:function(e){return"!"===e.charAt(0)?e.substr(1):e}},noslash:{encodePath:c,decodePath:s},slash:{encodePath:s,decodePath:s}};function x(e){var t=e.indexOf("#");return-1===t?e:e.slice(0,t)}function S(){var e=window.location.href,t=e.indexOf("#");return-1===t?"":e.substring(t+1)}function _(e){window.location.replace(x(window.location.href)+"#"+e)}function C(e){void 0===e&&(e={}),h||(0,l.Z)(!1);var t=window.history,n=(window.navigator.userAgent.indexOf("Firefox"),e),a=n.getUserConfirmation,o=void 0===a?g:a,i=n.hashType,c=void 0===i?"slash":i,b=e.basename?d(s(e.basename)):"",v=E[c],y=v.encodePath,w=v.decodePath;function C(){var e=w(S());return b&&(e=u(e,b)),f(e)}var T=m();function A(e){(0,r.Z)(z,e),z.length=t.length,T.notifyListeners(z.location,z.action)}var L=!1,R=null;function N(){var e,t,n=S(),r=y(n);if(n!==r)_(r);else{var a=C(),i=z.location;if(!L&&(t=a,(e=i).pathname===t.pathname&&e.search===t.search&&e.hash===t.hash))return;if(R===p(a))return;R=null,function(e){if(L)L=!1,A();else{var t="POP";T.confirmTransitionTo(e,t,o,(function(n){n?A({action:t,location:e}):function(e){var t=z.location,n=D.lastIndexOf(p(t));-1===n&&(n=0);var r=D.lastIndexOf(p(e));-1===r&&(r=0);var a=n-r;a&&(L=!0,M(a))}(e)}))}}(a)}}var O=S(),P=y(O);O!==P&&_(P);var I=C(),D=[p(I)];function M(e){t.go(e)}var F=0;function B(e){1===(F+=e)&&1===e?window.addEventListener(k,N):0===F&&window.removeEventListener(k,N)}var j=!1;var z={length:t.length,action:"POP",location:I,createHref:function(e){var t=document.querySelector("base"),n="";return t&&t.getAttribute("href")&&(n=x(window.location.href)),n+"#"+y(b+p(e))},push:function(e,t){var n="PUSH",r=f(e,void 0,void 0,z.location);T.confirmTransitionTo(r,n,o,(function(e){if(e){var t=p(r),a=y(b+t);if(S()!==a){R=t,function(e){window.location.hash=e}(a);var o=D.lastIndexOf(p(z.location)),i=D.slice(0,o+1);i.push(t),D=i,A({action:n,location:r})}else A()}}))},replace:function(e,t){var n="REPLACE",r=f(e,void 0,void 0,z.location);T.confirmTransitionTo(r,n,o,(function(e){if(e){var t=p(r),a=y(b+t);S()!==a&&(R=t,_(a));var o=D.indexOf(p(z.location));-1!==o&&(D[o]=t),A({action:n,location:r})}}))},go:M,goBack:function(){M(-1)},goForward:function(){M(1)},block:function(e){void 0===e&&(e=!1);var t=T.setPrompt(e);return j||(B(1),j=!0),function(){return j&&(j=!1,B(-1)),t()}},listen:function(e){var t=T.appendListener(e);return B(1),function(){B(-1),t()}}};return z}function T(e,t,n){return Math.min(Math.max(e,t),n)}function A(e){void 0===e&&(e={});var t=e,n=t.getUserConfirmation,a=t.initialEntries,o=void 0===a?["/"]:a,i=t.initialIndex,l=void 0===i?0:i,s=t.keyLength,c=void 0===s?6:s,u=m();function d(e){(0,r.Z)(w,e),w.length=w.entries.length,u.notifyListeners(w.location,w.action)}function h(){return Math.random().toString(36).substr(2,c)}var g=T(l,0,o.length-1),b=o.map((function(e){return f(e,void 0,"string"==typeof e?h():e.key||h())})),v=p;function y(e){var t=T(w.index+e,0,w.entries.length-1),r=w.entries[t];u.confirmTransitionTo(r,"POP",n,(function(e){e?d({action:"POP",location:r,index:t}):d()}))}var w={length:b.length,action:"POP",location:b[g],index:g,entries:b,createHref:v,push:function(e,t){var r="PUSH",a=f(e,t,h(),w.location);u.confirmTransitionTo(a,r,n,(function(e){if(e){var t=w.index+1,n=w.entries.slice(0);n.length>t?n.splice(t,n.length-t,a):n.push(a),d({action:r,location:a,index:t,entries:n})}}))},replace:function(e,t){var r="REPLACE",a=f(e,t,h(),w.location);u.confirmTransitionTo(a,r,n,(function(e){e&&(w.entries[w.index]=a,d({action:r,location:a}))}))},go:y,goBack:function(){y(-1)},goForward:function(){y(1)},canGo:function(e){var t=w.index+e;return t>=0&&t<w.entries.length},block:function(e){return void 0===e&&(e=!1),u.setPrompt(e)},listen:function(e){return u.appendListener(e)}};return w}},8679:(e,t,n)=>{"use strict";var r=n(9864),a={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},o={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},i={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},l={};function s(e){return r.isMemo(e)?i:l[e.$$typeof]||a}l[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},l[r.Memo]=i;var c=Object.defineProperty,u=Object.getOwnPropertyNames,d=Object.getOwnPropertySymbols,p=Object.getOwnPropertyDescriptor,f=Object.getPrototypeOf,m=Object.prototype;e.exports=function e(t,n,r){if("string"!=typeof n){if(m){var a=f(n);a&&a!==m&&e(t,a,r)}var i=u(n);d&&(i=i.concat(d(n)));for(var l=s(t),h=s(n),g=0;g<i.length;++g){var b=i[g];if(!(o[b]||r&&r[b]||h&&h[b]||l&&l[b])){var v=p(n,b);try{c(t,b,v)}catch(y){}}}}return t}},1143:e=>{"use strict";e.exports=function(e,t,n,r,a,o,i,l){if(!e){var s;if(void 0===t)s=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var c=[n,r,a,o,i,l],u=0;(s=new Error(t.replace(/%s/g,(function(){return c[u++]})))).name="Invariant Violation"}throw s.framesToPop=1,s}}},5826:e=>{e.exports=Array.isArray||function(e){return"[object Array]"==Object.prototype.toString.call(e)}},2497:(e,t,n)=>{"use strict";n.r(t)},2295:(e,t,n)=>{"use strict";n.r(t)},4865:function(e,t,n){var r,a;r=function(){var e,t,n={version:"0.2.0"},r=n.settings={minimum:.08,easing:"ease",positionUsing:"",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,showSpinner:!0,barSelector:'[role="bar"]',spinnerSelector:'[role="spinner"]',parent:"body",template:'<div class="bar" role="bar"><div class="peg"></div></div><div class="spinner" role="spinner"><div class="spinner-icon"></div></div>'};function a(e,t,n){return e<t?t:e>n?n:e}function o(e){return 100*(-1+e)}function i(e,t,n){var a;return(a="translate3d"===r.positionUsing?{transform:"translate3d("+o(e)+"%,0,0)"}:"translate"===r.positionUsing?{transform:"translate("+o(e)+"%,0)"}:{"margin-left":o(e)+"%"}).transition="all "+t+"ms "+n,a}n.configure=function(e){var t,n;for(t in e)void 0!==(n=e[t])&&e.hasOwnProperty(t)&&(r[t]=n);return this},n.status=null,n.set=function(e){var t=n.isStarted();e=a(e,r.minimum,1),n.status=1===e?null:e;var o=n.render(!t),c=o.querySelector(r.barSelector),u=r.speed,d=r.easing;return o.offsetWidth,l((function(t){""===r.positionUsing&&(r.positionUsing=n.getPositioningCSS()),s(c,i(e,u,d)),1===e?(s(o,{transition:"none",opacity:1}),o.offsetWidth,setTimeout((function(){s(o,{transition:"all "+u+"ms linear",opacity:0}),setTimeout((function(){n.remove(),t()}),u)}),u)):setTimeout(t,u)})),this},n.isStarted=function(){return"number"==typeof n.status},n.start=function(){n.status||n.set(0);var e=function(){setTimeout((function(){n.status&&(n.trickle(),e())}),r.trickleSpeed)};return r.trickle&&e(),this},n.done=function(e){return e||n.status?n.inc(.3+.5*Math.random()).set(1):this},n.inc=function(e){var t=n.status;return t?("number"!=typeof e&&(e=(1-t)*a(Math.random()*t,.1,.95)),t=a(t+e,0,.994),n.set(t)):n.start()},n.trickle=function(){return n.inc(Math.random()*r.trickleRate)},e=0,t=0,n.promise=function(r){return r&&"resolved"!==r.state()?(0===t&&n.start(),e++,t++,r.always((function(){0==--t?(e=0,n.done()):n.set((e-t)/e)})),this):this},n.render=function(e){if(n.isRendered())return document.getElementById("nprogress");u(document.documentElement,"nprogress-busy");var t=document.createElement("div");t.id="nprogress",t.innerHTML=r.template;var a,i=t.querySelector(r.barSelector),l=e?"-100":o(n.status||0),c=document.querySelector(r.parent);return s(i,{transition:"all 0 linear",transform:"translate3d("+l+"%,0,0)"}),r.showSpinner||(a=t.querySelector(r.spinnerSelector))&&f(a),c!=document.body&&u(c,"nprogress-custom-parent"),c.appendChild(t),t},n.remove=function(){d(document.documentElement,"nprogress-busy"),d(document.querySelector(r.parent),"nprogress-custom-parent");var e=document.getElementById("nprogress");e&&f(e)},n.isRendered=function(){return!!document.getElementById("nprogress")},n.getPositioningCSS=function(){var e=document.body.style,t="WebkitTransform"in e?"Webkit":"MozTransform"in e?"Moz":"msTransform"in e?"ms":"OTransform"in e?"O":"";return t+"Perspective"in e?"translate3d":t+"Transform"in e?"translate":"margin"};var l=function(){var e=[];function t(){var n=e.shift();n&&n(t)}return function(n){e.push(n),1==e.length&&t()}}(),s=function(){var e=["Webkit","O","Moz","ms"],t={};function n(e){return e.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,(function(e,t){return t.toUpperCase()}))}function r(t){var n=document.body.style;if(t in n)return t;for(var r,a=e.length,o=t.charAt(0).toUpperCase()+t.slice(1);a--;)if((r=e[a]+o)in n)return r;return t}function a(e){return e=n(e),t[e]||(t[e]=r(e))}function o(e,t,n){t=a(t),e.style[t]=n}return function(e,t){var n,r,a=arguments;if(2==a.length)for(n in t)void 0!==(r=t[n])&&t.hasOwnProperty(n)&&o(e,n,r);else o(e,a[1],a[2])}}();function c(e,t){return("string"==typeof e?e:p(e)).indexOf(" "+t+" ")>=0}function u(e,t){var n=p(e),r=n+t;c(n,t)||(e.className=r.substring(1))}function d(e,t){var n,r=p(e);c(e,t)&&(n=r.replace(" "+t+" "," "),e.className=n.substring(1,n.length-1))}function p(e){return(" "+(e.className||"")+" ").replace(/\s+/gi," ")}function f(e){e&&e.parentNode&&e.parentNode.removeChild(e)}return n},void 0===(a="function"==typeof r?r.call(t,n,t,e):r)||(e.exports=a)},7418:e=>{"use strict";var t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;function a(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(a){return!1}}()?Object.assign:function(e,o){for(var i,l,s=a(e),c=1;c<arguments.length;c++){for(var u in i=Object(arguments[c]))n.call(i,u)&&(s[u]=i[u]);if(t){l=t(i);for(var d=0;d<l.length;d++)r.call(i,l[d])&&(s[l[d]]=i[l[d]])}}return s}},9901:e=>{e.exports&&(e.exports={core:{meta:{path:"components/prism-core.js",option:"mandatory"},core:"Core"},themes:{meta:{path:"themes/{id}.css",link:"index.html?theme={id}",exclusive:!0},prism:{title:"Default",option:"default"},"prism-dark":"Dark","prism-funky":"Funky","prism-okaidia":{title:"Okaidia",owner:"ocodia"},"prism-twilight":{title:"Twilight",owner:"remybach"},"prism-coy":{title:"Coy",owner:"tshedor"},"prism-solarizedlight":{title:"Solarized Light",owner:"hectormatos2011 "},"prism-tomorrow":{title:"Tomorrow Night",owner:"Rosey"}},languages:{meta:{path:"components/prism-{id}",noCSS:!0,examplesPath:"examples/prism-{id}",addCheckAll:!0},markup:{title:"Markup",alias:["html","xml","svg","mathml","ssml","atom","rss"],aliasTitles:{html:"HTML",xml:"XML",svg:"SVG",mathml:"MathML",ssml:"SSML",atom:"Atom",rss:"RSS"},option:"default"},css:{title:"CSS",option:"default",modify:"markup"},clike:{title:"C-like",option:"default"},javascript:{title:"JavaScript",require:"clike",modify:"markup",optional:"regex",alias:"js",option:"default"},abap:{title:"ABAP",owner:"dellagustin"},abnf:{title:"ABNF",owner:"RunDevelopment"},actionscript:{title:"ActionScript",require:"javascript",modify:"markup",owner:"Golmote"},ada:{title:"Ada",owner:"Lucretia"},agda:{title:"Agda",owner:"xy-ren"},al:{title:"AL",owner:"RunDevelopment"},antlr4:{title:"ANTLR4",alias:"g4",owner:"RunDevelopment"},apacheconf:{title:"Apache Configuration",owner:"GuiTeK"},apex:{title:"Apex",require:["clike","sql"],owner:"RunDevelopment"},apl:{title:"APL",owner:"ngn"},applescript:{title:"AppleScript",owner:"Golmote"},aql:{title:"AQL",owner:"RunDevelopment"},arduino:{title:"Arduino",require:"cpp",alias:"ino",owner:"dkern"},arff:{title:"ARFF",owner:"Golmote"},armasm:{title:"ARM Assembly",alias:"arm-asm",owner:"RunDevelopment"},arturo:{title:"Arturo",alias:"art",optional:["bash","css","javascript","markup","markdown","sql"],owner:"drkameleon"},asciidoc:{alias:"adoc",title:"AsciiDoc",owner:"Golmote"},aspnet:{title:"ASP.NET (C#)",require:["markup","csharp"],owner:"nauzilus"},asm6502:{title:"6502 Assembly",owner:"kzurawel"},asmatmel:{title:"Atmel AVR Assembly",owner:"cerkit"},autohotkey:{title:"AutoHotkey",owner:"aviaryan"},autoit:{title:"AutoIt",owner:"Golmote"},avisynth:{title:"AviSynth",alias:"avs",owner:"Zinfidel"},"avro-idl":{title:"Avro IDL",alias:"avdl",owner:"RunDevelopment"},awk:{title:"AWK",alias:"gawk",aliasTitles:{gawk:"GAWK"},owner:"RunDevelopment"},bash:{title:"Bash",alias:["sh","shell"],aliasTitles:{sh:"Shell",shell:"Shell"},owner:"zeitgeist87"},basic:{title:"BASIC",owner:"Golmote"},batch:{title:"Batch",owner:"Golmote"},bbcode:{title:"BBcode",alias:"shortcode",aliasTitles:{shortcode:"Shortcode"},owner:"RunDevelopment"},bbj:{title:"BBj",owner:"hyyan"},bicep:{title:"Bicep",owner:"johnnyreilly"},birb:{title:"Birb",require:"clike",owner:"Calamity210"},bison:{title:"Bison",require:"c",owner:"Golmote"},bnf:{title:"BNF",alias:"rbnf",aliasTitles:{rbnf:"RBNF"},owner:"RunDevelopment"},bqn:{title:"BQN",owner:"yewscion"},brainfuck:{title:"Brainfuck",owner:"Golmote"},brightscript:{title:"BrightScript",owner:"RunDevelopment"},bro:{title:"Bro",owner:"wayward710"},bsl:{title:"BSL (1C:Enterprise)",alias:"oscript",aliasTitles:{oscript:"OneScript"},owner:"Diversus23"},c:{title:"C",require:"clike",owner:"zeitgeist87"},csharp:{title:"C#",require:"clike",alias:["cs","dotnet"],owner:"mvalipour"},cpp:{title:"C++",require:"c",owner:"zeitgeist87"},cfscript:{title:"CFScript",require:"clike",alias:"cfc",owner:"mjclemente"},chaiscript:{title:"ChaiScript",require:["clike","cpp"],owner:"RunDevelopment"},cil:{title:"CIL",owner:"sbrl"},cilkc:{title:"Cilk/C",require:"c",alias:"cilk-c",owner:"OpenCilk"},cilkcpp:{title:"Cilk/C++",require:"cpp",alias:["cilk-cpp","cilk"],owner:"OpenCilk"},clojure:{title:"Clojure",owner:"troglotit"},cmake:{title:"CMake",owner:"mjrogozinski"},cobol:{title:"COBOL",owner:"RunDevelopment"},coffeescript:{title:"CoffeeScript",require:"javascript",alias:"coffee",owner:"R-osey"},concurnas:{title:"Concurnas",alias:"conc",owner:"jasontatton"},csp:{title:"Content-Security-Policy",owner:"ScottHelme"},cooklang:{title:"Cooklang",owner:"ahue"},coq:{title:"Coq",owner:"RunDevelopment"},crystal:{title:"Crystal",require:"ruby",owner:"MakeNowJust"},"css-extras":{title:"CSS Extras",require:"css",modify:"css",owner:"milesj"},csv:{title:"CSV",owner:"RunDevelopment"},cue:{title:"CUE",owner:"RunDevelopment"},cypher:{title:"Cypher",owner:"RunDevelopment"},d:{title:"D",require:"clike",owner:"Golmote"},dart:{title:"Dart",require:"clike",owner:"Golmote"},dataweave:{title:"DataWeave",owner:"machaval"},dax:{title:"DAX",owner:"peterbud"},dhall:{title:"Dhall",owner:"RunDevelopment"},diff:{title:"Diff",owner:"uranusjr"},django:{title:"Django/Jinja2",require:"markup-templating",alias:"jinja2",owner:"romanvm"},"dns-zone-file":{title:"DNS zone file",owner:"RunDevelopment",alias:"dns-zone"},docker:{title:"Docker",alias:"dockerfile",owner:"JustinBeckwith"},dot:{title:"DOT (Graphviz)",alias:"gv",optional:"markup",owner:"RunDevelopment"},ebnf:{title:"EBNF",owner:"RunDevelopment"},editorconfig:{title:"EditorConfig",owner:"osipxd"},eiffel:{title:"Eiffel",owner:"Conaclos"},ejs:{title:"EJS",require:["javascript","markup-templating"],owner:"RunDevelopment",alias:"eta",aliasTitles:{eta:"Eta"}},elixir:{title:"Elixir",owner:"Golmote"},elm:{title:"Elm",owner:"zwilias"},etlua:{title:"Embedded Lua templating",require:["lua","markup-templating"],owner:"RunDevelopment"},erb:{title:"ERB",require:["ruby","markup-templating"],owner:"Golmote"},erlang:{title:"Erlang",owner:"Golmote"},"excel-formula":{title:"Excel Formula",alias:["xlsx","xls"],owner:"RunDevelopment"},fsharp:{title:"F#",require:"clike",owner:"simonreynolds7"},factor:{title:"Factor",owner:"catb0t"},false:{title:"False",owner:"edukisto"},"firestore-security-rules":{title:"Firestore security rules",require:"clike",owner:"RunDevelopment"},flow:{title:"Flow",require:"javascript",owner:"Golmote"},fortran:{title:"Fortran",owner:"Golmote"},ftl:{title:"FreeMarker Template Language",require:"markup-templating",owner:"RunDevelopment"},gml:{title:"GameMaker Language",alias:"gamemakerlanguage",require:"clike",owner:"LiarOnce"},gap:{title:"GAP (CAS)",owner:"RunDevelopment"},gcode:{title:"G-code",owner:"RunDevelopment"},gdscript:{title:"GDScript",owner:"RunDevelopment"},gedcom:{title:"GEDCOM",owner:"Golmote"},gettext:{title:"gettext",alias:"po",owner:"RunDevelopment"},gherkin:{title:"Gherkin",owner:"hason"},git:{title:"Git",owner:"lgiraudel"},glsl:{title:"GLSL",require:"c",owner:"Golmote"},gn:{title:"GN",alias:"gni",owner:"RunDevelopment"},"linker-script":{title:"GNU Linker Script",alias:"ld",owner:"RunDevelopment"},go:{title:"Go",require:"clike",owner:"arnehormann"},"go-module":{title:"Go module",alias:"go-mod",owner:"RunDevelopment"},gradle:{title:"Gradle",require:"clike",owner:"zeabdelkhalek-badido18"},graphql:{title:"GraphQL",optional:"markdown",owner:"Golmote"},groovy:{title:"Groovy",require:"clike",owner:"robfletcher"},haml:{title:"Haml",require:"ruby",optional:["css","css-extras","coffeescript","erb","javascript","less","markdown","scss","textile"],owner:"Golmote"},handlebars:{title:"Handlebars",require:"markup-templating",alias:["hbs","mustache"],aliasTitles:{mustache:"Mustache"},owner:"Golmote"},haskell:{title:"Haskell",alias:"hs",owner:"bholst"},haxe:{title:"Haxe",require:"clike",optional:"regex",owner:"Golmote"},hcl:{title:"HCL",owner:"outsideris"},hlsl:{title:"HLSL",require:"c",owner:"RunDevelopment"},hoon:{title:"Hoon",owner:"matildepark"},http:{title:"HTTP",optional:["csp","css","hpkp","hsts","javascript","json","markup","uri"],owner:"danielgtaylor"},hpkp:{title:"HTTP Public-Key-Pins",owner:"ScottHelme"},hsts:{title:"HTTP Strict-Transport-Security",owner:"ScottHelme"},ichigojam:{title:"IchigoJam",owner:"BlueCocoa"},icon:{title:"Icon",owner:"Golmote"},"icu-message-format":{title:"ICU Message Format",owner:"RunDevelopment"},idris:{title:"Idris",alias:"idr",owner:"KeenS",require:"haskell"},ignore:{title:".ignore",owner:"osipxd",alias:["gitignore","hgignore","npmignore"],aliasTitles:{gitignore:".gitignore",hgignore:".hgignore",npmignore:".npmignore"}},inform7:{title:"Inform 7",owner:"Golmote"},ini:{title:"Ini",owner:"aviaryan"},io:{title:"Io",owner:"AlesTsurko"},j:{title:"J",owner:"Golmote"},java:{title:"Java",require:"clike",owner:"sherblot"},javadoc:{title:"JavaDoc",require:["markup","java","javadoclike"],modify:"java",optional:"scala",owner:"RunDevelopment"},javadoclike:{title:"JavaDoc-like",modify:["java","javascript","php"],owner:"RunDevelopment"},javastacktrace:{title:"Java stack trace",owner:"RunDevelopment"},jexl:{title:"Jexl",owner:"czosel"},jolie:{title:"Jolie",require:"clike",owner:"thesave"},jq:{title:"JQ",owner:"RunDevelopment"},jsdoc:{title:"JSDoc",require:["javascript","javadoclike","typescript"],modify:"javascript",optional:["actionscript","coffeescript"],owner:"RunDevelopment"},"js-extras":{title:"JS Extras",require:"javascript",modify:"javascript",optional:["actionscript","coffeescript","flow","n4js","typescript"],owner:"RunDevelopment"},json:{title:"JSON",alias:"webmanifest",aliasTitles:{webmanifest:"Web App Manifest"},owner:"CupOfTea696"},json5:{title:"JSON5",require:"json",owner:"RunDevelopment"},jsonp:{title:"JSONP",require:"json",owner:"RunDevelopment"},jsstacktrace:{title:"JS stack trace",owner:"sbrl"},"js-templates":{title:"JS Templates",require:"javascript",modify:"javascript",optional:["css","css-extras","graphql","markdown","markup","sql"],owner:"RunDevelopment"},julia:{title:"Julia",owner:"cdagnino"},keepalived:{title:"Keepalived Configure",owner:"dev-itsheng"},keyman:{title:"Keyman",owner:"mcdurdin"},kotlin:{title:"Kotlin",alias:["kt","kts"],aliasTitles:{kts:"Kotlin Script"},require:"clike",owner:"Golmote"},kumir:{title:"KuMir (\u041a\u0443\u041c\u0438\u0440)",alias:"kum",owner:"edukisto"},kusto:{title:"Kusto",owner:"RunDevelopment"},latex:{title:"LaTeX",alias:["tex","context"],aliasTitles:{tex:"TeX",context:"ConTeXt"},owner:"japborst"},latte:{title:"Latte",require:["clike","markup-templating","php"],owner:"nette"},less:{title:"Less",require:"css",optional:"css-extras",owner:"Golmote"},lilypond:{title:"LilyPond",require:"scheme",alias:"ly",owner:"RunDevelopment"},liquid:{title:"Liquid",require:"markup-templating",owner:"cinhtau"},lisp:{title:"Lisp",alias:["emacs","elisp","emacs-lisp"],owner:"JuanCaicedo"},livescript:{title:"LiveScript",owner:"Golmote"},llvm:{title:"LLVM IR",owner:"porglezomp"},log:{title:"Log file",optional:"javastacktrace",owner:"RunDevelopment"},lolcode:{title:"LOLCODE",owner:"Golmote"},lua:{title:"Lua",owner:"Golmote"},magma:{title:"Magma (CAS)",owner:"RunDevelopment"},makefile:{title:"Makefile",owner:"Golmote"},markdown:{title:"Markdown",require:"markup",optional:"yaml",alias:"md",owner:"Golmote"},"markup-templating":{title:"Markup templating",require:"markup",owner:"Golmote"},mata:{title:"Mata",owner:"RunDevelopment"},matlab:{title:"MATLAB",owner:"Golmote"},maxscript:{title:"MAXScript",owner:"RunDevelopment"},mel:{title:"MEL",owner:"Golmote"},mermaid:{title:"Mermaid",owner:"RunDevelopment"},metafont:{title:"METAFONT",owner:"LaeriExNihilo"},mizar:{title:"Mizar",owner:"Golmote"},mongodb:{title:"MongoDB",owner:"airs0urce",require:"javascript"},monkey:{title:"Monkey",owner:"Golmote"},moonscript:{title:"MoonScript",alias:"moon",owner:"RunDevelopment"},n1ql:{title:"N1QL",owner:"TMWilds"},n4js:{title:"N4JS",require:"javascript",optional:"jsdoc",alias:"n4jsd",owner:"bsmith-n4"},"nand2tetris-hdl":{title:"Nand To Tetris HDL",owner:"stephanmax"},naniscript:{title:"Naninovel Script",owner:"Elringus",alias:"nani"},nasm:{title:"NASM",owner:"rbmj"},neon:{title:"NEON",owner:"nette"},nevod:{title:"Nevod",owner:"nezaboodka"},nginx:{title:"nginx",owner:"volado"},nim:{title:"Nim",owner:"Golmote"},nix:{title:"Nix",owner:"Golmote"},nsis:{title:"NSIS",owner:"idleberg"},objectivec:{title:"Objective-C",require:"c",alias:"objc",owner:"uranusjr"},ocaml:{title:"OCaml",owner:"Golmote"},odin:{title:"Odin",owner:"edukisto"},opencl:{title:"OpenCL",require:"c",modify:["c","cpp"],owner:"Milania1"},openqasm:{title:"OpenQasm",alias:"qasm",owner:"RunDevelopment"},oz:{title:"Oz",owner:"Golmote"},parigp:{title:"PARI/GP",owner:"Golmote"},parser:{title:"Parser",require:"markup",owner:"Golmote"},pascal:{title:"Pascal",alias:"objectpascal",aliasTitles:{objectpascal:"Object Pascal"},owner:"Golmote"},pascaligo:{title:"Pascaligo",owner:"DefinitelyNotAGoat"},psl:{title:"PATROL Scripting Language",owner:"bertysentry"},pcaxis:{title:"PC-Axis",alias:"px",owner:"RunDevelopment"},peoplecode:{title:"PeopleCode",alias:"pcode",owner:"RunDevelopment"},perl:{title:"Perl",owner:"Golmote"},php:{title:"PHP",require:"markup-templating",owner:"milesj"},phpdoc:{title:"PHPDoc",require:["php","javadoclike"],modify:"php",owner:"RunDevelopment"},"php-extras":{title:"PHP Extras",require:"php",modify:"php",owner:"milesj"},"plant-uml":{title:"PlantUML",alias:"plantuml",owner:"RunDevelopment"},plsql:{title:"PL/SQL",require:"sql",owner:"Golmote"},powerquery:{title:"PowerQuery",alias:["pq","mscript"],owner:"peterbud"},powershell:{title:"PowerShell",owner:"nauzilus"},processing:{title:"Processing",require:"clike",owner:"Golmote"},prolog:{title:"Prolog",owner:"Golmote"},promql:{title:"PromQL",owner:"arendjr"},properties:{title:".properties",owner:"Golmote"},protobuf:{title:"Protocol Buffers",require:"clike",owner:"just-boris"},pug:{title:"Pug",require:["markup","javascript"],optional:["coffeescript","ejs","handlebars","less","livescript","markdown","scss","stylus","twig"],owner:"Golmote"},puppet:{title:"Puppet",owner:"Golmote"},pure:{title:"Pure",optional:["c","cpp","fortran"],owner:"Golmote"},purebasic:{title:"PureBasic",require:"clike",alias:"pbfasm",owner:"HeX0R101"},purescript:{title:"PureScript",require:"haskell",alias:"purs",owner:"sriharshachilakapati"},python:{title:"Python",alias:"py",owner:"multipetros"},qsharp:{title:"Q#",require:"clike",alias:"qs",owner:"fedonman"},q:{title:"Q (kdb+ database)",owner:"Golmote"},qml:{title:"QML",require:"javascript",owner:"RunDevelopment"},qore:{title:"Qore",require:"clike",owner:"temnroegg"},r:{title:"R",owner:"Golmote"},racket:{title:"Racket",require:"scheme",alias:"rkt",owner:"RunDevelopment"},cshtml:{title:"Razor C#",alias:"razor",require:["markup","csharp"],optional:["css","css-extras","javascript","js-extras"],owner:"RunDevelopment"},jsx:{title:"React JSX",require:["markup","javascript"],optional:["jsdoc","js-extras","js-templates"],owner:"vkbansal"},tsx:{title:"React TSX",require:["jsx","typescript"]},reason:{title:"Reason",require:"clike",owner:"Golmote"},regex:{title:"Regex",owner:"RunDevelopment"},rego:{title:"Rego",owner:"JordanSh"},renpy:{title:"Ren'py",alias:"rpy",owner:"HyuchiaDiego"},rescript:{title:"ReScript",alias:"res",owner:"vmarcosp"},rest:{title:"reST (reStructuredText)",owner:"Golmote"},rip:{title:"Rip",owner:"ravinggenius"},roboconf:{title:"Roboconf",owner:"Golmote"},robotframework:{title:"Robot Framework",alias:"robot",owner:"RunDevelopment"},ruby:{title:"Ruby",require:"clike",alias:"rb",owner:"samflores"},rust:{title:"Rust",owner:"Golmote"},sas:{title:"SAS",optional:["groovy","lua","sql"],owner:"Golmote"},sass:{title:"Sass (Sass)",require:"css",optional:"css-extras",owner:"Golmote"},scss:{title:"Sass (SCSS)",require:"css",optional:"css-extras",owner:"MoOx"},scala:{title:"Scala",require:"java",owner:"jozic"},scheme:{title:"Scheme",owner:"bacchus123"},"shell-session":{title:"Shell session",require:"bash",alias:["sh-session","shellsession"],owner:"RunDevelopment"},smali:{title:"Smali",owner:"RunDevelopment"},smalltalk:{title:"Smalltalk",owner:"Golmote"},smarty:{title:"Smarty",require:"markup-templating",optional:"php",owner:"Golmote"},sml:{title:"SML",alias:"smlnj",aliasTitles:{smlnj:"SML/NJ"},owner:"RunDevelopment"},solidity:{title:"Solidity (Ethereum)",alias:"sol",require:"clike",owner:"glachaud"},"solution-file":{title:"Solution file",alias:"sln",owner:"RunDevelopment"},soy:{title:"Soy (Closure Template)",require:"markup-templating",owner:"Golmote"},sparql:{title:"SPARQL",require:"turtle",owner:"Triply-Dev",alias:"rq"},"splunk-spl":{title:"Splunk SPL",owner:"RunDevelopment"},sqf:{title:"SQF: Status Quo Function (Arma 3)",require:"clike",owner:"RunDevelopment"},sql:{title:"SQL",owner:"multipetros"},squirrel:{title:"Squirrel",require:"clike",owner:"RunDevelopment"},stan:{title:"Stan",owner:"RunDevelopment"},stata:{title:"Stata Ado",require:["mata","java","python"],owner:"RunDevelopment"},iecst:{title:"Structured Text (IEC 61131-3)",owner:"serhioromano"},stylus:{title:"Stylus",owner:"vkbansal"},supercollider:{title:"SuperCollider",alias:"sclang",owner:"RunDevelopment"},swift:{title:"Swift",owner:"chrischares"},systemd:{title:"Systemd configuration file",owner:"RunDevelopment"},"t4-templating":{title:"T4 templating",owner:"RunDevelopment"},"t4-cs":{title:"T4 Text Templates (C#)",require:["t4-templating","csharp"],alias:"t4",owner:"RunDevelopment"},"t4-vb":{title:"T4 Text Templates (VB)",require:["t4-templating","vbnet"],owner:"RunDevelopment"},tap:{title:"TAP",owner:"isaacs",require:"yaml"},tcl:{title:"Tcl",owner:"PeterChaplin"},tt2:{title:"Template Toolkit 2",require:["clike","markup-templating"],owner:"gflohr"},textile:{title:"Textile",require:"markup",optional:"css",owner:"Golmote"},toml:{title:"TOML",owner:"RunDevelopment"},tremor:{title:"Tremor",alias:["trickle","troy"],owner:"darach",aliasTitles:{trickle:"trickle",troy:"troy"}},turtle:{title:"Turtle",alias:"trig",aliasTitles:{trig:"TriG"},owner:"jakubklimek"},twig:{title:"Twig",require:"markup-templating",owner:"brandonkelly"},typescript:{title:"TypeScript",require:"javascript",optional:"js-templates",alias:"ts",owner:"vkbansal"},typoscript:{title:"TypoScript",alias:"tsconfig",aliasTitles:{tsconfig:"TSConfig"},owner:"dkern"},unrealscript:{title:"UnrealScript",alias:["uscript","uc"],owner:"RunDevelopment"},uorazor:{title:"UO Razor Script",owner:"jaseowns"},uri:{title:"URI",alias:"url",aliasTitles:{url:"URL"},owner:"RunDevelopment"},v:{title:"V",require:"clike",owner:"taggon"},vala:{title:"Vala",require:"clike",optional:"regex",owner:"TemplarVolk"},vbnet:{title:"VB.Net",require:"basic",owner:"Bigsby"},velocity:{title:"Velocity",require:"markup",owner:"Golmote"},verilog:{title:"Verilog",owner:"a-rey"},vhdl:{title:"VHDL",owner:"a-rey"},vim:{title:"vim",owner:"westonganger"},"visual-basic":{title:"Visual Basic",alias:["vb","vba"],aliasTitles:{vba:"VBA"},owner:"Golmote"},warpscript:{title:"WarpScript",owner:"RunDevelopment"},wasm:{title:"WebAssembly",owner:"Golmote"},"web-idl":{title:"Web IDL",alias:"webidl",owner:"RunDevelopment"},wgsl:{title:"WGSL",owner:"Dr4gonthree"},wiki:{title:"Wiki markup",require:"markup",owner:"Golmote"},wolfram:{title:"Wolfram language",alias:["mathematica","nb","wl"],aliasTitles:{mathematica:"Mathematica",nb:"Mathematica Notebook"},owner:"msollami"},wren:{title:"Wren",owner:"clsource"},xeora:{title:"Xeora",require:"markup",alias:"xeoracube",aliasTitles:{xeoracube:"XeoraCube"},owner:"freakmaxi"},"xml-doc":{title:"XML doc (.net)",require:"markup",modify:["csharp","fsharp","vbnet"],owner:"RunDevelopment"},xojo:{title:"Xojo (REALbasic)",owner:"Golmote"},xquery:{title:"XQuery",require:"markup",owner:"Golmote"},yaml:{title:"YAML",alias:"yml",owner:"hason"},yang:{title:"YANG",owner:"RunDevelopment"},zig:{title:"Zig",owner:"RunDevelopment"}},plugins:{meta:{path:"plugins/{id}/prism-{id}",link:"plugins/{id}/"},"line-highlight":{title:"Line Highlight",description:"Highlights specific lines and/or line ranges."},"line-numbers":{title:"Line Numbers",description:"Line number at the beginning of code lines.",owner:"kuba-kubula"},"show-invisibles":{title:"Show Invisibles",description:"Show hidden characters such as tabs and line breaks.",optional:["autolinker","data-uri-highlight"]},autolinker:{title:"Autolinker",description:"Converts URLs and emails in code to clickable links. Parses Markdown links in comments."},wpd:{title:"WebPlatform Docs",description:'Makes tokens link to <a href="https://webplatform.github.io/docs/">WebPlatform.org documentation</a>. The links open in a new tab.'},"custom-class":{title:"Custom Class",description:"This plugin allows you to prefix Prism's default classes (<code>.comment</code> can become <code>.namespace--comment</code>) or replace them with your defined ones (like <code>.editor__comment</code>). You can even add new classes.",owner:"dvkndn",noCSS:!0},"file-highlight":{title:"File Highlight",description:"Fetch external files and highlight them with Prism. Used on the Prism website itself.",noCSS:!0},"show-language":{title:"Show Language",description:"Display the highlighted language in code blocks (inline code does not show the label).",owner:"nauzilus",noCSS:!0,require:"toolbar"},"jsonp-highlight":{title:"JSONP Highlight",description:"Fetch content with JSONP and highlight some interesting content (e.g. GitHub/Gists or Bitbucket API).",noCSS:!0,owner:"nauzilus"},"highlight-keywords":{title:"Highlight Keywords",description:"Adds special CSS classes for each keyword for fine-grained highlighting.",owner:"vkbansal",noCSS:!0},"remove-initial-line-feed":{title:"Remove initial line feed",description:"Removes the initial line feed in code blocks.",owner:"Golmote",noCSS:!0},"inline-color":{title:"Inline color",description:"Adds a small inline preview for colors in style sheets.",require:"css-extras",owner:"RunDevelopment"},previewers:{title:"Previewers",description:"Previewers for angles, colors, gradients, easing and time.",require:"css-extras",owner:"Golmote"},autoloader:{title:"Autoloader",description:"Automatically loads the needed languages to highlight the code blocks.",owner:"Golmote",noCSS:!0},"keep-markup":{title:"Keep Markup",description:"Prevents custom markup from being dropped out during highlighting.",owner:"Golmote",optional:"normalize-whitespace",noCSS:!0},"command-line":{title:"Command Line",description:"Display a command line with a prompt and, optionally, the output/response from the commands.",owner:"chriswells0"},"unescaped-markup":{title:"Unescaped Markup",description:"Write markup without having to escape anything."},"normalize-whitespace":{title:"Normalize Whitespace",description:"Supports multiple operations to normalize whitespace in code blocks.",owner:"zeitgeist87",optional:"unescaped-markup",noCSS:!0},"data-uri-highlight":{title:"Data-URI Highlight",description:"Highlights data-URI contents.",owner:"Golmote",noCSS:!0},toolbar:{title:"Toolbar",description:"Attach a toolbar for plugins to easily register buttons on the top of a code block.",owner:"mAAdhaTTah"},"copy-to-clipboard":{title:"Copy to Clipboard Button",description:"Add a button that copies the code block to the clipboard when clicked.",owner:"mAAdhaTTah",require:"toolbar",noCSS:!0},"download-button":{title:"Download Button",description:"A button in the toolbar of a code block adding a convenient way to download a code file.",owner:"Golmote",require:"toolbar",noCSS:!0},"match-braces":{title:"Match braces",description:"Highlights matching braces.",owner:"RunDevelopment"},"diff-highlight":{title:"Diff Highlight",description:"Highlights the code inside diff blocks.",owner:"RunDevelopment",require:"diff"},"filter-highlight-all":{title:"Filter highlightAll",description:"Filters the elements the <code>highlightAll</code> and <code>highlightAllUnder</code> methods actually highlight.",owner:"RunDevelopment",noCSS:!0},treeview:{title:"Treeview",description:"A language with special styles to highlight file system tree structures.",owner:"Golmote"}}})},2885:(e,t,n)=>{const r=n(9901),a=n(9642),o=new Set;function i(e){void 0===e?e=Object.keys(r.languages).filter((e=>"meta"!=e)):Array.isArray(e)||(e=[e]);const t=[...o,...Object.keys(Prism.languages)];a(r,e,t).load((e=>{if(!(e in r.languages))return void(i.silent||console.warn("Language does not exist: "+e));const t="./prism-"+e;delete n.c[n(6500).resolve(t)],delete Prism.languages[e],n(6500)(t),o.add(e)}))}i.silent=!1,e.exports=i},6726:(e,t,n)=>{var r={"./":2885};function a(e){var t=o(e);return n(t)}function o(e){if(!n.o(r,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return r[e]}a.keys=function(){return Object.keys(r)},a.resolve=o,e.exports=a,a.id=6726},6500:(e,t,n)=>{var r={"./":2885};function a(e){var t=o(e);return n(t)}function o(e){if(!n.o(r,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return r[e]}a.keys=function(){return Object.keys(r)},a.resolve=o,e.exports=a,a.id=6500},9642:e=>{"use strict";var t=function(){var e=function(){};function t(e,t){Array.isArray(e)?e.forEach(t):null!=e&&t(e,0)}function n(e){for(var t={},n=0,r=e.length;n<r;n++)t[e[n]]=!0;return t}function r(e){var n={},r=[];function a(r,o){if(!(r in n)){o.push(r);var i=o.indexOf(r);if(i<o.length-1)throw new Error("Circular dependency: "+o.slice(i).join(" -> "));var l={},s=e[r];if(s){function c(t){if(!(t in e))throw new Error(r+" depends on an unknown component "+t);if(!(t in l))for(var i in a(t,o),l[t]=!0,n[t])l[i]=!0}t(s.require,c),t(s.optional,c),t(s.modify,c)}n[r]=l,o.pop()}}return function(e){var t=n[e];return t||(a(e,r),t=n[e]),t}}function a(e){for(var t in e)return!0;return!1}return function(o,i,l){var s=function(e){var t={};for(var n in e){var r=e[n];for(var a in r)if("meta"!=a){var o=r[a];t[a]="string"==typeof o?{title:o}:o}}return t}(o),c=function(e){var n;return function(r){if(r in e)return r;if(!n)for(var a in n={},e){var o=e[a];t(o&&o.alias,(function(t){if(t in n)throw new Error(t+" cannot be alias for both "+a+" and "+n[t]);if(t in e)throw new Error(t+" cannot be alias of "+a+" because it is a component.");n[t]=a}))}return n[r]||r}}(s);i=i.map(c),l=(l||[]).map(c);var u=n(i),d=n(l);i.forEach((function e(n){var r=s[n];t(r&&r.require,(function(t){t in d||(u[t]=!0,e(t))}))}));for(var p,f=r(s),m=u;a(m);){for(var h in p={},m){var g=s[h];t(g&&g.modify,(function(e){e in d&&(p[e]=!0)}))}for(var b in d)if(!(b in u))for(var v in f(b))if(v in u){p[b]=!0;break}for(var y in m=p)u[y]=!0}var w={getIds:function(){var e=[];return w.load((function(t){e.push(t)})),e},load:function(t,n){return function(t,n,r,a){var o=a?a.series:void 0,i=a?a.parallel:e,l={},s={};function c(e){if(e in l)return l[e];s[e]=!0;var a,u=[];for(var d in t(e))d in n&&u.push(d);if(0===u.length)a=r(e);else{var p=i(u.map((function(e){var t=c(e);return delete s[e],t})));o?a=o(p,(function(){return r(e)})):r(e)}return l[e]=a}for(var u in n)c(u);var d=[];for(var p in s)d.push(l[p]);return i(d)}(f,u,t,n)}};return w}}();e.exports=t},2703:(e,t,n)=>{"use strict";var r=n(414);function a(){}function o(){}o.resetWarningCache=a,e.exports=function(){function e(e,t,n,a,o,i){if(i!==r){var l=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw l.name="Invariant Violation",l}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:o,resetWarningCache:a};return n.PropTypes=n,n}},5697:(e,t,n)=>{e.exports=n(2703)()},414:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},4448:(e,t,n)=>{"use strict";var r=n(7294),a=n(7418),o=n(3840);function i(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n<arguments.length;n++)t+="&args[]="+encodeURIComponent(arguments[n]);return"Minified React error #"+e+"; visit "+t+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}if(!r)throw Error(i(227));var l=new Set,s={};function c(e,t){u(e,t),u(e+"Capture",t)}function u(e,t){for(s[e]=t,e=0;e<t.length;e++)l.add(t[e])}var d=!("undefined"==typeof window||void 0===window.document||void 0===window.document.createElement),p=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,f=Object.prototype.hasOwnProperty,m={},h={};function g(e,t,n,r,a,o,i){this.acceptsBooleans=2===t||3===t||4===t,this.attributeName=r,this.attributeNamespace=a,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=o,this.removeEmptyString=i}var b={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach((function(e){b[e]=new g(e,0,!1,e,null,!1,!1)})),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach((function(e){var t=e[0];b[t]=new g(t,1,!1,e[1],null,!1,!1)})),["contentEditable","draggable","spellCheck","value"].forEach((function(e){b[e]=new g(e,2,!1,e.toLowerCase(),null,!1,!1)})),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach((function(e){b[e]=new g(e,2,!1,e,null,!1,!1)})),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach((function(e){b[e]=new g(e,3,!1,e.toLowerCase(),null,!1,!1)})),["checked","multiple","muted","selected"].forEach((function(e){b[e]=new g(e,3,!0,e,null,!1,!1)})),["capture","download"].forEach((function(e){b[e]=new g(e,4,!1,e,null,!1,!1)})),["cols","rows","size","span"].forEach((function(e){b[e]=new g(e,6,!1,e,null,!1,!1)})),["rowSpan","start"].forEach((function(e){b[e]=new g(e,5,!1,e.toLowerCase(),null,!1,!1)}));var v=/[\-:]([a-z])/g;function y(e){return e[1].toUpperCase()}function w(e,t,n,r){var a=b.hasOwnProperty(t)?b[t]:null;(null!==a?0===a.type:!r&&(2<t.length&&("o"===t[0]||"O"===t[0])&&("n"===t[1]||"N"===t[1])))||(function(e,t,n,r){if(null==t||function(e,t,n,r){if(null!==n&&0===n.type)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return!r&&(null!==n?!n.acceptsBooleans:"data-"!==(e=e.toLowerCase().slice(0,5))&&"aria-"!==e);default:return!1}}(e,t,n,r))return!0;if(r)return!1;if(null!==n)switch(n.type){case 3:return!t;case 4:return!1===t;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}(t,n,a,r)&&(n=null),r||null===a?function(e){return!!f.call(h,e)||!f.call(m,e)&&(p.test(e)?h[e]=!0:(m[e]=!0,!1))}(t)&&(null===n?e.removeAttribute(t):e.setAttribute(t,""+n)):a.mustUseProperty?e[a.propertyName]=null===n?3!==a.type&&"":n:(t=a.attributeName,r=a.attributeNamespace,null===n?e.removeAttribute(t):(n=3===(a=a.type)||4===a&&!0===n?"":""+n,r?e.setAttributeNS(r,t,n):e.setAttribute(t,n))))}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach((function(e){var t=e.replace(v,y);b[t]=new g(t,1,!1,e,null,!1,!1)})),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach((function(e){var t=e.replace(v,y);b[t]=new g(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)})),["xml:base","xml:lang","xml:space"].forEach((function(e){var t=e.replace(v,y);b[t]=new g(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)})),["tabIndex","crossOrigin"].forEach((function(e){b[e]=new g(e,1,!1,e.toLowerCase(),null,!1,!1)})),b.xlinkHref=new g("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach((function(e){b[e]=new g(e,1,!1,e.toLowerCase(),null,!0,!0)}));var k=r.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,E=60103,x=60106,S=60107,_=60108,C=60114,T=60109,A=60110,L=60112,R=60113,N=60120,O=60115,P=60116,I=60121,D=60128,M=60129,F=60130,B=60131;if("function"==typeof Symbol&&Symbol.for){var j=Symbol.for;E=j("react.element"),x=j("react.portal"),S=j("react.fragment"),_=j("react.strict_mode"),C=j("react.profiler"),T=j("react.provider"),A=j("react.context"),L=j("react.forward_ref"),R=j("react.suspense"),N=j("react.suspense_list"),O=j("react.memo"),P=j("react.lazy"),I=j("react.block"),j("react.scope"),D=j("react.opaque.id"),M=j("react.debug_trace_mode"),F=j("react.offscreen"),B=j("react.legacy_hidden")}var z,U="function"==typeof Symbol&&Symbol.iterator;function q(e){return null===e||"object"!=typeof e?null:"function"==typeof(e=U&&e[U]||e["@@iterator"])?e:null}function $(e){if(void 0===z)try{throw Error()}catch(n){var t=n.stack.trim().match(/\n( *(at )?)/);z=t&&t[1]||""}return"\n"+z+e}var H=!1;function G(e,t){if(!e||H)return"";H=!0;var n=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{if(t)if(t=function(){throw Error()},Object.defineProperty(t.prototype,"props",{set:function(){throw Error()}}),"object"==typeof Reflect&&Reflect.construct){try{Reflect.construct(t,[])}catch(s){var r=s}Reflect.construct(e,[],t)}else{try{t.call()}catch(s){r=s}e.call(t.prototype)}else{try{throw Error()}catch(s){r=s}e()}}catch(s){if(s&&r&&"string"==typeof s.stack){for(var a=s.stack.split("\n"),o=r.stack.split("\n"),i=a.length-1,l=o.length-1;1<=i&&0<=l&&a[i]!==o[l];)l--;for(;1<=i&&0<=l;i--,l--)if(a[i]!==o[l]){if(1!==i||1!==l)do{if(i--,0>--l||a[i]!==o[l])return"\n"+a[i].replace(" at new "," at ")}while(1<=i&&0<=l);break}}}finally{H=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?$(e):""}function Z(e){switch(e.tag){case 5:return $(e.type);case 16:return $("Lazy");case 13:return $("Suspense");case 19:return $("SuspenseList");case 0:case 2:case 15:return e=G(e.type,!1);case 11:return e=G(e.type.render,!1);case 22:return e=G(e.type._render,!1);case 1:return e=G(e.type,!0);default:return""}}function V(e){if(null==e)return null;if("function"==typeof e)return e.displayName||e.name||null;if("string"==typeof e)return e;switch(e){case S:return"Fragment";case x:return"Portal";case C:return"Profiler";case _:return"StrictMode";case R:return"Suspense";case N:return"SuspenseList"}if("object"==typeof e)switch(e.$$typeof){case A:return(e.displayName||"Context")+".Consumer";case T:return(e._context.displayName||"Context")+".Provider";case L:var t=e.render;return t=t.displayName||t.name||"",e.displayName||(""!==t?"ForwardRef("+t+")":"ForwardRef");case O:return V(e.type);case I:return V(e._render);case P:t=e._payload,e=e._init;try{return V(e(t))}catch(n){}}return null}function W(e){switch(typeof e){case"boolean":case"number":case"object":case"string":case"undefined":return e;default:return""}}function Y(e){var t=e.type;return(e=e.nodeName)&&"input"===e.toLowerCase()&&("checkbox"===t||"radio"===t)}function K(e){e._valueTracker||(e._valueTracker=function(e){var t=Y(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&void 0!==n&&"function"==typeof n.get&&"function"==typeof n.set){var a=n.get,o=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return a.call(this)},set:function(e){r=""+e,o.call(this,e)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(e){r=""+e},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}(e))}function Q(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=Y(e)?e.checked?"true":"false":e.value),(e=r)!==n&&(t.setValue(e),!0)}function X(e){if(void 0===(e=e||("undefined"!=typeof document?document:void 0)))return null;try{return e.activeElement||e.body}catch(t){return e.body}}function J(e,t){var n=t.checked;return a({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:null!=n?n:e._wrapperState.initialChecked})}function ee(e,t){var n=null==t.defaultValue?"":t.defaultValue,r=null!=t.checked?t.checked:t.defaultChecked;n=W(null!=t.value?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:"checkbox"===t.type||"radio"===t.type?null!=t.checked:null!=t.value}}function te(e,t){null!=(t=t.checked)&&w(e,"checked",t,!1)}function ne(e,t){te(e,t);var n=W(t.value),r=t.type;if(null!=n)"number"===r?(0===n&&""===e.value||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if("submit"===r||"reset"===r)return void e.removeAttribute("value");t.hasOwnProperty("value")?ae(e,t.type,n):t.hasOwnProperty("defaultValue")&&ae(e,t.type,W(t.defaultValue)),null==t.checked&&null!=t.defaultChecked&&(e.defaultChecked=!!t.defaultChecked)}function re(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!("submit"!==r&&"reset"!==r||void 0!==t.value&&null!==t.value))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}""!==(n=e.name)&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,""!==n&&(e.name=n)}function ae(e,t,n){"number"===t&&X(e.ownerDocument)===e||(null==n?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}function oe(e,t){return e=a({children:void 0},t),(t=function(e){var t="";return r.Children.forEach(e,(function(e){null!=e&&(t+=e)})),t}(t.children))&&(e.children=t),e}function ie(e,t,n,r){if(e=e.options,t){t={};for(var a=0;a<n.length;a++)t["$"+n[a]]=!0;for(n=0;n<e.length;n++)a=t.hasOwnProperty("$"+e[n].value),e[n].selected!==a&&(e[n].selected=a),a&&r&&(e[n].defaultSelected=!0)}else{for(n=""+W(n),t=null,a=0;a<e.length;a++){if(e[a].value===n)return e[a].selected=!0,void(r&&(e[a].defaultSelected=!0));null!==t||e[a].disabled||(t=e[a])}null!==t&&(t.selected=!0)}}function le(e,t){if(null!=t.dangerouslySetInnerHTML)throw Error(i(91));return a({},t,{value:void 0,defaultValue:void 0,children:""+e._wrapperState.initialValue})}function se(e,t){var n=t.value;if(null==n){if(n=t.children,t=t.defaultValue,null!=n){if(null!=t)throw Error(i(92));if(Array.isArray(n)){if(!(1>=n.length))throw Error(i(93));n=n[0]}t=n}null==t&&(t=""),n=t}e._wrapperState={initialValue:W(n)}}function ce(e,t){var n=W(t.value),r=W(t.defaultValue);null!=n&&((n=""+n)!==e.value&&(e.value=n),null==t.defaultValue&&e.defaultValue!==n&&(e.defaultValue=n)),null!=r&&(e.defaultValue=""+r)}function ue(e){var t=e.textContent;t===e._wrapperState.initialValue&&""!==t&&null!==t&&(e.value=t)}var de="http://www.w3.org/1999/xhtml",pe="http://www.w3.org/2000/svg";function fe(e){switch(e){case"svg":return"http://www.w3.org/2000/svg";case"math":return"http://www.w3.org/1998/Math/MathML";default:return"http://www.w3.org/1999/xhtml"}}function me(e,t){return null==e||"http://www.w3.org/1999/xhtml"===e?fe(t):"http://www.w3.org/2000/svg"===e&&"foreignObject"===t?"http://www.w3.org/1999/xhtml":e}var he,ge,be=(ge=function(e,t){if(e.namespaceURI!==pe||"innerHTML"in e)e.innerHTML=t;else{for((he=he||document.createElement("div")).innerHTML="<svg>"+t.valueOf().toString()+"</svg>",t=he.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}},"undefined"!=typeof MSApp&&MSApp.execUnsafeLocalFunction?function(e,t,n,r){MSApp.execUnsafeLocalFunction((function(){return ge(e,t)}))}:ge);function ve(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&3===n.nodeType)return void(n.nodeValue=t)}e.textContent=t}var ye={animationIterationCount:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},we=["Webkit","ms","Moz","O"];function ke(e,t,n){return null==t||"boolean"==typeof t||""===t?"":n||"number"!=typeof t||0===t||ye.hasOwnProperty(e)&&ye[e]?(""+t).trim():t+"px"}function Ee(e,t){for(var n in e=e.style,t)if(t.hasOwnProperty(n)){var r=0===n.indexOf("--"),a=ke(n,t[n],r);"float"===n&&(n="cssFloat"),r?e.setProperty(n,a):e[n]=a}}Object.keys(ye).forEach((function(e){we.forEach((function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),ye[t]=ye[e]}))}));var xe=a({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Se(e,t){if(t){if(xe[e]&&(null!=t.children||null!=t.dangerouslySetInnerHTML))throw Error(i(137,e));if(null!=t.dangerouslySetInnerHTML){if(null!=t.children)throw Error(i(60));if("object"!=typeof t.dangerouslySetInnerHTML||!("__html"in t.dangerouslySetInnerHTML))throw Error(i(61))}if(null!=t.style&&"object"!=typeof t.style)throw Error(i(62))}}function _e(e,t){if(-1===e.indexOf("-"))return"string"==typeof t.is;switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}function Ce(e){return(e=e.target||e.srcElement||window).correspondingUseElement&&(e=e.correspondingUseElement),3===e.nodeType?e.parentNode:e}var Te=null,Ae=null,Le=null;function Re(e){if(e=ra(e)){if("function"!=typeof Te)throw Error(i(280));var t=e.stateNode;t&&(t=oa(t),Te(e.stateNode,e.type,t))}}function Ne(e){Ae?Le?Le.push(e):Le=[e]:Ae=e}function Oe(){if(Ae){var e=Ae,t=Le;if(Le=Ae=null,Re(e),t)for(e=0;e<t.length;e++)Re(t[e])}}function Pe(e,t){return e(t)}function Ie(e,t,n,r,a){return e(t,n,r,a)}function De(){}var Me=Pe,Fe=!1,Be=!1;function je(){null===Ae&&null===Le||(De(),Oe())}function ze(e,t){var n=e.stateNode;if(null===n)return null;var r=oa(n);if(null===r)return null;n=r[t];e:switch(t){case"onClick":case"onClickCapture":case"onDoubleClick":case"onDoubleClickCapture":case"onMouseDown":case"onMouseDownCapture":case"onMouseMove":case"onMouseMoveCapture":case"onMouseUp":case"onMouseUpCapture":case"onMouseEnter":(r=!r.disabled)||(r=!("button"===(e=e.type)||"input"===e||"select"===e||"textarea"===e)),e=!r;break e;default:e=!1}if(e)return null;if(n&&"function"!=typeof n)throw Error(i(231,t,typeof n));return n}var Ue=!1;if(d)try{var qe={};Object.defineProperty(qe,"passive",{get:function(){Ue=!0}}),window.addEventListener("test",qe,qe),window.removeEventListener("test",qe,qe)}catch(ge){Ue=!1}function $e(e,t,n,r,a,o,i,l,s){var c=Array.prototype.slice.call(arguments,3);try{t.apply(n,c)}catch(u){this.onError(u)}}var He=!1,Ge=null,Ze=!1,Ve=null,We={onError:function(e){He=!0,Ge=e}};function Ye(e,t,n,r,a,o,i,l,s){He=!1,Ge=null,$e.apply(We,arguments)}function Ke(e){var t=e,n=e;if(e.alternate)for(;t.return;)t=t.return;else{e=t;do{0!=(1026&(t=e).flags)&&(n=t.return),e=t.return}while(e)}return 3===t.tag?n:null}function Qe(e){if(13===e.tag){var t=e.memoizedState;if(null===t&&(null!==(e=e.alternate)&&(t=e.memoizedState)),null!==t)return t.dehydrated}return null}function Xe(e){if(Ke(e)!==e)throw Error(i(188))}function Je(e){if(e=function(e){var t=e.alternate;if(!t){if(null===(t=Ke(e)))throw Error(i(188));return t!==e?null:e}for(var n=e,r=t;;){var a=n.return;if(null===a)break;var o=a.alternate;if(null===o){if(null!==(r=a.return)){n=r;continue}break}if(a.child===o.child){for(o=a.child;o;){if(o===n)return Xe(a),e;if(o===r)return Xe(a),t;o=o.sibling}throw Error(i(188))}if(n.return!==r.return)n=a,r=o;else{for(var l=!1,s=a.child;s;){if(s===n){l=!0,n=a,r=o;break}if(s===r){l=!0,r=a,n=o;break}s=s.sibling}if(!l){for(s=o.child;s;){if(s===n){l=!0,n=o,r=a;break}if(s===r){l=!0,r=o,n=a;break}s=s.sibling}if(!l)throw Error(i(189))}}if(n.alternate!==r)throw Error(i(190))}if(3!==n.tag)throw Error(i(188));return n.stateNode.current===n?e:t}(e),!e)return null;for(var t=e;;){if(5===t.tag||6===t.tag)return t;if(t.child)t.child.return=t,t=t.child;else{if(t===e)break;for(;!t.sibling;){if(!t.return||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}}return null}function et(e,t){for(var n=e.alternate;null!==t;){if(t===e||t===n)return!0;t=t.return}return!1}var tt,nt,rt,at,ot=!1,it=[],lt=null,st=null,ct=null,ut=new Map,dt=new Map,pt=[],ft="mousedown mouseup touchcancel touchend touchstart auxclick dblclick pointercancel pointerdown pointerup dragend dragstart drop compositionend compositionstart keydown keypress keyup input textInput copy cut paste click change contextmenu reset submit".split(" ");function mt(e,t,n,r,a){return{blockedOn:e,domEventName:t,eventSystemFlags:16|n,nativeEvent:a,targetContainers:[r]}}function ht(e,t){switch(e){case"focusin":case"focusout":lt=null;break;case"dragenter":case"dragleave":st=null;break;case"mouseover":case"mouseout":ct=null;break;case"pointerover":case"pointerout":ut.delete(t.pointerId);break;case"gotpointercapture":case"lostpointercapture":dt.delete(t.pointerId)}}function gt(e,t,n,r,a,o){return null===e||e.nativeEvent!==o?(e=mt(t,n,r,a,o),null!==t&&(null!==(t=ra(t))&&nt(t)),e):(e.eventSystemFlags|=r,t=e.targetContainers,null!==a&&-1===t.indexOf(a)&&t.push(a),e)}function bt(e){var t=na(e.target);if(null!==t){var n=Ke(t);if(null!==n)if(13===(t=n.tag)){if(null!==(t=Qe(n)))return e.blockedOn=t,void at(e.lanePriority,(function(){o.unstable_runWithPriority(e.priority,(function(){rt(n)}))}))}else if(3===t&&n.stateNode.hydrate)return void(e.blockedOn=3===n.tag?n.stateNode.containerInfo:null)}e.blockedOn=null}function vt(e){if(null!==e.blockedOn)return!1;for(var t=e.targetContainers;0<t.length;){var n=Jt(e.domEventName,e.eventSystemFlags,t[0],e.nativeEvent);if(null!==n)return null!==(t=ra(n))&&nt(t),e.blockedOn=n,!1;t.shift()}return!0}function yt(e,t,n){vt(e)&&n.delete(t)}function wt(){for(ot=!1;0<it.length;){var e=it[0];if(null!==e.blockedOn){null!==(e=ra(e.blockedOn))&&tt(e);break}for(var t=e.targetContainers;0<t.length;){var n=Jt(e.domEventName,e.eventSystemFlags,t[0],e.nativeEvent);if(null!==n){e.blockedOn=n;break}t.shift()}null===e.blockedOn&&it.shift()}null!==lt&&vt(lt)&&(lt=null),null!==st&&vt(st)&&(st=null),null!==ct&&vt(ct)&&(ct=null),ut.forEach(yt),dt.forEach(yt)}function kt(e,t){e.blockedOn===t&&(e.blockedOn=null,ot||(ot=!0,o.unstable_scheduleCallback(o.unstable_NormalPriority,wt)))}function Et(e){function t(t){return kt(t,e)}if(0<it.length){kt(it[0],e);for(var n=1;n<it.length;n++){var r=it[n];r.blockedOn===e&&(r.blockedOn=null)}}for(null!==lt&&kt(lt,e),null!==st&&kt(st,e),null!==ct&&kt(ct,e),ut.forEach(t),dt.forEach(t),n=0;n<pt.length;n++)(r=pt[n]).blockedOn===e&&(r.blockedOn=null);for(;0<pt.length&&null===(n=pt[0]).blockedOn;)bt(n),null===n.blockedOn&&pt.shift()}function xt(e,t){var n={};return n[e.toLowerCase()]=t.toLowerCase(),n["Webkit"+e]="webkit"+t,n["Moz"+e]="moz"+t,n}var St={animationend:xt("Animation","AnimationEnd"),animationiteration:xt("Animation","AnimationIteration"),animationstart:xt("Animation","AnimationStart"),transitionend:xt("Transition","TransitionEnd")},_t={},Ct={};function Tt(e){if(_t[e])return _t[e];if(!St[e])return e;var t,n=St[e];for(t in n)if(n.hasOwnProperty(t)&&t in Ct)return _t[e]=n[t];return e}d&&(Ct=document.createElement("div").style,"AnimationEvent"in window||(delete St.animationend.animation,delete St.animationiteration.animation,delete St.animationstart.animation),"TransitionEvent"in window||delete St.transitionend.transition);var At=Tt("animationend"),Lt=Tt("animationiteration"),Rt=Tt("animationstart"),Nt=Tt("transitionend"),Ot=new Map,Pt=new Map,It=["abort","abort",At,"animationEnd",Lt,"animationIteration",Rt,"animationStart","canplay","canPlay","canplaythrough","canPlayThrough","durationchange","durationChange","emptied","emptied","encrypted","encrypted","ended","ended","error","error","gotpointercapture","gotPointerCapture","load","load","loadeddata","loadedData","loadedmetadata","loadedMetadata","loadstart","loadStart","lostpointercapture","lostPointerCapture","playing","playing","progress","progress","seeking","seeking","stalled","stalled","suspend","suspend","timeupdate","timeUpdate",Nt,"transitionEnd","waiting","waiting"];function Dt(e,t){for(var n=0;n<e.length;n+=2){var r=e[n],a=e[n+1];a="on"+(a[0].toUpperCase()+a.slice(1)),Pt.set(r,t),Ot.set(r,a),c(a,[r])}}(0,o.unstable_now)();var Mt=8;function Ft(e){if(0!=(1&e))return Mt=15,1;if(0!=(2&e))return Mt=14,2;if(0!=(4&e))return Mt=13,4;var t=24&e;return 0!==t?(Mt=12,t):0!=(32&e)?(Mt=11,32):0!==(t=192&e)?(Mt=10,t):0!=(256&e)?(Mt=9,256):0!==(t=3584&e)?(Mt=8,t):0!=(4096&e)?(Mt=7,4096):0!==(t=4186112&e)?(Mt=6,t):0!==(t=62914560&e)?(Mt=5,t):67108864&e?(Mt=4,67108864):0!=(134217728&e)?(Mt=3,134217728):0!==(t=805306368&e)?(Mt=2,t):0!=(1073741824&e)?(Mt=1,1073741824):(Mt=8,e)}function Bt(e,t){var n=e.pendingLanes;if(0===n)return Mt=0;var r=0,a=0,o=e.expiredLanes,i=e.suspendedLanes,l=e.pingedLanes;if(0!==o)r=o,a=Mt=15;else if(0!==(o=134217727&n)){var s=o&~i;0!==s?(r=Ft(s),a=Mt):0!==(l&=o)&&(r=Ft(l),a=Mt)}else 0!==(o=n&~i)?(r=Ft(o),a=Mt):0!==l&&(r=Ft(l),a=Mt);if(0===r)return 0;if(r=n&((0>(r=31-Ht(r))?0:1<<r)<<1)-1,0!==t&&t!==r&&0==(t&i)){if(Ft(t),a<=Mt)return t;Mt=a}if(0!==(t=e.entangledLanes))for(e=e.entanglements,t&=r;0<t;)a=1<<(n=31-Ht(t)),r|=e[n],t&=~a;return r}function jt(e){return 0!==(e=-1073741825&e.pendingLanes)?e:1073741824&e?1073741824:0}function zt(e,t){switch(e){case 15:return 1;case 14:return 2;case 12:return 0===(e=Ut(24&~t))?zt(10,t):e;case 10:return 0===(e=Ut(192&~t))?zt(8,t):e;case 8:return 0===(e=Ut(3584&~t))&&(0===(e=Ut(4186112&~t))&&(e=512)),e;case 2:return 0===(t=Ut(805306368&~t))&&(t=268435456),t}throw Error(i(358,e))}function Ut(e){return e&-e}function qt(e){for(var t=[],n=0;31>n;n++)t.push(e);return t}function $t(e,t,n){e.pendingLanes|=t;var r=t-1;e.suspendedLanes&=r,e.pingedLanes&=r,(e=e.eventTimes)[t=31-Ht(t)]=n}var Ht=Math.clz32?Math.clz32:function(e){return 0===e?32:31-(Gt(e)/Zt|0)|0},Gt=Math.log,Zt=Math.LN2;var Vt=o.unstable_UserBlockingPriority,Wt=o.unstable_runWithPriority,Yt=!0;function Kt(e,t,n,r){Fe||De();var a=Xt,o=Fe;Fe=!0;try{Ie(a,e,t,n,r)}finally{(Fe=o)||je()}}function Qt(e,t,n,r){Wt(Vt,Xt.bind(null,e,t,n,r))}function Xt(e,t,n,r){var a;if(Yt)if((a=0==(4&t))&&0<it.length&&-1<ft.indexOf(e))e=mt(null,e,t,n,r),it.push(e);else{var o=Jt(e,t,n,r);if(null===o)a&&ht(e,r);else{if(a){if(-1<ft.indexOf(e))return e=mt(o,e,t,n,r),void it.push(e);if(function(e,t,n,r,a){switch(t){case"focusin":return lt=gt(lt,e,t,n,r,a),!0;case"dragenter":return st=gt(st,e,t,n,r,a),!0;case"mouseover":return ct=gt(ct,e,t,n,r,a),!0;case"pointerover":var o=a.pointerId;return ut.set(o,gt(ut.get(o)||null,e,t,n,r,a)),!0;case"gotpointercapture":return o=a.pointerId,dt.set(o,gt(dt.get(o)||null,e,t,n,r,a)),!0}return!1}(o,e,t,n,r))return;ht(e,r)}Dr(e,t,r,null,n)}}}function Jt(e,t,n,r){var a=Ce(r);if(null!==(a=na(a))){var o=Ke(a);if(null===o)a=null;else{var i=o.tag;if(13===i){if(null!==(a=Qe(o)))return a;a=null}else if(3===i){if(o.stateNode.hydrate)return 3===o.tag?o.stateNode.containerInfo:null;a=null}else o!==a&&(a=null)}}return Dr(e,t,r,a,n),null}var en=null,tn=null,nn=null;function rn(){if(nn)return nn;var e,t,n=tn,r=n.length,a="value"in en?en.value:en.textContent,o=a.length;for(e=0;e<r&&n[e]===a[e];e++);var i=r-e;for(t=1;t<=i&&n[r-t]===a[o-t];t++);return nn=a.slice(e,1<t?1-t:void 0)}function an(e){var t=e.keyCode;return"charCode"in e?0===(e=e.charCode)&&13===t&&(e=13):e=t,10===e&&(e=13),32<=e||13===e?e:0}function on(){return!0}function ln(){return!1}function sn(e){function t(t,n,r,a,o){for(var i in this._reactName=t,this._targetInst=r,this.type=n,this.nativeEvent=a,this.target=o,this.currentTarget=null,e)e.hasOwnProperty(i)&&(t=e[i],this[i]=t?t(a):a[i]);return this.isDefaultPrevented=(null!=a.defaultPrevented?a.defaultPrevented:!1===a.returnValue)?on:ln,this.isPropagationStopped=ln,this}return a(t.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=on)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=on)},persist:function(){},isPersistent:on}),t}var cn,un,dn,pn={eventPhase:0,bubbles:0,cancelable:0,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:0,isTrusted:0},fn=sn(pn),mn=a({},pn,{view:0,detail:0}),hn=sn(mn),gn=a({},mn,{screenX:0,screenY:0,clientX:0,clientY:0,pageX:0,pageY:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,getModifierState:An,button:0,buttons:0,relatedTarget:function(e){return void 0===e.relatedTarget?e.fromElement===e.srcElement?e.toElement:e.fromElement:e.relatedTarget},movementX:function(e){return"movementX"in e?e.movementX:(e!==dn&&(dn&&"mousemove"===e.type?(cn=e.screenX-dn.screenX,un=e.screenY-dn.screenY):un=cn=0,dn=e),cn)},movementY:function(e){return"movementY"in e?e.movementY:un}}),bn=sn(gn),vn=sn(a({},gn,{dataTransfer:0})),yn=sn(a({},mn,{relatedTarget:0})),wn=sn(a({},pn,{animationName:0,elapsedTime:0,pseudoElement:0})),kn=a({},pn,{clipboardData:function(e){return"clipboardData"in e?e.clipboardData:window.clipboardData}}),En=sn(kn),xn=sn(a({},pn,{data:0})),Sn={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},_n={8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",45:"Insert",46:"Delete",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"NumLock",145:"ScrollLock",224:"Meta"},Cn={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"};function Tn(e){var t=this.nativeEvent;return t.getModifierState?t.getModifierState(e):!!(e=Cn[e])&&!!t[e]}function An(){return Tn}var Ln=a({},mn,{key:function(e){if(e.key){var t=Sn[e.key]||e.key;if("Unidentified"!==t)return t}return"keypress"===e.type?13===(e=an(e))?"Enter":String.fromCharCode(e):"keydown"===e.type||"keyup"===e.type?_n[e.keyCode]||"Unidentified":""},code:0,location:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,repeat:0,locale:0,getModifierState:An,charCode:function(e){return"keypress"===e.type?an(e):0},keyCode:function(e){return"keydown"===e.type||"keyup"===e.type?e.keyCode:0},which:function(e){return"keypress"===e.type?an(e):"keydown"===e.type||"keyup"===e.type?e.keyCode:0}}),Rn=sn(Ln),Nn=sn(a({},gn,{pointerId:0,width:0,height:0,pressure:0,tangentialPressure:0,tiltX:0,tiltY:0,twist:0,pointerType:0,isPrimary:0})),On=sn(a({},mn,{touches:0,targetTouches:0,changedTouches:0,altKey:0,metaKey:0,ctrlKey:0,shiftKey:0,getModifierState:An})),Pn=sn(a({},pn,{propertyName:0,elapsedTime:0,pseudoElement:0})),In=a({},gn,{deltaX:function(e){return"deltaX"in e?e.deltaX:"wheelDeltaX"in e?-e.wheelDeltaX:0},deltaY:function(e){return"deltaY"in e?e.deltaY:"wheelDeltaY"in e?-e.wheelDeltaY:"wheelDelta"in e?-e.wheelDelta:0},deltaZ:0,deltaMode:0}),Dn=sn(In),Mn=[9,13,27,32],Fn=d&&"CompositionEvent"in window,Bn=null;d&&"documentMode"in document&&(Bn=document.documentMode);var jn=d&&"TextEvent"in window&&!Bn,zn=d&&(!Fn||Bn&&8<Bn&&11>=Bn),Un=String.fromCharCode(32),qn=!1;function $n(e,t){switch(e){case"keyup":return-1!==Mn.indexOf(t.keyCode);case"keydown":return 229!==t.keyCode;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Hn(e){return"object"==typeof(e=e.detail)&&"data"in e?e.data:null}var Gn=!1;var Zn={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};function Vn(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return"input"===t?!!Zn[e.type]:"textarea"===t}function Wn(e,t,n,r){Ne(r),0<(t=Fr(t,"onChange")).length&&(n=new fn("onChange","change",null,n,r),e.push({event:n,listeners:t}))}var Yn=null,Kn=null;function Qn(e){Lr(e,0)}function Xn(e){if(Q(aa(e)))return e}function Jn(e,t){if("change"===e)return t}var er=!1;if(d){var tr;if(d){var nr="oninput"in document;if(!nr){var rr=document.createElement("div");rr.setAttribute("oninput","return;"),nr="function"==typeof rr.oninput}tr=nr}else tr=!1;er=tr&&(!document.documentMode||9<document.documentMode)}function ar(){Yn&&(Yn.detachEvent("onpropertychange",or),Kn=Yn=null)}function or(e){if("value"===e.propertyName&&Xn(Kn)){var t=[];if(Wn(t,Kn,e,Ce(e)),e=Qn,Fe)e(t);else{Fe=!0;try{Pe(e,t)}finally{Fe=!1,je()}}}}function ir(e,t,n){"focusin"===e?(ar(),Kn=n,(Yn=t).attachEvent("onpropertychange",or)):"focusout"===e&&ar()}function lr(e){if("selectionchange"===e||"keyup"===e||"keydown"===e)return Xn(Kn)}function sr(e,t){if("click"===e)return Xn(t)}function cr(e,t){if("input"===e||"change"===e)return Xn(t)}var ur="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},dr=Object.prototype.hasOwnProperty;function pr(e,t){if(ur(e,t))return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;var n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(r=0;r<n.length;r++)if(!dr.call(t,n[r])||!ur(e[n[r]],t[n[r]]))return!1;return!0}function fr(e){for(;e&&e.firstChild;)e=e.firstChild;return e}function mr(e,t){var n,r=fr(e);for(e=0;r;){if(3===r.nodeType){if(n=e+r.textContent.length,e<=t&&n>=t)return{node:r,offset:t-e};e=n}e:{for(;r;){if(r.nextSibling){r=r.nextSibling;break e}r=r.parentNode}r=void 0}r=fr(r)}}function hr(e,t){return!(!e||!t)&&(e===t||(!e||3!==e.nodeType)&&(t&&3===t.nodeType?hr(e,t.parentNode):"contains"in e?e.contains(t):!!e.compareDocumentPosition&&!!(16&e.compareDocumentPosition(t))))}function gr(){for(var e=window,t=X();t instanceof e.HTMLIFrameElement;){try{var n="string"==typeof t.contentWindow.location.href}catch(r){n=!1}if(!n)break;t=X((e=t.contentWindow).document)}return t}function br(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&("input"===t&&("text"===e.type||"search"===e.type||"tel"===e.type||"url"===e.type||"password"===e.type)||"textarea"===t||"true"===e.contentEditable)}var vr=d&&"documentMode"in document&&11>=document.documentMode,yr=null,wr=null,kr=null,Er=!1;function xr(e,t,n){var r=n.window===n?n.document:9===n.nodeType?n:n.ownerDocument;Er||null==yr||yr!==X(r)||("selectionStart"in(r=yr)&&br(r)?r={start:r.selectionStart,end:r.selectionEnd}:r={anchorNode:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection()).anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset},kr&&pr(kr,r)||(kr=r,0<(r=Fr(wr,"onSelect")).length&&(t=new fn("onSelect","select",null,t,n),e.push({event:t,listeners:r}),t.target=yr)))}Dt("cancel cancel click click close close contextmenu contextMenu copy copy cut cut auxclick auxClick dblclick doubleClick dragend dragEnd dragstart dragStart drop drop focusin focus focusout blur input input invalid invalid keydown keyDown keypress keyPress keyup keyUp mousedown mouseDown mouseup mouseUp paste paste pause pause play play pointercancel pointerCancel pointerdown pointerDown pointerup pointerUp ratechange rateChange reset reset seeked seeked submit submit touchcancel touchCancel touchend touchEnd touchstart touchStart volumechange volumeChange".split(" "),0),Dt("drag drag dragenter dragEnter dragexit dragExit dragleave dragLeave dragover dragOver mousemove mouseMove mouseout mouseOut mouseover mouseOver pointermove pointerMove pointerout pointerOut pointerover pointerOver scroll scroll toggle toggle touchmove touchMove wheel wheel".split(" "),1),Dt(It,2);for(var Sr="change selectionchange textInput compositionstart compositionend compositionupdate".split(" "),_r=0;_r<Sr.length;_r++)Pt.set(Sr[_r],0);u("onMouseEnter",["mouseout","mouseover"]),u("onMouseLeave",["mouseout","mouseover"]),u("onPointerEnter",["pointerout","pointerover"]),u("onPointerLeave",["pointerout","pointerover"]),c("onChange","change click focusin focusout input keydown keyup selectionchange".split(" ")),c("onSelect","focusout contextmenu dragend focusin keydown keyup mousedown mouseup selectionchange".split(" ")),c("onBeforeInput",["compositionend","keypress","textInput","paste"]),c("onCompositionEnd","compositionend focusout keydown keypress keyup mousedown".split(" ")),c("onCompositionStart","compositionstart focusout keydown keypress keyup mousedown".split(" ")),c("onCompositionUpdate","compositionupdate focusout keydown keypress keyup mousedown".split(" "));var Cr="abort canplay canplaythrough durationchange emptied encrypted ended error loadeddata loadedmetadata loadstart pause play playing progress ratechange seeked seeking stalled suspend timeupdate volumechange waiting".split(" "),Tr=new Set("cancel close invalid load scroll toggle".split(" ").concat(Cr));function Ar(e,t,n){var r=e.type||"unknown-event";e.currentTarget=n,function(e,t,n,r,a,o,l,s,c){if(Ye.apply(this,arguments),He){if(!He)throw Error(i(198));var u=Ge;He=!1,Ge=null,Ze||(Ze=!0,Ve=u)}}(r,t,void 0,e),e.currentTarget=null}function Lr(e,t){t=0!=(4&t);for(var n=0;n<e.length;n++){var r=e[n],a=r.event;r=r.listeners;e:{var o=void 0;if(t)for(var i=r.length-1;0<=i;i--){var l=r[i],s=l.instance,c=l.currentTarget;if(l=l.listener,s!==o&&a.isPropagationStopped())break e;Ar(a,l,c),o=s}else for(i=0;i<r.length;i++){if(s=(l=r[i]).instance,c=l.currentTarget,l=l.listener,s!==o&&a.isPropagationStopped())break e;Ar(a,l,c),o=s}}}if(Ze)throw e=Ve,Ze=!1,Ve=null,e}function Rr(e,t){var n=ia(t),r=e+"__bubble";n.has(r)||(Ir(t,e,2,!1),n.add(r))}var Nr="_reactListening"+Math.random().toString(36).slice(2);function Or(e){e[Nr]||(e[Nr]=!0,l.forEach((function(t){Tr.has(t)||Pr(t,!1,e,null),Pr(t,!0,e,null)})))}function Pr(e,t,n,r){var a=4<arguments.length&&void 0!==arguments[4]?arguments[4]:0,o=n;if("selectionchange"===e&&9!==n.nodeType&&(o=n.ownerDocument),null!==r&&!t&&Tr.has(e)){if("scroll"!==e)return;a|=2,o=r}var i=ia(o),l=e+"__"+(t?"capture":"bubble");i.has(l)||(t&&(a|=4),Ir(o,e,a,t),i.add(l))}function Ir(e,t,n,r){var a=Pt.get(t);switch(void 0===a?2:a){case 0:a=Kt;break;case 1:a=Qt;break;default:a=Xt}n=a.bind(null,t,n,e),a=void 0,!Ue||"touchstart"!==t&&"touchmove"!==t&&"wheel"!==t||(a=!0),r?void 0!==a?e.addEventListener(t,n,{capture:!0,passive:a}):e.addEventListener(t,n,!0):void 0!==a?e.addEventListener(t,n,{passive:a}):e.addEventListener(t,n,!1)}function Dr(e,t,n,r,a){var o=r;if(0==(1&t)&&0==(2&t)&&null!==r)e:for(;;){if(null===r)return;var i=r.tag;if(3===i||4===i){var l=r.stateNode.containerInfo;if(l===a||8===l.nodeType&&l.parentNode===a)break;if(4===i)for(i=r.return;null!==i;){var s=i.tag;if((3===s||4===s)&&((s=i.stateNode.containerInfo)===a||8===s.nodeType&&s.parentNode===a))return;i=i.return}for(;null!==l;){if(null===(i=na(l)))return;if(5===(s=i.tag)||6===s){r=o=i;continue e}l=l.parentNode}}r=r.return}!function(e,t,n){if(Be)return e(t,n);Be=!0;try{Me(e,t,n)}finally{Be=!1,je()}}((function(){var r=o,a=Ce(n),i=[];e:{var l=Ot.get(e);if(void 0!==l){var s=fn,c=e;switch(e){case"keypress":if(0===an(n))break e;case"keydown":case"keyup":s=Rn;break;case"focusin":c="focus",s=yn;break;case"focusout":c="blur",s=yn;break;case"beforeblur":case"afterblur":s=yn;break;case"click":if(2===n.button)break e;case"auxclick":case"dblclick":case"mousedown":case"mousemove":case"mouseup":case"mouseout":case"mouseover":case"contextmenu":s=bn;break;case"drag":case"dragend":case"dragenter":case"dragexit":case"dragleave":case"dragover":case"dragstart":case"drop":s=vn;break;case"touchcancel":case"touchend":case"touchmove":case"touchstart":s=On;break;case At:case Lt:case Rt:s=wn;break;case Nt:s=Pn;break;case"scroll":s=hn;break;case"wheel":s=Dn;break;case"copy":case"cut":case"paste":s=En;break;case"gotpointercapture":case"lostpointercapture":case"pointercancel":case"pointerdown":case"pointermove":case"pointerout":case"pointerover":case"pointerup":s=Nn}var u=0!=(4&t),d=!u&&"scroll"===e,p=u?null!==l?l+"Capture":null:l;u=[];for(var f,m=r;null!==m;){var h=(f=m).stateNode;if(5===f.tag&&null!==h&&(f=h,null!==p&&(null!=(h=ze(m,p))&&u.push(Mr(m,h,f)))),d)break;m=m.return}0<u.length&&(l=new s(l,c,null,n,a),i.push({event:l,listeners:u}))}}if(0==(7&t)){if(s="mouseout"===e||"pointerout"===e,(!(l="mouseover"===e||"pointerover"===e)||0!=(16&t)||!(c=n.relatedTarget||n.fromElement)||!na(c)&&!c[ea])&&(s||l)&&(l=a.window===a?a:(l=a.ownerDocument)?l.defaultView||l.parentWindow:window,s?(s=r,null!==(c=(c=n.relatedTarget||n.toElement)?na(c):null)&&(c!==(d=Ke(c))||5!==c.tag&&6!==c.tag)&&(c=null)):(s=null,c=r),s!==c)){if(u=bn,h="onMouseLeave",p="onMouseEnter",m="mouse","pointerout"!==e&&"pointerover"!==e||(u=Nn,h="onPointerLeave",p="onPointerEnter",m="pointer"),d=null==s?l:aa(s),f=null==c?l:aa(c),(l=new u(h,m+"leave",s,n,a)).target=d,l.relatedTarget=f,h=null,na(a)===r&&((u=new u(p,m+"enter",c,n,a)).target=f,u.relatedTarget=d,h=u),d=h,s&&c)e:{for(p=c,m=0,f=u=s;f;f=Br(f))m++;for(f=0,h=p;h;h=Br(h))f++;for(;0<m-f;)u=Br(u),m--;for(;0<f-m;)p=Br(p),f--;for(;m--;){if(u===p||null!==p&&u===p.alternate)break e;u=Br(u),p=Br(p)}u=null}else u=null;null!==s&&jr(i,l,s,u,!1),null!==c&&null!==d&&jr(i,d,c,u,!0)}if("select"===(s=(l=r?aa(r):window).nodeName&&l.nodeName.toLowerCase())||"input"===s&&"file"===l.type)var g=Jn;else if(Vn(l))if(er)g=cr;else{g=lr;var b=ir}else(s=l.nodeName)&&"input"===s.toLowerCase()&&("checkbox"===l.type||"radio"===l.type)&&(g=sr);switch(g&&(g=g(e,r))?Wn(i,g,n,a):(b&&b(e,l,r),"focusout"===e&&(b=l._wrapperState)&&b.controlled&&"number"===l.type&&ae(l,"number",l.value)),b=r?aa(r):window,e){case"focusin":(Vn(b)||"true"===b.contentEditable)&&(yr=b,wr=r,kr=null);break;case"focusout":kr=wr=yr=null;break;case"mousedown":Er=!0;break;case"contextmenu":case"mouseup":case"dragend":Er=!1,xr(i,n,a);break;case"selectionchange":if(vr)break;case"keydown":case"keyup":xr(i,n,a)}var v;if(Fn)e:{switch(e){case"compositionstart":var y="onCompositionStart";break e;case"compositionend":y="onCompositionEnd";break e;case"compositionupdate":y="onCompositionUpdate";break e}y=void 0}else Gn?$n(e,n)&&(y="onCompositionEnd"):"keydown"===e&&229===n.keyCode&&(y="onCompositionStart");y&&(zn&&"ko"!==n.locale&&(Gn||"onCompositionStart"!==y?"onCompositionEnd"===y&&Gn&&(v=rn()):(tn="value"in(en=a)?en.value:en.textContent,Gn=!0)),0<(b=Fr(r,y)).length&&(y=new xn(y,e,null,n,a),i.push({event:y,listeners:b}),v?y.data=v:null!==(v=Hn(n))&&(y.data=v))),(v=jn?function(e,t){switch(e){case"compositionend":return Hn(t);case"keypress":return 32!==t.which?null:(qn=!0,Un);case"textInput":return(e=t.data)===Un&&qn?null:e;default:return null}}(e,n):function(e,t){if(Gn)return"compositionend"===e||!Fn&&$n(e,t)?(e=rn(),nn=tn=en=null,Gn=!1,e):null;switch(e){case"paste":default:return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1<t.char.length)return t.char;if(t.which)return String.fromCharCode(t.which)}return null;case"compositionend":return zn&&"ko"!==t.locale?null:t.data}}(e,n))&&(0<(r=Fr(r,"onBeforeInput")).length&&(a=new xn("onBeforeInput","beforeinput",null,n,a),i.push({event:a,listeners:r}),a.data=v))}Lr(i,t)}))}function Mr(e,t,n){return{instance:e,listener:t,currentTarget:n}}function Fr(e,t){for(var n=t+"Capture",r=[];null!==e;){var a=e,o=a.stateNode;5===a.tag&&null!==o&&(a=o,null!=(o=ze(e,n))&&r.unshift(Mr(e,o,a)),null!=(o=ze(e,t))&&r.push(Mr(e,o,a))),e=e.return}return r}function Br(e){if(null===e)return null;do{e=e.return}while(e&&5!==e.tag);return e||null}function jr(e,t,n,r,a){for(var o=t._reactName,i=[];null!==n&&n!==r;){var l=n,s=l.alternate,c=l.stateNode;if(null!==s&&s===r)break;5===l.tag&&null!==c&&(l=c,a?null!=(s=ze(n,o))&&i.unshift(Mr(n,s,l)):a||null!=(s=ze(n,o))&&i.push(Mr(n,s,l))),n=n.return}0!==i.length&&e.push({event:t,listeners:i})}function zr(){}var Ur=null,qr=null;function $r(e,t){switch(e){case"button":case"input":case"select":case"textarea":return!!t.autoFocus}return!1}function Hr(e,t){return"textarea"===e||"option"===e||"noscript"===e||"string"==typeof t.children||"number"==typeof t.children||"object"==typeof t.dangerouslySetInnerHTML&&null!==t.dangerouslySetInnerHTML&&null!=t.dangerouslySetInnerHTML.__html}var Gr="function"==typeof setTimeout?setTimeout:void 0,Zr="function"==typeof clearTimeout?clearTimeout:void 0;function Vr(e){1===e.nodeType?e.textContent="":9===e.nodeType&&(null!=(e=e.body)&&(e.textContent=""))}function Wr(e){for(;null!=e;e=e.nextSibling){var t=e.nodeType;if(1===t||3===t)break}return e}function Yr(e){e=e.previousSibling;for(var t=0;e;){if(8===e.nodeType){var n=e.data;if("$"===n||"$!"===n||"$?"===n){if(0===t)return e;t--}else"/$"===n&&t++}e=e.previousSibling}return null}var Kr=0;var Qr=Math.random().toString(36).slice(2),Xr="__reactFiber$"+Qr,Jr="__reactProps$"+Qr,ea="__reactContainer$"+Qr,ta="__reactEvents$"+Qr;function na(e){var t=e[Xr];if(t)return t;for(var n=e.parentNode;n;){if(t=n[ea]||n[Xr]){if(n=t.alternate,null!==t.child||null!==n&&null!==n.child)for(e=Yr(e);null!==e;){if(n=e[Xr])return n;e=Yr(e)}return t}n=(e=n).parentNode}return null}function ra(e){return!(e=e[Xr]||e[ea])||5!==e.tag&&6!==e.tag&&13!==e.tag&&3!==e.tag?null:e}function aa(e){if(5===e.tag||6===e.tag)return e.stateNode;throw Error(i(33))}function oa(e){return e[Jr]||null}function ia(e){var t=e[ta];return void 0===t&&(t=e[ta]=new Set),t}var la=[],sa=-1;function ca(e){return{current:e}}function ua(e){0>sa||(e.current=la[sa],la[sa]=null,sa--)}function da(e,t){sa++,la[sa]=e.current,e.current=t}var pa={},fa=ca(pa),ma=ca(!1),ha=pa;function ga(e,t){var n=e.type.contextTypes;if(!n)return pa;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var a,o={};for(a in n)o[a]=t[a];return r&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=o),o}function ba(e){return null!=(e=e.childContextTypes)}function va(){ua(ma),ua(fa)}function ya(e,t,n){if(fa.current!==pa)throw Error(i(168));da(fa,t),da(ma,n)}function wa(e,t,n){var r=e.stateNode;if(e=t.childContextTypes,"function"!=typeof r.getChildContext)return n;for(var o in r=r.getChildContext())if(!(o in e))throw Error(i(108,V(t)||"Unknown",o));return a({},n,r)}function ka(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||pa,ha=fa.current,da(fa,e),da(ma,ma.current),!0}function Ea(e,t,n){var r=e.stateNode;if(!r)throw Error(i(169));n?(e=wa(e,t,ha),r.__reactInternalMemoizedMergedChildContext=e,ua(ma),ua(fa),da(fa,e)):ua(ma),da(ma,n)}var xa=null,Sa=null,_a=o.unstable_runWithPriority,Ca=o.unstable_scheduleCallback,Ta=o.unstable_cancelCallback,Aa=o.unstable_shouldYield,La=o.unstable_requestPaint,Ra=o.unstable_now,Na=o.unstable_getCurrentPriorityLevel,Oa=o.unstable_ImmediatePriority,Pa=o.unstable_UserBlockingPriority,Ia=o.unstable_NormalPriority,Da=o.unstable_LowPriority,Ma=o.unstable_IdlePriority,Fa={},Ba=void 0!==La?La:function(){},ja=null,za=null,Ua=!1,qa=Ra(),$a=1e4>qa?Ra:function(){return Ra()-qa};function Ha(){switch(Na()){case Oa:return 99;case Pa:return 98;case Ia:return 97;case Da:return 96;case Ma:return 95;default:throw Error(i(332))}}function Ga(e){switch(e){case 99:return Oa;case 98:return Pa;case 97:return Ia;case 96:return Da;case 95:return Ma;default:throw Error(i(332))}}function Za(e,t){return e=Ga(e),_a(e,t)}function Va(e,t,n){return e=Ga(e),Ca(e,t,n)}function Wa(){if(null!==za){var e=za;za=null,Ta(e)}Ya()}function Ya(){if(!Ua&&null!==ja){Ua=!0;var e=0;try{var t=ja;Za(99,(function(){for(;e<t.length;e++){var n=t[e];do{n=n(!0)}while(null!==n)}})),ja=null}catch(n){throw null!==ja&&(ja=ja.slice(e+1)),Ca(Oa,Wa),n}finally{Ua=!1}}}var Ka=k.ReactCurrentBatchConfig;function Qa(e,t){if(e&&e.defaultProps){for(var n in t=a({},t),e=e.defaultProps)void 0===t[n]&&(t[n]=e[n]);return t}return t}var Xa=ca(null),Ja=null,eo=null,to=null;function no(){to=eo=Ja=null}function ro(e){var t=Xa.current;ua(Xa),e.type._context._currentValue=t}function ao(e,t){for(;null!==e;){var n=e.alternate;if((e.childLanes&t)===t){if(null===n||(n.childLanes&t)===t)break;n.childLanes|=t}else e.childLanes|=t,null!==n&&(n.childLanes|=t);e=e.return}}function oo(e,t){Ja=e,to=eo=null,null!==(e=e.dependencies)&&null!==e.firstContext&&(0!=(e.lanes&t)&&(Fi=!0),e.firstContext=null)}function io(e,t){if(to!==e&&!1!==t&&0!==t)if("number"==typeof t&&1073741823!==t||(to=e,t=1073741823),t={context:e,observedBits:t,next:null},null===eo){if(null===Ja)throw Error(i(308));eo=t,Ja.dependencies={lanes:0,firstContext:t,responders:null}}else eo=eo.next=t;return e._currentValue}var lo=!1;function so(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null},effects:null}}function co(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function uo(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function po(e,t){if(null!==(e=e.updateQueue)){var n=(e=e.shared).pending;null===n?t.next=t:(t.next=n.next,n.next=t),e.pending=t}}function fo(e,t){var n=e.updateQueue,r=e.alternate;if(null!==r&&n===(r=r.updateQueue)){var a=null,o=null;if(null!==(n=n.firstBaseUpdate)){do{var i={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};null===o?a=o=i:o=o.next=i,n=n.next}while(null!==n);null===o?a=o=t:o=o.next=t}else a=o=t;return n={baseState:r.baseState,firstBaseUpdate:a,lastBaseUpdate:o,shared:r.shared,effects:r.effects},void(e.updateQueue=n)}null===(e=n.lastBaseUpdate)?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function mo(e,t,n,r){var o=e.updateQueue;lo=!1;var i=o.firstBaseUpdate,l=o.lastBaseUpdate,s=o.shared.pending;if(null!==s){o.shared.pending=null;var c=s,u=c.next;c.next=null,null===l?i=u:l.next=u,l=c;var d=e.alternate;if(null!==d){var p=(d=d.updateQueue).lastBaseUpdate;p!==l&&(null===p?d.firstBaseUpdate=u:p.next=u,d.lastBaseUpdate=c)}}if(null!==i){for(p=o.baseState,l=0,d=u=c=null;;){s=i.lane;var f=i.eventTime;if((r&s)===s){null!==d&&(d=d.next={eventTime:f,lane:0,tag:i.tag,payload:i.payload,callback:i.callback,next:null});e:{var m=e,h=i;switch(s=t,f=n,h.tag){case 1:if("function"==typeof(m=h.payload)){p=m.call(f,p,s);break e}p=m;break e;case 3:m.flags=-4097&m.flags|64;case 0:if(null==(s="function"==typeof(m=h.payload)?m.call(f,p,s):m))break e;p=a({},p,s);break e;case 2:lo=!0}}null!==i.callback&&(e.flags|=32,null===(s=o.effects)?o.effects=[i]:s.push(i))}else f={eventTime:f,lane:s,tag:i.tag,payload:i.payload,callback:i.callback,next:null},null===d?(u=d=f,c=p):d=d.next=f,l|=s;if(null===(i=i.next)){if(null===(s=o.shared.pending))break;i=s.next,s.next=null,o.lastBaseUpdate=s,o.shared.pending=null}}null===d&&(c=p),o.baseState=c,o.firstBaseUpdate=u,o.lastBaseUpdate=d,Ul|=l,e.lanes=l,e.memoizedState=p}}function ho(e,t,n){if(e=t.effects,t.effects=null,null!==e)for(t=0;t<e.length;t++){var r=e[t],a=r.callback;if(null!==a){if(r.callback=null,r=n,"function"!=typeof a)throw Error(i(191,a));a.call(r)}}}var go=(new r.Component).refs;function bo(e,t,n,r){n=null==(n=n(r,t=e.memoizedState))?t:a({},t,n),e.memoizedState=n,0===e.lanes&&(e.updateQueue.baseState=n)}var vo={isMounted:function(e){return!!(e=e._reactInternals)&&Ke(e)===e},enqueueSetState:function(e,t,n){e=e._reactInternals;var r=ps(),a=fs(e),o=uo(r,a);o.payload=t,null!=n&&(o.callback=n),po(e,o),ms(e,a,r)},enqueueReplaceState:function(e,t,n){e=e._reactInternals;var r=ps(),a=fs(e),o=uo(r,a);o.tag=1,o.payload=t,null!=n&&(o.callback=n),po(e,o),ms(e,a,r)},enqueueForceUpdate:function(e,t){e=e._reactInternals;var n=ps(),r=fs(e),a=uo(n,r);a.tag=2,null!=t&&(a.callback=t),po(e,a),ms(e,r,n)}};function yo(e,t,n,r,a,o,i){return"function"==typeof(e=e.stateNode).shouldComponentUpdate?e.shouldComponentUpdate(r,o,i):!t.prototype||!t.prototype.isPureReactComponent||(!pr(n,r)||!pr(a,o))}function wo(e,t,n){var r=!1,a=pa,o=t.contextType;return"object"==typeof o&&null!==o?o=io(o):(a=ba(t)?ha:fa.current,o=(r=null!=(r=t.contextTypes))?ga(e,a):pa),t=new t(n,o),e.memoizedState=null!==t.state&&void 0!==t.state?t.state:null,t.updater=vo,e.stateNode=t,t._reactInternals=e,r&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=a,e.__reactInternalMemoizedMaskedChildContext=o),t}function ko(e,t,n,r){e=t.state,"function"==typeof t.componentWillReceiveProps&&t.componentWillReceiveProps(n,r),"function"==typeof t.UNSAFE_componentWillReceiveProps&&t.UNSAFE_componentWillReceiveProps(n,r),t.state!==e&&vo.enqueueReplaceState(t,t.state,null)}function Eo(e,t,n,r){var a=e.stateNode;a.props=n,a.state=e.memoizedState,a.refs=go,so(e);var o=t.contextType;"object"==typeof o&&null!==o?a.context=io(o):(o=ba(t)?ha:fa.current,a.context=ga(e,o)),mo(e,n,a,r),a.state=e.memoizedState,"function"==typeof(o=t.getDerivedStateFromProps)&&(bo(e,t,o,n),a.state=e.memoizedState),"function"==typeof t.getDerivedStateFromProps||"function"==typeof a.getSnapshotBeforeUpdate||"function"!=typeof a.UNSAFE_componentWillMount&&"function"!=typeof a.componentWillMount||(t=a.state,"function"==typeof a.componentWillMount&&a.componentWillMount(),"function"==typeof a.UNSAFE_componentWillMount&&a.UNSAFE_componentWillMount(),t!==a.state&&vo.enqueueReplaceState(a,a.state,null),mo(e,n,a,r),a.state=e.memoizedState),"function"==typeof a.componentDidMount&&(e.flags|=4)}var xo=Array.isArray;function So(e,t,n){if(null!==(e=n.ref)&&"function"!=typeof e&&"object"!=typeof e){if(n._owner){if(n=n._owner){if(1!==n.tag)throw Error(i(309));var r=n.stateNode}if(!r)throw Error(i(147,e));var a=""+e;return null!==t&&null!==t.ref&&"function"==typeof t.ref&&t.ref._stringRef===a?t.ref:(t=function(e){var t=r.refs;t===go&&(t=r.refs={}),null===e?delete t[a]:t[a]=e},t._stringRef=a,t)}if("string"!=typeof e)throw Error(i(284));if(!n._owner)throw Error(i(290,e))}return e}function _o(e,t){if("textarea"!==e.type)throw Error(i(31,"[object Object]"===Object.prototype.toString.call(t)?"object with keys {"+Object.keys(t).join(", ")+"}":t))}function Co(e){function t(t,n){if(e){var r=t.lastEffect;null!==r?(r.nextEffect=n,t.lastEffect=n):t.firstEffect=t.lastEffect=n,n.nextEffect=null,n.flags=8}}function n(n,r){if(!e)return null;for(;null!==r;)t(n,r),r=r.sibling;return null}function r(e,t){for(e=new Map;null!==t;)null!==t.key?e.set(t.key,t):e.set(t.index,t),t=t.sibling;return e}function a(e,t){return(e=Zs(e,t)).index=0,e.sibling=null,e}function o(t,n,r){return t.index=r,e?null!==(r=t.alternate)?(r=r.index)<n?(t.flags=2,n):r:(t.flags=2,n):n}function l(t){return e&&null===t.alternate&&(t.flags=2),t}function s(e,t,n,r){return null===t||6!==t.tag?((t=Ks(n,e.mode,r)).return=e,t):((t=a(t,n)).return=e,t)}function c(e,t,n,r){return null!==t&&t.elementType===n.type?((r=a(t,n.props)).ref=So(e,t,n),r.return=e,r):((r=Vs(n.type,n.key,n.props,null,e.mode,r)).ref=So(e,t,n),r.return=e,r)}function u(e,t,n,r){return null===t||4!==t.tag||t.stateNode.containerInfo!==n.containerInfo||t.stateNode.implementation!==n.implementation?((t=Qs(n,e.mode,r)).return=e,t):((t=a(t,n.children||[])).return=e,t)}function d(e,t,n,r,o){return null===t||7!==t.tag?((t=Ws(n,e.mode,r,o)).return=e,t):((t=a(t,n)).return=e,t)}function p(e,t,n){if("string"==typeof t||"number"==typeof t)return(t=Ks(""+t,e.mode,n)).return=e,t;if("object"==typeof t&&null!==t){switch(t.$$typeof){case E:return(n=Vs(t.type,t.key,t.props,null,e.mode,n)).ref=So(e,null,t),n.return=e,n;case x:return(t=Qs(t,e.mode,n)).return=e,t}if(xo(t)||q(t))return(t=Ws(t,e.mode,n,null)).return=e,t;_o(e,t)}return null}function f(e,t,n,r){var a=null!==t?t.key:null;if("string"==typeof n||"number"==typeof n)return null!==a?null:s(e,t,""+n,r);if("object"==typeof n&&null!==n){switch(n.$$typeof){case E:return n.key===a?n.type===S?d(e,t,n.props.children,r,a):c(e,t,n,r):null;case x:return n.key===a?u(e,t,n,r):null}if(xo(n)||q(n))return null!==a?null:d(e,t,n,r,null);_o(e,n)}return null}function m(e,t,n,r,a){if("string"==typeof r||"number"==typeof r)return s(t,e=e.get(n)||null,""+r,a);if("object"==typeof r&&null!==r){switch(r.$$typeof){case E:return e=e.get(null===r.key?n:r.key)||null,r.type===S?d(t,e,r.props.children,a,r.key):c(t,e,r,a);case x:return u(t,e=e.get(null===r.key?n:r.key)||null,r,a)}if(xo(r)||q(r))return d(t,e=e.get(n)||null,r,a,null);_o(t,r)}return null}function h(a,i,l,s){for(var c=null,u=null,d=i,h=i=0,g=null;null!==d&&h<l.length;h++){d.index>h?(g=d,d=null):g=d.sibling;var b=f(a,d,l[h],s);if(null===b){null===d&&(d=g);break}e&&d&&null===b.alternate&&t(a,d),i=o(b,i,h),null===u?c=b:u.sibling=b,u=b,d=g}if(h===l.length)return n(a,d),c;if(null===d){for(;h<l.length;h++)null!==(d=p(a,l[h],s))&&(i=o(d,i,h),null===u?c=d:u.sibling=d,u=d);return c}for(d=r(a,d);h<l.length;h++)null!==(g=m(d,a,h,l[h],s))&&(e&&null!==g.alternate&&d.delete(null===g.key?h:g.key),i=o(g,i,h),null===u?c=g:u.sibling=g,u=g);return e&&d.forEach((function(e){return t(a,e)})),c}function g(a,l,s,c){var u=q(s);if("function"!=typeof u)throw Error(i(150));if(null==(s=u.call(s)))throw Error(i(151));for(var d=u=null,h=l,g=l=0,b=null,v=s.next();null!==h&&!v.done;g++,v=s.next()){h.index>g?(b=h,h=null):b=h.sibling;var y=f(a,h,v.value,c);if(null===y){null===h&&(h=b);break}e&&h&&null===y.alternate&&t(a,h),l=o(y,l,g),null===d?u=y:d.sibling=y,d=y,h=b}if(v.done)return n(a,h),u;if(null===h){for(;!v.done;g++,v=s.next())null!==(v=p(a,v.value,c))&&(l=o(v,l,g),null===d?u=v:d.sibling=v,d=v);return u}for(h=r(a,h);!v.done;g++,v=s.next())null!==(v=m(h,a,g,v.value,c))&&(e&&null!==v.alternate&&h.delete(null===v.key?g:v.key),l=o(v,l,g),null===d?u=v:d.sibling=v,d=v);return e&&h.forEach((function(e){return t(a,e)})),u}return function(e,r,o,s){var c="object"==typeof o&&null!==o&&o.type===S&&null===o.key;c&&(o=o.props.children);var u="object"==typeof o&&null!==o;if(u)switch(o.$$typeof){case E:e:{for(u=o.key,c=r;null!==c;){if(c.key===u){if(7===c.tag){if(o.type===S){n(e,c.sibling),(r=a(c,o.props.children)).return=e,e=r;break e}}else if(c.elementType===o.type){n(e,c.sibling),(r=a(c,o.props)).ref=So(e,c,o),r.return=e,e=r;break e}n(e,c);break}t(e,c),c=c.sibling}o.type===S?((r=Ws(o.props.children,e.mode,s,o.key)).return=e,e=r):((s=Vs(o.type,o.key,o.props,null,e.mode,s)).ref=So(e,r,o),s.return=e,e=s)}return l(e);case x:e:{for(c=o.key;null!==r;){if(r.key===c){if(4===r.tag&&r.stateNode.containerInfo===o.containerInfo&&r.stateNode.implementation===o.implementation){n(e,r.sibling),(r=a(r,o.children||[])).return=e,e=r;break e}n(e,r);break}t(e,r),r=r.sibling}(r=Qs(o,e.mode,s)).return=e,e=r}return l(e)}if("string"==typeof o||"number"==typeof o)return o=""+o,null!==r&&6===r.tag?(n(e,r.sibling),(r=a(r,o)).return=e,e=r):(n(e,r),(r=Ks(o,e.mode,s)).return=e,e=r),l(e);if(xo(o))return h(e,r,o,s);if(q(o))return g(e,r,o,s);if(u&&_o(e,o),void 0===o&&!c)switch(e.tag){case 1:case 22:case 0:case 11:case 15:throw Error(i(152,V(e.type)||"Component"))}return n(e,r)}}var To=Co(!0),Ao=Co(!1),Lo={},Ro=ca(Lo),No=ca(Lo),Oo=ca(Lo);function Po(e){if(e===Lo)throw Error(i(174));return e}function Io(e,t){switch(da(Oo,t),da(No,e),da(Ro,Lo),e=t.nodeType){case 9:case 11:t=(t=t.documentElement)?t.namespaceURI:me(null,"");break;default:t=me(t=(e=8===e?t.parentNode:t).namespaceURI||null,e=e.tagName)}ua(Ro),da(Ro,t)}function Do(){ua(Ro),ua(No),ua(Oo)}function Mo(e){Po(Oo.current);var t=Po(Ro.current),n=me(t,e.type);t!==n&&(da(No,e),da(Ro,n))}function Fo(e){No.current===e&&(ua(Ro),ua(No))}var Bo=ca(0);function jo(e){for(var t=e;null!==t;){if(13===t.tag){var n=t.memoizedState;if(null!==n&&(null===(n=n.dehydrated)||"$?"===n.data||"$!"===n.data))return t}else if(19===t.tag&&void 0!==t.memoizedProps.revealOrder){if(0!=(64&t.flags))return t}else if(null!==t.child){t.child.return=t,t=t.child;continue}if(t===e)break;for(;null===t.sibling;){if(null===t.return||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}var zo=null,Uo=null,qo=!1;function $o(e,t){var n=Hs(5,null,null,0);n.elementType="DELETED",n.type="DELETED",n.stateNode=t,n.return=e,n.flags=8,null!==e.lastEffect?(e.lastEffect.nextEffect=n,e.lastEffect=n):e.firstEffect=e.lastEffect=n}function Ho(e,t){switch(e.tag){case 5:var n=e.type;return null!==(t=1!==t.nodeType||n.toLowerCase()!==t.nodeName.toLowerCase()?null:t)&&(e.stateNode=t,!0);case 6:return null!==(t=""===e.pendingProps||3!==t.nodeType?null:t)&&(e.stateNode=t,!0);default:return!1}}function Go(e){if(qo){var t=Uo;if(t){var n=t;if(!Ho(e,t)){if(!(t=Wr(n.nextSibling))||!Ho(e,t))return e.flags=-1025&e.flags|2,qo=!1,void(zo=e);$o(zo,n)}zo=e,Uo=Wr(t.firstChild)}else e.flags=-1025&e.flags|2,qo=!1,zo=e}}function Zo(e){for(e=e.return;null!==e&&5!==e.tag&&3!==e.tag&&13!==e.tag;)e=e.return;zo=e}function Vo(e){if(e!==zo)return!1;if(!qo)return Zo(e),qo=!0,!1;var t=e.type;if(5!==e.tag||"head"!==t&&"body"!==t&&!Hr(t,e.memoizedProps))for(t=Uo;t;)$o(e,t),t=Wr(t.nextSibling);if(Zo(e),13===e.tag){if(!(e=null!==(e=e.memoizedState)?e.dehydrated:null))throw Error(i(317));e:{for(e=e.nextSibling,t=0;e;){if(8===e.nodeType){var n=e.data;if("/$"===n){if(0===t){Uo=Wr(e.nextSibling);break e}t--}else"$"!==n&&"$!"!==n&&"$?"!==n||t++}e=e.nextSibling}Uo=null}}else Uo=zo?Wr(e.stateNode.nextSibling):null;return!0}function Wo(){Uo=zo=null,qo=!1}var Yo=[];function Ko(){for(var e=0;e<Yo.length;e++)Yo[e]._workInProgressVersionPrimary=null;Yo.length=0}var Qo=k.ReactCurrentDispatcher,Xo=k.ReactCurrentBatchConfig,Jo=0,ei=null,ti=null,ni=null,ri=!1,ai=!1;function oi(){throw Error(i(321))}function ii(e,t){if(null===t)return!1;for(var n=0;n<t.length&&n<e.length;n++)if(!ur(e[n],t[n]))return!1;return!0}function li(e,t,n,r,a,o){if(Jo=o,ei=t,t.memoizedState=null,t.updateQueue=null,t.lanes=0,Qo.current=null===e||null===e.memoizedState?Pi:Ii,e=n(r,a),ai){o=0;do{if(ai=!1,!(25>o))throw Error(i(301));o+=1,ni=ti=null,t.updateQueue=null,Qo.current=Di,e=n(r,a)}while(ai)}if(Qo.current=Oi,t=null!==ti&&null!==ti.next,Jo=0,ni=ti=ei=null,ri=!1,t)throw Error(i(300));return e}function si(){var e={memoizedState:null,baseState:null,baseQueue:null,queue:null,next:null};return null===ni?ei.memoizedState=ni=e:ni=ni.next=e,ni}function ci(){if(null===ti){var e=ei.alternate;e=null!==e?e.memoizedState:null}else e=ti.next;var t=null===ni?ei.memoizedState:ni.next;if(null!==t)ni=t,ti=e;else{if(null===e)throw Error(i(310));e={memoizedState:(ti=e).memoizedState,baseState:ti.baseState,baseQueue:ti.baseQueue,queue:ti.queue,next:null},null===ni?ei.memoizedState=ni=e:ni=ni.next=e}return ni}function ui(e,t){return"function"==typeof t?t(e):t}function di(e){var t=ci(),n=t.queue;if(null===n)throw Error(i(311));n.lastRenderedReducer=e;var r=ti,a=r.baseQueue,o=n.pending;if(null!==o){if(null!==a){var l=a.next;a.next=o.next,o.next=l}r.baseQueue=a=o,n.pending=null}if(null!==a){a=a.next,r=r.baseState;var s=l=o=null,c=a;do{var u=c.lane;if((Jo&u)===u)null!==s&&(s=s.next={lane:0,action:c.action,eagerReducer:c.eagerReducer,eagerState:c.eagerState,next:null}),r=c.eagerReducer===e?c.eagerState:e(r,c.action);else{var d={lane:u,action:c.action,eagerReducer:c.eagerReducer,eagerState:c.eagerState,next:null};null===s?(l=s=d,o=r):s=s.next=d,ei.lanes|=u,Ul|=u}c=c.next}while(null!==c&&c!==a);null===s?o=r:s.next=l,ur(r,t.memoizedState)||(Fi=!0),t.memoizedState=r,t.baseState=o,t.baseQueue=s,n.lastRenderedState=r}return[t.memoizedState,n.dispatch]}function pi(e){var t=ci(),n=t.queue;if(null===n)throw Error(i(311));n.lastRenderedReducer=e;var r=n.dispatch,a=n.pending,o=t.memoizedState;if(null!==a){n.pending=null;var l=a=a.next;do{o=e(o,l.action),l=l.next}while(l!==a);ur(o,t.memoizedState)||(Fi=!0),t.memoizedState=o,null===t.baseQueue&&(t.baseState=o),n.lastRenderedState=o}return[o,r]}function fi(e,t,n){var r=t._getVersion;r=r(t._source);var a=t._workInProgressVersionPrimary;if(null!==a?e=a===r:(e=e.mutableReadLanes,(e=(Jo&e)===e)&&(t._workInProgressVersionPrimary=r,Yo.push(t))),e)return n(t._source);throw Yo.push(t),Error(i(350))}function mi(e,t,n,r){var a=Pl;if(null===a)throw Error(i(349));var o=t._getVersion,l=o(t._source),s=Qo.current,c=s.useState((function(){return fi(a,t,n)})),u=c[1],d=c[0];c=ni;var p=e.memoizedState,f=p.refs,m=f.getSnapshot,h=p.source;p=p.subscribe;var g=ei;return e.memoizedState={refs:f,source:t,subscribe:r},s.useEffect((function(){f.getSnapshot=n,f.setSnapshot=u;var e=o(t._source);if(!ur(l,e)){e=n(t._source),ur(d,e)||(u(e),e=fs(g),a.mutableReadLanes|=e&a.pendingLanes),e=a.mutableReadLanes,a.entangledLanes|=e;for(var r=a.entanglements,i=e;0<i;){var s=31-Ht(i),c=1<<s;r[s]|=e,i&=~c}}}),[n,t,r]),s.useEffect((function(){return r(t._source,(function(){var e=f.getSnapshot,n=f.setSnapshot;try{n(e(t._source));var r=fs(g);a.mutableReadLanes|=r&a.pendingLanes}catch(o){n((function(){throw o}))}}))}),[t,r]),ur(m,n)&&ur(h,t)&&ur(p,r)||((e={pending:null,dispatch:null,lastRenderedReducer:ui,lastRenderedState:d}).dispatch=u=Ni.bind(null,ei,e),c.queue=e,c.baseQueue=null,d=fi(a,t,n),c.memoizedState=c.baseState=d),d}function hi(e,t,n){return mi(ci(),e,t,n)}function gi(e){var t=si();return"function"==typeof e&&(e=e()),t.memoizedState=t.baseState=e,e=(e=t.queue={pending:null,dispatch:null,lastRenderedReducer:ui,lastRenderedState:e}).dispatch=Ni.bind(null,ei,e),[t.memoizedState,e]}function bi(e,t,n,r){return e={tag:e,create:t,destroy:n,deps:r,next:null},null===(t=ei.updateQueue)?(t={lastEffect:null},ei.updateQueue=t,t.lastEffect=e.next=e):null===(n=t.lastEffect)?t.lastEffect=e.next=e:(r=n.next,n.next=e,e.next=r,t.lastEffect=e),e}function vi(e){return e={current:e},si().memoizedState=e}function yi(){return ci().memoizedState}function wi(e,t,n,r){var a=si();ei.flags|=e,a.memoizedState=bi(1|t,n,void 0,void 0===r?null:r)}function ki(e,t,n,r){var a=ci();r=void 0===r?null:r;var o=void 0;if(null!==ti){var i=ti.memoizedState;if(o=i.destroy,null!==r&&ii(r,i.deps))return void bi(t,n,o,r)}ei.flags|=e,a.memoizedState=bi(1|t,n,o,r)}function Ei(e,t){return wi(516,4,e,t)}function xi(e,t){return ki(516,4,e,t)}function Si(e,t){return ki(4,2,e,t)}function _i(e,t){return"function"==typeof t?(e=e(),t(e),function(){t(null)}):null!=t?(e=e(),t.current=e,function(){t.current=null}):void 0}function Ci(e,t,n){return n=null!=n?n.concat([e]):null,ki(4,2,_i.bind(null,t,e),n)}function Ti(){}function Ai(e,t){var n=ci();t=void 0===t?null:t;var r=n.memoizedState;return null!==r&&null!==t&&ii(t,r[1])?r[0]:(n.memoizedState=[e,t],e)}function Li(e,t){var n=ci();t=void 0===t?null:t;var r=n.memoizedState;return null!==r&&null!==t&&ii(t,r[1])?r[0]:(e=e(),n.memoizedState=[e,t],e)}function Ri(e,t){var n=Ha();Za(98>n?98:n,(function(){e(!0)})),Za(97<n?97:n,(function(){var n=Xo.transition;Xo.transition=1;try{e(!1),t()}finally{Xo.transition=n}}))}function Ni(e,t,n){var r=ps(),a=fs(e),o={lane:a,action:n,eagerReducer:null,eagerState:null,next:null},i=t.pending;if(null===i?o.next=o:(o.next=i.next,i.next=o),t.pending=o,i=e.alternate,e===ei||null!==i&&i===ei)ai=ri=!0;else{if(0===e.lanes&&(null===i||0===i.lanes)&&null!==(i=t.lastRenderedReducer))try{var l=t.lastRenderedState,s=i(l,n);if(o.eagerReducer=i,o.eagerState=s,ur(s,l))return}catch(c){}ms(e,a,r)}}var Oi={readContext:io,useCallback:oi,useContext:oi,useEffect:oi,useImperativeHandle:oi,useLayoutEffect:oi,useMemo:oi,useReducer:oi,useRef:oi,useState:oi,useDebugValue:oi,useDeferredValue:oi,useTransition:oi,useMutableSource:oi,useOpaqueIdentifier:oi,unstable_isNewReconciler:!1},Pi={readContext:io,useCallback:function(e,t){return si().memoizedState=[e,void 0===t?null:t],e},useContext:io,useEffect:Ei,useImperativeHandle:function(e,t,n){return n=null!=n?n.concat([e]):null,wi(4,2,_i.bind(null,t,e),n)},useLayoutEffect:function(e,t){return wi(4,2,e,t)},useMemo:function(e,t){var n=si();return t=void 0===t?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=si();return t=void 0!==n?n(t):t,r.memoizedState=r.baseState=t,e=(e=r.queue={pending:null,dispatch:null,lastRenderedReducer:e,lastRenderedState:t}).dispatch=Ni.bind(null,ei,e),[r.memoizedState,e]},useRef:vi,useState:gi,useDebugValue:Ti,useDeferredValue:function(e){var t=gi(e),n=t[0],r=t[1];return Ei((function(){var t=Xo.transition;Xo.transition=1;try{r(e)}finally{Xo.transition=t}}),[e]),n},useTransition:function(){var e=gi(!1),t=e[0];return vi(e=Ri.bind(null,e[1])),[e,t]},useMutableSource:function(e,t,n){var r=si();return r.memoizedState={refs:{getSnapshot:t,setSnapshot:null},source:e,subscribe:n},mi(r,e,t,n)},useOpaqueIdentifier:function(){if(qo){var e=!1,t=function(e){return{$$typeof:D,toString:e,valueOf:e}}((function(){throw e||(e=!0,n("r:"+(Kr++).toString(36))),Error(i(355))})),n=gi(t)[1];return 0==(2&ei.mode)&&(ei.flags|=516,bi(5,(function(){n("r:"+(Kr++).toString(36))}),void 0,null)),t}return gi(t="r:"+(Kr++).toString(36)),t},unstable_isNewReconciler:!1},Ii={readContext:io,useCallback:Ai,useContext:io,useEffect:xi,useImperativeHandle:Ci,useLayoutEffect:Si,useMemo:Li,useReducer:di,useRef:yi,useState:function(){return di(ui)},useDebugValue:Ti,useDeferredValue:function(e){var t=di(ui),n=t[0],r=t[1];return xi((function(){var t=Xo.transition;Xo.transition=1;try{r(e)}finally{Xo.transition=t}}),[e]),n},useTransition:function(){var e=di(ui)[0];return[yi().current,e]},useMutableSource:hi,useOpaqueIdentifier:function(){return di(ui)[0]},unstable_isNewReconciler:!1},Di={readContext:io,useCallback:Ai,useContext:io,useEffect:xi,useImperativeHandle:Ci,useLayoutEffect:Si,useMemo:Li,useReducer:pi,useRef:yi,useState:function(){return pi(ui)},useDebugValue:Ti,useDeferredValue:function(e){var t=pi(ui),n=t[0],r=t[1];return xi((function(){var t=Xo.transition;Xo.transition=1;try{r(e)}finally{Xo.transition=t}}),[e]),n},useTransition:function(){var e=pi(ui)[0];return[yi().current,e]},useMutableSource:hi,useOpaqueIdentifier:function(){return pi(ui)[0]},unstable_isNewReconciler:!1},Mi=k.ReactCurrentOwner,Fi=!1;function Bi(e,t,n,r){t.child=null===e?Ao(t,null,n,r):To(t,e.child,n,r)}function ji(e,t,n,r,a){n=n.render;var o=t.ref;return oo(t,a),r=li(e,t,n,r,o,a),null===e||Fi?(t.flags|=1,Bi(e,t,r,a),t.child):(t.updateQueue=e.updateQueue,t.flags&=-517,e.lanes&=~a,ol(e,t,a))}function zi(e,t,n,r,a,o){if(null===e){var i=n.type;return"function"!=typeof i||Gs(i)||void 0!==i.defaultProps||null!==n.compare||void 0!==n.defaultProps?((e=Vs(n.type,null,r,t,t.mode,o)).ref=t.ref,e.return=t,t.child=e):(t.tag=15,t.type=i,Ui(e,t,i,r,a,o))}return i=e.child,0==(a&o)&&(a=i.memoizedProps,(n=null!==(n=n.compare)?n:pr)(a,r)&&e.ref===t.ref)?ol(e,t,o):(t.flags|=1,(e=Zs(i,r)).ref=t.ref,e.return=t,t.child=e)}function Ui(e,t,n,r,a,o){if(null!==e&&pr(e.memoizedProps,r)&&e.ref===t.ref){if(Fi=!1,0==(o&a))return t.lanes=e.lanes,ol(e,t,o);0!=(16384&e.flags)&&(Fi=!0)}return Hi(e,t,n,r,o)}function qi(e,t,n){var r=t.pendingProps,a=r.children,o=null!==e?e.memoizedState:null;if("hidden"===r.mode||"unstable-defer-without-hiding"===r.mode)if(0==(4&t.mode))t.memoizedState={baseLanes:0},Es(t,n);else{if(0==(1073741824&n))return e=null!==o?o.baseLanes|n:n,t.lanes=t.childLanes=1073741824,t.memoizedState={baseLanes:e},Es(t,e),null;t.memoizedState={baseLanes:0},Es(t,null!==o?o.baseLanes:n)}else null!==o?(r=o.baseLanes|n,t.memoizedState=null):r=n,Es(t,r);return Bi(e,t,a,n),t.child}function $i(e,t){var n=t.ref;(null===e&&null!==n||null!==e&&e.ref!==n)&&(t.flags|=128)}function Hi(e,t,n,r,a){var o=ba(n)?ha:fa.current;return o=ga(t,o),oo(t,a),n=li(e,t,n,r,o,a),null===e||Fi?(t.flags|=1,Bi(e,t,n,a),t.child):(t.updateQueue=e.updateQueue,t.flags&=-517,e.lanes&=~a,ol(e,t,a))}function Gi(e,t,n,r,a){if(ba(n)){var o=!0;ka(t)}else o=!1;if(oo(t,a),null===t.stateNode)null!==e&&(e.alternate=null,t.alternate=null,t.flags|=2),wo(t,n,r),Eo(t,n,r,a),r=!0;else if(null===e){var i=t.stateNode,l=t.memoizedProps;i.props=l;var s=i.context,c=n.contextType;"object"==typeof c&&null!==c?c=io(c):c=ga(t,c=ba(n)?ha:fa.current);var u=n.getDerivedStateFromProps,d="function"==typeof u||"function"==typeof i.getSnapshotBeforeUpdate;d||"function"!=typeof i.UNSAFE_componentWillReceiveProps&&"function"!=typeof i.componentWillReceiveProps||(l!==r||s!==c)&&ko(t,i,r,c),lo=!1;var p=t.memoizedState;i.state=p,mo(t,r,i,a),s=t.memoizedState,l!==r||p!==s||ma.current||lo?("function"==typeof u&&(bo(t,n,u,r),s=t.memoizedState),(l=lo||yo(t,n,l,r,p,s,c))?(d||"function"!=typeof i.UNSAFE_componentWillMount&&"function"!=typeof i.componentWillMount||("function"==typeof i.componentWillMount&&i.componentWillMount(),"function"==typeof i.UNSAFE_componentWillMount&&i.UNSAFE_componentWillMount()),"function"==typeof i.componentDidMount&&(t.flags|=4)):("function"==typeof i.componentDidMount&&(t.flags|=4),t.memoizedProps=r,t.memoizedState=s),i.props=r,i.state=s,i.context=c,r=l):("function"==typeof i.componentDidMount&&(t.flags|=4),r=!1)}else{i=t.stateNode,co(e,t),l=t.memoizedProps,c=t.type===t.elementType?l:Qa(t.type,l),i.props=c,d=t.pendingProps,p=i.context,"object"==typeof(s=n.contextType)&&null!==s?s=io(s):s=ga(t,s=ba(n)?ha:fa.current);var f=n.getDerivedStateFromProps;(u="function"==typeof f||"function"==typeof i.getSnapshotBeforeUpdate)||"function"!=typeof i.UNSAFE_componentWillReceiveProps&&"function"!=typeof i.componentWillReceiveProps||(l!==d||p!==s)&&ko(t,i,r,s),lo=!1,p=t.memoizedState,i.state=p,mo(t,r,i,a);var m=t.memoizedState;l!==d||p!==m||ma.current||lo?("function"==typeof f&&(bo(t,n,f,r),m=t.memoizedState),(c=lo||yo(t,n,c,r,p,m,s))?(u||"function"!=typeof i.UNSAFE_componentWillUpdate&&"function"!=typeof i.componentWillUpdate||("function"==typeof i.componentWillUpdate&&i.componentWillUpdate(r,m,s),"function"==typeof i.UNSAFE_componentWillUpdate&&i.UNSAFE_componentWillUpdate(r,m,s)),"function"==typeof i.componentDidUpdate&&(t.flags|=4),"function"==typeof i.getSnapshotBeforeUpdate&&(t.flags|=256)):("function"!=typeof i.componentDidUpdate||l===e.memoizedProps&&p===e.memoizedState||(t.flags|=4),"function"!=typeof i.getSnapshotBeforeUpdate||l===e.memoizedProps&&p===e.memoizedState||(t.flags|=256),t.memoizedProps=r,t.memoizedState=m),i.props=r,i.state=m,i.context=s,r=c):("function"!=typeof i.componentDidUpdate||l===e.memoizedProps&&p===e.memoizedState||(t.flags|=4),"function"!=typeof i.getSnapshotBeforeUpdate||l===e.memoizedProps&&p===e.memoizedState||(t.flags|=256),r=!1)}return Zi(e,t,n,r,o,a)}function Zi(e,t,n,r,a,o){$i(e,t);var i=0!=(64&t.flags);if(!r&&!i)return a&&Ea(t,n,!1),ol(e,t,o);r=t.stateNode,Mi.current=t;var l=i&&"function"!=typeof n.getDerivedStateFromError?null:r.render();return t.flags|=1,null!==e&&i?(t.child=To(t,e.child,null,o),t.child=To(t,null,l,o)):Bi(e,t,l,o),t.memoizedState=r.state,a&&Ea(t,n,!0),t.child}function Vi(e){var t=e.stateNode;t.pendingContext?ya(0,t.pendingContext,t.pendingContext!==t.context):t.context&&ya(0,t.context,!1),Io(e,t.containerInfo)}var Wi,Yi,Ki,Qi={dehydrated:null,retryLane:0};function Xi(e,t,n){var r,a=t.pendingProps,o=Bo.current,i=!1;return(r=0!=(64&t.flags))||(r=(null===e||null!==e.memoizedState)&&0!=(2&o)),r?(i=!0,t.flags&=-65):null!==e&&null===e.memoizedState||void 0===a.fallback||!0===a.unstable_avoidThisFallback||(o|=1),da(Bo,1&o),null===e?(void 0!==a.fallback&&Go(t),e=a.children,o=a.fallback,i?(e=Ji(t,e,o,n),t.child.memoizedState={baseLanes:n},t.memoizedState=Qi,e):"number"==typeof a.unstable_expectedLoadTime?(e=Ji(t,e,o,n),t.child.memoizedState={baseLanes:n},t.memoizedState=Qi,t.lanes=33554432,e):((n=Ys({mode:"visible",children:e},t.mode,n,null)).return=t,t.child=n)):(e.memoizedState,i?(a=tl(e,t,a.children,a.fallback,n),i=t.child,o=e.child.memoizedState,i.memoizedState=null===o?{baseLanes:n}:{baseLanes:o.baseLanes|n},i.childLanes=e.childLanes&~n,t.memoizedState=Qi,a):(n=el(e,t,a.children,n),t.memoizedState=null,n))}function Ji(e,t,n,r){var a=e.mode,o=e.child;return t={mode:"hidden",children:t},0==(2&a)&&null!==o?(o.childLanes=0,o.pendingProps=t):o=Ys(t,a,0,null),n=Ws(n,a,r,null),o.return=e,n.return=e,o.sibling=n,e.child=o,n}function el(e,t,n,r){var a=e.child;return e=a.sibling,n=Zs(a,{mode:"visible",children:n}),0==(2&t.mode)&&(n.lanes=r),n.return=t,n.sibling=null,null!==e&&(e.nextEffect=null,e.flags=8,t.firstEffect=t.lastEffect=e),t.child=n}function tl(e,t,n,r,a){var o=t.mode,i=e.child;e=i.sibling;var l={mode:"hidden",children:n};return 0==(2&o)&&t.child!==i?((n=t.child).childLanes=0,n.pendingProps=l,null!==(i=n.lastEffect)?(t.firstEffect=n.firstEffect,t.lastEffect=i,i.nextEffect=null):t.firstEffect=t.lastEffect=null):n=Zs(i,l),null!==e?r=Zs(e,r):(r=Ws(r,o,a,null)).flags|=2,r.return=t,n.return=t,n.sibling=r,t.child=n,r}function nl(e,t){e.lanes|=t;var n=e.alternate;null!==n&&(n.lanes|=t),ao(e.return,t)}function rl(e,t,n,r,a,o){var i=e.memoizedState;null===i?e.memoizedState={isBackwards:t,rendering:null,renderingStartTime:0,last:r,tail:n,tailMode:a,lastEffect:o}:(i.isBackwards=t,i.rendering=null,i.renderingStartTime=0,i.last=r,i.tail=n,i.tailMode=a,i.lastEffect=o)}function al(e,t,n){var r=t.pendingProps,a=r.revealOrder,o=r.tail;if(Bi(e,t,r.children,n),0!=(2&(r=Bo.current)))r=1&r|2,t.flags|=64;else{if(null!==e&&0!=(64&e.flags))e:for(e=t.child;null!==e;){if(13===e.tag)null!==e.memoizedState&&nl(e,n);else if(19===e.tag)nl(e,n);else if(null!==e.child){e.child.return=e,e=e.child;continue}if(e===t)break e;for(;null===e.sibling;){if(null===e.return||e.return===t)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}r&=1}if(da(Bo,r),0==(2&t.mode))t.memoizedState=null;else switch(a){case"forwards":for(n=t.child,a=null;null!==n;)null!==(e=n.alternate)&&null===jo(e)&&(a=n),n=n.sibling;null===(n=a)?(a=t.child,t.child=null):(a=n.sibling,n.sibling=null),rl(t,!1,a,n,o,t.lastEffect);break;case"backwards":for(n=null,a=t.child,t.child=null;null!==a;){if(null!==(e=a.alternate)&&null===jo(e)){t.child=a;break}e=a.sibling,a.sibling=n,n=a,a=e}rl(t,!0,n,null,o,t.lastEffect);break;case"together":rl(t,!1,null,null,void 0,t.lastEffect);break;default:t.memoizedState=null}return t.child}function ol(e,t,n){if(null!==e&&(t.dependencies=e.dependencies),Ul|=t.lanes,0!=(n&t.childLanes)){if(null!==e&&t.child!==e.child)throw Error(i(153));if(null!==t.child){for(n=Zs(e=t.child,e.pendingProps),t.child=n,n.return=t;null!==e.sibling;)e=e.sibling,(n=n.sibling=Zs(e,e.pendingProps)).return=t;n.sibling=null}return t.child}return null}function il(e,t){if(!qo)switch(e.tailMode){case"hidden":t=e.tail;for(var n=null;null!==t;)null!==t.alternate&&(n=t),t=t.sibling;null===n?e.tail=null:n.sibling=null;break;case"collapsed":n=e.tail;for(var r=null;null!==n;)null!==n.alternate&&(r=n),n=n.sibling;null===r?t||null===e.tail?e.tail=null:e.tail.sibling=null:r.sibling=null}}function ll(e,t,n){var r=t.pendingProps;switch(t.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return null;case 1:case 17:return ba(t.type)&&va(),null;case 3:return Do(),ua(ma),ua(fa),Ko(),(r=t.stateNode).pendingContext&&(r.context=r.pendingContext,r.pendingContext=null),null!==e&&null!==e.child||(Vo(t)?t.flags|=4:r.hydrate||(t.flags|=256)),null;case 5:Fo(t);var o=Po(Oo.current);if(n=t.type,null!==e&&null!=t.stateNode)Yi(e,t,n,r),e.ref!==t.ref&&(t.flags|=128);else{if(!r){if(null===t.stateNode)throw Error(i(166));return null}if(e=Po(Ro.current),Vo(t)){r=t.stateNode,n=t.type;var l=t.memoizedProps;switch(r[Xr]=t,r[Jr]=l,n){case"dialog":Rr("cancel",r),Rr("close",r);break;case"iframe":case"object":case"embed":Rr("load",r);break;case"video":case"audio":for(e=0;e<Cr.length;e++)Rr(Cr[e],r);break;case"source":Rr("error",r);break;case"img":case"image":case"link":Rr("error",r),Rr("load",r);break;case"details":Rr("toggle",r);break;case"input":ee(r,l),Rr("invalid",r);break;case"select":r._wrapperState={wasMultiple:!!l.multiple},Rr("invalid",r);break;case"textarea":se(r,l),Rr("invalid",r)}for(var c in Se(n,l),e=null,l)l.hasOwnProperty(c)&&(o=l[c],"children"===c?"string"==typeof o?r.textContent!==o&&(e=["children",o]):"number"==typeof o&&r.textContent!==""+o&&(e=["children",""+o]):s.hasOwnProperty(c)&&null!=o&&"onScroll"===c&&Rr("scroll",r));switch(n){case"input":K(r),re(r,l,!0);break;case"textarea":K(r),ue(r);break;case"select":case"option":break;default:"function"==typeof l.onClick&&(r.onclick=zr)}r=e,t.updateQueue=r,null!==r&&(t.flags|=4)}else{switch(c=9===o.nodeType?o:o.ownerDocument,e===de&&(e=fe(n)),e===de?"script"===n?((e=c.createElement("div")).innerHTML="<script><\/script>",e=e.removeChild(e.firstChild)):"string"==typeof r.is?e=c.createElement(n,{is:r.is}):(e=c.createElement(n),"select"===n&&(c=e,r.multiple?c.multiple=!0:r.size&&(c.size=r.size))):e=c.createElementNS(e,n),e[Xr]=t,e[Jr]=r,Wi(e,t),t.stateNode=e,c=_e(n,r),n){case"dialog":Rr("cancel",e),Rr("close",e),o=r;break;case"iframe":case"object":case"embed":Rr("load",e),o=r;break;case"video":case"audio":for(o=0;o<Cr.length;o++)Rr(Cr[o],e);o=r;break;case"source":Rr("error",e),o=r;break;case"img":case"image":case"link":Rr("error",e),Rr("load",e),o=r;break;case"details":Rr("toggle",e),o=r;break;case"input":ee(e,r),o=J(e,r),Rr("invalid",e);break;case"option":o=oe(e,r);break;case"select":e._wrapperState={wasMultiple:!!r.multiple},o=a({},r,{value:void 0}),Rr("invalid",e);break;case"textarea":se(e,r),o=le(e,r),Rr("invalid",e);break;default:o=r}Se(n,o);var u=o;for(l in u)if(u.hasOwnProperty(l)){var d=u[l];"style"===l?Ee(e,d):"dangerouslySetInnerHTML"===l?null!=(d=d?d.__html:void 0)&&be(e,d):"children"===l?"string"==typeof d?("textarea"!==n||""!==d)&&ve(e,d):"number"==typeof d&&ve(e,""+d):"suppressContentEditableWarning"!==l&&"suppressHydrationWarning"!==l&&"autoFocus"!==l&&(s.hasOwnProperty(l)?null!=d&&"onScroll"===l&&Rr("scroll",e):null!=d&&w(e,l,d,c))}switch(n){case"input":K(e),re(e,r,!1);break;case"textarea":K(e),ue(e);break;case"option":null!=r.value&&e.setAttribute("value",""+W(r.value));break;case"select":e.multiple=!!r.multiple,null!=(l=r.value)?ie(e,!!r.multiple,l,!1):null!=r.defaultValue&&ie(e,!!r.multiple,r.defaultValue,!0);break;default:"function"==typeof o.onClick&&(e.onclick=zr)}$r(n,r)&&(t.flags|=4)}null!==t.ref&&(t.flags|=128)}return null;case 6:if(e&&null!=t.stateNode)Ki(0,t,e.memoizedProps,r);else{if("string"!=typeof r&&null===t.stateNode)throw Error(i(166));n=Po(Oo.current),Po(Ro.current),Vo(t)?(r=t.stateNode,n=t.memoizedProps,r[Xr]=t,r.nodeValue!==n&&(t.flags|=4)):((r=(9===n.nodeType?n:n.ownerDocument).createTextNode(r))[Xr]=t,t.stateNode=r)}return null;case 13:return ua(Bo),r=t.memoizedState,0!=(64&t.flags)?(t.lanes=n,t):(r=null!==r,n=!1,null===e?void 0!==t.memoizedProps.fallback&&Vo(t):n=null!==e.memoizedState,r&&!n&&0!=(2&t.mode)&&(null===e&&!0!==t.memoizedProps.unstable_avoidThisFallback||0!=(1&Bo.current)?0===Bl&&(Bl=3):(0!==Bl&&3!==Bl||(Bl=4),null===Pl||0==(134217727&Ul)&&0==(134217727&ql)||vs(Pl,Dl))),(r||n)&&(t.flags|=4),null);case 4:return Do(),null===e&&Or(t.stateNode.containerInfo),null;case 10:return ro(t),null;case 19:if(ua(Bo),null===(r=t.memoizedState))return null;if(l=0!=(64&t.flags),null===(c=r.rendering))if(l)il(r,!1);else{if(0!==Bl||null!==e&&0!=(64&e.flags))for(e=t.child;null!==e;){if(null!==(c=jo(e))){for(t.flags|=64,il(r,!1),null!==(l=c.updateQueue)&&(t.updateQueue=l,t.flags|=4),null===r.lastEffect&&(t.firstEffect=null),t.lastEffect=r.lastEffect,r=n,n=t.child;null!==n;)e=r,(l=n).flags&=2,l.nextEffect=null,l.firstEffect=null,l.lastEffect=null,null===(c=l.alternate)?(l.childLanes=0,l.lanes=e,l.child=null,l.memoizedProps=null,l.memoizedState=null,l.updateQueue=null,l.dependencies=null,l.stateNode=null):(l.childLanes=c.childLanes,l.lanes=c.lanes,l.child=c.child,l.memoizedProps=c.memoizedProps,l.memoizedState=c.memoizedState,l.updateQueue=c.updateQueue,l.type=c.type,e=c.dependencies,l.dependencies=null===e?null:{lanes:e.lanes,firstContext:e.firstContext}),n=n.sibling;return da(Bo,1&Bo.current|2),t.child}e=e.sibling}null!==r.tail&&$a()>Zl&&(t.flags|=64,l=!0,il(r,!1),t.lanes=33554432)}else{if(!l)if(null!==(e=jo(c))){if(t.flags|=64,l=!0,null!==(n=e.updateQueue)&&(t.updateQueue=n,t.flags|=4),il(r,!0),null===r.tail&&"hidden"===r.tailMode&&!c.alternate&&!qo)return null!==(t=t.lastEffect=r.lastEffect)&&(t.nextEffect=null),null}else 2*$a()-r.renderingStartTime>Zl&&1073741824!==n&&(t.flags|=64,l=!0,il(r,!1),t.lanes=33554432);r.isBackwards?(c.sibling=t.child,t.child=c):(null!==(n=r.last)?n.sibling=c:t.child=c,r.last=c)}return null!==r.tail?(n=r.tail,r.rendering=n,r.tail=n.sibling,r.lastEffect=t.lastEffect,r.renderingStartTime=$a(),n.sibling=null,t=Bo.current,da(Bo,l?1&t|2:1&t),n):null;case 23:case 24:return xs(),null!==e&&null!==e.memoizedState!=(null!==t.memoizedState)&&"unstable-defer-without-hiding"!==r.mode&&(t.flags|=4),null}throw Error(i(156,t.tag))}function sl(e){switch(e.tag){case 1:ba(e.type)&&va();var t=e.flags;return 4096&t?(e.flags=-4097&t|64,e):null;case 3:if(Do(),ua(ma),ua(fa),Ko(),0!=(64&(t=e.flags)))throw Error(i(285));return e.flags=-4097&t|64,e;case 5:return Fo(e),null;case 13:return ua(Bo),4096&(t=e.flags)?(e.flags=-4097&t|64,e):null;case 19:return ua(Bo),null;case 4:return Do(),null;case 10:return ro(e),null;case 23:case 24:return xs(),null;default:return null}}function cl(e,t){try{var n="",r=t;do{n+=Z(r),r=r.return}while(r);var a=n}catch(o){a="\nError generating stack: "+o.message+"\n"+o.stack}return{value:e,source:t,stack:a}}function ul(e,t){try{console.error(t.value)}catch(n){setTimeout((function(){throw n}))}}Wi=function(e,t){for(var n=t.child;null!==n;){if(5===n.tag||6===n.tag)e.appendChild(n.stateNode);else if(4!==n.tag&&null!==n.child){n.child.return=n,n=n.child;continue}if(n===t)break;for(;null===n.sibling;){if(null===n.return||n.return===t)return;n=n.return}n.sibling.return=n.return,n=n.sibling}},Yi=function(e,t,n,r){var o=e.memoizedProps;if(o!==r){e=t.stateNode,Po(Ro.current);var i,l=null;switch(n){case"input":o=J(e,o),r=J(e,r),l=[];break;case"option":o=oe(e,o),r=oe(e,r),l=[];break;case"select":o=a({},o,{value:void 0}),r=a({},r,{value:void 0}),l=[];break;case"textarea":o=le(e,o),r=le(e,r),l=[];break;default:"function"!=typeof o.onClick&&"function"==typeof r.onClick&&(e.onclick=zr)}for(d in Se(n,r),n=null,o)if(!r.hasOwnProperty(d)&&o.hasOwnProperty(d)&&null!=o[d])if("style"===d){var c=o[d];for(i in c)c.hasOwnProperty(i)&&(n||(n={}),n[i]="")}else"dangerouslySetInnerHTML"!==d&&"children"!==d&&"suppressContentEditableWarning"!==d&&"suppressHydrationWarning"!==d&&"autoFocus"!==d&&(s.hasOwnProperty(d)?l||(l=[]):(l=l||[]).push(d,null));for(d in r){var u=r[d];if(c=null!=o?o[d]:void 0,r.hasOwnProperty(d)&&u!==c&&(null!=u||null!=c))if("style"===d)if(c){for(i in c)!c.hasOwnProperty(i)||u&&u.hasOwnProperty(i)||(n||(n={}),n[i]="");for(i in u)u.hasOwnProperty(i)&&c[i]!==u[i]&&(n||(n={}),n[i]=u[i])}else n||(l||(l=[]),l.push(d,n)),n=u;else"dangerouslySetInnerHTML"===d?(u=u?u.__html:void 0,c=c?c.__html:void 0,null!=u&&c!==u&&(l=l||[]).push(d,u)):"children"===d?"string"!=typeof u&&"number"!=typeof u||(l=l||[]).push(d,""+u):"suppressContentEditableWarning"!==d&&"suppressHydrationWarning"!==d&&(s.hasOwnProperty(d)?(null!=u&&"onScroll"===d&&Rr("scroll",e),l||c===u||(l=[])):"object"==typeof u&&null!==u&&u.$$typeof===D?u.toString():(l=l||[]).push(d,u))}n&&(l=l||[]).push("style",n);var d=l;(t.updateQueue=d)&&(t.flags|=4)}},Ki=function(e,t,n,r){n!==r&&(t.flags|=4)};var dl="function"==typeof WeakMap?WeakMap:Map;function pl(e,t,n){(n=uo(-1,n)).tag=3,n.payload={element:null};var r=t.value;return n.callback=function(){Kl||(Kl=!0,Ql=r),ul(0,t)},n}function fl(e,t,n){(n=uo(-1,n)).tag=3;var r=e.type.getDerivedStateFromError;if("function"==typeof r){var a=t.value;n.payload=function(){return ul(0,t),r(a)}}var o=e.stateNode;return null!==o&&"function"==typeof o.componentDidCatch&&(n.callback=function(){"function"!=typeof r&&(null===Xl?Xl=new Set([this]):Xl.add(this),ul(0,t));var e=t.stack;this.componentDidCatch(t.value,{componentStack:null!==e?e:""})}),n}var ml="function"==typeof WeakSet?WeakSet:Set;function hl(e){var t=e.ref;if(null!==t)if("function"==typeof t)try{t(null)}catch(n){zs(e,n)}else t.current=null}function gl(e,t){switch(t.tag){case 0:case 11:case 15:case 22:case 5:case 6:case 4:case 17:return;case 1:if(256&t.flags&&null!==e){var n=e.memoizedProps,r=e.memoizedState;t=(e=t.stateNode).getSnapshotBeforeUpdate(t.elementType===t.type?n:Qa(t.type,n),r),e.__reactInternalSnapshotBeforeUpdate=t}return;case 3:return void(256&t.flags&&Vr(t.stateNode.containerInfo))}throw Error(i(163))}function bl(e,t,n){switch(n.tag){case 0:case 11:case 15:case 22:if(null!==(t=null!==(t=n.updateQueue)?t.lastEffect:null)){e=t=t.next;do{if(3==(3&e.tag)){var r=e.create;e.destroy=r()}e=e.next}while(e!==t)}if(null!==(t=null!==(t=n.updateQueue)?t.lastEffect:null)){e=t=t.next;do{var a=e;r=a.next,0!=(4&(a=a.tag))&&0!=(1&a)&&(Fs(n,e),Ms(n,e)),e=r}while(e!==t)}return;case 1:return e=n.stateNode,4&n.flags&&(null===t?e.componentDidMount():(r=n.elementType===n.type?t.memoizedProps:Qa(n.type,t.memoizedProps),e.componentDidUpdate(r,t.memoizedState,e.__reactInternalSnapshotBeforeUpdate))),void(null!==(t=n.updateQueue)&&ho(n,t,e));case 3:if(null!==(t=n.updateQueue)){if(e=null,null!==n.child)switch(n.child.tag){case 5:case 1:e=n.child.stateNode}ho(n,t,e)}return;case 5:return e=n.stateNode,void(null===t&&4&n.flags&&$r(n.type,n.memoizedProps)&&e.focus());case 6:case 4:case 12:case 19:case 17:case 20:case 21:case 23:case 24:return;case 13:return void(null===n.memoizedState&&(n=n.alternate,null!==n&&(n=n.memoizedState,null!==n&&(n=n.dehydrated,null!==n&&Et(n)))))}throw Error(i(163))}function vl(e,t){for(var n=e;;){if(5===n.tag){var r=n.stateNode;if(t)"function"==typeof(r=r.style).setProperty?r.setProperty("display","none","important"):r.display="none";else{r=n.stateNode;var a=n.memoizedProps.style;a=null!=a&&a.hasOwnProperty("display")?a.display:null,r.style.display=ke("display",a)}}else if(6===n.tag)n.stateNode.nodeValue=t?"":n.memoizedProps;else if((23!==n.tag&&24!==n.tag||null===n.memoizedState||n===e)&&null!==n.child){n.child.return=n,n=n.child;continue}if(n===e)break;for(;null===n.sibling;){if(null===n.return||n.return===e)return;n=n.return}n.sibling.return=n.return,n=n.sibling}}function yl(e,t){if(Sa&&"function"==typeof Sa.onCommitFiberUnmount)try{Sa.onCommitFiberUnmount(xa,t)}catch(o){}switch(t.tag){case 0:case 11:case 14:case 15:case 22:if(null!==(e=t.updateQueue)&&null!==(e=e.lastEffect)){var n=e=e.next;do{var r=n,a=r.destroy;if(r=r.tag,void 0!==a)if(0!=(4&r))Fs(t,n);else{r=t;try{a()}catch(o){zs(r,o)}}n=n.next}while(n!==e)}break;case 1:if(hl(t),"function"==typeof(e=t.stateNode).componentWillUnmount)try{e.props=t.memoizedProps,e.state=t.memoizedState,e.componentWillUnmount()}catch(o){zs(t,o)}break;case 5:hl(t);break;case 4:_l(e,t)}}function wl(e){e.alternate=null,e.child=null,e.dependencies=null,e.firstEffect=null,e.lastEffect=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.return=null,e.updateQueue=null}function kl(e){return 5===e.tag||3===e.tag||4===e.tag}function El(e){e:{for(var t=e.return;null!==t;){if(kl(t))break e;t=t.return}throw Error(i(160))}var n=t;switch(t=n.stateNode,n.tag){case 5:var r=!1;break;case 3:case 4:t=t.containerInfo,r=!0;break;default:throw Error(i(161))}16&n.flags&&(ve(t,""),n.flags&=-17);e:t:for(n=e;;){for(;null===n.sibling;){if(null===n.return||kl(n.return)){n=null;break e}n=n.return}for(n.sibling.return=n.return,n=n.sibling;5!==n.tag&&6!==n.tag&&18!==n.tag;){if(2&n.flags)continue t;if(null===n.child||4===n.tag)continue t;n.child.return=n,n=n.child}if(!(2&n.flags)){n=n.stateNode;break e}}r?xl(e,n,t):Sl(e,n,t)}function xl(e,t,n){var r=e.tag,a=5===r||6===r;if(a)e=a?e.stateNode:e.stateNode.instance,t?8===n.nodeType?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(8===n.nodeType?(t=n.parentNode).insertBefore(e,n):(t=n).appendChild(e),null!=(n=n._reactRootContainer)||null!==t.onclick||(t.onclick=zr));else if(4!==r&&null!==(e=e.child))for(xl(e,t,n),e=e.sibling;null!==e;)xl(e,t,n),e=e.sibling}function Sl(e,t,n){var r=e.tag,a=5===r||6===r;if(a)e=a?e.stateNode:e.stateNode.instance,t?n.insertBefore(e,t):n.appendChild(e);else if(4!==r&&null!==(e=e.child))for(Sl(e,t,n),e=e.sibling;null!==e;)Sl(e,t,n),e=e.sibling}function _l(e,t){for(var n,r,a=t,o=!1;;){if(!o){o=a.return;e:for(;;){if(null===o)throw Error(i(160));switch(n=o.stateNode,o.tag){case 5:r=!1;break e;case 3:case 4:n=n.containerInfo,r=!0;break e}o=o.return}o=!0}if(5===a.tag||6===a.tag){e:for(var l=e,s=a,c=s;;)if(yl(l,c),null!==c.child&&4!==c.tag)c.child.return=c,c=c.child;else{if(c===s)break e;for(;null===c.sibling;){if(null===c.return||c.return===s)break e;c=c.return}c.sibling.return=c.return,c=c.sibling}r?(l=n,s=a.stateNode,8===l.nodeType?l.parentNode.removeChild(s):l.removeChild(s)):n.removeChild(a.stateNode)}else if(4===a.tag){if(null!==a.child){n=a.stateNode.containerInfo,r=!0,a.child.return=a,a=a.child;continue}}else if(yl(e,a),null!==a.child){a.child.return=a,a=a.child;continue}if(a===t)break;for(;null===a.sibling;){if(null===a.return||a.return===t)return;4===(a=a.return).tag&&(o=!1)}a.sibling.return=a.return,a=a.sibling}}function Cl(e,t){switch(t.tag){case 0:case 11:case 14:case 15:case 22:var n=t.updateQueue;if(null!==(n=null!==n?n.lastEffect:null)){var r=n=n.next;do{3==(3&r.tag)&&(e=r.destroy,r.destroy=void 0,void 0!==e&&e()),r=r.next}while(r!==n)}return;case 1:case 12:case 17:return;case 5:if(null!=(n=t.stateNode)){r=t.memoizedProps;var a=null!==e?e.memoizedProps:r;e=t.type;var o=t.updateQueue;if(t.updateQueue=null,null!==o){for(n[Jr]=r,"input"===e&&"radio"===r.type&&null!=r.name&&te(n,r),_e(e,a),t=_e(e,r),a=0;a<o.length;a+=2){var l=o[a],s=o[a+1];"style"===l?Ee(n,s):"dangerouslySetInnerHTML"===l?be(n,s):"children"===l?ve(n,s):w(n,l,s,t)}switch(e){case"input":ne(n,r);break;case"textarea":ce(n,r);break;case"select":e=n._wrapperState.wasMultiple,n._wrapperState.wasMultiple=!!r.multiple,null!=(o=r.value)?ie(n,!!r.multiple,o,!1):e!==!!r.multiple&&(null!=r.defaultValue?ie(n,!!r.multiple,r.defaultValue,!0):ie(n,!!r.multiple,r.multiple?[]:"",!1))}}}return;case 6:if(null===t.stateNode)throw Error(i(162));return void(t.stateNode.nodeValue=t.memoizedProps);case 3:return void((n=t.stateNode).hydrate&&(n.hydrate=!1,Et(n.containerInfo)));case 13:return null!==t.memoizedState&&(Gl=$a(),vl(t.child,!0)),void Tl(t);case 19:return void Tl(t);case 23:case 24:return void vl(t,null!==t.memoizedState)}throw Error(i(163))}function Tl(e){var t=e.updateQueue;if(null!==t){e.updateQueue=null;var n=e.stateNode;null===n&&(n=e.stateNode=new ml),t.forEach((function(t){var r=qs.bind(null,e,t);n.has(t)||(n.add(t),t.then(r,r))}))}}function Al(e,t){return null!==e&&(null===(e=e.memoizedState)||null!==e.dehydrated)&&(null!==(t=t.memoizedState)&&null===t.dehydrated)}var Ll=Math.ceil,Rl=k.ReactCurrentDispatcher,Nl=k.ReactCurrentOwner,Ol=0,Pl=null,Il=null,Dl=0,Ml=0,Fl=ca(0),Bl=0,jl=null,zl=0,Ul=0,ql=0,$l=0,Hl=null,Gl=0,Zl=1/0;function Vl(){Zl=$a()+500}var Wl,Yl=null,Kl=!1,Ql=null,Xl=null,Jl=!1,es=null,ts=90,ns=[],rs=[],as=null,os=0,is=null,ls=-1,ss=0,cs=0,us=null,ds=!1;function ps(){return 0!=(48&Ol)?$a():-1!==ls?ls:ls=$a()}function fs(e){if(0==(2&(e=e.mode)))return 1;if(0==(4&e))return 99===Ha()?1:2;if(0===ss&&(ss=zl),0!==Ka.transition){0!==cs&&(cs=null!==Hl?Hl.pendingLanes:0),e=ss;var t=4186112&~cs;return 0===(t&=-t)&&(0===(t=(e=4186112&~e)&-e)&&(t=8192)),t}return e=Ha(),0!=(4&Ol)&&98===e?e=zt(12,ss):e=zt(e=function(e){switch(e){case 99:return 15;case 98:return 10;case 97:case 96:return 8;case 95:return 2;default:return 0}}(e),ss),e}function ms(e,t,n){if(50<os)throw os=0,is=null,Error(i(185));if(null===(e=hs(e,t)))return null;$t(e,t,n),e===Pl&&(ql|=t,4===Bl&&vs(e,Dl));var r=Ha();1===t?0!=(8&Ol)&&0==(48&Ol)?ys(e):(gs(e,n),0===Ol&&(Vl(),Wa())):(0==(4&Ol)||98!==r&&99!==r||(null===as?as=new Set([e]):as.add(e)),gs(e,n)),Hl=e}function hs(e,t){e.lanes|=t;var n=e.alternate;for(null!==n&&(n.lanes|=t),n=e,e=e.return;null!==e;)e.childLanes|=t,null!==(n=e.alternate)&&(n.childLanes|=t),n=e,e=e.return;return 3===n.tag?n.stateNode:null}function gs(e,t){for(var n=e.callbackNode,r=e.suspendedLanes,a=e.pingedLanes,o=e.expirationTimes,l=e.pendingLanes;0<l;){var s=31-Ht(l),c=1<<s,u=o[s];if(-1===u){if(0==(c&r)||0!=(c&a)){u=t,Ft(c);var d=Mt;o[s]=10<=d?u+250:6<=d?u+5e3:-1}}else u<=t&&(e.expiredLanes|=c);l&=~c}if(r=Bt(e,e===Pl?Dl:0),t=Mt,0===r)null!==n&&(n!==Fa&&Ta(n),e.callbackNode=null,e.callbackPriority=0);else{if(null!==n){if(e.callbackPriority===t)return;n!==Fa&&Ta(n)}15===t?(n=ys.bind(null,e),null===ja?(ja=[n],za=Ca(Oa,Ya)):ja.push(n),n=Fa):14===t?n=Va(99,ys.bind(null,e)):(n=function(e){switch(e){case 15:case 14:return 99;case 13:case 12:case 11:case 10:return 98;case 9:case 8:case 7:case 6:case 4:case 5:return 97;case 3:case 2:case 1:return 95;case 0:return 90;default:throw Error(i(358,e))}}(t),n=Va(n,bs.bind(null,e))),e.callbackPriority=t,e.callbackNode=n}}function bs(e){if(ls=-1,cs=ss=0,0!=(48&Ol))throw Error(i(327));var t=e.callbackNode;if(Ds()&&e.callbackNode!==t)return null;var n=Bt(e,e===Pl?Dl:0);if(0===n)return null;var r=n,a=Ol;Ol|=16;var o=Cs();for(Pl===e&&Dl===r||(Vl(),Ss(e,r));;)try{Ls();break}catch(s){_s(e,s)}if(no(),Rl.current=o,Ol=a,null!==Il?r=0:(Pl=null,Dl=0,r=Bl),0!=(zl&ql))Ss(e,0);else if(0!==r){if(2===r&&(Ol|=64,e.hydrate&&(e.hydrate=!1,Vr(e.containerInfo)),0!==(n=jt(e))&&(r=Ts(e,n))),1===r)throw t=jl,Ss(e,0),vs(e,n),gs(e,$a()),t;switch(e.finishedWork=e.current.alternate,e.finishedLanes=n,r){case 0:case 1:throw Error(i(345));case 2:case 5:Os(e);break;case 3:if(vs(e,n),(62914560&n)===n&&10<(r=Gl+500-$a())){if(0!==Bt(e,0))break;if(((a=e.suspendedLanes)&n)!==n){ps(),e.pingedLanes|=e.suspendedLanes&a;break}e.timeoutHandle=Gr(Os.bind(null,e),r);break}Os(e);break;case 4:if(vs(e,n),(4186112&n)===n)break;for(r=e.eventTimes,a=-1;0<n;){var l=31-Ht(n);o=1<<l,(l=r[l])>a&&(a=l),n&=~o}if(n=a,10<(n=(120>(n=$a()-n)?120:480>n?480:1080>n?1080:1920>n?1920:3e3>n?3e3:4320>n?4320:1960*Ll(n/1960))-n)){e.timeoutHandle=Gr(Os.bind(null,e),n);break}Os(e);break;default:throw Error(i(329))}}return gs(e,$a()),e.callbackNode===t?bs.bind(null,e):null}function vs(e,t){for(t&=~$l,t&=~ql,e.suspendedLanes|=t,e.pingedLanes&=~t,e=e.expirationTimes;0<t;){var n=31-Ht(t),r=1<<n;e[n]=-1,t&=~r}}function ys(e){if(0!=(48&Ol))throw Error(i(327));if(Ds(),e===Pl&&0!=(e.expiredLanes&Dl)){var t=Dl,n=Ts(e,t);0!=(zl&ql)&&(n=Ts(e,t=Bt(e,t)))}else n=Ts(e,t=Bt(e,0));if(0!==e.tag&&2===n&&(Ol|=64,e.hydrate&&(e.hydrate=!1,Vr(e.containerInfo)),0!==(t=jt(e))&&(n=Ts(e,t))),1===n)throw n=jl,Ss(e,0),vs(e,t),gs(e,$a()),n;return e.finishedWork=e.current.alternate,e.finishedLanes=t,Os(e),gs(e,$a()),null}function ws(e,t){var n=Ol;Ol|=1;try{return e(t)}finally{0===(Ol=n)&&(Vl(),Wa())}}function ks(e,t){var n=Ol;Ol&=-2,Ol|=8;try{return e(t)}finally{0===(Ol=n)&&(Vl(),Wa())}}function Es(e,t){da(Fl,Ml),Ml|=t,zl|=t}function xs(){Ml=Fl.current,ua(Fl)}function Ss(e,t){e.finishedWork=null,e.finishedLanes=0;var n=e.timeoutHandle;if(-1!==n&&(e.timeoutHandle=-1,Zr(n)),null!==Il)for(n=Il.return;null!==n;){var r=n;switch(r.tag){case 1:null!=(r=r.type.childContextTypes)&&va();break;case 3:Do(),ua(ma),ua(fa),Ko();break;case 5:Fo(r);break;case 4:Do();break;case 13:case 19:ua(Bo);break;case 10:ro(r);break;case 23:case 24:xs()}n=n.return}Pl=e,Il=Zs(e.current,null),Dl=Ml=zl=t,Bl=0,jl=null,$l=ql=Ul=0}function _s(e,t){for(;;){var n=Il;try{if(no(),Qo.current=Oi,ri){for(var r=ei.memoizedState;null!==r;){var a=r.queue;null!==a&&(a.pending=null),r=r.next}ri=!1}if(Jo=0,ni=ti=ei=null,ai=!1,Nl.current=null,null===n||null===n.return){Bl=1,jl=t,Il=null;break}e:{var o=e,i=n.return,l=n,s=t;if(t=Dl,l.flags|=2048,l.firstEffect=l.lastEffect=null,null!==s&&"object"==typeof s&&"function"==typeof s.then){var c=s;if(0==(2&l.mode)){var u=l.alternate;u?(l.updateQueue=u.updateQueue,l.memoizedState=u.memoizedState,l.lanes=u.lanes):(l.updateQueue=null,l.memoizedState=null)}var d=0!=(1&Bo.current),p=i;do{var f;if(f=13===p.tag){var m=p.memoizedState;if(null!==m)f=null!==m.dehydrated;else{var h=p.memoizedProps;f=void 0!==h.fallback&&(!0!==h.unstable_avoidThisFallback||!d)}}if(f){var g=p.updateQueue;if(null===g){var b=new Set;b.add(c),p.updateQueue=b}else g.add(c);if(0==(2&p.mode)){if(p.flags|=64,l.flags|=16384,l.flags&=-2981,1===l.tag)if(null===l.alternate)l.tag=17;else{var v=uo(-1,1);v.tag=2,po(l,v)}l.lanes|=1;break e}s=void 0,l=t;var y=o.pingCache;if(null===y?(y=o.pingCache=new dl,s=new Set,y.set(c,s)):void 0===(s=y.get(c))&&(s=new Set,y.set(c,s)),!s.has(l)){s.add(l);var w=Us.bind(null,o,c,l);c.then(w,w)}p.flags|=4096,p.lanes=t;break e}p=p.return}while(null!==p);s=Error((V(l.type)||"A React component")+" suspended while rendering, but no fallback UI was specified.\n\nAdd a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display.")}5!==Bl&&(Bl=2),s=cl(s,l),p=i;do{switch(p.tag){case 3:o=s,p.flags|=4096,t&=-t,p.lanes|=t,fo(p,pl(0,o,t));break e;case 1:o=s;var k=p.type,E=p.stateNode;if(0==(64&p.flags)&&("function"==typeof k.getDerivedStateFromError||null!==E&&"function"==typeof E.componentDidCatch&&(null===Xl||!Xl.has(E)))){p.flags|=4096,t&=-t,p.lanes|=t,fo(p,fl(p,o,t));break e}}p=p.return}while(null!==p)}Ns(n)}catch(x){t=x,Il===n&&null!==n&&(Il=n=n.return);continue}break}}function Cs(){var e=Rl.current;return Rl.current=Oi,null===e?Oi:e}function Ts(e,t){var n=Ol;Ol|=16;var r=Cs();for(Pl===e&&Dl===t||Ss(e,t);;)try{As();break}catch(a){_s(e,a)}if(no(),Ol=n,Rl.current=r,null!==Il)throw Error(i(261));return Pl=null,Dl=0,Bl}function As(){for(;null!==Il;)Rs(Il)}function Ls(){for(;null!==Il&&!Aa();)Rs(Il)}function Rs(e){var t=Wl(e.alternate,e,Ml);e.memoizedProps=e.pendingProps,null===t?Ns(e):Il=t,Nl.current=null}function Ns(e){var t=e;do{var n=t.alternate;if(e=t.return,0==(2048&t.flags)){if(null!==(n=ll(n,t,Ml)))return void(Il=n);if(24!==(n=t).tag&&23!==n.tag||null===n.memoizedState||0!=(1073741824&Ml)||0==(4&n.mode)){for(var r=0,a=n.child;null!==a;)r|=a.lanes|a.childLanes,a=a.sibling;n.childLanes=r}null!==e&&0==(2048&e.flags)&&(null===e.firstEffect&&(e.firstEffect=t.firstEffect),null!==t.lastEffect&&(null!==e.lastEffect&&(e.lastEffect.nextEffect=t.firstEffect),e.lastEffect=t.lastEffect),1<t.flags&&(null!==e.lastEffect?e.lastEffect.nextEffect=t:e.firstEffect=t,e.lastEffect=t))}else{if(null!==(n=sl(t)))return n.flags&=2047,void(Il=n);null!==e&&(e.firstEffect=e.lastEffect=null,e.flags|=2048)}if(null!==(t=t.sibling))return void(Il=t);Il=t=e}while(null!==t);0===Bl&&(Bl=5)}function Os(e){var t=Ha();return Za(99,Ps.bind(null,e,t)),null}function Ps(e,t){do{Ds()}while(null!==es);if(0!=(48&Ol))throw Error(i(327));var n=e.finishedWork;if(null===n)return null;if(e.finishedWork=null,e.finishedLanes=0,n===e.current)throw Error(i(177));e.callbackNode=null;var r=n.lanes|n.childLanes,a=r,o=e.pendingLanes&~a;e.pendingLanes=a,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=a,e.mutableReadLanes&=a,e.entangledLanes&=a,a=e.entanglements;for(var l=e.eventTimes,s=e.expirationTimes;0<o;){var c=31-Ht(o),u=1<<c;a[c]=0,l[c]=-1,s[c]=-1,o&=~u}if(null!==as&&0==(24&r)&&as.has(e)&&as.delete(e),e===Pl&&(Il=Pl=null,Dl=0),1<n.flags?null!==n.lastEffect?(n.lastEffect.nextEffect=n,r=n.firstEffect):r=n:r=n.firstEffect,null!==r){if(a=Ol,Ol|=32,Nl.current=null,Ur=Yt,br(l=gr())){if("selectionStart"in l)s={start:l.selectionStart,end:l.selectionEnd};else e:if(s=(s=l.ownerDocument)&&s.defaultView||window,(u=s.getSelection&&s.getSelection())&&0!==u.rangeCount){s=u.anchorNode,o=u.anchorOffset,c=u.focusNode,u=u.focusOffset;try{s.nodeType,c.nodeType}catch(C){s=null;break e}var d=0,p=-1,f=-1,m=0,h=0,g=l,b=null;t:for(;;){for(var v;g!==s||0!==o&&3!==g.nodeType||(p=d+o),g!==c||0!==u&&3!==g.nodeType||(f=d+u),3===g.nodeType&&(d+=g.nodeValue.length),null!==(v=g.firstChild);)b=g,g=v;for(;;){if(g===l)break t;if(b===s&&++m===o&&(p=d),b===c&&++h===u&&(f=d),null!==(v=g.nextSibling))break;b=(g=b).parentNode}g=v}s=-1===p||-1===f?null:{start:p,end:f}}else s=null;s=s||{start:0,end:0}}else s=null;qr={focusedElem:l,selectionRange:s},Yt=!1,us=null,ds=!1,Yl=r;do{try{Is()}catch(C){if(null===Yl)throw Error(i(330));zs(Yl,C),Yl=Yl.nextEffect}}while(null!==Yl);us=null,Yl=r;do{try{for(l=e;null!==Yl;){var y=Yl.flags;if(16&y&&ve(Yl.stateNode,""),128&y){var w=Yl.alternate;if(null!==w){var k=w.ref;null!==k&&("function"==typeof k?k(null):k.current=null)}}switch(1038&y){case 2:El(Yl),Yl.flags&=-3;break;case 6:El(Yl),Yl.flags&=-3,Cl(Yl.alternate,Yl);break;case 1024:Yl.flags&=-1025;break;case 1028:Yl.flags&=-1025,Cl(Yl.alternate,Yl);break;case 4:Cl(Yl.alternate,Yl);break;case 8:_l(l,s=Yl);var E=s.alternate;wl(s),null!==E&&wl(E)}Yl=Yl.nextEffect}}catch(C){if(null===Yl)throw Error(i(330));zs(Yl,C),Yl=Yl.nextEffect}}while(null!==Yl);if(k=qr,w=gr(),y=k.focusedElem,l=k.selectionRange,w!==y&&y&&y.ownerDocument&&hr(y.ownerDocument.documentElement,y)){null!==l&&br(y)&&(w=l.start,void 0===(k=l.end)&&(k=w),"selectionStart"in y?(y.selectionStart=w,y.selectionEnd=Math.min(k,y.value.length)):(k=(w=y.ownerDocument||document)&&w.defaultView||window).getSelection&&(k=k.getSelection(),s=y.textContent.length,E=Math.min(l.start,s),l=void 0===l.end?E:Math.min(l.end,s),!k.extend&&E>l&&(s=l,l=E,E=s),s=mr(y,E),o=mr(y,l),s&&o&&(1!==k.rangeCount||k.anchorNode!==s.node||k.anchorOffset!==s.offset||k.focusNode!==o.node||k.focusOffset!==o.offset)&&((w=w.createRange()).setStart(s.node,s.offset),k.removeAllRanges(),E>l?(k.addRange(w),k.extend(o.node,o.offset)):(w.setEnd(o.node,o.offset),k.addRange(w))))),w=[];for(k=y;k=k.parentNode;)1===k.nodeType&&w.push({element:k,left:k.scrollLeft,top:k.scrollTop});for("function"==typeof y.focus&&y.focus(),y=0;y<w.length;y++)(k=w[y]).element.scrollLeft=k.left,k.element.scrollTop=k.top}Yt=!!Ur,qr=Ur=null,e.current=n,Yl=r;do{try{for(y=e;null!==Yl;){var x=Yl.flags;if(36&x&&bl(y,Yl.alternate,Yl),128&x){w=void 0;var S=Yl.ref;if(null!==S){var _=Yl.stateNode;Yl.tag,w=_,"function"==typeof S?S(w):S.current=w}}Yl=Yl.nextEffect}}catch(C){if(null===Yl)throw Error(i(330));zs(Yl,C),Yl=Yl.nextEffect}}while(null!==Yl);Yl=null,Ba(),Ol=a}else e.current=n;if(Jl)Jl=!1,es=e,ts=t;else for(Yl=r;null!==Yl;)t=Yl.nextEffect,Yl.nextEffect=null,8&Yl.flags&&((x=Yl).sibling=null,x.stateNode=null),Yl=t;if(0===(r=e.pendingLanes)&&(Xl=null),1===r?e===is?os++:(os=0,is=e):os=0,n=n.stateNode,Sa&&"function"==typeof Sa.onCommitFiberRoot)try{Sa.onCommitFiberRoot(xa,n,void 0,64==(64&n.current.flags))}catch(C){}if(gs(e,$a()),Kl)throw Kl=!1,e=Ql,Ql=null,e;return 0!=(8&Ol)||Wa(),null}function Is(){for(;null!==Yl;){var e=Yl.alternate;ds||null===us||(0!=(8&Yl.flags)?et(Yl,us)&&(ds=!0):13===Yl.tag&&Al(e,Yl)&&et(Yl,us)&&(ds=!0));var t=Yl.flags;0!=(256&t)&&gl(e,Yl),0==(512&t)||Jl||(Jl=!0,Va(97,(function(){return Ds(),null}))),Yl=Yl.nextEffect}}function Ds(){if(90!==ts){var e=97<ts?97:ts;return ts=90,Za(e,Bs)}return!1}function Ms(e,t){ns.push(t,e),Jl||(Jl=!0,Va(97,(function(){return Ds(),null})))}function Fs(e,t){rs.push(t,e),Jl||(Jl=!0,Va(97,(function(){return Ds(),null})))}function Bs(){if(null===es)return!1;var e=es;if(es=null,0!=(48&Ol))throw Error(i(331));var t=Ol;Ol|=32;var n=rs;rs=[];for(var r=0;r<n.length;r+=2){var a=n[r],o=n[r+1],l=a.destroy;if(a.destroy=void 0,"function"==typeof l)try{l()}catch(c){if(null===o)throw Error(i(330));zs(o,c)}}for(n=ns,ns=[],r=0;r<n.length;r+=2){a=n[r],o=n[r+1];try{var s=a.create;a.destroy=s()}catch(c){if(null===o)throw Error(i(330));zs(o,c)}}for(s=e.current.firstEffect;null!==s;)e=s.nextEffect,s.nextEffect=null,8&s.flags&&(s.sibling=null,s.stateNode=null),s=e;return Ol=t,Wa(),!0}function js(e,t,n){po(e,t=pl(0,t=cl(n,t),1)),t=ps(),null!==(e=hs(e,1))&&($t(e,1,t),gs(e,t))}function zs(e,t){if(3===e.tag)js(e,e,t);else for(var n=e.return;null!==n;){if(3===n.tag){js(n,e,t);break}if(1===n.tag){var r=n.stateNode;if("function"==typeof n.type.getDerivedStateFromError||"function"==typeof r.componentDidCatch&&(null===Xl||!Xl.has(r))){var a=fl(n,e=cl(t,e),1);if(po(n,a),a=ps(),null!==(n=hs(n,1)))$t(n,1,a),gs(n,a);else if("function"==typeof r.componentDidCatch&&(null===Xl||!Xl.has(r)))try{r.componentDidCatch(t,e)}catch(o){}break}}n=n.return}}function Us(e,t,n){var r=e.pingCache;null!==r&&r.delete(t),t=ps(),e.pingedLanes|=e.suspendedLanes&n,Pl===e&&(Dl&n)===n&&(4===Bl||3===Bl&&(62914560&Dl)===Dl&&500>$a()-Gl?Ss(e,0):$l|=n),gs(e,t)}function qs(e,t){var n=e.stateNode;null!==n&&n.delete(t),0===(t=0)&&(0==(2&(t=e.mode))?t=1:0==(4&t)?t=99===Ha()?1:2:(0===ss&&(ss=zl),0===(t=Ut(62914560&~ss))&&(t=4194304))),n=ps(),null!==(e=hs(e,t))&&($t(e,t,n),gs(e,n))}function $s(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.flags=0,this.lastEffect=this.firstEffect=this.nextEffect=null,this.childLanes=this.lanes=0,this.alternate=null}function Hs(e,t,n,r){return new $s(e,t,n,r)}function Gs(e){return!(!(e=e.prototype)||!e.isReactComponent)}function Zs(e,t){var n=e.alternate;return null===n?((n=Hs(e.tag,t,e.key,e.mode)).elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.nextEffect=null,n.firstEffect=null,n.lastEffect=null),n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=null===t?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Vs(e,t,n,r,a,o){var l=2;if(r=e,"function"==typeof e)Gs(e)&&(l=1);else if("string"==typeof e)l=5;else e:switch(e){case S:return Ws(n.children,a,o,t);case M:l=8,a|=16;break;case _:l=8,a|=1;break;case C:return(e=Hs(12,n,t,8|a)).elementType=C,e.type=C,e.lanes=o,e;case R:return(e=Hs(13,n,t,a)).type=R,e.elementType=R,e.lanes=o,e;case N:return(e=Hs(19,n,t,a)).elementType=N,e.lanes=o,e;case F:return Ys(n,a,o,t);case B:return(e=Hs(24,n,t,a)).elementType=B,e.lanes=o,e;default:if("object"==typeof e&&null!==e)switch(e.$$typeof){case T:l=10;break e;case A:l=9;break e;case L:l=11;break e;case O:l=14;break e;case P:l=16,r=null;break e;case I:l=22;break e}throw Error(i(130,null==e?e:typeof e,""))}return(t=Hs(l,n,t,a)).elementType=e,t.type=r,t.lanes=o,t}function Ws(e,t,n,r){return(e=Hs(7,e,r,t)).lanes=n,e}function Ys(e,t,n,r){return(e=Hs(23,e,r,t)).elementType=F,e.lanes=n,e}function Ks(e,t,n){return(e=Hs(6,e,null,t)).lanes=n,e}function Qs(e,t,n){return(t=Hs(4,null!==e.children?e.children:[],e.key,t)).lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Xs(e,t,n){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.pendingContext=this.context=null,this.hydrate=n,this.callbackNode=null,this.callbackPriority=0,this.eventTimes=qt(0),this.expirationTimes=qt(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=qt(0),this.mutableSourceEagerHydrationData=null}function Js(e,t,n){var r=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null;return{$$typeof:x,key:null==r?null:""+r,children:e,containerInfo:t,implementation:n}}function ec(e,t,n,r){var a=t.current,o=ps(),l=fs(a);e:if(n){t:{if(Ke(n=n._reactInternals)!==n||1!==n.tag)throw Error(i(170));var s=n;do{switch(s.tag){case 3:s=s.stateNode.context;break t;case 1:if(ba(s.type)){s=s.stateNode.__reactInternalMemoizedMergedChildContext;break t}}s=s.return}while(null!==s);throw Error(i(171))}if(1===n.tag){var c=n.type;if(ba(c)){n=wa(n,c,s);break e}}n=s}else n=pa;return null===t.context?t.context=n:t.pendingContext=n,(t=uo(o,l)).payload={element:e},null!==(r=void 0===r?null:r)&&(t.callback=r),po(a,t),ms(a,l,o),l}function tc(e){return(e=e.current).child?(e.child.tag,e.child.stateNode):null}function nc(e,t){if(null!==(e=e.memoizedState)&&null!==e.dehydrated){var n=e.retryLane;e.retryLane=0!==n&&n<t?n:t}}function rc(e,t){nc(e,t),(e=e.alternate)&&nc(e,t)}function ac(e,t,n){var r=null!=n&&null!=n.hydrationOptions&&n.hydrationOptions.mutableSources||null;if(n=new Xs(e,t,null!=n&&!0===n.hydrate),t=Hs(3,null,null,2===t?7:1===t?3:0),n.current=t,t.stateNode=n,so(t),e[ea]=n.current,Or(8===e.nodeType?e.parentNode:e),r)for(e=0;e<r.length;e++){var a=(t=r[e])._getVersion;a=a(t._source),null==n.mutableSourceEagerHydrationData?n.mutableSourceEagerHydrationData=[t,a]:n.mutableSourceEagerHydrationData.push(t,a)}this._internalRoot=n}function oc(e){return!(!e||1!==e.nodeType&&9!==e.nodeType&&11!==e.nodeType&&(8!==e.nodeType||" react-mount-point-unstable "!==e.nodeValue))}function ic(e,t,n,r,a){var o=n._reactRootContainer;if(o){var i=o._internalRoot;if("function"==typeof a){var l=a;a=function(){var e=tc(i);l.call(e)}}ec(t,i,e,a)}else{if(o=n._reactRootContainer=function(e,t){if(t||(t=!(!(t=e?9===e.nodeType?e.documentElement:e.firstChild:null)||1!==t.nodeType||!t.hasAttribute("data-reactroot"))),!t)for(var n;n=e.lastChild;)e.removeChild(n);return new ac(e,0,t?{hydrate:!0}:void 0)}(n,r),i=o._internalRoot,"function"==typeof a){var s=a;a=function(){var e=tc(i);s.call(e)}}ks((function(){ec(t,i,e,a)}))}return tc(i)}function lc(e,t){var n=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null;if(!oc(t))throw Error(i(200));return Js(e,t,null,n)}Wl=function(e,t,n){var r=t.lanes;if(null!==e)if(e.memoizedProps!==t.pendingProps||ma.current)Fi=!0;else{if(0==(n&r)){switch(Fi=!1,t.tag){case 3:Vi(t),Wo();break;case 5:Mo(t);break;case 1:ba(t.type)&&ka(t);break;case 4:Io(t,t.stateNode.containerInfo);break;case 10:r=t.memoizedProps.value;var a=t.type._context;da(Xa,a._currentValue),a._currentValue=r;break;case 13:if(null!==t.memoizedState)return 0!=(n&t.child.childLanes)?Xi(e,t,n):(da(Bo,1&Bo.current),null!==(t=ol(e,t,n))?t.sibling:null);da(Bo,1&Bo.current);break;case 19:if(r=0!=(n&t.childLanes),0!=(64&e.flags)){if(r)return al(e,t,n);t.flags|=64}if(null!==(a=t.memoizedState)&&(a.rendering=null,a.tail=null,a.lastEffect=null),da(Bo,Bo.current),r)break;return null;case 23:case 24:return t.lanes=0,qi(e,t,n)}return ol(e,t,n)}Fi=0!=(16384&e.flags)}else Fi=!1;switch(t.lanes=0,t.tag){case 2:if(r=t.type,null!==e&&(e.alternate=null,t.alternate=null,t.flags|=2),e=t.pendingProps,a=ga(t,fa.current),oo(t,n),a=li(null,t,r,e,a,n),t.flags|=1,"object"==typeof a&&null!==a&&"function"==typeof a.render&&void 0===a.$$typeof){if(t.tag=1,t.memoizedState=null,t.updateQueue=null,ba(r)){var o=!0;ka(t)}else o=!1;t.memoizedState=null!==a.state&&void 0!==a.state?a.state:null,so(t);var l=r.getDerivedStateFromProps;"function"==typeof l&&bo(t,r,l,e),a.updater=vo,t.stateNode=a,a._reactInternals=t,Eo(t,r,e,n),t=Zi(null,t,r,!0,o,n)}else t.tag=0,Bi(null,t,a,n),t=t.child;return t;case 16:a=t.elementType;e:{switch(null!==e&&(e.alternate=null,t.alternate=null,t.flags|=2),e=t.pendingProps,a=(o=a._init)(a._payload),t.type=a,o=t.tag=function(e){if("function"==typeof e)return Gs(e)?1:0;if(null!=e){if((e=e.$$typeof)===L)return 11;if(e===O)return 14}return 2}(a),e=Qa(a,e),o){case 0:t=Hi(null,t,a,e,n);break e;case 1:t=Gi(null,t,a,e,n);break e;case 11:t=ji(null,t,a,e,n);break e;case 14:t=zi(null,t,a,Qa(a.type,e),r,n);break e}throw Error(i(306,a,""))}return t;case 0:return r=t.type,a=t.pendingProps,Hi(e,t,r,a=t.elementType===r?a:Qa(r,a),n);case 1:return r=t.type,a=t.pendingProps,Gi(e,t,r,a=t.elementType===r?a:Qa(r,a),n);case 3:if(Vi(t),r=t.updateQueue,null===e||null===r)throw Error(i(282));if(r=t.pendingProps,a=null!==(a=t.memoizedState)?a.element:null,co(e,t),mo(t,r,null,n),(r=t.memoizedState.element)===a)Wo(),t=ol(e,t,n);else{if((o=(a=t.stateNode).hydrate)&&(Uo=Wr(t.stateNode.containerInfo.firstChild),zo=t,o=qo=!0),o){if(null!=(e=a.mutableSourceEagerHydrationData))for(a=0;a<e.length;a+=2)(o=e[a])._workInProgressVersionPrimary=e[a+1],Yo.push(o);for(n=Ao(t,null,r,n),t.child=n;n;)n.flags=-3&n.flags|1024,n=n.sibling}else Bi(e,t,r,n),Wo();t=t.child}return t;case 5:return Mo(t),null===e&&Go(t),r=t.type,a=t.pendingProps,o=null!==e?e.memoizedProps:null,l=a.children,Hr(r,a)?l=null:null!==o&&Hr(r,o)&&(t.flags|=16),$i(e,t),Bi(e,t,l,n),t.child;case 6:return null===e&&Go(t),null;case 13:return Xi(e,t,n);case 4:return Io(t,t.stateNode.containerInfo),r=t.pendingProps,null===e?t.child=To(t,null,r,n):Bi(e,t,r,n),t.child;case 11:return r=t.type,a=t.pendingProps,ji(e,t,r,a=t.elementType===r?a:Qa(r,a),n);case 7:return Bi(e,t,t.pendingProps,n),t.child;case 8:case 12:return Bi(e,t,t.pendingProps.children,n),t.child;case 10:e:{r=t.type._context,a=t.pendingProps,l=t.memoizedProps,o=a.value;var s=t.type._context;if(da(Xa,s._currentValue),s._currentValue=o,null!==l)if(s=l.value,0===(o=ur(s,o)?0:0|("function"==typeof r._calculateChangedBits?r._calculateChangedBits(s,o):1073741823))){if(l.children===a.children&&!ma.current){t=ol(e,t,n);break e}}else for(null!==(s=t.child)&&(s.return=t);null!==s;){var c=s.dependencies;if(null!==c){l=s.child;for(var u=c.firstContext;null!==u;){if(u.context===r&&0!=(u.observedBits&o)){1===s.tag&&((u=uo(-1,n&-n)).tag=2,po(s,u)),s.lanes|=n,null!==(u=s.alternate)&&(u.lanes|=n),ao(s.return,n),c.lanes|=n;break}u=u.next}}else l=10===s.tag&&s.type===t.type?null:s.child;if(null!==l)l.return=s;else for(l=s;null!==l;){if(l===t){l=null;break}if(null!==(s=l.sibling)){s.return=l.return,l=s;break}l=l.return}s=l}Bi(e,t,a.children,n),t=t.child}return t;case 9:return a=t.type,r=(o=t.pendingProps).children,oo(t,n),r=r(a=io(a,o.unstable_observedBits)),t.flags|=1,Bi(e,t,r,n),t.child;case 14:return o=Qa(a=t.type,t.pendingProps),zi(e,t,a,o=Qa(a.type,o),r,n);case 15:return Ui(e,t,t.type,t.pendingProps,r,n);case 17:return r=t.type,a=t.pendingProps,a=t.elementType===r?a:Qa(r,a),null!==e&&(e.alternate=null,t.alternate=null,t.flags|=2),t.tag=1,ba(r)?(e=!0,ka(t)):e=!1,oo(t,n),wo(t,r,a),Eo(t,r,a,n),Zi(null,t,r,!0,e,n);case 19:return al(e,t,n);case 23:case 24:return qi(e,t,n)}throw Error(i(156,t.tag))},ac.prototype.render=function(e){ec(e,this._internalRoot,null,null)},ac.prototype.unmount=function(){var e=this._internalRoot,t=e.containerInfo;ec(null,e,null,(function(){t[ea]=null}))},tt=function(e){13===e.tag&&(ms(e,4,ps()),rc(e,4))},nt=function(e){13===e.tag&&(ms(e,67108864,ps()),rc(e,67108864))},rt=function(e){if(13===e.tag){var t=ps(),n=fs(e);ms(e,n,t),rc(e,n)}},at=function(e,t){return t()},Te=function(e,t,n){switch(t){case"input":if(ne(e,n),t=n.name,"radio"===n.type&&null!=t){for(n=e;n.parentNode;)n=n.parentNode;for(n=n.querySelectorAll("input[name="+JSON.stringify(""+t)+'][type="radio"]'),t=0;t<n.length;t++){var r=n[t];if(r!==e&&r.form===e.form){var a=oa(r);if(!a)throw Error(i(90));Q(r),ne(r,a)}}}break;case"textarea":ce(e,n);break;case"select":null!=(t=n.value)&&ie(e,!!n.multiple,t,!1)}},Pe=ws,Ie=function(e,t,n,r,a){var o=Ol;Ol|=4;try{return Za(98,e.bind(null,t,n,r,a))}finally{0===(Ol=o)&&(Vl(),Wa())}},De=function(){0==(49&Ol)&&(function(){if(null!==as){var e=as;as=null,e.forEach((function(e){e.expiredLanes|=24&e.pendingLanes,gs(e,$a())}))}Wa()}(),Ds())},Me=function(e,t){var n=Ol;Ol|=2;try{return e(t)}finally{0===(Ol=n)&&(Vl(),Wa())}};var sc={Events:[ra,aa,oa,Ne,Oe,Ds,{current:!1}]},cc={findFiberByHostInstance:na,bundleType:0,version:"17.0.2",rendererPackageName:"react-dom"},uc={bundleType:cc.bundleType,version:cc.version,rendererPackageName:cc.rendererPackageName,rendererConfig:cc.rendererConfig,overrideHookState:null,overrideHookStateDeletePath:null,overrideHookStateRenamePath:null,overrideProps:null,overridePropsDeletePath:null,overridePropsRenamePath:null,setSuspenseHandler:null,scheduleUpdate:null,currentDispatcherRef:k.ReactCurrentDispatcher,findHostInstanceByFiber:function(e){return null===(e=Je(e))?null:e.stateNode},findFiberByHostInstance:cc.findFiberByHostInstance||function(){return null},findHostInstancesForRefresh:null,scheduleRefresh:null,scheduleRoot:null,setRefreshHandler:null,getCurrentFiber:null};if("undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__){var dc=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(!dc.isDisabled&&dc.supportsFiber)try{xa=dc.inject(uc),Sa=dc}catch(ge){}}t.hydrate=function(e,t,n){if(!oc(t))throw Error(i(200));return ic(null,e,t,!0,n)}},3935:(e,t,n)=>{"use strict";!function e(){if("undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE)try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}(),e.exports=n(4448)},9590:e=>{var t="undefined"!=typeof Element,n="function"==typeof Map,r="function"==typeof Set,a="function"==typeof ArrayBuffer&&!!ArrayBuffer.isView;function o(e,i){if(e===i)return!0;if(e&&i&&"object"==typeof e&&"object"==typeof i){if(e.constructor!==i.constructor)return!1;var l,s,c,u;if(Array.isArray(e)){if((l=e.length)!=i.length)return!1;for(s=l;0!=s--;)if(!o(e[s],i[s]))return!1;return!0}if(n&&e instanceof Map&&i instanceof Map){if(e.size!==i.size)return!1;for(u=e.entries();!(s=u.next()).done;)if(!i.has(s.value[0]))return!1;for(u=e.entries();!(s=u.next()).done;)if(!o(s.value[1],i.get(s.value[0])))return!1;return!0}if(r&&e instanceof Set&&i instanceof Set){if(e.size!==i.size)return!1;for(u=e.entries();!(s=u.next()).done;)if(!i.has(s.value[0]))return!1;return!0}if(a&&ArrayBuffer.isView(e)&&ArrayBuffer.isView(i)){if((l=e.length)!=i.length)return!1;for(s=l;0!=s--;)if(e[s]!==i[s])return!1;return!0}if(e.constructor===RegExp)return e.source===i.source&&e.flags===i.flags;if(e.valueOf!==Object.prototype.valueOf)return e.valueOf()===i.valueOf();if(e.toString!==Object.prototype.toString)return e.toString()===i.toString();if((l=(c=Object.keys(e)).length)!==Object.keys(i).length)return!1;for(s=l;0!=s--;)if(!Object.prototype.hasOwnProperty.call(i,c[s]))return!1;if(t&&e instanceof Element)return!1;for(s=l;0!=s--;)if(("_owner"!==c[s]&&"__v"!==c[s]&&"__o"!==c[s]||!e.$$typeof)&&!o(e[c[s]],i[c[s]]))return!1;return!0}return e!=e&&i!=i}e.exports=function(e,t){try{return o(e,t)}catch(n){if((n.message||"").match(/stack|recursion/i))return console.warn("react-fast-compare cannot handle circular refs"),!1;throw n}}},405:(e,t,n)=>{"use strict";n.d(t,{B6:()=>G,ql:()=>J});var r=n(7294),a=n(5697),o=n.n(a),i=n(9590),l=n.n(i),s=n(1143),c=n.n(s),u=n(6774),d=n.n(u);function p(){return p=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},p.apply(this,arguments)}function f(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,m(e,t)}function m(e,t){return m=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},m(e,t)}function h(e,t){if(null==e)return{};var n,r,a={},o=Object.keys(e);for(r=0;r<o.length;r++)t.indexOf(n=o[r])>=0||(a[n]=e[n]);return a}var g={BASE:"base",BODY:"body",HEAD:"head",HTML:"html",LINK:"link",META:"meta",NOSCRIPT:"noscript",SCRIPT:"script",STYLE:"style",TITLE:"title",FRAGMENT:"Symbol(react.fragment)"},b={rel:["amphtml","canonical","alternate"]},v={type:["application/ld+json"]},y={charset:"",name:["robots","description"],property:["og:type","og:title","og:url","og:image","og:image:alt","og:description","twitter:url","twitter:title","twitter:description","twitter:image","twitter:image:alt","twitter:card","twitter:site"]},w=Object.keys(g).map((function(e){return g[e]})),k={accesskey:"accessKey",charset:"charSet",class:"className",contenteditable:"contentEditable",contextmenu:"contextMenu","http-equiv":"httpEquiv",itemprop:"itemProp",tabindex:"tabIndex"},E=Object.keys(k).reduce((function(e,t){return e[k[t]]=t,e}),{}),x=function(e,t){for(var n=e.length-1;n>=0;n-=1){var r=e[n];if(Object.prototype.hasOwnProperty.call(r,t))return r[t]}return null},S=function(e){var t=x(e,g.TITLE),n=x(e,"titleTemplate");if(Array.isArray(t)&&(t=t.join("")),n&&t)return n.replace(/%s/g,(function(){return t}));var r=x(e,"defaultTitle");return t||r||void 0},_=function(e){return x(e,"onChangeClientState")||function(){}},C=function(e,t){return t.filter((function(t){return void 0!==t[e]})).map((function(t){return t[e]})).reduce((function(e,t){return p({},e,t)}),{})},T=function(e,t){return t.filter((function(e){return void 0!==e[g.BASE]})).map((function(e){return e[g.BASE]})).reverse().reduce((function(t,n){if(!t.length)for(var r=Object.keys(n),a=0;a<r.length;a+=1){var o=r[a].toLowerCase();if(-1!==e.indexOf(o)&&n[o])return t.concat(n)}return t}),[])},A=function(e,t,n){var r={};return n.filter((function(t){return!!Array.isArray(t[e])||(void 0!==t[e]&&console&&"function"==typeof console.warn&&console.warn("Helmet: "+e+' should be of type "Array". Instead found type "'+typeof t[e]+'"'),!1)})).map((function(t){return t[e]})).reverse().reduce((function(e,n){var a={};n.filter((function(e){for(var n,o=Object.keys(e),i=0;i<o.length;i+=1){var l=o[i],s=l.toLowerCase();-1===t.indexOf(s)||"rel"===n&&"canonical"===e[n].toLowerCase()||"rel"===s&&"stylesheet"===e[s].toLowerCase()||(n=s),-1===t.indexOf(l)||"innerHTML"!==l&&"cssText"!==l&&"itemprop"!==l||(n=l)}if(!n||!e[n])return!1;var c=e[n].toLowerCase();return r[n]||(r[n]={}),a[n]||(a[n]={}),!r[n][c]&&(a[n][c]=!0,!0)})).reverse().forEach((function(t){return e.push(t)}));for(var o=Object.keys(a),i=0;i<o.length;i+=1){var l=o[i],s=p({},r[l],a[l]);r[l]=s}return e}),[]).reverse()},L=function(e,t){if(Array.isArray(e)&&e.length)for(var n=0;n<e.length;n+=1)if(e[n][t])return!0;return!1},R=function(e){return Array.isArray(e)?e.join(""):e},N=function(e,t){return Array.isArray(e)?e.reduce((function(e,n){return function(e,t){for(var n=Object.keys(e),r=0;r<n.length;r+=1)if(t[n[r]]&&t[n[r]].includes(e[n[r]]))return!0;return!1}(n,t)?e.priority.push(n):e.default.push(n),e}),{priority:[],default:[]}):{default:e}},O=function(e,t){var n;return p({},e,((n={})[t]=void 0,n))},P=[g.NOSCRIPT,g.SCRIPT,g.STYLE],I=function(e,t){return void 0===t&&(t=!0),!1===t?String(e):String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")},D=function(e){return Object.keys(e).reduce((function(t,n){var r=void 0!==e[n]?n+'="'+e[n]+'"':""+n;return t?t+" "+r:r}),"")},M=function(e,t){return void 0===t&&(t={}),Object.keys(e).reduce((function(t,n){return t[k[n]||n]=e[n],t}),t)},F=function(e,t){return t.map((function(t,n){var a,o=((a={key:n})["data-rh"]=!0,a);return Object.keys(t).forEach((function(e){var n=k[e]||e;"innerHTML"===n||"cssText"===n?o.dangerouslySetInnerHTML={__html:t.innerHTML||t.cssText}:o[n]=t[e]})),r.createElement(e,o)}))},B=function(e,t,n){switch(e){case g.TITLE:return{toComponent:function(){return n=t.titleAttributes,(a={key:e=t.title})["data-rh"]=!0,o=M(n,a),[r.createElement(g.TITLE,o,e)];var e,n,a,o},toString:function(){return function(e,t,n,r){var a=D(n),o=R(t);return a?"<"+e+' data-rh="true" '+a+">"+I(o,r)+"</"+e+">":"<"+e+' data-rh="true">'+I(o,r)+"</"+e+">"}(e,t.title,t.titleAttributes,n)}};case"bodyAttributes":case"htmlAttributes":return{toComponent:function(){return M(t)},toString:function(){return D(t)}};default:return{toComponent:function(){return F(e,t)},toString:function(){return function(e,t,n){return t.reduce((function(t,r){var a=Object.keys(r).filter((function(e){return!("innerHTML"===e||"cssText"===e)})).reduce((function(e,t){var a=void 0===r[t]?t:t+'="'+I(r[t],n)+'"';return e?e+" "+a:a}),""),o=r.innerHTML||r.cssText||"",i=-1===P.indexOf(e);return t+"<"+e+' data-rh="true" '+a+(i?"/>":">"+o+"</"+e+">")}),"")}(e,t,n)}}}},j=function(e){var t=e.baseTag,n=e.bodyAttributes,r=e.encode,a=e.htmlAttributes,o=e.noscriptTags,i=e.styleTags,l=e.title,s=void 0===l?"":l,c=e.titleAttributes,u=e.linkTags,d=e.metaTags,p=e.scriptTags,f={toComponent:function(){},toString:function(){return""}};if(e.prioritizeSeoTags){var m=function(e){var t=e.linkTags,n=e.scriptTags,r=e.encode,a=N(e.metaTags,y),o=N(t,b),i=N(n,v);return{priorityMethods:{toComponent:function(){return[].concat(F(g.META,a.priority),F(g.LINK,o.priority),F(g.SCRIPT,i.priority))},toString:function(){return B(g.META,a.priority,r)+" "+B(g.LINK,o.priority,r)+" "+B(g.SCRIPT,i.priority,r)}},metaTags:a.default,linkTags:o.default,scriptTags:i.default}}(e);f=m.priorityMethods,u=m.linkTags,d=m.metaTags,p=m.scriptTags}return{priority:f,base:B(g.BASE,t,r),bodyAttributes:B("bodyAttributes",n,r),htmlAttributes:B("htmlAttributes",a,r),link:B(g.LINK,u,r),meta:B(g.META,d,r),noscript:B(g.NOSCRIPT,o,r),script:B(g.SCRIPT,p,r),style:B(g.STYLE,i,r),title:B(g.TITLE,{title:s,titleAttributes:c},r)}},z=[],U=function(e,t){var n=this;void 0===t&&(t="undefined"!=typeof document),this.instances=[],this.value={setHelmet:function(e){n.context.helmet=e},helmetInstances:{get:function(){return n.canUseDOM?z:n.instances},add:function(e){(n.canUseDOM?z:n.instances).push(e)},remove:function(e){var t=(n.canUseDOM?z:n.instances).indexOf(e);(n.canUseDOM?z:n.instances).splice(t,1)}}},this.context=e,this.canUseDOM=t,t||(e.helmet=j({baseTag:[],bodyAttributes:{},encodeSpecialCharacters:!0,htmlAttributes:{},linkTags:[],metaTags:[],noscriptTags:[],scriptTags:[],styleTags:[],title:"",titleAttributes:{}}))},q=r.createContext({}),$=o().shape({setHelmet:o().func,helmetInstances:o().shape({get:o().func,add:o().func,remove:o().func})}),H="undefined"!=typeof document,G=function(e){function t(n){var r;return(r=e.call(this,n)||this).helmetData=new U(r.props.context,t.canUseDOM),r}return f(t,e),t.prototype.render=function(){return r.createElement(q.Provider,{value:this.helmetData.value},this.props.children)},t}(r.Component);G.canUseDOM=H,G.propTypes={context:o().shape({helmet:o().shape()}),children:o().node.isRequired},G.defaultProps={context:{}},G.displayName="HelmetProvider";var Z=function(e,t){var n,r=document.head||document.querySelector(g.HEAD),a=r.querySelectorAll(e+"[data-rh]"),o=[].slice.call(a),i=[];return t&&t.length&&t.forEach((function(t){var r=document.createElement(e);for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&("innerHTML"===a?r.innerHTML=t.innerHTML:"cssText"===a?r.styleSheet?r.styleSheet.cssText=t.cssText:r.appendChild(document.createTextNode(t.cssText)):r.setAttribute(a,void 0===t[a]?"":t[a]));r.setAttribute("data-rh","true"),o.some((function(e,t){return n=t,r.isEqualNode(e)}))?o.splice(n,1):i.push(r)})),o.forEach((function(e){return e.parentNode.removeChild(e)})),i.forEach((function(e){return r.appendChild(e)})),{oldTags:o,newTags:i}},V=function(e,t){var n=document.getElementsByTagName(e)[0];if(n){for(var r=n.getAttribute("data-rh"),a=r?r.split(","):[],o=[].concat(a),i=Object.keys(t),l=0;l<i.length;l+=1){var s=i[l],c=t[s]||"";n.getAttribute(s)!==c&&n.setAttribute(s,c),-1===a.indexOf(s)&&a.push(s);var u=o.indexOf(s);-1!==u&&o.splice(u,1)}for(var d=o.length-1;d>=0;d-=1)n.removeAttribute(o[d]);a.length===o.length?n.removeAttribute("data-rh"):n.getAttribute("data-rh")!==i.join(",")&&n.setAttribute("data-rh",i.join(","))}},W=function(e,t){var n=e.baseTag,r=e.htmlAttributes,a=e.linkTags,o=e.metaTags,i=e.noscriptTags,l=e.onChangeClientState,s=e.scriptTags,c=e.styleTags,u=e.title,d=e.titleAttributes;V(g.BODY,e.bodyAttributes),V(g.HTML,r),function(e,t){void 0!==e&&document.title!==e&&(document.title=R(e)),V(g.TITLE,t)}(u,d);var p={baseTag:Z(g.BASE,n),linkTags:Z(g.LINK,a),metaTags:Z(g.META,o),noscriptTags:Z(g.NOSCRIPT,i),scriptTags:Z(g.SCRIPT,s),styleTags:Z(g.STYLE,c)},f={},m={};Object.keys(p).forEach((function(e){var t=p[e],n=t.newTags,r=t.oldTags;n.length&&(f[e]=n),r.length&&(m[e]=p[e].oldTags)})),t&&t(),l(e,f,m)},Y=null,K=function(e){function t(){for(var t,n=arguments.length,r=new Array(n),a=0;a<n;a++)r[a]=arguments[a];return(t=e.call.apply(e,[this].concat(r))||this).rendered=!1,t}f(t,e);var n=t.prototype;return n.shouldComponentUpdate=function(e){return!d()(e,this.props)},n.componentDidUpdate=function(){this.emitChange()},n.componentWillUnmount=function(){this.props.context.helmetInstances.remove(this),this.emitChange()},n.emitChange=function(){var e,t,n=this.props.context,r=n.setHelmet,a=null,o=(e=n.helmetInstances.get().map((function(e){var t=p({},e.props);return delete t.context,t})),{baseTag:T(["href"],e),bodyAttributes:C("bodyAttributes",e),defer:x(e,"defer"),encode:x(e,"encodeSpecialCharacters"),htmlAttributes:C("htmlAttributes",e),linkTags:A(g.LINK,["rel","href"],e),metaTags:A(g.META,["name","charset","http-equiv","property","itemprop"],e),noscriptTags:A(g.NOSCRIPT,["innerHTML"],e),onChangeClientState:_(e),scriptTags:A(g.SCRIPT,["src","innerHTML"],e),styleTags:A(g.STYLE,["cssText"],e),title:S(e),titleAttributes:C("titleAttributes",e),prioritizeSeoTags:L(e,"prioritizeSeoTags")});G.canUseDOM?(t=o,Y&&cancelAnimationFrame(Y),t.defer?Y=requestAnimationFrame((function(){W(t,(function(){Y=null}))})):(W(t),Y=null)):j&&(a=j(o)),r(a)},n.init=function(){this.rendered||(this.rendered=!0,this.props.context.helmetInstances.add(this),this.emitChange())},n.render=function(){return this.init(),null},t}(r.Component);K.propTypes={context:$.isRequired},K.displayName="HelmetDispatcher";var Q=["children"],X=["children"],J=function(e){function t(){return e.apply(this,arguments)||this}f(t,e);var n=t.prototype;return n.shouldComponentUpdate=function(e){return!l()(O(this.props,"helmetData"),O(e,"helmetData"))},n.mapNestedChildrenToProps=function(e,t){if(!t)return null;switch(e.type){case g.SCRIPT:case g.NOSCRIPT:return{innerHTML:t};case g.STYLE:return{cssText:t};default:throw new Error("<"+e.type+" /> elements are self-closing and can not contain children. Refer to our API for more information.")}},n.flattenArrayTypeChildren=function(e){var t,n=e.child,r=e.arrayTypeChildren;return p({},r,((t={})[n.type]=[].concat(r[n.type]||[],[p({},e.newChildProps,this.mapNestedChildrenToProps(n,e.nestedChildren))]),t))},n.mapObjectTypeChildren=function(e){var t,n,r=e.child,a=e.newProps,o=e.newChildProps,i=e.nestedChildren;switch(r.type){case g.TITLE:return p({},a,((t={})[r.type]=i,t.titleAttributes=p({},o),t));case g.BODY:return p({},a,{bodyAttributes:p({},o)});case g.HTML:return p({},a,{htmlAttributes:p({},o)});default:return p({},a,((n={})[r.type]=p({},o),n))}},n.mapArrayTypeChildrenToProps=function(e,t){var n=p({},t);return Object.keys(e).forEach((function(t){var r;n=p({},n,((r={})[t]=e[t],r))})),n},n.warnOnInvalidChildren=function(e,t){return c()(w.some((function(t){return e.type===t})),"function"==typeof e.type?"You may be attempting to nest <Helmet> components within each other, which is not allowed. Refer to our API for more information.":"Only elements types "+w.join(", ")+" are allowed. Helmet does not support rendering <"+e.type+"> elements. Refer to our API for more information."),c()(!t||"string"==typeof t||Array.isArray(t)&&!t.some((function(e){return"string"!=typeof e})),"Helmet expects a string as a child of <"+e.type+">. Did you forget to wrap your children in braces? ( <"+e.type+">{``}</"+e.type+"> ) Refer to our API for more information."),!0},n.mapChildrenToProps=function(e,t){var n=this,a={};return r.Children.forEach(e,(function(e){if(e&&e.props){var r=e.props,o=r.children,i=h(r,Q),l=Object.keys(i).reduce((function(e,t){return e[E[t]||t]=i[t],e}),{}),s=e.type;switch("symbol"==typeof s?s=s.toString():n.warnOnInvalidChildren(e,o),s){case g.FRAGMENT:t=n.mapChildrenToProps(o,t);break;case g.LINK:case g.META:case g.NOSCRIPT:case g.SCRIPT:case g.STYLE:a=n.flattenArrayTypeChildren({child:e,arrayTypeChildren:a,newChildProps:l,nestedChildren:o});break;default:t=n.mapObjectTypeChildren({child:e,newProps:t,newChildProps:l,nestedChildren:o})}}})),this.mapArrayTypeChildrenToProps(a,t)},n.render=function(){var e=this.props,t=e.children,n=h(e,X),a=p({},n),o=n.helmetData;return t&&(a=this.mapChildrenToProps(t,a)),!o||o instanceof U||(o=new U(o.context,o.instances)),o?r.createElement(K,p({},a,{context:o.value,helmetData:void 0})):r.createElement(q.Consumer,null,(function(e){return r.createElement(K,p({},a,{context:e}))}))},t}(r.Component);J.propTypes={base:o().object,bodyAttributes:o().object,children:o().oneOfType([o().arrayOf(o().node),o().node]),defaultTitle:o().string,defer:o().bool,encodeSpecialCharacters:o().bool,htmlAttributes:o().object,link:o().arrayOf(o().object),meta:o().arrayOf(o().object),noscript:o().arrayOf(o().object),onChangeClientState:o().func,script:o().arrayOf(o().object),style:o().arrayOf(o().object),title:o().string,titleAttributes:o().object,titleTemplate:o().string,prioritizeSeoTags:o().bool,helmetData:o().object},J.defaultProps={defer:!0,encodeSpecialCharacters:!0,prioritizeSeoTags:!1},J.displayName="Helmet"},9921:(e,t)=>{"use strict";var n="function"==typeof Symbol&&Symbol.for,r=n?Symbol.for("react.element"):60103,a=n?Symbol.for("react.portal"):60106,o=n?Symbol.for("react.fragment"):60107,i=n?Symbol.for("react.strict_mode"):60108,l=n?Symbol.for("react.profiler"):60114,s=n?Symbol.for("react.provider"):60109,c=n?Symbol.for("react.context"):60110,u=n?Symbol.for("react.async_mode"):60111,d=n?Symbol.for("react.concurrent_mode"):60111,p=n?Symbol.for("react.forward_ref"):60112,f=n?Symbol.for("react.suspense"):60113,m=n?Symbol.for("react.suspense_list"):60120,h=n?Symbol.for("react.memo"):60115,g=n?Symbol.for("react.lazy"):60116,b=n?Symbol.for("react.block"):60121,v=n?Symbol.for("react.fundamental"):60117,y=n?Symbol.for("react.responder"):60118,w=n?Symbol.for("react.scope"):60119;function k(e){if("object"==typeof e&&null!==e){var t=e.$$typeof;switch(t){case r:switch(e=e.type){case u:case d:case o:case l:case i:case f:return e;default:switch(e=e&&e.$$typeof){case c:case p:case g:case h:case s:return e;default:return t}}case a:return t}}}function E(e){return k(e)===d}t.AsyncMode=u,t.ConcurrentMode=d,t.ContextConsumer=c,t.ContextProvider=s,t.Element=r,t.ForwardRef=p,t.Fragment=o,t.Lazy=g,t.Memo=h,t.Portal=a,t.Profiler=l,t.StrictMode=i,t.Suspense=f,t.isAsyncMode=function(e){return E(e)||k(e)===u},t.isConcurrentMode=E,t.isContextConsumer=function(e){return k(e)===c},t.isContextProvider=function(e){return k(e)===s},t.isElement=function(e){return"object"==typeof e&&null!==e&&e.$$typeof===r},t.isForwardRef=function(e){return k(e)===p},t.isFragment=function(e){return k(e)===o},t.isLazy=function(e){return k(e)===g},t.isMemo=function(e){return k(e)===h},t.isPortal=function(e){return k(e)===a},t.isProfiler=function(e){return k(e)===l},t.isStrictMode=function(e){return k(e)===i},t.isSuspense=function(e){return k(e)===f},t.isValidElementType=function(e){return"string"==typeof e||"function"==typeof e||e===o||e===d||e===l||e===i||e===f||e===m||"object"==typeof e&&null!==e&&(e.$$typeof===g||e.$$typeof===h||e.$$typeof===s||e.$$typeof===c||e.$$typeof===p||e.$$typeof===v||e.$$typeof===y||e.$$typeof===w||e.$$typeof===b)},t.typeOf=k},9864:(e,t,n)=>{"use strict";e.exports=n(9921)},8356:(e,t,n)=>{"use strict";function r(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,e.__proto__=t}function a(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(){return i=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},i.apply(this,arguments)}var l=n(7294),s=n(5697),c=[],u=[];function d(e){var t=e(),n={loading:!0,loaded:null,error:null};return n.promise=t.then((function(e){return n.loading=!1,n.loaded=e,e})).catch((function(e){throw n.loading=!1,n.error=e,e})),n}function p(e){var t={loading:!1,loaded:{},error:null},n=[];try{Object.keys(e).forEach((function(r){var a=d(e[r]);a.loading?t.loading=!0:(t.loaded[r]=a.loaded,t.error=a.error),n.push(a.promise),a.promise.then((function(e){t.loaded[r]=e})).catch((function(e){t.error=e}))}))}catch(r){t.error=r}return t.promise=Promise.all(n).then((function(e){return t.loading=!1,e})).catch((function(e){throw t.loading=!1,e})),t}function f(e,t){return l.createElement((n=e)&&n.__esModule?n.default:n,t);var n}function m(e,t){var d,p;if(!t.loading)throw new Error("react-loadable requires a `loading` component");var m=i({loader:null,loading:null,delay:200,timeout:null,render:f,webpack:null,modules:null},t),h=null;function g(){return h||(h=e(m.loader)),h.promise}return c.push(g),"function"==typeof m.webpack&&u.push((function(){if((0,m.webpack)().every((function(e){return void 0!==e&&void 0!==n.m[e]})))return g()})),p=d=function(t){function n(n){var r;return o(a(a(r=t.call(this,n)||this)),"retry",(function(){r.setState({error:null,loading:!0,timedOut:!1}),h=e(m.loader),r._loadModule()})),g(),r.state={error:h.error,pastDelay:!1,timedOut:!1,loading:h.loading,loaded:h.loaded},r}r(n,t),n.preload=function(){return g()};var i=n.prototype;return i.UNSAFE_componentWillMount=function(){this._loadModule()},i.componentDidMount=function(){this._mounted=!0},i._loadModule=function(){var e=this;if(this.context.loadable&&Array.isArray(m.modules)&&m.modules.forEach((function(t){e.context.loadable.report(t)})),h.loading){var t=function(t){e._mounted&&e.setState(t)};"number"==typeof m.delay&&(0===m.delay?this.setState({pastDelay:!0}):this._delay=setTimeout((function(){t({pastDelay:!0})}),m.delay)),"number"==typeof m.timeout&&(this._timeout=setTimeout((function(){t({timedOut:!0})}),m.timeout));var n=function(){t({error:h.error,loaded:h.loaded,loading:h.loading}),e._clearTimeouts()};h.promise.then((function(){return n(),null})).catch((function(e){return n(),null}))}},i.componentWillUnmount=function(){this._mounted=!1,this._clearTimeouts()},i._clearTimeouts=function(){clearTimeout(this._delay),clearTimeout(this._timeout)},i.render=function(){return this.state.loading||this.state.error?l.createElement(m.loading,{isLoading:this.state.loading,pastDelay:this.state.pastDelay,timedOut:this.state.timedOut,error:this.state.error,retry:this.retry}):this.state.loaded?m.render(this.state.loaded,this.props):null},n}(l.Component),o(d,"contextTypes",{loadable:s.shape({report:s.func.isRequired})}),p}function h(e){return m(d,e)}h.Map=function(e){if("function"!=typeof e.render)throw new Error("LoadableMap requires a `render(loaded, props)` function");return m(p,e)};var g=function(e){function t(){return e.apply(this,arguments)||this}r(t,e);var n=t.prototype;return n.getChildContext=function(){return{loadable:{report:this.props.report}}},n.render=function(){return l.Children.only(this.props.children)},t}(l.Component);function b(e){for(var t=[];e.length;){var n=e.pop();t.push(n())}return Promise.all(t).then((function(){if(e.length)return b(e)}))}o(g,"propTypes",{report:s.func.isRequired}),o(g,"childContextTypes",{loadable:s.shape({report:s.func.isRequired}).isRequired}),h.Capture=g,h.preloadAll=function(){return new Promise((function(e,t){b(c).then(e,t)}))},h.preloadReady=function(){return new Promise((function(e,t){b(u).then(e,e)}))},e.exports=h},8790:(e,t,n)=>{"use strict";n.d(t,{H:()=>l,f:()=>i});var r=n(6550),a=n(7462),o=n(7294);function i(e,t,n){return void 0===n&&(n=[]),e.some((function(e){var a=e.path?(0,r.LX)(t,e):n.length?n[n.length-1].match:r.F0.computeRootMatch(t);return a&&(n.push({route:e,match:a}),e.routes&&i(e.routes,t,n)),a})),n}function l(e,t,n){return void 0===t&&(t={}),void 0===n&&(n={}),e?o.createElement(r.rs,n,e.map((function(e,n){return o.createElement(r.AW,{key:e.key||n,path:e.path,exact:e.exact,strict:e.strict,render:function(n){return e.render?e.render((0,a.Z)({},n,{},t,{route:e})):o.createElement(e.component,(0,a.Z)({},n,t,{route:e}))}})}))):null}},3727:(e,t,n)=>{"use strict";n.d(t,{OL:()=>y,VK:()=>u,rU:()=>g});var r=n(6550),a=n(5068),o=n(7294),i=n(9318),l=n(7462),s=n(3366),c=n(8776),u=function(e){function t(){for(var t,n=arguments.length,r=new Array(n),a=0;a<n;a++)r[a]=arguments[a];return(t=e.call.apply(e,[this].concat(r))||this).history=(0,i.lX)(t.props),t}return(0,a.Z)(t,e),t.prototype.render=function(){return o.createElement(r.F0,{history:this.history,children:this.props.children})},t}(o.Component);o.Component;var d=function(e,t){return"function"==typeof e?e(t):e},p=function(e,t){return"string"==typeof e?(0,i.ob)(e,null,null,t):e},f=function(e){return e},m=o.forwardRef;void 0===m&&(m=f);var h=m((function(e,t){var n=e.innerRef,r=e.navigate,a=e.onClick,i=(0,s.Z)(e,["innerRef","navigate","onClick"]),c=i.target,u=(0,l.Z)({},i,{onClick:function(e){try{a&&a(e)}catch(t){throw e.preventDefault(),t}e.defaultPrevented||0!==e.button||c&&"_self"!==c||function(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}(e)||(e.preventDefault(),r())}});return u.ref=f!==m&&t||n,o.createElement("a",u)}));var g=m((function(e,t){var n=e.component,a=void 0===n?h:n,u=e.replace,g=e.to,b=e.innerRef,v=(0,s.Z)(e,["component","replace","to","innerRef"]);return o.createElement(r.s6.Consumer,null,(function(e){e||(0,c.Z)(!1);var n=e.history,r=p(d(g,e.location),e.location),s=r?n.createHref(r):"",h=(0,l.Z)({},v,{href:s,navigate:function(){var t=d(g,e.location),r=(0,i.Ep)(e.location)===(0,i.Ep)(p(t));(u||r?n.replace:n.push)(t)}});return f!==m?h.ref=t||b:h.innerRef=b,o.createElement(a,h)}))})),b=function(e){return e},v=o.forwardRef;void 0===v&&(v=b);var y=v((function(e,t){var n=e["aria-current"],a=void 0===n?"page":n,i=e.activeClassName,u=void 0===i?"active":i,f=e.activeStyle,m=e.className,h=e.exact,y=e.isActive,w=e.location,k=e.sensitive,E=e.strict,x=e.style,S=e.to,_=e.innerRef,C=(0,s.Z)(e,["aria-current","activeClassName","activeStyle","className","exact","isActive","location","sensitive","strict","style","to","innerRef"]);return o.createElement(r.s6.Consumer,null,(function(e){e||(0,c.Z)(!1);var n=w||e.location,i=p(d(S,n),n),s=i.pathname,T=s&&s.replace(/([.+*?=^!:${}()[\]|/\\])/g,"\\$1"),A=T?(0,r.LX)(n.pathname,{path:T,exact:h,sensitive:k,strict:E}):null,L=!!(y?y(A,n):A),R="function"==typeof m?m(L):m,N="function"==typeof x?x(L):x;L&&(R=function(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return t.filter((function(e){return e})).join(" ")}(R,u),N=(0,l.Z)({},N,f));var O=(0,l.Z)({"aria-current":L&&a||null,className:R,style:N,to:i},C);return b!==v?O.ref=t||_:O.innerRef=_,o.createElement(g,O)}))}))},6550:(e,t,n)=>{"use strict";n.d(t,{AW:()=>S,F0:()=>w,LX:()=>x,TH:()=>P,k6:()=>O,rs:()=>R,s6:()=>y});var r=n(5068),a=n(7294),o=n(5697),i=n.n(o),l=n(9318),s=n(8776),c=n(7462),u=n(9658),d=n.n(u),p=(n(9864),n(3366)),f=(n(8679),1073741823),m="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:void 0!==n.g?n.g:{};function h(e){var t=[];return{on:function(e){t.push(e)},off:function(e){t=t.filter((function(t){return t!==e}))},get:function(){return e},set:function(n,r){e=n,t.forEach((function(t){return t(e,r)}))}}}var g=a.createContext||function(e,t){var n,o,l="__create-react-context-"+function(){var e="__global_unique_id__";return m[e]=(m[e]||0)+1}()+"__",s=function(e){function n(){for(var t,n=arguments.length,r=new Array(n),a=0;a<n;a++)r[a]=arguments[a];return(t=e.call.apply(e,[this].concat(r))||this).emitter=h(t.props.value),t}(0,r.Z)(n,e);var a=n.prototype;return a.getChildContext=function(){var e;return(e={})[l]=this.emitter,e},a.componentWillReceiveProps=function(e){if(this.props.value!==e.value){var n,r=this.props.value,a=e.value;((o=r)===(i=a)?0!==o||1/o==1/i:o!=o&&i!=i)?n=0:(n="function"==typeof t?t(r,a):f,0!==(n|=0)&&this.emitter.set(e.value,n))}var o,i},a.render=function(){return this.props.children},n}(a.Component);s.childContextTypes=((n={})[l]=i().object.isRequired,n);var c=function(t){function n(){for(var e,n=arguments.length,r=new Array(n),a=0;a<n;a++)r[a]=arguments[a];return(e=t.call.apply(t,[this].concat(r))||this).observedBits=void 0,e.state={value:e.getValue()},e.onUpdate=function(t,n){0!=((0|e.observedBits)&n)&&e.setState({value:e.getValue()})},e}(0,r.Z)(n,t);var a=n.prototype;return a.componentWillReceiveProps=function(e){var t=e.observedBits;this.observedBits=null==t?f:t},a.componentDidMount=function(){this.context[l]&&this.context[l].on(this.onUpdate);var e=this.props.observedBits;this.observedBits=null==e?f:e},a.componentWillUnmount=function(){this.context[l]&&this.context[l].off(this.onUpdate)},a.getValue=function(){return this.context[l]?this.context[l].get():e},a.render=function(){return(e=this.props.children,Array.isArray(e)?e[0]:e)(this.state.value);var e},n}(a.Component);return c.contextTypes=((o={})[l]=i().object,o),{Provider:s,Consumer:c}},b=function(e){var t=g();return t.displayName=e,t},v=b("Router-History"),y=b("Router"),w=function(e){function t(t){var n;return(n=e.call(this,t)||this).state={location:t.history.location},n._isMounted=!1,n._pendingLocation=null,t.staticContext||(n.unlisten=t.history.listen((function(e){n._pendingLocation=e}))),n}(0,r.Z)(t,e),t.computeRootMatch=function(e){return{path:"/",url:"/",params:{},isExact:"/"===e}};var n=t.prototype;return n.componentDidMount=function(){var e=this;this._isMounted=!0,this.unlisten&&this.unlisten(),this.props.staticContext||(this.unlisten=this.props.history.listen((function(t){e._isMounted&&e.setState({location:t})}))),this._pendingLocation&&this.setState({location:this._pendingLocation})},n.componentWillUnmount=function(){this.unlisten&&(this.unlisten(),this._isMounted=!1,this._pendingLocation=null)},n.render=function(){return a.createElement(y.Provider,{value:{history:this.props.history,location:this.state.location,match:t.computeRootMatch(this.state.location.pathname),staticContext:this.props.staticContext}},a.createElement(v.Provider,{children:this.props.children||null,value:this.props.history}))},t}(a.Component);a.Component;a.Component;var k={},E=0;function x(e,t){void 0===t&&(t={}),("string"==typeof t||Array.isArray(t))&&(t={path:t});var n=t,r=n.path,a=n.exact,o=void 0!==a&&a,i=n.strict,l=void 0!==i&&i,s=n.sensitive,c=void 0!==s&&s;return[].concat(r).reduce((function(t,n){if(!n&&""!==n)return null;if(t)return t;var r=function(e,t){var n=""+t.end+t.strict+t.sensitive,r=k[n]||(k[n]={});if(r[e])return r[e];var a=[],o={regexp:d()(e,a,t),keys:a};return E<1e4&&(r[e]=o,E++),o}(n,{end:o,strict:l,sensitive:c}),a=r.regexp,i=r.keys,s=a.exec(e);if(!s)return null;var u=s[0],p=s.slice(1),f=e===u;return o&&!f?null:{path:n,url:"/"===n&&""===u?"/":u,isExact:f,params:i.reduce((function(e,t,n){return e[t.name]=p[n],e}),{})}}),null)}var S=function(e){function t(){return e.apply(this,arguments)||this}return(0,r.Z)(t,e),t.prototype.render=function(){var e=this;return a.createElement(y.Consumer,null,(function(t){t||(0,s.Z)(!1);var n=e.props.location||t.location,r=e.props.computedMatch?e.props.computedMatch:e.props.path?x(n.pathname,e.props):t.match,o=(0,c.Z)({},t,{location:n,match:r}),i=e.props,l=i.children,u=i.component,d=i.render;return Array.isArray(l)&&function(e){return 0===a.Children.count(e)}(l)&&(l=null),a.createElement(y.Provider,{value:o},o.match?l?"function"==typeof l?l(o):l:u?a.createElement(u,o):d?d(o):null:"function"==typeof l?l(o):null)}))},t}(a.Component);function _(e){return"/"===e.charAt(0)?e:"/"+e}function C(e,t){if(!e)return t;var n=_(e);return 0!==t.pathname.indexOf(n)?t:(0,c.Z)({},t,{pathname:t.pathname.substr(n.length)})}function T(e){return"string"==typeof e?e:(0,l.Ep)(e)}function A(e){return function(){(0,s.Z)(!1)}}function L(){}a.Component;var R=function(e){function t(){return e.apply(this,arguments)||this}return(0,r.Z)(t,e),t.prototype.render=function(){var e=this;return a.createElement(y.Consumer,null,(function(t){t||(0,s.Z)(!1);var n,r,o=e.props.location||t.location;return a.Children.forEach(e.props.children,(function(e){if(null==r&&a.isValidElement(e)){n=e;var i=e.props.path||e.props.from;r=i?x(o.pathname,(0,c.Z)({},e.props,{path:i})):t.match}})),r?a.cloneElement(n,{location:o,computedMatch:r}):null}))},t}(a.Component);var N=a.useContext;function O(){return N(v)}function P(){return N(y).location}},9658:(e,t,n)=>{var r=n(5826);e.exports=f,e.exports.parse=o,e.exports.compile=function(e,t){return l(o(e,t),t)},e.exports.tokensToFunction=l,e.exports.tokensToRegExp=p;var a=new RegExp(["(\\\\.)","([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))"].join("|"),"g");function o(e,t){for(var n,r=[],o=0,i=0,l="",u=t&&t.delimiter||"/";null!=(n=a.exec(e));){var d=n[0],p=n[1],f=n.index;if(l+=e.slice(i,f),i=f+d.length,p)l+=p[1];else{var m=e[i],h=n[2],g=n[3],b=n[4],v=n[5],y=n[6],w=n[7];l&&(r.push(l),l="");var k=null!=h&&null!=m&&m!==h,E="+"===y||"*"===y,x="?"===y||"*"===y,S=n[2]||u,_=b||v;r.push({name:g||o++,prefix:h||"",delimiter:S,optional:x,repeat:E,partial:k,asterisk:!!w,pattern:_?c(_):w?".*":"[^"+s(S)+"]+?"})}}return i<e.length&&(l+=e.substr(i)),l&&r.push(l),r}function i(e){return encodeURI(e).replace(/[\/?#]/g,(function(e){return"%"+e.charCodeAt(0).toString(16).toUpperCase()}))}function l(e,t){for(var n=new Array(e.length),a=0;a<e.length;a++)"object"==typeof e[a]&&(n[a]=new RegExp("^(?:"+e[a].pattern+")$",d(t)));return function(t,a){for(var o="",l=t||{},s=(a||{}).pretty?i:encodeURIComponent,c=0;c<e.length;c++){var u=e[c];if("string"!=typeof u){var d,p=l[u.name];if(null==p){if(u.optional){u.partial&&(o+=u.prefix);continue}throw new TypeError('Expected "'+u.name+'" to be defined')}if(r(p)){if(!u.repeat)throw new TypeError('Expected "'+u.name+'" to not repeat, but received `'+JSON.stringify(p)+"`");if(0===p.length){if(u.optional)continue;throw new TypeError('Expected "'+u.name+'" to not be empty')}for(var f=0;f<p.length;f++){if(d=s(p[f]),!n[c].test(d))throw new TypeError('Expected all "'+u.name+'" to match "'+u.pattern+'", but received `'+JSON.stringify(d)+"`");o+=(0===f?u.prefix:u.delimiter)+d}}else{if(d=u.asterisk?encodeURI(p).replace(/[?#]/g,(function(e){return"%"+e.charCodeAt(0).toString(16).toUpperCase()})):s(p),!n[c].test(d))throw new TypeError('Expected "'+u.name+'" to match "'+u.pattern+'", but received "'+d+'"');o+=u.prefix+d}}else o+=u}return o}}function s(e){return e.replace(/([.+*?=^!:${}()[\]|\/\\])/g,"\\$1")}function c(e){return e.replace(/([=!:$\/()])/g,"\\$1")}function u(e,t){return e.keys=t,e}function d(e){return e&&e.sensitive?"":"i"}function p(e,t,n){r(t)||(n=t||n,t=[]);for(var a=(n=n||{}).strict,o=!1!==n.end,i="",l=0;l<e.length;l++){var c=e[l];if("string"==typeof c)i+=s(c);else{var p=s(c.prefix),f="(?:"+c.pattern+")";t.push(c),c.repeat&&(f+="(?:"+p+f+")*"),i+=f=c.optional?c.partial?p+"("+f+")?":"(?:"+p+"("+f+"))?":p+"("+f+")"}}var m=s(n.delimiter||"/"),h=i.slice(-m.length)===m;return a||(i=(h?i.slice(0,-m.length):i)+"(?:"+m+"(?=$))?"),i+=o?"$":a&&h?"":"(?="+m+"|$)",u(new RegExp("^"+i,d(n)),t)}function f(e,t,n){return r(t)||(n=t||n,t=[]),n=n||{},e instanceof RegExp?function(e,t){var n=e.source.match(/\((?!\?)/g);if(n)for(var r=0;r<n.length;r++)t.push({name:r,prefix:null,delimiter:null,optional:!1,repeat:!1,partial:!1,asterisk:!1,pattern:null});return u(e,t)}(e,t):r(e)?function(e,t,n){for(var r=[],a=0;a<e.length;a++)r.push(f(e[a],t,n).source);return u(new RegExp("(?:"+r.join("|")+")",d(n)),t)}(e,t,n):function(e,t,n){return p(o(e,n),t,n)}(e,t,n)}},2408:(e,t,n)=>{"use strict";var r=n(7418),a=60103,o=60106;t.Fragment=60107,t.StrictMode=60108,t.Profiler=60114;var i=60109,l=60110,s=60112;t.Suspense=60113;var c=60115,u=60116;if("function"==typeof Symbol&&Symbol.for){var d=Symbol.for;a=d("react.element"),o=d("react.portal"),t.Fragment=d("react.fragment"),t.StrictMode=d("react.strict_mode"),t.Profiler=d("react.profiler"),i=d("react.provider"),l=d("react.context"),s=d("react.forward_ref"),t.Suspense=d("react.suspense"),c=d("react.memo"),u=d("react.lazy")}var p="function"==typeof Symbol&&Symbol.iterator;function f(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n<arguments.length;n++)t+="&args[]="+encodeURIComponent(arguments[n]);return"Minified React error #"+e+"; visit "+t+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}var m={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},h={};function g(e,t,n){this.props=e,this.context=t,this.refs=h,this.updater=n||m}function b(){}function v(e,t,n){this.props=e,this.context=t,this.refs=h,this.updater=n||m}g.prototype.isReactComponent={},g.prototype.setState=function(e,t){if("object"!=typeof e&&"function"!=typeof e&&null!=e)throw Error(f(85));this.updater.enqueueSetState(this,e,t,"setState")},g.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")},b.prototype=g.prototype;var y=v.prototype=new b;y.constructor=v,r(y,g.prototype),y.isPureReactComponent=!0;var w={current:null},k=Object.prototype.hasOwnProperty,E={key:!0,ref:!0,__self:!0,__source:!0};function x(e,t,n){var r,o={},i=null,l=null;if(null!=t)for(r in void 0!==t.ref&&(l=t.ref),void 0!==t.key&&(i=""+t.key),t)k.call(t,r)&&!E.hasOwnProperty(r)&&(o[r]=t[r]);var s=arguments.length-2;if(1===s)o.children=n;else if(1<s){for(var c=Array(s),u=0;u<s;u++)c[u]=arguments[u+2];o.children=c}if(e&&e.defaultProps)for(r in s=e.defaultProps)void 0===o[r]&&(o[r]=s[r]);return{$$typeof:a,type:e,key:i,ref:l,props:o,_owner:w.current}}function S(e){return"object"==typeof e&&null!==e&&e.$$typeof===a}var _=/\/+/g;function C(e,t){return"object"==typeof e&&null!==e&&null!=e.key?function(e){var t={"=":"=0",":":"=2"};return"$"+e.replace(/[=:]/g,(function(e){return t[e]}))}(""+e.key):t.toString(36)}function T(e,t,n,r,i){var l=typeof e;"undefined"!==l&&"boolean"!==l||(e=null);var s=!1;if(null===e)s=!0;else switch(l){case"string":case"number":s=!0;break;case"object":switch(e.$$typeof){case a:case o:s=!0}}if(s)return i=i(s=e),e=""===r?"."+C(s,0):r,Array.isArray(i)?(n="",null!=e&&(n=e.replace(_,"$&/")+"/"),T(i,t,n,"",(function(e){return e}))):null!=i&&(S(i)&&(i=function(e,t){return{$$typeof:a,type:e.type,key:t,ref:e.ref,props:e.props,_owner:e._owner}}(i,n+(!i.key||s&&s.key===i.key?"":(""+i.key).replace(_,"$&/")+"/")+e)),t.push(i)),1;if(s=0,r=""===r?".":r+":",Array.isArray(e))for(var c=0;c<e.length;c++){var u=r+C(l=e[c],c);s+=T(l,t,n,u,i)}else if(u=function(e){return null===e||"object"!=typeof e?null:"function"==typeof(e=p&&e[p]||e["@@iterator"])?e:null}(e),"function"==typeof u)for(e=u.call(e),c=0;!(l=e.next()).done;)s+=T(l=l.value,t,n,u=r+C(l,c++),i);else if("object"===l)throw t=""+e,Error(f(31,"[object Object]"===t?"object with keys {"+Object.keys(e).join(", ")+"}":t));return s}function A(e,t,n){if(null==e)return e;var r=[],a=0;return T(e,r,"","",(function(e){return t.call(n,e,a++)})),r}function L(e){if(-1===e._status){var t=e._result;t=t(),e._status=0,e._result=t,t.then((function(t){0===e._status&&(t=t.default,e._status=1,e._result=t)}),(function(t){0===e._status&&(e._status=2,e._result=t)}))}if(1===e._status)return e._result;throw e._result}var R={current:null};function N(){var e=R.current;if(null===e)throw Error(f(321));return e}var O={ReactCurrentDispatcher:R,ReactCurrentBatchConfig:{transition:0},ReactCurrentOwner:w,IsSomeRendererActing:{current:!1},assign:r};t.Children={map:A,forEach:function(e,t,n){A(e,(function(){t.apply(this,arguments)}),n)},count:function(e){var t=0;return A(e,(function(){t++})),t},toArray:function(e){return A(e,(function(e){return e}))||[]},only:function(e){if(!S(e))throw Error(f(143));return e}},t.Component=g,t.PureComponent=v,t.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=O,t.cloneElement=function(e,t,n){if(null==e)throw Error(f(267,e));var o=r({},e.props),i=e.key,l=e.ref,s=e._owner;if(null!=t){if(void 0!==t.ref&&(l=t.ref,s=w.current),void 0!==t.key&&(i=""+t.key),e.type&&e.type.defaultProps)var c=e.type.defaultProps;for(u in t)k.call(t,u)&&!E.hasOwnProperty(u)&&(o[u]=void 0===t[u]&&void 0!==c?c[u]:t[u])}var u=arguments.length-2;if(1===u)o.children=n;else if(1<u){c=Array(u);for(var d=0;d<u;d++)c[d]=arguments[d+2];o.children=c}return{$$typeof:a,type:e.type,key:i,ref:l,props:o,_owner:s}},t.createContext=function(e,t){return void 0===t&&(t=null),(e={$$typeof:l,_calculateChangedBits:t,_currentValue:e,_currentValue2:e,_threadCount:0,Provider:null,Consumer:null}).Provider={$$typeof:i,_context:e},e.Consumer=e},t.createElement=x,t.createFactory=function(e){var t=x.bind(null,e);return t.type=e,t},t.createRef=function(){return{current:null}},t.forwardRef=function(e){return{$$typeof:s,render:e}},t.isValidElement=S,t.lazy=function(e){return{$$typeof:u,_payload:{_status:-1,_result:e},_init:L}},t.memo=function(e,t){return{$$typeof:c,type:e,compare:void 0===t?null:t}},t.useCallback=function(e,t){return N().useCallback(e,t)},t.useContext=function(e,t){return N().useContext(e,t)},t.useDebugValue=function(){},t.useEffect=function(e,t){return N().useEffect(e,t)},t.useImperativeHandle=function(e,t,n){return N().useImperativeHandle(e,t,n)},t.useLayoutEffect=function(e,t){return N().useLayoutEffect(e,t)},t.useMemo=function(e,t){return N().useMemo(e,t)},t.useReducer=function(e,t,n){return N().useReducer(e,t,n)},t.useRef=function(e){return N().useRef(e)},t.useState=function(e){return N().useState(e)},t.version="17.0.2"},7294:(e,t,n)=>{"use strict";e.exports=n(2408)},53:(e,t)=>{"use strict";var n,r,a,o;if("object"==typeof performance&&"function"==typeof performance.now){var i=performance;t.unstable_now=function(){return i.now()}}else{var l=Date,s=l.now();t.unstable_now=function(){return l.now()-s}}if("undefined"==typeof window||"function"!=typeof MessageChannel){var c=null,u=null,d=function(){if(null!==c)try{var e=t.unstable_now();c(!0,e),c=null}catch(n){throw setTimeout(d,0),n}};n=function(e){null!==c?setTimeout(n,0,e):(c=e,setTimeout(d,0))},r=function(e,t){u=setTimeout(e,t)},a=function(){clearTimeout(u)},t.unstable_shouldYield=function(){return!1},o=t.unstable_forceFrameRate=function(){}}else{var p=window.setTimeout,f=window.clearTimeout;if("undefined"!=typeof console){var m=window.cancelAnimationFrame;"function"!=typeof window.requestAnimationFrame&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://reactjs.org/link/react-polyfills"),"function"!=typeof m&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://reactjs.org/link/react-polyfills")}var h=!1,g=null,b=-1,v=5,y=0;t.unstable_shouldYield=function(){return t.unstable_now()>=y},o=function(){},t.unstable_forceFrameRate=function(e){0>e||125<e?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):v=0<e?Math.floor(1e3/e):5};var w=new MessageChannel,k=w.port2;w.port1.onmessage=function(){if(null!==g){var e=t.unstable_now();y=e+v;try{g(!0,e)?k.postMessage(null):(h=!1,g=null)}catch(n){throw k.postMessage(null),n}}else h=!1},n=function(e){g=e,h||(h=!0,k.postMessage(null))},r=function(e,n){b=p((function(){e(t.unstable_now())}),n)},a=function(){f(b),b=-1}}function E(e,t){var n=e.length;e.push(t);e:for(;;){var r=n-1>>>1,a=e[r];if(!(void 0!==a&&0<_(a,t)))break e;e[r]=t,e[n]=a,n=r}}function x(e){return void 0===(e=e[0])?null:e}function S(e){var t=e[0];if(void 0!==t){var n=e.pop();if(n!==t){e[0]=n;e:for(var r=0,a=e.length;r<a;){var o=2*(r+1)-1,i=e[o],l=o+1,s=e[l];if(void 0!==i&&0>_(i,n))void 0!==s&&0>_(s,i)?(e[r]=s,e[l]=n,r=l):(e[r]=i,e[o]=n,r=o);else{if(!(void 0!==s&&0>_(s,n)))break e;e[r]=s,e[l]=n,r=l}}}return t}return null}function _(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}var C=[],T=[],A=1,L=null,R=3,N=!1,O=!1,P=!1;function I(e){for(var t=x(T);null!==t;){if(null===t.callback)S(T);else{if(!(t.startTime<=e))break;S(T),t.sortIndex=t.expirationTime,E(C,t)}t=x(T)}}function D(e){if(P=!1,I(e),!O)if(null!==x(C))O=!0,n(M);else{var t=x(T);null!==t&&r(D,t.startTime-e)}}function M(e,n){O=!1,P&&(P=!1,a()),N=!0;var o=R;try{for(I(n),L=x(C);null!==L&&(!(L.expirationTime>n)||e&&!t.unstable_shouldYield());){var i=L.callback;if("function"==typeof i){L.callback=null,R=L.priorityLevel;var l=i(L.expirationTime<=n);n=t.unstable_now(),"function"==typeof l?L.callback=l:L===x(C)&&S(C),I(n)}else S(C);L=x(C)}if(null!==L)var s=!0;else{var c=x(T);null!==c&&r(D,c.startTime-n),s=!1}return s}finally{L=null,R=o,N=!1}}var F=o;t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(e){e.callback=null},t.unstable_continueExecution=function(){O||N||(O=!0,n(M))},t.unstable_getCurrentPriorityLevel=function(){return R},t.unstable_getFirstCallbackNode=function(){return x(C)},t.unstable_next=function(e){switch(R){case 1:case 2:case 3:var t=3;break;default:t=R}var n=R;R=t;try{return e()}finally{R=n}},t.unstable_pauseExecution=function(){},t.unstable_requestPaint=F,t.unstable_runWithPriority=function(e,t){switch(e){case 1:case 2:case 3:case 4:case 5:break;default:e=3}var n=R;R=e;try{return t()}finally{R=n}},t.unstable_scheduleCallback=function(e,o,i){var l=t.unstable_now();switch("object"==typeof i&&null!==i?i="number"==typeof(i=i.delay)&&0<i?l+i:l:i=l,e){case 1:var s=-1;break;case 2:s=250;break;case 5:s=1073741823;break;case 4:s=1e4;break;default:s=5e3}return e={id:A++,callback:o,priorityLevel:e,startTime:i,expirationTime:s=i+s,sortIndex:-1},i>l?(e.sortIndex=i,E(T,e),null===x(C)&&e===x(T)&&(P?a():P=!0,r(D,i-l))):(e.sortIndex=s,E(C,e),O||N||(O=!0,n(M))),e},t.unstable_wrapCallback=function(e){var t=R;return function(){var n=R;R=t;try{return e.apply(this,arguments)}finally{R=n}}}},3840:(e,t,n)=>{"use strict";e.exports=n(53)},6774:e=>{e.exports=function(e,t,n,r){var a=n?n.call(r,e,t):void 0;if(void 0!==a)return!!a;if(e===t)return!0;if("object"!=typeof e||!e||"object"!=typeof t||!t)return!1;var o=Object.keys(e),i=Object.keys(t);if(o.length!==i.length)return!1;for(var l=Object.prototype.hasOwnProperty.bind(t),s=0;s<o.length;s++){var c=o[s];if(!l(c))return!1;var u=e[c],d=t[c];if(!1===(a=n?n.call(r,u,d,c):void 0)||void 0===a&&u!==d)return!1}return!0}},3250:(e,t,n)=>{"use strict";var r=n(7294);var a="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},o=r.useState,i=r.useEffect,l=r.useLayoutEffect,s=r.useDebugValue;function c(e){var t=e.getSnapshot;e=e.value;try{var n=t();return!a(e,n)}catch(r){return!0}}var u="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(e,t){var n=t(),r=o({inst:{value:n,getSnapshot:t}}),a=r[0].inst,u=r[1];return l((function(){a.value=n,a.getSnapshot=t,c(a)&&u({inst:a})}),[e,n,t]),i((function(){return c(a)&&u({inst:a}),e((function(){c(a)&&u({inst:a})}))}),[e]),s(n),n};void 0!==r.useSyncExternalStore&&r.useSyncExternalStore},1688:(e,t,n)=>{"use strict";n(3250)},6809:(e,t,n)=>{"use strict";n.d(t,{Z:()=>r});const r={title:"Copacetic",url:"https://project-copacetic.github.io",baseUrl:"/copacetic/website/",onBrokenLinks:"ignore",onBrokenMarkdownLinks:"warn",favicon:"img/favicon.ico",trailingSlash:!1,organizationName:"project-copacetic",projectName:"copacetic",deploymentBranch:"gh-pages",i18n:{defaultLocale:"en",locales:["en"],path:"i18n",localeConfigs:{}},presets:[["classic",{docs:{sidebarPath:"/home/runner/work/copacetic/copacetic/website/sidebars.js",routeBasePath:"/"},blog:!1,theme:{customCss:"/home/runner/work/copacetic/copacetic/website/src/css/custom.css"},gtag:{trackingID:"G-3RC20QPKNS",anonymizeIP:!0}}]],themeConfig:{navbar:{title:"Copacetic",logo:{alt:"Copacetic Logo",src:"img/logo.png",href:"https://project-copacetic.github.io/copacetic/"},items:[{type:"docsVersionDropdown",position:"right",dropdownItemsBefore:[],dropdownItemsAfter:[]},{href:"https://github.com/project-copacetic/copacetic",position:"right",className:"header-github-link","aria-label":"GitHub repository"}],hideOnScroll:!1},footer:{style:"dark",copyright:'Copyright \xa9 2024 Linux Foundation. The Linux Foundation\xae (TLF) has registered trademarks and uses trademarks. For a list of TLF trademarks, see <a href="https://www.linuxfoundation.org/trademark-usage/">Trademark Usage</a>.',links:[]},prism:{theme:{plain:{color:"#393A34",backgroundColor:"#f6f8fa"},styles:[{types:["comment","prolog","doctype","cdata"],style:{color:"#999988",fontStyle:"italic"}},{types:["namespace"],style:{opacity:.7}},{types:["string","attr-value"],style:{color:"#e3116c"}},{types:["punctuation","operator"],style:{color:"#393A34"}},{types:["entity","url","symbol","number","boolean","variable","constant","property","regex","inserted"],style:{color:"#36acaa"}},{types:["atrule","keyword","attr-name","selector"],style:{color:"#00a4db"}},{types:["function","deleted","tag"],style:{color:"#d73a49"}},{types:["function-variable"],style:{color:"#6f42c1"}},{types:["tag","selector","keyword"],style:{color:"#00009f"}}]},darkTheme:{plain:{color:"#F8F8F2",backgroundColor:"#282A36"},styles:[{types:["prolog","constant","builtin"],style:{color:"rgb(189, 147, 249)"}},{types:["inserted","function"],style:{color:"rgb(80, 250, 123)"}},{types:["deleted"],style:{color:"rgb(255, 85, 85)"}},{types:["changed"],style:{color:"rgb(255, 184, 108)"}},{types:["punctuation","symbol"],style:{color:"rgb(248, 248, 242)"}},{types:["string","char","tag","selector"],style:{color:"rgb(255, 121, 198)"}},{types:["keyword","variable"],style:{color:"rgb(189, 147, 249)",fontStyle:"italic"}},{types:["comment"],style:{color:"rgb(98, 114, 164)"}},{types:["attr-name"],style:{color:"rgb(241, 250, 140)"}}]},additionalLanguages:[],magicComments:[{className:"theme-code-block-highlighted-line",line:"highlight-next-line",block:{start:"highlight-start",end:"highlight-end"}}]},colorMode:{defaultMode:"light",disableSwitch:!1,respectPrefersColorScheme:!1},docs:{versionPersistence:"localStorage",sidebar:{hideable:!1,autoCollapseCategories:!1}},metadata:[],tableOfContents:{minHeadingLevel:2,maxHeadingLevel:3}},baseUrlIssueBanner:!0,onDuplicateRoutes:"warn",staticDirectories:["static"],customFields:{},plugins:[],themes:[],scripts:[],headTags:[],stylesheets:[],clientModules:[],tagline:"",titleDelimiter:"|",noIndex:!1,markdown:{mermaid:!1}}},7462:(e,t,n)=>{"use strict";function r(){return r=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},r.apply(this,arguments)}n.d(t,{Z:()=>r})},5068:(e,t,n)=>{"use strict";function r(e,t){return r=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},r(e,t)}function a(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,r(e,t)}n.d(t,{Z:()=>a})},3366:(e,t,n)=>{"use strict";function r(e,t){if(null==e)return{};var n,r,a={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(a[n]=e[n]);return a}n.d(t,{Z:()=>r})},8776:(e,t,n)=>{"use strict";n.d(t,{Z:()=>a});var r="Invariant failed";function a(e,t){if(!e)throw new Error(r)}},7529:e=>{"use strict";e.exports={}},6887:e=>{"use strict";e.exports=JSON.parse('{"/copacetic/website/next-d7c":{"__comp":"1be78505","__context":{"plugin":"804a5934"},"versionMetadata":"935f2afb"},"/copacetic/website/next-e13":{"__comp":"17896441","content":"a09c2993"},"/copacetic/website/next/best-practices-d9c":{"__comp":"17896441","content":"e4d3cddf"},"/copacetic/website/next/code-of-conduct-c0e":{"__comp":"17896441","content":"dea0f9ea"},"/copacetic/website/next/contributing-08c":{"__comp":"17896441","content":"4d54d076"},"/copacetic/website/next/custom-address-437":{"__comp":"17896441","content":"27cf52aa"},"/copacetic/website/next/design-623":{"__comp":"17896441","content":"0aeccac2"},"/copacetic/website/next/development-tips-868":{"__comp":"17896441","content":"baa9408d"},"/copacetic/website/next/faq-93b":{"__comp":"17896441","content":"0480b142"},"/copacetic/website/next/github-action-5b0":{"__comp":"17896441","content":"14aa7e32"},"/copacetic/website/next/installation-896":{"__comp":"17896441","content":"3b8c55ea"},"/copacetic/website/next/maintainer-guidelines-619":{"__comp":"17896441","content":"282f11b9"},"/copacetic/website/next/output-f6d":{"__comp":"17896441","content":"e8c0720c"},"/copacetic/website/next/quick-start-d17":{"__comp":"17896441","content":"72e14192"},"/copacetic/website/next/release-d24":{"__comp":"17896441","content":"05a474a1"},"/copacetic/website/next/scanner-plugins-390":{"__comp":"17896441","content":"bfa18532"},"/copacetic/website/next/troubleshooting-eda":{"__comp":"17896441","content":"9d9f8394"},"/copacetic/website/v0.1.x-063":{"__comp":"1be78505","__context":{"plugin":"804a5934"},"versionMetadata":"c5934ffc"},"/copacetic/website/v0.1.x-a63":{"__comp":"17896441","content":"d2339658"},"/copacetic/website/v0.1.x/code-of-conduct-88f":{"__comp":"17896441","content":"bab6f5ab"},"/copacetic/website/v0.1.x/contributing-64c":{"__comp":"17896441","content":"ea9eabff"},"/copacetic/website/v0.1.x/design-85e":{"__comp":"17896441","content":"0a7bb60d"},"/copacetic/website/v0.1.x/faq-162":{"__comp":"17896441","content":"053b5658"},"/copacetic/website/v0.1.x/installation-e86":{"__comp":"17896441","content":"2133a534"},"/copacetic/website/v0.1.x/quick-start-b29":{"__comp":"17896441","content":"6173fde8"},"/copacetic/website/v0.2.x-e48":{"__comp":"1be78505","__context":{"plugin":"804a5934"},"versionMetadata":"299b5310"},"/copacetic/website/v0.2.x-881":{"__comp":"17896441","content":"d9461b6c"},"/copacetic/website/v0.2.x/code-of-conduct-f68":{"__comp":"17896441","content":"b407a98a"},"/copacetic/website/v0.2.x/contributing-ee0":{"__comp":"17896441","content":"fcc13998"},"/copacetic/website/v0.2.x/design-f57":{"__comp":"17896441","content":"4cb9f763"},"/copacetic/website/v0.2.x/faq-49f":{"__comp":"17896441","content":"934782ba"},"/copacetic/website/v0.2.x/installation-874":{"__comp":"17896441","content":"86b9c768"},"/copacetic/website/v0.2.x/quick-start-5f6":{"__comp":"17896441","content":"0c380bb3"},"/copacetic/website/v0.3.x-ec1":{"__comp":"1be78505","__context":{"plugin":"804a5934"},"versionMetadata":"6bd0979b"},"/copacetic/website/v0.3.x-265":{"__comp":"17896441","content":"57b2432c"},"/copacetic/website/v0.3.x/code-of-conduct-729":{"__comp":"17896441","content":"89eebbd3"},"/copacetic/website/v0.3.x/contributing-47d":{"__comp":"17896441","content":"7e72de0f"},"/copacetic/website/v0.3.x/design-7b5":{"__comp":"17896441","content":"9f65b58a"},"/copacetic/website/v0.3.x/faq-8a7":{"__comp":"17896441","content":"2a636ef6"},"/copacetic/website/v0.3.x/installation-c8d":{"__comp":"17896441","content":"b5d5e16b"},"/copacetic/website/v0.3.x/quick-start-0e0":{"__comp":"17896441","content":"f9459e94"},"/copacetic/website/v0.4.x-c9b":{"__comp":"1be78505","__context":{"plugin":"804a5934"},"versionMetadata":"c698fe77"},"/copacetic/website/v0.4.x-c77":{"__comp":"17896441","content":"22539a87"},"/copacetic/website/v0.4.x/code-of-conduct-f71":{"__comp":"17896441","content":"c12dc9fd"},"/copacetic/website/v0.4.x/contributing-cf7":{"__comp":"17896441","content":"48ae5635"},"/copacetic/website/v0.4.x/design-800":{"__comp":"17896441","content":"c13ba925"},"/copacetic/website/v0.4.x/faq-951":{"__comp":"17896441","content":"b9421f89"},"/copacetic/website/v0.4.x/github-action-608":{"__comp":"17896441","content":"1b7d20b2"},"/copacetic/website/v0.4.x/installation-f2c":{"__comp":"17896441","content":"3fcb412e"},"/copacetic/website/v0.4.x/quick-start-d0f":{"__comp":"17896441","content":"9e350ec0"},"/copacetic/website/v0.4.x/release-b08":{"__comp":"17896441","content":"f3bd9382"},"/copacetic/website/v0.4.x/troubleshooting-61e":{"__comp":"17896441","content":"040fbf97"},"/copacetic/website/v0.5.x-429":{"__comp":"1be78505","__context":{"plugin":"804a5934"},"versionMetadata":"efdb11b6"},"/copacetic/website/v0.5.x-de3":{"__comp":"17896441","content":"c66bbf8a"},"/copacetic/website/v0.5.x/code-of-conduct-369":{"__comp":"17896441","content":"11d58a17"},"/copacetic/website/v0.5.x/contributing-22c":{"__comp":"17896441","content":"0d9a188d"},"/copacetic/website/v0.5.x/design-87e":{"__comp":"17896441","content":"22dce6f4"},"/copacetic/website/v0.5.x/faq-b07":{"__comp":"17896441","content":"fa2b770f"},"/copacetic/website/v0.5.x/github-action-5c5":{"__comp":"17896441","content":"512645f2"},"/copacetic/website/v0.5.x/installation-8b9":{"__comp":"17896441","content":"c73303db"},"/copacetic/website/v0.5.x/output-9d0":{"__comp":"17896441","content":"9f27d5ef"},"/copacetic/website/v0.5.x/quick-start-e12":{"__comp":"17896441","content":"34f2f592"},"/copacetic/website/v0.5.x/release-b3f":{"__comp":"17896441","content":"77a9ed43"},"/copacetic/website/v0.5.x/scanner-plugins-a63":{"__comp":"17896441","content":"5c8ee200"},"/copacetic/website/v0.5.x/troubleshooting-3ce":{"__comp":"17896441","content":"4d7fa969"},"/copacetic/website/-581":{"__comp":"1be78505","__context":{"plugin":"804a5934"},"versionMetadata":"ff86e818"},"/copacetic/website/-c19":{"__comp":"17896441","content":"17e02f37"},"/copacetic/website/best-practices-79d":{"__comp":"17896441","content":"499c1190"},"/copacetic/website/code-of-conduct-833":{"__comp":"17896441","content":"5fc6e4d2"},"/copacetic/website/contributing-d35":{"__comp":"17896441","content":"801664dc"},"/copacetic/website/custom-address-c6b":{"__comp":"17896441","content":"9b66f67b"},"/copacetic/website/design-b0d":{"__comp":"17896441","content":"a95b810a"},"/copacetic/website/development-tips-18c":{"__comp":"17896441","content":"4e9dbf89"},"/copacetic/website/faq-b74":{"__comp":"17896441","content":"12042f32"},"/copacetic/website/github-action-8d1":{"__comp":"17896441","content":"14b4e536"},"/copacetic/website/installation-90f":{"__comp":"17896441","content":"7d213ce9"},"/copacetic/website/maintainer-guidelines-28d":{"__comp":"17896441","content":"4468d236"},"/copacetic/website/output-924":{"__comp":"17896441","content":"683b919f"},"/copacetic/website/quick-start-c5e":{"__comp":"17896441","content":"84979900"},"/copacetic/website/release-06e":{"__comp":"17896441","content":"98016c8b"},"/copacetic/website/scanner-plugins-822":{"__comp":"17896441","content":"d832efb0"},"/copacetic/website/troubleshooting-b63":{"__comp":"17896441","content":"e99d2929"}}')}},e=>{e.O(0,[532],(()=>{return t=7221,e(e.s=t);var t}));e.O()}]); \ No newline at end of file diff --git a/website/assets/js/main.d9039351.js b/website/assets/js/main.d9039351.js new file mode 100644 index 00000000..dd796e60 --- /dev/null +++ b/website/assets/js/main.d9039351.js @@ -0,0 +1,2 @@ +/*! For license information please see main.d9039351.js.LICENSE.txt */ +(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8792],{8328:(e,t,n)=>{"use strict";n.d(t,{A:()=>p});n(6540);var r=n(3259),a=n.n(r),o=n(4054);const i={"040fbf97":[()=>n.e(3105).then(n.bind(n,7478)),"@site/versioned_docs/version-v0.4.x/troubleshooting.md",7478],"0480b142":[()=>n.e(8070).then(n.bind(n,8614)),"@site/docs/faq.md",8614],"053b5658":[()=>n.e(3831).then(n.bind(n,8243)),"@site/versioned_docs/version-v0.1.x/faq.md",8243],"05a474a1":[()=>n.e(2338).then(n.bind(n,6679)),"@site/docs/release.md",6679],"0a7bb60d":[()=>n.e(7173).then(n.bind(n,6353)),"@site/versioned_docs/version-v0.1.x/design.md",6353],"0aeccac2":[()=>n.e(1965).then(n.bind(n,1970)),"@site/docs/design.md",1970],"0c380bb3":[()=>n.e(2150).then(n.bind(n,5420)),"@site/versioned_docs/version-v0.2.x/quick-start.md",5420],"0d9a188d":[()=>n.e(1882).then(n.bind(n,2687)),"@site/versioned_docs/version-v0.5.x/contributing.md",2687],"11d58a17":[()=>n.e(7973).then(n.bind(n,7695)),"@site/versioned_docs/version-v0.5.x/code-of-conduct.md",7695],"12042f32":[()=>n.e(1653).then(n.bind(n,7894)),"@site/versioned_docs/version-v0.6.x/faq.md",7894],"14aa7e32":[()=>n.e(3409).then(n.bind(n,4186)),"@site/docs/github-action.md",4186],"14b4e536":[()=>n.e(8355).then(n.bind(n,5306)),"@site/versioned_docs/version-v0.6.x/github-action.md",5306],17896441:[()=>Promise.all([n.e(1869),n.e(8401)]).then(n.bind(n,4313)),"@theme/DocItem",4313],"17e02f37":[()=>n.e(7594).then(n.bind(n,1264)),"@site/versioned_docs/version-v0.6.x/introduction.md",1264],"1b7d20b2":[()=>n.e(8313).then(n.bind(n,3988)),"@site/versioned_docs/version-v0.4.x/github-action.md",3988],"2133a534":[()=>n.e(2193).then(n.bind(n,7763)),"@site/versioned_docs/version-v0.1.x/installation.md",7763],"22539a87":[()=>n.e(9702).then(n.bind(n,5890)),"@site/versioned_docs/version-v0.4.x/introduction.md",5890],"22dce6f4":[()=>n.e(3227).then(n.bind(n,8013)),"@site/versioned_docs/version-v0.5.x/design.md",8013],"27cf52aa":[()=>n.e(4938).then(n.bind(n,2986)),"@site/docs/custom-address.md",2986],"282f11b9":[()=>n.e(2180).then(n.bind(n,7928)),"@site/docs/maintainer-guidelines.md",7928],"299b5310":[()=>n.e(1842).then(n.t.bind(n,9043,19)),"~docs/default/version-v-0-2-x-metadata-prop-6dd.json",9043],"2a636ef6":[()=>n.e(1866).then(n.bind(n,5865)),"@site/versioned_docs/version-v0.3.x/faq.md",5865],"34f2f592":[()=>n.e(4364).then(n.bind(n,5645)),"@site/versioned_docs/version-v0.5.x/quick-start.md",5645],"3b8c55ea":[()=>n.e(6803).then(n.bind(n,3668)),"@site/docs/installation.md",3668],"3fcb412e":[()=>n.e(2201).then(n.bind(n,646)),"@site/versioned_docs/version-v0.4.x/installation.md",646],"4468d236":[()=>n.e(2676).then(n.bind(n,8059)),"@site/versioned_docs/version-v0.6.x/maintainer-guidelines.md",8059],"48ae5635":[()=>n.e(1508).then(n.bind(n,1690)),"@site/versioned_docs/version-v0.4.x/contributing.md",1690],"499c1190":[()=>n.e(3163).then(n.bind(n,4701)),"@site/versioned_docs/version-v0.6.x/best-practices.md",4701],"4cb9f763":[()=>n.e(6615).then(n.bind(n,3062)),"@site/versioned_docs/version-v0.2.x/design.md",3062],"4d54d076":[()=>n.e(1459).then(n.bind(n,6564)),"@site/docs/contributing.md",6564],"4d7fa969":[()=>n.e(105).then(n.bind(n,5405)),"@site/versioned_docs/version-v0.5.x/troubleshooting.md",5405],"4e9dbf89":[()=>n.e(5336).then(n.bind(n,8214)),"@site/versioned_docs/version-v0.6.x/development-tips.md",8214],"512645f2":[()=>n.e(9516).then(n.bind(n,2963)),"@site/versioned_docs/version-v0.5.x/github-action.md",2963],"57b2432c":[()=>n.e(3773).then(n.bind(n,2597)),"@site/versioned_docs/version-v0.3.x/introduction.md",2597],"5c8ee200":[()=>n.e(5911).then(n.bind(n,6776)),"@site/versioned_docs/version-v0.5.x/scanner-plugins.md",6776],"5e95c892":[()=>n.e(9647).then(n.bind(n,7121)),"@theme/DocsRoot",7121],"5fc6e4d2":[()=>n.e(4140).then(n.bind(n,3350)),"@site/versioned_docs/version-v0.6.x/code-of-conduct.md",3350],"6173fde8":[()=>n.e(1439).then(n.bind(n,4489)),"@site/versioned_docs/version-v0.1.x/quick-start.md",4489],"683b919f":[()=>n.e(4521).then(n.bind(n,65)),"@site/versioned_docs/version-v0.6.x/output.md",65],"6bd0979b":[()=>n.e(1786).then(n.t.bind(n,9232,19)),"~docs/default/version-v-0-3-x-metadata-prop-338.json",9232],"72e14192":[()=>n.e(2814).then(n.bind(n,3744)),"@site/docs/quick-start.md",3744],"77a9ed43":[()=>n.e(2171).then(n.bind(n,9378)),"@site/versioned_docs/version-v0.5.x/release.md",9378],"7d213ce9":[()=>n.e(3033).then(n.bind(n,3748)),"@site/versioned_docs/version-v0.6.x/installation.md",3748],"7e72de0f":[()=>n.e(4519).then(n.bind(n,4717)),"@site/versioned_docs/version-v0.3.x/contributing.md",4717],"801664dc":[()=>n.e(5819).then(n.bind(n,2004)),"@site/versioned_docs/version-v0.6.x/contributing.md",2004],"804a5934":[()=>n.e(1157).then(n.t.bind(n,1966,19)),"/home/runner/work/copacetic/copacetic/website/.docusaurus/docusaurus-plugin-content-docs/default/plugin-route-context-module-100.json",1966],84979900:[()=>n.e(4269).then(n.bind(n,1632)),"@site/versioned_docs/version-v0.6.x/quick-start.md",1632],"86b9c768":[()=>n.e(8442).then(n.bind(n,8704)),"@site/versioned_docs/version-v0.2.x/installation.md",8704],"89eebbd3":[()=>n.e(6683).then(n.bind(n,7365)),"@site/versioned_docs/version-v0.3.x/code-of-conduct.md",7365],"934782ba":[()=>n.e(5713).then(n.bind(n,6602)),"@site/versioned_docs/version-v0.2.x/faq.md",6602],"935f2afb":[()=>n.e(8581).then(n.t.bind(n,5610,19)),"~docs/default/version-current-metadata-prop-751.json",5610],"98016c8b":[()=>n.e(90).then(n.bind(n,7847)),"@site/versioned_docs/version-v0.6.x/release.md",7847],"9b66f67b":[()=>n.e(1837).then(n.bind(n,9530)),"@site/versioned_docs/version-v0.6.x/custom-address.md",9530],"9d9f8394":[()=>n.e(9013).then(n.bind(n,5520)),"@site/docs/troubleshooting.md",5520],"9e350ec0":[()=>n.e(3817).then(n.bind(n,3014)),"@site/versioned_docs/version-v0.4.x/quick-start.md",3014],"9f27d5ef":[()=>n.e(9727).then(n.bind(n,6242)),"@site/versioned_docs/version-v0.5.x/output.md",6242],"9f65b58a":[()=>n.e(3379).then(n.bind(n,8715)),"@site/versioned_docs/version-v0.3.x/design.md",8715],a09c2993:[()=>n.e(5899).then(n.bind(n,1456)),"@site/docs/introduction.md",1456],a7bd4aaa:[()=>n.e(7098).then(n.bind(n,4532)),"@theme/DocVersionRoot",4532],a94703ab:[()=>Promise.all([n.e(1869),n.e(9048)]).then(n.bind(n,2559)),"@theme/DocRoot",2559],a95b810a:[()=>n.e(6524).then(n.bind(n,5970)),"@site/versioned_docs/version-v0.6.x/design.md",5970],b407a98a:[()=>n.e(8409).then(n.bind(n,5218)),"@site/versioned_docs/version-v0.2.x/code-of-conduct.md",5218],b5d5e16b:[()=>n.e(7231).then(n.bind(n,137)),"@site/versioned_docs/version-v0.3.x/installation.md",137],b9421f89:[()=>n.e(3978).then(n.bind(n,7192)),"@site/versioned_docs/version-v0.4.x/faq.md",7192],baa9408d:[()=>n.e(9222).then(n.bind(n,7750)),"@site/docs/development-tips.md",7750],bab6f5ab:[()=>n.e(9162).then(n.bind(n,2363)),"@site/versioned_docs/version-v0.1.x/code-of-conduct.md",2363],bfa18532:[()=>n.e(8399).then(n.bind(n,4781)),"@site/docs/scanner-plugins.md",4781],c12dc9fd:[()=>n.e(7391).then(n.bind(n,4916)),"@site/versioned_docs/version-v0.4.x/code-of-conduct.md",4916],c13ba925:[()=>n.e(621).then(n.bind(n,5496)),"@site/versioned_docs/version-v0.4.x/design.md",5496],c5934ffc:[()=>n.e(2728).then(n.t.bind(n,9564,19)),"~docs/default/version-v-0-1-x-metadata-prop-912.json",9564],c66bbf8a:[()=>n.e(6613).then(n.bind(n,7811)),"@site/versioned_docs/version-v0.5.x/introduction.md",7811],c698fe77:[()=>n.e(8222).then(n.t.bind(n,3046,19)),"~docs/default/version-v-0-4-x-metadata-prop-1ae.json",3046],c73303db:[()=>n.e(4914).then(n.bind(n,3927)),"@site/versioned_docs/version-v0.5.x/installation.md",3927],d2339658:[()=>n.e(1909).then(n.bind(n,6791)),"@site/versioned_docs/version-v0.1.x/introduction.md",6791],d832efb0:[()=>n.e(2375).then(n.bind(n,2285)),"@site/versioned_docs/version-v0.6.x/scanner-plugins.md",2285],d9461b6c:[()=>n.e(1268).then(n.bind(n,308)),"@site/versioned_docs/version-v0.2.x/introduction.md",308],dea0f9ea:[()=>n.e(9228).then(n.bind(n,9350)),"@site/docs/code-of-conduct.md",9350],e4d3cddf:[()=>n.e(864).then(n.bind(n,6461)),"@site/docs/best-practices.md",6461],e8c0720c:[()=>n.e(6229).then(n.bind(n,8209)),"@site/docs/output.md",8209],e99d2929:[()=>n.e(782).then(n.bind(n,9632)),"@site/versioned_docs/version-v0.6.x/troubleshooting.md",9632],ea9eabff:[()=>n.e(9590).then(n.bind(n,7979)),"@site/versioned_docs/version-v0.1.x/contributing.md",7979],efdb11b6:[()=>n.e(4586).then(n.t.bind(n,3412,19)),"~docs/default/version-v-0-5-x-metadata-prop-c47.json",3412],f3bd9382:[()=>n.e(2898).then(n.bind(n,8389)),"@site/versioned_docs/version-v0.4.x/release.md",8389],f9459e94:[()=>n.e(7508).then(n.bind(n,9379)),"@site/versioned_docs/version-v0.3.x/quick-start.md",9379],fa2b770f:[()=>n.e(5368).then(n.bind(n,2847)),"@site/versioned_docs/version-v0.5.x/faq.md",2847],fcc13998:[()=>n.e(9077).then(n.bind(n,6080)),"@site/versioned_docs/version-v0.2.x/contributing.md",6080],ff86e818:[()=>n.e(8293).then(n.t.bind(n,8441,19)),"~docs/default/version-v-0-6-x-metadata-prop-c58.json",8441]};var s=n(4848);function l(e){let{error:t,retry:n,pastDelay:r}=e;return t?(0,s.jsxs)("div",{style:{textAlign:"center",color:"#fff",backgroundColor:"#fa383e",borderColor:"#fa383e",borderStyle:"solid",borderRadius:"0.25rem",borderWidth:"1px",boxSizing:"border-box",display:"block",padding:"1rem",flex:"0 0 50%",marginLeft:"25%",marginRight:"25%",marginTop:"5rem",maxWidth:"50%",width:"100%"},children:[(0,s.jsx)("p",{children:String(t)}),(0,s.jsx)("div",{children:(0,s.jsx)("button",{type:"button",onClick:n,children:"Retry"})})]}):r?(0,s.jsx)("div",{style:{display:"flex",justifyContent:"center",alignItems:"center",height:"100vh"},children:(0,s.jsx)("svg",{id:"loader",style:{width:128,height:110,position:"absolute",top:"calc(100vh - 64%)"},viewBox:"0 0 45 45",xmlns:"http://www.w3.org/2000/svg",stroke:"#61dafb",children:(0,s.jsxs)("g",{fill:"none",fillRule:"evenodd",transform:"translate(1 1)",strokeWidth:"2",children:[(0,s.jsxs)("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0",children:[(0,s.jsx)("animate",{attributeName:"r",begin:"1.5s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),(0,s.jsx)("animate",{attributeName:"stroke-opacity",begin:"1.5s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),(0,s.jsx)("animate",{attributeName:"stroke-width",begin:"1.5s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})]}),(0,s.jsxs)("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0",children:[(0,s.jsx)("animate",{attributeName:"r",begin:"3s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),(0,s.jsx)("animate",{attributeName:"stroke-opacity",begin:"3s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),(0,s.jsx)("animate",{attributeName:"stroke-width",begin:"3s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})]}),(0,s.jsx)("circle",{cx:"22",cy:"22",r:"8",children:(0,s.jsx)("animate",{attributeName:"r",begin:"0s",dur:"1.5s",values:"6;1;2;3;4;5;6",calcMode:"linear",repeatCount:"indefinite"})})]})})}):null}var c=n(6921),u=n(3102);function d(e,t){if("*"===e)return a()({loading:l,loader:()=>n.e(2237).then(n.bind(n,2237)),modules:["@theme/NotFound"],webpack:()=>[2237],render(e,t){const n=e.default;return(0,s.jsx)(u.W,{value:{plugin:{name:"native",id:"default"}},children:(0,s.jsx)(n,{...t})})}});const r=o[`${e}-${t}`],d={},p=[],f=[],m=(0,c.A)(r);return Object.entries(m).forEach((e=>{let[t,n]=e;const r=i[n];r&&(d[t]=r[0],p.push(r[1]),f.push(r[2]))})),a().Map({loading:l,loader:d,modules:p,webpack:()=>f,render(t,n){const a=JSON.parse(JSON.stringify(r));Object.entries(t).forEach((t=>{let[n,r]=t;const o=r.default;if(!o)throw new Error(`The page component at ${e} doesn't have a default export. This makes it impossible to render anything. Consider default-exporting a React component.`);"object"!=typeof o&&"function"!=typeof o||Object.keys(r).filter((e=>"default"!==e)).forEach((e=>{o[e]=r[e]}));let i=a;const s=n.split(".");s.slice(0,-1).forEach((e=>{i=i[e]})),i[s[s.length-1]]=o}));const o=a.__comp;delete a.__comp;const i=a.__context;return delete a.__context,(0,s.jsx)(u.W,{value:i,children:(0,s.jsx)(o,{...a,...n})})}})}const p=[{path:"/copacetic/website/",component:d("/copacetic/website/","054"),routes:[{path:"/copacetic/website/next",component:d("/copacetic/website/next","083"),routes:[{path:"/copacetic/website/next",component:d("/copacetic/website/next","116"),routes:[{path:"/copacetic/website/next",component:d("/copacetic/website/next","d4d"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/best-practices",component:d("/copacetic/website/next/best-practices","355"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/code-of-conduct",component:d("/copacetic/website/next/code-of-conduct","1d7"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/contributing",component:d("/copacetic/website/next/contributing","ffb"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/custom-address",component:d("/copacetic/website/next/custom-address","3bc"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/design",component:d("/copacetic/website/next/design","4ec"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/development-tips",component:d("/copacetic/website/next/development-tips","8b3"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/faq",component:d("/copacetic/website/next/faq","037"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/github-action",component:d("/copacetic/website/next/github-action","e36"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/installation",component:d("/copacetic/website/next/installation","9c7"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/maintainer-guidelines",component:d("/copacetic/website/next/maintainer-guidelines","43d"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/output",component:d("/copacetic/website/next/output","335"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/quick-start",component:d("/copacetic/website/next/quick-start","77d"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/release",component:d("/copacetic/website/next/release","eff"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/scanner-plugins",component:d("/copacetic/website/next/scanner-plugins","89e"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/next/troubleshooting",component:d("/copacetic/website/next/troubleshooting","95a"),exact:!0,sidebar:"sidebar"}]}]},{path:"/copacetic/website/v0.1.x",component:d("/copacetic/website/v0.1.x","855"),routes:[{path:"/copacetic/website/v0.1.x",component:d("/copacetic/website/v0.1.x","8fd"),routes:[{path:"/copacetic/website/v0.1.x",component:d("/copacetic/website/v0.1.x","602"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.1.x/code-of-conduct",component:d("/copacetic/website/v0.1.x/code-of-conduct","334"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.1.x/contributing",component:d("/copacetic/website/v0.1.x/contributing","3a5"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.1.x/design",component:d("/copacetic/website/v0.1.x/design","338"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.1.x/faq",component:d("/copacetic/website/v0.1.x/faq","0bd"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.1.x/installation",component:d("/copacetic/website/v0.1.x/installation","125"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.1.x/quick-start",component:d("/copacetic/website/v0.1.x/quick-start","be0"),exact:!0,sidebar:"sidebar"}]}]},{path:"/copacetic/website/v0.2.x",component:d("/copacetic/website/v0.2.x","8db"),routes:[{path:"/copacetic/website/v0.2.x",component:d("/copacetic/website/v0.2.x","0cc"),routes:[{path:"/copacetic/website/v0.2.x",component:d("/copacetic/website/v0.2.x","3b5"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.2.x/code-of-conduct",component:d("/copacetic/website/v0.2.x/code-of-conduct","017"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.2.x/contributing",component:d("/copacetic/website/v0.2.x/contributing","058"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.2.x/design",component:d("/copacetic/website/v0.2.x/design","2ed"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.2.x/faq",component:d("/copacetic/website/v0.2.x/faq","8a1"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.2.x/installation",component:d("/copacetic/website/v0.2.x/installation","3e5"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.2.x/quick-start",component:d("/copacetic/website/v0.2.x/quick-start","5d1"),exact:!0,sidebar:"sidebar"}]}]},{path:"/copacetic/website/v0.3.x",component:d("/copacetic/website/v0.3.x","ea4"),routes:[{path:"/copacetic/website/v0.3.x",component:d("/copacetic/website/v0.3.x","9b9"),routes:[{path:"/copacetic/website/v0.3.x",component:d("/copacetic/website/v0.3.x","13c"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.3.x/code-of-conduct",component:d("/copacetic/website/v0.3.x/code-of-conduct","112"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.3.x/contributing",component:d("/copacetic/website/v0.3.x/contributing","f42"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.3.x/design",component:d("/copacetic/website/v0.3.x/design","7a6"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.3.x/faq",component:d("/copacetic/website/v0.3.x/faq","2d3"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.3.x/installation",component:d("/copacetic/website/v0.3.x/installation","888"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.3.x/quick-start",component:d("/copacetic/website/v0.3.x/quick-start","f27"),exact:!0,sidebar:"sidebar"}]}]},{path:"/copacetic/website/v0.4.x",component:d("/copacetic/website/v0.4.x","72c"),routes:[{path:"/copacetic/website/v0.4.x",component:d("/copacetic/website/v0.4.x","48a"),routes:[{path:"/copacetic/website/v0.4.x",component:d("/copacetic/website/v0.4.x","5ee"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.4.x/code-of-conduct",component:d("/copacetic/website/v0.4.x/code-of-conduct","6a2"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.4.x/contributing",component:d("/copacetic/website/v0.4.x/contributing","722"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.4.x/design",component:d("/copacetic/website/v0.4.x/design","fbd"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.4.x/faq",component:d("/copacetic/website/v0.4.x/faq","d1c"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.4.x/github-action",component:d("/copacetic/website/v0.4.x/github-action","41b"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.4.x/installation",component:d("/copacetic/website/v0.4.x/installation","1df"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.4.x/quick-start",component:d("/copacetic/website/v0.4.x/quick-start","558"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.4.x/release",component:d("/copacetic/website/v0.4.x/release","828"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.4.x/troubleshooting",component:d("/copacetic/website/v0.4.x/troubleshooting","71a"),exact:!0,sidebar:"sidebar"}]}]},{path:"/copacetic/website/v0.5.x",component:d("/copacetic/website/v0.5.x","dd7"),routes:[{path:"/copacetic/website/v0.5.x",component:d("/copacetic/website/v0.5.x","1f5"),routes:[{path:"/copacetic/website/v0.5.x",component:d("/copacetic/website/v0.5.x","b4f"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/code-of-conduct",component:d("/copacetic/website/v0.5.x/code-of-conduct","836"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/contributing",component:d("/copacetic/website/v0.5.x/contributing","386"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/design",component:d("/copacetic/website/v0.5.x/design","4c6"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/faq",component:d("/copacetic/website/v0.5.x/faq","f8a"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/github-action",component:d("/copacetic/website/v0.5.x/github-action","842"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/installation",component:d("/copacetic/website/v0.5.x/installation","c90"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/output",component:d("/copacetic/website/v0.5.x/output","60e"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/quick-start",component:d("/copacetic/website/v0.5.x/quick-start","962"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/release",component:d("/copacetic/website/v0.5.x/release","85e"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/scanner-plugins",component:d("/copacetic/website/v0.5.x/scanner-plugins","f13"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/v0.5.x/troubleshooting",component:d("/copacetic/website/v0.5.x/troubleshooting","9db"),exact:!0,sidebar:"sidebar"}]}]},{path:"/copacetic/website/",component:d("/copacetic/website/","589"),routes:[{path:"/copacetic/website/",component:d("/copacetic/website/","1a4"),routes:[{path:"/copacetic/website/",component:d("/copacetic/website/","ef0"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/best-practices",component:d("/copacetic/website/best-practices","025"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/code-of-conduct",component:d("/copacetic/website/code-of-conduct","73d"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/contributing",component:d("/copacetic/website/contributing","62f"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/custom-address",component:d("/copacetic/website/custom-address","ffc"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/design",component:d("/copacetic/website/design","9d5"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/development-tips",component:d("/copacetic/website/development-tips","375"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/faq",component:d("/copacetic/website/faq","5db"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/github-action",component:d("/copacetic/website/github-action","39b"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/installation",component:d("/copacetic/website/installation","9b0"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/maintainer-guidelines",component:d("/copacetic/website/maintainer-guidelines","7d0"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/output",component:d("/copacetic/website/output","985"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/quick-start",component:d("/copacetic/website/quick-start","002"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/release",component:d("/copacetic/website/release","ed0"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/scanner-plugins",component:d("/copacetic/website/scanner-plugins","5d7"),exact:!0,sidebar:"sidebar"},{path:"/copacetic/website/troubleshooting",component:d("/copacetic/website/troubleshooting","236"),exact:!0,sidebar:"sidebar"}]}]}]},{path:"*",component:d("*")}]},6125:(e,t,n)=>{"use strict";n.d(t,{o:()=>o,x:()=>i});var r=n(6540),a=n(4848);const o=r.createContext(!1);function i(e){let{children:t}=e;const[n,i]=(0,r.useState)(!1);return(0,r.useEffect)((()=>{i(!0)}),[]),(0,a.jsx)(o.Provider,{value:n,children:t})}},8536:(e,t,n)=>{"use strict";var r=n(6540),a=n(5338),o=n(4625),i=n(545),s=n(8193);const l=[n(1911),n(119),n(6134),n(6294),n(1043)];var c=n(8328),u=n(6347),d=n(2831),p=n(4848);function f(e){let{children:t}=e;return(0,p.jsx)(p.Fragment,{children:t})}var m=n(5260),h=n(4586),g=n(6025),b=n(6342),v=n(1003),y=n(2131),w=n(4090),x=n(2967),k=n(440),S=n(1463);function _(){const{i18n:{currentLocale:e,defaultLocale:t,localeConfigs:n}}=(0,h.A)(),r=(0,y.o)(),a=n[e].htmlLang,o=e=>e.replace("-","_");return(0,p.jsxs)(m.A,{children:[Object.entries(n).map((e=>{let[t,{htmlLang:n}]=e;return(0,p.jsx)("link",{rel:"alternate",href:r.createUrl({locale:t,fullyQualified:!0}),hrefLang:n},t)})),(0,p.jsx)("link",{rel:"alternate",href:r.createUrl({locale:t,fullyQualified:!0}),hrefLang:"x-default"}),(0,p.jsx)("meta",{property:"og:locale",content:o(a)}),Object.values(n).filter((e=>a!==e.htmlLang)).map((e=>(0,p.jsx)("meta",{property:"og:locale:alternate",content:o(e.htmlLang)},`meta-og-${e.htmlLang}`)))]})}function E(e){let{permalink:t}=e;const{siteConfig:{url:n}}=(0,h.A)(),r=function(){const{siteConfig:{url:e,baseUrl:t,trailingSlash:n}}=(0,h.A)(),{pathname:r}=(0,u.zy)();return e+(0,k.applyTrailingSlash)((0,g.A)(r),{trailingSlash:n,baseUrl:t})}(),a=t?`${n}${t}`:r;return(0,p.jsxs)(m.A,{children:[(0,p.jsx)("meta",{property:"og:url",content:a}),(0,p.jsx)("link",{rel:"canonical",href:a})]})}function C(){const{i18n:{currentLocale:e}}=(0,h.A)(),{metadata:t,image:n}=(0,b.p)();return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)(m.A,{children:[(0,p.jsx)("meta",{name:"twitter:card",content:"summary_large_image"}),(0,p.jsx)("body",{className:w.w})]}),n&&(0,p.jsx)(v.be,{image:n}),(0,p.jsx)(E,{}),(0,p.jsx)(_,{}),(0,p.jsx)(S.A,{tag:x.Cy,locale:e}),(0,p.jsx)(m.A,{children:t.map(((e,t)=>(0,p.jsx)("meta",{...e},t)))})]})}const A=new Map;function T(e){if(A.has(e.pathname))return{...e,pathname:A.get(e.pathname)};if((0,d.u)(c.A,e.pathname).some((e=>{let{route:t}=e;return!0===t.exact})))return A.set(e.pathname,e.pathname),e;const t=e.pathname.trim().replace(/(?:\/index)?\.html$/,"")||"/";return A.set(e.pathname,t),{...e,pathname:t}}var j=n(6125),N=n(6988),L=n(205);function R(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r<t;r++)n[r-1]=arguments[r];const a=l.map((t=>{const r=t.default?.[e]??t[e];return r?.(...n)}));return()=>a.forEach((e=>e?.()))}const P=function(e){let{children:t,location:n,previousLocation:r}=e;return(0,L.A)((()=>{r!==n&&(!function(e){let{location:t,previousLocation:n}=e;if(!n)return;const r=t.pathname===n.pathname,a=t.hash===n.hash,o=t.search===n.search;if(r&&a&&!o)return;const{hash:i}=t;if(i){const e=decodeURIComponent(i.substring(1)),t=document.getElementById(e);t?.scrollIntoView()}else window.scrollTo(0,0)}({location:n,previousLocation:r}),R("onRouteDidUpdate",{previousLocation:r,location:n}))}),[r,n]),t};function O(e){const t=Array.from(new Set([e,decodeURI(e)])).map((e=>(0,d.u)(c.A,e))).flat();return Promise.all(t.map((e=>e.route.component.preload?.())))}class D extends r.Component{previousLocation;routeUpdateCleanupCb;constructor(e){super(e),this.previousLocation=null,this.routeUpdateCleanupCb=s.A.canUseDOM?R("onRouteUpdate",{previousLocation:null,location:this.props.location}):()=>{},this.state={nextRouteHasLoaded:!0}}shouldComponentUpdate(e,t){if(e.location===this.props.location)return t.nextRouteHasLoaded;const n=e.location;return this.previousLocation=this.props.location,this.setState({nextRouteHasLoaded:!1}),this.routeUpdateCleanupCb=R("onRouteUpdate",{previousLocation:this.previousLocation,location:n}),O(n.pathname).then((()=>{this.routeUpdateCleanupCb(),this.setState({nextRouteHasLoaded:!0})})).catch((e=>{console.warn(e),window.location.reload()})),!1}render(){const{children:e,location:t}=this.props;return(0,p.jsx)(P,{previousLocation:this.previousLocation,location:t,children:(0,p.jsx)(u.qh,{location:t,render:()=>e})})}}const I=D,F="__docusaurus-base-url-issue-banner-container",M="__docusaurus-base-url-issue-banner",z="__docusaurus-base-url-issue-banner-suggestion-container";function B(e){return`\ndocument.addEventListener('DOMContentLoaded', function maybeInsertBanner() {\n var shouldInsert = typeof window['docusaurus'] === 'undefined';\n shouldInsert && insertBanner();\n});\n\nfunction insertBanner() {\n var bannerContainer = document.createElement('div');\n bannerContainer.id = '${F}';\n var bannerHtml = ${JSON.stringify(function(e){return`\n<div id="${M}" style="border: thick solid red; background-color: rgb(255, 230, 179); margin: 20px; padding: 20px; font-size: 20px;">\n <p style="font-weight: bold; font-size: 30px;">Your Docusaurus site did not load properly.</p>\n <p>A very common reason is a wrong site <a href="https://docusaurus.io/docs/docusaurus.config.js/#baseUrl" style="font-weight: bold;">baseUrl configuration</a>.</p>\n <p>Current configured baseUrl = <span style="font-weight: bold; color: red;">${e}</span> ${"/"===e?" (default value)":""}</p>\n <p>We suggest trying baseUrl = <span id="${z}" style="font-weight: bold; color: green;"></span></p>\n</div>\n`}(e)).replace(/</g,"\\<")};\n bannerContainer.innerHTML = bannerHtml;\n document.body.prepend(bannerContainer);\n var suggestionContainer = document.getElementById('${z}');\n var actualHomePagePath = window.location.pathname;\n var suggestedBaseUrl = actualHomePagePath.substr(-1) === '/'\n ? actualHomePagePath\n : actualHomePagePath + '/';\n suggestionContainer.innerHTML = suggestedBaseUrl;\n}\n`}function $(){const{siteConfig:{baseUrl:e}}=(0,h.A)();return(0,p.jsx)(p.Fragment,{children:!s.A.canUseDOM&&(0,p.jsx)(m.A,{children:(0,p.jsx)("script",{children:B(e)})})})}function q(){const{siteConfig:{baseUrl:e,baseUrlIssueBanner:t}}=(0,h.A)(),{pathname:n}=(0,u.zy)();return t&&n===e?(0,p.jsx)($,{}):null}function U(){const{siteConfig:{favicon:e,title:t,noIndex:n},i18n:{currentLocale:r,localeConfigs:a}}=(0,h.A)(),o=(0,g.A)(e),{htmlLang:i,direction:s}=a[r];return(0,p.jsxs)(m.A,{children:[(0,p.jsx)("html",{lang:i,dir:s}),(0,p.jsx)("title",{children:t}),(0,p.jsx)("meta",{property:"og:title",content:t}),(0,p.jsx)("meta",{name:"viewport",content:"width=device-width, initial-scale=1.0"}),n&&(0,p.jsx)("meta",{name:"robots",content:"noindex, nofollow"}),e&&(0,p.jsx)("link",{rel:"icon",href:o})]})}var H=n(7489),G=n(2303);function V(){const e=(0,G.A)();return(0,p.jsx)(m.A,{children:(0,p.jsx)("html",{"data-has-hydrated":e})})}function W(){const e=(0,d.v)(c.A),t=(0,u.zy)();return(0,p.jsx)(H.A,{children:(0,p.jsx)(N.l,{children:(0,p.jsxs)(j.x,{children:[(0,p.jsxs)(f,{children:[(0,p.jsx)(U,{}),(0,p.jsx)(C,{}),(0,p.jsx)(q,{}),(0,p.jsx)(I,{location:T(t),children:e})]}),(0,p.jsx)(V,{})]})})})}var Q=n(4054);const K=function(e){try{return document.createElement("link").relList.supports(e)}catch{return!1}}("prefetch")?function(e){return new Promise(((t,n)=>{if("undefined"==typeof document)return void n();const r=document.createElement("link");r.setAttribute("rel","prefetch"),r.setAttribute("href",e),r.onload=()=>t(),r.onerror=()=>n();const a=document.getElementsByTagName("head")[0]??document.getElementsByName("script")[0]?.parentNode;a?.appendChild(r)}))}:function(e){return new Promise(((t,n)=>{const r=new XMLHttpRequest;r.open("GET",e,!0),r.withCredentials=!0,r.onload=()=>{200===r.status?t():n()},r.send(null)}))};var Y=n(6921);const Z=new Set,X=new Set,J=()=>navigator.connection?.effectiveType.includes("2g")||navigator.connection?.saveData,ee={prefetch(e){if(!(e=>!J()&&!X.has(e)&&!Z.has(e))(e))return!1;Z.add(e);const t=(0,d.u)(c.A,e).flatMap((e=>{return t=e.route.path,Object.entries(Q).filter((e=>{let[n]=e;return n.replace(/-[^-]+$/,"")===t})).flatMap((e=>{let[,t]=e;return Object.values((0,Y.A)(t))}));var t}));return Promise.all(t.map((e=>{const t=n.gca(e);return t&&!t.includes("undefined")?K(t).catch((()=>{})):Promise.resolve()})))},preload:e=>!!(e=>!J()&&!X.has(e))(e)&&(X.add(e),O(e))},te=Object.freeze(ee),ne=Boolean(!0);if(s.A.canUseDOM){window.docusaurus=te;const e=document.getElementById("__docusaurus"),t=(0,p.jsx)(i.vd,{children:(0,p.jsx)(o.Kd,{children:(0,p.jsx)(W,{})})}),n=(e,t)=>{console.error("Docusaurus React Root onRecoverableError:",e,t)},s=()=>{if(ne)r.startTransition((()=>{a.hydrateRoot(e,t,{onRecoverableError:n})}));else{const o=a.createRoot(e,{onRecoverableError:n});r.startTransition((()=>{o.render(t)}))}};O(window.location.pathname).then(s)}},6988:(e,t,n)=>{"use strict";n.d(t,{o:()=>d,l:()=>p});var r=n(6540),a=n(4784);const o=JSON.parse('{"docusaurus-plugin-content-docs":{"default":{"path":"/copacetic/website/","versions":[{"name":"current","label":"Next","isLast":false,"path":"/copacetic/website/next","mainDocId":"introduction","docs":[{"id":"best-practices","path":"/copacetic/website/next/best-practices","sidebar":"sidebar"},{"id":"code-of-conduct","path":"/copacetic/website/next/code-of-conduct","sidebar":"sidebar"},{"id":"contributing","path":"/copacetic/website/next/contributing","sidebar":"sidebar"},{"id":"custom-address","path":"/copacetic/website/next/custom-address","sidebar":"sidebar"},{"id":"design","path":"/copacetic/website/next/design","sidebar":"sidebar"},{"id":"development-tips","path":"/copacetic/website/next/development-tips","sidebar":"sidebar"},{"id":"faq","path":"/copacetic/website/next/faq","sidebar":"sidebar"},{"id":"github-action","path":"/copacetic/website/next/github-action","sidebar":"sidebar"},{"id":"installation","path":"/copacetic/website/next/installation","sidebar":"sidebar"},{"id":"introduction","path":"/copacetic/website/next/","sidebar":"sidebar"},{"id":"maintainer-guidelines","path":"/copacetic/website/next/maintainer-guidelines","sidebar":"sidebar"},{"id":"output","path":"/copacetic/website/next/output","sidebar":"sidebar"},{"id":"quick-start","path":"/copacetic/website/next/quick-start","sidebar":"sidebar"},{"id":"release","path":"/copacetic/website/next/release","sidebar":"sidebar"},{"id":"scanner-plugins","path":"/copacetic/website/next/scanner-plugins","sidebar":"sidebar"},{"id":"troubleshooting","path":"/copacetic/website/next/troubleshooting","sidebar":"sidebar"}],"draftIds":[],"sidebars":{"sidebar":{"link":{"path":"/copacetic/website/next/","label":"introduction"}}}},{"name":"v0.6.x","label":"v0.6.x","isLast":true,"path":"/copacetic/website/","mainDocId":"introduction","docs":[{"id":"best-practices","path":"/copacetic/website/best-practices","sidebar":"sidebar"},{"id":"code-of-conduct","path":"/copacetic/website/code-of-conduct","sidebar":"sidebar"},{"id":"contributing","path":"/copacetic/website/contributing","sidebar":"sidebar"},{"id":"custom-address","path":"/copacetic/website/custom-address","sidebar":"sidebar"},{"id":"design","path":"/copacetic/website/design","sidebar":"sidebar"},{"id":"development-tips","path":"/copacetic/website/development-tips","sidebar":"sidebar"},{"id":"faq","path":"/copacetic/website/faq","sidebar":"sidebar"},{"id":"github-action","path":"/copacetic/website/github-action","sidebar":"sidebar"},{"id":"installation","path":"/copacetic/website/installation","sidebar":"sidebar"},{"id":"introduction","path":"/copacetic/website/","sidebar":"sidebar"},{"id":"maintainer-guidelines","path":"/copacetic/website/maintainer-guidelines","sidebar":"sidebar"},{"id":"output","path":"/copacetic/website/output","sidebar":"sidebar"},{"id":"quick-start","path":"/copacetic/website/quick-start","sidebar":"sidebar"},{"id":"release","path":"/copacetic/website/release","sidebar":"sidebar"},{"id":"scanner-plugins","path":"/copacetic/website/scanner-plugins","sidebar":"sidebar"},{"id":"troubleshooting","path":"/copacetic/website/troubleshooting","sidebar":"sidebar"}],"draftIds":[],"sidebars":{"sidebar":{"link":{"path":"/copacetic/website/","label":"introduction"}}}},{"name":"v0.5.x","label":"v0.5.x","isLast":false,"path":"/copacetic/website/v0.5.x","mainDocId":"introduction","docs":[{"id":"code-of-conduct","path":"/copacetic/website/v0.5.x/code-of-conduct","sidebar":"sidebar"},{"id":"contributing","path":"/copacetic/website/v0.5.x/contributing","sidebar":"sidebar"},{"id":"design","path":"/copacetic/website/v0.5.x/design","sidebar":"sidebar"},{"id":"faq","path":"/copacetic/website/v0.5.x/faq","sidebar":"sidebar"},{"id":"github-action","path":"/copacetic/website/v0.5.x/github-action","sidebar":"sidebar"},{"id":"installation","path":"/copacetic/website/v0.5.x/installation","sidebar":"sidebar"},{"id":"introduction","path":"/copacetic/website/v0.5.x/","sidebar":"sidebar"},{"id":"output","path":"/copacetic/website/v0.5.x/output","sidebar":"sidebar"},{"id":"quick-start","path":"/copacetic/website/v0.5.x/quick-start","sidebar":"sidebar"},{"id":"release","path":"/copacetic/website/v0.5.x/release","sidebar":"sidebar"},{"id":"scanner-plugins","path":"/copacetic/website/v0.5.x/scanner-plugins","sidebar":"sidebar"},{"id":"troubleshooting","path":"/copacetic/website/v0.5.x/troubleshooting","sidebar":"sidebar"}],"draftIds":[],"sidebars":{"sidebar":{"link":{"path":"/copacetic/website/v0.5.x/","label":"introduction"}}}},{"name":"v0.4.x","label":"v0.4.x","isLast":false,"path":"/copacetic/website/v0.4.x","mainDocId":"introduction","docs":[{"id":"code-of-conduct","path":"/copacetic/website/v0.4.x/code-of-conduct","sidebar":"sidebar"},{"id":"contributing","path":"/copacetic/website/v0.4.x/contributing","sidebar":"sidebar"},{"id":"design","path":"/copacetic/website/v0.4.x/design","sidebar":"sidebar"},{"id":"faq","path":"/copacetic/website/v0.4.x/faq","sidebar":"sidebar"},{"id":"github-action","path":"/copacetic/website/v0.4.x/github-action","sidebar":"sidebar"},{"id":"installation","path":"/copacetic/website/v0.4.x/installation","sidebar":"sidebar"},{"id":"introduction","path":"/copacetic/website/v0.4.x/","sidebar":"sidebar"},{"id":"quick-start","path":"/copacetic/website/v0.4.x/quick-start","sidebar":"sidebar"},{"id":"release","path":"/copacetic/website/v0.4.x/release","sidebar":"sidebar"},{"id":"troubleshooting","path":"/copacetic/website/v0.4.x/troubleshooting","sidebar":"sidebar"}],"draftIds":[],"sidebars":{"sidebar":{"link":{"path":"/copacetic/website/v0.4.x/","label":"introduction"}}}},{"name":"v0.3.x","label":"v0.3.x","isLast":false,"path":"/copacetic/website/v0.3.x","mainDocId":"introduction","docs":[{"id":"code-of-conduct","path":"/copacetic/website/v0.3.x/code-of-conduct","sidebar":"sidebar"},{"id":"contributing","path":"/copacetic/website/v0.3.x/contributing","sidebar":"sidebar"},{"id":"design","path":"/copacetic/website/v0.3.x/design","sidebar":"sidebar"},{"id":"faq","path":"/copacetic/website/v0.3.x/faq","sidebar":"sidebar"},{"id":"installation","path":"/copacetic/website/v0.3.x/installation","sidebar":"sidebar"},{"id":"introduction","path":"/copacetic/website/v0.3.x/","sidebar":"sidebar"},{"id":"quick-start","path":"/copacetic/website/v0.3.x/quick-start","sidebar":"sidebar"}],"draftIds":[],"sidebars":{"sidebar":{"link":{"path":"/copacetic/website/v0.3.x/","label":"introduction"}}}},{"name":"v0.2.x","label":"v0.2.x","isLast":false,"path":"/copacetic/website/v0.2.x","mainDocId":"introduction","docs":[{"id":"code-of-conduct","path":"/copacetic/website/v0.2.x/code-of-conduct","sidebar":"sidebar"},{"id":"contributing","path":"/copacetic/website/v0.2.x/contributing","sidebar":"sidebar"},{"id":"design","path":"/copacetic/website/v0.2.x/design","sidebar":"sidebar"},{"id":"faq","path":"/copacetic/website/v0.2.x/faq","sidebar":"sidebar"},{"id":"installation","path":"/copacetic/website/v0.2.x/installation","sidebar":"sidebar"},{"id":"introduction","path":"/copacetic/website/v0.2.x/","sidebar":"sidebar"},{"id":"quick-start","path":"/copacetic/website/v0.2.x/quick-start","sidebar":"sidebar"}],"draftIds":[],"sidebars":{"sidebar":{"link":{"path":"/copacetic/website/v0.2.x/","label":"introduction"}}}},{"name":"v0.1.x","label":"v0.1.x","isLast":false,"path":"/copacetic/website/v0.1.x","mainDocId":"introduction","docs":[{"id":"code-of-conduct","path":"/copacetic/website/v0.1.x/code-of-conduct","sidebar":"sidebar"},{"id":"contributing","path":"/copacetic/website/v0.1.x/contributing","sidebar":"sidebar"},{"id":"design","path":"/copacetic/website/v0.1.x/design","sidebar":"sidebar"},{"id":"faq","path":"/copacetic/website/v0.1.x/faq","sidebar":"sidebar"},{"id":"installation","path":"/copacetic/website/v0.1.x/installation","sidebar":"sidebar"},{"id":"introduction","path":"/copacetic/website/v0.1.x/","sidebar":"sidebar"},{"id":"quick-start","path":"/copacetic/website/v0.1.x/quick-start","sidebar":"sidebar"}],"draftIds":[],"sidebars":{"sidebar":{"link":{"path":"/copacetic/website/v0.1.x/","label":"introduction"}}}}],"breadcrumbs":true}},"docusaurus-plugin-google-gtag":{"default":{"trackingID":["G-3RC20QPKNS"],"anonymizeIP":true,"id":"default"}}}'),i=JSON.parse('{"defaultLocale":"en","locales":["en"],"path":"i18n","currentLocale":"en","localeConfigs":{"en":{"label":"English","direction":"ltr","htmlLang":"en","calendar":"gregory","path":"en"}}}');var s=n(2654);const l=JSON.parse('{"docusaurusVersion":"3.2.1","siteVersion":"0.0.0","pluginVersions":{"docusaurus-plugin-content-docs":{"type":"package","name":"@docusaurus/plugin-content-docs","version":"3.2.1"},"docusaurus-plugin-content-pages":{"type":"package","name":"@docusaurus/plugin-content-pages","version":"3.2.1"},"docusaurus-plugin-google-gtag":{"type":"package","name":"@docusaurus/plugin-google-gtag","version":"3.2.1"},"docusaurus-plugin-sitemap":{"type":"package","name":"@docusaurus/plugin-sitemap","version":"3.2.1"},"docusaurus-theme-classic":{"type":"package","name":"@docusaurus/theme-classic","version":"3.2.1"}}}');var c=n(4848);const u={siteConfig:a.A,siteMetadata:l,globalData:o,i18n:i,codeTranslations:s},d=r.createContext(u);function p(e){let{children:t}=e;return(0,c.jsx)(d.Provider,{value:u,children:t})}},7489:(e,t,n)=>{"use strict";n.d(t,{A:()=>h});var r=n(6540),a=n(8193),o=n(5260),i=n(440),s=n(781),l=n(3102),c=n(4848);function u(e){let{error:t,tryAgain:n}=e;return(0,c.jsxs)("div",{style:{display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"flex-start",minHeight:"100vh",width:"100%",maxWidth:"80ch",fontSize:"20px",margin:"0 auto",padding:"1rem"},children:[(0,c.jsx)("h1",{style:{fontSize:"3rem"},children:"This page crashed"}),(0,c.jsx)("button",{type:"button",onClick:n,style:{margin:"1rem 0",fontSize:"2rem",cursor:"pointer",borderRadius:20,padding:"1rem"},children:"Try again"}),(0,c.jsx)(d,{error:t})]})}function d(e){let{error:t}=e;const n=(0,i.getErrorCausalChain)(t).map((e=>e.message)).join("\n\nCause:\n");return(0,c.jsx)("p",{style:{whiteSpace:"pre-wrap"},children:n})}function p(e){let{children:t}=e;return(0,c.jsx)(l.W,{value:{plugin:{name:"docusaurus-core-error-boundary",id:"default"}},children:t})}function f(e){let{error:t,tryAgain:n}=e;return(0,c.jsx)(p,{children:(0,c.jsxs)(h,{fallback:()=>(0,c.jsx)(u,{error:t,tryAgain:n}),children:[(0,c.jsx)(o.A,{children:(0,c.jsx)("title",{children:"Page Error"})}),(0,c.jsx)(s.A,{children:(0,c.jsx)(u,{error:t,tryAgain:n})})]})})}const m=e=>(0,c.jsx)(f,{...e});class h extends r.Component{constructor(e){super(e),this.state={error:null}}componentDidCatch(e){a.A.canUseDOM&&this.setState({error:e})}render(){const{children:e}=this.props,{error:t}=this.state;if(t){const e={error:t,tryAgain:()=>this.setState({error:null})};return(this.props.fallback??m)(e)}return e??null}}},8193:(e,t,n)=>{"use strict";n.d(t,{A:()=>a});const r="undefined"!=typeof window&&"document"in window&&"createElement"in window.document,a={canUseDOM:r,canUseEventListeners:r&&("addEventListener"in window||"attachEvent"in window),canUseIntersectionObserver:r&&"IntersectionObserver"in window,canUseViewport:r&&"screen"in window}},5260:(e,t,n)=>{"use strict";n.d(t,{A:()=>o});n(6540);var r=n(545),a=n(4848);function o(e){return(0,a.jsx)(r.mg,{...e})}},8774:(e,t,n)=>{"use strict";n.d(t,{A:()=>f});var r=n(6540),a=n(4625),o=n(440),i=n(4586),s=n(6654),l=n(8193),c=n(3427),u=n(6025),d=n(4848);function p(e,t){let{isNavLink:n,to:p,href:f,activeClassName:m,isActive:h,"data-noBrokenLinkCheck":g,autoAddBaseUrl:b=!0,...v}=e;const{siteConfig:{trailingSlash:y,baseUrl:w}}=(0,i.A)(),{withBaseUrl:x}=(0,u.h)(),k=(0,c.A)(),S=(0,r.useRef)(null);(0,r.useImperativeHandle)(t,(()=>S.current));const _=p||f;const E=(0,s.A)(_),C=_?.replace("pathname://","");let A=void 0!==C?(T=C,b&&(e=>e.startsWith("/"))(T)?x(T):T):void 0;var T;A&&E&&(A=(0,o.applyTrailingSlash)(A,{trailingSlash:y,baseUrl:w}));const j=(0,r.useRef)(!1),N=n?a.k2:a.N_,L=l.A.canUseIntersectionObserver,R=(0,r.useRef)(),P=()=>{j.current||null==A||(window.docusaurus.preload(A),j.current=!0)};(0,r.useEffect)((()=>(!L&&E&&null!=A&&window.docusaurus.prefetch(A),()=>{L&&R.current&&R.current.disconnect()})),[R,A,L,E]);const O=A?.startsWith("#")??!1,D=!v.target||"_self"===v.target,I=!A||!E||!D||O;return g||!O&&I||k.collectLink(A),v.id&&k.collectAnchor(v.id),I?(0,d.jsx)("a",{ref:S,href:A,..._&&!E&&{target:"_blank",rel:"noopener noreferrer"},...v}):(0,d.jsx)(N,{...v,onMouseEnter:P,onTouchStart:P,innerRef:e=>{S.current=e,L&&e&&E&&(R.current=new window.IntersectionObserver((t=>{t.forEach((t=>{e===t.target&&(t.isIntersecting||t.intersectionRatio>0)&&(R.current.unobserve(e),R.current.disconnect(),null!=A&&window.docusaurus.prefetch(A))}))})),R.current.observe(e))},to:A,...n&&{isActive:h,activeClassName:m}})}const f=r.forwardRef(p)},418:(e,t,n)=>{"use strict";n.d(t,{A:()=>r});const r=()=>null},1312:(e,t,n)=>{"use strict";n.d(t,{A:()=>c,T:()=>l});var r=n(6540),a=n(4848);function o(e,t){const n=e.split(/(\{\w+\})/).map(((e,n)=>{if(n%2==1){const n=t?.[e.slice(1,-1)];if(void 0!==n)return n}return e}));return n.some((e=>(0,r.isValidElement)(e)))?n.map(((e,t)=>(0,r.isValidElement)(e)?r.cloneElement(e,{key:t}):e)).filter((e=>""!==e)):n.join("")}var i=n(2654);function s(e){let{id:t,message:n}=e;if(void 0===t&&void 0===n)throw new Error("Docusaurus translation declarations must have at least a translation id or a default translation message");return i[t??n]??n??t}function l(e,t){let{message:n,id:r}=e;return o(s({message:n,id:r}),t)}function c(e){let{children:t,id:n,values:r}=e;if(t&&"string"!=typeof t)throw console.warn("Illegal <Translate> children",t),new Error("The Docusaurus <Translate> component only accept simple string values");const i=s({message:t,id:n});return(0,a.jsx)(a.Fragment,{children:o(i,r)})}},7065:(e,t,n)=>{"use strict";n.d(t,{W:()=>r});const r="default"},6654:(e,t,n)=>{"use strict";function r(e){return/^(?:\w*:|\/\/)/.test(e)}function a(e){return void 0!==e&&!r(e)}n.d(t,{A:()=>a,z:()=>r})},6025:(e,t,n)=>{"use strict";n.d(t,{A:()=>s,h:()=>i});var r=n(6540),a=n(4586),o=n(6654);function i(){const{siteConfig:{baseUrl:e,url:t}}=(0,a.A)(),n=(0,r.useCallback)(((n,r)=>function(e,t,n,r){let{forcePrependBaseUrl:a=!1,absolute:i=!1}=void 0===r?{}:r;if(!n||n.startsWith("#")||(0,o.z)(n))return n;if(a)return t+n.replace(/^\//,"");if(n===t.replace(/\/$/,""))return t;const s=n.startsWith(t)?n:t+n.replace(/^\//,"");return i?e+s:s}(t,e,n,r)),[t,e]);return{withBaseUrl:n}}function s(e,t){void 0===t&&(t={});const{withBaseUrl:n}=i();return n(e,t)}},3427:(e,t,n)=>{"use strict";n.d(t,{A:()=>i});var r=n(6540);n(4848);const a=r.createContext({collectAnchor:()=>{},collectLink:()=>{}}),o=()=>(0,r.useContext)(a);function i(){return o()}},4586:(e,t,n)=>{"use strict";n.d(t,{A:()=>o});var r=n(6540),a=n(6988);function o(){return(0,r.useContext)(a.o)}},2303:(e,t,n)=>{"use strict";n.d(t,{A:()=>o});var r=n(6540),a=n(6125);function o(){return(0,r.useContext)(a.o)}},205:(e,t,n)=>{"use strict";n.d(t,{A:()=>a});var r=n(6540);const a=n(8193).A.canUseDOM?r.useLayoutEffect:r.useEffect},6921:(e,t,n)=>{"use strict";n.d(t,{A:()=>a});const r=e=>"object"==typeof e&&!!e&&Object.keys(e).length>0;function a(e){const t={};return function e(n,a){Object.entries(n).forEach((n=>{let[o,i]=n;const s=a?`${a}.${o}`:o;r(i)?e(i,s):t[s]=i}))}(e),t}},3102:(e,t,n)=>{"use strict";n.d(t,{W:()=>i,o:()=>o});var r=n(6540),a=n(4848);const o=r.createContext(null);function i(e){let{children:t,value:n}=e;const i=r.useContext(o),s=(0,r.useMemo)((()=>function(e){let{parent:t,value:n}=e;if(!t){if(!n)throw new Error("Unexpected: no Docusaurus route context found");if(!("plugin"in n))throw new Error("Unexpected: Docusaurus topmost route context has no `plugin` attribute");return n}const r={...t.data,...n?.data};return{plugin:t.plugin,data:r}}({parent:i,value:n})),[i,n]);return(0,a.jsx)(o.Provider,{value:s,children:t})}},4070:(e,t,n)=>{"use strict";n.d(t,{zK:()=>h,vT:()=>p,Gy:()=>u,HW:()=>g,ht:()=>d,r7:()=>m,jh:()=>f});var r=n(6347),a=n(4586),o=n(7065);function i(e,t){void 0===t&&(t={});const n=function(){const{globalData:e}=(0,a.A)();return e}()[e];if(!n&&t.failfast)throw new Error(`Docusaurus plugin global data not found for "${e}" plugin.`);return n}const s=e=>e.versions.find((e=>e.isLast));function l(e,t){const n=function(e,t){const n=s(e);return[...e.versions.filter((e=>e!==n)),n].find((e=>!!(0,r.B6)(t,{path:e.path,exact:!1,strict:!1})))}(e,t),a=n?.docs.find((e=>!!(0,r.B6)(t,{path:e.path,exact:!0,strict:!1})));return{activeVersion:n,activeDoc:a,alternateDocVersions:a?function(t){const n={};return e.versions.forEach((e=>{e.docs.forEach((r=>{r.id===t&&(n[e.name]=r)}))})),n}(a.id):{}}}const c={},u=()=>i("docusaurus-plugin-content-docs")??c,d=e=>{try{return function(e,t,n){void 0===t&&(t=o.W),void 0===n&&(n={});const r=i(e),a=r?.[t];if(!a&&n.failfast)throw new Error(`Docusaurus plugin global data not found for "${e}" plugin with id "${t}".`);return a}("docusaurus-plugin-content-docs",e,{failfast:!0})}catch(t){throw new Error("You are using a feature of the Docusaurus docs plugin, but this plugin does not seem to be enabled"+("Default"===e?"":` (pluginId=${e}`),{cause:t})}};function p(e){void 0===e&&(e={});const t=u(),{pathname:n}=(0,r.zy)();return function(e,t,n){void 0===n&&(n={});const a=Object.entries(e).sort(((e,t)=>t[1].path.localeCompare(e[1].path))).find((e=>{let[,n]=e;return!!(0,r.B6)(t,{path:n.path,exact:!1,strict:!1})})),o=a?{pluginId:a[0],pluginData:a[1]}:void 0;if(!o&&n.failfast)throw new Error(`Can't find active docs plugin for "${t}" pathname, while it was expected to be found. Maybe you tried to use a docs feature that can only be used on a docs-related page? Existing docs plugin paths are: ${Object.values(e).map((e=>e.path)).join(", ")}`);return o}(t,n,e)}function f(e){return d(e).versions}function m(e){const t=d(e);return s(t)}function h(e){const t=d(e),{pathname:n}=(0,r.zy)();return l(t,n)}function g(e){const t=d(e),{pathname:n}=(0,r.zy)();return function(e,t){const n=s(e);return{latestDocSuggestion:l(e,t).alternateDocVersions[n.name],latestVersionSuggestion:n}}(t,n)}},1911:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r={onRouteDidUpdate(e){let{location:t,previousLocation:n}=e;!n||t.pathname===n.pathname&&t.search===n.search&&t.hash===n.hash||setTimeout((()=>{window.gtag("set","page_path",t.pathname+t.search+t.hash),window.gtag("event","page_view")}))}}},6294:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(5947),a=n.n(r);a().configure({showSpinner:!1});const o={onRouteUpdate(e){let{location:t,previousLocation:n}=e;if(n&&t.pathname!==n.pathname){const e=window.setTimeout((()=>{a().start()}),200);return()=>window.clearTimeout(e)}},onRouteDidUpdate(){a().done()}}},6134:(e,t,n)=>{"use strict";n.r(t);var r=n(1765),a=n(4784);!function(e){const{themeConfig:{prism:t}}=a.A,{additionalLanguages:r}=t;globalThis.Prism=e,r.forEach((e=>{"php"===e&&n(9700),n(8692)(`./prism-${e}`)})),delete globalThis.Prism}(r.My)},1107:(e,t,n)=>{"use strict";n.d(t,{A:()=>u});n(6540);var r=n(4164),a=n(1312),o=n(6342),i=n(8774),s=n(3427);const l={anchorWithStickyNavbar:"anchorWithStickyNavbar_LWe7",anchorWithHideOnScrollNavbar:"anchorWithHideOnScrollNavbar_WYt5"};var c=n(4848);function u(e){let{as:t,id:n,...u}=e;const d=(0,s.A)(),{navbar:{hideOnScroll:p}}=(0,o.p)();if("h1"===t||!n)return(0,c.jsx)(t,{...u,id:void 0});d.collectAnchor(n);const f=(0,a.T)({id:"theme.common.headingLinkTitle",message:"Direct link to {heading}",description:"Title for link to heading"},{heading:"string"==typeof u.children?u.children:n});return(0,c.jsxs)(t,{...u,className:(0,r.A)("anchor",p?l.anchorWithHideOnScrollNavbar:l.anchorWithStickyNavbar,u.className),id:n,children:[u.children,(0,c.jsx)(i.A,{className:"hash-link",to:`#${n}`,"aria-label":f,title:f,children:"\u200b"})]})}},3186:(e,t,n)=>{"use strict";n.d(t,{A:()=>o});n(6540);const r={iconExternalLink:"iconExternalLink_nPIU"};var a=n(4848);function o(e){let{width:t=13.5,height:n=13.5}=e;return(0,a.jsx)("svg",{width:t,height:n,"aria-hidden":"true",viewBox:"0 0 24 24",className:r.iconExternalLink,children:(0,a.jsx)("path",{fill:"currentColor",d:"M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"})})}},781:(e,t,n)=>{"use strict";n.d(t,{A:()=>ft});var r=n(6540),a=n(4164),o=n(7489),i=n(1003),s=n(6347),l=n(1312),c=n(5062),u=n(4848);const d="__docusaurus_skipToContent_fallback";function p(e){e.setAttribute("tabindex","-1"),e.focus(),e.removeAttribute("tabindex")}function f(){const e=(0,r.useRef)(null),{action:t}=(0,s.W6)(),n=(0,r.useCallback)((e=>{e.preventDefault();const t=document.querySelector("main:first-of-type")??document.getElementById(d);t&&p(t)}),[]);return(0,c.$)((n=>{let{location:r}=n;e.current&&!r.hash&&"PUSH"===t&&p(e.current)})),{containerRef:e,onClick:n}}const m=(0,l.T)({id:"theme.common.skipToMainContent",description:"The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation",message:"Skip to main content"});function h(e){const t=e.children??m,{containerRef:n,onClick:r}=f();return(0,u.jsx)("div",{ref:n,role:"region","aria-label":m,children:(0,u.jsx)("a",{...e,href:`#${d}`,onClick:r,children:t})})}var g=n(7559),b=n(4090);const v={skipToContent:"skipToContent_fXgn"};function y(){return(0,u.jsx)(h,{className:v.skipToContent})}var w=n(6342),x=n(5041);function k(e){let{width:t=21,height:n=21,color:r="currentColor",strokeWidth:a=1.2,className:o,...i}=e;return(0,u.jsx)("svg",{viewBox:"0 0 15 15",width:t,height:n,...i,children:(0,u.jsx)("g",{stroke:r,strokeWidth:a,children:(0,u.jsx)("path",{d:"M.75.75l13.5 13.5M14.25.75L.75 14.25"})})})}const S={closeButton:"closeButton_CVFx"};function _(e){return(0,u.jsx)("button",{type:"button","aria-label":(0,l.T)({id:"theme.AnnouncementBar.closeButtonAriaLabel",message:"Close",description:"The ARIA label for close button of announcement bar"}),...e,className:(0,a.A)("clean-btn close",S.closeButton,e.className),children:(0,u.jsx)(k,{width:14,height:14,strokeWidth:3.1})})}const E={content:"content_knG7"};function C(e){const{announcementBar:t}=(0,w.p)(),{content:n}=t;return(0,u.jsx)("div",{...e,className:(0,a.A)(E.content,e.className),dangerouslySetInnerHTML:{__html:n}})}const A={announcementBar:"announcementBar_mb4j",announcementBarPlaceholder:"announcementBarPlaceholder_vyr4",announcementBarClose:"announcementBarClose_gvF7",announcementBarContent:"announcementBarContent_xLdY"};function T(){const{announcementBar:e}=(0,w.p)(),{isActive:t,close:n}=(0,x.Mj)();if(!t)return null;const{backgroundColor:r,textColor:a,isCloseable:o}=e;return(0,u.jsxs)("div",{className:A.announcementBar,style:{backgroundColor:r,color:a},role:"banner",children:[o&&(0,u.jsx)("div",{className:A.announcementBarPlaceholder}),(0,u.jsx)(C,{className:A.announcementBarContent}),o&&(0,u.jsx)(_,{onClick:n,className:A.announcementBarClose})]})}var j=n(9876),N=n(3104);var L=n(9532),R=n(5600);const P=r.createContext(null);function O(e){let{children:t}=e;const n=function(){const e=(0,j.M)(),t=(0,R.YL)(),[n,a]=(0,r.useState)(!1),o=null!==t.component,i=(0,L.ZC)(o);return(0,r.useEffect)((()=>{o&&!i&&a(!0)}),[o,i]),(0,r.useEffect)((()=>{o?e.shown||a(!0):a(!1)}),[e.shown,o]),(0,r.useMemo)((()=>[n,a]),[n])}();return(0,u.jsx)(P.Provider,{value:n,children:t})}function D(e){if(e.component){const t=e.component;return(0,u.jsx)(t,{...e.props})}}function I(){const e=(0,r.useContext)(P);if(!e)throw new L.dV("NavbarSecondaryMenuDisplayProvider");const[t,n]=e,a=(0,r.useCallback)((()=>n(!1)),[n]),o=(0,R.YL)();return(0,r.useMemo)((()=>({shown:t,hide:a,content:D(o)})),[a,o,t])}function F(e){let{header:t,primaryMenu:n,secondaryMenu:r}=e;const{shown:o}=I();return(0,u.jsxs)("div",{className:"navbar-sidebar",children:[t,(0,u.jsxs)("div",{className:(0,a.A)("navbar-sidebar__items",{"navbar-sidebar__items--show-secondary":o}),children:[(0,u.jsx)("div",{className:"navbar-sidebar__item menu",children:n}),(0,u.jsx)("div",{className:"navbar-sidebar__item menu",children:r})]})]})}var M=n(5293),z=n(2303);function B(e){return(0,u.jsx)("svg",{viewBox:"0 0 24 24",width:24,height:24,...e,children:(0,u.jsx)("path",{fill:"currentColor",d:"M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"})})}function $(e){return(0,u.jsx)("svg",{viewBox:"0 0 24 24",width:24,height:24,...e,children:(0,u.jsx)("path",{fill:"currentColor",d:"M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"})})}const q={toggle:"toggle_vylO",toggleButton:"toggleButton_gllP",darkToggleIcon:"darkToggleIcon_wfgR",lightToggleIcon:"lightToggleIcon_pyhR",toggleButtonDisabled:"toggleButtonDisabled_aARS"};function U(e){let{className:t,buttonClassName:n,value:r,onChange:o}=e;const i=(0,z.A)(),s=(0,l.T)({message:"Switch between dark and light mode (currently {mode})",id:"theme.colorToggle.ariaLabel",description:"The ARIA label for the navbar color mode toggle"},{mode:"dark"===r?(0,l.T)({message:"dark mode",id:"theme.colorToggle.ariaLabel.mode.dark",description:"The name for the dark color mode"}):(0,l.T)({message:"light mode",id:"theme.colorToggle.ariaLabel.mode.light",description:"The name for the light color mode"})});return(0,u.jsx)("div",{className:(0,a.A)(q.toggle,t),children:(0,u.jsxs)("button",{className:(0,a.A)("clean-btn",q.toggleButton,!i&&q.toggleButtonDisabled,n),type:"button",onClick:()=>o("dark"===r?"light":"dark"),disabled:!i,title:s,"aria-label":s,"aria-live":"polite",children:[(0,u.jsx)(B,{className:(0,a.A)(q.toggleIcon,q.lightToggleIcon)}),(0,u.jsx)($,{className:(0,a.A)(q.toggleIcon,q.darkToggleIcon)})]})})}const H=r.memo(U),G={darkNavbarColorModeToggle:"darkNavbarColorModeToggle_X3D1"};function V(e){let{className:t}=e;const n=(0,w.p)().navbar.style,r=(0,w.p)().colorMode.disableSwitch,{colorMode:a,setColorMode:o}=(0,M.G)();return r?null:(0,u.jsx)(H,{className:t,buttonClassName:"dark"===n?G.darkNavbarColorModeToggle:void 0,value:a,onChange:o})}var W=n(3465);function Q(){return(0,u.jsx)(W.A,{className:"navbar__brand",imageClassName:"navbar__logo",titleClassName:"navbar__title text--truncate"})}function K(){const e=(0,j.M)();return(0,u.jsx)("button",{type:"button","aria-label":(0,l.T)({id:"theme.docs.sidebar.closeSidebarButtonAriaLabel",message:"Close navigation bar",description:"The ARIA label for close button of mobile sidebar"}),className:"clean-btn navbar-sidebar__close",onClick:()=>e.toggle(),children:(0,u.jsx)(k,{color:"var(--ifm-color-emphasis-600)"})})}function Y(){return(0,u.jsxs)("div",{className:"navbar-sidebar__brand",children:[(0,u.jsx)(Q,{}),(0,u.jsx)(V,{className:"margin-right--md"}),(0,u.jsx)(K,{})]})}var Z=n(8774),X=n(6025),J=n(6654);function ee(e,t){return void 0!==e&&void 0!==t&&new RegExp(e,"gi").test(t)}var te=n(3186);function ne(e){let{activeBasePath:t,activeBaseRegex:n,to:r,href:a,label:o,html:i,isDropdownLink:s,prependBaseUrlToHref:l,...c}=e;const d=(0,X.A)(r),p=(0,X.A)(t),f=(0,X.A)(a,{forcePrependBaseUrl:!0}),m=o&&a&&!(0,J.A)(a),h=i?{dangerouslySetInnerHTML:{__html:i}}:{children:(0,u.jsxs)(u.Fragment,{children:[o,m&&(0,u.jsx)(te.A,{...s&&{width:12,height:12}})]})};return a?(0,u.jsx)(Z.A,{href:l?f:a,...c,...h}):(0,u.jsx)(Z.A,{to:d,isNavLink:!0,...(t||n)&&{isActive:(e,t)=>n?ee(n,t.pathname):t.pathname.startsWith(p)},...c,...h})}function re(e){let{className:t,isDropdownItem:n=!1,...r}=e;const o=(0,u.jsx)(ne,{className:(0,a.A)(n?"dropdown__link":"navbar__item navbar__link",t),isDropdownLink:n,...r});return n?(0,u.jsx)("li",{children:o}):o}function ae(e){let{className:t,isDropdownItem:n,...r}=e;return(0,u.jsx)("li",{className:"menu__list-item",children:(0,u.jsx)(ne,{className:(0,a.A)("menu__link",t),...r})})}function oe(e){let{mobile:t=!1,position:n,...r}=e;const a=t?ae:re;return(0,u.jsx)(a,{...r,activeClassName:r.activeClassName??(t?"menu__link--active":"navbar__link--active")})}var ie=n(1422),se=n(9169),le=n(4586);const ce={dropdownNavbarItemMobile:"dropdownNavbarItemMobile_S0Fm"};function ue(e,t){return e.some((e=>function(e,t){return!!(0,se.ys)(e.to,t)||!!ee(e.activeBaseRegex,t)||!(!e.activeBasePath||!t.startsWith(e.activeBasePath))}(e,t)))}function de(e){let{items:t,position:n,className:o,onClick:i,...s}=e;const l=(0,r.useRef)(null),[c,d]=(0,r.useState)(!1);return(0,r.useEffect)((()=>{const e=e=>{l.current&&!l.current.contains(e.target)&&d(!1)};return document.addEventListener("mousedown",e),document.addEventListener("touchstart",e),document.addEventListener("focusin",e),()=>{document.removeEventListener("mousedown",e),document.removeEventListener("touchstart",e),document.removeEventListener("focusin",e)}}),[l]),(0,u.jsxs)("div",{ref:l,className:(0,a.A)("navbar__item","dropdown","dropdown--hoverable",{"dropdown--right":"right"===n,"dropdown--show":c}),children:[(0,u.jsx)(ne,{"aria-haspopup":"true","aria-expanded":c,role:"button",href:s.to?void 0:"#",className:(0,a.A)("navbar__link",o),...s,onClick:s.to?void 0:e=>e.preventDefault(),onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),d(!c))},children:s.children??s.label}),(0,u.jsx)("ul",{className:"dropdown__menu",children:t.map(((e,t)=>(0,r.createElement)(Ee,{isDropdownItem:!0,activeClassName:"dropdown__link--active",...e,key:t})))})]})}function pe(e){let{items:t,className:n,position:o,onClick:i,...l}=e;const c=function(){const{siteConfig:{baseUrl:e}}=(0,le.A)(),{pathname:t}=(0,s.zy)();return t.replace(e,"/")}(),d=ue(t,c),{collapsed:p,toggleCollapsed:f,setCollapsed:m}=(0,ie.u)({initialState:()=>!d});return(0,r.useEffect)((()=>{d&&m(!d)}),[c,d,m]),(0,u.jsxs)("li",{className:(0,a.A)("menu__list-item",{"menu__list-item--collapsed":p}),children:[(0,u.jsx)(ne,{role:"button",className:(0,a.A)(ce.dropdownNavbarItemMobile,"menu__link menu__link--sublist menu__link--sublist-caret",n),...l,onClick:e=>{e.preventDefault(),f()},children:l.children??l.label}),(0,u.jsx)(ie.N,{lazy:!0,as:"ul",className:"menu__list",collapsed:p,children:t.map(((e,t)=>(0,r.createElement)(Ee,{mobile:!0,isDropdownItem:!0,onClick:i,activeClassName:"menu__link--active",...e,key:t})))})]})}function fe(e){let{mobile:t=!1,...n}=e;const r=t?pe:de;return(0,u.jsx)(r,{...n})}var me=n(2131);function he(e){let{width:t=20,height:n=20,...r}=e;return(0,u.jsx)("svg",{viewBox:"0 0 24 24",width:t,height:n,"aria-hidden":!0,...r,children:(0,u.jsx)("path",{fill:"currentColor",d:"M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"})})}const ge="iconLanguage_nlXk";var be=n(418);const ve={navbarSearchContainer:"navbarSearchContainer_Bca1"};function ye(e){let{children:t,className:n}=e;return(0,u.jsx)("div",{className:(0,a.A)(n,ve.navbarSearchContainer),children:t})}var we=n(4070),xe=n(1754);var ke=n(5597);const Se=e=>e.docs.find((t=>t.id===e.mainDocId));const _e={default:oe,localeDropdown:function(e){let{mobile:t,dropdownItemsBefore:n,dropdownItemsAfter:r,queryString:a="",...o}=e;const{i18n:{currentLocale:i,locales:c,localeConfigs:d}}=(0,le.A)(),p=(0,me.o)(),{search:f,hash:m}=(0,s.zy)(),h=[...n,...c.map((e=>{const n=`${`pathname://${p.createUrl({locale:e,fullyQualified:!1})}`}${f}${m}${a}`;return{label:d[e].label,lang:d[e].htmlLang,to:n,target:"_self",autoAddBaseUrl:!1,className:e===i?t?"menu__link--active":"dropdown__link--active":""}})),...r],g=t?(0,l.T)({message:"Languages",id:"theme.navbar.mobileLanguageDropdown.label",description:"The label for the mobile language switcher dropdown"}):d[i].label;return(0,u.jsx)(fe,{...o,mobile:t,label:(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(he,{className:ge}),g]}),items:h})},search:function(e){let{mobile:t,className:n}=e;return t?null:(0,u.jsx)(ye,{className:n,children:(0,u.jsx)(be.A,{})})},dropdown:fe,html:function(e){let{value:t,className:n,mobile:r=!1,isDropdownItem:o=!1}=e;const i=o?"li":"div";return(0,u.jsx)(i,{className:(0,a.A)({navbar__item:!r&&!o,"menu__list-item":r},n),dangerouslySetInnerHTML:{__html:t}})},doc:function(e){let{docId:t,label:n,docsPluginId:r,...a}=e;const{activeDoc:o}=(0,we.zK)(r),i=(0,xe.QB)(t,r),s=o?.path===i?.path;return null===i||i.unlisted&&!s?null:(0,u.jsx)(oe,{exact:!0,...a,isActive:()=>s||!!o?.sidebar&&o.sidebar===i.sidebar,label:n??i.id,to:i.path})},docSidebar:function(e){let{sidebarId:t,label:n,docsPluginId:r,...a}=e;const{activeDoc:o}=(0,we.zK)(r),i=(0,xe.fW)(t,r).link;if(!i)throw new Error(`DocSidebarNavbarItem: Sidebar with ID "${t}" doesn't have anything to be linked to.`);return(0,u.jsx)(oe,{exact:!0,...a,isActive:()=>o?.sidebar===t,label:n??i.label,to:i.path})},docsVersion:function(e){let{label:t,to:n,docsPluginId:r,...a}=e;const o=(0,xe.Vd)(r)[0],i=t??o.label,s=n??(e=>e.docs.find((t=>t.id===e.mainDocId)))(o).path;return(0,u.jsx)(oe,{...a,label:i,to:s})},docsVersionDropdown:function(e){let{mobile:t,docsPluginId:n,dropdownActiveClassDisabled:r,dropdownItemsBefore:a,dropdownItemsAfter:o,...i}=e;const{search:c,hash:d}=(0,s.zy)(),p=(0,we.zK)(n),f=(0,we.jh)(n),{savePreferredVersionName:m}=(0,ke.g1)(n),h=[...a,...f.map((e=>{const t=p.alternateDocVersions[e.name]??Se(e);return{label:e.label,to:`${t.path}${c}${d}`,isActive:()=>e===p.activeVersion,onClick:()=>m(e.name)}})),...o],g=(0,xe.Vd)(n)[0],b=t&&h.length>1?(0,l.T)({id:"theme.navbar.mobileVersionsDropdown.label",message:"Versions",description:"The label for the navbar versions dropdown on mobile view"}):g.label,v=t&&h.length>1?void 0:Se(g).path;return h.length<=1?(0,u.jsx)(oe,{...i,mobile:t,label:b,to:v,isActive:r?()=>!1:void 0}):(0,u.jsx)(fe,{...i,mobile:t,label:b,to:v,items:h,isActive:r?()=>!1:void 0})}};function Ee(e){let{type:t,...n}=e;const r=function(e,t){return e&&"default"!==e?e:"items"in t?"dropdown":"default"}(t,n),a=_e[r];if(!a)throw new Error(`No NavbarItem component found for type "${t}".`);return(0,u.jsx)(a,{...n})}function Ce(){const e=(0,j.M)(),t=(0,w.p)().navbar.items;return(0,u.jsx)("ul",{className:"menu__list",children:t.map(((t,n)=>(0,r.createElement)(Ee,{mobile:!0,...t,onClick:()=>e.toggle(),key:n})))})}function Ae(e){return(0,u.jsx)("button",{...e,type:"button",className:"clean-btn navbar-sidebar__back",children:(0,u.jsx)(l.A,{id:"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel",description:"The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)",children:"\u2190 Back to main menu"})})}function Te(){const e=0===(0,w.p)().navbar.items.length,t=I();return(0,u.jsxs)(u.Fragment,{children:[!e&&(0,u.jsx)(Ae,{onClick:()=>t.hide()}),t.content]})}function je(){const e=(0,j.M)();var t;return void 0===(t=e.shown)&&(t=!0),(0,r.useEffect)((()=>(document.body.style.overflow=t?"hidden":"visible",()=>{document.body.style.overflow="visible"})),[t]),e.shouldRender?(0,u.jsx)(F,{header:(0,u.jsx)(Y,{}),primaryMenu:(0,u.jsx)(Ce,{}),secondaryMenu:(0,u.jsx)(Te,{})}):null}const Ne={navbarHideable:"navbarHideable_m1mJ",navbarHidden:"navbarHidden_jGov"};function Le(e){return(0,u.jsx)("div",{role:"presentation",...e,className:(0,a.A)("navbar-sidebar__backdrop",e.className)})}function Re(e){let{children:t}=e;const{navbar:{hideOnScroll:n,style:o}}=(0,w.p)(),i=(0,j.M)(),{navbarRef:s,isNavbarVisible:d}=function(e){const[t,n]=(0,r.useState)(e),a=(0,r.useRef)(!1),o=(0,r.useRef)(0),i=(0,r.useCallback)((e=>{null!==e&&(o.current=e.getBoundingClientRect().height)}),[]);return(0,N.Mq)(((t,r)=>{let{scrollY:i}=t;if(!e)return;if(i<o.current)return void n(!0);if(a.current)return void(a.current=!1);const s=r?.scrollY,l=document.documentElement.scrollHeight-o.current,c=window.innerHeight;s&&i>=s?n(!1):i+c<l&&n(!0)})),(0,c.$)((t=>{if(!e)return;const r=t.location.hash;if(r?document.getElementById(r.substring(1)):void 0)return a.current=!0,void n(!1);n(!0)})),{navbarRef:i,isNavbarVisible:t}}(n);return(0,u.jsxs)("nav",{ref:s,"aria-label":(0,l.T)({id:"theme.NavBar.navAriaLabel",message:"Main",description:"The ARIA label for the main navigation"}),className:(0,a.A)("navbar","navbar--fixed-top",n&&[Ne.navbarHideable,!d&&Ne.navbarHidden],{"navbar--dark":"dark"===o,"navbar--primary":"primary"===o,"navbar-sidebar--show":i.shown}),children:[t,(0,u.jsx)(Le,{onClick:i.toggle}),(0,u.jsx)(je,{})]})}var Pe=n(440);const Oe={errorBoundaryError:"errorBoundaryError_a6uf",errorBoundaryFallback:"errorBoundaryFallback_VBag"};function De(e){return(0,u.jsx)("button",{type:"button",...e,children:(0,u.jsx)(l.A,{id:"theme.ErrorPageContent.tryAgain",description:"The label of the button to try again rendering when the React error boundary captures an error",children:"Try again"})})}function Ie(e){let{error:t}=e;const n=(0,Pe.getErrorCausalChain)(t).map((e=>e.message)).join("\n\nCause:\n");return(0,u.jsx)("p",{className:Oe.errorBoundaryError,children:n})}class Fe extends r.Component{componentDidCatch(e,t){throw this.props.onError(e,t)}render(){return this.props.children}}const Me="right";function ze(e){let{width:t=30,height:n=30,className:r,...a}=e;return(0,u.jsx)("svg",{className:r,width:t,height:n,viewBox:"0 0 30 30","aria-hidden":"true",...a,children:(0,u.jsx)("path",{stroke:"currentColor",strokeLinecap:"round",strokeMiterlimit:"10",strokeWidth:"2",d:"M4 7h22M4 15h22M4 23h22"})})}function Be(){const{toggle:e,shown:t}=(0,j.M)();return(0,u.jsx)("button",{onClick:e,"aria-label":(0,l.T)({id:"theme.docs.sidebar.toggleSidebarButtonAriaLabel",message:"Toggle navigation bar",description:"The ARIA label for hamburger menu button of mobile navigation"}),"aria-expanded":t,className:"navbar__toggle clean-btn",type:"button",children:(0,u.jsx)(ze,{})})}const $e={colorModeToggle:"colorModeToggle_DEke"};function qe(e){let{items:t}=e;return(0,u.jsx)(u.Fragment,{children:t.map(((e,t)=>(0,u.jsx)(Fe,{onError:t=>new Error(`A theme navbar item failed to render.\nPlease double-check the following navbar item (themeConfig.navbar.items) of your Docusaurus config:\n${JSON.stringify(e,null,2)}`,{cause:t}),children:(0,u.jsx)(Ee,{...e})},t)))})}function Ue(e){let{left:t,right:n}=e;return(0,u.jsxs)("div",{className:"navbar__inner",children:[(0,u.jsx)("div",{className:"navbar__items",children:t}),(0,u.jsx)("div",{className:"navbar__items navbar__items--right",children:n})]})}function He(){const e=(0,j.M)(),t=(0,w.p)().navbar.items,[n,r]=function(e){function t(e){return"left"===(e.position??Me)}return[e.filter(t),e.filter((e=>!t(e)))]}(t),a=t.find((e=>"search"===e.type));return(0,u.jsx)(Ue,{left:(0,u.jsxs)(u.Fragment,{children:[!e.disabled&&(0,u.jsx)(Be,{}),(0,u.jsx)(Q,{}),(0,u.jsx)(qe,{items:n})]}),right:(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(qe,{items:r}),(0,u.jsx)(V,{className:$e.colorModeToggle}),!a&&(0,u.jsx)(ye,{children:(0,u.jsx)(be.A,{})})]})})}function Ge(){return(0,u.jsx)(Re,{children:(0,u.jsx)(He,{})})}function Ve(e){let{item:t}=e;const{to:n,href:r,label:a,prependBaseUrlToHref:o,...i}=t,s=(0,X.A)(n),l=(0,X.A)(r,{forcePrependBaseUrl:!0});return(0,u.jsxs)(Z.A,{className:"footer__link-item",...r?{href:o?l:r}:{to:s},...i,children:[a,r&&!(0,J.A)(r)&&(0,u.jsx)(te.A,{})]})}function We(e){let{item:t}=e;return t.html?(0,u.jsx)("li",{className:"footer__item",dangerouslySetInnerHTML:{__html:t.html}}):(0,u.jsx)("li",{className:"footer__item",children:(0,u.jsx)(Ve,{item:t})},t.href??t.to)}function Qe(e){let{column:t}=e;return(0,u.jsxs)("div",{className:"col footer__col",children:[(0,u.jsx)("div",{className:"footer__title",children:t.title}),(0,u.jsx)("ul",{className:"footer__items clean-list",children:t.items.map(((e,t)=>(0,u.jsx)(We,{item:e},t)))})]})}function Ke(e){let{columns:t}=e;return(0,u.jsx)("div",{className:"row footer__links",children:t.map(((e,t)=>(0,u.jsx)(Qe,{column:e},t)))})}function Ye(){return(0,u.jsx)("span",{className:"footer__link-separator",children:"\xb7"})}function Ze(e){let{item:t}=e;return t.html?(0,u.jsx)("span",{className:"footer__link-item",dangerouslySetInnerHTML:{__html:t.html}}):(0,u.jsx)(Ve,{item:t})}function Xe(e){let{links:t}=e;return(0,u.jsx)("div",{className:"footer__links text--center",children:(0,u.jsx)("div",{className:"footer__links",children:t.map(((e,n)=>(0,u.jsxs)(r.Fragment,{children:[(0,u.jsx)(Ze,{item:e}),t.length!==n+1&&(0,u.jsx)(Ye,{})]},n)))})})}function Je(e){let{links:t}=e;return function(e){return"title"in e[0]}(t)?(0,u.jsx)(Ke,{columns:t}):(0,u.jsx)(Xe,{links:t})}var et=n(1122);const tt={footerLogoLink:"footerLogoLink_BH7S"};function nt(e){let{logo:t}=e;const{withBaseUrl:n}=(0,X.h)(),r={light:n(t.src),dark:n(t.srcDark??t.src)};return(0,u.jsx)(et.A,{className:(0,a.A)("footer__logo",t.className),alt:t.alt,sources:r,width:t.width,height:t.height,style:t.style})}function rt(e){let{logo:t}=e;return t.href?(0,u.jsx)(Z.A,{href:t.href,className:tt.footerLogoLink,target:t.target,children:(0,u.jsx)(nt,{logo:t})}):(0,u.jsx)(nt,{logo:t})}function at(e){let{copyright:t}=e;return(0,u.jsx)("div",{className:"footer__copyright",dangerouslySetInnerHTML:{__html:t}})}function ot(e){let{style:t,links:n,logo:r,copyright:o}=e;return(0,u.jsx)("footer",{className:(0,a.A)("footer",{"footer--dark":"dark"===t}),children:(0,u.jsxs)("div",{className:"container container-fluid",children:[n,(r||o)&&(0,u.jsxs)("div",{className:"footer__bottom text--center",children:[r&&(0,u.jsx)("div",{className:"margin-bottom--sm",children:r}),o]})]})})}function it(){const{footer:e}=(0,w.p)();if(!e)return null;const{copyright:t,links:n,logo:r,style:a}=e;return(0,u.jsx)(ot,{style:a,links:n&&n.length>0&&(0,u.jsx)(Je,{links:n}),logo:r&&(0,u.jsx)(rt,{logo:r}),copyright:t&&(0,u.jsx)(at,{copyright:t})})}const st=r.memo(it),lt=(0,L.fM)([M.a,x.oq,N.Tv,ke.VQ,i.Jx,function(e){let{children:t}=e;return(0,u.jsx)(R.y_,{children:(0,u.jsx)(j.e,{children:(0,u.jsx)(O,{children:t})})})}]);function ct(e){let{children:t}=e;return(0,u.jsx)(lt,{children:t})}var ut=n(1107);function dt(e){let{error:t,tryAgain:n}=e;return(0,u.jsx)("main",{className:"container margin-vert--xl",children:(0,u.jsx)("div",{className:"row",children:(0,u.jsxs)("div",{className:"col col--6 col--offset-3",children:[(0,u.jsx)(ut.A,{as:"h1",className:"hero__title",children:(0,u.jsx)(l.A,{id:"theme.ErrorPageContent.title",description:"The title of the fallback page when the page crashed",children:"This page crashed."})}),(0,u.jsx)("div",{className:"margin-vert--lg",children:(0,u.jsx)(De,{onClick:n,className:"button button--primary shadow--lw"})}),(0,u.jsx)("hr",{}),(0,u.jsx)("div",{className:"margin-vert--md",children:(0,u.jsx)(Ie,{error:t})})]})})})}const pt={mainWrapper:"mainWrapper_z2l0"};function ft(e){const{children:t,noFooter:n,wrapperClassName:r,title:s,description:l}=e;return(0,b.J)(),(0,u.jsxs)(ct,{children:[(0,u.jsx)(i.be,{title:s,description:l}),(0,u.jsx)(y,{}),(0,u.jsx)(T,{}),(0,u.jsx)(Ge,{}),(0,u.jsx)("div",{id:d,className:(0,a.A)(g.G.wrapper.main,pt.mainWrapper,r),children:(0,u.jsx)(o.A,{fallback:e=>(0,u.jsx)(dt,{...e}),children:t})}),!n&&(0,u.jsx)(st,{})]})}},3465:(e,t,n)=>{"use strict";n.d(t,{A:()=>u});n(6540);var r=n(8774),a=n(6025),o=n(4586),i=n(6342),s=n(1122),l=n(4848);function c(e){let{logo:t,alt:n,imageClassName:r}=e;const o={light:(0,a.A)(t.src),dark:(0,a.A)(t.srcDark||t.src)},i=(0,l.jsx)(s.A,{className:t.className,sources:o,height:t.height,width:t.width,alt:n,style:t.style});return r?(0,l.jsx)("div",{className:r,children:i}):i}function u(e){const{siteConfig:{title:t}}=(0,o.A)(),{navbar:{title:n,logo:s}}=(0,i.p)(),{imageClassName:u,titleClassName:d,...p}=e,f=(0,a.A)(s?.href||"/"),m=n?"":t,h=s?.alt??m;return(0,l.jsxs)(r.A,{to:f,...p,...s?.target&&{target:s.target},children:[s&&(0,l.jsx)(c,{logo:s,alt:h,imageClassName:u}),null!=n&&(0,l.jsx)("b",{className:d,children:n})]})}},1463:(e,t,n)=>{"use strict";n.d(t,{A:()=>o});n(6540);var r=n(5260),a=n(4848);function o(e){let{locale:t,version:n,tag:o}=e;const i=t;return(0,a.jsxs)(r.A,{children:[t&&(0,a.jsx)("meta",{name:"docusaurus_locale",content:t}),n&&(0,a.jsx)("meta",{name:"docusaurus_version",content:n}),o&&(0,a.jsx)("meta",{name:"docusaurus_tag",content:o}),i&&(0,a.jsx)("meta",{name:"docsearch:language",content:i}),n&&(0,a.jsx)("meta",{name:"docsearch:version",content:n}),o&&(0,a.jsx)("meta",{name:"docsearch:docusaurus_tag",content:o})]})}},1122:(e,t,n)=>{"use strict";n.d(t,{A:()=>u});var r=n(6540),a=n(4164),o=n(2303),i=n(5293);const s={themedComponent:"themedComponent_mlkZ","themedComponent--light":"themedComponent--light_NVdE","themedComponent--dark":"themedComponent--dark_xIcU"};var l=n(4848);function c(e){let{className:t,children:n}=e;const c=(0,o.A)(),{colorMode:u}=(0,i.G)();return(0,l.jsx)(l.Fragment,{children:(c?"dark"===u?["dark"]:["light"]:["light","dark"]).map((e=>{const o=n({theme:e,className:(0,a.A)(t,s.themedComponent,s[`themedComponent--${e}`])});return(0,l.jsx)(r.Fragment,{children:o},e)}))})}function u(e){const{sources:t,className:n,alt:r,...a}=e;return(0,l.jsx)(c,{className:n,children:e=>{let{theme:n,className:o}=e;return(0,l.jsx)("img",{src:t[n],alt:r,className:o,...a})}})}},1422:(e,t,n)=>{"use strict";n.d(t,{N:()=>b,u:()=>c});var r=n(6540),a=n(8193),o=n(205),i=n(3109),s=n(4848);const l="ease-in-out";function c(e){let{initialState:t}=e;const[n,a]=(0,r.useState)(t??!1),o=(0,r.useCallback)((()=>{a((e=>!e))}),[]);return{collapsed:n,setCollapsed:a,toggleCollapsed:o}}const u={display:"none",overflow:"hidden",height:"0px"},d={display:"block",overflow:"visible",height:"auto"};function p(e,t){const n=t?u:d;e.style.display=n.display,e.style.overflow=n.overflow,e.style.height=n.height}function f(e){let{collapsibleRef:t,collapsed:n,animation:a}=e;const o=(0,r.useRef)(!1);(0,r.useEffect)((()=>{const e=t.current;function r(){const t=e.scrollHeight,n=a?.duration??function(e){if((0,i.O)())return 1;const t=e/36;return Math.round(10*(4+15*t**.25+t/5))}(t);return{transition:`height ${n}ms ${a?.easing??l}`,height:`${t}px`}}function s(){const t=r();e.style.transition=t.transition,e.style.height=t.height}if(!o.current)return p(e,n),void(o.current=!0);return e.style.willChange="height",function(){const t=requestAnimationFrame((()=>{n?(s(),requestAnimationFrame((()=>{e.style.height=u.height,e.style.overflow=u.overflow}))):(e.style.display="block",requestAnimationFrame((()=>{s()})))}));return()=>cancelAnimationFrame(t)}()}),[t,n,a])}function m(e){if(!a.A.canUseDOM)return e?u:d}function h(e){let{as:t="div",collapsed:n,children:a,animation:o,onCollapseTransitionEnd:i,className:l,disableSSRStyle:c}=e;const u=(0,r.useRef)(null);return f({collapsibleRef:u,collapsed:n,animation:o}),(0,s.jsx)(t,{ref:u,style:c?void 0:m(n),onTransitionEnd:e=>{"height"===e.propertyName&&(p(u.current,n),i?.(n))},className:l,children:a})}function g(e){let{collapsed:t,...n}=e;const[a,i]=(0,r.useState)(!t),[l,c]=(0,r.useState)(t);return(0,o.A)((()=>{t||i(!0)}),[t]),(0,o.A)((()=>{a&&c(t)}),[a,t]),a?(0,s.jsx)(h,{...n,collapsed:l}):null}function b(e){let{lazy:t,...n}=e;const r=t?g:h;return(0,s.jsx)(r,{...n})}},5041:(e,t,n)=>{"use strict";n.d(t,{Mj:()=>h,oq:()=>m});var r=n(6540),a=n(2303),o=n(9466),i=n(9532),s=n(6342),l=n(4848);const c=(0,o.Wf)("docusaurus.announcement.dismiss"),u=(0,o.Wf)("docusaurus.announcement.id"),d=()=>"true"===c.get(),p=e=>c.set(String(e)),f=r.createContext(null);function m(e){let{children:t}=e;const n=function(){const{announcementBar:e}=(0,s.p)(),t=(0,a.A)(),[n,o]=(0,r.useState)((()=>!!t&&d()));(0,r.useEffect)((()=>{o(d())}),[]);const i=(0,r.useCallback)((()=>{p(!0),o(!0)}),[]);return(0,r.useEffect)((()=>{if(!e)return;const{id:t}=e;let n=u.get();"annoucement-bar"===n&&(n="announcement-bar");const r=t!==n;u.set(t),r&&p(!1),!r&&d()||o(!1)}),[e]),(0,r.useMemo)((()=>({isActive:!!e&&!n,close:i})),[e,n,i])}();return(0,l.jsx)(f.Provider,{value:n,children:t})}function h(){const e=(0,r.useContext)(f);if(!e)throw new i.dV("AnnouncementBarProvider");return e}},5293:(e,t,n)=>{"use strict";n.d(t,{G:()=>b,a:()=>g});var r=n(6540),a=n(8193),o=n(9532),i=n(9466),s=n(6342),l=n(4848);const c=r.createContext(void 0),u="theme",d=(0,i.Wf)(u),p={light:"light",dark:"dark"},f=e=>e===p.dark?p.dark:p.light,m=e=>a.A.canUseDOM?f(document.documentElement.getAttribute("data-theme")):f(e),h=e=>{d.set(f(e))};function g(e){let{children:t}=e;const n=function(){const{colorMode:{defaultMode:e,disableSwitch:t,respectPrefersColorScheme:n}}=(0,s.p)(),[a,o]=(0,r.useState)(m(e));(0,r.useEffect)((()=>{t&&d.del()}),[t]);const i=(0,r.useCallback)((function(t,r){void 0===r&&(r={});const{persist:a=!0}=r;t?(o(t),a&&h(t)):(o(n?window.matchMedia("(prefers-color-scheme: dark)").matches?p.dark:p.light:e),d.del())}),[n,e]);(0,r.useEffect)((()=>{document.documentElement.setAttribute("data-theme",f(a))}),[a]),(0,r.useEffect)((()=>{if(t)return;const e=e=>{if(e.key!==u)return;const t=d.get();null!==t&&i(f(t))};return window.addEventListener("storage",e),()=>window.removeEventListener("storage",e)}),[t,i]);const l=(0,r.useRef)(!1);return(0,r.useEffect)((()=>{if(t&&!n)return;const e=window.matchMedia("(prefers-color-scheme: dark)"),r=()=>{window.matchMedia("print").matches||l.current?l.current=window.matchMedia("print").matches:i(null)};return e.addListener(r),()=>e.removeListener(r)}),[i,t,n]),(0,r.useMemo)((()=>({colorMode:a,setColorMode:i,get isDarkTheme(){return a===p.dark},setLightTheme(){i(p.light)},setDarkTheme(){i(p.dark)}})),[a,i])}();return(0,l.jsx)(c.Provider,{value:n,children:t})}function b(){const e=(0,r.useContext)(c);if(null==e)throw new o.dV("ColorModeProvider","Please see https://docusaurus.io/docs/api/themes/configuration#use-color-mode.");return e}},5597:(e,t,n)=>{"use strict";n.d(t,{VQ:()=>b,g1:()=>y});var r=n(6540),a=n(4070),o=n(7065),i=n(6342),s=n(1754),l=n(9532),c=n(9466),u=n(4848);const d=e=>`docs-preferred-version-${e}`,p={save:(e,t,n)=>{(0,c.Wf)(d(e),{persistence:t}).set(n)},read:(e,t)=>(0,c.Wf)(d(e),{persistence:t}).get(),clear:(e,t)=>{(0,c.Wf)(d(e),{persistence:t}).del()}},f=e=>Object.fromEntries(e.map((e=>[e,{preferredVersionName:null}])));const m=r.createContext(null);function h(){const e=(0,a.Gy)(),t=(0,i.p)().docs.versionPersistence,n=(0,r.useMemo)((()=>Object.keys(e)),[e]),[o,s]=(0,r.useState)((()=>f(n)));(0,r.useEffect)((()=>{s(function(e){let{pluginIds:t,versionPersistence:n,allDocsData:r}=e;function a(e){const t=p.read(e,n);return r[e].versions.some((e=>e.name===t))?{preferredVersionName:t}:(p.clear(e,n),{preferredVersionName:null})}return Object.fromEntries(t.map((e=>[e,a(e)])))}({allDocsData:e,versionPersistence:t,pluginIds:n}))}),[e,t,n]);return[o,(0,r.useMemo)((()=>({savePreferredVersion:function(e,n){p.save(e,t,n),s((t=>({...t,[e]:{preferredVersionName:n}})))}})),[t])]}function g(e){let{children:t}=e;const n=h();return(0,u.jsx)(m.Provider,{value:n,children:t})}function b(e){let{children:t}=e;return s.C5?(0,u.jsx)(g,{children:t}):(0,u.jsx)(u.Fragment,{children:t})}function v(){const e=(0,r.useContext)(m);if(!e)throw new l.dV("DocsPreferredVersionContextProvider");return e}function y(e){void 0===e&&(e=o.W);const t=(0,a.ht)(e),[n,i]=v(),{preferredVersionName:s}=n[e];return{preferredVersion:t.versions.find((e=>e.name===s))??null,savePreferredVersionName:(0,r.useCallback)((t=>{i.savePreferredVersion(e,t)}),[i,e])}}},6588:(e,t,n)=>{"use strict";n.d(t,{V:()=>l,t:()=>c});var r=n(6540),a=n(9532),o=n(4848);const i=Symbol("EmptyContext"),s=r.createContext(i);function l(e){let{children:t,name:n,items:a}=e;const i=(0,r.useMemo)((()=>n&&a?{name:n,items:a}:null),[n,a]);return(0,o.jsx)(s.Provider,{value:i,children:t})}function c(){const e=(0,r.useContext)(s);if(e===i)throw new a.dV("DocsSidebarProvider");return e}},2252:(e,t,n)=>{"use strict";n.d(t,{n:()=>s,r:()=>l});var r=n(6540),a=n(9532),o=n(4848);const i=r.createContext(null);function s(e){let{children:t,version:n}=e;return(0,o.jsx)(i.Provider,{value:n,children:t})}function l(){const e=(0,r.useContext)(i);if(null===e)throw new a.dV("DocsVersionProvider");return e}},9876:(e,t,n)=>{"use strict";n.d(t,{e:()=>f,M:()=>m});var r=n(6540),a=n(5600),o=n(4581),i=n(6347),s=n(9532);function l(e){!function(e){const t=(0,i.W6)(),n=(0,s._q)(e);(0,r.useEffect)((()=>t.block(((e,t)=>n(e,t)))),[t,n])}(((t,n)=>{if("POP"===n)return e(t,n)}))}var c=n(6342),u=n(4848);const d=r.createContext(void 0);function p(){const e=function(){const e=(0,a.YL)(),{items:t}=(0,c.p)().navbar;return 0===t.length&&!e.component}(),t=(0,o.l)(),n=!e&&"mobile"===t,[i,s]=(0,r.useState)(!1);l((()=>{if(i)return s(!1),!1}));const u=(0,r.useCallback)((()=>{s((e=>!e))}),[]);return(0,r.useEffect)((()=>{"desktop"===t&&s(!1)}),[t]),(0,r.useMemo)((()=>({disabled:e,shouldRender:n,toggle:u,shown:i})),[e,n,u,i])}function f(e){let{children:t}=e;const n=p();return(0,u.jsx)(d.Provider,{value:n,children:t})}function m(){const e=r.useContext(d);if(void 0===e)throw new s.dV("NavbarMobileSidebarProvider");return e}},5600:(e,t,n)=>{"use strict";n.d(t,{GX:()=>c,YL:()=>l,y_:()=>s});var r=n(6540),a=n(9532),o=n(4848);const i=r.createContext(null);function s(e){let{children:t}=e;const n=(0,r.useState)({component:null,props:null});return(0,o.jsx)(i.Provider,{value:n,children:t})}function l(){const e=(0,r.useContext)(i);if(!e)throw new a.dV("NavbarSecondaryMenuContentProvider");return e[0]}function c(e){let{component:t,props:n}=e;const o=(0,r.useContext)(i);if(!o)throw new a.dV("NavbarSecondaryMenuContentProvider");const[,s]=o,l=(0,a.Be)(n);return(0,r.useEffect)((()=>{s({component:t,props:l})}),[s,t,l]),(0,r.useEffect)((()=>()=>s({component:null,props:null})),[s]),null}},4090:(e,t,n)=>{"use strict";n.d(t,{w:()=>a,J:()=>o});var r=n(6540);const a="navigation-with-keyboard";function o(){(0,r.useEffect)((()=>{function e(e){"keydown"===e.type&&"Tab"===e.key&&document.body.classList.add(a),"mousedown"===e.type&&document.body.classList.remove(a)}return document.addEventListener("keydown",e),document.addEventListener("mousedown",e),()=>{document.body.classList.remove(a),document.removeEventListener("keydown",e),document.removeEventListener("mousedown",e)}}),[])}},4581:(e,t,n)=>{"use strict";n.d(t,{l:()=>s});var r=n(6540),a=n(8193);const o={desktop:"desktop",mobile:"mobile",ssr:"ssr"},i=996;function s(e){let{desktopBreakpoint:t=i}=void 0===e?{}:e;const[n,s]=(0,r.useState)((()=>"ssr"));return(0,r.useEffect)((()=>{function e(){s(function(e){if(!a.A.canUseDOM)throw new Error("getWindowSize() should only be called after React hydration");return window.innerWidth>e?o.desktop:o.mobile}(t))}return e(),window.addEventListener("resize",e),()=>{window.removeEventListener("resize",e)}}),[t]),n}},7559:(e,t,n)=>{"use strict";n.d(t,{G:()=>r});const r={page:{blogListPage:"blog-list-page",blogPostPage:"blog-post-page",blogTagsListPage:"blog-tags-list-page",blogTagPostListPage:"blog-tags-post-list-page",docsDocPage:"docs-doc-page",docsTagsListPage:"docs-tags-list-page",docsTagDocListPage:"docs-tags-doc-list-page",mdxPage:"mdx-page"},wrapper:{main:"main-wrapper",blogPages:"blog-wrapper",docsPages:"docs-wrapper",mdxPages:"mdx-wrapper"},common:{editThisPage:"theme-edit-this-page",lastUpdated:"theme-last-updated",backToTopButton:"theme-back-to-top-button",codeBlock:"theme-code-block",admonition:"theme-admonition",unlistedBanner:"theme-unlisted-banner",admonitionType:e=>`theme-admonition-${e}`},layout:{},docs:{docVersionBanner:"theme-doc-version-banner",docVersionBadge:"theme-doc-version-badge",docBreadcrumbs:"theme-doc-breadcrumbs",docMarkdown:"theme-doc-markdown",docTocMobile:"theme-doc-toc-mobile",docTocDesktop:"theme-doc-toc-desktop",docFooter:"theme-doc-footer",docFooterTagsRow:"theme-doc-footer-tags-row",docFooterEditMetaRow:"theme-doc-footer-edit-meta-row",docSidebarContainer:"theme-doc-sidebar-container",docSidebarMenu:"theme-doc-sidebar-menu",docSidebarItemCategory:"theme-doc-sidebar-item-category",docSidebarItemLink:"theme-doc-sidebar-item-link",docSidebarItemCategoryLevel:e=>`theme-doc-sidebar-item-category-level-${e}`,docSidebarItemLinkLevel:e=>`theme-doc-sidebar-item-link-level-${e}`},blog:{blogFooterTagsRow:"theme-blog-footer-tags-row",blogFooterEditMetaRow:"theme-blog-footer-edit-meta-row"}}},3109:(e,t,n)=>{"use strict";function r(){return window.matchMedia("(prefers-reduced-motion: reduce)").matches}n.d(t,{O:()=>r})},1754:(e,t,n)=>{"use strict";n.d(t,{Nr:()=>f,w8:()=>g,C5:()=>p,B5:()=>_,Vd:()=>x,QB:()=>S,fW:()=>k,OF:()=>w,Y:()=>v});var r=n(6540),a=n(6347),o=n(2831),i=n(4070),s=n(5597),l=n(2252),c=n(6588);function u(e){return Array.from(new Set(e))}var d=n(9169);const p=!!i.Gy;function f(e){return"link"!==e.type||e.unlisted?"category"===e.type?function(e){if(e.href&&!e.linkUnlisted)return e.href;for(const t of e.items){const e=f(t);if(e)return e}}(e):void 0:e.href}const m=(e,t)=>void 0!==e&&(0,d.ys)(e,t),h=(e,t)=>e.some((e=>g(e,t)));function g(e,t){return"link"===e.type?m(e.href,t):"category"===e.type&&(m(e.href,t)||h(e.items,t))}function b(e,t){switch(e.type){case"category":return g(e,t)||e.items.some((e=>b(e,t)));case"link":return!e.unlisted||g(e,t);default:return!0}}function v(e,t){return(0,r.useMemo)((()=>e.filter((e=>b(e,t)))),[e,t])}function y(e){let{sidebarItems:t,pathname:n,onlyCategories:r=!1}=e;const a=[];return function e(t){for(const o of t)if("category"===o.type&&((0,d.ys)(o.href,n)||e(o.items))||"link"===o.type&&(0,d.ys)(o.href,n)){return r&&"category"!==o.type||a.unshift(o),!0}return!1}(t),a}function w(){const e=(0,c.t)(),{pathname:t}=(0,a.zy)(),n=(0,i.vT)()?.pluginData.breadcrumbs;return!1!==n&&e?y({sidebarItems:e.items,pathname:t}):null}function x(e){const{activeVersion:t}=(0,i.zK)(e),{preferredVersion:n}=(0,s.g1)(e),a=(0,i.r7)(e);return(0,r.useMemo)((()=>u([t,n,a].filter(Boolean))),[t,n,a])}function k(e,t){const n=x(t);return(0,r.useMemo)((()=>{const t=n.flatMap((e=>e.sidebars?Object.entries(e.sidebars):[])),r=t.find((t=>t[0]===e));if(!r)throw new Error(`Can't find any sidebar with id "${e}" in version${n.length>1?"s":""} ${n.map((e=>e.name)).join(", ")}".\nAvailable sidebar ids are:\n- ${t.map((e=>e[0])).join("\n- ")}`);return r[1]}),[e,n])}function S(e,t){const n=x(t);return(0,r.useMemo)((()=>{const t=n.flatMap((e=>e.docs)),r=t.find((t=>t.id===e));if(!r){if(n.flatMap((e=>e.draftIds)).includes(e))return null;throw new Error(`Couldn't find any doc with id "${e}" in version${n.length>1?"s":""} "${n.map((e=>e.name)).join(", ")}".\nAvailable doc ids are:\n- ${u(t.map((e=>e.id))).join("\n- ")}`)}return r}),[e,n])}function _(e){let{route:t}=e;const n=(0,a.zy)(),r=(0,l.r)(),i=t.routes,s=i.find((e=>(0,a.B6)(n.pathname,e)));if(!s)return null;const c=s.sidebar,u=c?r.docsSidebars[c]:void 0;return{docElement:(0,o.v)(i),sidebarName:c,sidebarItems:u}}},1003:(e,t,n)=>{"use strict";n.d(t,{e3:()=>f,be:()=>d,Jx:()=>m});var r=n(6540),a=n(4164),o=n(5260),i=n(3102);function s(){const e=r.useContext(i.o);if(!e)throw new Error("Unexpected: no Docusaurus route context found");return e}var l=n(6025),c=n(4586);var u=n(4848);function d(e){let{title:t,description:n,keywords:r,image:a,children:i}=e;const s=function(e){const{siteConfig:t}=(0,c.A)(),{title:n,titleDelimiter:r}=t;return e?.trim().length?`${e.trim()} ${r} ${n}`:n}(t),{withBaseUrl:d}=(0,l.h)(),p=a?d(a,{absolute:!0}):void 0;return(0,u.jsxs)(o.A,{children:[t&&(0,u.jsx)("title",{children:s}),t&&(0,u.jsx)("meta",{property:"og:title",content:s}),n&&(0,u.jsx)("meta",{name:"description",content:n}),n&&(0,u.jsx)("meta",{property:"og:description",content:n}),r&&(0,u.jsx)("meta",{name:"keywords",content:Array.isArray(r)?r.join(","):r}),p&&(0,u.jsx)("meta",{property:"og:image",content:p}),p&&(0,u.jsx)("meta",{name:"twitter:image",content:p}),i]})}const p=r.createContext(void 0);function f(e){let{className:t,children:n}=e;const i=r.useContext(p),s=(0,a.A)(i,t);return(0,u.jsxs)(p.Provider,{value:s,children:[(0,u.jsx)(o.A,{children:(0,u.jsx)("html",{className:s})}),n]})}function m(e){let{children:t}=e;const n=s(),r=`plugin-${n.plugin.name.replace(/docusaurus-(?:plugin|theme)-(?:content-)?/gi,"")}`;const o=`plugin-id-${n.plugin.id}`;return(0,u.jsx)(f,{className:(0,a.A)(r,o),children:t})}},9532:(e,t,n)=>{"use strict";n.d(t,{Be:()=>c,ZC:()=>s,_q:()=>i,dV:()=>l,fM:()=>u});var r=n(6540),a=n(205),o=n(4848);function i(e){const t=(0,r.useRef)(e);return(0,a.A)((()=>{t.current=e}),[e]),(0,r.useCallback)((function(){return t.current(...arguments)}),[])}function s(e){const t=(0,r.useRef)();return(0,a.A)((()=>{t.current=e})),t.current}class l extends Error{constructor(e,t){super(),this.name="ReactContextError",this.message=`Hook ${this.stack?.split("\n")[1]?.match(/at (?:\w+\.)?(?<name>\w+)/)?.groups.name??""} is called outside the <${e}>. ${t??""}`}}function c(e){const t=Object.entries(e);return t.sort(((e,t)=>e[0].localeCompare(t[0]))),(0,r.useMemo)((()=>e),t.flat())}function u(e){return t=>{let{children:n}=t;return(0,o.jsx)(o.Fragment,{children:e.reduceRight(((e,t)=>(0,o.jsx)(t,{children:e})),n)})}}},9169:(e,t,n)=>{"use strict";n.d(t,{Dt:()=>s,ys:()=>i});var r=n(6540),a=n(8328),o=n(4586);function i(e,t){const n=e=>(!e||e.endsWith("/")?e:`${e}/`)?.toLowerCase();return n(e)===n(t)}function s(){const{baseUrl:e}=(0,o.A)().siteConfig;return(0,r.useMemo)((()=>function(e){let{baseUrl:t,routes:n}=e;function r(e){return e.path===t&&!0===e.exact}function a(e){return e.path===t&&!e.exact}return function e(t){if(0===t.length)return;return t.find(r)||e(t.filter(a).flatMap((e=>e.routes??[])))}(n)}({routes:a.A,baseUrl:e})),[e])}},3104:(e,t,n)=>{"use strict";n.d(t,{Mq:()=>p,Tv:()=>c,gk:()=>f});var r=n(6540),a=n(8193),o=n(2303),i=(n(205),n(9532)),s=n(4848);const l=r.createContext(void 0);function c(e){let{children:t}=e;const n=function(){const e=(0,r.useRef)(!0);return(0,r.useMemo)((()=>({scrollEventsEnabledRef:e,enableScrollEvents:()=>{e.current=!0},disableScrollEvents:()=>{e.current=!1}})),[])}();return(0,s.jsx)(l.Provider,{value:n,children:t})}function u(){const e=(0,r.useContext)(l);if(null==e)throw new i.dV("ScrollControllerProvider");return e}const d=()=>a.A.canUseDOM?{scrollX:window.pageXOffset,scrollY:window.pageYOffset}:null;function p(e,t){void 0===t&&(t=[]);const{scrollEventsEnabledRef:n}=u(),a=(0,r.useRef)(d()),o=(0,i._q)(e);(0,r.useEffect)((()=>{const e=()=>{if(!n.current)return;const e=d();o(e,a.current),a.current=e},t={passive:!0};return e(),window.addEventListener("scroll",e,t),()=>window.removeEventListener("scroll",e,t)}),[o,n,...t])}function f(){const e=(0,r.useRef)(null),t=(0,o.A)()&&"smooth"===getComputedStyle(document.documentElement).scrollBehavior;return{startScroll:n=>{e.current=t?function(e){return window.scrollTo({top:e,behavior:"smooth"}),()=>{}}(n):function(e){let t=null;const n=document.documentElement.scrollTop>e;return function r(){const a=document.documentElement.scrollTop;(n&&a>e||!n&&a<e)&&(t=requestAnimationFrame(r),window.scrollTo(0,Math.floor(.85*(a-e))+e))}(),()=>t&&cancelAnimationFrame(t)}(n)},cancelScroll:()=>e.current?.()}}},2967:(e,t,n)=>{"use strict";n.d(t,{Cy:()=>r,tU:()=>a});n(4586);const r="default";function a(e,t){return`docs-${e}-${t}`}},9466:(e,t,n)=>{"use strict";n.d(t,{Wf:()=>l});n(6540);const r="localStorage";function a(e){let{key:t,oldValue:n,newValue:r,storage:a}=e;if(n===r)return;const o=document.createEvent("StorageEvent");o.initStorageEvent("storage",!1,!1,t,n,r,window.location.href,a),window.dispatchEvent(o)}function o(e){if(void 0===e&&(e=r),"undefined"==typeof window)throw new Error("Browser storage is not available on Node.js/Docusaurus SSR process.");if("none"===e)return null;try{return window[e]}catch(n){return t=n,i||(console.warn("Docusaurus browser storage is not available.\nPossible reasons: running Docusaurus in an iframe, in an incognito browser session, or using too strict browser privacy settings.",t),i=!0),null}var t}let i=!1;const s={get:()=>null,set:()=>{},del:()=>{},listen:()=>()=>{}};function l(e,t){if("undefined"==typeof window)return function(e){function t(){throw new Error(`Illegal storage API usage for storage key "${e}".\nDocusaurus storage APIs are not supposed to be called on the server-rendering process.\nPlease only call storage APIs in effects and event handlers.`)}return{get:t,set:t,del:t,listen:t}}(e);const n=o(t?.persistence);return null===n?s:{get:()=>{try{return n.getItem(e)}catch(t){return console.error(`Docusaurus storage error, can't get key=${e}`,t),null}},set:t=>{try{const r=n.getItem(e);n.setItem(e,t),a({key:e,oldValue:r,newValue:t,storage:n})}catch(r){console.error(`Docusaurus storage error, can't set ${e}=${t}`,r)}},del:()=>{try{const t=n.getItem(e);n.removeItem(e),a({key:e,oldValue:t,newValue:null,storage:n})}catch(t){console.error(`Docusaurus storage error, can't delete key=${e}`,t)}},listen:t=>{try{const r=r=>{r.storageArea===n&&r.key===e&&t(r)};return window.addEventListener("storage",r),()=>window.removeEventListener("storage",r)}catch(r){return console.error(`Docusaurus storage error, can't listen for changes of key=${e}`,r),()=>{}}}}}},2131:(e,t,n)=>{"use strict";n.d(t,{o:()=>i});var r=n(4586),a=n(6347),o=n(440);function i(){const{siteConfig:{baseUrl:e,url:t,trailingSlash:n},i18n:{defaultLocale:i,currentLocale:s}}=(0,r.A)(),{pathname:l}=(0,a.zy)(),c=(0,o.applyTrailingSlash)(l,{trailingSlash:n,baseUrl:e}),u=s===i?e:e.replace(`/${s}/`,"/"),d=c.replace(e,"");return{createUrl:function(e){let{locale:n,fullyQualified:r}=e;return`${r?t:""}${function(e){return e===i?`${u}`:`${u}${e}/`}(n)}${d}`}}}},5062:(e,t,n)=>{"use strict";n.d(t,{$:()=>i});var r=n(6540),a=n(6347),o=n(9532);function i(e){const t=(0,a.zy)(),n=(0,o.ZC)(t),i=(0,o._q)(e);(0,r.useEffect)((()=>{n&&t!==n&&i({location:t,previousLocation:n})}),[i,t,n])}},6342:(e,t,n)=>{"use strict";n.d(t,{p:()=>a});var r=n(4586);function a(){return(0,r.A)().siteConfig.themeConfig}},2983:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.removeTrailingSlash=t.addLeadingSlash=t.addTrailingSlash=void 0;const r=n(2566);function a(e){return e.endsWith("/")?e:`${e}/`}function o(e){return(0,r.removeSuffix)(e,"/")}t.addTrailingSlash=a,t.default=function(e,t){const{trailingSlash:n,baseUrl:r}=t;if(e.startsWith("#"))return e;if(void 0===n)return e;const[i]=e.split(/[#?]/),s="/"===i||i===r?i:(l=i,n?a(l):o(l));var l;return e.replace(i,s)},t.addLeadingSlash=function(e){return(0,r.addPrefix)(e,"/")},t.removeTrailingSlash=o},253:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getErrorCausalChain=void 0,t.getErrorCausalChain=function e(t){return t.cause?[t,...e(t.cause)]:[t]}},440:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.getErrorCausalChain=t.removePrefix=t.addSuffix=t.removeSuffix=t.addPrefix=t.removeTrailingSlash=t.addLeadingSlash=t.addTrailingSlash=t.applyTrailingSlash=t.blogPostContainerID=void 0,t.blogPostContainerID="__blog-post-container";var a=n(2983);Object.defineProperty(t,"applyTrailingSlash",{enumerable:!0,get:function(){return r(a).default}}),Object.defineProperty(t,"addTrailingSlash",{enumerable:!0,get:function(){return a.addTrailingSlash}}),Object.defineProperty(t,"addLeadingSlash",{enumerable:!0,get:function(){return a.addLeadingSlash}}),Object.defineProperty(t,"removeTrailingSlash",{enumerable:!0,get:function(){return a.removeTrailingSlash}});var o=n(2566);Object.defineProperty(t,"addPrefix",{enumerable:!0,get:function(){return o.addPrefix}}),Object.defineProperty(t,"removeSuffix",{enumerable:!0,get:function(){return o.removeSuffix}}),Object.defineProperty(t,"addSuffix",{enumerable:!0,get:function(){return o.addSuffix}}),Object.defineProperty(t,"removePrefix",{enumerable:!0,get:function(){return o.removePrefix}});var i=n(253);Object.defineProperty(t,"getErrorCausalChain",{enumerable:!0,get:function(){return i.getErrorCausalChain}})},2566:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.removePrefix=t.addSuffix=t.removeSuffix=t.addPrefix=void 0,t.addPrefix=function(e,t){return e.startsWith(t)?e:`${t}${e}`},t.removeSuffix=function(e,t){return""===t?e:e.endsWith(t)?e.slice(0,-t.length):e},t.addSuffix=function(e,t){return e.endsWith(t)?e:`${e}${t}`},t.removePrefix=function(e,t){return e.startsWith(t)?e.slice(t.length):e}},1513:(e,t,n)=>{"use strict";n.d(t,{zR:()=>w,TM:()=>C,yJ:()=>f,sC:()=>T,AO:()=>p});var r=n(8168);function a(e){return"/"===e.charAt(0)}function o(e,t){for(var n=t,r=n+1,a=e.length;r<a;n+=1,r+=1)e[n]=e[r];e.pop()}const i=function(e,t){void 0===t&&(t="");var n,r=e&&e.split("/")||[],i=t&&t.split("/")||[],s=e&&a(e),l=t&&a(t),c=s||l;if(e&&a(e)?i=r:r.length&&(i.pop(),i=i.concat(r)),!i.length)return"/";if(i.length){var u=i[i.length-1];n="."===u||".."===u||""===u}else n=!1;for(var d=0,p=i.length;p>=0;p--){var f=i[p];"."===f?o(i,p):".."===f?(o(i,p),d++):d&&(o(i,p),d--)}if(!c)for(;d--;d)i.unshift("..");!c||""===i[0]||i[0]&&a(i[0])||i.unshift("");var m=i.join("/");return n&&"/"!==m.substr(-1)&&(m+="/"),m};var s=n(1561);function l(e){return"/"===e.charAt(0)?e:"/"+e}function c(e){return"/"===e.charAt(0)?e.substr(1):e}function u(e,t){return function(e,t){return 0===e.toLowerCase().indexOf(t.toLowerCase())&&-1!=="/?#".indexOf(e.charAt(t.length))}(e,t)?e.substr(t.length):e}function d(e){return"/"===e.charAt(e.length-1)?e.slice(0,-1):e}function p(e){var t=e.pathname,n=e.search,r=e.hash,a=t||"/";return n&&"?"!==n&&(a+="?"===n.charAt(0)?n:"?"+n),r&&"#"!==r&&(a+="#"===r.charAt(0)?r:"#"+r),a}function f(e,t,n,a){var o;"string"==typeof e?(o=function(e){var t=e||"/",n="",r="",a=t.indexOf("#");-1!==a&&(r=t.substr(a),t=t.substr(0,a));var o=t.indexOf("?");return-1!==o&&(n=t.substr(o),t=t.substr(0,o)),{pathname:t,search:"?"===n?"":n,hash:"#"===r?"":r}}(e),o.state=t):(void 0===(o=(0,r.A)({},e)).pathname&&(o.pathname=""),o.search?"?"!==o.search.charAt(0)&&(o.search="?"+o.search):o.search="",o.hash?"#"!==o.hash.charAt(0)&&(o.hash="#"+o.hash):o.hash="",void 0!==t&&void 0===o.state&&(o.state=t));try{o.pathname=decodeURI(o.pathname)}catch(s){throw s instanceof URIError?new URIError('Pathname "'+o.pathname+'" could not be decoded. This is likely caused by an invalid percent-encoding.'):s}return n&&(o.key=n),a?o.pathname?"/"!==o.pathname.charAt(0)&&(o.pathname=i(o.pathname,a.pathname)):o.pathname=a.pathname:o.pathname||(o.pathname="/"),o}function m(){var e=null;var t=[];return{setPrompt:function(t){return e=t,function(){e===t&&(e=null)}},confirmTransitionTo:function(t,n,r,a){if(null!=e){var o="function"==typeof e?e(t,n):e;"string"==typeof o?"function"==typeof r?r(o,a):a(!0):a(!1!==o)}else a(!0)},appendListener:function(e){var n=!0;function r(){n&&e.apply(void 0,arguments)}return t.push(r),function(){n=!1,t=t.filter((function(e){return e!==r}))}},notifyListeners:function(){for(var e=arguments.length,n=new Array(e),r=0;r<e;r++)n[r]=arguments[r];t.forEach((function(e){return e.apply(void 0,n)}))}}}var h=!("undefined"==typeof window||!window.document||!window.document.createElement);function g(e,t){t(window.confirm(e))}var b="popstate",v="hashchange";function y(){try{return window.history.state||{}}catch(e){return{}}}function w(e){void 0===e&&(e={}),h||(0,s.A)(!1);var t,n=window.history,a=(-1===(t=window.navigator.userAgent).indexOf("Android 2.")&&-1===t.indexOf("Android 4.0")||-1===t.indexOf("Mobile Safari")||-1!==t.indexOf("Chrome")||-1!==t.indexOf("Windows Phone"))&&window.history&&"pushState"in window.history,o=!(-1===window.navigator.userAgent.indexOf("Trident")),i=e,c=i.forceRefresh,w=void 0!==c&&c,x=i.getUserConfirmation,k=void 0===x?g:x,S=i.keyLength,_=void 0===S?6:S,E=e.basename?d(l(e.basename)):"";function C(e){var t=e||{},n=t.key,r=t.state,a=window.location,o=a.pathname+a.search+a.hash;return E&&(o=u(o,E)),f(o,r,n)}function A(){return Math.random().toString(36).substr(2,_)}var T=m();function j(e){(0,r.A)($,e),$.length=n.length,T.notifyListeners($.location,$.action)}function N(e){(function(e){return void 0===e.state&&-1===navigator.userAgent.indexOf("CriOS")})(e)||P(C(e.state))}function L(){P(C(y()))}var R=!1;function P(e){if(R)R=!1,j();else{T.confirmTransitionTo(e,"POP",k,(function(t){t?j({action:"POP",location:e}):function(e){var t=$.location,n=D.indexOf(t.key);-1===n&&(n=0);var r=D.indexOf(e.key);-1===r&&(r=0);var a=n-r;a&&(R=!0,F(a))}(e)}))}}var O=C(y()),D=[O.key];function I(e){return E+p(e)}function F(e){n.go(e)}var M=0;function z(e){1===(M+=e)&&1===e?(window.addEventListener(b,N),o&&window.addEventListener(v,L)):0===M&&(window.removeEventListener(b,N),o&&window.removeEventListener(v,L))}var B=!1;var $={length:n.length,action:"POP",location:O,createHref:I,push:function(e,t){var r="PUSH",o=f(e,t,A(),$.location);T.confirmTransitionTo(o,r,k,(function(e){if(e){var t=I(o),i=o.key,s=o.state;if(a)if(n.pushState({key:i,state:s},null,t),w)window.location.href=t;else{var l=D.indexOf($.location.key),c=D.slice(0,l+1);c.push(o.key),D=c,j({action:r,location:o})}else window.location.href=t}}))},replace:function(e,t){var r="REPLACE",o=f(e,t,A(),$.location);T.confirmTransitionTo(o,r,k,(function(e){if(e){var t=I(o),i=o.key,s=o.state;if(a)if(n.replaceState({key:i,state:s},null,t),w)window.location.replace(t);else{var l=D.indexOf($.location.key);-1!==l&&(D[l]=o.key),j({action:r,location:o})}else window.location.replace(t)}}))},go:F,goBack:function(){F(-1)},goForward:function(){F(1)},block:function(e){void 0===e&&(e=!1);var t=T.setPrompt(e);return B||(z(1),B=!0),function(){return B&&(B=!1,z(-1)),t()}},listen:function(e){var t=T.appendListener(e);return z(1),function(){z(-1),t()}}};return $}var x="hashchange",k={hashbang:{encodePath:function(e){return"!"===e.charAt(0)?e:"!/"+c(e)},decodePath:function(e){return"!"===e.charAt(0)?e.substr(1):e}},noslash:{encodePath:c,decodePath:l},slash:{encodePath:l,decodePath:l}};function S(e){var t=e.indexOf("#");return-1===t?e:e.slice(0,t)}function _(){var e=window.location.href,t=e.indexOf("#");return-1===t?"":e.substring(t+1)}function E(e){window.location.replace(S(window.location.href)+"#"+e)}function C(e){void 0===e&&(e={}),h||(0,s.A)(!1);var t=window.history,n=(window.navigator.userAgent.indexOf("Firefox"),e),a=n.getUserConfirmation,o=void 0===a?g:a,i=n.hashType,c=void 0===i?"slash":i,b=e.basename?d(l(e.basename)):"",v=k[c],y=v.encodePath,w=v.decodePath;function C(){var e=w(_());return b&&(e=u(e,b)),f(e)}var A=m();function T(e){(0,r.A)(B,e),B.length=t.length,A.notifyListeners(B.location,B.action)}var j=!1,N=null;function L(){var e,t,n=_(),r=y(n);if(n!==r)E(r);else{var a=C(),i=B.location;if(!j&&(t=a,(e=i).pathname===t.pathname&&e.search===t.search&&e.hash===t.hash))return;if(N===p(a))return;N=null,function(e){if(j)j=!1,T();else{var t="POP";A.confirmTransitionTo(e,t,o,(function(n){n?T({action:t,location:e}):function(e){var t=B.location,n=D.lastIndexOf(p(t));-1===n&&(n=0);var r=D.lastIndexOf(p(e));-1===r&&(r=0);var a=n-r;a&&(j=!0,I(a))}(e)}))}}(a)}}var R=_(),P=y(R);R!==P&&E(P);var O=C(),D=[p(O)];function I(e){t.go(e)}var F=0;function M(e){1===(F+=e)&&1===e?window.addEventListener(x,L):0===F&&window.removeEventListener(x,L)}var z=!1;var B={length:t.length,action:"POP",location:O,createHref:function(e){var t=document.querySelector("base"),n="";return t&&t.getAttribute("href")&&(n=S(window.location.href)),n+"#"+y(b+p(e))},push:function(e,t){var n="PUSH",r=f(e,void 0,void 0,B.location);A.confirmTransitionTo(r,n,o,(function(e){if(e){var t=p(r),a=y(b+t);if(_()!==a){N=t,function(e){window.location.hash=e}(a);var o=D.lastIndexOf(p(B.location)),i=D.slice(0,o+1);i.push(t),D=i,T({action:n,location:r})}else T()}}))},replace:function(e,t){var n="REPLACE",r=f(e,void 0,void 0,B.location);A.confirmTransitionTo(r,n,o,(function(e){if(e){var t=p(r),a=y(b+t);_()!==a&&(N=t,E(a));var o=D.indexOf(p(B.location));-1!==o&&(D[o]=t),T({action:n,location:r})}}))},go:I,goBack:function(){I(-1)},goForward:function(){I(1)},block:function(e){void 0===e&&(e=!1);var t=A.setPrompt(e);return z||(M(1),z=!0),function(){return z&&(z=!1,M(-1)),t()}},listen:function(e){var t=A.appendListener(e);return M(1),function(){M(-1),t()}}};return B}function A(e,t,n){return Math.min(Math.max(e,t),n)}function T(e){void 0===e&&(e={});var t=e,n=t.getUserConfirmation,a=t.initialEntries,o=void 0===a?["/"]:a,i=t.initialIndex,s=void 0===i?0:i,l=t.keyLength,c=void 0===l?6:l,u=m();function d(e){(0,r.A)(w,e),w.length=w.entries.length,u.notifyListeners(w.location,w.action)}function h(){return Math.random().toString(36).substr(2,c)}var g=A(s,0,o.length-1),b=o.map((function(e){return f(e,void 0,"string"==typeof e?h():e.key||h())})),v=p;function y(e){var t=A(w.index+e,0,w.entries.length-1),r=w.entries[t];u.confirmTransitionTo(r,"POP",n,(function(e){e?d({action:"POP",location:r,index:t}):d()}))}var w={length:b.length,action:"POP",location:b[g],index:g,entries:b,createHref:v,push:function(e,t){var r="PUSH",a=f(e,t,h(),w.location);u.confirmTransitionTo(a,r,n,(function(e){if(e){var t=w.index+1,n=w.entries.slice(0);n.length>t?n.splice(t,n.length-t,a):n.push(a),d({action:r,location:a,index:t,entries:n})}}))},replace:function(e,t){var r="REPLACE",a=f(e,t,h(),w.location);u.confirmTransitionTo(a,r,n,(function(e){e&&(w.entries[w.index]=a,d({action:r,location:a}))}))},go:y,goBack:function(){y(-1)},goForward:function(){y(1)},canGo:function(e){var t=w.index+e;return t>=0&&t<w.entries.length},block:function(e){return void 0===e&&(e=!1),u.setPrompt(e)},listen:function(e){return u.appendListener(e)}};return w}},4146:(e,t,n)=>{"use strict";var r=n(4363),a={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},o={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},i={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},s={};function l(e){return r.isMemo(e)?i:s[e.$$typeof]||a}s[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},s[r.Memo]=i;var c=Object.defineProperty,u=Object.getOwnPropertyNames,d=Object.getOwnPropertySymbols,p=Object.getOwnPropertyDescriptor,f=Object.getPrototypeOf,m=Object.prototype;e.exports=function e(t,n,r){if("string"!=typeof n){if(m){var a=f(n);a&&a!==m&&e(t,a,r)}var i=u(n);d&&(i=i.concat(d(n)));for(var s=l(t),h=l(n),g=0;g<i.length;++g){var b=i[g];if(!(o[b]||r&&r[b]||h&&h[b]||s&&s[b])){var v=p(n,b);try{c(t,b,v)}catch(y){}}}}return t}},311:e=>{"use strict";e.exports=function(e,t,n,r,a,o,i,s){if(!e){var l;if(void 0===t)l=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var c=[n,r,a,o,i,s],u=0;(l=new Error(t.replace(/%s/g,(function(){return c[u++]})))).name="Invariant Violation"}throw l.framesToPop=1,l}}},4634:e=>{e.exports=Array.isArray||function(e){return"[object Array]"==Object.prototype.toString.call(e)}},119:(e,t,n)=>{"use strict";n.r(t)},1043:(e,t,n)=>{"use strict";n.r(t)},5947:function(e,t,n){var r,a;r=function(){var e,t,n={version:"0.2.0"},r=n.settings={minimum:.08,easing:"ease",positionUsing:"",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,showSpinner:!0,barSelector:'[role="bar"]',spinnerSelector:'[role="spinner"]',parent:"body",template:'<div class="bar" role="bar"><div class="peg"></div></div><div class="spinner" role="spinner"><div class="spinner-icon"></div></div>'};function a(e,t,n){return e<t?t:e>n?n:e}function o(e){return 100*(-1+e)}function i(e,t,n){var a;return(a="translate3d"===r.positionUsing?{transform:"translate3d("+o(e)+"%,0,0)"}:"translate"===r.positionUsing?{transform:"translate("+o(e)+"%,0)"}:{"margin-left":o(e)+"%"}).transition="all "+t+"ms "+n,a}n.configure=function(e){var t,n;for(t in e)void 0!==(n=e[t])&&e.hasOwnProperty(t)&&(r[t]=n);return this},n.status=null,n.set=function(e){var t=n.isStarted();e=a(e,r.minimum,1),n.status=1===e?null:e;var o=n.render(!t),c=o.querySelector(r.barSelector),u=r.speed,d=r.easing;return o.offsetWidth,s((function(t){""===r.positionUsing&&(r.positionUsing=n.getPositioningCSS()),l(c,i(e,u,d)),1===e?(l(o,{transition:"none",opacity:1}),o.offsetWidth,setTimeout((function(){l(o,{transition:"all "+u+"ms linear",opacity:0}),setTimeout((function(){n.remove(),t()}),u)}),u)):setTimeout(t,u)})),this},n.isStarted=function(){return"number"==typeof n.status},n.start=function(){n.status||n.set(0);var e=function(){setTimeout((function(){n.status&&(n.trickle(),e())}),r.trickleSpeed)};return r.trickle&&e(),this},n.done=function(e){return e||n.status?n.inc(.3+.5*Math.random()).set(1):this},n.inc=function(e){var t=n.status;return t?("number"!=typeof e&&(e=(1-t)*a(Math.random()*t,.1,.95)),t=a(t+e,0,.994),n.set(t)):n.start()},n.trickle=function(){return n.inc(Math.random()*r.trickleRate)},e=0,t=0,n.promise=function(r){return r&&"resolved"!==r.state()?(0===t&&n.start(),e++,t++,r.always((function(){0==--t?(e=0,n.done()):n.set((e-t)/e)})),this):this},n.render=function(e){if(n.isRendered())return document.getElementById("nprogress");u(document.documentElement,"nprogress-busy");var t=document.createElement("div");t.id="nprogress",t.innerHTML=r.template;var a,i=t.querySelector(r.barSelector),s=e?"-100":o(n.status||0),c=document.querySelector(r.parent);return l(i,{transition:"all 0 linear",transform:"translate3d("+s+"%,0,0)"}),r.showSpinner||(a=t.querySelector(r.spinnerSelector))&&f(a),c!=document.body&&u(c,"nprogress-custom-parent"),c.appendChild(t),t},n.remove=function(){d(document.documentElement,"nprogress-busy"),d(document.querySelector(r.parent),"nprogress-custom-parent");var e=document.getElementById("nprogress");e&&f(e)},n.isRendered=function(){return!!document.getElementById("nprogress")},n.getPositioningCSS=function(){var e=document.body.style,t="WebkitTransform"in e?"Webkit":"MozTransform"in e?"Moz":"msTransform"in e?"ms":"OTransform"in e?"O":"";return t+"Perspective"in e?"translate3d":t+"Transform"in e?"translate":"margin"};var s=function(){var e=[];function t(){var n=e.shift();n&&n(t)}return function(n){e.push(n),1==e.length&&t()}}(),l=function(){var e=["Webkit","O","Moz","ms"],t={};function n(e){return e.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,(function(e,t){return t.toUpperCase()}))}function r(t){var n=document.body.style;if(t in n)return t;for(var r,a=e.length,o=t.charAt(0).toUpperCase()+t.slice(1);a--;)if((r=e[a]+o)in n)return r;return t}function a(e){return e=n(e),t[e]||(t[e]=r(e))}function o(e,t,n){t=a(t),e.style[t]=n}return function(e,t){var n,r,a=arguments;if(2==a.length)for(n in t)void 0!==(r=t[n])&&t.hasOwnProperty(n)&&o(e,n,r);else o(e,a[1],a[2])}}();function c(e,t){return("string"==typeof e?e:p(e)).indexOf(" "+t+" ")>=0}function u(e,t){var n=p(e),r=n+t;c(n,t)||(e.className=r.substring(1))}function d(e,t){var n,r=p(e);c(e,t)&&(n=r.replace(" "+t+" "," "),e.className=n.substring(1,n.length-1))}function p(e){return(" "+(e.className||"")+" ").replace(/\s+/gi," ")}function f(e){e&&e.parentNode&&e.parentNode.removeChild(e)}return n},void 0===(a="function"==typeof r?r.call(t,n,t,e):r)||(e.exports=a)},6969:e=>{e.exports&&(e.exports={core:{meta:{path:"components/prism-core.js",option:"mandatory"},core:"Core"},themes:{meta:{path:"themes/{id}.css",link:"index.html?theme={id}",exclusive:!0},prism:{title:"Default",option:"default"},"prism-dark":"Dark","prism-funky":"Funky","prism-okaidia":{title:"Okaidia",owner:"ocodia"},"prism-twilight":{title:"Twilight",owner:"remybach"},"prism-coy":{title:"Coy",owner:"tshedor"},"prism-solarizedlight":{title:"Solarized Light",owner:"hectormatos2011 "},"prism-tomorrow":{title:"Tomorrow Night",owner:"Rosey"}},languages:{meta:{path:"components/prism-{id}",noCSS:!0,examplesPath:"examples/prism-{id}",addCheckAll:!0},markup:{title:"Markup",alias:["html","xml","svg","mathml","ssml","atom","rss"],aliasTitles:{html:"HTML",xml:"XML",svg:"SVG",mathml:"MathML",ssml:"SSML",atom:"Atom",rss:"RSS"},option:"default"},css:{title:"CSS",option:"default",modify:"markup"},clike:{title:"C-like",option:"default"},javascript:{title:"JavaScript",require:"clike",modify:"markup",optional:"regex",alias:"js",option:"default"},abap:{title:"ABAP",owner:"dellagustin"},abnf:{title:"ABNF",owner:"RunDevelopment"},actionscript:{title:"ActionScript",require:"javascript",modify:"markup",owner:"Golmote"},ada:{title:"Ada",owner:"Lucretia"},agda:{title:"Agda",owner:"xy-ren"},al:{title:"AL",owner:"RunDevelopment"},antlr4:{title:"ANTLR4",alias:"g4",owner:"RunDevelopment"},apacheconf:{title:"Apache Configuration",owner:"GuiTeK"},apex:{title:"Apex",require:["clike","sql"],owner:"RunDevelopment"},apl:{title:"APL",owner:"ngn"},applescript:{title:"AppleScript",owner:"Golmote"},aql:{title:"AQL",owner:"RunDevelopment"},arduino:{title:"Arduino",require:"cpp",alias:"ino",owner:"dkern"},arff:{title:"ARFF",owner:"Golmote"},armasm:{title:"ARM Assembly",alias:"arm-asm",owner:"RunDevelopment"},arturo:{title:"Arturo",alias:"art",optional:["bash","css","javascript","markup","markdown","sql"],owner:"drkameleon"},asciidoc:{alias:"adoc",title:"AsciiDoc",owner:"Golmote"},aspnet:{title:"ASP.NET (C#)",require:["markup","csharp"],owner:"nauzilus"},asm6502:{title:"6502 Assembly",owner:"kzurawel"},asmatmel:{title:"Atmel AVR Assembly",owner:"cerkit"},autohotkey:{title:"AutoHotkey",owner:"aviaryan"},autoit:{title:"AutoIt",owner:"Golmote"},avisynth:{title:"AviSynth",alias:"avs",owner:"Zinfidel"},"avro-idl":{title:"Avro IDL",alias:"avdl",owner:"RunDevelopment"},awk:{title:"AWK",alias:"gawk",aliasTitles:{gawk:"GAWK"},owner:"RunDevelopment"},bash:{title:"Bash",alias:["sh","shell"],aliasTitles:{sh:"Shell",shell:"Shell"},owner:"zeitgeist87"},basic:{title:"BASIC",owner:"Golmote"},batch:{title:"Batch",owner:"Golmote"},bbcode:{title:"BBcode",alias:"shortcode",aliasTitles:{shortcode:"Shortcode"},owner:"RunDevelopment"},bbj:{title:"BBj",owner:"hyyan"},bicep:{title:"Bicep",owner:"johnnyreilly"},birb:{title:"Birb",require:"clike",owner:"Calamity210"},bison:{title:"Bison",require:"c",owner:"Golmote"},bnf:{title:"BNF",alias:"rbnf",aliasTitles:{rbnf:"RBNF"},owner:"RunDevelopment"},bqn:{title:"BQN",owner:"yewscion"},brainfuck:{title:"Brainfuck",owner:"Golmote"},brightscript:{title:"BrightScript",owner:"RunDevelopment"},bro:{title:"Bro",owner:"wayward710"},bsl:{title:"BSL (1C:Enterprise)",alias:"oscript",aliasTitles:{oscript:"OneScript"},owner:"Diversus23"},c:{title:"C",require:"clike",owner:"zeitgeist87"},csharp:{title:"C#",require:"clike",alias:["cs","dotnet"],owner:"mvalipour"},cpp:{title:"C++",require:"c",owner:"zeitgeist87"},cfscript:{title:"CFScript",require:"clike",alias:"cfc",owner:"mjclemente"},chaiscript:{title:"ChaiScript",require:["clike","cpp"],owner:"RunDevelopment"},cil:{title:"CIL",owner:"sbrl"},cilkc:{title:"Cilk/C",require:"c",alias:"cilk-c",owner:"OpenCilk"},cilkcpp:{title:"Cilk/C++",require:"cpp",alias:["cilk-cpp","cilk"],owner:"OpenCilk"},clojure:{title:"Clojure",owner:"troglotit"},cmake:{title:"CMake",owner:"mjrogozinski"},cobol:{title:"COBOL",owner:"RunDevelopment"},coffeescript:{title:"CoffeeScript",require:"javascript",alias:"coffee",owner:"R-osey"},concurnas:{title:"Concurnas",alias:"conc",owner:"jasontatton"},csp:{title:"Content-Security-Policy",owner:"ScottHelme"},cooklang:{title:"Cooklang",owner:"ahue"},coq:{title:"Coq",owner:"RunDevelopment"},crystal:{title:"Crystal",require:"ruby",owner:"MakeNowJust"},"css-extras":{title:"CSS Extras",require:"css",modify:"css",owner:"milesj"},csv:{title:"CSV",owner:"RunDevelopment"},cue:{title:"CUE",owner:"RunDevelopment"},cypher:{title:"Cypher",owner:"RunDevelopment"},d:{title:"D",require:"clike",owner:"Golmote"},dart:{title:"Dart",require:"clike",owner:"Golmote"},dataweave:{title:"DataWeave",owner:"machaval"},dax:{title:"DAX",owner:"peterbud"},dhall:{title:"Dhall",owner:"RunDevelopment"},diff:{title:"Diff",owner:"uranusjr"},django:{title:"Django/Jinja2",require:"markup-templating",alias:"jinja2",owner:"romanvm"},"dns-zone-file":{title:"DNS zone file",owner:"RunDevelopment",alias:"dns-zone"},docker:{title:"Docker",alias:"dockerfile",owner:"JustinBeckwith"},dot:{title:"DOT (Graphviz)",alias:"gv",optional:"markup",owner:"RunDevelopment"},ebnf:{title:"EBNF",owner:"RunDevelopment"},editorconfig:{title:"EditorConfig",owner:"osipxd"},eiffel:{title:"Eiffel",owner:"Conaclos"},ejs:{title:"EJS",require:["javascript","markup-templating"],owner:"RunDevelopment",alias:"eta",aliasTitles:{eta:"Eta"}},elixir:{title:"Elixir",owner:"Golmote"},elm:{title:"Elm",owner:"zwilias"},etlua:{title:"Embedded Lua templating",require:["lua","markup-templating"],owner:"RunDevelopment"},erb:{title:"ERB",require:["ruby","markup-templating"],owner:"Golmote"},erlang:{title:"Erlang",owner:"Golmote"},"excel-formula":{title:"Excel Formula",alias:["xlsx","xls"],owner:"RunDevelopment"},fsharp:{title:"F#",require:"clike",owner:"simonreynolds7"},factor:{title:"Factor",owner:"catb0t"},false:{title:"False",owner:"edukisto"},"firestore-security-rules":{title:"Firestore security rules",require:"clike",owner:"RunDevelopment"},flow:{title:"Flow",require:"javascript",owner:"Golmote"},fortran:{title:"Fortran",owner:"Golmote"},ftl:{title:"FreeMarker Template Language",require:"markup-templating",owner:"RunDevelopment"},gml:{title:"GameMaker Language",alias:"gamemakerlanguage",require:"clike",owner:"LiarOnce"},gap:{title:"GAP (CAS)",owner:"RunDevelopment"},gcode:{title:"G-code",owner:"RunDevelopment"},gdscript:{title:"GDScript",owner:"RunDevelopment"},gedcom:{title:"GEDCOM",owner:"Golmote"},gettext:{title:"gettext",alias:"po",owner:"RunDevelopment"},gherkin:{title:"Gherkin",owner:"hason"},git:{title:"Git",owner:"lgiraudel"},glsl:{title:"GLSL",require:"c",owner:"Golmote"},gn:{title:"GN",alias:"gni",owner:"RunDevelopment"},"linker-script":{title:"GNU Linker Script",alias:"ld",owner:"RunDevelopment"},go:{title:"Go",require:"clike",owner:"arnehormann"},"go-module":{title:"Go module",alias:"go-mod",owner:"RunDevelopment"},gradle:{title:"Gradle",require:"clike",owner:"zeabdelkhalek-badido18"},graphql:{title:"GraphQL",optional:"markdown",owner:"Golmote"},groovy:{title:"Groovy",require:"clike",owner:"robfletcher"},haml:{title:"Haml",require:"ruby",optional:["css","css-extras","coffeescript","erb","javascript","less","markdown","scss","textile"],owner:"Golmote"},handlebars:{title:"Handlebars",require:"markup-templating",alias:["hbs","mustache"],aliasTitles:{mustache:"Mustache"},owner:"Golmote"},haskell:{title:"Haskell",alias:"hs",owner:"bholst"},haxe:{title:"Haxe",require:"clike",optional:"regex",owner:"Golmote"},hcl:{title:"HCL",owner:"outsideris"},hlsl:{title:"HLSL",require:"c",owner:"RunDevelopment"},hoon:{title:"Hoon",owner:"matildepark"},http:{title:"HTTP",optional:["csp","css","hpkp","hsts","javascript","json","markup","uri"],owner:"danielgtaylor"},hpkp:{title:"HTTP Public-Key-Pins",owner:"ScottHelme"},hsts:{title:"HTTP Strict-Transport-Security",owner:"ScottHelme"},ichigojam:{title:"IchigoJam",owner:"BlueCocoa"},icon:{title:"Icon",owner:"Golmote"},"icu-message-format":{title:"ICU Message Format",owner:"RunDevelopment"},idris:{title:"Idris",alias:"idr",owner:"KeenS",require:"haskell"},ignore:{title:".ignore",owner:"osipxd",alias:["gitignore","hgignore","npmignore"],aliasTitles:{gitignore:".gitignore",hgignore:".hgignore",npmignore:".npmignore"}},inform7:{title:"Inform 7",owner:"Golmote"},ini:{title:"Ini",owner:"aviaryan"},io:{title:"Io",owner:"AlesTsurko"},j:{title:"J",owner:"Golmote"},java:{title:"Java",require:"clike",owner:"sherblot"},javadoc:{title:"JavaDoc",require:["markup","java","javadoclike"],modify:"java",optional:"scala",owner:"RunDevelopment"},javadoclike:{title:"JavaDoc-like",modify:["java","javascript","php"],owner:"RunDevelopment"},javastacktrace:{title:"Java stack trace",owner:"RunDevelopment"},jexl:{title:"Jexl",owner:"czosel"},jolie:{title:"Jolie",require:"clike",owner:"thesave"},jq:{title:"JQ",owner:"RunDevelopment"},jsdoc:{title:"JSDoc",require:["javascript","javadoclike","typescript"],modify:"javascript",optional:["actionscript","coffeescript"],owner:"RunDevelopment"},"js-extras":{title:"JS Extras",require:"javascript",modify:"javascript",optional:["actionscript","coffeescript","flow","n4js","typescript"],owner:"RunDevelopment"},json:{title:"JSON",alias:"webmanifest",aliasTitles:{webmanifest:"Web App Manifest"},owner:"CupOfTea696"},json5:{title:"JSON5",require:"json",owner:"RunDevelopment"},jsonp:{title:"JSONP",require:"json",owner:"RunDevelopment"},jsstacktrace:{title:"JS stack trace",owner:"sbrl"},"js-templates":{title:"JS Templates",require:"javascript",modify:"javascript",optional:["css","css-extras","graphql","markdown","markup","sql"],owner:"RunDevelopment"},julia:{title:"Julia",owner:"cdagnino"},keepalived:{title:"Keepalived Configure",owner:"dev-itsheng"},keyman:{title:"Keyman",owner:"mcdurdin"},kotlin:{title:"Kotlin",alias:["kt","kts"],aliasTitles:{kts:"Kotlin Script"},require:"clike",owner:"Golmote"},kumir:{title:"KuMir (\u041a\u0443\u041c\u0438\u0440)",alias:"kum",owner:"edukisto"},kusto:{title:"Kusto",owner:"RunDevelopment"},latex:{title:"LaTeX",alias:["tex","context"],aliasTitles:{tex:"TeX",context:"ConTeXt"},owner:"japborst"},latte:{title:"Latte",require:["clike","markup-templating","php"],owner:"nette"},less:{title:"Less",require:"css",optional:"css-extras",owner:"Golmote"},lilypond:{title:"LilyPond",require:"scheme",alias:"ly",owner:"RunDevelopment"},liquid:{title:"Liquid",require:"markup-templating",owner:"cinhtau"},lisp:{title:"Lisp",alias:["emacs","elisp","emacs-lisp"],owner:"JuanCaicedo"},livescript:{title:"LiveScript",owner:"Golmote"},llvm:{title:"LLVM IR",owner:"porglezomp"},log:{title:"Log file",optional:"javastacktrace",owner:"RunDevelopment"},lolcode:{title:"LOLCODE",owner:"Golmote"},lua:{title:"Lua",owner:"Golmote"},magma:{title:"Magma (CAS)",owner:"RunDevelopment"},makefile:{title:"Makefile",owner:"Golmote"},markdown:{title:"Markdown",require:"markup",optional:"yaml",alias:"md",owner:"Golmote"},"markup-templating":{title:"Markup templating",require:"markup",owner:"Golmote"},mata:{title:"Mata",owner:"RunDevelopment"},matlab:{title:"MATLAB",owner:"Golmote"},maxscript:{title:"MAXScript",owner:"RunDevelopment"},mel:{title:"MEL",owner:"Golmote"},mermaid:{title:"Mermaid",owner:"RunDevelopment"},metafont:{title:"METAFONT",owner:"LaeriExNihilo"},mizar:{title:"Mizar",owner:"Golmote"},mongodb:{title:"MongoDB",owner:"airs0urce",require:"javascript"},monkey:{title:"Monkey",owner:"Golmote"},moonscript:{title:"MoonScript",alias:"moon",owner:"RunDevelopment"},n1ql:{title:"N1QL",owner:"TMWilds"},n4js:{title:"N4JS",require:"javascript",optional:"jsdoc",alias:"n4jsd",owner:"bsmith-n4"},"nand2tetris-hdl":{title:"Nand To Tetris HDL",owner:"stephanmax"},naniscript:{title:"Naninovel Script",owner:"Elringus",alias:"nani"},nasm:{title:"NASM",owner:"rbmj"},neon:{title:"NEON",owner:"nette"},nevod:{title:"Nevod",owner:"nezaboodka"},nginx:{title:"nginx",owner:"volado"},nim:{title:"Nim",owner:"Golmote"},nix:{title:"Nix",owner:"Golmote"},nsis:{title:"NSIS",owner:"idleberg"},objectivec:{title:"Objective-C",require:"c",alias:"objc",owner:"uranusjr"},ocaml:{title:"OCaml",owner:"Golmote"},odin:{title:"Odin",owner:"edukisto"},opencl:{title:"OpenCL",require:"c",modify:["c","cpp"],owner:"Milania1"},openqasm:{title:"OpenQasm",alias:"qasm",owner:"RunDevelopment"},oz:{title:"Oz",owner:"Golmote"},parigp:{title:"PARI/GP",owner:"Golmote"},parser:{title:"Parser",require:"markup",owner:"Golmote"},pascal:{title:"Pascal",alias:"objectpascal",aliasTitles:{objectpascal:"Object Pascal"},owner:"Golmote"},pascaligo:{title:"Pascaligo",owner:"DefinitelyNotAGoat"},psl:{title:"PATROL Scripting Language",owner:"bertysentry"},pcaxis:{title:"PC-Axis",alias:"px",owner:"RunDevelopment"},peoplecode:{title:"PeopleCode",alias:"pcode",owner:"RunDevelopment"},perl:{title:"Perl",owner:"Golmote"},php:{title:"PHP",require:"markup-templating",owner:"milesj"},phpdoc:{title:"PHPDoc",require:["php","javadoclike"],modify:"php",owner:"RunDevelopment"},"php-extras":{title:"PHP Extras",require:"php",modify:"php",owner:"milesj"},"plant-uml":{title:"PlantUML",alias:"plantuml",owner:"RunDevelopment"},plsql:{title:"PL/SQL",require:"sql",owner:"Golmote"},powerquery:{title:"PowerQuery",alias:["pq","mscript"],owner:"peterbud"},powershell:{title:"PowerShell",owner:"nauzilus"},processing:{title:"Processing",require:"clike",owner:"Golmote"},prolog:{title:"Prolog",owner:"Golmote"},promql:{title:"PromQL",owner:"arendjr"},properties:{title:".properties",owner:"Golmote"},protobuf:{title:"Protocol Buffers",require:"clike",owner:"just-boris"},pug:{title:"Pug",require:["markup","javascript"],optional:["coffeescript","ejs","handlebars","less","livescript","markdown","scss","stylus","twig"],owner:"Golmote"},puppet:{title:"Puppet",owner:"Golmote"},pure:{title:"Pure",optional:["c","cpp","fortran"],owner:"Golmote"},purebasic:{title:"PureBasic",require:"clike",alias:"pbfasm",owner:"HeX0R101"},purescript:{title:"PureScript",require:"haskell",alias:"purs",owner:"sriharshachilakapati"},python:{title:"Python",alias:"py",owner:"multipetros"},qsharp:{title:"Q#",require:"clike",alias:"qs",owner:"fedonman"},q:{title:"Q (kdb+ database)",owner:"Golmote"},qml:{title:"QML",require:"javascript",owner:"RunDevelopment"},qore:{title:"Qore",require:"clike",owner:"temnroegg"},r:{title:"R",owner:"Golmote"},racket:{title:"Racket",require:"scheme",alias:"rkt",owner:"RunDevelopment"},cshtml:{title:"Razor C#",alias:"razor",require:["markup","csharp"],optional:["css","css-extras","javascript","js-extras"],owner:"RunDevelopment"},jsx:{title:"React JSX",require:["markup","javascript"],optional:["jsdoc","js-extras","js-templates"],owner:"vkbansal"},tsx:{title:"React TSX",require:["jsx","typescript"]},reason:{title:"Reason",require:"clike",owner:"Golmote"},regex:{title:"Regex",owner:"RunDevelopment"},rego:{title:"Rego",owner:"JordanSh"},renpy:{title:"Ren'py",alias:"rpy",owner:"HyuchiaDiego"},rescript:{title:"ReScript",alias:"res",owner:"vmarcosp"},rest:{title:"reST (reStructuredText)",owner:"Golmote"},rip:{title:"Rip",owner:"ravinggenius"},roboconf:{title:"Roboconf",owner:"Golmote"},robotframework:{title:"Robot Framework",alias:"robot",owner:"RunDevelopment"},ruby:{title:"Ruby",require:"clike",alias:"rb",owner:"samflores"},rust:{title:"Rust",owner:"Golmote"},sas:{title:"SAS",optional:["groovy","lua","sql"],owner:"Golmote"},sass:{title:"Sass (Sass)",require:"css",optional:"css-extras",owner:"Golmote"},scss:{title:"Sass (SCSS)",require:"css",optional:"css-extras",owner:"MoOx"},scala:{title:"Scala",require:"java",owner:"jozic"},scheme:{title:"Scheme",owner:"bacchus123"},"shell-session":{title:"Shell session",require:"bash",alias:["sh-session","shellsession"],owner:"RunDevelopment"},smali:{title:"Smali",owner:"RunDevelopment"},smalltalk:{title:"Smalltalk",owner:"Golmote"},smarty:{title:"Smarty",require:"markup-templating",optional:"php",owner:"Golmote"},sml:{title:"SML",alias:"smlnj",aliasTitles:{smlnj:"SML/NJ"},owner:"RunDevelopment"},solidity:{title:"Solidity (Ethereum)",alias:"sol",require:"clike",owner:"glachaud"},"solution-file":{title:"Solution file",alias:"sln",owner:"RunDevelopment"},soy:{title:"Soy (Closure Template)",require:"markup-templating",owner:"Golmote"},sparql:{title:"SPARQL",require:"turtle",owner:"Triply-Dev",alias:"rq"},"splunk-spl":{title:"Splunk SPL",owner:"RunDevelopment"},sqf:{title:"SQF: Status Quo Function (Arma 3)",require:"clike",owner:"RunDevelopment"},sql:{title:"SQL",owner:"multipetros"},squirrel:{title:"Squirrel",require:"clike",owner:"RunDevelopment"},stan:{title:"Stan",owner:"RunDevelopment"},stata:{title:"Stata Ado",require:["mata","java","python"],owner:"RunDevelopment"},iecst:{title:"Structured Text (IEC 61131-3)",owner:"serhioromano"},stylus:{title:"Stylus",owner:"vkbansal"},supercollider:{title:"SuperCollider",alias:"sclang",owner:"RunDevelopment"},swift:{title:"Swift",owner:"chrischares"},systemd:{title:"Systemd configuration file",owner:"RunDevelopment"},"t4-templating":{title:"T4 templating",owner:"RunDevelopment"},"t4-cs":{title:"T4 Text Templates (C#)",require:["t4-templating","csharp"],alias:"t4",owner:"RunDevelopment"},"t4-vb":{title:"T4 Text Templates (VB)",require:["t4-templating","vbnet"],owner:"RunDevelopment"},tap:{title:"TAP",owner:"isaacs",require:"yaml"},tcl:{title:"Tcl",owner:"PeterChaplin"},tt2:{title:"Template Toolkit 2",require:["clike","markup-templating"],owner:"gflohr"},textile:{title:"Textile",require:"markup",optional:"css",owner:"Golmote"},toml:{title:"TOML",owner:"RunDevelopment"},tremor:{title:"Tremor",alias:["trickle","troy"],owner:"darach",aliasTitles:{trickle:"trickle",troy:"troy"}},turtle:{title:"Turtle",alias:"trig",aliasTitles:{trig:"TriG"},owner:"jakubklimek"},twig:{title:"Twig",require:"markup-templating",owner:"brandonkelly"},typescript:{title:"TypeScript",require:"javascript",optional:"js-templates",alias:"ts",owner:"vkbansal"},typoscript:{title:"TypoScript",alias:"tsconfig",aliasTitles:{tsconfig:"TSConfig"},owner:"dkern"},unrealscript:{title:"UnrealScript",alias:["uscript","uc"],owner:"RunDevelopment"},uorazor:{title:"UO Razor Script",owner:"jaseowns"},uri:{title:"URI",alias:"url",aliasTitles:{url:"URL"},owner:"RunDevelopment"},v:{title:"V",require:"clike",owner:"taggon"},vala:{title:"Vala",require:"clike",optional:"regex",owner:"TemplarVolk"},vbnet:{title:"VB.Net",require:"basic",owner:"Bigsby"},velocity:{title:"Velocity",require:"markup",owner:"Golmote"},verilog:{title:"Verilog",owner:"a-rey"},vhdl:{title:"VHDL",owner:"a-rey"},vim:{title:"vim",owner:"westonganger"},"visual-basic":{title:"Visual Basic",alias:["vb","vba"],aliasTitles:{vba:"VBA"},owner:"Golmote"},warpscript:{title:"WarpScript",owner:"RunDevelopment"},wasm:{title:"WebAssembly",owner:"Golmote"},"web-idl":{title:"Web IDL",alias:"webidl",owner:"RunDevelopment"},wgsl:{title:"WGSL",owner:"Dr4gonthree"},wiki:{title:"Wiki markup",require:"markup",owner:"Golmote"},wolfram:{title:"Wolfram language",alias:["mathematica","nb","wl"],aliasTitles:{mathematica:"Mathematica",nb:"Mathematica Notebook"},owner:"msollami"},wren:{title:"Wren",owner:"clsource"},xeora:{title:"Xeora",require:"markup",alias:"xeoracube",aliasTitles:{xeoracube:"XeoraCube"},owner:"freakmaxi"},"xml-doc":{title:"XML doc (.net)",require:"markup",modify:["csharp","fsharp","vbnet"],owner:"RunDevelopment"},xojo:{title:"Xojo (REALbasic)",owner:"Golmote"},xquery:{title:"XQuery",require:"markup",owner:"Golmote"},yaml:{title:"YAML",alias:"yml",owner:"hason"},yang:{title:"YANG",owner:"RunDevelopment"},zig:{title:"Zig",owner:"RunDevelopment"}},plugins:{meta:{path:"plugins/{id}/prism-{id}",link:"plugins/{id}/"},"line-highlight":{title:"Line Highlight",description:"Highlights specific lines and/or line ranges."},"line-numbers":{title:"Line Numbers",description:"Line number at the beginning of code lines.",owner:"kuba-kubula"},"show-invisibles":{title:"Show Invisibles",description:"Show hidden characters such as tabs and line breaks.",optional:["autolinker","data-uri-highlight"]},autolinker:{title:"Autolinker",description:"Converts URLs and emails in code to clickable links. Parses Markdown links in comments."},wpd:{title:"WebPlatform Docs",description:'Makes tokens link to <a href="https://webplatform.github.io/docs/">WebPlatform.org documentation</a>. The links open in a new tab.'},"custom-class":{title:"Custom Class",description:"This plugin allows you to prefix Prism's default classes (<code>.comment</code> can become <code>.namespace--comment</code>) or replace them with your defined ones (like <code>.editor__comment</code>). You can even add new classes.",owner:"dvkndn",noCSS:!0},"file-highlight":{title:"File Highlight",description:"Fetch external files and highlight them with Prism. Used on the Prism website itself.",noCSS:!0},"show-language":{title:"Show Language",description:"Display the highlighted language in code blocks (inline code does not show the label).",owner:"nauzilus",noCSS:!0,require:"toolbar"},"jsonp-highlight":{title:"JSONP Highlight",description:"Fetch content with JSONP and highlight some interesting content (e.g. GitHub/Gists or Bitbucket API).",noCSS:!0,owner:"nauzilus"},"highlight-keywords":{title:"Highlight Keywords",description:"Adds special CSS classes for each keyword for fine-grained highlighting.",owner:"vkbansal",noCSS:!0},"remove-initial-line-feed":{title:"Remove initial line feed",description:"Removes the initial line feed in code blocks.",owner:"Golmote",noCSS:!0},"inline-color":{title:"Inline color",description:"Adds a small inline preview for colors in style sheets.",require:"css-extras",owner:"RunDevelopment"},previewers:{title:"Previewers",description:"Previewers for angles, colors, gradients, easing and time.",require:"css-extras",owner:"Golmote"},autoloader:{title:"Autoloader",description:"Automatically loads the needed languages to highlight the code blocks.",owner:"Golmote",noCSS:!0},"keep-markup":{title:"Keep Markup",description:"Prevents custom markup from being dropped out during highlighting.",owner:"Golmote",optional:"normalize-whitespace",noCSS:!0},"command-line":{title:"Command Line",description:"Display a command line with a prompt and, optionally, the output/response from the commands.",owner:"chriswells0"},"unescaped-markup":{title:"Unescaped Markup",description:"Write markup without having to escape anything."},"normalize-whitespace":{title:"Normalize Whitespace",description:"Supports multiple operations to normalize whitespace in code blocks.",owner:"zeitgeist87",optional:"unescaped-markup",noCSS:!0},"data-uri-highlight":{title:"Data-URI Highlight",description:"Highlights data-URI contents.",owner:"Golmote",noCSS:!0},toolbar:{title:"Toolbar",description:"Attach a toolbar for plugins to easily register buttons on the top of a code block.",owner:"mAAdhaTTah"},"copy-to-clipboard":{title:"Copy to Clipboard Button",description:"Add a button that copies the code block to the clipboard when clicked.",owner:"mAAdhaTTah",require:"toolbar",noCSS:!0},"download-button":{title:"Download Button",description:"A button in the toolbar of a code block adding a convenient way to download a code file.",owner:"Golmote",require:"toolbar",noCSS:!0},"match-braces":{title:"Match braces",description:"Highlights matching braces.",owner:"RunDevelopment"},"diff-highlight":{title:"Diff Highlight",description:"Highlights the code inside diff blocks.",owner:"RunDevelopment",require:"diff"},"filter-highlight-all":{title:"Filter highlightAll",description:"Filters the elements the <code>highlightAll</code> and <code>highlightAllUnder</code> methods actually highlight.",owner:"RunDevelopment",noCSS:!0},treeview:{title:"Treeview",description:"A language with special styles to highlight file system tree structures.",owner:"Golmote"}}})},8722:(e,t,n)=>{const r=n(6969),a=n(8380),o=new Set;function i(e){void 0===e?e=Object.keys(r.languages).filter((e=>"meta"!=e)):Array.isArray(e)||(e=[e]);const t=[...o,...Object.keys(Prism.languages)];a(r,e,t).load((e=>{if(!(e in r.languages))return void(i.silent||console.warn("Language does not exist: "+e));const t="./prism-"+e;delete n.c[n(3157).resolve(t)],delete Prism.languages[e],n(3157)(t),o.add(e)}))}i.silent=!1,e.exports=i},9700:()=>{!function(e){function t(e,t){return"___"+e.toUpperCase()+t+"___"}Object.defineProperties(e.languages["markup-templating"]={},{buildPlaceholders:{value:function(n,r,a,o){if(n.language===r){var i=n.tokenStack=[];n.code=n.code.replace(a,(function(e){if("function"==typeof o&&!o(e))return e;for(var a,s=i.length;-1!==n.code.indexOf(a=t(r,s));)++s;return i[s]=e,a})),n.grammar=e.languages.markup}}},tokenizePlaceholders:{value:function(n,r){if(n.language===r&&n.tokenStack){n.grammar=e.languages[r];var a=0,o=Object.keys(n.tokenStack);!function i(s){for(var l=0;l<s.length&&!(a>=o.length);l++){var c=s[l];if("string"==typeof c||c.content&&"string"==typeof c.content){var u=o[a],d=n.tokenStack[u],p="string"==typeof c?c:c.content,f=t(r,u),m=p.indexOf(f);if(m>-1){++a;var h=p.substring(0,m),g=new e.Token(r,e.tokenize(d,n.grammar),"language-"+r,d),b=p.substring(m+f.length),v=[];h&&v.push.apply(v,i([h])),v.push(g),b&&v.push.apply(v,i([b])),"string"==typeof c?s.splice.apply(s,[l,1].concat(v)):c.content=v}}else c.content&&i(c.content)}return s}(n.tokens)}}}})}(Prism)},8692:(e,t,n)=>{var r={"./":8722};function a(e){var t=o(e);return n(t)}function o(e){if(!n.o(r,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return r[e]}a.keys=function(){return Object.keys(r)},a.resolve=o,e.exports=a,a.id=8692},3157:(e,t,n)=>{var r={"./":8722};function a(e){var t=o(e);return n(t)}function o(e){if(!n.o(r,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return r[e]}a.keys=function(){return Object.keys(r)},a.resolve=o,e.exports=a,a.id=3157},8380:e=>{"use strict";var t=function(){var e=function(){};function t(e,t){Array.isArray(e)?e.forEach(t):null!=e&&t(e,0)}function n(e){for(var t={},n=0,r=e.length;n<r;n++)t[e[n]]=!0;return t}function r(e){var n={},r=[];function a(r,o){if(!(r in n)){o.push(r);var i=o.indexOf(r);if(i<o.length-1)throw new Error("Circular dependency: "+o.slice(i).join(" -> "));var s={},l=e[r];if(l){function c(t){if(!(t in e))throw new Error(r+" depends on an unknown component "+t);if(!(t in s))for(var i in a(t,o),s[t]=!0,n[t])s[i]=!0}t(l.require,c),t(l.optional,c),t(l.modify,c)}n[r]=s,o.pop()}}return function(e){var t=n[e];return t||(a(e,r),t=n[e]),t}}function a(e){for(var t in e)return!0;return!1}return function(o,i,s){var l=function(e){var t={};for(var n in e){var r=e[n];for(var a in r)if("meta"!=a){var o=r[a];t[a]="string"==typeof o?{title:o}:o}}return t}(o),c=function(e){var n;return function(r){if(r in e)return r;if(!n)for(var a in n={},e){var o=e[a];t(o&&o.alias,(function(t){if(t in n)throw new Error(t+" cannot be alias for both "+a+" and "+n[t]);if(t in e)throw new Error(t+" cannot be alias of "+a+" because it is a component.");n[t]=a}))}return n[r]||r}}(l);i=i.map(c),s=(s||[]).map(c);var u=n(i),d=n(s);i.forEach((function e(n){var r=l[n];t(r&&r.require,(function(t){t in d||(u[t]=!0,e(t))}))}));for(var p,f=r(l),m=u;a(m);){for(var h in p={},m){var g=l[h];t(g&&g.modify,(function(e){e in d&&(p[e]=!0)}))}for(var b in d)if(!(b in u))for(var v in f(b))if(v in u){p[b]=!0;break}for(var y in m=p)u[y]=!0}var w={getIds:function(){var e=[];return w.load((function(t){e.push(t)})),e},load:function(t,n){return function(t,n,r,a){var o=a?a.series:void 0,i=a?a.parallel:e,s={},l={};function c(e){if(e in s)return s[e];l[e]=!0;var a,u=[];for(var d in t(e))d in n&&u.push(d);if(0===u.length)a=r(e);else{var p=i(u.map((function(e){var t=c(e);return delete l[e],t})));o?a=o(p,(function(){return r(e)})):r(e)}return s[e]=a}for(var u in n)c(u);var d=[];for(var p in l)d.push(s[p]);return i(d)}(f,u,t,n)}};return w}}();e.exports=t},2694:(e,t,n)=>{"use strict";var r=n(6925);function a(){}function o(){}o.resetWarningCache=a,e.exports=function(){function e(e,t,n,a,o,i){if(i!==r){var s=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw s.name="Invariant Violation",s}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:o,resetWarningCache:a};return n.PropTypes=n,n}},5556:(e,t,n)=>{e.exports=n(2694)()},6925:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},2551:(e,t,n)=>{"use strict";var r=n(6540),a=n(9982);function o(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n<arguments.length;n++)t+="&args[]="+encodeURIComponent(arguments[n]);return"Minified React error #"+e+"; visit "+t+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}var i=new Set,s={};function l(e,t){c(e,t),c(e+"Capture",t)}function c(e,t){for(s[e]=t,e=0;e<t.length;e++)i.add(t[e])}var u=!("undefined"==typeof window||void 0===window.document||void 0===window.document.createElement),d=Object.prototype.hasOwnProperty,p=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,f={},m={};function h(e,t,n,r,a,o,i){this.acceptsBooleans=2===t||3===t||4===t,this.attributeName=r,this.attributeNamespace=a,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=o,this.removeEmptyString=i}var g={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach((function(e){g[e]=new h(e,0,!1,e,null,!1,!1)})),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach((function(e){var t=e[0];g[t]=new h(t,1,!1,e[1],null,!1,!1)})),["contentEditable","draggable","spellCheck","value"].forEach((function(e){g[e]=new h(e,2,!1,e.toLowerCase(),null,!1,!1)})),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach((function(e){g[e]=new h(e,2,!1,e,null,!1,!1)})),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach((function(e){g[e]=new h(e,3,!1,e.toLowerCase(),null,!1,!1)})),["checked","multiple","muted","selected"].forEach((function(e){g[e]=new h(e,3,!0,e,null,!1,!1)})),["capture","download"].forEach((function(e){g[e]=new h(e,4,!1,e,null,!1,!1)})),["cols","rows","size","span"].forEach((function(e){g[e]=new h(e,6,!1,e,null,!1,!1)})),["rowSpan","start"].forEach((function(e){g[e]=new h(e,5,!1,e.toLowerCase(),null,!1,!1)}));var b=/[\-:]([a-z])/g;function v(e){return e[1].toUpperCase()}function y(e,t,n,r){var a=g.hasOwnProperty(t)?g[t]:null;(null!==a?0!==a.type:r||!(2<t.length)||"o"!==t[0]&&"O"!==t[0]||"n"!==t[1]&&"N"!==t[1])&&(function(e,t,n,r){if(null==t||function(e,t,n,r){if(null!==n&&0===n.type)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return!r&&(null!==n?!n.acceptsBooleans:"data-"!==(e=e.toLowerCase().slice(0,5))&&"aria-"!==e);default:return!1}}(e,t,n,r))return!0;if(r)return!1;if(null!==n)switch(n.type){case 3:return!t;case 4:return!1===t;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}(t,n,a,r)&&(n=null),r||null===a?function(e){return!!d.call(m,e)||!d.call(f,e)&&(p.test(e)?m[e]=!0:(f[e]=!0,!1))}(t)&&(null===n?e.removeAttribute(t):e.setAttribute(t,""+n)):a.mustUseProperty?e[a.propertyName]=null===n?3!==a.type&&"":n:(t=a.attributeName,r=a.attributeNamespace,null===n?e.removeAttribute(t):(n=3===(a=a.type)||4===a&&!0===n?"":""+n,r?e.setAttributeNS(r,t,n):e.setAttribute(t,n))))}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach((function(e){var t=e.replace(b,v);g[t]=new h(t,1,!1,e,null,!1,!1)})),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach((function(e){var t=e.replace(b,v);g[t]=new h(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)})),["xml:base","xml:lang","xml:space"].forEach((function(e){var t=e.replace(b,v);g[t]=new h(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)})),["tabIndex","crossOrigin"].forEach((function(e){g[e]=new h(e,1,!1,e.toLowerCase(),null,!1,!1)})),g.xlinkHref=new h("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach((function(e){g[e]=new h(e,1,!1,e.toLowerCase(),null,!0,!0)}));var w=r.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,x=Symbol.for("react.element"),k=Symbol.for("react.portal"),S=Symbol.for("react.fragment"),_=Symbol.for("react.strict_mode"),E=Symbol.for("react.profiler"),C=Symbol.for("react.provider"),A=Symbol.for("react.context"),T=Symbol.for("react.forward_ref"),j=Symbol.for("react.suspense"),N=Symbol.for("react.suspense_list"),L=Symbol.for("react.memo"),R=Symbol.for("react.lazy");Symbol.for("react.scope"),Symbol.for("react.debug_trace_mode");var P=Symbol.for("react.offscreen");Symbol.for("react.legacy_hidden"),Symbol.for("react.cache"),Symbol.for("react.tracing_marker");var O=Symbol.iterator;function D(e){return null===e||"object"!=typeof e?null:"function"==typeof(e=O&&e[O]||e["@@iterator"])?e:null}var I,F=Object.assign;function M(e){if(void 0===I)try{throw Error()}catch(n){var t=n.stack.trim().match(/\n( *(at )?)/);I=t&&t[1]||""}return"\n"+I+e}var z=!1;function B(e,t){if(!e||z)return"";z=!0;var n=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{if(t)if(t=function(){throw Error()},Object.defineProperty(t.prototype,"props",{set:function(){throw Error()}}),"object"==typeof Reflect&&Reflect.construct){try{Reflect.construct(t,[])}catch(c){var r=c}Reflect.construct(e,[],t)}else{try{t.call()}catch(c){r=c}e.call(t.prototype)}else{try{throw Error()}catch(c){r=c}e()}}catch(c){if(c&&r&&"string"==typeof c.stack){for(var a=c.stack.split("\n"),o=r.stack.split("\n"),i=a.length-1,s=o.length-1;1<=i&&0<=s&&a[i]!==o[s];)s--;for(;1<=i&&0<=s;i--,s--)if(a[i]!==o[s]){if(1!==i||1!==s)do{if(i--,0>--s||a[i]!==o[s]){var l="\n"+a[i].replace(" at new "," at ");return e.displayName&&l.includes("<anonymous>")&&(l=l.replace("<anonymous>",e.displayName)),l}}while(1<=i&&0<=s);break}}}finally{z=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?M(e):""}function $(e){switch(e.tag){case 5:return M(e.type);case 16:return M("Lazy");case 13:return M("Suspense");case 19:return M("SuspenseList");case 0:case 2:case 15:return e=B(e.type,!1);case 11:return e=B(e.type.render,!1);case 1:return e=B(e.type,!0);default:return""}}function q(e){if(null==e)return null;if("function"==typeof e)return e.displayName||e.name||null;if("string"==typeof e)return e;switch(e){case S:return"Fragment";case k:return"Portal";case E:return"Profiler";case _:return"StrictMode";case j:return"Suspense";case N:return"SuspenseList"}if("object"==typeof e)switch(e.$$typeof){case A:return(e.displayName||"Context")+".Consumer";case C:return(e._context.displayName||"Context")+".Provider";case T:var t=e.render;return(e=e.displayName)||(e=""!==(e=t.displayName||t.name||"")?"ForwardRef("+e+")":"ForwardRef"),e;case L:return null!==(t=e.displayName||null)?t:q(e.type)||"Memo";case R:t=e._payload,e=e._init;try{return q(e(t))}catch(n){}}return null}function U(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=(e=t.render).displayName||e.name||"",t.displayName||(""!==e?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return q(t);case 8:return t===_?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if("function"==typeof t)return t.displayName||t.name||null;if("string"==typeof t)return t}return null}function H(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":case"object":return e;default:return""}}function G(e){var t=e.type;return(e=e.nodeName)&&"input"===e.toLowerCase()&&("checkbox"===t||"radio"===t)}function V(e){e._valueTracker||(e._valueTracker=function(e){var t=G(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&void 0!==n&&"function"==typeof n.get&&"function"==typeof n.set){var a=n.get,o=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return a.call(this)},set:function(e){r=""+e,o.call(this,e)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(e){r=""+e},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}(e))}function W(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=G(e)?e.checked?"true":"false":e.value),(e=r)!==n&&(t.setValue(e),!0)}function Q(e){if(void 0===(e=e||("undefined"!=typeof document?document:void 0)))return null;try{return e.activeElement||e.body}catch(t){return e.body}}function K(e,t){var n=t.checked;return F({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:null!=n?n:e._wrapperState.initialChecked})}function Y(e,t){var n=null==t.defaultValue?"":t.defaultValue,r=null!=t.checked?t.checked:t.defaultChecked;n=H(null!=t.value?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:"checkbox"===t.type||"radio"===t.type?null!=t.checked:null!=t.value}}function Z(e,t){null!=(t=t.checked)&&y(e,"checked",t,!1)}function X(e,t){Z(e,t);var n=H(t.value),r=t.type;if(null!=n)"number"===r?(0===n&&""===e.value||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if("submit"===r||"reset"===r)return void e.removeAttribute("value");t.hasOwnProperty("value")?ee(e,t.type,n):t.hasOwnProperty("defaultValue")&&ee(e,t.type,H(t.defaultValue)),null==t.checked&&null!=t.defaultChecked&&(e.defaultChecked=!!t.defaultChecked)}function J(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!("submit"!==r&&"reset"!==r||void 0!==t.value&&null!==t.value))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}""!==(n=e.name)&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,""!==n&&(e.name=n)}function ee(e,t,n){"number"===t&&Q(e.ownerDocument)===e||(null==n?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var te=Array.isArray;function ne(e,t,n,r){if(e=e.options,t){t={};for(var a=0;a<n.length;a++)t["$"+n[a]]=!0;for(n=0;n<e.length;n++)a=t.hasOwnProperty("$"+e[n].value),e[n].selected!==a&&(e[n].selected=a),a&&r&&(e[n].defaultSelected=!0)}else{for(n=""+H(n),t=null,a=0;a<e.length;a++){if(e[a].value===n)return e[a].selected=!0,void(r&&(e[a].defaultSelected=!0));null!==t||e[a].disabled||(t=e[a])}null!==t&&(t.selected=!0)}}function re(e,t){if(null!=t.dangerouslySetInnerHTML)throw Error(o(91));return F({},t,{value:void 0,defaultValue:void 0,children:""+e._wrapperState.initialValue})}function ae(e,t){var n=t.value;if(null==n){if(n=t.children,t=t.defaultValue,null!=n){if(null!=t)throw Error(o(92));if(te(n)){if(1<n.length)throw Error(o(93));n=n[0]}t=n}null==t&&(t=""),n=t}e._wrapperState={initialValue:H(n)}}function oe(e,t){var n=H(t.value),r=H(t.defaultValue);null!=n&&((n=""+n)!==e.value&&(e.value=n),null==t.defaultValue&&e.defaultValue!==n&&(e.defaultValue=n)),null!=r&&(e.defaultValue=""+r)}function ie(e){var t=e.textContent;t===e._wrapperState.initialValue&&""!==t&&null!==t&&(e.value=t)}function se(e){switch(e){case"svg":return"http://www.w3.org/2000/svg";case"math":return"http://www.w3.org/1998/Math/MathML";default:return"http://www.w3.org/1999/xhtml"}}function le(e,t){return null==e||"http://www.w3.org/1999/xhtml"===e?se(t):"http://www.w3.org/2000/svg"===e&&"foreignObject"===t?"http://www.w3.org/1999/xhtml":e}var ce,ue,de=(ue=function(e,t){if("http://www.w3.org/2000/svg"!==e.namespaceURI||"innerHTML"in e)e.innerHTML=t;else{for((ce=ce||document.createElement("div")).innerHTML="<svg>"+t.valueOf().toString()+"</svg>",t=ce.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}},"undefined"!=typeof MSApp&&MSApp.execUnsafeLocalFunction?function(e,t,n,r){MSApp.execUnsafeLocalFunction((function(){return ue(e,t)}))}:ue);function pe(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&3===n.nodeType)return void(n.nodeValue=t)}e.textContent=t}var fe={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},me=["Webkit","ms","Moz","O"];function he(e,t,n){return null==t||"boolean"==typeof t||""===t?"":n||"number"!=typeof t||0===t||fe.hasOwnProperty(e)&&fe[e]?(""+t).trim():t+"px"}function ge(e,t){for(var n in e=e.style,t)if(t.hasOwnProperty(n)){var r=0===n.indexOf("--"),a=he(n,t[n],r);"float"===n&&(n="cssFloat"),r?e.setProperty(n,a):e[n]=a}}Object.keys(fe).forEach((function(e){me.forEach((function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),fe[t]=fe[e]}))}));var be=F({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function ve(e,t){if(t){if(be[e]&&(null!=t.children||null!=t.dangerouslySetInnerHTML))throw Error(o(137,e));if(null!=t.dangerouslySetInnerHTML){if(null!=t.children)throw Error(o(60));if("object"!=typeof t.dangerouslySetInnerHTML||!("__html"in t.dangerouslySetInnerHTML))throw Error(o(61))}if(null!=t.style&&"object"!=typeof t.style)throw Error(o(62))}}function ye(e,t){if(-1===e.indexOf("-"))return"string"==typeof t.is;switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var we=null;function xe(e){return(e=e.target||e.srcElement||window).correspondingUseElement&&(e=e.correspondingUseElement),3===e.nodeType?e.parentNode:e}var ke=null,Se=null,_e=null;function Ee(e){if(e=ya(e)){if("function"!=typeof ke)throw Error(o(280));var t=e.stateNode;t&&(t=xa(t),ke(e.stateNode,e.type,t))}}function Ce(e){Se?_e?_e.push(e):_e=[e]:Se=e}function Ae(){if(Se){var e=Se,t=_e;if(_e=Se=null,Ee(e),t)for(e=0;e<t.length;e++)Ee(t[e])}}function Te(e,t){return e(t)}function je(){}var Ne=!1;function Le(e,t,n){if(Ne)return e(t,n);Ne=!0;try{return Te(e,t,n)}finally{Ne=!1,(null!==Se||null!==_e)&&(je(),Ae())}}function Re(e,t){var n=e.stateNode;if(null===n)return null;var r=xa(n);if(null===r)return null;n=r[t];e:switch(t){case"onClick":case"onClickCapture":case"onDoubleClick":case"onDoubleClickCapture":case"onMouseDown":case"onMouseDownCapture":case"onMouseMove":case"onMouseMoveCapture":case"onMouseUp":case"onMouseUpCapture":case"onMouseEnter":(r=!r.disabled)||(r=!("button"===(e=e.type)||"input"===e||"select"===e||"textarea"===e)),e=!r;break e;default:e=!1}if(e)return null;if(n&&"function"!=typeof n)throw Error(o(231,t,typeof n));return n}var Pe=!1;if(u)try{var Oe={};Object.defineProperty(Oe,"passive",{get:function(){Pe=!0}}),window.addEventListener("test",Oe,Oe),window.removeEventListener("test",Oe,Oe)}catch(ue){Pe=!1}function De(e,t,n,r,a,o,i,s,l){var c=Array.prototype.slice.call(arguments,3);try{t.apply(n,c)}catch(u){this.onError(u)}}var Ie=!1,Fe=null,Me=!1,ze=null,Be={onError:function(e){Ie=!0,Fe=e}};function $e(e,t,n,r,a,o,i,s,l){Ie=!1,Fe=null,De.apply(Be,arguments)}function qe(e){var t=e,n=e;if(e.alternate)for(;t.return;)t=t.return;else{e=t;do{!!(4098&(t=e).flags)&&(n=t.return),e=t.return}while(e)}return 3===t.tag?n:null}function Ue(e){if(13===e.tag){var t=e.memoizedState;if(null===t&&(null!==(e=e.alternate)&&(t=e.memoizedState)),null!==t)return t.dehydrated}return null}function He(e){if(qe(e)!==e)throw Error(o(188))}function Ge(e){return null!==(e=function(e){var t=e.alternate;if(!t){if(null===(t=qe(e)))throw Error(o(188));return t!==e?null:e}for(var n=e,r=t;;){var a=n.return;if(null===a)break;var i=a.alternate;if(null===i){if(null!==(r=a.return)){n=r;continue}break}if(a.child===i.child){for(i=a.child;i;){if(i===n)return He(a),e;if(i===r)return He(a),t;i=i.sibling}throw Error(o(188))}if(n.return!==r.return)n=a,r=i;else{for(var s=!1,l=a.child;l;){if(l===n){s=!0,n=a,r=i;break}if(l===r){s=!0,r=a,n=i;break}l=l.sibling}if(!s){for(l=i.child;l;){if(l===n){s=!0,n=i,r=a;break}if(l===r){s=!0,r=i,n=a;break}l=l.sibling}if(!s)throw Error(o(189))}}if(n.alternate!==r)throw Error(o(190))}if(3!==n.tag)throw Error(o(188));return n.stateNode.current===n?e:t}(e))?Ve(e):null}function Ve(e){if(5===e.tag||6===e.tag)return e;for(e=e.child;null!==e;){var t=Ve(e);if(null!==t)return t;e=e.sibling}return null}var We=a.unstable_scheduleCallback,Qe=a.unstable_cancelCallback,Ke=a.unstable_shouldYield,Ye=a.unstable_requestPaint,Ze=a.unstable_now,Xe=a.unstable_getCurrentPriorityLevel,Je=a.unstable_ImmediatePriority,et=a.unstable_UserBlockingPriority,tt=a.unstable_NormalPriority,nt=a.unstable_LowPriority,rt=a.unstable_IdlePriority,at=null,ot=null;var it=Math.clz32?Math.clz32:function(e){return e>>>=0,0===e?32:31-(st(e)/lt|0)|0},st=Math.log,lt=Math.LN2;var ct=64,ut=4194304;function dt(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return 4194240&e;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return 130023424&e;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function pt(e,t){var n=e.pendingLanes;if(0===n)return 0;var r=0,a=e.suspendedLanes,o=e.pingedLanes,i=268435455&n;if(0!==i){var s=i&~a;0!==s?r=dt(s):0!==(o&=i)&&(r=dt(o))}else 0!==(i=n&~a)?r=dt(i):0!==o&&(r=dt(o));if(0===r)return 0;if(0!==t&&t!==r&&!(t&a)&&((a=r&-r)>=(o=t&-t)||16===a&&4194240&o))return t;if(4&r&&(r|=16&n),0!==(t=e.entangledLanes))for(e=e.entanglements,t&=r;0<t;)a=1<<(n=31-it(t)),r|=e[n],t&=~a;return r}function ft(e,t){switch(e){case 1:case 2:case 4:return t+250;case 8:case 16:case 32:case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;default:return-1}}function mt(e){return 0!==(e=-1073741825&e.pendingLanes)?e:1073741824&e?1073741824:0}function ht(){var e=ct;return!(4194240&(ct<<=1))&&(ct=64),e}function gt(e){for(var t=[],n=0;31>n;n++)t.push(e);return t}function bt(e,t,n){e.pendingLanes|=t,536870912!==t&&(e.suspendedLanes=0,e.pingedLanes=0),(e=e.eventTimes)[t=31-it(t)]=n}function vt(e,t){var n=e.entangledLanes|=t;for(e=e.entanglements;n;){var r=31-it(n),a=1<<r;a&t|e[r]&t&&(e[r]|=t),n&=~a}}var yt=0;function wt(e){return 1<(e&=-e)?4<e?268435455&e?16:536870912:4:1}var xt,kt,St,_t,Et,Ct=!1,At=[],Tt=null,jt=null,Nt=null,Lt=new Map,Rt=new Map,Pt=[],Ot="mousedown mouseup touchcancel touchend touchstart auxclick dblclick pointercancel pointerdown pointerup dragend dragstart drop compositionend compositionstart keydown keypress keyup input textInput copy cut paste click change contextmenu reset submit".split(" ");function Dt(e,t){switch(e){case"focusin":case"focusout":Tt=null;break;case"dragenter":case"dragleave":jt=null;break;case"mouseover":case"mouseout":Nt=null;break;case"pointerover":case"pointerout":Lt.delete(t.pointerId);break;case"gotpointercapture":case"lostpointercapture":Rt.delete(t.pointerId)}}function It(e,t,n,r,a,o){return null===e||e.nativeEvent!==o?(e={blockedOn:t,domEventName:n,eventSystemFlags:r,nativeEvent:o,targetContainers:[a]},null!==t&&(null!==(t=ya(t))&&kt(t)),e):(e.eventSystemFlags|=r,t=e.targetContainers,null!==a&&-1===t.indexOf(a)&&t.push(a),e)}function Ft(e){var t=va(e.target);if(null!==t){var n=qe(t);if(null!==n)if(13===(t=n.tag)){if(null!==(t=Ue(n)))return e.blockedOn=t,void Et(e.priority,(function(){St(n)}))}else if(3===t&&n.stateNode.current.memoizedState.isDehydrated)return void(e.blockedOn=3===n.tag?n.stateNode.containerInfo:null)}e.blockedOn=null}function Mt(e){if(null!==e.blockedOn)return!1;for(var t=e.targetContainers;0<t.length;){var n=Kt(e.domEventName,e.eventSystemFlags,t[0],e.nativeEvent);if(null!==n)return null!==(t=ya(n))&&kt(t),e.blockedOn=n,!1;var r=new(n=e.nativeEvent).constructor(n.type,n);we=r,n.target.dispatchEvent(r),we=null,t.shift()}return!0}function zt(e,t,n){Mt(e)&&n.delete(t)}function Bt(){Ct=!1,null!==Tt&&Mt(Tt)&&(Tt=null),null!==jt&&Mt(jt)&&(jt=null),null!==Nt&&Mt(Nt)&&(Nt=null),Lt.forEach(zt),Rt.forEach(zt)}function $t(e,t){e.blockedOn===t&&(e.blockedOn=null,Ct||(Ct=!0,a.unstable_scheduleCallback(a.unstable_NormalPriority,Bt)))}function qt(e){function t(t){return $t(t,e)}if(0<At.length){$t(At[0],e);for(var n=1;n<At.length;n++){var r=At[n];r.blockedOn===e&&(r.blockedOn=null)}}for(null!==Tt&&$t(Tt,e),null!==jt&&$t(jt,e),null!==Nt&&$t(Nt,e),Lt.forEach(t),Rt.forEach(t),n=0;n<Pt.length;n++)(r=Pt[n]).blockedOn===e&&(r.blockedOn=null);for(;0<Pt.length&&null===(n=Pt[0]).blockedOn;)Ft(n),null===n.blockedOn&&Pt.shift()}var Ut=w.ReactCurrentBatchConfig,Ht=!0;function Gt(e,t,n,r){var a=yt,o=Ut.transition;Ut.transition=null;try{yt=1,Wt(e,t,n,r)}finally{yt=a,Ut.transition=o}}function Vt(e,t,n,r){var a=yt,o=Ut.transition;Ut.transition=null;try{yt=4,Wt(e,t,n,r)}finally{yt=a,Ut.transition=o}}function Wt(e,t,n,r){if(Ht){var a=Kt(e,t,n,r);if(null===a)Hr(e,t,r,Qt,n),Dt(e,r);else if(function(e,t,n,r,a){switch(t){case"focusin":return Tt=It(Tt,e,t,n,r,a),!0;case"dragenter":return jt=It(jt,e,t,n,r,a),!0;case"mouseover":return Nt=It(Nt,e,t,n,r,a),!0;case"pointerover":var o=a.pointerId;return Lt.set(o,It(Lt.get(o)||null,e,t,n,r,a)),!0;case"gotpointercapture":return o=a.pointerId,Rt.set(o,It(Rt.get(o)||null,e,t,n,r,a)),!0}return!1}(a,e,t,n,r))r.stopPropagation();else if(Dt(e,r),4&t&&-1<Ot.indexOf(e)){for(;null!==a;){var o=ya(a);if(null!==o&&xt(o),null===(o=Kt(e,t,n,r))&&Hr(e,t,r,Qt,n),o===a)break;a=o}null!==a&&r.stopPropagation()}else Hr(e,t,r,null,n)}}var Qt=null;function Kt(e,t,n,r){if(Qt=null,null!==(e=va(e=xe(r))))if(null===(t=qe(e)))e=null;else if(13===(n=t.tag)){if(null!==(e=Ue(t)))return e;e=null}else if(3===n){if(t.stateNode.current.memoizedState.isDehydrated)return 3===t.tag?t.stateNode.containerInfo:null;e=null}else t!==e&&(e=null);return Qt=e,null}function Yt(e){switch(e){case"cancel":case"click":case"close":case"contextmenu":case"copy":case"cut":case"auxclick":case"dblclick":case"dragend":case"dragstart":case"drop":case"focusin":case"focusout":case"input":case"invalid":case"keydown":case"keypress":case"keyup":case"mousedown":case"mouseup":case"paste":case"pause":case"play":case"pointercancel":case"pointerdown":case"pointerup":case"ratechange":case"reset":case"resize":case"seeked":case"submit":case"touchcancel":case"touchend":case"touchstart":case"volumechange":case"change":case"selectionchange":case"textInput":case"compositionstart":case"compositionend":case"compositionupdate":case"beforeblur":case"afterblur":case"beforeinput":case"blur":case"fullscreenchange":case"focus":case"hashchange":case"popstate":case"select":case"selectstart":return 1;case"drag":case"dragenter":case"dragexit":case"dragleave":case"dragover":case"mousemove":case"mouseout":case"mouseover":case"pointermove":case"pointerout":case"pointerover":case"scroll":case"toggle":case"touchmove":case"wheel":case"mouseenter":case"mouseleave":case"pointerenter":case"pointerleave":return 4;case"message":switch(Xe()){case Je:return 1;case et:return 4;case tt:case nt:return 16;case rt:return 536870912;default:return 16}default:return 16}}var Zt=null,Xt=null,Jt=null;function en(){if(Jt)return Jt;var e,t,n=Xt,r=n.length,a="value"in Zt?Zt.value:Zt.textContent,o=a.length;for(e=0;e<r&&n[e]===a[e];e++);var i=r-e;for(t=1;t<=i&&n[r-t]===a[o-t];t++);return Jt=a.slice(e,1<t?1-t:void 0)}function tn(e){var t=e.keyCode;return"charCode"in e?0===(e=e.charCode)&&13===t&&(e=13):e=t,10===e&&(e=13),32<=e||13===e?e:0}function nn(){return!0}function rn(){return!1}function an(e){function t(t,n,r,a,o){for(var i in this._reactName=t,this._targetInst=r,this.type=n,this.nativeEvent=a,this.target=o,this.currentTarget=null,e)e.hasOwnProperty(i)&&(t=e[i],this[i]=t?t(a):a[i]);return this.isDefaultPrevented=(null!=a.defaultPrevented?a.defaultPrevented:!1===a.returnValue)?nn:rn,this.isPropagationStopped=rn,this}return F(t.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=nn)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=nn)},persist:function(){},isPersistent:nn}),t}var on,sn,ln,cn={eventPhase:0,bubbles:0,cancelable:0,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:0,isTrusted:0},un=an(cn),dn=F({},cn,{view:0,detail:0}),pn=an(dn),fn=F({},dn,{screenX:0,screenY:0,clientX:0,clientY:0,pageX:0,pageY:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,getModifierState:En,button:0,buttons:0,relatedTarget:function(e){return void 0===e.relatedTarget?e.fromElement===e.srcElement?e.toElement:e.fromElement:e.relatedTarget},movementX:function(e){return"movementX"in e?e.movementX:(e!==ln&&(ln&&"mousemove"===e.type?(on=e.screenX-ln.screenX,sn=e.screenY-ln.screenY):sn=on=0,ln=e),on)},movementY:function(e){return"movementY"in e?e.movementY:sn}}),mn=an(fn),hn=an(F({},fn,{dataTransfer:0})),gn=an(F({},dn,{relatedTarget:0})),bn=an(F({},cn,{animationName:0,elapsedTime:0,pseudoElement:0})),vn=F({},cn,{clipboardData:function(e){return"clipboardData"in e?e.clipboardData:window.clipboardData}}),yn=an(vn),wn=an(F({},cn,{data:0})),xn={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},kn={8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",45:"Insert",46:"Delete",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"NumLock",145:"ScrollLock",224:"Meta"},Sn={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"};function _n(e){var t=this.nativeEvent;return t.getModifierState?t.getModifierState(e):!!(e=Sn[e])&&!!t[e]}function En(){return _n}var Cn=F({},dn,{key:function(e){if(e.key){var t=xn[e.key]||e.key;if("Unidentified"!==t)return t}return"keypress"===e.type?13===(e=tn(e))?"Enter":String.fromCharCode(e):"keydown"===e.type||"keyup"===e.type?kn[e.keyCode]||"Unidentified":""},code:0,location:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,repeat:0,locale:0,getModifierState:En,charCode:function(e){return"keypress"===e.type?tn(e):0},keyCode:function(e){return"keydown"===e.type||"keyup"===e.type?e.keyCode:0},which:function(e){return"keypress"===e.type?tn(e):"keydown"===e.type||"keyup"===e.type?e.keyCode:0}}),An=an(Cn),Tn=an(F({},fn,{pointerId:0,width:0,height:0,pressure:0,tangentialPressure:0,tiltX:0,tiltY:0,twist:0,pointerType:0,isPrimary:0})),jn=an(F({},dn,{touches:0,targetTouches:0,changedTouches:0,altKey:0,metaKey:0,ctrlKey:0,shiftKey:0,getModifierState:En})),Nn=an(F({},cn,{propertyName:0,elapsedTime:0,pseudoElement:0})),Ln=F({},fn,{deltaX:function(e){return"deltaX"in e?e.deltaX:"wheelDeltaX"in e?-e.wheelDeltaX:0},deltaY:function(e){return"deltaY"in e?e.deltaY:"wheelDeltaY"in e?-e.wheelDeltaY:"wheelDelta"in e?-e.wheelDelta:0},deltaZ:0,deltaMode:0}),Rn=an(Ln),Pn=[9,13,27,32],On=u&&"CompositionEvent"in window,Dn=null;u&&"documentMode"in document&&(Dn=document.documentMode);var In=u&&"TextEvent"in window&&!Dn,Fn=u&&(!On||Dn&&8<Dn&&11>=Dn),Mn=String.fromCharCode(32),zn=!1;function Bn(e,t){switch(e){case"keyup":return-1!==Pn.indexOf(t.keyCode);case"keydown":return 229!==t.keyCode;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function $n(e){return"object"==typeof(e=e.detail)&&"data"in e?e.data:null}var qn=!1;var Un={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};function Hn(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return"input"===t?!!Un[e.type]:"textarea"===t}function Gn(e,t,n,r){Ce(r),0<(t=Vr(t,"onChange")).length&&(n=new un("onChange","change",null,n,r),e.push({event:n,listeners:t}))}var Vn=null,Wn=null;function Qn(e){Mr(e,0)}function Kn(e){if(W(wa(e)))return e}function Yn(e,t){if("change"===e)return t}var Zn=!1;if(u){var Xn;if(u){var Jn="oninput"in document;if(!Jn){var er=document.createElement("div");er.setAttribute("oninput","return;"),Jn="function"==typeof er.oninput}Xn=Jn}else Xn=!1;Zn=Xn&&(!document.documentMode||9<document.documentMode)}function tr(){Vn&&(Vn.detachEvent("onpropertychange",nr),Wn=Vn=null)}function nr(e){if("value"===e.propertyName&&Kn(Wn)){var t=[];Gn(t,Wn,e,xe(e)),Le(Qn,t)}}function rr(e,t,n){"focusin"===e?(tr(),Wn=n,(Vn=t).attachEvent("onpropertychange",nr)):"focusout"===e&&tr()}function ar(e){if("selectionchange"===e||"keyup"===e||"keydown"===e)return Kn(Wn)}function or(e,t){if("click"===e)return Kn(t)}function ir(e,t){if("input"===e||"change"===e)return Kn(t)}var sr="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t};function lr(e,t){if(sr(e,t))return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;var n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(r=0;r<n.length;r++){var a=n[r];if(!d.call(t,a)||!sr(e[a],t[a]))return!1}return!0}function cr(e){for(;e&&e.firstChild;)e=e.firstChild;return e}function ur(e,t){var n,r=cr(e);for(e=0;r;){if(3===r.nodeType){if(n=e+r.textContent.length,e<=t&&n>=t)return{node:r,offset:t-e};e=n}e:{for(;r;){if(r.nextSibling){r=r.nextSibling;break e}r=r.parentNode}r=void 0}r=cr(r)}}function dr(e,t){return!(!e||!t)&&(e===t||(!e||3!==e.nodeType)&&(t&&3===t.nodeType?dr(e,t.parentNode):"contains"in e?e.contains(t):!!e.compareDocumentPosition&&!!(16&e.compareDocumentPosition(t))))}function pr(){for(var e=window,t=Q();t instanceof e.HTMLIFrameElement;){try{var n="string"==typeof t.contentWindow.location.href}catch(r){n=!1}if(!n)break;t=Q((e=t.contentWindow).document)}return t}function fr(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&("input"===t&&("text"===e.type||"search"===e.type||"tel"===e.type||"url"===e.type||"password"===e.type)||"textarea"===t||"true"===e.contentEditable)}function mr(e){var t=pr(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&dr(n.ownerDocument.documentElement,n)){if(null!==r&&fr(n))if(t=r.start,void 0===(e=r.end)&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if((e=(t=n.ownerDocument||document)&&t.defaultView||window).getSelection){e=e.getSelection();var a=n.textContent.length,o=Math.min(r.start,a);r=void 0===r.end?o:Math.min(r.end,a),!e.extend&&o>r&&(a=r,r=o,o=a),a=ur(n,o);var i=ur(n,r);a&&i&&(1!==e.rangeCount||e.anchorNode!==a.node||e.anchorOffset!==a.offset||e.focusNode!==i.node||e.focusOffset!==i.offset)&&((t=t.createRange()).setStart(a.node,a.offset),e.removeAllRanges(),o>r?(e.addRange(t),e.extend(i.node,i.offset)):(t.setEnd(i.node,i.offset),e.addRange(t)))}for(t=[],e=n;e=e.parentNode;)1===e.nodeType&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for("function"==typeof n.focus&&n.focus(),n=0;n<t.length;n++)(e=t[n]).element.scrollLeft=e.left,e.element.scrollTop=e.top}}var hr=u&&"documentMode"in document&&11>=document.documentMode,gr=null,br=null,vr=null,yr=!1;function wr(e,t,n){var r=n.window===n?n.document:9===n.nodeType?n:n.ownerDocument;yr||null==gr||gr!==Q(r)||("selectionStart"in(r=gr)&&fr(r)?r={start:r.selectionStart,end:r.selectionEnd}:r={anchorNode:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection()).anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset},vr&&lr(vr,r)||(vr=r,0<(r=Vr(br,"onSelect")).length&&(t=new un("onSelect","select",null,t,n),e.push({event:t,listeners:r}),t.target=gr)))}function xr(e,t){var n={};return n[e.toLowerCase()]=t.toLowerCase(),n["Webkit"+e]="webkit"+t,n["Moz"+e]="moz"+t,n}var kr={animationend:xr("Animation","AnimationEnd"),animationiteration:xr("Animation","AnimationIteration"),animationstart:xr("Animation","AnimationStart"),transitionend:xr("Transition","TransitionEnd")},Sr={},_r={};function Er(e){if(Sr[e])return Sr[e];if(!kr[e])return e;var t,n=kr[e];for(t in n)if(n.hasOwnProperty(t)&&t in _r)return Sr[e]=n[t];return e}u&&(_r=document.createElement("div").style,"AnimationEvent"in window||(delete kr.animationend.animation,delete kr.animationiteration.animation,delete kr.animationstart.animation),"TransitionEvent"in window||delete kr.transitionend.transition);var Cr=Er("animationend"),Ar=Er("animationiteration"),Tr=Er("animationstart"),jr=Er("transitionend"),Nr=new Map,Lr="abort auxClick cancel canPlay canPlayThrough click close contextMenu copy cut drag dragEnd dragEnter dragExit dragLeave dragOver dragStart drop durationChange emptied encrypted ended error gotPointerCapture input invalid keyDown keyPress keyUp load loadedData loadedMetadata loadStart lostPointerCapture mouseDown mouseMove mouseOut mouseOver mouseUp paste pause play playing pointerCancel pointerDown pointerMove pointerOut pointerOver pointerUp progress rateChange reset resize seeked seeking stalled submit suspend timeUpdate touchCancel touchEnd touchStart volumeChange scroll toggle touchMove waiting wheel".split(" ");function Rr(e,t){Nr.set(e,t),l(t,[e])}for(var Pr=0;Pr<Lr.length;Pr++){var Or=Lr[Pr];Rr(Or.toLowerCase(),"on"+(Or[0].toUpperCase()+Or.slice(1)))}Rr(Cr,"onAnimationEnd"),Rr(Ar,"onAnimationIteration"),Rr(Tr,"onAnimationStart"),Rr("dblclick","onDoubleClick"),Rr("focusin","onFocus"),Rr("focusout","onBlur"),Rr(jr,"onTransitionEnd"),c("onMouseEnter",["mouseout","mouseover"]),c("onMouseLeave",["mouseout","mouseover"]),c("onPointerEnter",["pointerout","pointerover"]),c("onPointerLeave",["pointerout","pointerover"]),l("onChange","change click focusin focusout input keydown keyup selectionchange".split(" ")),l("onSelect","focusout contextmenu dragend focusin keydown keyup mousedown mouseup selectionchange".split(" ")),l("onBeforeInput",["compositionend","keypress","textInput","paste"]),l("onCompositionEnd","compositionend focusout keydown keypress keyup mousedown".split(" ")),l("onCompositionStart","compositionstart focusout keydown keypress keyup mousedown".split(" ")),l("onCompositionUpdate","compositionupdate focusout keydown keypress keyup mousedown".split(" "));var Dr="abort canplay canplaythrough durationchange emptied encrypted ended error loadeddata loadedmetadata loadstart pause play playing progress ratechange resize seeked seeking stalled suspend timeupdate volumechange waiting".split(" "),Ir=new Set("cancel close invalid load scroll toggle".split(" ").concat(Dr));function Fr(e,t,n){var r=e.type||"unknown-event";e.currentTarget=n,function(e,t,n,r,a,i,s,l,c){if($e.apply(this,arguments),Ie){if(!Ie)throw Error(o(198));var u=Fe;Ie=!1,Fe=null,Me||(Me=!0,ze=u)}}(r,t,void 0,e),e.currentTarget=null}function Mr(e,t){t=!!(4&t);for(var n=0;n<e.length;n++){var r=e[n],a=r.event;r=r.listeners;e:{var o=void 0;if(t)for(var i=r.length-1;0<=i;i--){var s=r[i],l=s.instance,c=s.currentTarget;if(s=s.listener,l!==o&&a.isPropagationStopped())break e;Fr(a,s,c),o=l}else for(i=0;i<r.length;i++){if(l=(s=r[i]).instance,c=s.currentTarget,s=s.listener,l!==o&&a.isPropagationStopped())break e;Fr(a,s,c),o=l}}}if(Me)throw e=ze,Me=!1,ze=null,e}function zr(e,t){var n=t[ha];void 0===n&&(n=t[ha]=new Set);var r=e+"__bubble";n.has(r)||(Ur(t,e,2,!1),n.add(r))}function Br(e,t,n){var r=0;t&&(r|=4),Ur(n,e,r,t)}var $r="_reactListening"+Math.random().toString(36).slice(2);function qr(e){if(!e[$r]){e[$r]=!0,i.forEach((function(t){"selectionchange"!==t&&(Ir.has(t)||Br(t,!1,e),Br(t,!0,e))}));var t=9===e.nodeType?e:e.ownerDocument;null===t||t[$r]||(t[$r]=!0,Br("selectionchange",!1,t))}}function Ur(e,t,n,r){switch(Yt(t)){case 1:var a=Gt;break;case 4:a=Vt;break;default:a=Wt}n=a.bind(null,t,n,e),a=void 0,!Pe||"touchstart"!==t&&"touchmove"!==t&&"wheel"!==t||(a=!0),r?void 0!==a?e.addEventListener(t,n,{capture:!0,passive:a}):e.addEventListener(t,n,!0):void 0!==a?e.addEventListener(t,n,{passive:a}):e.addEventListener(t,n,!1)}function Hr(e,t,n,r,a){var o=r;if(!(1&t||2&t||null===r))e:for(;;){if(null===r)return;var i=r.tag;if(3===i||4===i){var s=r.stateNode.containerInfo;if(s===a||8===s.nodeType&&s.parentNode===a)break;if(4===i)for(i=r.return;null!==i;){var l=i.tag;if((3===l||4===l)&&((l=i.stateNode.containerInfo)===a||8===l.nodeType&&l.parentNode===a))return;i=i.return}for(;null!==s;){if(null===(i=va(s)))return;if(5===(l=i.tag)||6===l){r=o=i;continue e}s=s.parentNode}}r=r.return}Le((function(){var r=o,a=xe(n),i=[];e:{var s=Nr.get(e);if(void 0!==s){var l=un,c=e;switch(e){case"keypress":if(0===tn(n))break e;case"keydown":case"keyup":l=An;break;case"focusin":c="focus",l=gn;break;case"focusout":c="blur",l=gn;break;case"beforeblur":case"afterblur":l=gn;break;case"click":if(2===n.button)break e;case"auxclick":case"dblclick":case"mousedown":case"mousemove":case"mouseup":case"mouseout":case"mouseover":case"contextmenu":l=mn;break;case"drag":case"dragend":case"dragenter":case"dragexit":case"dragleave":case"dragover":case"dragstart":case"drop":l=hn;break;case"touchcancel":case"touchend":case"touchmove":case"touchstart":l=jn;break;case Cr:case Ar:case Tr:l=bn;break;case jr:l=Nn;break;case"scroll":l=pn;break;case"wheel":l=Rn;break;case"copy":case"cut":case"paste":l=yn;break;case"gotpointercapture":case"lostpointercapture":case"pointercancel":case"pointerdown":case"pointermove":case"pointerout":case"pointerover":case"pointerup":l=Tn}var u=!!(4&t),d=!u&&"scroll"===e,p=u?null!==s?s+"Capture":null:s;u=[];for(var f,m=r;null!==m;){var h=(f=m).stateNode;if(5===f.tag&&null!==h&&(f=h,null!==p&&(null!=(h=Re(m,p))&&u.push(Gr(m,h,f)))),d)break;m=m.return}0<u.length&&(s=new l(s,c,null,n,a),i.push({event:s,listeners:u}))}}if(!(7&t)){if(l="mouseout"===e||"pointerout"===e,(!(s="mouseover"===e||"pointerover"===e)||n===we||!(c=n.relatedTarget||n.fromElement)||!va(c)&&!c[ma])&&(l||s)&&(s=a.window===a?a:(s=a.ownerDocument)?s.defaultView||s.parentWindow:window,l?(l=r,null!==(c=(c=n.relatedTarget||n.toElement)?va(c):null)&&(c!==(d=qe(c))||5!==c.tag&&6!==c.tag)&&(c=null)):(l=null,c=r),l!==c)){if(u=mn,h="onMouseLeave",p="onMouseEnter",m="mouse","pointerout"!==e&&"pointerover"!==e||(u=Tn,h="onPointerLeave",p="onPointerEnter",m="pointer"),d=null==l?s:wa(l),f=null==c?s:wa(c),(s=new u(h,m+"leave",l,n,a)).target=d,s.relatedTarget=f,h=null,va(a)===r&&((u=new u(p,m+"enter",c,n,a)).target=f,u.relatedTarget=d,h=u),d=h,l&&c)e:{for(p=c,m=0,f=u=l;f;f=Wr(f))m++;for(f=0,h=p;h;h=Wr(h))f++;for(;0<m-f;)u=Wr(u),m--;for(;0<f-m;)p=Wr(p),f--;for(;m--;){if(u===p||null!==p&&u===p.alternate)break e;u=Wr(u),p=Wr(p)}u=null}else u=null;null!==l&&Qr(i,s,l,u,!1),null!==c&&null!==d&&Qr(i,d,c,u,!0)}if("select"===(l=(s=r?wa(r):window).nodeName&&s.nodeName.toLowerCase())||"input"===l&&"file"===s.type)var g=Yn;else if(Hn(s))if(Zn)g=ir;else{g=ar;var b=rr}else(l=s.nodeName)&&"input"===l.toLowerCase()&&("checkbox"===s.type||"radio"===s.type)&&(g=or);switch(g&&(g=g(e,r))?Gn(i,g,n,a):(b&&b(e,s,r),"focusout"===e&&(b=s._wrapperState)&&b.controlled&&"number"===s.type&&ee(s,"number",s.value)),b=r?wa(r):window,e){case"focusin":(Hn(b)||"true"===b.contentEditable)&&(gr=b,br=r,vr=null);break;case"focusout":vr=br=gr=null;break;case"mousedown":yr=!0;break;case"contextmenu":case"mouseup":case"dragend":yr=!1,wr(i,n,a);break;case"selectionchange":if(hr)break;case"keydown":case"keyup":wr(i,n,a)}var v;if(On)e:{switch(e){case"compositionstart":var y="onCompositionStart";break e;case"compositionend":y="onCompositionEnd";break e;case"compositionupdate":y="onCompositionUpdate";break e}y=void 0}else qn?Bn(e,n)&&(y="onCompositionEnd"):"keydown"===e&&229===n.keyCode&&(y="onCompositionStart");y&&(Fn&&"ko"!==n.locale&&(qn||"onCompositionStart"!==y?"onCompositionEnd"===y&&qn&&(v=en()):(Xt="value"in(Zt=a)?Zt.value:Zt.textContent,qn=!0)),0<(b=Vr(r,y)).length&&(y=new wn(y,e,null,n,a),i.push({event:y,listeners:b}),v?y.data=v:null!==(v=$n(n))&&(y.data=v))),(v=In?function(e,t){switch(e){case"compositionend":return $n(t);case"keypress":return 32!==t.which?null:(zn=!0,Mn);case"textInput":return(e=t.data)===Mn&&zn?null:e;default:return null}}(e,n):function(e,t){if(qn)return"compositionend"===e||!On&&Bn(e,t)?(e=en(),Jt=Xt=Zt=null,qn=!1,e):null;switch(e){case"paste":default:return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1<t.char.length)return t.char;if(t.which)return String.fromCharCode(t.which)}return null;case"compositionend":return Fn&&"ko"!==t.locale?null:t.data}}(e,n))&&(0<(r=Vr(r,"onBeforeInput")).length&&(a=new wn("onBeforeInput","beforeinput",null,n,a),i.push({event:a,listeners:r}),a.data=v))}Mr(i,t)}))}function Gr(e,t,n){return{instance:e,listener:t,currentTarget:n}}function Vr(e,t){for(var n=t+"Capture",r=[];null!==e;){var a=e,o=a.stateNode;5===a.tag&&null!==o&&(a=o,null!=(o=Re(e,n))&&r.unshift(Gr(e,o,a)),null!=(o=Re(e,t))&&r.push(Gr(e,o,a))),e=e.return}return r}function Wr(e){if(null===e)return null;do{e=e.return}while(e&&5!==e.tag);return e||null}function Qr(e,t,n,r,a){for(var o=t._reactName,i=[];null!==n&&n!==r;){var s=n,l=s.alternate,c=s.stateNode;if(null!==l&&l===r)break;5===s.tag&&null!==c&&(s=c,a?null!=(l=Re(n,o))&&i.unshift(Gr(n,l,s)):a||null!=(l=Re(n,o))&&i.push(Gr(n,l,s))),n=n.return}0!==i.length&&e.push({event:t,listeners:i})}var Kr=/\r\n?/g,Yr=/\u0000|\uFFFD/g;function Zr(e){return("string"==typeof e?e:""+e).replace(Kr,"\n").replace(Yr,"")}function Xr(e,t,n){if(t=Zr(t),Zr(e)!==t&&n)throw Error(o(425))}function Jr(){}var ea=null,ta=null;function na(e,t){return"textarea"===e||"noscript"===e||"string"==typeof t.children||"number"==typeof t.children||"object"==typeof t.dangerouslySetInnerHTML&&null!==t.dangerouslySetInnerHTML&&null!=t.dangerouslySetInnerHTML.__html}var ra="function"==typeof setTimeout?setTimeout:void 0,aa="function"==typeof clearTimeout?clearTimeout:void 0,oa="function"==typeof Promise?Promise:void 0,ia="function"==typeof queueMicrotask?queueMicrotask:void 0!==oa?function(e){return oa.resolve(null).then(e).catch(sa)}:ra;function sa(e){setTimeout((function(){throw e}))}function la(e,t){var n=t,r=0;do{var a=n.nextSibling;if(e.removeChild(n),a&&8===a.nodeType)if("/$"===(n=a.data)){if(0===r)return e.removeChild(a),void qt(t);r--}else"$"!==n&&"$?"!==n&&"$!"!==n||r++;n=a}while(n);qt(t)}function ca(e){for(;null!=e;e=e.nextSibling){var t=e.nodeType;if(1===t||3===t)break;if(8===t){if("$"===(t=e.data)||"$!"===t||"$?"===t)break;if("/$"===t)return null}}return e}function ua(e){e=e.previousSibling;for(var t=0;e;){if(8===e.nodeType){var n=e.data;if("$"===n||"$!"===n||"$?"===n){if(0===t)return e;t--}else"/$"===n&&t++}e=e.previousSibling}return null}var da=Math.random().toString(36).slice(2),pa="__reactFiber$"+da,fa="__reactProps$"+da,ma="__reactContainer$"+da,ha="__reactEvents$"+da,ga="__reactListeners$"+da,ba="__reactHandles$"+da;function va(e){var t=e[pa];if(t)return t;for(var n=e.parentNode;n;){if(t=n[ma]||n[pa]){if(n=t.alternate,null!==t.child||null!==n&&null!==n.child)for(e=ua(e);null!==e;){if(n=e[pa])return n;e=ua(e)}return t}n=(e=n).parentNode}return null}function ya(e){return!(e=e[pa]||e[ma])||5!==e.tag&&6!==e.tag&&13!==e.tag&&3!==e.tag?null:e}function wa(e){if(5===e.tag||6===e.tag)return e.stateNode;throw Error(o(33))}function xa(e){return e[fa]||null}var ka=[],Sa=-1;function _a(e){return{current:e}}function Ea(e){0>Sa||(e.current=ka[Sa],ka[Sa]=null,Sa--)}function Ca(e,t){Sa++,ka[Sa]=e.current,e.current=t}var Aa={},Ta=_a(Aa),ja=_a(!1),Na=Aa;function La(e,t){var n=e.type.contextTypes;if(!n)return Aa;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var a,o={};for(a in n)o[a]=t[a];return r&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=o),o}function Ra(e){return null!=(e=e.childContextTypes)}function Pa(){Ea(ja),Ea(Ta)}function Oa(e,t,n){if(Ta.current!==Aa)throw Error(o(168));Ca(Ta,t),Ca(ja,n)}function Da(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,"function"!=typeof r.getChildContext)return n;for(var a in r=r.getChildContext())if(!(a in t))throw Error(o(108,U(e)||"Unknown",a));return F({},n,r)}function Ia(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||Aa,Na=Ta.current,Ca(Ta,e),Ca(ja,ja.current),!0}function Fa(e,t,n){var r=e.stateNode;if(!r)throw Error(o(169));n?(e=Da(e,t,Na),r.__reactInternalMemoizedMergedChildContext=e,Ea(ja),Ea(Ta),Ca(Ta,e)):Ea(ja),Ca(ja,n)}var Ma=null,za=!1,Ba=!1;function $a(e){null===Ma?Ma=[e]:Ma.push(e)}function qa(){if(!Ba&&null!==Ma){Ba=!0;var e=0,t=yt;try{var n=Ma;for(yt=1;e<n.length;e++){var r=n[e];do{r=r(!0)}while(null!==r)}Ma=null,za=!1}catch(a){throw null!==Ma&&(Ma=Ma.slice(e+1)),We(Je,qa),a}finally{yt=t,Ba=!1}}return null}var Ua=[],Ha=0,Ga=null,Va=0,Wa=[],Qa=0,Ka=null,Ya=1,Za="";function Xa(e,t){Ua[Ha++]=Va,Ua[Ha++]=Ga,Ga=e,Va=t}function Ja(e,t,n){Wa[Qa++]=Ya,Wa[Qa++]=Za,Wa[Qa++]=Ka,Ka=e;var r=Ya;e=Za;var a=32-it(r)-1;r&=~(1<<a),n+=1;var o=32-it(t)+a;if(30<o){var i=a-a%5;o=(r&(1<<i)-1).toString(32),r>>=i,a-=i,Ya=1<<32-it(t)+a|n<<a|r,Za=o+e}else Ya=1<<o|n<<a|r,Za=e}function eo(e){null!==e.return&&(Xa(e,1),Ja(e,1,0))}function to(e){for(;e===Ga;)Ga=Ua[--Ha],Ua[Ha]=null,Va=Ua[--Ha],Ua[Ha]=null;for(;e===Ka;)Ka=Wa[--Qa],Wa[Qa]=null,Za=Wa[--Qa],Wa[Qa]=null,Ya=Wa[--Qa],Wa[Qa]=null}var no=null,ro=null,ao=!1,oo=null;function io(e,t){var n=Lc(5,null,null,0);n.elementType="DELETED",n.stateNode=t,n.return=e,null===(t=e.deletions)?(e.deletions=[n],e.flags|=16):t.push(n)}function so(e,t){switch(e.tag){case 5:var n=e.type;return null!==(t=1!==t.nodeType||n.toLowerCase()!==t.nodeName.toLowerCase()?null:t)&&(e.stateNode=t,no=e,ro=ca(t.firstChild),!0);case 6:return null!==(t=""===e.pendingProps||3!==t.nodeType?null:t)&&(e.stateNode=t,no=e,ro=null,!0);case 13:return null!==(t=8!==t.nodeType?null:t)&&(n=null!==Ka?{id:Ya,overflow:Za}:null,e.memoizedState={dehydrated:t,treeContext:n,retryLane:1073741824},(n=Lc(18,null,null,0)).stateNode=t,n.return=e,e.child=n,no=e,ro=null,!0);default:return!1}}function lo(e){return!(!(1&e.mode)||128&e.flags)}function co(e){if(ao){var t=ro;if(t){var n=t;if(!so(e,t)){if(lo(e))throw Error(o(418));t=ca(n.nextSibling);var r=no;t&&so(e,t)?io(r,n):(e.flags=-4097&e.flags|2,ao=!1,no=e)}}else{if(lo(e))throw Error(o(418));e.flags=-4097&e.flags|2,ao=!1,no=e}}}function uo(e){for(e=e.return;null!==e&&5!==e.tag&&3!==e.tag&&13!==e.tag;)e=e.return;no=e}function po(e){if(e!==no)return!1;if(!ao)return uo(e),ao=!0,!1;var t;if((t=3!==e.tag)&&!(t=5!==e.tag)&&(t="head"!==(t=e.type)&&"body"!==t&&!na(e.type,e.memoizedProps)),t&&(t=ro)){if(lo(e))throw fo(),Error(o(418));for(;t;)io(e,t),t=ca(t.nextSibling)}if(uo(e),13===e.tag){if(!(e=null!==(e=e.memoizedState)?e.dehydrated:null))throw Error(o(317));e:{for(e=e.nextSibling,t=0;e;){if(8===e.nodeType){var n=e.data;if("/$"===n){if(0===t){ro=ca(e.nextSibling);break e}t--}else"$"!==n&&"$!"!==n&&"$?"!==n||t++}e=e.nextSibling}ro=null}}else ro=no?ca(e.stateNode.nextSibling):null;return!0}function fo(){for(var e=ro;e;)e=ca(e.nextSibling)}function mo(){ro=no=null,ao=!1}function ho(e){null===oo?oo=[e]:oo.push(e)}var go=w.ReactCurrentBatchConfig;function bo(e,t,n){if(null!==(e=n.ref)&&"function"!=typeof e&&"object"!=typeof e){if(n._owner){if(n=n._owner){if(1!==n.tag)throw Error(o(309));var r=n.stateNode}if(!r)throw Error(o(147,e));var a=r,i=""+e;return null!==t&&null!==t.ref&&"function"==typeof t.ref&&t.ref._stringRef===i?t.ref:(t=function(e){var t=a.refs;null===e?delete t[i]:t[i]=e},t._stringRef=i,t)}if("string"!=typeof e)throw Error(o(284));if(!n._owner)throw Error(o(290,e))}return e}function vo(e,t){throw e=Object.prototype.toString.call(t),Error(o(31,"[object Object]"===e?"object with keys {"+Object.keys(t).join(", ")+"}":e))}function yo(e){return(0,e._init)(e._payload)}function wo(e){function t(t,n){if(e){var r=t.deletions;null===r?(t.deletions=[n],t.flags|=16):r.push(n)}}function n(n,r){if(!e)return null;for(;null!==r;)t(n,r),r=r.sibling;return null}function r(e,t){for(e=new Map;null!==t;)null!==t.key?e.set(t.key,t):e.set(t.index,t),t=t.sibling;return e}function a(e,t){return(e=Pc(e,t)).index=0,e.sibling=null,e}function i(t,n,r){return t.index=r,e?null!==(r=t.alternate)?(r=r.index)<n?(t.flags|=2,n):r:(t.flags|=2,n):(t.flags|=1048576,n)}function s(t){return e&&null===t.alternate&&(t.flags|=2),t}function l(e,t,n,r){return null===t||6!==t.tag?((t=Fc(n,e.mode,r)).return=e,t):((t=a(t,n)).return=e,t)}function c(e,t,n,r){var o=n.type;return o===S?d(e,t,n.props.children,r,n.key):null!==t&&(t.elementType===o||"object"==typeof o&&null!==o&&o.$$typeof===R&&yo(o)===t.type)?((r=a(t,n.props)).ref=bo(e,t,n),r.return=e,r):((r=Oc(n.type,n.key,n.props,null,e.mode,r)).ref=bo(e,t,n),r.return=e,r)}function u(e,t,n,r){return null===t||4!==t.tag||t.stateNode.containerInfo!==n.containerInfo||t.stateNode.implementation!==n.implementation?((t=Mc(n,e.mode,r)).return=e,t):((t=a(t,n.children||[])).return=e,t)}function d(e,t,n,r,o){return null===t||7!==t.tag?((t=Dc(n,e.mode,r,o)).return=e,t):((t=a(t,n)).return=e,t)}function p(e,t,n){if("string"==typeof t&&""!==t||"number"==typeof t)return(t=Fc(""+t,e.mode,n)).return=e,t;if("object"==typeof t&&null!==t){switch(t.$$typeof){case x:return(n=Oc(t.type,t.key,t.props,null,e.mode,n)).ref=bo(e,null,t),n.return=e,n;case k:return(t=Mc(t,e.mode,n)).return=e,t;case R:return p(e,(0,t._init)(t._payload),n)}if(te(t)||D(t))return(t=Dc(t,e.mode,n,null)).return=e,t;vo(e,t)}return null}function f(e,t,n,r){var a=null!==t?t.key:null;if("string"==typeof n&&""!==n||"number"==typeof n)return null!==a?null:l(e,t,""+n,r);if("object"==typeof n&&null!==n){switch(n.$$typeof){case x:return n.key===a?c(e,t,n,r):null;case k:return n.key===a?u(e,t,n,r):null;case R:return f(e,t,(a=n._init)(n._payload),r)}if(te(n)||D(n))return null!==a?null:d(e,t,n,r,null);vo(e,n)}return null}function m(e,t,n,r,a){if("string"==typeof r&&""!==r||"number"==typeof r)return l(t,e=e.get(n)||null,""+r,a);if("object"==typeof r&&null!==r){switch(r.$$typeof){case x:return c(t,e=e.get(null===r.key?n:r.key)||null,r,a);case k:return u(t,e=e.get(null===r.key?n:r.key)||null,r,a);case R:return m(e,t,n,(0,r._init)(r._payload),a)}if(te(r)||D(r))return d(t,e=e.get(n)||null,r,a,null);vo(t,r)}return null}function h(a,o,s,l){for(var c=null,u=null,d=o,h=o=0,g=null;null!==d&&h<s.length;h++){d.index>h?(g=d,d=null):g=d.sibling;var b=f(a,d,s[h],l);if(null===b){null===d&&(d=g);break}e&&d&&null===b.alternate&&t(a,d),o=i(b,o,h),null===u?c=b:u.sibling=b,u=b,d=g}if(h===s.length)return n(a,d),ao&&Xa(a,h),c;if(null===d){for(;h<s.length;h++)null!==(d=p(a,s[h],l))&&(o=i(d,o,h),null===u?c=d:u.sibling=d,u=d);return ao&&Xa(a,h),c}for(d=r(a,d);h<s.length;h++)null!==(g=m(d,a,h,s[h],l))&&(e&&null!==g.alternate&&d.delete(null===g.key?h:g.key),o=i(g,o,h),null===u?c=g:u.sibling=g,u=g);return e&&d.forEach((function(e){return t(a,e)})),ao&&Xa(a,h),c}function g(a,s,l,c){var u=D(l);if("function"!=typeof u)throw Error(o(150));if(null==(l=u.call(l)))throw Error(o(151));for(var d=u=null,h=s,g=s=0,b=null,v=l.next();null!==h&&!v.done;g++,v=l.next()){h.index>g?(b=h,h=null):b=h.sibling;var y=f(a,h,v.value,c);if(null===y){null===h&&(h=b);break}e&&h&&null===y.alternate&&t(a,h),s=i(y,s,g),null===d?u=y:d.sibling=y,d=y,h=b}if(v.done)return n(a,h),ao&&Xa(a,g),u;if(null===h){for(;!v.done;g++,v=l.next())null!==(v=p(a,v.value,c))&&(s=i(v,s,g),null===d?u=v:d.sibling=v,d=v);return ao&&Xa(a,g),u}for(h=r(a,h);!v.done;g++,v=l.next())null!==(v=m(h,a,g,v.value,c))&&(e&&null!==v.alternate&&h.delete(null===v.key?g:v.key),s=i(v,s,g),null===d?u=v:d.sibling=v,d=v);return e&&h.forEach((function(e){return t(a,e)})),ao&&Xa(a,g),u}return function e(r,o,i,l){if("object"==typeof i&&null!==i&&i.type===S&&null===i.key&&(i=i.props.children),"object"==typeof i&&null!==i){switch(i.$$typeof){case x:e:{for(var c=i.key,u=o;null!==u;){if(u.key===c){if((c=i.type)===S){if(7===u.tag){n(r,u.sibling),(o=a(u,i.props.children)).return=r,r=o;break e}}else if(u.elementType===c||"object"==typeof c&&null!==c&&c.$$typeof===R&&yo(c)===u.type){n(r,u.sibling),(o=a(u,i.props)).ref=bo(r,u,i),o.return=r,r=o;break e}n(r,u);break}t(r,u),u=u.sibling}i.type===S?((o=Dc(i.props.children,r.mode,l,i.key)).return=r,r=o):((l=Oc(i.type,i.key,i.props,null,r.mode,l)).ref=bo(r,o,i),l.return=r,r=l)}return s(r);case k:e:{for(u=i.key;null!==o;){if(o.key===u){if(4===o.tag&&o.stateNode.containerInfo===i.containerInfo&&o.stateNode.implementation===i.implementation){n(r,o.sibling),(o=a(o,i.children||[])).return=r,r=o;break e}n(r,o);break}t(r,o),o=o.sibling}(o=Mc(i,r.mode,l)).return=r,r=o}return s(r);case R:return e(r,o,(u=i._init)(i._payload),l)}if(te(i))return h(r,o,i,l);if(D(i))return g(r,o,i,l);vo(r,i)}return"string"==typeof i&&""!==i||"number"==typeof i?(i=""+i,null!==o&&6===o.tag?(n(r,o.sibling),(o=a(o,i)).return=r,r=o):(n(r,o),(o=Fc(i,r.mode,l)).return=r,r=o),s(r)):n(r,o)}}var xo=wo(!0),ko=wo(!1),So=_a(null),_o=null,Eo=null,Co=null;function Ao(){Co=Eo=_o=null}function To(e){var t=So.current;Ea(So),e._currentValue=t}function jo(e,t,n){for(;null!==e;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,null!==r&&(r.childLanes|=t)):null!==r&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function No(e,t){_o=e,Co=Eo=null,null!==(e=e.dependencies)&&null!==e.firstContext&&(!!(e.lanes&t)&&(ys=!0),e.firstContext=null)}function Lo(e){var t=e._currentValue;if(Co!==e)if(e={context:e,memoizedValue:t,next:null},null===Eo){if(null===_o)throw Error(o(308));Eo=e,_o.dependencies={lanes:0,firstContext:e}}else Eo=Eo.next=e;return t}var Ro=null;function Po(e){null===Ro?Ro=[e]:Ro.push(e)}function Oo(e,t,n,r){var a=t.interleaved;return null===a?(n.next=n,Po(t)):(n.next=a.next,a.next=n),t.interleaved=n,Do(e,r)}function Do(e,t){e.lanes|=t;var n=e.alternate;for(null!==n&&(n.lanes|=t),n=e,e=e.return;null!==e;)e.childLanes|=t,null!==(n=e.alternate)&&(n.childLanes|=t),n=e,e=e.return;return 3===n.tag?n.stateNode:null}var Io=!1;function Fo(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Mo(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function zo(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function Bo(e,t,n){var r=e.updateQueue;if(null===r)return null;if(r=r.shared,2&Tl){var a=r.pending;return null===a?t.next=t:(t.next=a.next,a.next=t),r.pending=t,Do(e,n)}return null===(a=r.interleaved)?(t.next=t,Po(r)):(t.next=a.next,a.next=t),r.interleaved=t,Do(e,n)}function $o(e,t,n){if(null!==(t=t.updateQueue)&&(t=t.shared,4194240&n)){var r=t.lanes;n|=r&=e.pendingLanes,t.lanes=n,vt(e,n)}}function qo(e,t){var n=e.updateQueue,r=e.alternate;if(null!==r&&n===(r=r.updateQueue)){var a=null,o=null;if(null!==(n=n.firstBaseUpdate)){do{var i={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};null===o?a=o=i:o=o.next=i,n=n.next}while(null!==n);null===o?a=o=t:o=o.next=t}else a=o=t;return n={baseState:r.baseState,firstBaseUpdate:a,lastBaseUpdate:o,shared:r.shared,effects:r.effects},void(e.updateQueue=n)}null===(e=n.lastBaseUpdate)?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function Uo(e,t,n,r){var a=e.updateQueue;Io=!1;var o=a.firstBaseUpdate,i=a.lastBaseUpdate,s=a.shared.pending;if(null!==s){a.shared.pending=null;var l=s,c=l.next;l.next=null,null===i?o=c:i.next=c,i=l;var u=e.alternate;null!==u&&((s=(u=u.updateQueue).lastBaseUpdate)!==i&&(null===s?u.firstBaseUpdate=c:s.next=c,u.lastBaseUpdate=l))}if(null!==o){var d=a.baseState;for(i=0,u=c=l=null,s=o;;){var p=s.lane,f=s.eventTime;if((r&p)===p){null!==u&&(u=u.next={eventTime:f,lane:0,tag:s.tag,payload:s.payload,callback:s.callback,next:null});e:{var m=e,h=s;switch(p=t,f=n,h.tag){case 1:if("function"==typeof(m=h.payload)){d=m.call(f,d,p);break e}d=m;break e;case 3:m.flags=-65537&m.flags|128;case 0:if(null==(p="function"==typeof(m=h.payload)?m.call(f,d,p):m))break e;d=F({},d,p);break e;case 2:Io=!0}}null!==s.callback&&0!==s.lane&&(e.flags|=64,null===(p=a.effects)?a.effects=[s]:p.push(s))}else f={eventTime:f,lane:p,tag:s.tag,payload:s.payload,callback:s.callback,next:null},null===u?(c=u=f,l=d):u=u.next=f,i|=p;if(null===(s=s.next)){if(null===(s=a.shared.pending))break;s=(p=s).next,p.next=null,a.lastBaseUpdate=p,a.shared.pending=null}}if(null===u&&(l=d),a.baseState=l,a.firstBaseUpdate=c,a.lastBaseUpdate=u,null!==(t=a.shared.interleaved)){a=t;do{i|=a.lane,a=a.next}while(a!==t)}else null===o&&(a.shared.lanes=0);Il|=i,e.lanes=i,e.memoizedState=d}}function Ho(e,t,n){if(e=t.effects,t.effects=null,null!==e)for(t=0;t<e.length;t++){var r=e[t],a=r.callback;if(null!==a){if(r.callback=null,r=n,"function"!=typeof a)throw Error(o(191,a));a.call(r)}}}var Go={},Vo=_a(Go),Wo=_a(Go),Qo=_a(Go);function Ko(e){if(e===Go)throw Error(o(174));return e}function Yo(e,t){switch(Ca(Qo,t),Ca(Wo,e),Ca(Vo,Go),e=t.nodeType){case 9:case 11:t=(t=t.documentElement)?t.namespaceURI:le(null,"");break;default:t=le(t=(e=8===e?t.parentNode:t).namespaceURI||null,e=e.tagName)}Ea(Vo),Ca(Vo,t)}function Zo(){Ea(Vo),Ea(Wo),Ea(Qo)}function Xo(e){Ko(Qo.current);var t=Ko(Vo.current),n=le(t,e.type);t!==n&&(Ca(Wo,e),Ca(Vo,n))}function Jo(e){Wo.current===e&&(Ea(Vo),Ea(Wo))}var ei=_a(0);function ti(e){for(var t=e;null!==t;){if(13===t.tag){var n=t.memoizedState;if(null!==n&&(null===(n=n.dehydrated)||"$?"===n.data||"$!"===n.data))return t}else if(19===t.tag&&void 0!==t.memoizedProps.revealOrder){if(128&t.flags)return t}else if(null!==t.child){t.child.return=t,t=t.child;continue}if(t===e)break;for(;null===t.sibling;){if(null===t.return||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}var ni=[];function ri(){for(var e=0;e<ni.length;e++)ni[e]._workInProgressVersionPrimary=null;ni.length=0}var ai=w.ReactCurrentDispatcher,oi=w.ReactCurrentBatchConfig,ii=0,si=null,li=null,ci=null,ui=!1,di=!1,pi=0,fi=0;function mi(){throw Error(o(321))}function hi(e,t){if(null===t)return!1;for(var n=0;n<t.length&&n<e.length;n++)if(!sr(e[n],t[n]))return!1;return!0}function gi(e,t,n,r,a,i){if(ii=i,si=t,t.memoizedState=null,t.updateQueue=null,t.lanes=0,ai.current=null===e||null===e.memoizedState?Ji:es,e=n(r,a),di){i=0;do{if(di=!1,pi=0,25<=i)throw Error(o(301));i+=1,ci=li=null,t.updateQueue=null,ai.current=ts,e=n(r,a)}while(di)}if(ai.current=Xi,t=null!==li&&null!==li.next,ii=0,ci=li=si=null,ui=!1,t)throw Error(o(300));return e}function bi(){var e=0!==pi;return pi=0,e}function vi(){var e={memoizedState:null,baseState:null,baseQueue:null,queue:null,next:null};return null===ci?si.memoizedState=ci=e:ci=ci.next=e,ci}function yi(){if(null===li){var e=si.alternate;e=null!==e?e.memoizedState:null}else e=li.next;var t=null===ci?si.memoizedState:ci.next;if(null!==t)ci=t,li=e;else{if(null===e)throw Error(o(310));e={memoizedState:(li=e).memoizedState,baseState:li.baseState,baseQueue:li.baseQueue,queue:li.queue,next:null},null===ci?si.memoizedState=ci=e:ci=ci.next=e}return ci}function wi(e,t){return"function"==typeof t?t(e):t}function xi(e){var t=yi(),n=t.queue;if(null===n)throw Error(o(311));n.lastRenderedReducer=e;var r=li,a=r.baseQueue,i=n.pending;if(null!==i){if(null!==a){var s=a.next;a.next=i.next,i.next=s}r.baseQueue=a=i,n.pending=null}if(null!==a){i=a.next,r=r.baseState;var l=s=null,c=null,u=i;do{var d=u.lane;if((ii&d)===d)null!==c&&(c=c.next={lane:0,action:u.action,hasEagerState:u.hasEagerState,eagerState:u.eagerState,next:null}),r=u.hasEagerState?u.eagerState:e(r,u.action);else{var p={lane:d,action:u.action,hasEagerState:u.hasEagerState,eagerState:u.eagerState,next:null};null===c?(l=c=p,s=r):c=c.next=p,si.lanes|=d,Il|=d}u=u.next}while(null!==u&&u!==i);null===c?s=r:c.next=l,sr(r,t.memoizedState)||(ys=!0),t.memoizedState=r,t.baseState=s,t.baseQueue=c,n.lastRenderedState=r}if(null!==(e=n.interleaved)){a=e;do{i=a.lane,si.lanes|=i,Il|=i,a=a.next}while(a!==e)}else null===a&&(n.lanes=0);return[t.memoizedState,n.dispatch]}function ki(e){var t=yi(),n=t.queue;if(null===n)throw Error(o(311));n.lastRenderedReducer=e;var r=n.dispatch,a=n.pending,i=t.memoizedState;if(null!==a){n.pending=null;var s=a=a.next;do{i=e(i,s.action),s=s.next}while(s!==a);sr(i,t.memoizedState)||(ys=!0),t.memoizedState=i,null===t.baseQueue&&(t.baseState=i),n.lastRenderedState=i}return[i,r]}function Si(){}function _i(e,t){var n=si,r=yi(),a=t(),i=!sr(r.memoizedState,a);if(i&&(r.memoizedState=a,ys=!0),r=r.queue,Ii(Ai.bind(null,n,r,e),[e]),r.getSnapshot!==t||i||null!==ci&&1&ci.memoizedState.tag){if(n.flags|=2048,Li(9,Ci.bind(null,n,r,a,t),void 0,null),null===jl)throw Error(o(349));30&ii||Ei(n,t,a)}return a}function Ei(e,t,n){e.flags|=16384,e={getSnapshot:t,value:n},null===(t=si.updateQueue)?(t={lastEffect:null,stores:null},si.updateQueue=t,t.stores=[e]):null===(n=t.stores)?t.stores=[e]:n.push(e)}function Ci(e,t,n,r){t.value=n,t.getSnapshot=r,Ti(t)&&ji(e)}function Ai(e,t,n){return n((function(){Ti(t)&&ji(e)}))}function Ti(e){var t=e.getSnapshot;e=e.value;try{var n=t();return!sr(e,n)}catch(r){return!0}}function ji(e){var t=Do(e,1);null!==t&&nc(t,e,1,-1)}function Ni(e){var t=vi();return"function"==typeof e&&(e=e()),t.memoizedState=t.baseState=e,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:wi,lastRenderedState:e},t.queue=e,e=e.dispatch=Qi.bind(null,si,e),[t.memoizedState,e]}function Li(e,t,n,r){return e={tag:e,create:t,destroy:n,deps:r,next:null},null===(t=si.updateQueue)?(t={lastEffect:null,stores:null},si.updateQueue=t,t.lastEffect=e.next=e):null===(n=t.lastEffect)?t.lastEffect=e.next=e:(r=n.next,n.next=e,e.next=r,t.lastEffect=e),e}function Ri(){return yi().memoizedState}function Pi(e,t,n,r){var a=vi();si.flags|=e,a.memoizedState=Li(1|t,n,void 0,void 0===r?null:r)}function Oi(e,t,n,r){var a=yi();r=void 0===r?null:r;var o=void 0;if(null!==li){var i=li.memoizedState;if(o=i.destroy,null!==r&&hi(r,i.deps))return void(a.memoizedState=Li(t,n,o,r))}si.flags|=e,a.memoizedState=Li(1|t,n,o,r)}function Di(e,t){return Pi(8390656,8,e,t)}function Ii(e,t){return Oi(2048,8,e,t)}function Fi(e,t){return Oi(4,2,e,t)}function Mi(e,t){return Oi(4,4,e,t)}function zi(e,t){return"function"==typeof t?(e=e(),t(e),function(){t(null)}):null!=t?(e=e(),t.current=e,function(){t.current=null}):void 0}function Bi(e,t,n){return n=null!=n?n.concat([e]):null,Oi(4,4,zi.bind(null,t,e),n)}function $i(){}function qi(e,t){var n=yi();t=void 0===t?null:t;var r=n.memoizedState;return null!==r&&null!==t&&hi(t,r[1])?r[0]:(n.memoizedState=[e,t],e)}function Ui(e,t){var n=yi();t=void 0===t?null:t;var r=n.memoizedState;return null!==r&&null!==t&&hi(t,r[1])?r[0]:(e=e(),n.memoizedState=[e,t],e)}function Hi(e,t,n){return 21&ii?(sr(n,t)||(n=ht(),si.lanes|=n,Il|=n,e.baseState=!0),t):(e.baseState&&(e.baseState=!1,ys=!0),e.memoizedState=n)}function Gi(e,t){var n=yt;yt=0!==n&&4>n?n:4,e(!0);var r=oi.transition;oi.transition={};try{e(!1),t()}finally{yt=n,oi.transition=r}}function Vi(){return yi().memoizedState}function Wi(e,t,n){var r=tc(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},Ki(e))Yi(t,n);else if(null!==(n=Oo(e,t,n,r))){nc(n,e,r,ec()),Zi(n,t,r)}}function Qi(e,t,n){var r=tc(e),a={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(Ki(e))Yi(t,a);else{var o=e.alternate;if(0===e.lanes&&(null===o||0===o.lanes)&&null!==(o=t.lastRenderedReducer))try{var i=t.lastRenderedState,s=o(i,n);if(a.hasEagerState=!0,a.eagerState=s,sr(s,i)){var l=t.interleaved;return null===l?(a.next=a,Po(t)):(a.next=l.next,l.next=a),void(t.interleaved=a)}}catch(c){}null!==(n=Oo(e,t,a,r))&&(nc(n,e,r,a=ec()),Zi(n,t,r))}}function Ki(e){var t=e.alternate;return e===si||null!==t&&t===si}function Yi(e,t){di=ui=!0;var n=e.pending;null===n?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Zi(e,t,n){if(4194240&n){var r=t.lanes;n|=r&=e.pendingLanes,t.lanes=n,vt(e,n)}}var Xi={readContext:Lo,useCallback:mi,useContext:mi,useEffect:mi,useImperativeHandle:mi,useInsertionEffect:mi,useLayoutEffect:mi,useMemo:mi,useReducer:mi,useRef:mi,useState:mi,useDebugValue:mi,useDeferredValue:mi,useTransition:mi,useMutableSource:mi,useSyncExternalStore:mi,useId:mi,unstable_isNewReconciler:!1},Ji={readContext:Lo,useCallback:function(e,t){return vi().memoizedState=[e,void 0===t?null:t],e},useContext:Lo,useEffect:Di,useImperativeHandle:function(e,t,n){return n=null!=n?n.concat([e]):null,Pi(4194308,4,zi.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Pi(4194308,4,e,t)},useInsertionEffect:function(e,t){return Pi(4,2,e,t)},useMemo:function(e,t){var n=vi();return t=void 0===t?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=vi();return t=void 0!==n?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=Wi.bind(null,si,e),[r.memoizedState,e]},useRef:function(e){return e={current:e},vi().memoizedState=e},useState:Ni,useDebugValue:$i,useDeferredValue:function(e){return vi().memoizedState=e},useTransition:function(){var e=Ni(!1),t=e[0];return e=Gi.bind(null,e[1]),vi().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=si,a=vi();if(ao){if(void 0===n)throw Error(o(407));n=n()}else{if(n=t(),null===jl)throw Error(o(349));30&ii||Ei(r,t,n)}a.memoizedState=n;var i={value:n,getSnapshot:t};return a.queue=i,Di(Ai.bind(null,r,i,e),[e]),r.flags|=2048,Li(9,Ci.bind(null,r,i,n,t),void 0,null),n},useId:function(){var e=vi(),t=jl.identifierPrefix;if(ao){var n=Za;t=":"+t+"R"+(n=(Ya&~(1<<32-it(Ya)-1)).toString(32)+n),0<(n=pi++)&&(t+="H"+n.toString(32)),t+=":"}else t=":"+t+"r"+(n=fi++).toString(32)+":";return e.memoizedState=t},unstable_isNewReconciler:!1},es={readContext:Lo,useCallback:qi,useContext:Lo,useEffect:Ii,useImperativeHandle:Bi,useInsertionEffect:Fi,useLayoutEffect:Mi,useMemo:Ui,useReducer:xi,useRef:Ri,useState:function(){return xi(wi)},useDebugValue:$i,useDeferredValue:function(e){return Hi(yi(),li.memoizedState,e)},useTransition:function(){return[xi(wi)[0],yi().memoizedState]},useMutableSource:Si,useSyncExternalStore:_i,useId:Vi,unstable_isNewReconciler:!1},ts={readContext:Lo,useCallback:qi,useContext:Lo,useEffect:Ii,useImperativeHandle:Bi,useInsertionEffect:Fi,useLayoutEffect:Mi,useMemo:Ui,useReducer:ki,useRef:Ri,useState:function(){return ki(wi)},useDebugValue:$i,useDeferredValue:function(e){var t=yi();return null===li?t.memoizedState=e:Hi(t,li.memoizedState,e)},useTransition:function(){return[ki(wi)[0],yi().memoizedState]},useMutableSource:Si,useSyncExternalStore:_i,useId:Vi,unstable_isNewReconciler:!1};function ns(e,t){if(e&&e.defaultProps){for(var n in t=F({},t),e=e.defaultProps)void 0===t[n]&&(t[n]=e[n]);return t}return t}function rs(e,t,n,r){n=null==(n=n(r,t=e.memoizedState))?t:F({},t,n),e.memoizedState=n,0===e.lanes&&(e.updateQueue.baseState=n)}var as={isMounted:function(e){return!!(e=e._reactInternals)&&qe(e)===e},enqueueSetState:function(e,t,n){e=e._reactInternals;var r=ec(),a=tc(e),o=zo(r,a);o.payload=t,null!=n&&(o.callback=n),null!==(t=Bo(e,o,a))&&(nc(t,e,a,r),$o(t,e,a))},enqueueReplaceState:function(e,t,n){e=e._reactInternals;var r=ec(),a=tc(e),o=zo(r,a);o.tag=1,o.payload=t,null!=n&&(o.callback=n),null!==(t=Bo(e,o,a))&&(nc(t,e,a,r),$o(t,e,a))},enqueueForceUpdate:function(e,t){e=e._reactInternals;var n=ec(),r=tc(e),a=zo(n,r);a.tag=2,null!=t&&(a.callback=t),null!==(t=Bo(e,a,r))&&(nc(t,e,r,n),$o(t,e,r))}};function os(e,t,n,r,a,o,i){return"function"==typeof(e=e.stateNode).shouldComponentUpdate?e.shouldComponentUpdate(r,o,i):!t.prototype||!t.prototype.isPureReactComponent||(!lr(n,r)||!lr(a,o))}function is(e,t,n){var r=!1,a=Aa,o=t.contextType;return"object"==typeof o&&null!==o?o=Lo(o):(a=Ra(t)?Na:Ta.current,o=(r=null!=(r=t.contextTypes))?La(e,a):Aa),t=new t(n,o),e.memoizedState=null!==t.state&&void 0!==t.state?t.state:null,t.updater=as,e.stateNode=t,t._reactInternals=e,r&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=a,e.__reactInternalMemoizedMaskedChildContext=o),t}function ss(e,t,n,r){e=t.state,"function"==typeof t.componentWillReceiveProps&&t.componentWillReceiveProps(n,r),"function"==typeof t.UNSAFE_componentWillReceiveProps&&t.UNSAFE_componentWillReceiveProps(n,r),t.state!==e&&as.enqueueReplaceState(t,t.state,null)}function ls(e,t,n,r){var a=e.stateNode;a.props=n,a.state=e.memoizedState,a.refs={},Fo(e);var o=t.contextType;"object"==typeof o&&null!==o?a.context=Lo(o):(o=Ra(t)?Na:Ta.current,a.context=La(e,o)),a.state=e.memoizedState,"function"==typeof(o=t.getDerivedStateFromProps)&&(rs(e,t,o,n),a.state=e.memoizedState),"function"==typeof t.getDerivedStateFromProps||"function"==typeof a.getSnapshotBeforeUpdate||"function"!=typeof a.UNSAFE_componentWillMount&&"function"!=typeof a.componentWillMount||(t=a.state,"function"==typeof a.componentWillMount&&a.componentWillMount(),"function"==typeof a.UNSAFE_componentWillMount&&a.UNSAFE_componentWillMount(),t!==a.state&&as.enqueueReplaceState(a,a.state,null),Uo(e,n,a,r),a.state=e.memoizedState),"function"==typeof a.componentDidMount&&(e.flags|=4194308)}function cs(e,t){try{var n="",r=t;do{n+=$(r),r=r.return}while(r);var a=n}catch(o){a="\nError generating stack: "+o.message+"\n"+o.stack}return{value:e,source:t,stack:a,digest:null}}function us(e,t,n){return{value:e,source:null,stack:null!=n?n:null,digest:null!=t?t:null}}function ds(e,t){try{console.error(t.value)}catch(n){setTimeout((function(){throw n}))}}var ps="function"==typeof WeakMap?WeakMap:Map;function fs(e,t,n){(n=zo(-1,n)).tag=3,n.payload={element:null};var r=t.value;return n.callback=function(){Hl||(Hl=!0,Gl=r),ds(0,t)},n}function ms(e,t,n){(n=zo(-1,n)).tag=3;var r=e.type.getDerivedStateFromError;if("function"==typeof r){var a=t.value;n.payload=function(){return r(a)},n.callback=function(){ds(0,t)}}var o=e.stateNode;return null!==o&&"function"==typeof o.componentDidCatch&&(n.callback=function(){ds(0,t),"function"!=typeof r&&(null===Vl?Vl=new Set([this]):Vl.add(this));var e=t.stack;this.componentDidCatch(t.value,{componentStack:null!==e?e:""})}),n}function hs(e,t,n){var r=e.pingCache;if(null===r){r=e.pingCache=new ps;var a=new Set;r.set(t,a)}else void 0===(a=r.get(t))&&(a=new Set,r.set(t,a));a.has(n)||(a.add(n),e=Ec.bind(null,e,t,n),t.then(e,e))}function gs(e){do{var t;if((t=13===e.tag)&&(t=null===(t=e.memoizedState)||null!==t.dehydrated),t)return e;e=e.return}while(null!==e);return null}function bs(e,t,n,r,a){return 1&e.mode?(e.flags|=65536,e.lanes=a,e):(e===t?e.flags|=65536:(e.flags|=128,n.flags|=131072,n.flags&=-52805,1===n.tag&&(null===n.alternate?n.tag=17:((t=zo(-1,1)).tag=2,Bo(n,t,1))),n.lanes|=1),e)}var vs=w.ReactCurrentOwner,ys=!1;function ws(e,t,n,r){t.child=null===e?ko(t,null,n,r):xo(t,e.child,n,r)}function xs(e,t,n,r,a){n=n.render;var o=t.ref;return No(t,a),r=gi(e,t,n,r,o,a),n=bi(),null===e||ys?(ao&&n&&eo(t),t.flags|=1,ws(e,t,r,a),t.child):(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~a,Hs(e,t,a))}function ks(e,t,n,r,a){if(null===e){var o=n.type;return"function"!=typeof o||Rc(o)||void 0!==o.defaultProps||null!==n.compare||void 0!==n.defaultProps?((e=Oc(n.type,null,r,t,t.mode,a)).ref=t.ref,e.return=t,t.child=e):(t.tag=15,t.type=o,Ss(e,t,o,r,a))}if(o=e.child,!(e.lanes&a)){var i=o.memoizedProps;if((n=null!==(n=n.compare)?n:lr)(i,r)&&e.ref===t.ref)return Hs(e,t,a)}return t.flags|=1,(e=Pc(o,r)).ref=t.ref,e.return=t,t.child=e}function Ss(e,t,n,r,a){if(null!==e){var o=e.memoizedProps;if(lr(o,r)&&e.ref===t.ref){if(ys=!1,t.pendingProps=r=o,!(e.lanes&a))return t.lanes=e.lanes,Hs(e,t,a);131072&e.flags&&(ys=!0)}}return Cs(e,t,n,r,a)}function _s(e,t,n){var r=t.pendingProps,a=r.children,o=null!==e?e.memoizedState:null;if("hidden"===r.mode)if(1&t.mode){if(!(1073741824&n))return e=null!==o?o.baseLanes|n:n,t.lanes=t.childLanes=1073741824,t.memoizedState={baseLanes:e,cachePool:null,transitions:null},t.updateQueue=null,Ca(Pl,Rl),Rl|=e,null;t.memoizedState={baseLanes:0,cachePool:null,transitions:null},r=null!==o?o.baseLanes:n,Ca(Pl,Rl),Rl|=r}else t.memoizedState={baseLanes:0,cachePool:null,transitions:null},Ca(Pl,Rl),Rl|=n;else null!==o?(r=o.baseLanes|n,t.memoizedState=null):r=n,Ca(Pl,Rl),Rl|=r;return ws(e,t,a,n),t.child}function Es(e,t){var n=t.ref;(null===e&&null!==n||null!==e&&e.ref!==n)&&(t.flags|=512,t.flags|=2097152)}function Cs(e,t,n,r,a){var o=Ra(n)?Na:Ta.current;return o=La(t,o),No(t,a),n=gi(e,t,n,r,o,a),r=bi(),null===e||ys?(ao&&r&&eo(t),t.flags|=1,ws(e,t,n,a),t.child):(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~a,Hs(e,t,a))}function As(e,t,n,r,a){if(Ra(n)){var o=!0;Ia(t)}else o=!1;if(No(t,a),null===t.stateNode)Us(e,t),is(t,n,r),ls(t,n,r,a),r=!0;else if(null===e){var i=t.stateNode,s=t.memoizedProps;i.props=s;var l=i.context,c=n.contextType;"object"==typeof c&&null!==c?c=Lo(c):c=La(t,c=Ra(n)?Na:Ta.current);var u=n.getDerivedStateFromProps,d="function"==typeof u||"function"==typeof i.getSnapshotBeforeUpdate;d||"function"!=typeof i.UNSAFE_componentWillReceiveProps&&"function"!=typeof i.componentWillReceiveProps||(s!==r||l!==c)&&ss(t,i,r,c),Io=!1;var p=t.memoizedState;i.state=p,Uo(t,r,i,a),l=t.memoizedState,s!==r||p!==l||ja.current||Io?("function"==typeof u&&(rs(t,n,u,r),l=t.memoizedState),(s=Io||os(t,n,s,r,p,l,c))?(d||"function"!=typeof i.UNSAFE_componentWillMount&&"function"!=typeof i.componentWillMount||("function"==typeof i.componentWillMount&&i.componentWillMount(),"function"==typeof i.UNSAFE_componentWillMount&&i.UNSAFE_componentWillMount()),"function"==typeof i.componentDidMount&&(t.flags|=4194308)):("function"==typeof i.componentDidMount&&(t.flags|=4194308),t.memoizedProps=r,t.memoizedState=l),i.props=r,i.state=l,i.context=c,r=s):("function"==typeof i.componentDidMount&&(t.flags|=4194308),r=!1)}else{i=t.stateNode,Mo(e,t),s=t.memoizedProps,c=t.type===t.elementType?s:ns(t.type,s),i.props=c,d=t.pendingProps,p=i.context,"object"==typeof(l=n.contextType)&&null!==l?l=Lo(l):l=La(t,l=Ra(n)?Na:Ta.current);var f=n.getDerivedStateFromProps;(u="function"==typeof f||"function"==typeof i.getSnapshotBeforeUpdate)||"function"!=typeof i.UNSAFE_componentWillReceiveProps&&"function"!=typeof i.componentWillReceiveProps||(s!==d||p!==l)&&ss(t,i,r,l),Io=!1,p=t.memoizedState,i.state=p,Uo(t,r,i,a);var m=t.memoizedState;s!==d||p!==m||ja.current||Io?("function"==typeof f&&(rs(t,n,f,r),m=t.memoizedState),(c=Io||os(t,n,c,r,p,m,l)||!1)?(u||"function"!=typeof i.UNSAFE_componentWillUpdate&&"function"!=typeof i.componentWillUpdate||("function"==typeof i.componentWillUpdate&&i.componentWillUpdate(r,m,l),"function"==typeof i.UNSAFE_componentWillUpdate&&i.UNSAFE_componentWillUpdate(r,m,l)),"function"==typeof i.componentDidUpdate&&(t.flags|=4),"function"==typeof i.getSnapshotBeforeUpdate&&(t.flags|=1024)):("function"!=typeof i.componentDidUpdate||s===e.memoizedProps&&p===e.memoizedState||(t.flags|=4),"function"!=typeof i.getSnapshotBeforeUpdate||s===e.memoizedProps&&p===e.memoizedState||(t.flags|=1024),t.memoizedProps=r,t.memoizedState=m),i.props=r,i.state=m,i.context=l,r=c):("function"!=typeof i.componentDidUpdate||s===e.memoizedProps&&p===e.memoizedState||(t.flags|=4),"function"!=typeof i.getSnapshotBeforeUpdate||s===e.memoizedProps&&p===e.memoizedState||(t.flags|=1024),r=!1)}return Ts(e,t,n,r,o,a)}function Ts(e,t,n,r,a,o){Es(e,t);var i=!!(128&t.flags);if(!r&&!i)return a&&Fa(t,n,!1),Hs(e,t,o);r=t.stateNode,vs.current=t;var s=i&&"function"!=typeof n.getDerivedStateFromError?null:r.render();return t.flags|=1,null!==e&&i?(t.child=xo(t,e.child,null,o),t.child=xo(t,null,s,o)):ws(e,t,s,o),t.memoizedState=r.state,a&&Fa(t,n,!0),t.child}function js(e){var t=e.stateNode;t.pendingContext?Oa(0,t.pendingContext,t.pendingContext!==t.context):t.context&&Oa(0,t.context,!1),Yo(e,t.containerInfo)}function Ns(e,t,n,r,a){return mo(),ho(a),t.flags|=256,ws(e,t,n,r),t.child}var Ls,Rs,Ps,Os,Ds={dehydrated:null,treeContext:null,retryLane:0};function Is(e){return{baseLanes:e,cachePool:null,transitions:null}}function Fs(e,t,n){var r,a=t.pendingProps,i=ei.current,s=!1,l=!!(128&t.flags);if((r=l)||(r=(null===e||null!==e.memoizedState)&&!!(2&i)),r?(s=!0,t.flags&=-129):null!==e&&null===e.memoizedState||(i|=1),Ca(ei,1&i),null===e)return co(t),null!==(e=t.memoizedState)&&null!==(e=e.dehydrated)?(1&t.mode?"$!"===e.data?t.lanes=8:t.lanes=1073741824:t.lanes=1,null):(l=a.children,e=a.fallback,s?(a=t.mode,s=t.child,l={mode:"hidden",children:l},1&a||null===s?s=Ic(l,a,0,null):(s.childLanes=0,s.pendingProps=l),e=Dc(e,a,n,null),s.return=t,e.return=t,s.sibling=e,t.child=s,t.child.memoizedState=Is(n),t.memoizedState=Ds,e):Ms(t,l));if(null!==(i=e.memoizedState)&&null!==(r=i.dehydrated))return function(e,t,n,r,a,i,s){if(n)return 256&t.flags?(t.flags&=-257,zs(e,t,s,r=us(Error(o(422))))):null!==t.memoizedState?(t.child=e.child,t.flags|=128,null):(i=r.fallback,a=t.mode,r=Ic({mode:"visible",children:r.children},a,0,null),(i=Dc(i,a,s,null)).flags|=2,r.return=t,i.return=t,r.sibling=i,t.child=r,1&t.mode&&xo(t,e.child,null,s),t.child.memoizedState=Is(s),t.memoizedState=Ds,i);if(!(1&t.mode))return zs(e,t,s,null);if("$!"===a.data){if(r=a.nextSibling&&a.nextSibling.dataset)var l=r.dgst;return r=l,zs(e,t,s,r=us(i=Error(o(419)),r,void 0))}if(l=!!(s&e.childLanes),ys||l){if(null!==(r=jl)){switch(s&-s){case 4:a=2;break;case 16:a=8;break;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:a=32;break;case 536870912:a=268435456;break;default:a=0}0!==(a=a&(r.suspendedLanes|s)?0:a)&&a!==i.retryLane&&(i.retryLane=a,Do(e,a),nc(r,e,a,-1))}return hc(),zs(e,t,s,r=us(Error(o(421))))}return"$?"===a.data?(t.flags|=128,t.child=e.child,t=Ac.bind(null,e),a._reactRetry=t,null):(e=i.treeContext,ro=ca(a.nextSibling),no=t,ao=!0,oo=null,null!==e&&(Wa[Qa++]=Ya,Wa[Qa++]=Za,Wa[Qa++]=Ka,Ya=e.id,Za=e.overflow,Ka=t),t=Ms(t,r.children),t.flags|=4096,t)}(e,t,l,a,r,i,n);if(s){s=a.fallback,l=t.mode,r=(i=e.child).sibling;var c={mode:"hidden",children:a.children};return 1&l||t.child===i?(a=Pc(i,c)).subtreeFlags=14680064&i.subtreeFlags:((a=t.child).childLanes=0,a.pendingProps=c,t.deletions=null),null!==r?s=Pc(r,s):(s=Dc(s,l,n,null)).flags|=2,s.return=t,a.return=t,a.sibling=s,t.child=a,a=s,s=t.child,l=null===(l=e.child.memoizedState)?Is(n):{baseLanes:l.baseLanes|n,cachePool:null,transitions:l.transitions},s.memoizedState=l,s.childLanes=e.childLanes&~n,t.memoizedState=Ds,a}return e=(s=e.child).sibling,a=Pc(s,{mode:"visible",children:a.children}),!(1&t.mode)&&(a.lanes=n),a.return=t,a.sibling=null,null!==e&&(null===(n=t.deletions)?(t.deletions=[e],t.flags|=16):n.push(e)),t.child=a,t.memoizedState=null,a}function Ms(e,t){return(t=Ic({mode:"visible",children:t},e.mode,0,null)).return=e,e.child=t}function zs(e,t,n,r){return null!==r&&ho(r),xo(t,e.child,null,n),(e=Ms(t,t.pendingProps.children)).flags|=2,t.memoizedState=null,e}function Bs(e,t,n){e.lanes|=t;var r=e.alternate;null!==r&&(r.lanes|=t),jo(e.return,t,n)}function $s(e,t,n,r,a){var o=e.memoizedState;null===o?e.memoizedState={isBackwards:t,rendering:null,renderingStartTime:0,last:r,tail:n,tailMode:a}:(o.isBackwards=t,o.rendering=null,o.renderingStartTime=0,o.last=r,o.tail=n,o.tailMode=a)}function qs(e,t,n){var r=t.pendingProps,a=r.revealOrder,o=r.tail;if(ws(e,t,r.children,n),2&(r=ei.current))r=1&r|2,t.flags|=128;else{if(null!==e&&128&e.flags)e:for(e=t.child;null!==e;){if(13===e.tag)null!==e.memoizedState&&Bs(e,n,t);else if(19===e.tag)Bs(e,n,t);else if(null!==e.child){e.child.return=e,e=e.child;continue}if(e===t)break e;for(;null===e.sibling;){if(null===e.return||e.return===t)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}r&=1}if(Ca(ei,r),1&t.mode)switch(a){case"forwards":for(n=t.child,a=null;null!==n;)null!==(e=n.alternate)&&null===ti(e)&&(a=n),n=n.sibling;null===(n=a)?(a=t.child,t.child=null):(a=n.sibling,n.sibling=null),$s(t,!1,a,n,o);break;case"backwards":for(n=null,a=t.child,t.child=null;null!==a;){if(null!==(e=a.alternate)&&null===ti(e)){t.child=a;break}e=a.sibling,a.sibling=n,n=a,a=e}$s(t,!0,n,null,o);break;case"together":$s(t,!1,null,null,void 0);break;default:t.memoizedState=null}else t.memoizedState=null;return t.child}function Us(e,t){!(1&t.mode)&&null!==e&&(e.alternate=null,t.alternate=null,t.flags|=2)}function Hs(e,t,n){if(null!==e&&(t.dependencies=e.dependencies),Il|=t.lanes,!(n&t.childLanes))return null;if(null!==e&&t.child!==e.child)throw Error(o(153));if(null!==t.child){for(n=Pc(e=t.child,e.pendingProps),t.child=n,n.return=t;null!==e.sibling;)e=e.sibling,(n=n.sibling=Pc(e,e.pendingProps)).return=t;n.sibling=null}return t.child}function Gs(e,t){if(!ao)switch(e.tailMode){case"hidden":t=e.tail;for(var n=null;null!==t;)null!==t.alternate&&(n=t),t=t.sibling;null===n?e.tail=null:n.sibling=null;break;case"collapsed":n=e.tail;for(var r=null;null!==n;)null!==n.alternate&&(r=n),n=n.sibling;null===r?t||null===e.tail?e.tail=null:e.tail.sibling=null:r.sibling=null}}function Vs(e){var t=null!==e.alternate&&e.alternate.child===e.child,n=0,r=0;if(t)for(var a=e.child;null!==a;)n|=a.lanes|a.childLanes,r|=14680064&a.subtreeFlags,r|=14680064&a.flags,a.return=e,a=a.sibling;else for(a=e.child;null!==a;)n|=a.lanes|a.childLanes,r|=a.subtreeFlags,r|=a.flags,a.return=e,a=a.sibling;return e.subtreeFlags|=r,e.childLanes=n,t}function Ws(e,t,n){var r=t.pendingProps;switch(to(t),t.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return Vs(t),null;case 1:case 17:return Ra(t.type)&&Pa(),Vs(t),null;case 3:return r=t.stateNode,Zo(),Ea(ja),Ea(Ta),ri(),r.pendingContext&&(r.context=r.pendingContext,r.pendingContext=null),null!==e&&null!==e.child||(po(t)?t.flags|=4:null===e||e.memoizedState.isDehydrated&&!(256&t.flags)||(t.flags|=1024,null!==oo&&(ic(oo),oo=null))),Rs(e,t),Vs(t),null;case 5:Jo(t);var a=Ko(Qo.current);if(n=t.type,null!==e&&null!=t.stateNode)Ps(e,t,n,r,a),e.ref!==t.ref&&(t.flags|=512,t.flags|=2097152);else{if(!r){if(null===t.stateNode)throw Error(o(166));return Vs(t),null}if(e=Ko(Vo.current),po(t)){r=t.stateNode,n=t.type;var i=t.memoizedProps;switch(r[pa]=t,r[fa]=i,e=!!(1&t.mode),n){case"dialog":zr("cancel",r),zr("close",r);break;case"iframe":case"object":case"embed":zr("load",r);break;case"video":case"audio":for(a=0;a<Dr.length;a++)zr(Dr[a],r);break;case"source":zr("error",r);break;case"img":case"image":case"link":zr("error",r),zr("load",r);break;case"details":zr("toggle",r);break;case"input":Y(r,i),zr("invalid",r);break;case"select":r._wrapperState={wasMultiple:!!i.multiple},zr("invalid",r);break;case"textarea":ae(r,i),zr("invalid",r)}for(var l in ve(n,i),a=null,i)if(i.hasOwnProperty(l)){var c=i[l];"children"===l?"string"==typeof c?r.textContent!==c&&(!0!==i.suppressHydrationWarning&&Xr(r.textContent,c,e),a=["children",c]):"number"==typeof c&&r.textContent!==""+c&&(!0!==i.suppressHydrationWarning&&Xr(r.textContent,c,e),a=["children",""+c]):s.hasOwnProperty(l)&&null!=c&&"onScroll"===l&&zr("scroll",r)}switch(n){case"input":V(r),J(r,i,!0);break;case"textarea":V(r),ie(r);break;case"select":case"option":break;default:"function"==typeof i.onClick&&(r.onclick=Jr)}r=a,t.updateQueue=r,null!==r&&(t.flags|=4)}else{l=9===a.nodeType?a:a.ownerDocument,"http://www.w3.org/1999/xhtml"===e&&(e=se(n)),"http://www.w3.org/1999/xhtml"===e?"script"===n?((e=l.createElement("div")).innerHTML="<script><\/script>",e=e.removeChild(e.firstChild)):"string"==typeof r.is?e=l.createElement(n,{is:r.is}):(e=l.createElement(n),"select"===n&&(l=e,r.multiple?l.multiple=!0:r.size&&(l.size=r.size))):e=l.createElementNS(e,n),e[pa]=t,e[fa]=r,Ls(e,t,!1,!1),t.stateNode=e;e:{switch(l=ye(n,r),n){case"dialog":zr("cancel",e),zr("close",e),a=r;break;case"iframe":case"object":case"embed":zr("load",e),a=r;break;case"video":case"audio":for(a=0;a<Dr.length;a++)zr(Dr[a],e);a=r;break;case"source":zr("error",e),a=r;break;case"img":case"image":case"link":zr("error",e),zr("load",e),a=r;break;case"details":zr("toggle",e),a=r;break;case"input":Y(e,r),a=K(e,r),zr("invalid",e);break;case"option":default:a=r;break;case"select":e._wrapperState={wasMultiple:!!r.multiple},a=F({},r,{value:void 0}),zr("invalid",e);break;case"textarea":ae(e,r),a=re(e,r),zr("invalid",e)}for(i in ve(n,a),c=a)if(c.hasOwnProperty(i)){var u=c[i];"style"===i?ge(e,u):"dangerouslySetInnerHTML"===i?null!=(u=u?u.__html:void 0)&&de(e,u):"children"===i?"string"==typeof u?("textarea"!==n||""!==u)&&pe(e,u):"number"==typeof u&&pe(e,""+u):"suppressContentEditableWarning"!==i&&"suppressHydrationWarning"!==i&&"autoFocus"!==i&&(s.hasOwnProperty(i)?null!=u&&"onScroll"===i&&zr("scroll",e):null!=u&&y(e,i,u,l))}switch(n){case"input":V(e),J(e,r,!1);break;case"textarea":V(e),ie(e);break;case"option":null!=r.value&&e.setAttribute("value",""+H(r.value));break;case"select":e.multiple=!!r.multiple,null!=(i=r.value)?ne(e,!!r.multiple,i,!1):null!=r.defaultValue&&ne(e,!!r.multiple,r.defaultValue,!0);break;default:"function"==typeof a.onClick&&(e.onclick=Jr)}switch(n){case"button":case"input":case"select":case"textarea":r=!!r.autoFocus;break e;case"img":r=!0;break e;default:r=!1}}r&&(t.flags|=4)}null!==t.ref&&(t.flags|=512,t.flags|=2097152)}return Vs(t),null;case 6:if(e&&null!=t.stateNode)Os(e,t,e.memoizedProps,r);else{if("string"!=typeof r&&null===t.stateNode)throw Error(o(166));if(n=Ko(Qo.current),Ko(Vo.current),po(t)){if(r=t.stateNode,n=t.memoizedProps,r[pa]=t,(i=r.nodeValue!==n)&&null!==(e=no))switch(e.tag){case 3:Xr(r.nodeValue,n,!!(1&e.mode));break;case 5:!0!==e.memoizedProps.suppressHydrationWarning&&Xr(r.nodeValue,n,!!(1&e.mode))}i&&(t.flags|=4)}else(r=(9===n.nodeType?n:n.ownerDocument).createTextNode(r))[pa]=t,t.stateNode=r}return Vs(t),null;case 13:if(Ea(ei),r=t.memoizedState,null===e||null!==e.memoizedState&&null!==e.memoizedState.dehydrated){if(ao&&null!==ro&&1&t.mode&&!(128&t.flags))fo(),mo(),t.flags|=98560,i=!1;else if(i=po(t),null!==r&&null!==r.dehydrated){if(null===e){if(!i)throw Error(o(318));if(!(i=null!==(i=t.memoizedState)?i.dehydrated:null))throw Error(o(317));i[pa]=t}else mo(),!(128&t.flags)&&(t.memoizedState=null),t.flags|=4;Vs(t),i=!1}else null!==oo&&(ic(oo),oo=null),i=!0;if(!i)return 65536&t.flags?t:null}return 128&t.flags?(t.lanes=n,t):((r=null!==r)!==(null!==e&&null!==e.memoizedState)&&r&&(t.child.flags|=8192,1&t.mode&&(null===e||1&ei.current?0===Ol&&(Ol=3):hc())),null!==t.updateQueue&&(t.flags|=4),Vs(t),null);case 4:return Zo(),Rs(e,t),null===e&&qr(t.stateNode.containerInfo),Vs(t),null;case 10:return To(t.type._context),Vs(t),null;case 19:if(Ea(ei),null===(i=t.memoizedState))return Vs(t),null;if(r=!!(128&t.flags),null===(l=i.rendering))if(r)Gs(i,!1);else{if(0!==Ol||null!==e&&128&e.flags)for(e=t.child;null!==e;){if(null!==(l=ti(e))){for(t.flags|=128,Gs(i,!1),null!==(r=l.updateQueue)&&(t.updateQueue=r,t.flags|=4),t.subtreeFlags=0,r=n,n=t.child;null!==n;)e=r,(i=n).flags&=14680066,null===(l=i.alternate)?(i.childLanes=0,i.lanes=e,i.child=null,i.subtreeFlags=0,i.memoizedProps=null,i.memoizedState=null,i.updateQueue=null,i.dependencies=null,i.stateNode=null):(i.childLanes=l.childLanes,i.lanes=l.lanes,i.child=l.child,i.subtreeFlags=0,i.deletions=null,i.memoizedProps=l.memoizedProps,i.memoizedState=l.memoizedState,i.updateQueue=l.updateQueue,i.type=l.type,e=l.dependencies,i.dependencies=null===e?null:{lanes:e.lanes,firstContext:e.firstContext}),n=n.sibling;return Ca(ei,1&ei.current|2),t.child}e=e.sibling}null!==i.tail&&Ze()>ql&&(t.flags|=128,r=!0,Gs(i,!1),t.lanes=4194304)}else{if(!r)if(null!==(e=ti(l))){if(t.flags|=128,r=!0,null!==(n=e.updateQueue)&&(t.updateQueue=n,t.flags|=4),Gs(i,!0),null===i.tail&&"hidden"===i.tailMode&&!l.alternate&&!ao)return Vs(t),null}else 2*Ze()-i.renderingStartTime>ql&&1073741824!==n&&(t.flags|=128,r=!0,Gs(i,!1),t.lanes=4194304);i.isBackwards?(l.sibling=t.child,t.child=l):(null!==(n=i.last)?n.sibling=l:t.child=l,i.last=l)}return null!==i.tail?(t=i.tail,i.rendering=t,i.tail=t.sibling,i.renderingStartTime=Ze(),t.sibling=null,n=ei.current,Ca(ei,r?1&n|2:1&n),t):(Vs(t),null);case 22:case 23:return dc(),r=null!==t.memoizedState,null!==e&&null!==e.memoizedState!==r&&(t.flags|=8192),r&&1&t.mode?!!(1073741824&Rl)&&(Vs(t),6&t.subtreeFlags&&(t.flags|=8192)):Vs(t),null;case 24:case 25:return null}throw Error(o(156,t.tag))}function Qs(e,t){switch(to(t),t.tag){case 1:return Ra(t.type)&&Pa(),65536&(e=t.flags)?(t.flags=-65537&e|128,t):null;case 3:return Zo(),Ea(ja),Ea(Ta),ri(),65536&(e=t.flags)&&!(128&e)?(t.flags=-65537&e|128,t):null;case 5:return Jo(t),null;case 13:if(Ea(ei),null!==(e=t.memoizedState)&&null!==e.dehydrated){if(null===t.alternate)throw Error(o(340));mo()}return 65536&(e=t.flags)?(t.flags=-65537&e|128,t):null;case 19:return Ea(ei),null;case 4:return Zo(),null;case 10:return To(t.type._context),null;case 22:case 23:return dc(),null;default:return null}}Ls=function(e,t){for(var n=t.child;null!==n;){if(5===n.tag||6===n.tag)e.appendChild(n.stateNode);else if(4!==n.tag&&null!==n.child){n.child.return=n,n=n.child;continue}if(n===t)break;for(;null===n.sibling;){if(null===n.return||n.return===t)return;n=n.return}n.sibling.return=n.return,n=n.sibling}},Rs=function(){},Ps=function(e,t,n,r){var a=e.memoizedProps;if(a!==r){e=t.stateNode,Ko(Vo.current);var o,i=null;switch(n){case"input":a=K(e,a),r=K(e,r),i=[];break;case"select":a=F({},a,{value:void 0}),r=F({},r,{value:void 0}),i=[];break;case"textarea":a=re(e,a),r=re(e,r),i=[];break;default:"function"!=typeof a.onClick&&"function"==typeof r.onClick&&(e.onclick=Jr)}for(u in ve(n,r),n=null,a)if(!r.hasOwnProperty(u)&&a.hasOwnProperty(u)&&null!=a[u])if("style"===u){var l=a[u];for(o in l)l.hasOwnProperty(o)&&(n||(n={}),n[o]="")}else"dangerouslySetInnerHTML"!==u&&"children"!==u&&"suppressContentEditableWarning"!==u&&"suppressHydrationWarning"!==u&&"autoFocus"!==u&&(s.hasOwnProperty(u)?i||(i=[]):(i=i||[]).push(u,null));for(u in r){var c=r[u];if(l=null!=a?a[u]:void 0,r.hasOwnProperty(u)&&c!==l&&(null!=c||null!=l))if("style"===u)if(l){for(o in l)!l.hasOwnProperty(o)||c&&c.hasOwnProperty(o)||(n||(n={}),n[o]="");for(o in c)c.hasOwnProperty(o)&&l[o]!==c[o]&&(n||(n={}),n[o]=c[o])}else n||(i||(i=[]),i.push(u,n)),n=c;else"dangerouslySetInnerHTML"===u?(c=c?c.__html:void 0,l=l?l.__html:void 0,null!=c&&l!==c&&(i=i||[]).push(u,c)):"children"===u?"string"!=typeof c&&"number"!=typeof c||(i=i||[]).push(u,""+c):"suppressContentEditableWarning"!==u&&"suppressHydrationWarning"!==u&&(s.hasOwnProperty(u)?(null!=c&&"onScroll"===u&&zr("scroll",e),i||l===c||(i=[])):(i=i||[]).push(u,c))}n&&(i=i||[]).push("style",n);var u=i;(t.updateQueue=u)&&(t.flags|=4)}},Os=function(e,t,n,r){n!==r&&(t.flags|=4)};var Ks=!1,Ys=!1,Zs="function"==typeof WeakSet?WeakSet:Set,Xs=null;function Js(e,t){var n=e.ref;if(null!==n)if("function"==typeof n)try{n(null)}catch(r){_c(e,t,r)}else n.current=null}function el(e,t,n){try{n()}catch(r){_c(e,t,r)}}var tl=!1;function nl(e,t,n){var r=t.updateQueue;if(null!==(r=null!==r?r.lastEffect:null)){var a=r=r.next;do{if((a.tag&e)===e){var o=a.destroy;a.destroy=void 0,void 0!==o&&el(t,n,o)}a=a.next}while(a!==r)}}function rl(e,t){if(null!==(t=null!==(t=t.updateQueue)?t.lastEffect:null)){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function al(e){var t=e.ref;if(null!==t){var n=e.stateNode;e.tag,e=n,"function"==typeof t?t(e):t.current=e}}function ol(e){var t=e.alternate;null!==t&&(e.alternate=null,ol(t)),e.child=null,e.deletions=null,e.sibling=null,5===e.tag&&(null!==(t=e.stateNode)&&(delete t[pa],delete t[fa],delete t[ha],delete t[ga],delete t[ba])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function il(e){return 5===e.tag||3===e.tag||4===e.tag}function sl(e){e:for(;;){for(;null===e.sibling;){if(null===e.return||il(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;5!==e.tag&&6!==e.tag&&18!==e.tag;){if(2&e.flags)continue e;if(null===e.child||4===e.tag)continue e;e.child.return=e,e=e.child}if(!(2&e.flags))return e.stateNode}}function ll(e,t,n){var r=e.tag;if(5===r||6===r)e=e.stateNode,t?8===n.nodeType?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(8===n.nodeType?(t=n.parentNode).insertBefore(e,n):(t=n).appendChild(e),null!=(n=n._reactRootContainer)||null!==t.onclick||(t.onclick=Jr));else if(4!==r&&null!==(e=e.child))for(ll(e,t,n),e=e.sibling;null!==e;)ll(e,t,n),e=e.sibling}function cl(e,t,n){var r=e.tag;if(5===r||6===r)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(4!==r&&null!==(e=e.child))for(cl(e,t,n),e=e.sibling;null!==e;)cl(e,t,n),e=e.sibling}var ul=null,dl=!1;function pl(e,t,n){for(n=n.child;null!==n;)fl(e,t,n),n=n.sibling}function fl(e,t,n){if(ot&&"function"==typeof ot.onCommitFiberUnmount)try{ot.onCommitFiberUnmount(at,n)}catch(s){}switch(n.tag){case 5:Ys||Js(n,t);case 6:var r=ul,a=dl;ul=null,pl(e,t,n),dl=a,null!==(ul=r)&&(dl?(e=ul,n=n.stateNode,8===e.nodeType?e.parentNode.removeChild(n):e.removeChild(n)):ul.removeChild(n.stateNode));break;case 18:null!==ul&&(dl?(e=ul,n=n.stateNode,8===e.nodeType?la(e.parentNode,n):1===e.nodeType&&la(e,n),qt(e)):la(ul,n.stateNode));break;case 4:r=ul,a=dl,ul=n.stateNode.containerInfo,dl=!0,pl(e,t,n),ul=r,dl=a;break;case 0:case 11:case 14:case 15:if(!Ys&&(null!==(r=n.updateQueue)&&null!==(r=r.lastEffect))){a=r=r.next;do{var o=a,i=o.destroy;o=o.tag,void 0!==i&&(2&o||4&o)&&el(n,t,i),a=a.next}while(a!==r)}pl(e,t,n);break;case 1:if(!Ys&&(Js(n,t),"function"==typeof(r=n.stateNode).componentWillUnmount))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(s){_c(n,t,s)}pl(e,t,n);break;case 21:pl(e,t,n);break;case 22:1&n.mode?(Ys=(r=Ys)||null!==n.memoizedState,pl(e,t,n),Ys=r):pl(e,t,n);break;default:pl(e,t,n)}}function ml(e){var t=e.updateQueue;if(null!==t){e.updateQueue=null;var n=e.stateNode;null===n&&(n=e.stateNode=new Zs),t.forEach((function(t){var r=Tc.bind(null,e,t);n.has(t)||(n.add(t),t.then(r,r))}))}}function hl(e,t){var n=t.deletions;if(null!==n)for(var r=0;r<n.length;r++){var a=n[r];try{var i=e,s=t,l=s;e:for(;null!==l;){switch(l.tag){case 5:ul=l.stateNode,dl=!1;break e;case 3:case 4:ul=l.stateNode.containerInfo,dl=!0;break e}l=l.return}if(null===ul)throw Error(o(160));fl(i,s,a),ul=null,dl=!1;var c=a.alternate;null!==c&&(c.return=null),a.return=null}catch(u){_c(a,t,u)}}if(12854&t.subtreeFlags)for(t=t.child;null!==t;)gl(t,e),t=t.sibling}function gl(e,t){var n=e.alternate,r=e.flags;switch(e.tag){case 0:case 11:case 14:case 15:if(hl(t,e),bl(e),4&r){try{nl(3,e,e.return),rl(3,e)}catch(g){_c(e,e.return,g)}try{nl(5,e,e.return)}catch(g){_c(e,e.return,g)}}break;case 1:hl(t,e),bl(e),512&r&&null!==n&&Js(n,n.return);break;case 5:if(hl(t,e),bl(e),512&r&&null!==n&&Js(n,n.return),32&e.flags){var a=e.stateNode;try{pe(a,"")}catch(g){_c(e,e.return,g)}}if(4&r&&null!=(a=e.stateNode)){var i=e.memoizedProps,s=null!==n?n.memoizedProps:i,l=e.type,c=e.updateQueue;if(e.updateQueue=null,null!==c)try{"input"===l&&"radio"===i.type&&null!=i.name&&Z(a,i),ye(l,s);var u=ye(l,i);for(s=0;s<c.length;s+=2){var d=c[s],p=c[s+1];"style"===d?ge(a,p):"dangerouslySetInnerHTML"===d?de(a,p):"children"===d?pe(a,p):y(a,d,p,u)}switch(l){case"input":X(a,i);break;case"textarea":oe(a,i);break;case"select":var f=a._wrapperState.wasMultiple;a._wrapperState.wasMultiple=!!i.multiple;var m=i.value;null!=m?ne(a,!!i.multiple,m,!1):f!==!!i.multiple&&(null!=i.defaultValue?ne(a,!!i.multiple,i.defaultValue,!0):ne(a,!!i.multiple,i.multiple?[]:"",!1))}a[fa]=i}catch(g){_c(e,e.return,g)}}break;case 6:if(hl(t,e),bl(e),4&r){if(null===e.stateNode)throw Error(o(162));a=e.stateNode,i=e.memoizedProps;try{a.nodeValue=i}catch(g){_c(e,e.return,g)}}break;case 3:if(hl(t,e),bl(e),4&r&&null!==n&&n.memoizedState.isDehydrated)try{qt(t.containerInfo)}catch(g){_c(e,e.return,g)}break;case 4:default:hl(t,e),bl(e);break;case 13:hl(t,e),bl(e),8192&(a=e.child).flags&&(i=null!==a.memoizedState,a.stateNode.isHidden=i,!i||null!==a.alternate&&null!==a.alternate.memoizedState||($l=Ze())),4&r&&ml(e);break;case 22:if(d=null!==n&&null!==n.memoizedState,1&e.mode?(Ys=(u=Ys)||d,hl(t,e),Ys=u):hl(t,e),bl(e),8192&r){if(u=null!==e.memoizedState,(e.stateNode.isHidden=u)&&!d&&1&e.mode)for(Xs=e,d=e.child;null!==d;){for(p=Xs=d;null!==Xs;){switch(m=(f=Xs).child,f.tag){case 0:case 11:case 14:case 15:nl(4,f,f.return);break;case 1:Js(f,f.return);var h=f.stateNode;if("function"==typeof h.componentWillUnmount){r=f,n=f.return;try{t=r,h.props=t.memoizedProps,h.state=t.memoizedState,h.componentWillUnmount()}catch(g){_c(r,n,g)}}break;case 5:Js(f,f.return);break;case 22:if(null!==f.memoizedState){xl(p);continue}}null!==m?(m.return=f,Xs=m):xl(p)}d=d.sibling}e:for(d=null,p=e;;){if(5===p.tag){if(null===d){d=p;try{a=p.stateNode,u?"function"==typeof(i=a.style).setProperty?i.setProperty("display","none","important"):i.display="none":(l=p.stateNode,s=null!=(c=p.memoizedProps.style)&&c.hasOwnProperty("display")?c.display:null,l.style.display=he("display",s))}catch(g){_c(e,e.return,g)}}}else if(6===p.tag){if(null===d)try{p.stateNode.nodeValue=u?"":p.memoizedProps}catch(g){_c(e,e.return,g)}}else if((22!==p.tag&&23!==p.tag||null===p.memoizedState||p===e)&&null!==p.child){p.child.return=p,p=p.child;continue}if(p===e)break e;for(;null===p.sibling;){if(null===p.return||p.return===e)break e;d===p&&(d=null),p=p.return}d===p&&(d=null),p.sibling.return=p.return,p=p.sibling}}break;case 19:hl(t,e),bl(e),4&r&&ml(e);case 21:}}function bl(e){var t=e.flags;if(2&t){try{e:{for(var n=e.return;null!==n;){if(il(n)){var r=n;break e}n=n.return}throw Error(o(160))}switch(r.tag){case 5:var a=r.stateNode;32&r.flags&&(pe(a,""),r.flags&=-33),cl(e,sl(e),a);break;case 3:case 4:var i=r.stateNode.containerInfo;ll(e,sl(e),i);break;default:throw Error(o(161))}}catch(s){_c(e,e.return,s)}e.flags&=-3}4096&t&&(e.flags&=-4097)}function vl(e,t,n){Xs=e,yl(e,t,n)}function yl(e,t,n){for(var r=!!(1&e.mode);null!==Xs;){var a=Xs,o=a.child;if(22===a.tag&&r){var i=null!==a.memoizedState||Ks;if(!i){var s=a.alternate,l=null!==s&&null!==s.memoizedState||Ys;s=Ks;var c=Ys;if(Ks=i,(Ys=l)&&!c)for(Xs=a;null!==Xs;)l=(i=Xs).child,22===i.tag&&null!==i.memoizedState?kl(a):null!==l?(l.return=i,Xs=l):kl(a);for(;null!==o;)Xs=o,yl(o,t,n),o=o.sibling;Xs=a,Ks=s,Ys=c}wl(e)}else 8772&a.subtreeFlags&&null!==o?(o.return=a,Xs=o):wl(e)}}function wl(e){for(;null!==Xs;){var t=Xs;if(8772&t.flags){var n=t.alternate;try{if(8772&t.flags)switch(t.tag){case 0:case 11:case 15:Ys||rl(5,t);break;case 1:var r=t.stateNode;if(4&t.flags&&!Ys)if(null===n)r.componentDidMount();else{var a=t.elementType===t.type?n.memoizedProps:ns(t.type,n.memoizedProps);r.componentDidUpdate(a,n.memoizedState,r.__reactInternalSnapshotBeforeUpdate)}var i=t.updateQueue;null!==i&&Ho(t,i,r);break;case 3:var s=t.updateQueue;if(null!==s){if(n=null,null!==t.child)switch(t.child.tag){case 5:case 1:n=t.child.stateNode}Ho(t,s,n)}break;case 5:var l=t.stateNode;if(null===n&&4&t.flags){n=l;var c=t.memoizedProps;switch(t.type){case"button":case"input":case"select":case"textarea":c.autoFocus&&n.focus();break;case"img":c.src&&(n.src=c.src)}}break;case 6:case 4:case 12:case 19:case 17:case 21:case 22:case 23:case 25:break;case 13:if(null===t.memoizedState){var u=t.alternate;if(null!==u){var d=u.memoizedState;if(null!==d){var p=d.dehydrated;null!==p&&qt(p)}}}break;default:throw Error(o(163))}Ys||512&t.flags&&al(t)}catch(f){_c(t,t.return,f)}}if(t===e){Xs=null;break}if(null!==(n=t.sibling)){n.return=t.return,Xs=n;break}Xs=t.return}}function xl(e){for(;null!==Xs;){var t=Xs;if(t===e){Xs=null;break}var n=t.sibling;if(null!==n){n.return=t.return,Xs=n;break}Xs=t.return}}function kl(e){for(;null!==Xs;){var t=Xs;try{switch(t.tag){case 0:case 11:case 15:var n=t.return;try{rl(4,t)}catch(l){_c(t,n,l)}break;case 1:var r=t.stateNode;if("function"==typeof r.componentDidMount){var a=t.return;try{r.componentDidMount()}catch(l){_c(t,a,l)}}var o=t.return;try{al(t)}catch(l){_c(t,o,l)}break;case 5:var i=t.return;try{al(t)}catch(l){_c(t,i,l)}}}catch(l){_c(t,t.return,l)}if(t===e){Xs=null;break}var s=t.sibling;if(null!==s){s.return=t.return,Xs=s;break}Xs=t.return}}var Sl,_l=Math.ceil,El=w.ReactCurrentDispatcher,Cl=w.ReactCurrentOwner,Al=w.ReactCurrentBatchConfig,Tl=0,jl=null,Nl=null,Ll=0,Rl=0,Pl=_a(0),Ol=0,Dl=null,Il=0,Fl=0,Ml=0,zl=null,Bl=null,$l=0,ql=1/0,Ul=null,Hl=!1,Gl=null,Vl=null,Wl=!1,Ql=null,Kl=0,Yl=0,Zl=null,Xl=-1,Jl=0;function ec(){return 6&Tl?Ze():-1!==Xl?Xl:Xl=Ze()}function tc(e){return 1&e.mode?2&Tl&&0!==Ll?Ll&-Ll:null!==go.transition?(0===Jl&&(Jl=ht()),Jl):0!==(e=yt)?e:e=void 0===(e=window.event)?16:Yt(e.type):1}function nc(e,t,n,r){if(50<Yl)throw Yl=0,Zl=null,Error(o(185));bt(e,n,r),2&Tl&&e===jl||(e===jl&&(!(2&Tl)&&(Fl|=n),4===Ol&&sc(e,Ll)),rc(e,r),1===n&&0===Tl&&!(1&t.mode)&&(ql=Ze()+500,za&&qa()))}function rc(e,t){var n=e.callbackNode;!function(e,t){for(var n=e.suspendedLanes,r=e.pingedLanes,a=e.expirationTimes,o=e.pendingLanes;0<o;){var i=31-it(o),s=1<<i,l=a[i];-1===l?s&n&&!(s&r)||(a[i]=ft(s,t)):l<=t&&(e.expiredLanes|=s),o&=~s}}(e,t);var r=pt(e,e===jl?Ll:0);if(0===r)null!==n&&Qe(n),e.callbackNode=null,e.callbackPriority=0;else if(t=r&-r,e.callbackPriority!==t){if(null!=n&&Qe(n),1===t)0===e.tag?function(e){za=!0,$a(e)}(lc.bind(null,e)):$a(lc.bind(null,e)),ia((function(){!(6&Tl)&&qa()})),n=null;else{switch(wt(r)){case 1:n=Je;break;case 4:n=et;break;case 16:default:n=tt;break;case 536870912:n=rt}n=jc(n,ac.bind(null,e))}e.callbackPriority=t,e.callbackNode=n}}function ac(e,t){if(Xl=-1,Jl=0,6&Tl)throw Error(o(327));var n=e.callbackNode;if(kc()&&e.callbackNode!==n)return null;var r=pt(e,e===jl?Ll:0);if(0===r)return null;if(30&r||r&e.expiredLanes||t)t=gc(e,r);else{t=r;var a=Tl;Tl|=2;var i=mc();for(jl===e&&Ll===t||(Ul=null,ql=Ze()+500,pc(e,t));;)try{vc();break}catch(l){fc(e,l)}Ao(),El.current=i,Tl=a,null!==Nl?t=0:(jl=null,Ll=0,t=Ol)}if(0!==t){if(2===t&&(0!==(a=mt(e))&&(r=a,t=oc(e,a))),1===t)throw n=Dl,pc(e,0),sc(e,r),rc(e,Ze()),n;if(6===t)sc(e,r);else{if(a=e.current.alternate,!(30&r||function(e){for(var t=e;;){if(16384&t.flags){var n=t.updateQueue;if(null!==n&&null!==(n=n.stores))for(var r=0;r<n.length;r++){var a=n[r],o=a.getSnapshot;a=a.value;try{if(!sr(o(),a))return!1}catch(s){return!1}}}if(n=t.child,16384&t.subtreeFlags&&null!==n)n.return=t,t=n;else{if(t===e)break;for(;null===t.sibling;){if(null===t.return||t.return===e)return!0;t=t.return}t.sibling.return=t.return,t=t.sibling}}return!0}(a)||(t=gc(e,r),2===t&&(i=mt(e),0!==i&&(r=i,t=oc(e,i))),1!==t)))throw n=Dl,pc(e,0),sc(e,r),rc(e,Ze()),n;switch(e.finishedWork=a,e.finishedLanes=r,t){case 0:case 1:throw Error(o(345));case 2:case 5:xc(e,Bl,Ul);break;case 3:if(sc(e,r),(130023424&r)===r&&10<(t=$l+500-Ze())){if(0!==pt(e,0))break;if(((a=e.suspendedLanes)&r)!==r){ec(),e.pingedLanes|=e.suspendedLanes&a;break}e.timeoutHandle=ra(xc.bind(null,e,Bl,Ul),t);break}xc(e,Bl,Ul);break;case 4:if(sc(e,r),(4194240&r)===r)break;for(t=e.eventTimes,a=-1;0<r;){var s=31-it(r);i=1<<s,(s=t[s])>a&&(a=s),r&=~i}if(r=a,10<(r=(120>(r=Ze()-r)?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*_l(r/1960))-r)){e.timeoutHandle=ra(xc.bind(null,e,Bl,Ul),r);break}xc(e,Bl,Ul);break;default:throw Error(o(329))}}}return rc(e,Ze()),e.callbackNode===n?ac.bind(null,e):null}function oc(e,t){var n=zl;return e.current.memoizedState.isDehydrated&&(pc(e,t).flags|=256),2!==(e=gc(e,t))&&(t=Bl,Bl=n,null!==t&&ic(t)),e}function ic(e){null===Bl?Bl=e:Bl.push.apply(Bl,e)}function sc(e,t){for(t&=~Ml,t&=~Fl,e.suspendedLanes|=t,e.pingedLanes&=~t,e=e.expirationTimes;0<t;){var n=31-it(t),r=1<<n;e[n]=-1,t&=~r}}function lc(e){if(6&Tl)throw Error(o(327));kc();var t=pt(e,0);if(!(1&t))return rc(e,Ze()),null;var n=gc(e,t);if(0!==e.tag&&2===n){var r=mt(e);0!==r&&(t=r,n=oc(e,r))}if(1===n)throw n=Dl,pc(e,0),sc(e,t),rc(e,Ze()),n;if(6===n)throw Error(o(345));return e.finishedWork=e.current.alternate,e.finishedLanes=t,xc(e,Bl,Ul),rc(e,Ze()),null}function cc(e,t){var n=Tl;Tl|=1;try{return e(t)}finally{0===(Tl=n)&&(ql=Ze()+500,za&&qa())}}function uc(e){null!==Ql&&0===Ql.tag&&!(6&Tl)&&kc();var t=Tl;Tl|=1;var n=Al.transition,r=yt;try{if(Al.transition=null,yt=1,e)return e()}finally{yt=r,Al.transition=n,!(6&(Tl=t))&&qa()}}function dc(){Rl=Pl.current,Ea(Pl)}function pc(e,t){e.finishedWork=null,e.finishedLanes=0;var n=e.timeoutHandle;if(-1!==n&&(e.timeoutHandle=-1,aa(n)),null!==Nl)for(n=Nl.return;null!==n;){var r=n;switch(to(r),r.tag){case 1:null!=(r=r.type.childContextTypes)&&Pa();break;case 3:Zo(),Ea(ja),Ea(Ta),ri();break;case 5:Jo(r);break;case 4:Zo();break;case 13:case 19:Ea(ei);break;case 10:To(r.type._context);break;case 22:case 23:dc()}n=n.return}if(jl=e,Nl=e=Pc(e.current,null),Ll=Rl=t,Ol=0,Dl=null,Ml=Fl=Il=0,Bl=zl=null,null!==Ro){for(t=0;t<Ro.length;t++)if(null!==(r=(n=Ro[t]).interleaved)){n.interleaved=null;var a=r.next,o=n.pending;if(null!==o){var i=o.next;o.next=a,r.next=i}n.pending=r}Ro=null}return e}function fc(e,t){for(;;){var n=Nl;try{if(Ao(),ai.current=Xi,ui){for(var r=si.memoizedState;null!==r;){var a=r.queue;null!==a&&(a.pending=null),r=r.next}ui=!1}if(ii=0,ci=li=si=null,di=!1,pi=0,Cl.current=null,null===n||null===n.return){Ol=1,Dl=t,Nl=null;break}e:{var i=e,s=n.return,l=n,c=t;if(t=Ll,l.flags|=32768,null!==c&&"object"==typeof c&&"function"==typeof c.then){var u=c,d=l,p=d.tag;if(!(1&d.mode||0!==p&&11!==p&&15!==p)){var f=d.alternate;f?(d.updateQueue=f.updateQueue,d.memoizedState=f.memoizedState,d.lanes=f.lanes):(d.updateQueue=null,d.memoizedState=null)}var m=gs(s);if(null!==m){m.flags&=-257,bs(m,s,l,0,t),1&m.mode&&hs(i,u,t),c=u;var h=(t=m).updateQueue;if(null===h){var g=new Set;g.add(c),t.updateQueue=g}else h.add(c);break e}if(!(1&t)){hs(i,u,t),hc();break e}c=Error(o(426))}else if(ao&&1&l.mode){var b=gs(s);if(null!==b){!(65536&b.flags)&&(b.flags|=256),bs(b,s,l,0,t),ho(cs(c,l));break e}}i=c=cs(c,l),4!==Ol&&(Ol=2),null===zl?zl=[i]:zl.push(i),i=s;do{switch(i.tag){case 3:i.flags|=65536,t&=-t,i.lanes|=t,qo(i,fs(0,c,t));break e;case 1:l=c;var v=i.type,y=i.stateNode;if(!(128&i.flags||"function"!=typeof v.getDerivedStateFromError&&(null===y||"function"!=typeof y.componentDidCatch||null!==Vl&&Vl.has(y)))){i.flags|=65536,t&=-t,i.lanes|=t,qo(i,ms(i,l,t));break e}}i=i.return}while(null!==i)}wc(n)}catch(w){t=w,Nl===n&&null!==n&&(Nl=n=n.return);continue}break}}function mc(){var e=El.current;return El.current=Xi,null===e?Xi:e}function hc(){0!==Ol&&3!==Ol&&2!==Ol||(Ol=4),null===jl||!(268435455&Il)&&!(268435455&Fl)||sc(jl,Ll)}function gc(e,t){var n=Tl;Tl|=2;var r=mc();for(jl===e&&Ll===t||(Ul=null,pc(e,t));;)try{bc();break}catch(a){fc(e,a)}if(Ao(),Tl=n,El.current=r,null!==Nl)throw Error(o(261));return jl=null,Ll=0,Ol}function bc(){for(;null!==Nl;)yc(Nl)}function vc(){for(;null!==Nl&&!Ke();)yc(Nl)}function yc(e){var t=Sl(e.alternate,e,Rl);e.memoizedProps=e.pendingProps,null===t?wc(e):Nl=t,Cl.current=null}function wc(e){var t=e;do{var n=t.alternate;if(e=t.return,32768&t.flags){if(null!==(n=Qs(n,t)))return n.flags&=32767,void(Nl=n);if(null===e)return Ol=6,void(Nl=null);e.flags|=32768,e.subtreeFlags=0,e.deletions=null}else if(null!==(n=Ws(n,t,Rl)))return void(Nl=n);if(null!==(t=t.sibling))return void(Nl=t);Nl=t=e}while(null!==t);0===Ol&&(Ol=5)}function xc(e,t,n){var r=yt,a=Al.transition;try{Al.transition=null,yt=1,function(e,t,n,r){do{kc()}while(null!==Ql);if(6&Tl)throw Error(o(327));n=e.finishedWork;var a=e.finishedLanes;if(null===n)return null;if(e.finishedWork=null,e.finishedLanes=0,n===e.current)throw Error(o(177));e.callbackNode=null,e.callbackPriority=0;var i=n.lanes|n.childLanes;if(function(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0<n;){var a=31-it(n),o=1<<a;t[a]=0,r[a]=-1,e[a]=-1,n&=~o}}(e,i),e===jl&&(Nl=jl=null,Ll=0),!(2064&n.subtreeFlags)&&!(2064&n.flags)||Wl||(Wl=!0,jc(tt,(function(){return kc(),null}))),i=!!(15990&n.flags),!!(15990&n.subtreeFlags)||i){i=Al.transition,Al.transition=null;var s=yt;yt=1;var l=Tl;Tl|=4,Cl.current=null,function(e,t){if(ea=Ht,fr(e=pr())){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{var r=(n=(n=e.ownerDocument)&&n.defaultView||window).getSelection&&n.getSelection();if(r&&0!==r.rangeCount){n=r.anchorNode;var a=r.anchorOffset,i=r.focusNode;r=r.focusOffset;try{n.nodeType,i.nodeType}catch(x){n=null;break e}var s=0,l=-1,c=-1,u=0,d=0,p=e,f=null;t:for(;;){for(var m;p!==n||0!==a&&3!==p.nodeType||(l=s+a),p!==i||0!==r&&3!==p.nodeType||(c=s+r),3===p.nodeType&&(s+=p.nodeValue.length),null!==(m=p.firstChild);)f=p,p=m;for(;;){if(p===e)break t;if(f===n&&++u===a&&(l=s),f===i&&++d===r&&(c=s),null!==(m=p.nextSibling))break;f=(p=f).parentNode}p=m}n=-1===l||-1===c?null:{start:l,end:c}}else n=null}n=n||{start:0,end:0}}else n=null;for(ta={focusedElem:e,selectionRange:n},Ht=!1,Xs=t;null!==Xs;)if(e=(t=Xs).child,1028&t.subtreeFlags&&null!==e)e.return=t,Xs=e;else for(;null!==Xs;){t=Xs;try{var h=t.alternate;if(1024&t.flags)switch(t.tag){case 0:case 11:case 15:case 5:case 6:case 4:case 17:break;case 1:if(null!==h){var g=h.memoizedProps,b=h.memoizedState,v=t.stateNode,y=v.getSnapshotBeforeUpdate(t.elementType===t.type?g:ns(t.type,g),b);v.__reactInternalSnapshotBeforeUpdate=y}break;case 3:var w=t.stateNode.containerInfo;1===w.nodeType?w.textContent="":9===w.nodeType&&w.documentElement&&w.removeChild(w.documentElement);break;default:throw Error(o(163))}}catch(x){_c(t,t.return,x)}if(null!==(e=t.sibling)){e.return=t.return,Xs=e;break}Xs=t.return}h=tl,tl=!1}(e,n),gl(n,e),mr(ta),Ht=!!ea,ta=ea=null,e.current=n,vl(n,e,a),Ye(),Tl=l,yt=s,Al.transition=i}else e.current=n;if(Wl&&(Wl=!1,Ql=e,Kl=a),i=e.pendingLanes,0===i&&(Vl=null),function(e){if(ot&&"function"==typeof ot.onCommitFiberRoot)try{ot.onCommitFiberRoot(at,e,void 0,!(128&~e.current.flags))}catch(t){}}(n.stateNode),rc(e,Ze()),null!==t)for(r=e.onRecoverableError,n=0;n<t.length;n++)a=t[n],r(a.value,{componentStack:a.stack,digest:a.digest});if(Hl)throw Hl=!1,e=Gl,Gl=null,e;!!(1&Kl)&&0!==e.tag&&kc(),i=e.pendingLanes,1&i?e===Zl?Yl++:(Yl=0,Zl=e):Yl=0,qa()}(e,t,n,r)}finally{Al.transition=a,yt=r}return null}function kc(){if(null!==Ql){var e=wt(Kl),t=Al.transition,n=yt;try{if(Al.transition=null,yt=16>e?16:e,null===Ql)var r=!1;else{if(e=Ql,Ql=null,Kl=0,6&Tl)throw Error(o(331));var a=Tl;for(Tl|=4,Xs=e.current;null!==Xs;){var i=Xs,s=i.child;if(16&Xs.flags){var l=i.deletions;if(null!==l){for(var c=0;c<l.length;c++){var u=l[c];for(Xs=u;null!==Xs;){var d=Xs;switch(d.tag){case 0:case 11:case 15:nl(8,d,i)}var p=d.child;if(null!==p)p.return=d,Xs=p;else for(;null!==Xs;){var f=(d=Xs).sibling,m=d.return;if(ol(d),d===u){Xs=null;break}if(null!==f){f.return=m,Xs=f;break}Xs=m}}}var h=i.alternate;if(null!==h){var g=h.child;if(null!==g){h.child=null;do{var b=g.sibling;g.sibling=null,g=b}while(null!==g)}}Xs=i}}if(2064&i.subtreeFlags&&null!==s)s.return=i,Xs=s;else e:for(;null!==Xs;){if(2048&(i=Xs).flags)switch(i.tag){case 0:case 11:case 15:nl(9,i,i.return)}var v=i.sibling;if(null!==v){v.return=i.return,Xs=v;break e}Xs=i.return}}var y=e.current;for(Xs=y;null!==Xs;){var w=(s=Xs).child;if(2064&s.subtreeFlags&&null!==w)w.return=s,Xs=w;else e:for(s=y;null!==Xs;){if(2048&(l=Xs).flags)try{switch(l.tag){case 0:case 11:case 15:rl(9,l)}}catch(k){_c(l,l.return,k)}if(l===s){Xs=null;break e}var x=l.sibling;if(null!==x){x.return=l.return,Xs=x;break e}Xs=l.return}}if(Tl=a,qa(),ot&&"function"==typeof ot.onPostCommitFiberRoot)try{ot.onPostCommitFiberRoot(at,e)}catch(k){}r=!0}return r}finally{yt=n,Al.transition=t}}return!1}function Sc(e,t,n){e=Bo(e,t=fs(0,t=cs(n,t),1),1),t=ec(),null!==e&&(bt(e,1,t),rc(e,t))}function _c(e,t,n){if(3===e.tag)Sc(e,e,n);else for(;null!==t;){if(3===t.tag){Sc(t,e,n);break}if(1===t.tag){var r=t.stateNode;if("function"==typeof t.type.getDerivedStateFromError||"function"==typeof r.componentDidCatch&&(null===Vl||!Vl.has(r))){t=Bo(t,e=ms(t,e=cs(n,e),1),1),e=ec(),null!==t&&(bt(t,1,e),rc(t,e));break}}t=t.return}}function Ec(e,t,n){var r=e.pingCache;null!==r&&r.delete(t),t=ec(),e.pingedLanes|=e.suspendedLanes&n,jl===e&&(Ll&n)===n&&(4===Ol||3===Ol&&(130023424&Ll)===Ll&&500>Ze()-$l?pc(e,0):Ml|=n),rc(e,t)}function Cc(e,t){0===t&&(1&e.mode?(t=ut,!(130023424&(ut<<=1))&&(ut=4194304)):t=1);var n=ec();null!==(e=Do(e,t))&&(bt(e,t,n),rc(e,n))}function Ac(e){var t=e.memoizedState,n=0;null!==t&&(n=t.retryLane),Cc(e,n)}function Tc(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,a=e.memoizedState;null!==a&&(n=a.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(o(314))}null!==r&&r.delete(t),Cc(e,n)}function jc(e,t){return We(e,t)}function Nc(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Lc(e,t,n,r){return new Nc(e,t,n,r)}function Rc(e){return!(!(e=e.prototype)||!e.isReactComponent)}function Pc(e,t){var n=e.alternate;return null===n?((n=Lc(e.tag,t,e.key,e.mode)).elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=14680064&e.flags,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=null===t?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Oc(e,t,n,r,a,i){var s=2;if(r=e,"function"==typeof e)Rc(e)&&(s=1);else if("string"==typeof e)s=5;else e:switch(e){case S:return Dc(n.children,a,i,t);case _:s=8,a|=8;break;case E:return(e=Lc(12,n,t,2|a)).elementType=E,e.lanes=i,e;case j:return(e=Lc(13,n,t,a)).elementType=j,e.lanes=i,e;case N:return(e=Lc(19,n,t,a)).elementType=N,e.lanes=i,e;case P:return Ic(n,a,i,t);default:if("object"==typeof e&&null!==e)switch(e.$$typeof){case C:s=10;break e;case A:s=9;break e;case T:s=11;break e;case L:s=14;break e;case R:s=16,r=null;break e}throw Error(o(130,null==e?e:typeof e,""))}return(t=Lc(s,n,t,a)).elementType=e,t.type=r,t.lanes=i,t}function Dc(e,t,n,r){return(e=Lc(7,e,r,t)).lanes=n,e}function Ic(e,t,n,r){return(e=Lc(22,e,r,t)).elementType=P,e.lanes=n,e.stateNode={isHidden:!1},e}function Fc(e,t,n){return(e=Lc(6,e,null,t)).lanes=n,e}function Mc(e,t,n){return(t=Lc(4,null!==e.children?e.children:[],e.key,t)).lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function zc(e,t,n,r,a){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=gt(0),this.expirationTimes=gt(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=gt(0),this.identifierPrefix=r,this.onRecoverableError=a,this.mutableSourceEagerHydrationData=null}function Bc(e,t,n,r,a,o,i,s,l){return e=new zc(e,t,n,s,l),1===t?(t=1,!0===o&&(t|=8)):t=0,o=Lc(3,null,null,t),e.current=o,o.stateNode=e,o.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Fo(o),e}function $c(e){if(!e)return Aa;e:{if(qe(e=e._reactInternals)!==e||1!==e.tag)throw Error(o(170));var t=e;do{switch(t.tag){case 3:t=t.stateNode.context;break e;case 1:if(Ra(t.type)){t=t.stateNode.__reactInternalMemoizedMergedChildContext;break e}}t=t.return}while(null!==t);throw Error(o(171))}if(1===e.tag){var n=e.type;if(Ra(n))return Da(e,n,t)}return t}function qc(e,t,n,r,a,o,i,s,l){return(e=Bc(n,r,!0,e,0,o,0,s,l)).context=$c(null),n=e.current,(o=zo(r=ec(),a=tc(n))).callback=null!=t?t:null,Bo(n,o,a),e.current.lanes=a,bt(e,a,r),rc(e,r),e}function Uc(e,t,n,r){var a=t.current,o=ec(),i=tc(a);return n=$c(n),null===t.context?t.context=n:t.pendingContext=n,(t=zo(o,i)).payload={element:e},null!==(r=void 0===r?null:r)&&(t.callback=r),null!==(e=Bo(a,t,i))&&(nc(e,a,i,o),$o(e,a,i)),i}function Hc(e){return(e=e.current).child?(e.child.tag,e.child.stateNode):null}function Gc(e,t){if(null!==(e=e.memoizedState)&&null!==e.dehydrated){var n=e.retryLane;e.retryLane=0!==n&&n<t?n:t}}function Vc(e,t){Gc(e,t),(e=e.alternate)&&Gc(e,t)}Sl=function(e,t,n){if(null!==e)if(e.memoizedProps!==t.pendingProps||ja.current)ys=!0;else{if(!(e.lanes&n||128&t.flags))return ys=!1,function(e,t,n){switch(t.tag){case 3:js(t),mo();break;case 5:Xo(t);break;case 1:Ra(t.type)&&Ia(t);break;case 4:Yo(t,t.stateNode.containerInfo);break;case 10:var r=t.type._context,a=t.memoizedProps.value;Ca(So,r._currentValue),r._currentValue=a;break;case 13:if(null!==(r=t.memoizedState))return null!==r.dehydrated?(Ca(ei,1&ei.current),t.flags|=128,null):n&t.child.childLanes?Fs(e,t,n):(Ca(ei,1&ei.current),null!==(e=Hs(e,t,n))?e.sibling:null);Ca(ei,1&ei.current);break;case 19:if(r=!!(n&t.childLanes),128&e.flags){if(r)return qs(e,t,n);t.flags|=128}if(null!==(a=t.memoizedState)&&(a.rendering=null,a.tail=null,a.lastEffect=null),Ca(ei,ei.current),r)break;return null;case 22:case 23:return t.lanes=0,_s(e,t,n)}return Hs(e,t,n)}(e,t,n);ys=!!(131072&e.flags)}else ys=!1,ao&&1048576&t.flags&&Ja(t,Va,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;Us(e,t),e=t.pendingProps;var a=La(t,Ta.current);No(t,n),a=gi(null,t,r,e,a,n);var i=bi();return t.flags|=1,"object"==typeof a&&null!==a&&"function"==typeof a.render&&void 0===a.$$typeof?(t.tag=1,t.memoizedState=null,t.updateQueue=null,Ra(r)?(i=!0,Ia(t)):i=!1,t.memoizedState=null!==a.state&&void 0!==a.state?a.state:null,Fo(t),a.updater=as,t.stateNode=a,a._reactInternals=t,ls(t,r,e,n),t=Ts(null,t,r,!0,i,n)):(t.tag=0,ao&&i&&eo(t),ws(null,t,a,n),t=t.child),t;case 16:r=t.elementType;e:{switch(Us(e,t),e=t.pendingProps,r=(a=r._init)(r._payload),t.type=r,a=t.tag=function(e){if("function"==typeof e)return Rc(e)?1:0;if(null!=e){if((e=e.$$typeof)===T)return 11;if(e===L)return 14}return 2}(r),e=ns(r,e),a){case 0:t=Cs(null,t,r,e,n);break e;case 1:t=As(null,t,r,e,n);break e;case 11:t=xs(null,t,r,e,n);break e;case 14:t=ks(null,t,r,ns(r.type,e),n);break e}throw Error(o(306,r,""))}return t;case 0:return r=t.type,a=t.pendingProps,Cs(e,t,r,a=t.elementType===r?a:ns(r,a),n);case 1:return r=t.type,a=t.pendingProps,As(e,t,r,a=t.elementType===r?a:ns(r,a),n);case 3:e:{if(js(t),null===e)throw Error(o(387));r=t.pendingProps,a=(i=t.memoizedState).element,Mo(e,t),Uo(t,r,null,n);var s=t.memoizedState;if(r=s.element,i.isDehydrated){if(i={element:r,isDehydrated:!1,cache:s.cache,pendingSuspenseBoundaries:s.pendingSuspenseBoundaries,transitions:s.transitions},t.updateQueue.baseState=i,t.memoizedState=i,256&t.flags){t=Ns(e,t,r,n,a=cs(Error(o(423)),t));break e}if(r!==a){t=Ns(e,t,r,n,a=cs(Error(o(424)),t));break e}for(ro=ca(t.stateNode.containerInfo.firstChild),no=t,ao=!0,oo=null,n=ko(t,null,r,n),t.child=n;n;)n.flags=-3&n.flags|4096,n=n.sibling}else{if(mo(),r===a){t=Hs(e,t,n);break e}ws(e,t,r,n)}t=t.child}return t;case 5:return Xo(t),null===e&&co(t),r=t.type,a=t.pendingProps,i=null!==e?e.memoizedProps:null,s=a.children,na(r,a)?s=null:null!==i&&na(r,i)&&(t.flags|=32),Es(e,t),ws(e,t,s,n),t.child;case 6:return null===e&&co(t),null;case 13:return Fs(e,t,n);case 4:return Yo(t,t.stateNode.containerInfo),r=t.pendingProps,null===e?t.child=xo(t,null,r,n):ws(e,t,r,n),t.child;case 11:return r=t.type,a=t.pendingProps,xs(e,t,r,a=t.elementType===r?a:ns(r,a),n);case 7:return ws(e,t,t.pendingProps,n),t.child;case 8:case 12:return ws(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,a=t.pendingProps,i=t.memoizedProps,s=a.value,Ca(So,r._currentValue),r._currentValue=s,null!==i)if(sr(i.value,s)){if(i.children===a.children&&!ja.current){t=Hs(e,t,n);break e}}else for(null!==(i=t.child)&&(i.return=t);null!==i;){var l=i.dependencies;if(null!==l){s=i.child;for(var c=l.firstContext;null!==c;){if(c.context===r){if(1===i.tag){(c=zo(-1,n&-n)).tag=2;var u=i.updateQueue;if(null!==u){var d=(u=u.shared).pending;null===d?c.next=c:(c.next=d.next,d.next=c),u.pending=c}}i.lanes|=n,null!==(c=i.alternate)&&(c.lanes|=n),jo(i.return,n,t),l.lanes|=n;break}c=c.next}}else if(10===i.tag)s=i.type===t.type?null:i.child;else if(18===i.tag){if(null===(s=i.return))throw Error(o(341));s.lanes|=n,null!==(l=s.alternate)&&(l.lanes|=n),jo(s,n,t),s=i.sibling}else s=i.child;if(null!==s)s.return=i;else for(s=i;null!==s;){if(s===t){s=null;break}if(null!==(i=s.sibling)){i.return=s.return,s=i;break}s=s.return}i=s}ws(e,t,a.children,n),t=t.child}return t;case 9:return a=t.type,r=t.pendingProps.children,No(t,n),r=r(a=Lo(a)),t.flags|=1,ws(e,t,r,n),t.child;case 14:return a=ns(r=t.type,t.pendingProps),ks(e,t,r,a=ns(r.type,a),n);case 15:return Ss(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,a=t.pendingProps,a=t.elementType===r?a:ns(r,a),Us(e,t),t.tag=1,Ra(r)?(e=!0,Ia(t)):e=!1,No(t,n),is(t,r,a),ls(t,r,a,n),Ts(null,t,r,!0,e,n);case 19:return qs(e,t,n);case 22:return _s(e,t,n)}throw Error(o(156,t.tag))};var Wc="function"==typeof reportError?reportError:function(e){console.error(e)};function Qc(e){this._internalRoot=e}function Kc(e){this._internalRoot=e}function Yc(e){return!(!e||1!==e.nodeType&&9!==e.nodeType&&11!==e.nodeType)}function Zc(e){return!(!e||1!==e.nodeType&&9!==e.nodeType&&11!==e.nodeType&&(8!==e.nodeType||" react-mount-point-unstable "!==e.nodeValue))}function Xc(){}function Jc(e,t,n,r,a){var o=n._reactRootContainer;if(o){var i=o;if("function"==typeof a){var s=a;a=function(){var e=Hc(i);s.call(e)}}Uc(t,i,e,a)}else i=function(e,t,n,r,a){if(a){if("function"==typeof r){var o=r;r=function(){var e=Hc(i);o.call(e)}}var i=qc(t,r,e,0,null,!1,0,"",Xc);return e._reactRootContainer=i,e[ma]=i.current,qr(8===e.nodeType?e.parentNode:e),uc(),i}for(;a=e.lastChild;)e.removeChild(a);if("function"==typeof r){var s=r;r=function(){var e=Hc(l);s.call(e)}}var l=Bc(e,0,!1,null,0,!1,0,"",Xc);return e._reactRootContainer=l,e[ma]=l.current,qr(8===e.nodeType?e.parentNode:e),uc((function(){Uc(t,l,n,r)})),l}(n,t,e,a,r);return Hc(i)}Kc.prototype.render=Qc.prototype.render=function(e){var t=this._internalRoot;if(null===t)throw Error(o(409));Uc(e,t,null,null)},Kc.prototype.unmount=Qc.prototype.unmount=function(){var e=this._internalRoot;if(null!==e){this._internalRoot=null;var t=e.containerInfo;uc((function(){Uc(null,e,null,null)})),t[ma]=null}},Kc.prototype.unstable_scheduleHydration=function(e){if(e){var t=_t();e={blockedOn:null,target:e,priority:t};for(var n=0;n<Pt.length&&0!==t&&t<Pt[n].priority;n++);Pt.splice(n,0,e),0===n&&Ft(e)}},xt=function(e){switch(e.tag){case 3:var t=e.stateNode;if(t.current.memoizedState.isDehydrated){var n=dt(t.pendingLanes);0!==n&&(vt(t,1|n),rc(t,Ze()),!(6&Tl)&&(ql=Ze()+500,qa()))}break;case 13:uc((function(){var t=Do(e,1);if(null!==t){var n=ec();nc(t,e,1,n)}})),Vc(e,1)}},kt=function(e){if(13===e.tag){var t=Do(e,134217728);if(null!==t)nc(t,e,134217728,ec());Vc(e,134217728)}},St=function(e){if(13===e.tag){var t=tc(e),n=Do(e,t);if(null!==n)nc(n,e,t,ec());Vc(e,t)}},_t=function(){return yt},Et=function(e,t){var n=yt;try{return yt=e,t()}finally{yt=n}},ke=function(e,t,n){switch(t){case"input":if(X(e,n),t=n.name,"radio"===n.type&&null!=t){for(n=e;n.parentNode;)n=n.parentNode;for(n=n.querySelectorAll("input[name="+JSON.stringify(""+t)+'][type="radio"]'),t=0;t<n.length;t++){var r=n[t];if(r!==e&&r.form===e.form){var a=xa(r);if(!a)throw Error(o(90));W(r),X(r,a)}}}break;case"textarea":oe(e,n);break;case"select":null!=(t=n.value)&&ne(e,!!n.multiple,t,!1)}},Te=cc,je=uc;var eu={usingClientEntryPoint:!1,Events:[ya,wa,xa,Ce,Ae,cc]},tu={findFiberByHostInstance:va,bundleType:0,version:"18.3.0",rendererPackageName:"react-dom"},nu={bundleType:tu.bundleType,version:tu.version,rendererPackageName:tu.rendererPackageName,rendererConfig:tu.rendererConfig,overrideHookState:null,overrideHookStateDeletePath:null,overrideHookStateRenamePath:null,overrideProps:null,overridePropsDeletePath:null,overridePropsRenamePath:null,setErrorHandler:null,setSuspenseHandler:null,scheduleUpdate:null,currentDispatcherRef:w.ReactCurrentDispatcher,findHostInstanceByFiber:function(e){return null===(e=Ge(e))?null:e.stateNode},findFiberByHostInstance:tu.findFiberByHostInstance||function(){return null},findHostInstancesForRefresh:null,scheduleRefresh:null,scheduleRoot:null,setRefreshHandler:null,getCurrentFiber:null,reconcilerVersion:"18.3.0-next-8a015b68cc-20240425"};if("undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__){var ru=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(!ru.isDisabled&&ru.supportsFiber)try{at=ru.inject(nu),ot=ru}catch(ue){}}t.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=eu,t.createPortal=function(e,t){var n=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null;if(!Yc(t))throw Error(o(200));return function(e,t,n){var r=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null;return{$$typeof:k,key:null==r?null:""+r,children:e,containerInfo:t,implementation:n}}(e,t,null,n)},t.createRoot=function(e,t){if(!Yc(e))throw Error(o(299));var n=!1,r="",a=Wc;return null!=t&&(!0===t.unstable_strictMode&&(n=!0),void 0!==t.identifierPrefix&&(r=t.identifierPrefix),void 0!==t.onRecoverableError&&(a=t.onRecoverableError)),t=Bc(e,1,!1,null,0,n,0,r,a),e[ma]=t.current,qr(8===e.nodeType?e.parentNode:e),new Qc(t)},t.findDOMNode=function(e){if(null==e)return null;if(1===e.nodeType)return e;var t=e._reactInternals;if(void 0===t){if("function"==typeof e.render)throw Error(o(188));throw e=Object.keys(e).join(","),Error(o(268,e))}return e=null===(e=Ge(t))?null:e.stateNode},t.flushSync=function(e){return uc(e)},t.hydrate=function(e,t,n){if(!Zc(t))throw Error(o(200));return Jc(null,e,t,!0,n)},t.hydrateRoot=function(e,t,n){if(!Yc(e))throw Error(o(405));var r=null!=n&&n.hydratedSources||null,a=!1,i="",s=Wc;if(null!=n&&(!0===n.unstable_strictMode&&(a=!0),void 0!==n.identifierPrefix&&(i=n.identifierPrefix),void 0!==n.onRecoverableError&&(s=n.onRecoverableError)),t=qc(t,null,e,1,null!=n?n:null,a,0,i,s),e[ma]=t.current,qr(e),r)for(e=0;e<r.length;e++)a=(a=(n=r[e])._getVersion)(n._source),null==t.mutableSourceEagerHydrationData?t.mutableSourceEagerHydrationData=[n,a]:t.mutableSourceEagerHydrationData.push(n,a);return new Kc(t)},t.render=function(e,t,n){if(!Zc(t))throw Error(o(200));return Jc(null,e,t,!1,n)},t.unmountComponentAtNode=function(e){if(!Zc(e))throw Error(o(40));return!!e._reactRootContainer&&(uc((function(){Jc(null,null,e,!1,(function(){e._reactRootContainer=null,e[ma]=null}))})),!0)},t.unstable_batchedUpdates=cc,t.unstable_renderSubtreeIntoContainer=function(e,t,n,r){if(!Zc(n))throw Error(o(200));if(null==e||void 0===e._reactInternals)throw Error(o(38));return Jc(e,t,n,!1,r)},t.version="18.3.0-next-8a015b68cc-20240425"},5338:(e,t,n)=>{"use strict";var r=n(961);t.createRoot=r.createRoot,t.hydrateRoot=r.hydrateRoot},961:(e,t,n)=>{"use strict";!function e(){if("undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE)try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}(),e.exports=n(2551)},115:e=>{var t="undefined"!=typeof Element,n="function"==typeof Map,r="function"==typeof Set,a="function"==typeof ArrayBuffer&&!!ArrayBuffer.isView;function o(e,i){if(e===i)return!0;if(e&&i&&"object"==typeof e&&"object"==typeof i){if(e.constructor!==i.constructor)return!1;var s,l,c,u;if(Array.isArray(e)){if((s=e.length)!=i.length)return!1;for(l=s;0!=l--;)if(!o(e[l],i[l]))return!1;return!0}if(n&&e instanceof Map&&i instanceof Map){if(e.size!==i.size)return!1;for(u=e.entries();!(l=u.next()).done;)if(!i.has(l.value[0]))return!1;for(u=e.entries();!(l=u.next()).done;)if(!o(l.value[1],i.get(l.value[0])))return!1;return!0}if(r&&e instanceof Set&&i instanceof Set){if(e.size!==i.size)return!1;for(u=e.entries();!(l=u.next()).done;)if(!i.has(l.value[0]))return!1;return!0}if(a&&ArrayBuffer.isView(e)&&ArrayBuffer.isView(i)){if((s=e.length)!=i.length)return!1;for(l=s;0!=l--;)if(e[l]!==i[l])return!1;return!0}if(e.constructor===RegExp)return e.source===i.source&&e.flags===i.flags;if(e.valueOf!==Object.prototype.valueOf)return e.valueOf()===i.valueOf();if(e.toString!==Object.prototype.toString)return e.toString()===i.toString();if((s=(c=Object.keys(e)).length)!==Object.keys(i).length)return!1;for(l=s;0!=l--;)if(!Object.prototype.hasOwnProperty.call(i,c[l]))return!1;if(t&&e instanceof Element)return!1;for(l=s;0!=l--;)if(("_owner"!==c[l]&&"__v"!==c[l]&&"__o"!==c[l]||!e.$$typeof)&&!o(e[c[l]],i[c[l]]))return!1;return!0}return e!=e&&i!=i}e.exports=function(e,t){try{return o(e,t)}catch(n){if((n.message||"").match(/stack|recursion/i))return console.warn("react-fast-compare cannot handle circular refs"),!1;throw n}}},545:(e,t,n)=>{"use strict";n.d(t,{mg:()=>J,vd:()=>G});var r=n(6540),a=n(5556),o=n.n(a),i=n(115),s=n.n(i),l=n(311),c=n.n(l),u=n(2833),d=n.n(u);function p(){return p=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},p.apply(this,arguments)}function f(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,m(e,t)}function m(e,t){return m=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},m(e,t)}function h(e,t){if(null==e)return{};var n,r,a={},o=Object.keys(e);for(r=0;r<o.length;r++)t.indexOf(n=o[r])>=0||(a[n]=e[n]);return a}var g={BASE:"base",BODY:"body",HEAD:"head",HTML:"html",LINK:"link",META:"meta",NOSCRIPT:"noscript",SCRIPT:"script",STYLE:"style",TITLE:"title",FRAGMENT:"Symbol(react.fragment)"},b={rel:["amphtml","canonical","alternate"]},v={type:["application/ld+json"]},y={charset:"",name:["robots","description"],property:["og:type","og:title","og:url","og:image","og:image:alt","og:description","twitter:url","twitter:title","twitter:description","twitter:image","twitter:image:alt","twitter:card","twitter:site"]},w=Object.keys(g).map((function(e){return g[e]})),x={accesskey:"accessKey",charset:"charSet",class:"className",contenteditable:"contentEditable",contextmenu:"contextMenu","http-equiv":"httpEquiv",itemprop:"itemProp",tabindex:"tabIndex"},k=Object.keys(x).reduce((function(e,t){return e[x[t]]=t,e}),{}),S=function(e,t){for(var n=e.length-1;n>=0;n-=1){var r=e[n];if(Object.prototype.hasOwnProperty.call(r,t))return r[t]}return null},_=function(e){var t=S(e,g.TITLE),n=S(e,"titleTemplate");if(Array.isArray(t)&&(t=t.join("")),n&&t)return n.replace(/%s/g,(function(){return t}));var r=S(e,"defaultTitle");return t||r||void 0},E=function(e){return S(e,"onChangeClientState")||function(){}},C=function(e,t){return t.filter((function(t){return void 0!==t[e]})).map((function(t){return t[e]})).reduce((function(e,t){return p({},e,t)}),{})},A=function(e,t){return t.filter((function(e){return void 0!==e[g.BASE]})).map((function(e){return e[g.BASE]})).reverse().reduce((function(t,n){if(!t.length)for(var r=Object.keys(n),a=0;a<r.length;a+=1){var o=r[a].toLowerCase();if(-1!==e.indexOf(o)&&n[o])return t.concat(n)}return t}),[])},T=function(e,t,n){var r={};return n.filter((function(t){return!!Array.isArray(t[e])||(void 0!==t[e]&&console&&"function"==typeof console.warn&&console.warn("Helmet: "+e+' should be of type "Array". Instead found type "'+typeof t[e]+'"'),!1)})).map((function(t){return t[e]})).reverse().reduce((function(e,n){var a={};n.filter((function(e){for(var n,o=Object.keys(e),i=0;i<o.length;i+=1){var s=o[i],l=s.toLowerCase();-1===t.indexOf(l)||"rel"===n&&"canonical"===e[n].toLowerCase()||"rel"===l&&"stylesheet"===e[l].toLowerCase()||(n=l),-1===t.indexOf(s)||"innerHTML"!==s&&"cssText"!==s&&"itemprop"!==s||(n=s)}if(!n||!e[n])return!1;var c=e[n].toLowerCase();return r[n]||(r[n]={}),a[n]||(a[n]={}),!r[n][c]&&(a[n][c]=!0,!0)})).reverse().forEach((function(t){return e.push(t)}));for(var o=Object.keys(a),i=0;i<o.length;i+=1){var s=o[i],l=p({},r[s],a[s]);r[s]=l}return e}),[]).reverse()},j=function(e,t){if(Array.isArray(e)&&e.length)for(var n=0;n<e.length;n+=1)if(e[n][t])return!0;return!1},N=function(e){return Array.isArray(e)?e.join(""):e},L=function(e,t){return Array.isArray(e)?e.reduce((function(e,n){return function(e,t){for(var n=Object.keys(e),r=0;r<n.length;r+=1)if(t[n[r]]&&t[n[r]].includes(e[n[r]]))return!0;return!1}(n,t)?e.priority.push(n):e.default.push(n),e}),{priority:[],default:[]}):{default:e}},R=function(e,t){var n;return p({},e,((n={})[t]=void 0,n))},P=[g.NOSCRIPT,g.SCRIPT,g.STYLE],O=function(e,t){return void 0===t&&(t=!0),!1===t?String(e):String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")},D=function(e){return Object.keys(e).reduce((function(t,n){var r=void 0!==e[n]?n+'="'+e[n]+'"':""+n;return t?t+" "+r:r}),"")},I=function(e,t){return void 0===t&&(t={}),Object.keys(e).reduce((function(t,n){return t[x[n]||n]=e[n],t}),t)},F=function(e,t){return t.map((function(t,n){var a,o=((a={key:n})["data-rh"]=!0,a);return Object.keys(t).forEach((function(e){var n=x[e]||e;"innerHTML"===n||"cssText"===n?o.dangerouslySetInnerHTML={__html:t.innerHTML||t.cssText}:o[n]=t[e]})),r.createElement(e,o)}))},M=function(e,t,n){switch(e){case g.TITLE:return{toComponent:function(){return n=t.titleAttributes,(a={key:e=t.title})["data-rh"]=!0,o=I(n,a),[r.createElement(g.TITLE,o,e)];var e,n,a,o},toString:function(){return function(e,t,n,r){var a=D(n),o=N(t);return a?"<"+e+' data-rh="true" '+a+">"+O(o,r)+"</"+e+">":"<"+e+' data-rh="true">'+O(o,r)+"</"+e+">"}(e,t.title,t.titleAttributes,n)}};case"bodyAttributes":case"htmlAttributes":return{toComponent:function(){return I(t)},toString:function(){return D(t)}};default:return{toComponent:function(){return F(e,t)},toString:function(){return function(e,t,n){return t.reduce((function(t,r){var a=Object.keys(r).filter((function(e){return!("innerHTML"===e||"cssText"===e)})).reduce((function(e,t){var a=void 0===r[t]?t:t+'="'+O(r[t],n)+'"';return e?e+" "+a:a}),""),o=r.innerHTML||r.cssText||"",i=-1===P.indexOf(e);return t+"<"+e+' data-rh="true" '+a+(i?"/>":">"+o+"</"+e+">")}),"")}(e,t,n)}}}},z=function(e){var t=e.baseTag,n=e.bodyAttributes,r=e.encode,a=e.htmlAttributes,o=e.noscriptTags,i=e.styleTags,s=e.title,l=void 0===s?"":s,c=e.titleAttributes,u=e.linkTags,d=e.metaTags,p=e.scriptTags,f={toComponent:function(){},toString:function(){return""}};if(e.prioritizeSeoTags){var m=function(e){var t=e.linkTags,n=e.scriptTags,r=e.encode,a=L(e.metaTags,y),o=L(t,b),i=L(n,v);return{priorityMethods:{toComponent:function(){return[].concat(F(g.META,a.priority),F(g.LINK,o.priority),F(g.SCRIPT,i.priority))},toString:function(){return M(g.META,a.priority,r)+" "+M(g.LINK,o.priority,r)+" "+M(g.SCRIPT,i.priority,r)}},metaTags:a.default,linkTags:o.default,scriptTags:i.default}}(e);f=m.priorityMethods,u=m.linkTags,d=m.metaTags,p=m.scriptTags}return{priority:f,base:M(g.BASE,t,r),bodyAttributes:M("bodyAttributes",n,r),htmlAttributes:M("htmlAttributes",a,r),link:M(g.LINK,u,r),meta:M(g.META,d,r),noscript:M(g.NOSCRIPT,o,r),script:M(g.SCRIPT,p,r),style:M(g.STYLE,i,r),title:M(g.TITLE,{title:l,titleAttributes:c},r)}},B=[],$=function(e,t){var n=this;void 0===t&&(t="undefined"!=typeof document),this.instances=[],this.value={setHelmet:function(e){n.context.helmet=e},helmetInstances:{get:function(){return n.canUseDOM?B:n.instances},add:function(e){(n.canUseDOM?B:n.instances).push(e)},remove:function(e){var t=(n.canUseDOM?B:n.instances).indexOf(e);(n.canUseDOM?B:n.instances).splice(t,1)}}},this.context=e,this.canUseDOM=t,t||(e.helmet=z({baseTag:[],bodyAttributes:{},encodeSpecialCharacters:!0,htmlAttributes:{},linkTags:[],metaTags:[],noscriptTags:[],scriptTags:[],styleTags:[],title:"",titleAttributes:{}}))},q=r.createContext({}),U=o().shape({setHelmet:o().func,helmetInstances:o().shape({get:o().func,add:o().func,remove:o().func})}),H="undefined"!=typeof document,G=function(e){function t(n){var r;return(r=e.call(this,n)||this).helmetData=new $(r.props.context,t.canUseDOM),r}return f(t,e),t.prototype.render=function(){return r.createElement(q.Provider,{value:this.helmetData.value},this.props.children)},t}(r.Component);G.canUseDOM=H,G.propTypes={context:o().shape({helmet:o().shape()}),children:o().node.isRequired},G.defaultProps={context:{}},G.displayName="HelmetProvider";var V=function(e,t){var n,r=document.head||document.querySelector(g.HEAD),a=r.querySelectorAll(e+"[data-rh]"),o=[].slice.call(a),i=[];return t&&t.length&&t.forEach((function(t){var r=document.createElement(e);for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&("innerHTML"===a?r.innerHTML=t.innerHTML:"cssText"===a?r.styleSheet?r.styleSheet.cssText=t.cssText:r.appendChild(document.createTextNode(t.cssText)):r.setAttribute(a,void 0===t[a]?"":t[a]));r.setAttribute("data-rh","true"),o.some((function(e,t){return n=t,r.isEqualNode(e)}))?o.splice(n,1):i.push(r)})),o.forEach((function(e){return e.parentNode.removeChild(e)})),i.forEach((function(e){return r.appendChild(e)})),{oldTags:o,newTags:i}},W=function(e,t){var n=document.getElementsByTagName(e)[0];if(n){for(var r=n.getAttribute("data-rh"),a=r?r.split(","):[],o=[].concat(a),i=Object.keys(t),s=0;s<i.length;s+=1){var l=i[s],c=t[l]||"";n.getAttribute(l)!==c&&n.setAttribute(l,c),-1===a.indexOf(l)&&a.push(l);var u=o.indexOf(l);-1!==u&&o.splice(u,1)}for(var d=o.length-1;d>=0;d-=1)n.removeAttribute(o[d]);a.length===o.length?n.removeAttribute("data-rh"):n.getAttribute("data-rh")!==i.join(",")&&n.setAttribute("data-rh",i.join(","))}},Q=function(e,t){var n=e.baseTag,r=e.htmlAttributes,a=e.linkTags,o=e.metaTags,i=e.noscriptTags,s=e.onChangeClientState,l=e.scriptTags,c=e.styleTags,u=e.title,d=e.titleAttributes;W(g.BODY,e.bodyAttributes),W(g.HTML,r),function(e,t){void 0!==e&&document.title!==e&&(document.title=N(e)),W(g.TITLE,t)}(u,d);var p={baseTag:V(g.BASE,n),linkTags:V(g.LINK,a),metaTags:V(g.META,o),noscriptTags:V(g.NOSCRIPT,i),scriptTags:V(g.SCRIPT,l),styleTags:V(g.STYLE,c)},f={},m={};Object.keys(p).forEach((function(e){var t=p[e],n=t.newTags,r=t.oldTags;n.length&&(f[e]=n),r.length&&(m[e]=p[e].oldTags)})),t&&t(),s(e,f,m)},K=null,Y=function(e){function t(){for(var t,n=arguments.length,r=new Array(n),a=0;a<n;a++)r[a]=arguments[a];return(t=e.call.apply(e,[this].concat(r))||this).rendered=!1,t}f(t,e);var n=t.prototype;return n.shouldComponentUpdate=function(e){return!d()(e,this.props)},n.componentDidUpdate=function(){this.emitChange()},n.componentWillUnmount=function(){this.props.context.helmetInstances.remove(this),this.emitChange()},n.emitChange=function(){var e,t,n=this.props.context,r=n.setHelmet,a=null,o=(e=n.helmetInstances.get().map((function(e){var t=p({},e.props);return delete t.context,t})),{baseTag:A(["href"],e),bodyAttributes:C("bodyAttributes",e),defer:S(e,"defer"),encode:S(e,"encodeSpecialCharacters"),htmlAttributes:C("htmlAttributes",e),linkTags:T(g.LINK,["rel","href"],e),metaTags:T(g.META,["name","charset","http-equiv","property","itemprop"],e),noscriptTags:T(g.NOSCRIPT,["innerHTML"],e),onChangeClientState:E(e),scriptTags:T(g.SCRIPT,["src","innerHTML"],e),styleTags:T(g.STYLE,["cssText"],e),title:_(e),titleAttributes:C("titleAttributes",e),prioritizeSeoTags:j(e,"prioritizeSeoTags")});G.canUseDOM?(t=o,K&&cancelAnimationFrame(K),t.defer?K=requestAnimationFrame((function(){Q(t,(function(){K=null}))})):(Q(t),K=null)):z&&(a=z(o)),r(a)},n.init=function(){this.rendered||(this.rendered=!0,this.props.context.helmetInstances.add(this),this.emitChange())},n.render=function(){return this.init(),null},t}(r.Component);Y.propTypes={context:U.isRequired},Y.displayName="HelmetDispatcher";var Z=["children"],X=["children"],J=function(e){function t(){return e.apply(this,arguments)||this}f(t,e);var n=t.prototype;return n.shouldComponentUpdate=function(e){return!s()(R(this.props,"helmetData"),R(e,"helmetData"))},n.mapNestedChildrenToProps=function(e,t){if(!t)return null;switch(e.type){case g.SCRIPT:case g.NOSCRIPT:return{innerHTML:t};case g.STYLE:return{cssText:t};default:throw new Error("<"+e.type+" /> elements are self-closing and can not contain children. Refer to our API for more information.")}},n.flattenArrayTypeChildren=function(e){var t,n=e.child,r=e.arrayTypeChildren;return p({},r,((t={})[n.type]=[].concat(r[n.type]||[],[p({},e.newChildProps,this.mapNestedChildrenToProps(n,e.nestedChildren))]),t))},n.mapObjectTypeChildren=function(e){var t,n,r=e.child,a=e.newProps,o=e.newChildProps,i=e.nestedChildren;switch(r.type){case g.TITLE:return p({},a,((t={})[r.type]=i,t.titleAttributes=p({},o),t));case g.BODY:return p({},a,{bodyAttributes:p({},o)});case g.HTML:return p({},a,{htmlAttributes:p({},o)});default:return p({},a,((n={})[r.type]=p({},o),n))}},n.mapArrayTypeChildrenToProps=function(e,t){var n=p({},t);return Object.keys(e).forEach((function(t){var r;n=p({},n,((r={})[t]=e[t],r))})),n},n.warnOnInvalidChildren=function(e,t){return c()(w.some((function(t){return e.type===t})),"function"==typeof e.type?"You may be attempting to nest <Helmet> components within each other, which is not allowed. Refer to our API for more information.":"Only elements types "+w.join(", ")+" are allowed. Helmet does not support rendering <"+e.type+"> elements. Refer to our API for more information."),c()(!t||"string"==typeof t||Array.isArray(t)&&!t.some((function(e){return"string"!=typeof e})),"Helmet expects a string as a child of <"+e.type+">. Did you forget to wrap your children in braces? ( <"+e.type+">{``}</"+e.type+"> ) Refer to our API for more information."),!0},n.mapChildrenToProps=function(e,t){var n=this,a={};return r.Children.forEach(e,(function(e){if(e&&e.props){var r=e.props,o=r.children,i=h(r,Z),s=Object.keys(i).reduce((function(e,t){return e[k[t]||t]=i[t],e}),{}),l=e.type;switch("symbol"==typeof l?l=l.toString():n.warnOnInvalidChildren(e,o),l){case g.FRAGMENT:t=n.mapChildrenToProps(o,t);break;case g.LINK:case g.META:case g.NOSCRIPT:case g.SCRIPT:case g.STYLE:a=n.flattenArrayTypeChildren({child:e,arrayTypeChildren:a,newChildProps:s,nestedChildren:o});break;default:t=n.mapObjectTypeChildren({child:e,newProps:t,newChildProps:s,nestedChildren:o})}}})),this.mapArrayTypeChildrenToProps(a,t)},n.render=function(){var e=this.props,t=e.children,n=h(e,X),a=p({},n),o=n.helmetData;return t&&(a=this.mapChildrenToProps(t,a)),!o||o instanceof $||(o=new $(o.context,o.instances)),o?r.createElement(Y,p({},a,{context:o.value,helmetData:void 0})):r.createElement(q.Consumer,null,(function(e){return r.createElement(Y,p({},a,{context:e}))}))},t}(r.Component);J.propTypes={base:o().object,bodyAttributes:o().object,children:o().oneOfType([o().arrayOf(o().node),o().node]),defaultTitle:o().string,defer:o().bool,encodeSpecialCharacters:o().bool,htmlAttributes:o().object,link:o().arrayOf(o().object),meta:o().arrayOf(o().object),noscript:o().arrayOf(o().object),onChangeClientState:o().func,script:o().arrayOf(o().object),style:o().arrayOf(o().object),title:o().string,titleAttributes:o().object,titleTemplate:o().string,prioritizeSeoTags:o().bool,helmetData:o().object},J.defaultProps={defer:!0,encodeSpecialCharacters:!0,prioritizeSeoTags:!1},J.displayName="Helmet"},2799:(e,t)=>{"use strict";var n="function"==typeof Symbol&&Symbol.for,r=n?Symbol.for("react.element"):60103,a=n?Symbol.for("react.portal"):60106,o=n?Symbol.for("react.fragment"):60107,i=n?Symbol.for("react.strict_mode"):60108,s=n?Symbol.for("react.profiler"):60114,l=n?Symbol.for("react.provider"):60109,c=n?Symbol.for("react.context"):60110,u=n?Symbol.for("react.async_mode"):60111,d=n?Symbol.for("react.concurrent_mode"):60111,p=n?Symbol.for("react.forward_ref"):60112,f=n?Symbol.for("react.suspense"):60113,m=n?Symbol.for("react.suspense_list"):60120,h=n?Symbol.for("react.memo"):60115,g=n?Symbol.for("react.lazy"):60116,b=n?Symbol.for("react.block"):60121,v=n?Symbol.for("react.fundamental"):60117,y=n?Symbol.for("react.responder"):60118,w=n?Symbol.for("react.scope"):60119;function x(e){if("object"==typeof e&&null!==e){var t=e.$$typeof;switch(t){case r:switch(e=e.type){case u:case d:case o:case s:case i:case f:return e;default:switch(e=e&&e.$$typeof){case c:case p:case g:case h:case l:return e;default:return t}}case a:return t}}}function k(e){return x(e)===d}t.AsyncMode=u,t.ConcurrentMode=d,t.ContextConsumer=c,t.ContextProvider=l,t.Element=r,t.ForwardRef=p,t.Fragment=o,t.Lazy=g,t.Memo=h,t.Portal=a,t.Profiler=s,t.StrictMode=i,t.Suspense=f,t.isAsyncMode=function(e){return k(e)||x(e)===u},t.isConcurrentMode=k,t.isContextConsumer=function(e){return x(e)===c},t.isContextProvider=function(e){return x(e)===l},t.isElement=function(e){return"object"==typeof e&&null!==e&&e.$$typeof===r},t.isForwardRef=function(e){return x(e)===p},t.isFragment=function(e){return x(e)===o},t.isLazy=function(e){return x(e)===g},t.isMemo=function(e){return x(e)===h},t.isPortal=function(e){return x(e)===a},t.isProfiler=function(e){return x(e)===s},t.isStrictMode=function(e){return x(e)===i},t.isSuspense=function(e){return x(e)===f},t.isValidElementType=function(e){return"string"==typeof e||"function"==typeof e||e===o||e===d||e===s||e===i||e===f||e===m||"object"==typeof e&&null!==e&&(e.$$typeof===g||e.$$typeof===h||e.$$typeof===l||e.$$typeof===c||e.$$typeof===p||e.$$typeof===v||e.$$typeof===y||e.$$typeof===w||e.$$typeof===b)},t.typeOf=x},4363:(e,t,n)=>{"use strict";e.exports=n(2799)},3259:(e,t,n)=>{"use strict";function r(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,e.__proto__=t}function a(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(){return i=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},i.apply(this,arguments)}var s=n(6540),l=n(5556),c=[],u=[];function d(e){var t=e(),n={loading:!0,loaded:null,error:null};return n.promise=t.then((function(e){return n.loading=!1,n.loaded=e,e})).catch((function(e){throw n.loading=!1,n.error=e,e})),n}function p(e){var t={loading:!1,loaded:{},error:null},n=[];try{Object.keys(e).forEach((function(r){var a=d(e[r]);a.loading?t.loading=!0:(t.loaded[r]=a.loaded,t.error=a.error),n.push(a.promise),a.promise.then((function(e){t.loaded[r]=e})).catch((function(e){t.error=e}))}))}catch(r){t.error=r}return t.promise=Promise.all(n).then((function(e){return t.loading=!1,e})).catch((function(e){throw t.loading=!1,e})),t}function f(e,t){return s.createElement((n=e)&&n.__esModule?n.default:n,t);var n}function m(e,t){var d,p;if(!t.loading)throw new Error("react-loadable requires a `loading` component");var m=i({loader:null,loading:null,delay:200,timeout:null,render:f,webpack:null,modules:null},t),h=null;function g(){return h||(h=e(m.loader)),h.promise}return c.push(g),"function"==typeof m.webpack&&u.push((function(){if((0,m.webpack)().every((function(e){return void 0!==e&&void 0!==n.m[e]})))return g()})),p=d=function(t){function n(n){var r;return o(a(a(r=t.call(this,n)||this)),"retry",(function(){r.setState({error:null,loading:!0,timedOut:!1}),h=e(m.loader),r._loadModule()})),g(),r.state={error:h.error,pastDelay:!1,timedOut:!1,loading:h.loading,loaded:h.loaded},r}r(n,t),n.preload=function(){return g()};var i=n.prototype;return i.UNSAFE_componentWillMount=function(){this._loadModule()},i.componentDidMount=function(){this._mounted=!0},i._loadModule=function(){var e=this;if(this.context.loadable&&Array.isArray(m.modules)&&m.modules.forEach((function(t){e.context.loadable.report(t)})),h.loading){var t=function(t){e._mounted&&e.setState(t)};"number"==typeof m.delay&&(0===m.delay?this.setState({pastDelay:!0}):this._delay=setTimeout((function(){t({pastDelay:!0})}),m.delay)),"number"==typeof m.timeout&&(this._timeout=setTimeout((function(){t({timedOut:!0})}),m.timeout));var n=function(){t({error:h.error,loaded:h.loaded,loading:h.loading}),e._clearTimeouts()};h.promise.then((function(){return n(),null})).catch((function(e){return n(),null}))}},i.componentWillUnmount=function(){this._mounted=!1,this._clearTimeouts()},i._clearTimeouts=function(){clearTimeout(this._delay),clearTimeout(this._timeout)},i.render=function(){return this.state.loading||this.state.error?s.createElement(m.loading,{isLoading:this.state.loading,pastDelay:this.state.pastDelay,timedOut:this.state.timedOut,error:this.state.error,retry:this.retry}):this.state.loaded?m.render(this.state.loaded,this.props):null},n}(s.Component),o(d,"contextTypes",{loadable:l.shape({report:l.func.isRequired})}),p}function h(e){return m(d,e)}h.Map=function(e){if("function"!=typeof e.render)throw new Error("LoadableMap requires a `render(loaded, props)` function");return m(p,e)};var g=function(e){function t(){return e.apply(this,arguments)||this}r(t,e);var n=t.prototype;return n.getChildContext=function(){return{loadable:{report:this.props.report}}},n.render=function(){return s.Children.only(this.props.children)},t}(s.Component);function b(e){for(var t=[];e.length;){var n=e.pop();t.push(n())}return Promise.all(t).then((function(){if(e.length)return b(e)}))}o(g,"propTypes",{report:l.func.isRequired}),o(g,"childContextTypes",{loadable:l.shape({report:l.func.isRequired}).isRequired}),h.Capture=g,h.preloadAll=function(){return new Promise((function(e,t){b(c).then(e,t)}))},h.preloadReady=function(){return new Promise((function(e,t){b(u).then(e,e)}))},e.exports=h},2831:(e,t,n)=>{"use strict";n.d(t,{u:()=>i,v:()=>s});var r=n(6347),a=n(8168),o=n(6540);function i(e,t,n){return void 0===n&&(n=[]),e.some((function(e){var a=e.path?(0,r.B6)(t,e):n.length?n[n.length-1].match:r.Ix.computeRootMatch(t);return a&&(n.push({route:e,match:a}),e.routes&&i(e.routes,t,n)),a})),n}function s(e,t,n){return void 0===t&&(t={}),void 0===n&&(n={}),e?o.createElement(r.dO,n,e.map((function(e,n){return o.createElement(r.qh,{key:e.key||n,path:e.path,exact:e.exact,strict:e.strict,render:function(n){return e.render?e.render((0,a.A)({},n,{},t,{route:e})):o.createElement(e.component,(0,a.A)({},n,t,{route:e}))}})}))):null}},4625:(e,t,n)=>{"use strict";n.d(t,{Kd:()=>u,N_:()=>g,k2:()=>y});var r=n(6347),a=n(2892),o=n(6540),i=n(1513),s=n(8168),l=n(8587),c=n(1561),u=function(e){function t(){for(var t,n=arguments.length,r=new Array(n),a=0;a<n;a++)r[a]=arguments[a];return(t=e.call.apply(e,[this].concat(r))||this).history=(0,i.zR)(t.props),t}return(0,a.A)(t,e),t.prototype.render=function(){return o.createElement(r.Ix,{history:this.history,children:this.props.children})},t}(o.Component);o.Component;var d=function(e,t){return"function"==typeof e?e(t):e},p=function(e,t){return"string"==typeof e?(0,i.yJ)(e,null,null,t):e},f=function(e){return e},m=o.forwardRef;void 0===m&&(m=f);var h=m((function(e,t){var n=e.innerRef,r=e.navigate,a=e.onClick,i=(0,l.A)(e,["innerRef","navigate","onClick"]),c=i.target,u=(0,s.A)({},i,{onClick:function(e){try{a&&a(e)}catch(t){throw e.preventDefault(),t}e.defaultPrevented||0!==e.button||c&&"_self"!==c||function(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}(e)||(e.preventDefault(),r())}});return u.ref=f!==m&&t||n,o.createElement("a",u)}));var g=m((function(e,t){var n=e.component,a=void 0===n?h:n,u=e.replace,g=e.to,b=e.innerRef,v=(0,l.A)(e,["component","replace","to","innerRef"]);return o.createElement(r.XZ.Consumer,null,(function(e){e||(0,c.A)(!1);var n=e.history,r=p(d(g,e.location),e.location),l=r?n.createHref(r):"",h=(0,s.A)({},v,{href:l,navigate:function(){var t=d(g,e.location),r=(0,i.AO)(e.location)===(0,i.AO)(p(t));(u||r?n.replace:n.push)(t)}});return f!==m?h.ref=t||b:h.innerRef=b,o.createElement(a,h)}))})),b=function(e){return e},v=o.forwardRef;void 0===v&&(v=b);var y=v((function(e,t){var n=e["aria-current"],a=void 0===n?"page":n,i=e.activeClassName,u=void 0===i?"active":i,f=e.activeStyle,m=e.className,h=e.exact,y=e.isActive,w=e.location,x=e.sensitive,k=e.strict,S=e.style,_=e.to,E=e.innerRef,C=(0,l.A)(e,["aria-current","activeClassName","activeStyle","className","exact","isActive","location","sensitive","strict","style","to","innerRef"]);return o.createElement(r.XZ.Consumer,null,(function(e){e||(0,c.A)(!1);var n=w||e.location,i=p(d(_,n),n),l=i.pathname,A=l&&l.replace(/([.+*?=^!:${}()[\]|/\\])/g,"\\$1"),T=A?(0,r.B6)(n.pathname,{path:A,exact:h,sensitive:x,strict:k}):null,j=!!(y?y(T,n):T),N="function"==typeof m?m(j):m,L="function"==typeof S?S(j):S;j&&(N=function(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return t.filter((function(e){return e})).join(" ")}(N,u),L=(0,s.A)({},L,f));var R=(0,s.A)({"aria-current":j&&a||null,className:N,style:L,to:i},C);return b!==v?R.ref=t||E:R.innerRef=E,o.createElement(g,R)}))}))},6347:(e,t,n)=>{"use strict";n.d(t,{B6:()=>S,Ix:()=>y,W6:()=>R,XZ:()=>v,dO:()=>N,qh:()=>_,zy:()=>P});var r=n(2892),a=n(6540),o=n(5556),i=n.n(o),s=n(1513),l=n(1561),c=n(8168),u=n(8505),d=n.n(u),p=(n(4363),n(8587)),f=(n(4146),1073741823),m="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:void 0!==n.g?n.g:{};var h=a.createContext||function(e,t){var n,o,s="__create-react-context-"+function(){var e="__global_unique_id__";return m[e]=(m[e]||0)+1}()+"__",l=function(e){function n(){for(var t,n,r,a=arguments.length,o=new Array(a),i=0;i<a;i++)o[i]=arguments[i];return(t=e.call.apply(e,[this].concat(o))||this).emitter=(n=t.props.value,r=[],{on:function(e){r.push(e)},off:function(e){r=r.filter((function(t){return t!==e}))},get:function(){return n},set:function(e,t){n=e,r.forEach((function(e){return e(n,t)}))}}),t}(0,r.A)(n,e);var a=n.prototype;return a.getChildContext=function(){var e;return(e={})[s]=this.emitter,e},a.componentWillReceiveProps=function(e){if(this.props.value!==e.value){var n,r=this.props.value,a=e.value;((o=r)===(i=a)?0!==o||1/o==1/i:o!=o&&i!=i)?n=0:(n="function"==typeof t?t(r,a):f,0!==(n|=0)&&this.emitter.set(e.value,n))}var o,i},a.render=function(){return this.props.children},n}(a.Component);l.childContextTypes=((n={})[s]=i().object.isRequired,n);var c=function(t){function n(){for(var e,n=arguments.length,r=new Array(n),a=0;a<n;a++)r[a]=arguments[a];return(e=t.call.apply(t,[this].concat(r))||this).observedBits=void 0,e.state={value:e.getValue()},e.onUpdate=function(t,n){(0|e.observedBits)&n&&e.setState({value:e.getValue()})},e}(0,r.A)(n,t);var a=n.prototype;return a.componentWillReceiveProps=function(e){var t=e.observedBits;this.observedBits=null==t?f:t},a.componentDidMount=function(){this.context[s]&&this.context[s].on(this.onUpdate);var e=this.props.observedBits;this.observedBits=null==e?f:e},a.componentWillUnmount=function(){this.context[s]&&this.context[s].off(this.onUpdate)},a.getValue=function(){return this.context[s]?this.context[s].get():e},a.render=function(){return(e=this.props.children,Array.isArray(e)?e[0]:e)(this.state.value);var e},n}(a.Component);return c.contextTypes=((o={})[s]=i().object,o),{Provider:l,Consumer:c}},g=function(e){var t=h();return t.displayName=e,t},b=g("Router-History"),v=g("Router"),y=function(e){function t(t){var n;return(n=e.call(this,t)||this).state={location:t.history.location},n._isMounted=!1,n._pendingLocation=null,t.staticContext||(n.unlisten=t.history.listen((function(e){n._pendingLocation=e}))),n}(0,r.A)(t,e),t.computeRootMatch=function(e){return{path:"/",url:"/",params:{},isExact:"/"===e}};var n=t.prototype;return n.componentDidMount=function(){var e=this;this._isMounted=!0,this.unlisten&&this.unlisten(),this.props.staticContext||(this.unlisten=this.props.history.listen((function(t){e._isMounted&&e.setState({location:t})}))),this._pendingLocation&&this.setState({location:this._pendingLocation})},n.componentWillUnmount=function(){this.unlisten&&(this.unlisten(),this._isMounted=!1,this._pendingLocation=null)},n.render=function(){return a.createElement(v.Provider,{value:{history:this.props.history,location:this.state.location,match:t.computeRootMatch(this.state.location.pathname),staticContext:this.props.staticContext}},a.createElement(b.Provider,{children:this.props.children||null,value:this.props.history}))},t}(a.Component);a.Component;a.Component;var w={},x=1e4,k=0;function S(e,t){void 0===t&&(t={}),("string"==typeof t||Array.isArray(t))&&(t={path:t});var n=t,r=n.path,a=n.exact,o=void 0!==a&&a,i=n.strict,s=void 0!==i&&i,l=n.sensitive,c=void 0!==l&&l;return[].concat(r).reduce((function(t,n){if(!n&&""!==n)return null;if(t)return t;var r=function(e,t){var n=""+t.end+t.strict+t.sensitive,r=w[n]||(w[n]={});if(r[e])return r[e];var a=[],o={regexp:d()(e,a,t),keys:a};return k<x&&(r[e]=o,k++),o}(n,{end:o,strict:s,sensitive:c}),a=r.regexp,i=r.keys,l=a.exec(e);if(!l)return null;var u=l[0],p=l.slice(1),f=e===u;return o&&!f?null:{path:n,url:"/"===n&&""===u?"/":u,isExact:f,params:i.reduce((function(e,t,n){return e[t.name]=p[n],e}),{})}}),null)}var _=function(e){function t(){return e.apply(this,arguments)||this}return(0,r.A)(t,e),t.prototype.render=function(){var e=this;return a.createElement(v.Consumer,null,(function(t){t||(0,l.A)(!1);var n=e.props.location||t.location,r=e.props.computedMatch?e.props.computedMatch:e.props.path?S(n.pathname,e.props):t.match,o=(0,c.A)({},t,{location:n,match:r}),i=e.props,s=i.children,u=i.component,d=i.render;return Array.isArray(s)&&function(e){return 0===a.Children.count(e)}(s)&&(s=null),a.createElement(v.Provider,{value:o},o.match?s?"function"==typeof s?s(o):s:u?a.createElement(u,o):d?d(o):null:"function"==typeof s?s(o):null)}))},t}(a.Component);function E(e){return"/"===e.charAt(0)?e:"/"+e}function C(e,t){if(!e)return t;var n=E(e);return 0!==t.pathname.indexOf(n)?t:(0,c.A)({},t,{pathname:t.pathname.substr(n.length)})}function A(e){return"string"==typeof e?e:(0,s.AO)(e)}function T(e){return function(){(0,l.A)(!1)}}function j(){}a.Component;var N=function(e){function t(){return e.apply(this,arguments)||this}return(0,r.A)(t,e),t.prototype.render=function(){var e=this;return a.createElement(v.Consumer,null,(function(t){t||(0,l.A)(!1);var n,r,o=e.props.location||t.location;return a.Children.forEach(e.props.children,(function(e){if(null==r&&a.isValidElement(e)){n=e;var i=e.props.path||e.props.from;r=i?S(o.pathname,(0,c.A)({},e.props,{path:i})):t.match}})),r?a.cloneElement(n,{location:o,computedMatch:r}):null}))},t}(a.Component);var L=a.useContext;function R(){return L(b)}function P(){return L(v).location}},8505:(e,t,n)=>{var r=n(4634);e.exports=f,e.exports.parse=o,e.exports.compile=function(e,t){return s(o(e,t),t)},e.exports.tokensToFunction=s,e.exports.tokensToRegExp=p;var a=new RegExp(["(\\\\.)","([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))"].join("|"),"g");function o(e,t){for(var n,r=[],o=0,i=0,s="",u=t&&t.delimiter||"/";null!=(n=a.exec(e));){var d=n[0],p=n[1],f=n.index;if(s+=e.slice(i,f),i=f+d.length,p)s+=p[1];else{var m=e[i],h=n[2],g=n[3],b=n[4],v=n[5],y=n[6],w=n[7];s&&(r.push(s),s="");var x=null!=h&&null!=m&&m!==h,k="+"===y||"*"===y,S="?"===y||"*"===y,_=n[2]||u,E=b||v;r.push({name:g||o++,prefix:h||"",delimiter:_,optional:S,repeat:k,partial:x,asterisk:!!w,pattern:E?c(E):w?".*":"[^"+l(_)+"]+?"})}}return i<e.length&&(s+=e.substr(i)),s&&r.push(s),r}function i(e){return encodeURI(e).replace(/[\/?#]/g,(function(e){return"%"+e.charCodeAt(0).toString(16).toUpperCase()}))}function s(e,t){for(var n=new Array(e.length),a=0;a<e.length;a++)"object"==typeof e[a]&&(n[a]=new RegExp("^(?:"+e[a].pattern+")$",d(t)));return function(t,a){for(var o="",s=t||{},l=(a||{}).pretty?i:encodeURIComponent,c=0;c<e.length;c++){var u=e[c];if("string"!=typeof u){var d,p=s[u.name];if(null==p){if(u.optional){u.partial&&(o+=u.prefix);continue}throw new TypeError('Expected "'+u.name+'" to be defined')}if(r(p)){if(!u.repeat)throw new TypeError('Expected "'+u.name+'" to not repeat, but received `'+JSON.stringify(p)+"`");if(0===p.length){if(u.optional)continue;throw new TypeError('Expected "'+u.name+'" to not be empty')}for(var f=0;f<p.length;f++){if(d=l(p[f]),!n[c].test(d))throw new TypeError('Expected all "'+u.name+'" to match "'+u.pattern+'", but received `'+JSON.stringify(d)+"`");o+=(0===f?u.prefix:u.delimiter)+d}}else{if(d=u.asterisk?encodeURI(p).replace(/[?#]/g,(function(e){return"%"+e.charCodeAt(0).toString(16).toUpperCase()})):l(p),!n[c].test(d))throw new TypeError('Expected "'+u.name+'" to match "'+u.pattern+'", but received "'+d+'"');o+=u.prefix+d}}else o+=u}return o}}function l(e){return e.replace(/([.+*?=^!:${}()[\]|\/\\])/g,"\\$1")}function c(e){return e.replace(/([=!:$\/()])/g,"\\$1")}function u(e,t){return e.keys=t,e}function d(e){return e&&e.sensitive?"":"i"}function p(e,t,n){r(t)||(n=t||n,t=[]);for(var a=(n=n||{}).strict,o=!1!==n.end,i="",s=0;s<e.length;s++){var c=e[s];if("string"==typeof c)i+=l(c);else{var p=l(c.prefix),f="(?:"+c.pattern+")";t.push(c),c.repeat&&(f+="(?:"+p+f+")*"),i+=f=c.optional?c.partial?p+"("+f+")?":"(?:"+p+"("+f+"))?":p+"("+f+")"}}var m=l(n.delimiter||"/"),h=i.slice(-m.length)===m;return a||(i=(h?i.slice(0,-m.length):i)+"(?:"+m+"(?=$))?"),i+=o?"$":a&&h?"":"(?="+m+"|$)",u(new RegExp("^"+i,d(n)),t)}function f(e,t,n){return r(t)||(n=t||n,t=[]),n=n||{},e instanceof RegExp?function(e,t){var n=e.source.match(/\((?!\?)/g);if(n)for(var r=0;r<n.length;r++)t.push({name:r,prefix:null,delimiter:null,optional:!1,repeat:!1,partial:!1,asterisk:!1,pattern:null});return u(e,t)}(e,t):r(e)?function(e,t,n){for(var r=[],a=0;a<e.length;a++)r.push(f(e[a],t,n).source);return u(new RegExp("(?:"+r.join("|")+")",d(n)),t)}(e,t,n):function(e,t,n){return p(o(e,n),t,n)}(e,t,n)}},1020:(e,t,n)=>{"use strict";var r=n(6540),a=Symbol.for("react.element"),o=Symbol.for("react.fragment"),i=Object.prototype.hasOwnProperty,s=r.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,l={key:!0,ref:!0,__self:!0,__source:!0};function c(e,t,n){var r,o={},c=null,u=null;for(r in void 0!==n&&(c=""+n),void 0!==t.key&&(c=""+t.key),void 0!==t.ref&&(u=t.ref),t)i.call(t,r)&&!l.hasOwnProperty(r)&&(o[r]=t[r]);if(e&&e.defaultProps)for(r in t=e.defaultProps)void 0===o[r]&&(o[r]=t[r]);return{$$typeof:a,type:e,key:c,ref:u,props:o,_owner:s.current}}t.Fragment=o,t.jsx=c,t.jsxs=c},5287:(e,t)=>{"use strict";var n=Symbol.for("react.element"),r=Symbol.for("react.portal"),a=Symbol.for("react.fragment"),o=Symbol.for("react.strict_mode"),i=Symbol.for("react.profiler"),s=Symbol.for("react.provider"),l=Symbol.for("react.context"),c=Symbol.for("react.forward_ref"),u=Symbol.for("react.suspense"),d=Symbol.for("react.memo"),p=Symbol.for("react.lazy"),f=Symbol.iterator;var m={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},h=Object.assign,g={};function b(e,t,n){this.props=e,this.context=t,this.refs=g,this.updater=n||m}function v(){}function y(e,t,n){this.props=e,this.context=t,this.refs=g,this.updater=n||m}b.prototype.isReactComponent={},b.prototype.setState=function(e,t){if("object"!=typeof e&&"function"!=typeof e&&null!=e)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")},b.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")},v.prototype=b.prototype;var w=y.prototype=new v;w.constructor=y,h(w,b.prototype),w.isPureReactComponent=!0;var x=Array.isArray,k=Object.prototype.hasOwnProperty,S={current:null},_={key:!0,ref:!0,__self:!0,__source:!0};function E(e,t,r){var a,o={},i=null,s=null;if(null!=t)for(a in void 0!==t.ref&&(s=t.ref),void 0!==t.key&&(i=""+t.key),t)k.call(t,a)&&!_.hasOwnProperty(a)&&(o[a]=t[a]);var l=arguments.length-2;if(1===l)o.children=r;else if(1<l){for(var c=Array(l),u=0;u<l;u++)c[u]=arguments[u+2];o.children=c}if(e&&e.defaultProps)for(a in l=e.defaultProps)void 0===o[a]&&(o[a]=l[a]);return{$$typeof:n,type:e,key:i,ref:s,props:o,_owner:S.current}}function C(e){return"object"==typeof e&&null!==e&&e.$$typeof===n}var A=/\/+/g;function T(e,t){return"object"==typeof e&&null!==e&&null!=e.key?function(e){var t={"=":"=0",":":"=2"};return"$"+e.replace(/[=:]/g,(function(e){return t[e]}))}(""+e.key):t.toString(36)}function j(e,t,a,o,i){var s=typeof e;"undefined"!==s&&"boolean"!==s||(e=null);var l=!1;if(null===e)l=!0;else switch(s){case"string":case"number":l=!0;break;case"object":switch(e.$$typeof){case n:case r:l=!0}}if(l)return i=i(l=e),e=""===o?"."+T(l,0):o,x(i)?(a="",null!=e&&(a=e.replace(A,"$&/")+"/"),j(i,t,a,"",(function(e){return e}))):null!=i&&(C(i)&&(i=function(e,t){return{$$typeof:n,type:e.type,key:t,ref:e.ref,props:e.props,_owner:e._owner}}(i,a+(!i.key||l&&l.key===i.key?"":(""+i.key).replace(A,"$&/")+"/")+e)),t.push(i)),1;if(l=0,o=""===o?".":o+":",x(e))for(var c=0;c<e.length;c++){var u=o+T(s=e[c],c);l+=j(s,t,a,u,i)}else if(u=function(e){return null===e||"object"!=typeof e?null:"function"==typeof(e=f&&e[f]||e["@@iterator"])?e:null}(e),"function"==typeof u)for(e=u.call(e),c=0;!(s=e.next()).done;)l+=j(s=s.value,t,a,u=o+T(s,c++),i);else if("object"===s)throw t=String(e),Error("Objects are not valid as a React child (found: "+("[object Object]"===t?"object with keys {"+Object.keys(e).join(", ")+"}":t)+"). If you meant to render a collection of children, use an array instead.");return l}function N(e,t,n){if(null==e)return e;var r=[],a=0;return j(e,r,"","",(function(e){return t.call(n,e,a++)})),r}function L(e){if(-1===e._status){var t=e._result;(t=t()).then((function(t){0!==e._status&&-1!==e._status||(e._status=1,e._result=t)}),(function(t){0!==e._status&&-1!==e._status||(e._status=2,e._result=t)})),-1===e._status&&(e._status=0,e._result=t)}if(1===e._status)return e._result.default;throw e._result}var R={current:null},P={transition:null},O={ReactCurrentDispatcher:R,ReactCurrentBatchConfig:P,ReactCurrentOwner:S};t.Children={map:N,forEach:function(e,t,n){N(e,(function(){t.apply(this,arguments)}),n)},count:function(e){var t=0;return N(e,(function(){t++})),t},toArray:function(e){return N(e,(function(e){return e}))||[]},only:function(e){if(!C(e))throw Error("React.Children.only expected to receive a single React element child.");return e}},t.Component=b,t.Fragment=a,t.Profiler=i,t.PureComponent=y,t.StrictMode=o,t.Suspense=u,t.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=O,t.cloneElement=function(e,t,r){if(null==e)throw Error("React.cloneElement(...): The argument must be a React element, but you passed "+e+".");var a=h({},e.props),o=e.key,i=e.ref,s=e._owner;if(null!=t){if(void 0!==t.ref&&(i=t.ref,s=S.current),void 0!==t.key&&(o=""+t.key),e.type&&e.type.defaultProps)var l=e.type.defaultProps;for(c in t)k.call(t,c)&&!_.hasOwnProperty(c)&&(a[c]=void 0===t[c]&&void 0!==l?l[c]:t[c])}var c=arguments.length-2;if(1===c)a.children=r;else if(1<c){l=Array(c);for(var u=0;u<c;u++)l[u]=arguments[u+2];a.children=l}return{$$typeof:n,type:e.type,key:o,ref:i,props:a,_owner:s}},t.createContext=function(e){return(e={$$typeof:l,_currentValue:e,_currentValue2:e,_threadCount:0,Provider:null,Consumer:null,_defaultValue:null,_globalName:null}).Provider={$$typeof:s,_context:e},e.Consumer=e},t.createElement=E,t.createFactory=function(e){var t=E.bind(null,e);return t.type=e,t},t.createRef=function(){return{current:null}},t.forwardRef=function(e){return{$$typeof:c,render:e}},t.isValidElement=C,t.lazy=function(e){return{$$typeof:p,_payload:{_status:-1,_result:e},_init:L}},t.memo=function(e,t){return{$$typeof:d,type:e,compare:void 0===t?null:t}},t.startTransition=function(e){var t=P.transition;P.transition={};try{e()}finally{P.transition=t}},t.unstable_act=function(){throw Error("act(...) is not supported in production builds of React.")},t.useCallback=function(e,t){return R.current.useCallback(e,t)},t.useContext=function(e){return R.current.useContext(e)},t.useDebugValue=function(){},t.useDeferredValue=function(e){return R.current.useDeferredValue(e)},t.useEffect=function(e,t){return R.current.useEffect(e,t)},t.useId=function(){return R.current.useId()},t.useImperativeHandle=function(e,t,n){return R.current.useImperativeHandle(e,t,n)},t.useInsertionEffect=function(e,t){return R.current.useInsertionEffect(e,t)},t.useLayoutEffect=function(e,t){return R.current.useLayoutEffect(e,t)},t.useMemo=function(e,t){return R.current.useMemo(e,t)},t.useReducer=function(e,t,n){return R.current.useReducer(e,t,n)},t.useRef=function(e){return R.current.useRef(e)},t.useState=function(e){return R.current.useState(e)},t.useSyncExternalStore=function(e,t,n){return R.current.useSyncExternalStore(e,t,n)},t.useTransition=function(){return R.current.useTransition()},t.version="18.3.0"},6540:(e,t,n)=>{"use strict";e.exports=n(5287)},4848:(e,t,n)=>{"use strict";e.exports=n(1020)},7463:(e,t)=>{"use strict";function n(e,t){var n=e.length;e.push(t);e:for(;0<n;){var r=n-1>>>1,a=e[r];if(!(0<o(a,t)))break e;e[r]=t,e[n]=a,n=r}}function r(e){return 0===e.length?null:e[0]}function a(e){if(0===e.length)return null;var t=e[0],n=e.pop();if(n!==t){e[0]=n;e:for(var r=0,a=e.length,i=a>>>1;r<i;){var s=2*(r+1)-1,l=e[s],c=s+1,u=e[c];if(0>o(l,n))c<a&&0>o(u,l)?(e[r]=u,e[c]=n,r=c):(e[r]=l,e[s]=n,r=s);else{if(!(c<a&&0>o(u,n)))break e;e[r]=u,e[c]=n,r=c}}}return t}function o(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}if("object"==typeof performance&&"function"==typeof performance.now){var i=performance;t.unstable_now=function(){return i.now()}}else{var s=Date,l=s.now();t.unstable_now=function(){return s.now()-l}}var c=[],u=[],d=1,p=null,f=3,m=!1,h=!1,g=!1,b="function"==typeof setTimeout?setTimeout:null,v="function"==typeof clearTimeout?clearTimeout:null,y="undefined"!=typeof setImmediate?setImmediate:null;function w(e){for(var t=r(u);null!==t;){if(null===t.callback)a(u);else{if(!(t.startTime<=e))break;a(u),t.sortIndex=t.expirationTime,n(c,t)}t=r(u)}}function x(e){if(g=!1,w(e),!h)if(null!==r(c))h=!0,P(k);else{var t=r(u);null!==t&&O(x,t.startTime-e)}}function k(e,n){h=!1,g&&(g=!1,v(C),C=-1),m=!0;var o=f;try{for(w(n),p=r(c);null!==p&&(!(p.expirationTime>n)||e&&!j());){var i=p.callback;if("function"==typeof i){p.callback=null,f=p.priorityLevel;var s=i(p.expirationTime<=n);n=t.unstable_now(),"function"==typeof s?p.callback=s:p===r(c)&&a(c),w(n)}else a(c);p=r(c)}if(null!==p)var l=!0;else{var d=r(u);null!==d&&O(x,d.startTime-n),l=!1}return l}finally{p=null,f=o,m=!1}}"undefined"!=typeof navigator&&void 0!==navigator.scheduling&&void 0!==navigator.scheduling.isInputPending&&navigator.scheduling.isInputPending.bind(navigator.scheduling);var S,_=!1,E=null,C=-1,A=5,T=-1;function j(){return!(t.unstable_now()-T<A)}function N(){if(null!==E){var e=t.unstable_now();T=e;var n=!0;try{n=E(!0,e)}finally{n?S():(_=!1,E=null)}}else _=!1}if("function"==typeof y)S=function(){y(N)};else if("undefined"!=typeof MessageChannel){var L=new MessageChannel,R=L.port2;L.port1.onmessage=N,S=function(){R.postMessage(null)}}else S=function(){b(N,0)};function P(e){E=e,_||(_=!0,S())}function O(e,n){C=b((function(){e(t.unstable_now())}),n)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(e){e.callback=null},t.unstable_continueExecution=function(){h||m||(h=!0,P(k))},t.unstable_forceFrameRate=function(e){0>e||125<e?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):A=0<e?Math.floor(1e3/e):5},t.unstable_getCurrentPriorityLevel=function(){return f},t.unstable_getFirstCallbackNode=function(){return r(c)},t.unstable_next=function(e){switch(f){case 1:case 2:case 3:var t=3;break;default:t=f}var n=f;f=t;try{return e()}finally{f=n}},t.unstable_pauseExecution=function(){},t.unstable_requestPaint=function(){},t.unstable_runWithPriority=function(e,t){switch(e){case 1:case 2:case 3:case 4:case 5:break;default:e=3}var n=f;f=e;try{return t()}finally{f=n}},t.unstable_scheduleCallback=function(e,a,o){var i=t.unstable_now();switch("object"==typeof o&&null!==o?o="number"==typeof(o=o.delay)&&0<o?i+o:i:o=i,e){case 1:var s=-1;break;case 2:s=250;break;case 5:s=1073741823;break;case 4:s=1e4;break;default:s=5e3}return e={id:d++,callback:a,priorityLevel:e,startTime:o,expirationTime:s=o+s,sortIndex:-1},o>i?(e.sortIndex=o,n(u,e),null===r(c)&&e===r(u)&&(g?(v(C),C=-1):g=!0,O(x,o-i))):(e.sortIndex=s,n(c,e),h||m||(h=!0,P(k))),e},t.unstable_shouldYield=j,t.unstable_wrapCallback=function(e){var t=f;return function(){var n=f;f=t;try{return e.apply(this,arguments)}finally{f=n}}}},9982:(e,t,n)=>{"use strict";e.exports=n(7463)},2833:e=>{e.exports=function(e,t,n,r){var a=n?n.call(r,e,t):void 0;if(void 0!==a)return!!a;if(e===t)return!0;if("object"!=typeof e||!e||"object"!=typeof t||!t)return!1;var o=Object.keys(e),i=Object.keys(t);if(o.length!==i.length)return!1;for(var s=Object.prototype.hasOwnProperty.bind(t),l=0;l<o.length;l++){var c=o[l];if(!s(c))return!1;var u=e[c],d=t[c];if(!1===(a=n?n.call(r,u,d,c):void 0)||void 0===a&&u!==d)return!1}return!0}},4784:(e,t,n)=>{"use strict";n.d(t,{A:()=>r});const r={title:"Copacetic",url:"https://project-copacetic.github.io",baseUrl:"/copacetic/website/",onBrokenLinks:"ignore",onBrokenMarkdownLinks:"warn",favicon:"img/favicon.ico",trailingSlash:!1,organizationName:"project-copacetic",projectName:"copacetic",deploymentBranch:"gh-pages",i18n:{defaultLocale:"en",locales:["en"],path:"i18n",localeConfigs:{}},presets:[["classic",{docs:{sidebarPath:"/home/runner/work/copacetic/copacetic/website/sidebars.js",routeBasePath:"/"},blog:!1,theme:{customCss:"/home/runner/work/copacetic/copacetic/website/src/css/custom.css"},gtag:{trackingID:"G-3RC20QPKNS",anonymizeIP:!0}}]],themeConfig:{navbar:{title:"Copacetic",logo:{alt:"Copacetic Logo",src:"img/logo.png",href:"https://project-copacetic.github.io/copacetic/"},items:[{type:"docsVersionDropdown",position:"right",dropdownItemsBefore:[],dropdownItemsAfter:[]},{href:"https://github.com/project-copacetic/copacetic",position:"right",className:"header-github-link","aria-label":"GitHub repository"}],hideOnScroll:!1},footer:{style:"dark",copyright:'Copyright \xa9 2024 Linux Foundation. The Linux Foundation\xae (TLF) has registered trademarks and uses trademarks. For a list of TLF trademarks, see <a href="https://www.linuxfoundation.org/trademark-usage/">Trademark Usage</a>.',links:[]},prism:{theme:{plain:{color:"#393A34",backgroundColor:"#f6f8fa"},styles:[{types:["comment","prolog","doctype","cdata"],style:{color:"#999988",fontStyle:"italic"}},{types:["namespace"],style:{opacity:.7}},{types:["string","attr-value"],style:{color:"#e3116c"}},{types:["punctuation","operator"],style:{color:"#393A34"}},{types:["entity","url","symbol","number","boolean","variable","constant","property","regex","inserted"],style:{color:"#36acaa"}},{types:["atrule","keyword","attr-name","selector"],style:{color:"#00a4db"}},{types:["function","deleted","tag"],style:{color:"#d73a49"}},{types:["function-variable"],style:{color:"#6f42c1"}},{types:["tag","selector","keyword"],style:{color:"#00009f"}}]},darkTheme:{plain:{color:"#F8F8F2",backgroundColor:"#282A36"},styles:[{types:["prolog","constant","builtin"],style:{color:"rgb(189, 147, 249)"}},{types:["inserted","function"],style:{color:"rgb(80, 250, 123)"}},{types:["deleted"],style:{color:"rgb(255, 85, 85)"}},{types:["changed"],style:{color:"rgb(255, 184, 108)"}},{types:["punctuation","symbol"],style:{color:"rgb(248, 248, 242)"}},{types:["string","char","tag","selector"],style:{color:"rgb(255, 121, 198)"}},{types:["keyword","variable"],style:{color:"rgb(189, 147, 249)",fontStyle:"italic"}},{types:["comment"],style:{color:"rgb(98, 114, 164)"}},{types:["attr-name"],style:{color:"rgb(241, 250, 140)"}}]},additionalLanguages:[],magicComments:[{className:"theme-code-block-highlighted-line",line:"highlight-next-line",block:{start:"highlight-start",end:"highlight-end"}}]},colorMode:{defaultMode:"light",disableSwitch:!1,respectPrefersColorScheme:!1},docs:{versionPersistence:"localStorage",sidebar:{hideable:!1,autoCollapseCategories:!1}},metadata:[],tableOfContents:{minHeadingLevel:2,maxHeadingLevel:3}},baseUrlIssueBanner:!0,onBrokenAnchors:"warn",onDuplicateRoutes:"warn",staticDirectories:["static"],customFields:{},plugins:[],themes:[],scripts:[],headTags:[],stylesheets:[],clientModules:[],tagline:"",titleDelimiter:"|",noIndex:!1,markdown:{format:"mdx",mermaid:!1,mdx1Compat:{comments:!0,admonitions:!0,headingIds:!0}}}},8168:(e,t,n)=>{"use strict";function r(){return r=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},r.apply(this,arguments)}n.d(t,{A:()=>r})},2892:(e,t,n)=>{"use strict";function r(e,t){return r=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},r(e,t)}function a(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,r(e,t)}n.d(t,{A:()=>a})},8587:(e,t,n)=>{"use strict";function r(e,t){if(null==e)return{};var n,r,a={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(a[n]=e[n]);return a}n.d(t,{A:()=>r})},4164:(e,t,n)=>{"use strict";function r(e){var t,n,a="";if("string"==typeof e||"number"==typeof e)a+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(n=r(e[t]))&&(a&&(a+=" "),a+=n)}else for(n in e)e[n]&&(a&&(a+=" "),a+=n);return a}n.d(t,{A:()=>a});const a=function(){for(var e,t,n=0,a="",o=arguments.length;n<o;n++)(e=arguments[n])&&(t=r(e))&&(a&&(a+=" "),a+=t);return a}},1765:(e,t,n)=>{"use strict";n.d(t,{My:()=>A,f4:()=>ee});var r,a,o,i,s,l,c,u=n(6540),d=n(4164),p=Object.create,f=Object.defineProperty,m=Object.defineProperties,h=Object.getOwnPropertyDescriptor,g=Object.getOwnPropertyDescriptors,b=Object.getOwnPropertyNames,v=Object.getOwnPropertySymbols,y=Object.getPrototypeOf,w=Object.prototype.hasOwnProperty,x=Object.prototype.propertyIsEnumerable,k=(e,t,n)=>t in e?f(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,S=(e,t)=>{for(var n in t||(t={}))w.call(t,n)&&k(e,n,t[n]);if(v)for(var n of v(t))x.call(t,n)&&k(e,n,t[n]);return e},_=(e,t)=>m(e,g(t)),E=(e,t)=>{var n={};for(var r in e)w.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&v)for(var r of v(e))t.indexOf(r)<0&&x.call(e,r)&&(n[r]=e[r]);return n},C=(r={"../../node_modules/.pnpm/prismjs@1.29.0_patch_hash=vrxx3pzkik6jpmgpayxfjunetu/node_modules/prismjs/prism.js"(e,t){var n=function(){var e=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,n={},r={util:{encode:function e(t){return t instanceof a?new a(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/</g,"<").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).slice(8,-1)},objId:function(e){return e.__id||Object.defineProperty(e,"__id",{value:++t}),e.__id},clone:function e(t,n){var a,o;switch(n=n||{},r.util.type(t)){case"Object":if(o=r.util.objId(t),n[o])return n[o];for(var i in a={},n[o]=a,t)t.hasOwnProperty(i)&&(a[i]=e(t[i],n));return a;case"Array":return o=r.util.objId(t),n[o]?n[o]:(a=[],n[o]=a,t.forEach((function(t,r){a[r]=e(t,n)})),a);default:return t}},getLanguage:function(t){for(;t;){var n=e.exec(t.className);if(n)return n[1].toLowerCase();t=t.parentElement}return"none"},setLanguage:function(t,n){t.className=t.className.replace(RegExp(e,"gi"),""),t.classList.add("language-"+n)},isActive:function(e,t,n){for(var r="no-"+t;e;){var a=e.classList;if(a.contains(t))return!0;if(a.contains(r))return!1;e=e.parentElement}return!!n}},languages:{plain:n,plaintext:n,text:n,txt:n,extend:function(e,t){var n=r.util.clone(r.languages[e]);for(var a in t)n[a]=t[a];return n},insertBefore:function(e,t,n,a){var o=(a=a||r.languages)[e],i={};for(var s in o)if(o.hasOwnProperty(s)){if(s==t)for(var l in n)n.hasOwnProperty(l)&&(i[l]=n[l]);n.hasOwnProperty(s)||(i[s]=o[s])}var c=a[e];return a[e]=i,r.languages.DFS(r.languages,(function(t,n){n===c&&t!=e&&(this[t]=i)})),i},DFS:function e(t,n,a,o){o=o||{};var i=r.util.objId;for(var s in t)if(t.hasOwnProperty(s)){n.call(t,s,t[s],a||s);var l=t[s],c=r.util.type(l);"Object"!==c||o[i(l)]?"Array"!==c||o[i(l)]||(o[i(l)]=!0,e(l,n,s,o)):(o[i(l)]=!0,e(l,n,null,o))}}},plugins:{},highlight:function(e,t,n){var o={code:e,grammar:t,language:n};if(r.hooks.run("before-tokenize",o),!o.grammar)throw new Error('The language "'+o.language+'" has no grammar.');return o.tokens=r.tokenize(o.code,o.grammar),r.hooks.run("after-tokenize",o),a.stringify(r.util.encode(o.tokens),o.language)},tokenize:function(e,t){var n=t.rest;if(n){for(var r in n)t[r]=n[r];delete t.rest}var a=new s;return l(a,a.head,e),i(e,a,t,a.head,0),function(e){for(var t=[],n=e.head.next;n!==e.tail;)t.push(n.value),n=n.next;return t}(a)},hooks:{all:{},add:function(e,t){var n=r.hooks.all;n[e]=n[e]||[],n[e].push(t)},run:function(e,t){var n=r.hooks.all[e];if(n&&n.length)for(var a,o=0;a=n[o++];)a(t)}},Token:a};function a(e,t,n,r){this.type=e,this.content=t,this.alias=n,this.length=0|(r||"").length}function o(e,t,n,r){e.lastIndex=t;var a=e.exec(n);if(a&&r&&a[1]){var o=a[1].length;a.index+=o,a[0]=a[0].slice(o)}return a}function i(e,t,n,s,u,d){for(var p in n)if(n.hasOwnProperty(p)&&n[p]){var f=n[p];f=Array.isArray(f)?f:[f];for(var m=0;m<f.length;++m){if(d&&d.cause==p+","+m)return;var h=f[m],g=h.inside,b=!!h.lookbehind,v=!!h.greedy,y=h.alias;if(v&&!h.pattern.global){var w=h.pattern.toString().match(/[imsuy]*$/)[0];h.pattern=RegExp(h.pattern.source,w+"g")}for(var x=h.pattern||h,k=s.next,S=u;k!==t.tail&&!(d&&S>=d.reach);S+=k.value.length,k=k.next){var _=k.value;if(t.length>e.length)return;if(!(_ instanceof a)){var E,C=1;if(v){if(!(E=o(x,S,e,b))||E.index>=e.length)break;var A=E.index,T=E.index+E[0].length,j=S;for(j+=k.value.length;A>=j;)j+=(k=k.next).value.length;if(S=j-=k.value.length,k.value instanceof a)continue;for(var N=k;N!==t.tail&&(j<T||"string"==typeof N.value);N=N.next)C++,j+=N.value.length;C--,_=e.slice(S,j),E.index-=S}else if(!(E=o(x,0,_,b)))continue;A=E.index;var L=E[0],R=_.slice(0,A),P=_.slice(A+L.length),O=S+_.length;d&&O>d.reach&&(d.reach=O);var D=k.prev;if(R&&(D=l(t,D,R),S+=R.length),c(t,D,C),k=l(t,D,new a(p,g?r.tokenize(L,g):L,y,L)),P&&l(t,k,P),C>1){var I={cause:p+","+m,reach:O};i(e,t,n,k.prev,S,I),d&&I.reach>d.reach&&(d.reach=I.reach)}}}}}}function s(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function l(e,t,n){var r=t.next,a={value:n,prev:t,next:r};return t.next=a,r.prev=a,e.length++,a}function c(e,t,n){for(var r=t.next,a=0;a<n&&r!==e.tail;a++)r=r.next;t.next=r,r.prev=t,e.length-=a}return a.stringify=function e(t,n){if("string"==typeof t)return t;if(Array.isArray(t)){var a="";return t.forEach((function(t){a+=e(t,n)})),a}var o={type:t.type,content:e(t.content,n),tag:"span",classes:["token",t.type],attributes:{},language:n},i=t.alias;i&&(Array.isArray(i)?Array.prototype.push.apply(o.classes,i):o.classes.push(i)),r.hooks.run("wrap",o);var s="";for(var l in o.attributes)s+=" "+l+'="'+(o.attributes[l]||"").replace(/"/g,""")+'"';return"<"+o.tag+' class="'+o.classes.join(" ")+'"'+s+">"+o.content+"</"+o.tag+">"},r}();t.exports=n,n.default=n}},function(){return a||(0,r[b(r)[0]])((a={exports:{}}).exports,a),a.exports}),A=((e,t,n)=>(n=null!=e?p(y(e)):{},((e,t,n,r)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let a of b(t))w.call(e,a)||a===n||f(e,a,{get:()=>t[a],enumerable:!(r=h(t,a))||r.enumerable});return e})(!t&&e&&e.__esModule?n:f(n,"default",{value:e,enumerable:!0}),e)))(C());A.languages.markup={comment:{pattern:/<!--(?:(?!<!--)[\s\S])*?-->/,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/<!DOCTYPE(?:[^>"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|<!--(?:[^-]|-(?!->))*-->)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^<!|>$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},A.languages.markup.tag.inside["attr-value"].inside.entity=A.languages.markup.entity,A.languages.markup.doctype.inside["internal-subset"].inside=A.languages.markup,A.hooks.add("wrap",(function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))})),Object.defineProperty(A.languages.markup.tag,"addInlined",{value:function(e,t){var n;(t=((n=((n={})["language-"+t]={pattern:/(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i,lookbehind:!0,inside:A.languages[t]},n.cdata=/^<!\[CDATA\[|\]\]>$/i,{"included-cdata":{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,inside:n}}))["language-"+t]={pattern:/[\s\S]+/,inside:A.languages[t]},{}))[e]={pattern:RegExp(/(<__[^>]*>)(?:<!\[CDATA\[(?:[^\]]|\](?!\]>))*\]\]>|(?!<!\[CDATA\[)[\s\S])*?(?=<\/__>)/.source.replace(/__/g,(function(){return e})),"i"),lookbehind:!0,greedy:!0,inside:n},A.languages.insertBefore("markup","cdata",t)}}),Object.defineProperty(A.languages.markup.tag,"addAttribute",{value:function(e,t){A.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[t,"language-"+t],inside:A.languages[t]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),A.languages.html=A.languages.markup,A.languages.mathml=A.languages.markup,A.languages.svg=A.languages.markup,A.languages.xml=A.languages.extend("markup",{}),A.languages.ssml=A.languages.xml,A.languages.atom=A.languages.xml,A.languages.rss=A.languages.xml,o=A,i={pattern:/\\[\\(){}[\]^$+*?|.]/,alias:"escape"},l="(?:[^\\\\-]|"+(s=/\\(?:x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]+\}|0[0-7]{0,2}|[123][0-7]{2}|c[a-zA-Z]|.)/).source+")",l=RegExp(l+"-"+l),c={pattern:/(<|')[^<>']+(?=[>']$)/,lookbehind:!0,alias:"variable"},o.languages.regex={"char-class":{pattern:/((?:^|[^\\])(?:\\\\)*)\[(?:[^\\\]]|\\[\s\S])*\]/,lookbehind:!0,inside:{"char-class-negation":{pattern:/(^\[)\^/,lookbehind:!0,alias:"operator"},"char-class-punctuation":{pattern:/^\[|\]$/,alias:"punctuation"},range:{pattern:l,inside:{escape:s,"range-punctuation":{pattern:/-/,alias:"operator"}}},"special-escape":i,"char-set":{pattern:/\\[wsd]|\\p\{[^{}]+\}/i,alias:"class-name"},escape:s}},"special-escape":i,"char-set":{pattern:/\.|\\[wsd]|\\p\{[^{}]+\}/i,alias:"class-name"},backreference:[{pattern:/\\(?![123][0-7]{2})[1-9]/,alias:"keyword"},{pattern:/\\k<[^<>']+>/,alias:"keyword",inside:{"group-name":c}}],anchor:{pattern:/[$^]|\\[ABbGZz]/,alias:"function"},escape:s,group:[{pattern:/\((?:\?(?:<[^<>']+>|'[^<>']+'|[>:]|<?[=!]|[idmnsuxU]+(?:-[idmnsuxU]+)?:?))?/,alias:"punctuation",inside:{"group-name":c}},{pattern:/\)/,alias:"punctuation"}],quantifier:{pattern:/(?:[+*?]|\{\d+(?:,\d*)?\})[?+]?/,alias:"number"},alternation:{pattern:/\|/,alias:"keyword"}},A.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},A.languages.javascript=A.languages.extend("clike",{"class-name":[A.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),A.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,A.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp(/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source+/\//.source+"(?:"+/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/.source+"|"+/(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/.source+")"+/(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/.source),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:A.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:A.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:A.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:A.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:A.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),A.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:A.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),A.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),A.languages.markup&&(A.languages.markup.tag.addInlined("script","javascript"),A.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),A.languages.js=A.languages.javascript,A.languages.actionscript=A.languages.extend("javascript",{keyword:/\b(?:as|break|case|catch|class|const|default|delete|do|dynamic|each|else|extends|final|finally|for|function|get|if|implements|import|in|include|instanceof|interface|internal|is|namespace|native|new|null|override|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|use|var|void|while|with)\b/,operator:/\+\+|--|(?:[+\-*\/%^]|&&?|\|\|?|<<?|>>?>?|[!=]=?)=?|[~?@]/}),A.languages.actionscript["class-name"].alias="function",delete A.languages.actionscript.parameter,delete A.languages.actionscript["literal-property"],A.languages.markup&&A.languages.insertBefore("actionscript","string",{xml:{pattern:/(^|[^.])<\/?\w+(?:\s+[^\s>\/=]+=("|')(?:\\[\s\S]|(?!\2)[^\\])*\2)*\s*\/?>/,lookbehind:!0,inside:A.languages.markup}}),function(e){var t=/#(?!\{).+/,n={pattern:/#\{[^}]+\}/,alias:"variable"};e.languages.coffeescript=e.languages.extend("javascript",{comment:t,string:[{pattern:/'(?:\\[\s\S]|[^\\'])*'/,greedy:!0},{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0,inside:{interpolation:n}}],keyword:/\b(?:and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/,"class-member":{pattern:/@(?!\d)\w+/,alias:"variable"}}),e.languages.insertBefore("coffeescript","comment",{"multiline-comment":{pattern:/###[\s\S]+?###/,alias:"comment"},"block-regex":{pattern:/\/{3}[\s\S]*?\/{3}/,alias:"regex",inside:{comment:t,interpolation:n}}}),e.languages.insertBefore("coffeescript","string",{"inline-javascript":{pattern:/`(?:\\[\s\S]|[^\\`])*`/,inside:{delimiter:{pattern:/^`|`$/,alias:"punctuation"},script:{pattern:/[\s\S]+/,alias:"language-javascript",inside:e.languages.javascript}}},"multiline-string":[{pattern:/'''[\s\S]*?'''/,greedy:!0,alias:"string"},{pattern:/"""[\s\S]*?"""/,greedy:!0,alias:"string",inside:{interpolation:n}}]}),e.languages.insertBefore("coffeescript","keyword",{property:/(?!\d)\w+(?=\s*:(?!:))/}),delete e.languages.coffeescript["template-string"],e.languages.coffee=e.languages.coffeescript}(A),function(e){var t=e.languages.javadoclike={parameter:{pattern:/(^[\t ]*(?:\/{3}|\*|\/\*\*)\s*@(?:arg|arguments|param)\s+)\w+/m,lookbehind:!0},keyword:{pattern:/(^[\t ]*(?:\/{3}|\*|\/\*\*)\s*|\{)@[a-z][a-zA-Z-]+\b/m,lookbehind:!0},punctuation:/[{}]/};Object.defineProperty(t,"addSupport",{value:function(t,n){(t="string"==typeof t?[t]:t).forEach((function(t){var r=function(e){e.inside||(e.inside={}),e.inside.rest=n},a="doc-comment";if(o=e.languages[t]){var o,i=o[a];if((i=i||(o=e.languages.insertBefore(t,"comment",{"doc-comment":{pattern:/(^|[^\\])\/\*\*[^/][\s\S]*?(?:\*\/|$)/,lookbehind:!0,alias:"comment"}}))[a])instanceof RegExp&&(i=o[a]={pattern:i}),Array.isArray(i))for(var s=0,l=i.length;s<l;s++)i[s]instanceof RegExp&&(i[s]={pattern:i[s]}),r(i[s]);else r(i)}}))}}),t.addSupport(["java","javascript","php"],t)}(A),function(e){var t=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;(t=(e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:"+/[^;{\s"']|\s+(?!\s)/.source+"|"+t.source+")*?"+/(?:;|(?=\s*\{))/.source),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+t.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+t.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+t.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:t,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css,e.languages.markup))&&(t.tag.addInlined("style","css"),t.tag.addAttribute("style","css"))}(A),function(e){var t=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,n=(t=(e.languages.css.selector={pattern:e.languages.css.selector.pattern,lookbehind:!0,inside:t={"pseudo-element":/:(?:after|before|first-letter|first-line|selection)|::[-\w]+/,"pseudo-class":/:[-\w]+/,class:/\.[-\w]+/,id:/#[-\w]+/,attribute:{pattern:RegExp("\\[(?:[^[\\]\"']|"+t.source+")*\\]"),greedy:!0,inside:{punctuation:/^\[|\]$/,"case-sensitivity":{pattern:/(\s)[si]$/i,lookbehind:!0,alias:"keyword"},namespace:{pattern:/^(\s*)(?:(?!\s)[-*\w\xA0-\uFFFF])*\|(?!=)/,lookbehind:!0,inside:{punctuation:/\|$/}},"attr-name":{pattern:/^(\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+/,lookbehind:!0},"attr-value":[t,{pattern:/(=\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+(?=\s*$)/,lookbehind:!0}],operator:/[|~*^$]?=/}},"n-th":[{pattern:/(\(\s*)[+-]?\d*[\dn](?:\s*[+-]\s*\d+)?(?=\s*\))/,lookbehind:!0,inside:{number:/[\dn]+/,operator:/[+-]/}},{pattern:/(\(\s*)(?:even|odd)(?=\s*\))/i,lookbehind:!0}],combinator:/>|\+|~|\|\|/,punctuation:/[(),]/}},e.languages.css.atrule.inside["selector-function-argument"].inside=t,e.languages.insertBefore("css","property",{variable:{pattern:/(^|[^-\w\xA0-\uFFFF])--(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*/i,lookbehind:!0}}),{pattern:/(\b\d+)(?:%|[a-z]+(?![\w-]))/,lookbehind:!0}),{pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0});e.languages.insertBefore("css","function",{operator:{pattern:/(\s)[+\-*\/](?=\s)/,lookbehind:!0},hexcode:{pattern:/\B#[\da-f]{3,8}\b/i,alias:"color"},color:[{pattern:/(^|[^\w-])(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|RebeccaPurple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)(?![\w-])/i,lookbehind:!0},{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:t,number:n,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:t,number:n})}(A),function(e){var t=/[*&][^\s[\]{},]+/,n=/!(?:<[\w\-%#;/?:@&=+$,.!~*'()[\]]+>|(?:[a-zA-Z\d-]*!)?[\w\-%#;/?:@&=+$.~*'()]+)?/,r="(?:"+n.source+"(?:[ \t]+"+t.source+")?|"+t.source+"(?:[ \t]+"+n.source+")?)",a=/(?:[^\s\x00-\x08\x0e-\x1f!"#%&'*,\-:>?@[\]`{|}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]|[?:-]<PLAIN>)(?:[ \t]*(?:(?![#:])<PLAIN>|:<PLAIN>))*/.source.replace(/<PLAIN>/g,(function(){return/[^\s\x00-\x08\x0e-\x1f,[\]{}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]/.source})),o=/"(?:[^"\\\r\n]|\\.)*"|'(?:[^'\\\r\n]|\\.)*'/.source;function i(e,t){t=(t||"").replace(/m/g,"")+"m";var n=/([:\-,[{]\s*(?:\s<<prop>>[ \t]+)?)(?:<<value>>)(?=[ \t]*(?:$|,|\]|\}|(?:[\r\n]\s*)?#))/.source.replace(/<<prop>>/g,(function(){return r})).replace(/<<value>>/g,(function(){return e}));return RegExp(n,t)}e.languages.yaml={scalar:{pattern:RegExp(/([\-:]\s*(?:\s<<prop>>[ \t]+)?[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)\S[^\r\n]*(?:\2[^\r\n]+)*)/.source.replace(/<<prop>>/g,(function(){return r}))),lookbehind:!0,alias:"string"},comment:/#.*/,key:{pattern:RegExp(/((?:^|[:\-,[{\r\n?])[ \t]*(?:<<prop>>[ \t]+)?)<<key>>(?=\s*:\s)/.source.replace(/<<prop>>/g,(function(){return r})).replace(/<<key>>/g,(function(){return"(?:"+a+"|"+o+")"}))),lookbehind:!0,greedy:!0,alias:"atrule"},directive:{pattern:/(^[ \t]*)%.+/m,lookbehind:!0,alias:"important"},datetime:{pattern:i(/\d{4}-\d\d?-\d\d?(?:[tT]|[ \t]+)\d\d?:\d{2}:\d{2}(?:\.\d*)?(?:[ \t]*(?:Z|[-+]\d\d?(?::\d{2})?))?|\d{4}-\d{2}-\d{2}|\d\d?:\d{2}(?::\d{2}(?:\.\d*)?)?/.source),lookbehind:!0,alias:"number"},boolean:{pattern:i(/false|true/.source,"i"),lookbehind:!0,alias:"important"},null:{pattern:i(/null|~/.source,"i"),lookbehind:!0,alias:"important"},string:{pattern:i(o),lookbehind:!0,greedy:!0},number:{pattern:i(/[+-]?(?:0x[\da-f]+|0o[0-7]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?|\.inf|\.nan)/.source,"i"),lookbehind:!0},tag:n,important:t,punctuation:/---|[:[\]{}\-,|>?]|\.\.\./},e.languages.yml=e.languages.yaml}(A),function(e){var t=/(?:\\.|[^\\\n\r]|(?:\n|\r\n?)(?![\r\n]))/.source;function n(e){return e=e.replace(/<inner>/g,(function(){return t})),RegExp(/((?:^|[^\\])(?:\\{2})*)/.source+"(?:"+e+")")}var r=/(?:\\.|``(?:[^`\r\n]|`(?!`))+``|`[^`\r\n]+`|[^\\|\r\n`])+/.source,a=/\|?__(?:\|__)+\|?(?:(?:\n|\r\n?)|(?![\s\S]))/.source.replace(/__/g,(function(){return r})),o=/\|?[ \t]*:?-{3,}:?[ \t]*(?:\|[ \t]*:?-{3,}:?[ \t]*)+\|?(?:\n|\r\n?)/.source,i=(e.languages.markdown=e.languages.extend("markup",{}),e.languages.insertBefore("markdown","prolog",{"front-matter-block":{pattern:/(^(?:\s*[\r\n])?)---(?!.)[\s\S]*?[\r\n]---(?!.)/,lookbehind:!0,greedy:!0,inside:{punctuation:/^---|---$/,"front-matter":{pattern:/\S+(?:\s+\S+)*/,alias:["yaml","language-yaml"],inside:e.languages.yaml}}},blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},table:{pattern:RegExp("^"+a+o+"(?:"+a+")*","m"),inside:{"table-data-rows":{pattern:RegExp("^("+a+o+")(?:"+a+")*$"),lookbehind:!0,inside:{"table-data":{pattern:RegExp(r),inside:e.languages.markdown},punctuation:/\|/}},"table-line":{pattern:RegExp("^("+a+")"+o+"$"),lookbehind:!0,inside:{punctuation:/\||:?-{3,}:?/}},"table-header-row":{pattern:RegExp("^"+a+"$"),inside:{"table-header":{pattern:RegExp(r),alias:"important",inside:e.languages.markdown},punctuation:/\|/}}}},code:[{pattern:/((?:^|\n)[ \t]*\n|(?:^|\r\n?)[ \t]*\r\n?)(?: {4}|\t).+(?:(?:\n|\r\n?)(?: {4}|\t).+)*/,lookbehind:!0,alias:"keyword"},{pattern:/^```[\s\S]*?^```$/m,greedy:!0,inside:{"code-block":{pattern:/^(```.*(?:\n|\r\n?))[\s\S]+?(?=(?:\n|\r\n?)^```$)/m,lookbehind:!0},"code-language":{pattern:/^(```).+/,lookbehind:!0},punctuation:/```/}}],title:[{pattern:/\S.*(?:\n|\r\n?)(?:==+|--+)(?=[ \t]*$)/m,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:n(/\b__(?:(?!_)<inner>|_(?:(?!_)<inner>)+_)+__\b|\*\*(?:(?!\*)<inner>|\*(?:(?!\*)<inner>)+\*)+\*\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^..)[\s\S]+(?=..$)/,lookbehind:!0,inside:{}},punctuation:/\*\*|__/}},italic:{pattern:n(/\b_(?:(?!_)<inner>|__(?:(?!_)<inner>)+__)+_\b|\*(?:(?!\*)<inner>|\*\*(?:(?!\*)<inner>)+\*\*)+\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^.)[\s\S]+(?=.$)/,lookbehind:!0,inside:{}},punctuation:/[*_]/}},strike:{pattern:n(/(~~?)(?:(?!~)<inner>)+\2/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^~~?)[\s\S]+(?=\1$)/,lookbehind:!0,inside:{}},punctuation:/~~?/}},"code-snippet":{pattern:/(^|[^\\`])(?:``[^`\r\n]+(?:`[^`\r\n]+)*``(?!`)|`[^`\r\n]+`(?!`))/,lookbehind:!0,greedy:!0,alias:["code","keyword"]},url:{pattern:n(/!?\[(?:(?!\])<inner>)+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)|[ \t]?\[(?:(?!\])<inner>)+\])/.source),lookbehind:!0,greedy:!0,inside:{operator:/^!/,content:{pattern:/(^\[)[^\]]+(?=\])/,lookbehind:!0,inside:{}},variable:{pattern:/(^\][ \t]?\[)[^\]]+(?=\]$)/,lookbehind:!0},url:{pattern:/(^\]\()[^\s)]+/,lookbehind:!0},string:{pattern:/(^[ \t]+)"(?:\\.|[^"\\])*"(?=\)$)/,lookbehind:!0}}}}),["url","bold","italic","strike"].forEach((function(t){["url","bold","italic","strike","code-snippet"].forEach((function(n){t!==n&&(e.languages.markdown[t].inside.content.inside[n]=e.languages.markdown[n])}))})),e.hooks.add("after-tokenize",(function(e){"markdown"!==e.language&&"md"!==e.language||function e(t){if(t&&"string"!=typeof t)for(var n=0,r=t.length;n<r;n++){var a,o=t[n];"code"!==o.type?e(o.content):(a=o.content[1],o=o.content[3],a&&o&&"code-language"===a.type&&"code-block"===o.type&&"string"==typeof a.content&&(a=a.content.replace(/\b#/g,"sharp").replace(/\b\+\+/g,"pp"),a="language-"+(a=(/[a-z][\w-]*/i.exec(a)||[""])[0].toLowerCase()),o.alias?"string"==typeof o.alias?o.alias=[o.alias,a]:o.alias.push(a):o.alias=[a]))}}(e.tokens)})),e.hooks.add("wrap",(function(t){if("code-block"===t.type){for(var n="",r=0,a=t.classes.length;r<a;r++){var o=t.classes[r];if(o=/language-(.+)/.exec(o)){n=o[1];break}}var c,u=e.languages[n];u?t.content=e.highlight(t.content.replace(i,"").replace(/&(\w{1,8}|#x?[\da-f]{1,8});/gi,(function(e,t){var n;return"#"===(t=t.toLowerCase())[0]?(n="x"===t[1]?parseInt(t.slice(2),16):Number(t.slice(1)),l(n)):s[t]||e})),u,n):n&&"none"!==n&&e.plugins.autoloader&&(c="md-"+(new Date).valueOf()+"-"+Math.floor(1e16*Math.random()),t.attributes.id=c,e.plugins.autoloader.loadLanguages(n,(function(){var t=document.getElementById(c);t&&(t.innerHTML=e.highlight(t.textContent,e.languages[n],n))})))}})),RegExp(e.languages.markup.tag.pattern.source,"gi")),s={amp:"&",lt:"<",gt:">",quot:'"'},l=String.fromCodePoint||String.fromCharCode;e.languages.md=e.languages.markdown}(A),A.languages.graphql={comment:/#.*/,description:{pattern:/(?:"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*")(?=\s*[a-z_])/i,greedy:!0,alias:"string",inside:{"language-markdown":{pattern:/(^"(?:"")?)(?!\1)[\s\S]+(?=\1$)/,lookbehind:!0,inside:A.languages.markdown}}},string:{pattern:/"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*"/,greedy:!0},number:/(?:\B-|\b)\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,boolean:/\b(?:false|true)\b/,variable:/\$[a-z_]\w*/i,directive:{pattern:/@[a-z_]\w*/i,alias:"function"},"attr-name":{pattern:/\b[a-z_]\w*(?=\s*(?:\((?:[^()"]|"(?:\\.|[^\\"\r\n])*")*\))?:)/i,greedy:!0},"atom-input":{pattern:/\b[A-Z]\w*Input\b/,alias:"class-name"},scalar:/\b(?:Boolean|Float|ID|Int|String)\b/,constant:/\b[A-Z][A-Z_\d]*\b/,"class-name":{pattern:/(\b(?:enum|implements|interface|on|scalar|type|union)\s+|&\s*|:\s*|\[)[A-Z_]\w*/,lookbehind:!0},fragment:{pattern:/(\bfragment\s+|\.{3}\s*(?!on\b))[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-mutation":{pattern:/(\bmutation\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-query":{pattern:/(\bquery\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},keyword:/\b(?:directive|enum|extend|fragment|implements|input|interface|mutation|on|query|repeatable|scalar|schema|subscription|type|union)\b/,operator:/[!=|&]|\.{3}/,"property-query":/\w+(?=\s*\()/,object:/\w+(?=\s*\{)/,punctuation:/[!(){}\[\]:=,]/,property:/\w+/},A.hooks.add("after-tokenize",(function(e){if("graphql"===e.language)for(var t=e.tokens.filter((function(e){return"string"!=typeof e&&"comment"!==e.type&&"scalar"!==e.type})),n=0;n<t.length;){var r=t[n++];if("keyword"===r.type&&"mutation"===r.content){var a=[];if(d(["definition-mutation","punctuation"])&&"("===u(1).content){n+=2;var o=p(/^\($/,/^\)$/);if(-1===o)continue;for(;n<o;n++){var i=u(0);"variable"===i.type&&(f(i,"variable-input"),a.push(i.content))}n=o+1}if(d(["punctuation","property-query"])&&"{"===u(0).content&&(n++,f(u(0),"property-mutation"),0<a.length)){var s=p(/^\{$/,/^\}$/);if(-1!==s)for(var l=n;l<s;l++){var c=t[l];"variable"===c.type&&0<=a.indexOf(c.content)&&f(c,"variable-input")}}}}function u(e){return t[n+e]}function d(e,t){t=t||0;for(var n=0;n<e.length;n++){var r=u(n+t);if(!r||r.type!==e[n])return}return 1}function p(e,r){for(var a=1,o=n;o<t.length;o++){var i=t[o],s=i.content;if("punctuation"===i.type&&"string"==typeof s)if(e.test(s))a++;else if(r.test(s)&&0==--a)return o}return-1}function f(e,t){var n=e.alias;n?Array.isArray(n)||(e.alias=n=[n]):e.alias=n=[],n.push(t)}})),A.languages.sql={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:--|\/\/|#).*)/,lookbehind:!0},variable:[{pattern:/@(["'`])(?:\\[\s\S]|(?!\1)[^\\])+\1/,greedy:!0},/@[\w.$]+/],string:{pattern:/(^|[^@\\])("|')(?:\\[\s\S]|(?!\2)[^\\]|\2\2)*\2/,greedy:!0,lookbehind:!0},identifier:{pattern:/(^|[^@\\])`(?:\\[\s\S]|[^`\\]|``)*`/,greedy:!0,lookbehind:!0,inside:{punctuation:/^`|`$/}},function:/\b(?:AVG|COUNT|FIRST|FORMAT|LAST|LCASE|LEN|MAX|MID|MIN|MOD|NOW|ROUND|SUM|UCASE)(?=\s*\()/i,keyword:/\b(?:ACTION|ADD|AFTER|ALGORITHM|ALL|ALTER|ANALYZE|ANY|APPLY|AS|ASC|AUTHORIZATION|AUTO_INCREMENT|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADED?|CASE|CHAIN|CHAR(?:ACTER|SET)?|CHECK(?:POINT)?|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMNS?|COMMENT|COMMIT(?:TED)?|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS(?:TABLE)?|CONTINUE|CONVERT|CREATE|CROSS|CURRENT(?:_DATE|_TIME|_TIMESTAMP|_USER)?|CURSOR|CYCLE|DATA(?:BASES?)?|DATE(?:TIME)?|DAY|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DELIMITERS?|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DROP|DUMMY|DUMP(?:FILE)?|DUPLICATE|ELSE(?:IF)?|ENABLE|ENCLOSED|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPED?|EXCEPT|EXEC(?:UTE)?|EXISTS|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR(?: EACH ROW)?|FORCE|FOREIGN|FREETEXT(?:TABLE)?|FROM|FULL|FUNCTION|GEOMETRY(?:COLLECTION)?|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|HOUR|IDENTITY(?:COL|_INSERT)?|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|INVOKER|ISOLATION|ITERATE|JOIN|KEYS?|KILL|LANGUAGE|LAST|LEAVE|LEFT|LEVEL|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONG(?:BLOB|TEXT)|LOOP|MATCH(?:ED)?|MEDIUM(?:BLOB|INT|TEXT)|MERGE|MIDDLEINT|MINUTE|MODE|MODIFIES|MODIFY|MONTH|MULTI(?:LINESTRING|POINT|POLYGON)|NATIONAL|NATURAL|NCHAR|NEXT|NO|NONCLUSTERED|NULLIF|NUMERIC|OFF?|OFFSETS?|ON|OPEN(?:DATASOURCE|QUERY|ROWSET)?|OPTIMIZE|OPTION(?:ALLY)?|ORDER|OUT(?:ER|FILE)?|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREPARE|PREV|PRIMARY|PRINT|PRIVILEGES|PROC(?:EDURE)?|PUBLIC|PURGE|QUICK|RAISERROR|READS?|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEAT(?:ABLE)?|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESTORE|RESTRICT|RETURN(?:ING|S)?|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROW(?:COUNT|GUIDCOL|S)?|RTREE|RULE|SAVE(?:POINT)?|SCHEMA|SECOND|SELECT|SERIAL(?:IZABLE)?|SESSION(?:_USER)?|SET(?:USER)?|SHARE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|SQL|START(?:ING)?|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLES?|TABLESPACE|TEMP(?:ORARY|TABLE)?|TERMINATED|TEXT(?:SIZE)?|THEN|TIME(?:STAMP)?|TINY(?:BLOB|INT|TEXT)|TOP?|TRAN(?:SACTIONS?)?|TRIGGER|TRUNCATE|TSEQUAL|TYPES?|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNIQUE|UNLOCK|UNPIVOT|UNSIGNED|UPDATE(?:TEXT)?|USAGE|USE|USER|USING|VALUES?|VAR(?:BINARY|CHAR|CHARACTER|YING)|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH(?: ROLLUP|IN)?|WORK|WRITE(?:TEXT)?|YEAR)\b/i,boolean:/\b(?:FALSE|NULL|TRUE)\b/i,number:/\b0x[\da-f]+\b|\b\d+(?:\.\d*)?|\B\.\d+\b/i,operator:/[-+*\/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|DIV|ILIKE|IN|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/},function(e){var t=e.languages.javascript["template-string"],n=t.pattern.source,r=t.inside.interpolation,a=r.inside["interpolation-punctuation"],o=r.pattern.source;function i(t,r){if(e.languages[t])return{pattern:RegExp("((?:"+r+")\\s*)"+n),lookbehind:!0,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},"embedded-code":{pattern:/[\s\S]+/,alias:t}}}}function s(t,n,r){return t={code:t,grammar:n,language:r},e.hooks.run("before-tokenize",t),t.tokens=e.tokenize(t.code,t.grammar),e.hooks.run("after-tokenize",t),t.tokens}function l(t,n,i){var l=e.tokenize(t,{interpolation:{pattern:RegExp(o),lookbehind:!0}}),c=0,u={},d=(l=s(l.map((function(e){if("string"==typeof e)return e;var n,r;for(e=e.content;-1!==t.indexOf((r=c++,n="___"+i.toUpperCase()+"_"+r+"___")););return u[n]=e,n})).join(""),n,i),Object.keys(u));return c=0,function t(n){for(var o=0;o<n.length;o++){if(c>=d.length)return;var i,l,p,f,m,h,g,b=n[o];"string"==typeof b||"string"==typeof b.content?(i=d[c],-1!==(g=(h="string"==typeof b?b:b.content).indexOf(i))&&(++c,l=h.substring(0,g),m=u[i],p=void 0,(f={})["interpolation-punctuation"]=a,3===(f=e.tokenize(m,f)).length&&((p=[1,1]).push.apply(p,s(f[1],e.languages.javascript,"javascript")),f.splice.apply(f,p)),p=new e.Token("interpolation",f,r.alias,m),f=h.substring(g+i.length),m=[],l&&m.push(l),m.push(p),f&&(t(h=[f]),m.push.apply(m,h)),"string"==typeof b?(n.splice.apply(n,[o,1].concat(m)),o+=m.length-1):b.content=m)):(g=b.content,Array.isArray(g)?t(g):t([g]))}}(l),new e.Token(i,l,"language-"+i,t)}e.languages.javascript["template-string"]=[i("css",/\b(?:styled(?:\([^)]*\))?(?:\s*\.\s*\w+(?:\([^)]*\))*)*|css(?:\s*\.\s*(?:global|resolve))?|createGlobalStyle|keyframes)/.source),i("html",/\bhtml|\.\s*(?:inner|outer)HTML\s*\+?=/.source),i("svg",/\bsvg/.source),i("markdown",/\b(?:markdown|md)/.source),i("graphql",/\b(?:gql|graphql(?:\s*\.\s*experimental)?)/.source),i("sql",/\bsql/.source),t].filter(Boolean);var c={javascript:!0,js:!0,typescript:!0,ts:!0,jsx:!0,tsx:!0};function u(e){return"string"==typeof e?e:Array.isArray(e)?e.map(u).join(""):u(e.content)}e.hooks.add("after-tokenize",(function(t){t.language in c&&function t(n){for(var r=0,a=n.length;r<a;r++){var o,i,s,c=n[r];"string"!=typeof c&&(o=c.content,Array.isArray(o)?"template-string"===c.type?(c=o[1],3===o.length&&"string"!=typeof c&&"embedded-code"===c.type&&(i=u(c),c=c.alias,c=Array.isArray(c)?c[0]:c,s=e.languages[c])&&(o[1]=l(i,s,c))):t(o):"string"!=typeof o&&t([o]))}}(t.tokens)}))}(A),function(e){e.languages.typescript=e.languages.extend("javascript",{"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|type)\s+)(?!keyof\b)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?:\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\b/}),e.languages.typescript.keyword.push(/\b(?:abstract|declare|is|keyof|readonly|require)\b/,/\b(?:asserts|infer|interface|module|namespace|type)\b(?=\s*(?:[{_$a-zA-Z\xA0-\uFFFF]|$))/,/\btype\b(?=\s*(?:[\{*]|$))/),delete e.languages.typescript.parameter,delete e.languages.typescript["literal-property"];var t=e.languages.extend("typescript",{});delete t["class-name"],e.languages.typescript["class-name"].inside=t,e.languages.insertBefore("typescript","function",{decorator:{pattern:/@[$\w\xA0-\uFFFF]+/,inside:{at:{pattern:/^@/,alias:"operator"},function:/^[\s\S]+/}},"generic-function":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:t}}}}),e.languages.ts=e.languages.typescript}(A),function(e){var t=e.languages.javascript,n=/\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})+\}/.source,r="(@(?:arg|argument|param|property)\\s+(?:"+n+"\\s+)?)";e.languages.jsdoc=e.languages.extend("javadoclike",{parameter:{pattern:RegExp(r+/(?:(?!\s)[$\w\xA0-\uFFFF.])+(?=\s|$)/.source),lookbehind:!0,inside:{punctuation:/\./}}}),e.languages.insertBefore("jsdoc","keyword",{"optional-parameter":{pattern:RegExp(r+/\[(?:(?!\s)[$\w\xA0-\uFFFF.])+(?:=[^[\]]+)?\](?=\s|$)/.source),lookbehind:!0,inside:{parameter:{pattern:/(^\[)[$\w\xA0-\uFFFF\.]+/,lookbehind:!0,inside:{punctuation:/\./}},code:{pattern:/(=)[\s\S]*(?=\]$)/,lookbehind:!0,inside:t,alias:"language-javascript"},punctuation:/[=[\]]/}},"class-name":[{pattern:RegExp(/(@(?:augments|class|extends|interface|memberof!?|template|this|typedef)\s+(?:<TYPE>\s+)?)[A-Z]\w*(?:\.[A-Z]\w*)*/.source.replace(/<TYPE>/g,(function(){return n}))),lookbehind:!0,inside:{punctuation:/\./}},{pattern:RegExp("(@[a-z]+\\s+)"+n),lookbehind:!0,inside:{string:t.string,number:t.number,boolean:t.boolean,keyword:e.languages.typescript.keyword,operator:/=>|\.\.\.|[&|?:*]/,punctuation:/[.,;=<>{}()[\]]/}}],example:{pattern:/(@example\s+(?!\s))(?:[^@\s]|\s+(?!\s))+?(?=\s*(?:\*\s*)?(?:@\w|\*\/))/,lookbehind:!0,inside:{code:{pattern:/^([\t ]*(?:\*\s*)?)\S.*$/m,lookbehind:!0,inside:t,alias:"language-javascript"}}}}),e.languages.javadoclike.addSupport("javascript",e.languages.jsdoc)}(A),function(e){e.languages.flow=e.languages.extend("javascript",{}),e.languages.insertBefore("flow","keyword",{type:[{pattern:/\b(?:[Bb]oolean|Function|[Nn]umber|[Ss]tring|[Ss]ymbol|any|mixed|null|void)\b/,alias:"class-name"}]}),e.languages.flow["function-variable"].pattern=/(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=\s*(?:function\b|(?:\([^()]*\)(?:\s*:\s*\w+)?|(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/i,delete e.languages.flow.parameter,e.languages.insertBefore("flow","operator",{"flow-punctuation":{pattern:/\{\||\|\}/,alias:"punctuation"}}),Array.isArray(e.languages.flow.keyword)||(e.languages.flow.keyword=[e.languages.flow.keyword]),e.languages.flow.keyword.unshift({pattern:/(^|[^$]\b)(?:Class|declare|opaque|type)\b(?!\$)/,lookbehind:!0},{pattern:/(^|[^$]\B)\$(?:Diff|Enum|Exact|Keys|ObjMap|PropertyType|Record|Shape|Subtype|Supertype|await)\b(?!\$)/,lookbehind:!0})}(A),A.languages.n4js=A.languages.extend("javascript",{keyword:/\b(?:Array|any|boolean|break|case|catch|class|const|constructor|continue|debugger|declare|default|delete|do|else|enum|export|extends|false|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|module|new|null|number|package|private|protected|public|return|set|static|string|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/}),A.languages.insertBefore("n4js","constant",{annotation:{pattern:/@+\w+/,alias:"operator"}}),A.languages.n4jsd=A.languages.n4js,function(e){function t(e,t){return RegExp(e.replace(/<ID>/g,(function(){return/(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/.source})),t)}e.languages.insertBefore("javascript","function-variable",{"method-variable":{pattern:RegExp("(\\.\\s*)"+e.languages.javascript["function-variable"].pattern.source),lookbehind:!0,alias:["function-variable","method","function","property-access"]}}),e.languages.insertBefore("javascript","function",{method:{pattern:RegExp("(\\.\\s*)"+e.languages.javascript.function.source),lookbehind:!0,alias:["function","property-access"]}}),e.languages.insertBefore("javascript","constant",{"known-class-name":[{pattern:/\b(?:(?:Float(?:32|64)|(?:Int|Uint)(?:8|16|32)|Uint8Clamped)?Array|ArrayBuffer|BigInt|Boolean|DataView|Date|Error|Function|Intl|JSON|(?:Weak)?(?:Map|Set)|Math|Number|Object|Promise|Proxy|Reflect|RegExp|String|Symbol|WebAssembly)\b/,alias:"class-name"},{pattern:/\b(?:[A-Z]\w*)Error\b/,alias:"class-name"}]}),e.languages.insertBefore("javascript","keyword",{imports:{pattern:t(/(\bimport\b\s*)(?:<ID>(?:\s*,\s*(?:\*\s*as\s+<ID>|\{[^{}]*\}))?|\*\s*as\s+<ID>|\{[^{}]*\})(?=\s*\bfrom\b)/.source),lookbehind:!0,inside:e.languages.javascript},exports:{pattern:t(/(\bexport\b\s*)(?:\*(?:\s*as\s+<ID>)?(?=\s*\bfrom\b)|\{[^{}]*\})/.source),lookbehind:!0,inside:e.languages.javascript}}),e.languages.javascript.keyword.unshift({pattern:/\b(?:as|default|export|from|import)\b/,alias:"module"},{pattern:/\b(?:await|break|catch|continue|do|else|finally|for|if|return|switch|throw|try|while|yield)\b/,alias:"control-flow"},{pattern:/\bnull\b/,alias:["null","nil"]},{pattern:/\bundefined\b/,alias:"nil"}),e.languages.insertBefore("javascript","operator",{spread:{pattern:/\.{3}/,alias:"operator"},arrow:{pattern:/=>/,alias:"operator"}}),e.languages.insertBefore("javascript","punctuation",{"property-access":{pattern:t(/(\.\s*)#?<ID>/.source),lookbehind:!0},"maybe-class-name":{pattern:/(^|[^$\w\xA0-\uFFFF])[A-Z][$\w\xA0-\uFFFF]+/,lookbehind:!0},dom:{pattern:/\b(?:document|(?:local|session)Storage|location|navigator|performance|window)\b/,alias:"variable"},console:{pattern:/\bconsole(?=\s*\.)/,alias:"class-name"}});for(var n=["function","function-variable","method","method-variable","property-access"],r=0;r<n.length;r++){var a=n[r],o=e.languages.javascript[a];a=(o="RegExp"===e.util.type(o)?e.languages.javascript[a]={pattern:o}:o).inside||{};(o.inside=a)["maybe-class-name"]=/^[A-Z][\s\S]*/}}(A),function(e){var t=e.util.clone(e.languages.javascript),n=/(?:\s|\/\/.*(?!.)|\/\*(?:[^*]|\*(?!\/))\*\/)/.source,r=/(?:\{(?:\{(?:\{[^{}]*\}|[^{}])*\}|[^{}])*\})/.source,a=/(?:\{<S>*\.{3}(?:[^{}]|<BRACES>)*\})/.source;function o(e,t){return e=e.replace(/<S>/g,(function(){return n})).replace(/<BRACES>/g,(function(){return r})).replace(/<SPREAD>/g,(function(){return a})),RegExp(e,t)}function i(t){for(var n=[],r=0;r<t.length;r++){var a=t[r],o=!1;"string"!=typeof a&&("tag"===a.type&&a.content[0]&&"tag"===a.content[0].type?"</"===a.content[0].content[0].content?0<n.length&&n[n.length-1].tagName===s(a.content[0].content[1])&&n.pop():"/>"!==a.content[a.content.length-1].content&&n.push({tagName:s(a.content[0].content[1]),openedBraces:0}):0<n.length&&"punctuation"===a.type&&"{"===a.content?n[n.length-1].openedBraces++:0<n.length&&0<n[n.length-1].openedBraces&&"punctuation"===a.type&&"}"===a.content?n[n.length-1].openedBraces--:o=!0),(o||"string"==typeof a)&&0<n.length&&0===n[n.length-1].openedBraces&&(o=s(a),r<t.length-1&&("string"==typeof t[r+1]||"plain-text"===t[r+1].type)&&(o+=s(t[r+1]),t.splice(r+1,1)),0<r&&("string"==typeof t[r-1]||"plain-text"===t[r-1].type)&&(o=s(t[r-1])+o,t.splice(r-1,1),r--),t[r]=new e.Token("plain-text",o,null,o)),a.content&&"string"!=typeof a.content&&i(a.content)}}a=o(a).source,e.languages.jsx=e.languages.extend("markup",t),e.languages.jsx.tag.pattern=o(/<\/?(?:[\w.:-]+(?:<S>+(?:[\w.:$-]+(?:=(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s{'"/>=]+|<BRACES>))?|<SPREAD>))*<S>*\/?)?>/.source),e.languages.jsx.tag.inside.tag.pattern=/^<\/?[^\s>\/]*/,e.languages.jsx.tag.inside["attr-value"].pattern=/=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/,e.languages.jsx.tag.inside.tag.inside["class-name"]=/^[A-Z]\w*(?:\.[A-Z]\w*)*$/,e.languages.jsx.tag.inside.comment=t.comment,e.languages.insertBefore("inside","attr-name",{spread:{pattern:o(/<SPREAD>/.source),inside:e.languages.jsx}},e.languages.jsx.tag),e.languages.insertBefore("inside","special-attr",{script:{pattern:o(/=<BRACES>/.source),alias:"language-javascript",inside:{"script-punctuation":{pattern:/^=(?=\{)/,alias:"punctuation"},rest:e.languages.jsx}}},e.languages.jsx.tag);var s=function(e){return e?"string"==typeof e?e:"string"==typeof e.content?e.content:e.content.map(s).join(""):""};e.hooks.add("after-tokenize",(function(e){"jsx"!==e.language&&"tsx"!==e.language||i(e.tokens)}))}(A),function(e){var t=e.util.clone(e.languages.typescript);(t=(e.languages.tsx=e.languages.extend("jsx",t),delete e.languages.tsx.parameter,delete e.languages.tsx["literal-property"],e.languages.tsx.tag)).pattern=RegExp(/(^|[^\w$]|(?=<\/))/.source+"(?:"+t.pattern.source+")",t.pattern.flags),t.lookbehind=!0}(A),A.languages.swift={comment:{pattern:/(^|[^\\:])(?:\/\/.*|\/\*(?:[^/*]|\/(?!\*)|\*(?!\/)|\/\*(?:[^*]|\*(?!\/))*\*\/)*\*\/)/,lookbehind:!0,greedy:!0},"string-literal":[{pattern:RegExp(/(^|[^"#])/.source+"(?:"+/"(?:\\(?:\((?:[^()]|\([^()]*\))*\)|\r\n|[^(])|[^\\\r\n"])*"/.source+"|"+/"""(?:\\(?:\((?:[^()]|\([^()]*\))*\)|[^(])|[^\\"]|"(?!""))*"""/.source+")"+/(?!["#])/.source),lookbehind:!0,greedy:!0,inside:{interpolation:{pattern:/(\\\()(?:[^()]|\([^()]*\))*(?=\))/,lookbehind:!0,inside:null},"interpolation-punctuation":{pattern:/^\)|\\\($/,alias:"punctuation"},punctuation:/\\(?=[\r\n])/,string:/[\s\S]+/}},{pattern:RegExp(/(^|[^"#])(#+)/.source+"(?:"+/"(?:\\(?:#+\((?:[^()]|\([^()]*\))*\)|\r\n|[^#])|[^\\\r\n])*?"/.source+"|"+/"""(?:\\(?:#+\((?:[^()]|\([^()]*\))*\)|[^#])|[^\\])*?"""/.source+")\\2"),lookbehind:!0,greedy:!0,inside:{interpolation:{pattern:/(\\#+\()(?:[^()]|\([^()]*\))*(?=\))/,lookbehind:!0,inside:null},"interpolation-punctuation":{pattern:/^\)|\\#+\($/,alias:"punctuation"},string:/[\s\S]+/}}],directive:{pattern:RegExp(/#/.source+"(?:"+/(?:elseif|if)\b/.source+"(?:[ \t]*"+/(?:![ \t]*)?(?:\b\w+\b(?:[ \t]*\((?:[^()]|\([^()]*\))*\))?|\((?:[^()]|\([^()]*\))*\))(?:[ \t]*(?:&&|\|\|))?/.source+")+|"+/(?:else|endif)\b/.source+")"),alias:"property",inside:{"directive-name":/^#\w+/,boolean:/\b(?:false|true)\b/,number:/\b\d+(?:\.\d+)*\b/,operator:/!|&&|\|\||[<>]=?/,punctuation:/[(),]/}},literal:{pattern:/#(?:colorLiteral|column|dsohandle|file(?:ID|Literal|Path)?|function|imageLiteral|line)\b/,alias:"constant"},"other-directive":{pattern:/#\w+\b/,alias:"property"},attribute:{pattern:/@\w+/,alias:"atrule"},"function-definition":{pattern:/(\bfunc\s+)\w+/,lookbehind:!0,alias:"function"},label:{pattern:/\b(break|continue)\s+\w+|\b[a-zA-Z_]\w*(?=\s*:\s*(?:for|repeat|while)\b)/,lookbehind:!0,alias:"important"},keyword:/\b(?:Any|Protocol|Self|Type|actor|as|assignment|associatedtype|associativity|async|await|break|case|catch|class|continue|convenience|default|defer|deinit|didSet|do|dynamic|else|enum|extension|fallthrough|fileprivate|final|for|func|get|guard|higherThan|if|import|in|indirect|infix|init|inout|internal|is|isolated|lazy|left|let|lowerThan|mutating|none|nonisolated|nonmutating|open|operator|optional|override|postfix|precedencegroup|prefix|private|protocol|public|repeat|required|rethrows|return|right|safe|self|set|some|static|struct|subscript|super|switch|throw|throws|try|typealias|unowned|unsafe|var|weak|where|while|willSet)\b/,boolean:/\b(?:false|true)\b/,nil:{pattern:/\bnil\b/,alias:"constant"},"short-argument":/\$\d+\b/,omit:{pattern:/\b_\b/,alias:"keyword"},number:/\b(?:[\d_]+(?:\.[\de_]+)?|0x[a-f0-9_]+(?:\.[a-f0-9p_]+)?|0b[01_]+|0o[0-7_]+)\b/i,"class-name":/\b[A-Z](?:[A-Z_\d]*[a-z]\w*)?\b/,function:/\b[a-z_]\w*(?=\s*\()/i,constant:/\b(?:[A-Z_]{2,}|k[A-Z][A-Za-z_]+)\b/,operator:/[-+*/%=!<>&|^~?]+|\.[.\-+*/%=!<>&|^~?]+/,punctuation:/[{}[\]();,.:\\]/},A.languages.swift["string-literal"].forEach((function(e){e.inside.interpolation.inside=A.languages.swift})),function(e){e.languages.kotlin=e.languages.extend("clike",{keyword:{pattern:/(^|[^.])\b(?:abstract|actual|annotation|as|break|by|catch|class|companion|const|constructor|continue|crossinline|data|do|dynamic|else|enum|expect|external|final|finally|for|fun|get|if|import|in|infix|init|inline|inner|interface|internal|is|lateinit|noinline|null|object|open|operator|out|override|package|private|protected|public|reified|return|sealed|set|super|suspend|tailrec|this|throw|to|try|typealias|val|var|vararg|when|where|while)\b/,lookbehind:!0},function:[{pattern:/(?:`[^\r\n`]+`|\b\w+)(?=\s*\()/,greedy:!0},{pattern:/(\.)(?:`[^\r\n`]+`|\w+)(?=\s*\{)/,lookbehind:!0,greedy:!0}],number:/\b(?:0[xX][\da-fA-F]+(?:_[\da-fA-F]+)*|0[bB][01]+(?:_[01]+)*|\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+(?:_\d+)*)?[fFL]?)\b/,operator:/\+[+=]?|-[-=>]?|==?=?|!(?:!|==?)?|[\/*%<>]=?|[?:]:?|\.\.|&&|\|\||\b(?:and|inv|or|shl|shr|ushr|xor)\b/}),delete e.languages.kotlin["class-name"];var t={"interpolation-punctuation":{pattern:/^\$\{?|\}$/,alias:"punctuation"},expression:{pattern:/[\s\S]+/,inside:e.languages.kotlin}};e.languages.insertBefore("kotlin","string",{"string-literal":[{pattern:/"""(?:[^$]|\$(?:(?!\{)|\{[^{}]*\}))*?"""/,alias:"multiline",inside:{interpolation:{pattern:/\$(?:[a-z_]\w*|\{[^{}]*\})/i,inside:t},string:/[\s\S]+/}},{pattern:/"(?:[^"\\\r\n$]|\\.|\$(?:(?!\{)|\{[^{}]*\}))*"/,alias:"singleline",inside:{interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$(?:[a-z_]\w*|\{[^{}]*\})/i,lookbehind:!0,inside:t},string:/[\s\S]+/}}],char:{pattern:/'(?:[^'\\\r\n]|\\(?:.|u[a-fA-F0-9]{0,4}))'/,greedy:!0}}),delete e.languages.kotlin.string,e.languages.insertBefore("kotlin","keyword",{annotation:{pattern:/\B@(?:\w+:)?(?:[A-Z]\w*|\[[^\]]+\])/,alias:"builtin"}}),e.languages.insertBefore("kotlin","function",{label:{pattern:/\b\w+@|@\w+\b/,alias:"symbol"}}),e.languages.kt=e.languages.kotlin,e.languages.kts=e.languages.kotlin}(A),A.languages.c=A.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/,lookbehind:!0},keyword:/\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/,function:/\b[a-z_]\w*(?=\s*\()/i,number:/(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/}),A.languages.insertBefore("c","string",{char:{pattern:/'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/,greedy:!0}}),A.languages.insertBefore("c","string",{macro:{pattern:/(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},A.languages.c.string],char:A.languages.c.char,comment:A.languages.c.comment,"macro-name":[{pattern:/(^#\s*define\s+)\w+\b(?!\()/i,lookbehind:!0},{pattern:/(^#\s*define\s+)\w+\b(?=\()/i,lookbehind:!0,alias:"function"}],directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:A.languages.c}}}}),A.languages.insertBefore("c","function",{constant:/\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/}),delete A.languages.c.boolean,A.languages.objectivec=A.languages.extend("c",{string:{pattern:/@?"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},keyword:/\b(?:asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|in|inline|int|long|register|return|self|short|signed|sizeof|static|struct|super|switch|typedef|typeof|union|unsigned|void|volatile|while)\b|(?:@interface|@end|@implementation|@protocol|@class|@public|@protected|@private|@property|@try|@catch|@finally|@throw|@synthesize|@dynamic|@selector)\b/,operator:/-[->]?|\+\+?|!=?|<<?=?|>>?=?|==?|&&?|\|\|?|[~^%?*\/@]/}),delete A.languages.objectivec["class-name"],A.languages.objc=A.languages.objectivec,A.languages.reason=A.languages.extend("clike",{string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^\\\r\n"])*"/,greedy:!0},"class-name":/\b[A-Z]\w*/,keyword:/\b(?:and|as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|method|module|mutable|new|nonrec|object|of|open|or|private|rec|sig|struct|switch|then|to|try|type|val|virtual|when|while|with)\b/,operator:/\.{3}|:[:=]|\|>|->|=(?:==?|>)?|<=?|>=?|[|^?'#!~`]|[+\-*\/]\.?|\b(?:asr|land|lor|lsl|lsr|lxor|mod)\b/}),A.languages.insertBefore("reason","class-name",{char:{pattern:/'(?:\\x[\da-f]{2}|\\o[0-3][0-7][0-7]|\\\d{3}|\\.|[^'\\\r\n])'/,greedy:!0},constructor:/\b[A-Z]\w*\b(?!\s*\.)/,label:{pattern:/\b[a-z]\w*(?=::)/,alias:"symbol"}}),delete A.languages.reason.function,function(e){for(var t=/\/\*(?:[^*/]|\*(?!\/)|\/(?!\*)|<self>)*\*\//.source,n=0;n<2;n++)t=t.replace(/<self>/g,(function(){return t}));t=t.replace(/<self>/g,(function(){return/[^\s\S]/.source})),e.languages.rust={comment:[{pattern:RegExp(/(^|[^\\])/.source+t),lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/b?"(?:\\[\s\S]|[^\\"])*"|b?r(#*)"(?:[^"]|"(?!\1))*"\1/,greedy:!0},char:{pattern:/b?'(?:\\(?:x[0-7][\da-fA-F]|u\{(?:[\da-fA-F]_*){1,6}\}|.)|[^\\\r\n\t'])'/,greedy:!0},attribute:{pattern:/#!?\[(?:[^\[\]"]|"(?:\\[\s\S]|[^\\"])*")*\]/,greedy:!0,alias:"attr-name",inside:{string:null}},"closure-params":{pattern:/([=(,:]\s*|\bmove\s*)\|[^|]*\||\|[^|]*\|(?=\s*(?:\{|->))/,lookbehind:!0,greedy:!0,inside:{"closure-punctuation":{pattern:/^\||\|$/,alias:"punctuation"},rest:null}},"lifetime-annotation":{pattern:/'\w+/,alias:"symbol"},"fragment-specifier":{pattern:/(\$\w+:)[a-z]+/,lookbehind:!0,alias:"punctuation"},variable:/\$\w+/,"function-definition":{pattern:/(\bfn\s+)\w+/,lookbehind:!0,alias:"function"},"type-definition":{pattern:/(\b(?:enum|struct|trait|type|union)\s+)\w+/,lookbehind:!0,alias:"class-name"},"module-declaration":[{pattern:/(\b(?:crate|mod)\s+)[a-z][a-z_\d]*/,lookbehind:!0,alias:"namespace"},{pattern:/(\b(?:crate|self|super)\s*)::\s*[a-z][a-z_\d]*\b(?:\s*::(?:\s*[a-z][a-z_\d]*\s*::)*)?/,lookbehind:!0,alias:"namespace",inside:{punctuation:/::/}}],keyword:[/\b(?:Self|abstract|as|async|await|become|box|break|const|continue|crate|do|dyn|else|enum|extern|final|fn|for|if|impl|in|let|loop|macro|match|mod|move|mut|override|priv|pub|ref|return|self|static|struct|super|trait|try|type|typeof|union|unsafe|unsized|use|virtual|where|while|yield)\b/,/\b(?:bool|char|f(?:32|64)|[ui](?:8|16|32|64|128|size)|str)\b/],function:/\b[a-z_]\w*(?=\s*(?:::\s*<|\())/,macro:{pattern:/\b\w+!/,alias:"property"},constant:/\b[A-Z_][A-Z_\d]+\b/,"class-name":/\b[A-Z]\w*\b/,namespace:{pattern:/(?:\b[a-z][a-z_\d]*\s*::\s*)*\b[a-z][a-z_\d]*\s*::(?!\s*<)/,inside:{punctuation:/::/}},number:/\b(?:0x[\dA-Fa-f](?:_?[\dA-Fa-f])*|0o[0-7](?:_?[0-7])*|0b[01](?:_?[01])*|(?:(?:\d(?:_?\d)*)?\.)?\d(?:_?\d)*(?:[Ee][+-]?\d+)?)(?:_?(?:f32|f64|[iu](?:8|16|32|64|size)?))?\b/,boolean:/\b(?:false|true)\b/,punctuation:/->|\.\.=|\.{1,3}|::|[{}[\];(),:]/,operator:/[-+*\/%!^]=?|=[=>]?|&[&=]?|\|[|=]?|<<?=?|>>?=?|[@?]/},e.languages.rust["closure-params"].inside.rest=e.languages.rust,e.languages.rust.attribute.inside.string=e.languages.rust.string}(A),A.languages.go=A.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"|`[^`]*`/,lookbehind:!0,greedy:!0},keyword:/\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/,boolean:/\b(?:_|false|iota|nil|true)\b/,number:[/\b0(?:b[01_]+|o[0-7_]+)i?\b/i,/\b0x(?:[a-f\d_]+(?:\.[a-f\d_]*)?|\.[a-f\d_]+)(?:p[+-]?\d+(?:_\d+)*)?i?(?!\w)/i,/(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?[\d_]+)?i?(?!\w)/i],operator:/[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,builtin:/\b(?:append|bool|byte|cap|close|complex|complex(?:64|128)|copy|delete|error|float(?:32|64)|u?int(?:8|16|32|64)?|imag|len|make|new|panic|print(?:ln)?|real|recover|rune|string|uintptr)\b/}),A.languages.insertBefore("go","string",{char:{pattern:/'(?:\\.|[^'\\\r\n]){0,10}'/,greedy:!0}}),delete A.languages.go["class-name"],function(e){var t=/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,n=/\b(?!<keyword>)\w+(?:\s*\.\s*\w+)*\b/.source.replace(/<keyword>/g,(function(){return t.source}));e.languages.cpp=e.languages.extend("c",{"class-name":[{pattern:RegExp(/(\b(?:class|concept|enum|struct|typename)\s+)(?!<keyword>)\w+/.source.replace(/<keyword>/g,(function(){return t.source}))),lookbehind:!0},/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,/\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/],keyword:t,number:{pattern:/(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,greedy:!0},operator:/>>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,boolean:/\b(?:false|true)\b/}),e.languages.insertBefore("cpp","string",{module:{pattern:RegExp(/(\b(?:import|module)\s+)/.source+"(?:"+/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source+"|"+/<mod-name>(?:\s*:\s*<mod-name>)?|:\s*<mod-name>/.source.replace(/<mod-name>/g,(function(){return n}))+")"),lookbehind:!0,greedy:!0,inside:{string:/^[<"][\s\S]+/,operator:/:/,punctuation:/\./}},"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}}),e.languages.insertBefore("cpp","keyword",{"generic-function":{pattern:/\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i,inside:{function:/^\w+/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:e.languages.cpp}}}}),e.languages.insertBefore("cpp","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}}),e.languages.insertBefore("cpp","class-name",{"base-clause":{pattern:/(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:e.languages.extend("cpp",{})}}),e.languages.insertBefore("inside","double-colon",{"class-name":/\b[a-z_]\w*\b(?!\s*::)/i},e.languages.cpp["base-clause"])}(A),A.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},A.languages.python["string-interpolation"].inside.interpolation.inside.rest=A.languages.python,A.languages.py=A.languages.python;((e,t)=>{for(var n in t)f(e,n,{get:t[n],enumerable:!0})})({},{dracula:()=>T,duotoneDark:()=>j,duotoneLight:()=>N,github:()=>L,jettwaveDark:()=>H,jettwaveLight:()=>G,nightOwl:()=>R,nightOwlLight:()=>P,oceanicNext:()=>I,okaidia:()=>F,oneDark:()=>V,oneLight:()=>W,palenight:()=>M,shadesOfPurple:()=>z,synthwave84:()=>B,ultramin:()=>$,vsDark:()=>q,vsLight:()=>U});var T={plain:{color:"#F8F8F2",backgroundColor:"#282A36"},styles:[{types:["prolog","constant","builtin"],style:{color:"rgb(189, 147, 249)"}},{types:["inserted","function"],style:{color:"rgb(80, 250, 123)"}},{types:["deleted"],style:{color:"rgb(255, 85, 85)"}},{types:["changed"],style:{color:"rgb(255, 184, 108)"}},{types:["punctuation","symbol"],style:{color:"rgb(248, 248, 242)"}},{types:["string","char","tag","selector"],style:{color:"rgb(255, 121, 198)"}},{types:["keyword","variable"],style:{color:"rgb(189, 147, 249)",fontStyle:"italic"}},{types:["comment"],style:{color:"rgb(98, 114, 164)"}},{types:["attr-name"],style:{color:"rgb(241, 250, 140)"}}]},j={plain:{backgroundColor:"#2a2734",color:"#9a86fd"},styles:[{types:["comment","prolog","doctype","cdata","punctuation"],style:{color:"#6c6783"}},{types:["namespace"],style:{opacity:.7}},{types:["tag","operator","number"],style:{color:"#e09142"}},{types:["property","function"],style:{color:"#9a86fd"}},{types:["tag-id","selector","atrule-id"],style:{color:"#eeebff"}},{types:["attr-name"],style:{color:"#c4b9fe"}},{types:["boolean","string","entity","url","attr-value","keyword","control","directive","unit","statement","regex","atrule","placeholder","variable"],style:{color:"#ffcc99"}},{types:["deleted"],style:{textDecorationLine:"line-through"}},{types:["inserted"],style:{textDecorationLine:"underline"}},{types:["italic"],style:{fontStyle:"italic"}},{types:["important","bold"],style:{fontWeight:"bold"}},{types:["important"],style:{color:"#c4b9fe"}}]},N={plain:{backgroundColor:"#faf8f5",color:"#728fcb"},styles:[{types:["comment","prolog","doctype","cdata","punctuation"],style:{color:"#b6ad9a"}},{types:["namespace"],style:{opacity:.7}},{types:["tag","operator","number"],style:{color:"#063289"}},{types:["property","function"],style:{color:"#b29762"}},{types:["tag-id","selector","atrule-id"],style:{color:"#2d2006"}},{types:["attr-name"],style:{color:"#896724"}},{types:["boolean","string","entity","url","attr-value","keyword","control","directive","unit","statement","regex","atrule"],style:{color:"#728fcb"}},{types:["placeholder","variable"],style:{color:"#93abdc"}},{types:["deleted"],style:{textDecorationLine:"line-through"}},{types:["inserted"],style:{textDecorationLine:"underline"}},{types:["italic"],style:{fontStyle:"italic"}},{types:["important","bold"],style:{fontWeight:"bold"}},{types:["important"],style:{color:"#896724"}}]},L={plain:{color:"#393A34",backgroundColor:"#f6f8fa"},styles:[{types:["comment","prolog","doctype","cdata"],style:{color:"#999988",fontStyle:"italic"}},{types:["namespace"],style:{opacity:.7}},{types:["string","attr-value"],style:{color:"#e3116c"}},{types:["punctuation","operator"],style:{color:"#393A34"}},{types:["entity","url","symbol","number","boolean","variable","constant","property","regex","inserted"],style:{color:"#36acaa"}},{types:["atrule","keyword","attr-name","selector"],style:{color:"#00a4db"}},{types:["function","deleted","tag"],style:{color:"#d73a49"}},{types:["function-variable"],style:{color:"#6f42c1"}},{types:["tag","selector","keyword"],style:{color:"#00009f"}}]},R={plain:{color:"#d6deeb",backgroundColor:"#011627"},styles:[{types:["changed"],style:{color:"rgb(162, 191, 252)",fontStyle:"italic"}},{types:["deleted"],style:{color:"rgba(239, 83, 80, 0.56)",fontStyle:"italic"}},{types:["inserted","attr-name"],style:{color:"rgb(173, 219, 103)",fontStyle:"italic"}},{types:["comment"],style:{color:"rgb(99, 119, 119)",fontStyle:"italic"}},{types:["string","url"],style:{color:"rgb(173, 219, 103)"}},{types:["variable"],style:{color:"rgb(214, 222, 235)"}},{types:["number"],style:{color:"rgb(247, 140, 108)"}},{types:["builtin","char","constant","function"],style:{color:"rgb(130, 170, 255)"}},{types:["punctuation"],style:{color:"rgb(199, 146, 234)"}},{types:["selector","doctype"],style:{color:"rgb(199, 146, 234)",fontStyle:"italic"}},{types:["class-name"],style:{color:"rgb(255, 203, 139)"}},{types:["tag","operator","keyword"],style:{color:"rgb(127, 219, 202)"}},{types:["boolean"],style:{color:"rgb(255, 88, 116)"}},{types:["property"],style:{color:"rgb(128, 203, 196)"}},{types:["namespace"],style:{color:"rgb(178, 204, 214)"}}]},P={plain:{color:"#403f53",backgroundColor:"#FBFBFB"},styles:[{types:["changed"],style:{color:"rgb(162, 191, 252)",fontStyle:"italic"}},{types:["deleted"],style:{color:"rgba(239, 83, 80, 0.56)",fontStyle:"italic"}},{types:["inserted","attr-name"],style:{color:"rgb(72, 118, 214)",fontStyle:"italic"}},{types:["comment"],style:{color:"rgb(152, 159, 177)",fontStyle:"italic"}},{types:["string","builtin","char","constant","url"],style:{color:"rgb(72, 118, 214)"}},{types:["variable"],style:{color:"rgb(201, 103, 101)"}},{types:["number"],style:{color:"rgb(170, 9, 130)"}},{types:["punctuation"],style:{color:"rgb(153, 76, 195)"}},{types:["function","selector","doctype"],style:{color:"rgb(153, 76, 195)",fontStyle:"italic"}},{types:["class-name"],style:{color:"rgb(17, 17, 17)"}},{types:["tag"],style:{color:"rgb(153, 76, 195)"}},{types:["operator","property","keyword","namespace"],style:{color:"rgb(12, 150, 155)"}},{types:["boolean"],style:{color:"rgb(188, 84, 84)"}}]},O="#c5a5c5",D="#8dc891",I={plain:{backgroundColor:"#282c34",color:"#ffffff"},styles:[{types:["attr-name"],style:{color:O}},{types:["attr-value"],style:{color:D}},{types:["comment","block-comment","prolog","doctype","cdata","shebang"],style:{color:"#999999"}},{types:["property","number","function-name","constant","symbol","deleted"],style:{color:"#5a9bcf"}},{types:["boolean"],style:{color:"#ff8b50"}},{types:["tag"],style:{color:"#fc929e"}},{types:["string"],style:{color:D}},{types:["punctuation"],style:{color:D}},{types:["selector","char","builtin","inserted"],style:{color:"#D8DEE9"}},{types:["function"],style:{color:"#79b6f2"}},{types:["operator","entity","url","variable"],style:{color:"#d7deea"}},{types:["keyword"],style:{color:O}},{types:["atrule","class-name"],style:{color:"#FAC863"}},{types:["important"],style:{fontWeight:"400"}},{types:["bold"],style:{fontWeight:"bold"}},{types:["italic"],style:{fontStyle:"italic"}},{types:["namespace"],style:{opacity:.7}}]},F={plain:{color:"#f8f8f2",backgroundColor:"#272822"},styles:[{types:["changed"],style:{color:"rgb(162, 191, 252)",fontStyle:"italic"}},{types:["deleted"],style:{color:"#f92672",fontStyle:"italic"}},{types:["inserted"],style:{color:"rgb(173, 219, 103)",fontStyle:"italic"}},{types:["comment"],style:{color:"#8292a2",fontStyle:"italic"}},{types:["string","url"],style:{color:"#a6e22e"}},{types:["variable"],style:{color:"#f8f8f2"}},{types:["number"],style:{color:"#ae81ff"}},{types:["builtin","char","constant","function","class-name"],style:{color:"#e6db74"}},{types:["punctuation"],style:{color:"#f8f8f2"}},{types:["selector","doctype"],style:{color:"#a6e22e",fontStyle:"italic"}},{types:["tag","operator","keyword"],style:{color:"#66d9ef"}},{types:["boolean"],style:{color:"#ae81ff"}},{types:["namespace"],style:{color:"rgb(178, 204, 214)",opacity:.7}},{types:["tag","property"],style:{color:"#f92672"}},{types:["attr-name"],style:{color:"#a6e22e !important"}},{types:["doctype"],style:{color:"#8292a2"}},{types:["rule"],style:{color:"#e6db74"}}]},M={plain:{color:"#bfc7d5",backgroundColor:"#292d3e"},styles:[{types:["comment"],style:{color:"rgb(105, 112, 152)",fontStyle:"italic"}},{types:["string","inserted"],style:{color:"rgb(195, 232, 141)"}},{types:["number"],style:{color:"rgb(247, 140, 108)"}},{types:["builtin","char","constant","function"],style:{color:"rgb(130, 170, 255)"}},{types:["punctuation","selector"],style:{color:"rgb(199, 146, 234)"}},{types:["variable"],style:{color:"rgb(191, 199, 213)"}},{types:["class-name","attr-name"],style:{color:"rgb(255, 203, 107)"}},{types:["tag","deleted"],style:{color:"rgb(255, 85, 114)"}},{types:["operator"],style:{color:"rgb(137, 221, 255)"}},{types:["boolean"],style:{color:"rgb(255, 88, 116)"}},{types:["keyword"],style:{fontStyle:"italic"}},{types:["doctype"],style:{color:"rgb(199, 146, 234)",fontStyle:"italic"}},{types:["namespace"],style:{color:"rgb(178, 204, 214)"}},{types:["url"],style:{color:"rgb(221, 221, 221)"}}]},z={plain:{color:"#9EFEFF",backgroundColor:"#2D2A55"},styles:[{types:["changed"],style:{color:"rgb(255, 238, 128)"}},{types:["deleted"],style:{color:"rgba(239, 83, 80, 0.56)"}},{types:["inserted"],style:{color:"rgb(173, 219, 103)"}},{types:["comment"],style:{color:"rgb(179, 98, 255)",fontStyle:"italic"}},{types:["punctuation"],style:{color:"rgb(255, 255, 255)"}},{types:["constant"],style:{color:"rgb(255, 98, 140)"}},{types:["string","url"],style:{color:"rgb(165, 255, 144)"}},{types:["variable"],style:{color:"rgb(255, 238, 128)"}},{types:["number","boolean"],style:{color:"rgb(255, 98, 140)"}},{types:["attr-name"],style:{color:"rgb(255, 180, 84)"}},{types:["keyword","operator","property","namespace","tag","selector","doctype"],style:{color:"rgb(255, 157, 0)"}},{types:["builtin","char","constant","function","class-name"],style:{color:"rgb(250, 208, 0)"}}]},B={plain:{backgroundColor:"linear-gradient(to bottom, #2a2139 75%, #34294f)",backgroundImage:"#34294f",color:"#f92aad",textShadow:"0 0 2px #100c0f, 0 0 5px #dc078e33, 0 0 10px #fff3"},styles:[{types:["comment","block-comment","prolog","doctype","cdata"],style:{color:"#495495",fontStyle:"italic"}},{types:["punctuation"],style:{color:"#ccc"}},{types:["tag","attr-name","namespace","number","unit","hexcode","deleted"],style:{color:"#e2777a"}},{types:["property","selector"],style:{color:"#72f1b8",textShadow:"0 0 2px #100c0f, 0 0 10px #257c5575, 0 0 35px #21272475"}},{types:["function-name"],style:{color:"#6196cc"}},{types:["boolean","selector-id","function"],style:{color:"#fdfdfd",textShadow:"0 0 2px #001716, 0 0 3px #03edf975, 0 0 5px #03edf975, 0 0 8px #03edf975"}},{types:["class-name","maybe-class-name","builtin"],style:{color:"#fff5f6",textShadow:"0 0 2px #000, 0 0 10px #fc1f2c75, 0 0 5px #fc1f2c75, 0 0 25px #fc1f2c75"}},{types:["constant","symbol"],style:{color:"#f92aad",textShadow:"0 0 2px #100c0f, 0 0 5px #dc078e33, 0 0 10px #fff3"}},{types:["important","atrule","keyword","selector-class"],style:{color:"#f4eee4",textShadow:"0 0 2px #393a33, 0 0 8px #f39f0575, 0 0 2px #f39f0575"}},{types:["string","char","attr-value","regex","variable"],style:{color:"#f87c32"}},{types:["parameter"],style:{fontStyle:"italic"}},{types:["entity","url"],style:{color:"#67cdcc"}},{types:["operator"],style:{color:"ffffffee"}},{types:["important","bold"],style:{fontWeight:"bold"}},{types:["italic"],style:{fontStyle:"italic"}},{types:["entity"],style:{cursor:"help"}},{types:["inserted"],style:{color:"green"}}]},$={plain:{color:"#282a2e",backgroundColor:"#ffffff"},styles:[{types:["comment"],style:{color:"rgb(197, 200, 198)"}},{types:["string","number","builtin","variable"],style:{color:"rgb(150, 152, 150)"}},{types:["class-name","function","tag","attr-name"],style:{color:"rgb(40, 42, 46)"}}]},q={plain:{color:"#9CDCFE",backgroundColor:"#1E1E1E"},styles:[{types:["prolog"],style:{color:"rgb(0, 0, 128)"}},{types:["comment"],style:{color:"rgb(106, 153, 85)"}},{types:["builtin","changed","keyword","interpolation-punctuation"],style:{color:"rgb(86, 156, 214)"}},{types:["number","inserted"],style:{color:"rgb(181, 206, 168)"}},{types:["constant"],style:{color:"rgb(100, 102, 149)"}},{types:["attr-name","variable"],style:{color:"rgb(156, 220, 254)"}},{types:["deleted","string","attr-value","template-punctuation"],style:{color:"rgb(206, 145, 120)"}},{types:["selector"],style:{color:"rgb(215, 186, 125)"}},{types:["tag"],style:{color:"rgb(78, 201, 176)"}},{types:["tag"],languages:["markup"],style:{color:"rgb(86, 156, 214)"}},{types:["punctuation","operator"],style:{color:"rgb(212, 212, 212)"}},{types:["punctuation"],languages:["markup"],style:{color:"#808080"}},{types:["function"],style:{color:"rgb(220, 220, 170)"}},{types:["class-name"],style:{color:"rgb(78, 201, 176)"}},{types:["char"],style:{color:"rgb(209, 105, 105)"}}]},U={plain:{color:"#000000",backgroundColor:"#ffffff"},styles:[{types:["comment"],style:{color:"rgb(0, 128, 0)"}},{types:["builtin"],style:{color:"rgb(0, 112, 193)"}},{types:["number","variable","inserted"],style:{color:"rgb(9, 134, 88)"}},{types:["operator"],style:{color:"rgb(0, 0, 0)"}},{types:["constant","char"],style:{color:"rgb(129, 31, 63)"}},{types:["tag"],style:{color:"rgb(128, 0, 0)"}},{types:["attr-name"],style:{color:"rgb(255, 0, 0)"}},{types:["deleted","string"],style:{color:"rgb(163, 21, 21)"}},{types:["changed","punctuation"],style:{color:"rgb(4, 81, 165)"}},{types:["function","keyword"],style:{color:"rgb(0, 0, 255)"}},{types:["class-name"],style:{color:"rgb(38, 127, 153)"}}]},H={plain:{color:"#f8fafc",backgroundColor:"#011627"},styles:[{types:["prolog"],style:{color:"#000080"}},{types:["comment"],style:{color:"#6A9955"}},{types:["builtin","changed","keyword","interpolation-punctuation"],style:{color:"#569CD6"}},{types:["number","inserted"],style:{color:"#B5CEA8"}},{types:["constant"],style:{color:"#f8fafc"}},{types:["attr-name","variable"],style:{color:"#9CDCFE"}},{types:["deleted","string","attr-value","template-punctuation"],style:{color:"#cbd5e1"}},{types:["selector"],style:{color:"#D7BA7D"}},{types:["tag"],style:{color:"#0ea5e9"}},{types:["tag"],languages:["markup"],style:{color:"#0ea5e9"}},{types:["punctuation","operator"],style:{color:"#D4D4D4"}},{types:["punctuation"],languages:["markup"],style:{color:"#808080"}},{types:["function"],style:{color:"#7dd3fc"}},{types:["class-name"],style:{color:"#0ea5e9"}},{types:["char"],style:{color:"#D16969"}}]},G={plain:{color:"#0f172a",backgroundColor:"#f1f5f9"},styles:[{types:["prolog"],style:{color:"#000080"}},{types:["comment"],style:{color:"#6A9955"}},{types:["builtin","changed","keyword","interpolation-punctuation"],style:{color:"#0c4a6e"}},{types:["number","inserted"],style:{color:"#B5CEA8"}},{types:["constant"],style:{color:"#0f172a"}},{types:["attr-name","variable"],style:{color:"#0c4a6e"}},{types:["deleted","string","attr-value","template-punctuation"],style:{color:"#64748b"}},{types:["selector"],style:{color:"#D7BA7D"}},{types:["tag"],style:{color:"#0ea5e9"}},{types:["tag"],languages:["markup"],style:{color:"#0ea5e9"}},{types:["punctuation","operator"],style:{color:"#475569"}},{types:["punctuation"],languages:["markup"],style:{color:"#808080"}},{types:["function"],style:{color:"#0e7490"}},{types:["class-name"],style:{color:"#0ea5e9"}},{types:["char"],style:{color:"#D16969"}}]},V={plain:{backgroundColor:"hsl(220, 13%, 18%)",color:"hsl(220, 14%, 71%)",textShadow:"0 1px rgba(0, 0, 0, 0.3)"},styles:[{types:["comment","prolog","cdata"],style:{color:"hsl(220, 10%, 40%)"}},{types:["doctype","punctuation","entity"],style:{color:"hsl(220, 14%, 71%)"}},{types:["attr-name","class-name","maybe-class-name","boolean","constant","number","atrule"],style:{color:"hsl(29, 54%, 61%)"}},{types:["keyword"],style:{color:"hsl(286, 60%, 67%)"}},{types:["property","tag","symbol","deleted","important"],style:{color:"hsl(355, 65%, 65%)"}},{types:["selector","string","char","builtin","inserted","regex","attr-value"],style:{color:"hsl(95, 38%, 62%)"}},{types:["variable","operator","function"],style:{color:"hsl(207, 82%, 66%)"}},{types:["url"],style:{color:"hsl(187, 47%, 55%)"}},{types:["deleted"],style:{textDecorationLine:"line-through"}},{types:["inserted"],style:{textDecorationLine:"underline"}},{types:["italic"],style:{fontStyle:"italic"}},{types:["important","bold"],style:{fontWeight:"bold"}},{types:["important"],style:{color:"hsl(220, 14%, 71%)"}}]},W={plain:{backgroundColor:"hsl(230, 1%, 98%)",color:"hsl(230, 8%, 24%)"},styles:[{types:["comment","prolog","cdata"],style:{color:"hsl(230, 4%, 64%)"}},{types:["doctype","punctuation","entity"],style:{color:"hsl(230, 8%, 24%)"}},{types:["attr-name","class-name","boolean","constant","number","atrule"],style:{color:"hsl(35, 99%, 36%)"}},{types:["keyword"],style:{color:"hsl(301, 63%, 40%)"}},{types:["property","tag","symbol","deleted","important"],style:{color:"hsl(5, 74%, 59%)"}},{types:["selector","string","char","builtin","inserted","regex","attr-value","punctuation"],style:{color:"hsl(119, 34%, 47%)"}},{types:["variable","operator","function"],style:{color:"hsl(221, 87%, 60%)"}},{types:["url"],style:{color:"hsl(198, 99%, 37%)"}},{types:["deleted"],style:{textDecorationLine:"line-through"}},{types:["inserted"],style:{textDecorationLine:"underline"}},{types:["italic"],style:{fontStyle:"italic"}},{types:["important","bold"],style:{fontWeight:"bold"}},{types:["important"],style:{color:"hsl(230, 8%, 24%)"}}]},Q=(e,t)=>{const{plain:n}=e,r=e.styles.reduce(((e,n)=>{const{languages:r,style:a}=n;return r&&!r.includes(t)||n.types.forEach((t=>{const n=S(S({},e[t]),a);e[t]=n})),e}),{});return r.root=n,r.plain=_(S({},n),{backgroundColor:void 0}),r},K=/\r\n|\r|\n/,Y=e=>{0===e.length?e.push({types:["plain"],content:"\n",empty:!0}):1===e.length&&""===e[0].content&&(e[0].content="\n",e[0].empty=!0)},Z=(e,t)=>{const n=e.length;return n>0&&e[n-1]===t?e:e.concat(t)},X=e=>{const t=[[]],n=[e],r=[0],a=[e.length];let o=0,i=0,s=[];const l=[s];for(;i>-1;){for(;(o=r[i]++)<a[i];){let e,c=t[i];const u=n[i][o];if("string"==typeof u?(c=i>0?c:["plain"],e=u):(c=Z(c,u.type),u.alias&&(c=Z(c,u.alias)),e=u.content),"string"!=typeof e){i++,t.push(c),n.push(e),r.push(0),a.push(e.length);continue}const d=e.split(K),p=d.length;s.push({types:c,content:d[0]});for(let t=1;t<p;t++)Y(s),l.push(s=[]),s.push({types:c,content:d[t]})}i--,t.pop(),n.pop(),r.pop(),a.pop()}return Y(s),l},J=({children:e,language:t,code:n,theme:r,prism:a})=>{const o=t.toLowerCase(),i=((e,t)=>{const[n,r]=(0,u.useState)(Q(t,e)),a=(0,u.useRef)(),o=(0,u.useRef)();return(0,u.useEffect)((()=>{t===a.current&&e===o.current||(a.current=t,o.current=e,r(Q(t,e)))}),[e,t]),n})(o,r),s=(e=>(0,u.useCallback)((t=>{var n=t,{className:r,style:a,line:o}=n,i=E(n,["className","style","line"]);const s=_(S({},i),{className:(0,d.A)("token-line",r)});return"object"==typeof e&&"plain"in e&&(s.style=e.plain),"object"==typeof a&&(s.style=S(S({},s.style||{}),a)),s}),[e]))(i),l=(e=>{const t=(0,u.useCallback)((({types:t,empty:n})=>{if(null!=e)return 1===t.length&&"plain"===t[0]?null!=n?{display:"inline-block"}:void 0:1===t.length&&null!=n?e[t[0]]:Object.assign(null!=n?{display:"inline-block"}:{},...t.map((t=>e[t])))}),[e]);return(0,u.useCallback)((e=>{var n=e,{token:r,className:a,style:o}=n,i=E(n,["token","className","style"]);const s=_(S({},i),{className:(0,d.A)("token",...r.types,a),children:r.content,style:t(r)});return null!=o&&(s.style=S(S({},s.style||{}),o)),s}),[t])})(i),c=(({prism:e,code:t,grammar:n,language:r})=>{const a=(0,u.useRef)(e);return(0,u.useMemo)((()=>{if(null==n)return X([t]);const e={code:t,grammar:n,language:r,tokens:[]};return a.current.hooks.run("before-tokenize",e),e.tokens=a.current.tokenize(t,n),a.current.hooks.run("after-tokenize",e),X(e.tokens)}),[t,n,r])})({prism:a,language:o,code:n,grammar:a.languages[o]});return e({tokens:c,className:`prism-code language-${o}`,style:null!=i?i.root:{},getLineProps:s,getTokenProps:l})},ee=e=>(0,u.createElement)(J,_(S({},e),{prism:e.prism||A,theme:e.theme||q,code:e.code,language:e.language}))},1561:(e,t,n)=>{"use strict";n.d(t,{A:()=>o});var r=!0,a="Invariant failed";function o(e,t){if(!e){if(r)throw new Error(a);var n="function"==typeof t?t():t,o=n?"".concat(a,": ").concat(n):a;throw new Error(o)}}},2654:e=>{"use strict";e.exports={}},4054:e=>{"use strict";e.exports=JSON.parse('{"/copacetic/website/-054":{"__comp":"5e95c892","__context":{"plugin":"804a5934"}},"/copacetic/website/next-083":{"__comp":"a7bd4aaa","version":"935f2afb"},"/copacetic/website/next-116":{"__comp":"a94703ab"},"/copacetic/website/next-d4d":{"__comp":"17896441","content":"a09c2993"},"/copacetic/website/next/best-practices-355":{"__comp":"17896441","content":"e4d3cddf"},"/copacetic/website/next/code-of-conduct-1d7":{"__comp":"17896441","content":"dea0f9ea"},"/copacetic/website/next/contributing-ffb":{"__comp":"17896441","content":"4d54d076"},"/copacetic/website/next/custom-address-3bc":{"__comp":"17896441","content":"27cf52aa"},"/copacetic/website/next/design-4ec":{"__comp":"17896441","content":"0aeccac2"},"/copacetic/website/next/development-tips-8b3":{"__comp":"17896441","content":"baa9408d"},"/copacetic/website/next/faq-037":{"__comp":"17896441","content":"0480b142"},"/copacetic/website/next/github-action-e36":{"__comp":"17896441","content":"14aa7e32"},"/copacetic/website/next/installation-9c7":{"__comp":"17896441","content":"3b8c55ea"},"/copacetic/website/next/maintainer-guidelines-43d":{"__comp":"17896441","content":"282f11b9"},"/copacetic/website/next/output-335":{"__comp":"17896441","content":"e8c0720c"},"/copacetic/website/next/quick-start-77d":{"__comp":"17896441","content":"72e14192"},"/copacetic/website/next/release-eff":{"__comp":"17896441","content":"05a474a1"},"/copacetic/website/next/scanner-plugins-89e":{"__comp":"17896441","content":"bfa18532"},"/copacetic/website/next/troubleshooting-95a":{"__comp":"17896441","content":"9d9f8394"},"/copacetic/website/v0.1.x-855":{"__comp":"a7bd4aaa","version":"c5934ffc"},"/copacetic/website/v0.1.x-8fd":{"__comp":"a94703ab"},"/copacetic/website/v0.1.x-602":{"__comp":"17896441","content":"d2339658"},"/copacetic/website/v0.1.x/code-of-conduct-334":{"__comp":"17896441","content":"bab6f5ab"},"/copacetic/website/v0.1.x/contributing-3a5":{"__comp":"17896441","content":"ea9eabff"},"/copacetic/website/v0.1.x/design-338":{"__comp":"17896441","content":"0a7bb60d"},"/copacetic/website/v0.1.x/faq-0bd":{"__comp":"17896441","content":"053b5658"},"/copacetic/website/v0.1.x/installation-125":{"__comp":"17896441","content":"2133a534"},"/copacetic/website/v0.1.x/quick-start-be0":{"__comp":"17896441","content":"6173fde8"},"/copacetic/website/v0.2.x-8db":{"__comp":"a7bd4aaa","version":"299b5310"},"/copacetic/website/v0.2.x-0cc":{"__comp":"a94703ab"},"/copacetic/website/v0.2.x-3b5":{"__comp":"17896441","content":"d9461b6c"},"/copacetic/website/v0.2.x/code-of-conduct-017":{"__comp":"17896441","content":"b407a98a"},"/copacetic/website/v0.2.x/contributing-058":{"__comp":"17896441","content":"fcc13998"},"/copacetic/website/v0.2.x/design-2ed":{"__comp":"17896441","content":"4cb9f763"},"/copacetic/website/v0.2.x/faq-8a1":{"__comp":"17896441","content":"934782ba"},"/copacetic/website/v0.2.x/installation-3e5":{"__comp":"17896441","content":"86b9c768"},"/copacetic/website/v0.2.x/quick-start-5d1":{"__comp":"17896441","content":"0c380bb3"},"/copacetic/website/v0.3.x-ea4":{"__comp":"a7bd4aaa","version":"6bd0979b"},"/copacetic/website/v0.3.x-9b9":{"__comp":"a94703ab"},"/copacetic/website/v0.3.x-13c":{"__comp":"17896441","content":"57b2432c"},"/copacetic/website/v0.3.x/code-of-conduct-112":{"__comp":"17896441","content":"89eebbd3"},"/copacetic/website/v0.3.x/contributing-f42":{"__comp":"17896441","content":"7e72de0f"},"/copacetic/website/v0.3.x/design-7a6":{"__comp":"17896441","content":"9f65b58a"},"/copacetic/website/v0.3.x/faq-2d3":{"__comp":"17896441","content":"2a636ef6"},"/copacetic/website/v0.3.x/installation-888":{"__comp":"17896441","content":"b5d5e16b"},"/copacetic/website/v0.3.x/quick-start-f27":{"__comp":"17896441","content":"f9459e94"},"/copacetic/website/v0.4.x-72c":{"__comp":"a7bd4aaa","version":"c698fe77"},"/copacetic/website/v0.4.x-48a":{"__comp":"a94703ab"},"/copacetic/website/v0.4.x-5ee":{"__comp":"17896441","content":"22539a87"},"/copacetic/website/v0.4.x/code-of-conduct-6a2":{"__comp":"17896441","content":"c12dc9fd"},"/copacetic/website/v0.4.x/contributing-722":{"__comp":"17896441","content":"48ae5635"},"/copacetic/website/v0.4.x/design-fbd":{"__comp":"17896441","content":"c13ba925"},"/copacetic/website/v0.4.x/faq-d1c":{"__comp":"17896441","content":"b9421f89"},"/copacetic/website/v0.4.x/github-action-41b":{"__comp":"17896441","content":"1b7d20b2"},"/copacetic/website/v0.4.x/installation-1df":{"__comp":"17896441","content":"3fcb412e"},"/copacetic/website/v0.4.x/quick-start-558":{"__comp":"17896441","content":"9e350ec0"},"/copacetic/website/v0.4.x/release-828":{"__comp":"17896441","content":"f3bd9382"},"/copacetic/website/v0.4.x/troubleshooting-71a":{"__comp":"17896441","content":"040fbf97"},"/copacetic/website/v0.5.x-dd7":{"__comp":"a7bd4aaa","version":"efdb11b6"},"/copacetic/website/v0.5.x-1f5":{"__comp":"a94703ab"},"/copacetic/website/v0.5.x-b4f":{"__comp":"17896441","content":"c66bbf8a"},"/copacetic/website/v0.5.x/code-of-conduct-836":{"__comp":"17896441","content":"11d58a17"},"/copacetic/website/v0.5.x/contributing-386":{"__comp":"17896441","content":"0d9a188d"},"/copacetic/website/v0.5.x/design-4c6":{"__comp":"17896441","content":"22dce6f4"},"/copacetic/website/v0.5.x/faq-f8a":{"__comp":"17896441","content":"fa2b770f"},"/copacetic/website/v0.5.x/github-action-842":{"__comp":"17896441","content":"512645f2"},"/copacetic/website/v0.5.x/installation-c90":{"__comp":"17896441","content":"c73303db"},"/copacetic/website/v0.5.x/output-60e":{"__comp":"17896441","content":"9f27d5ef"},"/copacetic/website/v0.5.x/quick-start-962":{"__comp":"17896441","content":"34f2f592"},"/copacetic/website/v0.5.x/release-85e":{"__comp":"17896441","content":"77a9ed43"},"/copacetic/website/v0.5.x/scanner-plugins-f13":{"__comp":"17896441","content":"5c8ee200"},"/copacetic/website/v0.5.x/troubleshooting-9db":{"__comp":"17896441","content":"4d7fa969"},"/copacetic/website/-589":{"__comp":"a7bd4aaa","version":"ff86e818"},"/copacetic/website/-1a4":{"__comp":"a94703ab"},"/copacetic/website/-ef0":{"__comp":"17896441","content":"17e02f37"},"/copacetic/website/best-practices-025":{"__comp":"17896441","content":"499c1190"},"/copacetic/website/code-of-conduct-73d":{"__comp":"17896441","content":"5fc6e4d2"},"/copacetic/website/contributing-62f":{"__comp":"17896441","content":"801664dc"},"/copacetic/website/custom-address-ffc":{"__comp":"17896441","content":"9b66f67b"},"/copacetic/website/design-9d5":{"__comp":"17896441","content":"a95b810a"},"/copacetic/website/development-tips-375":{"__comp":"17896441","content":"4e9dbf89"},"/copacetic/website/faq-5db":{"__comp":"17896441","content":"12042f32"},"/copacetic/website/github-action-39b":{"__comp":"17896441","content":"14b4e536"},"/copacetic/website/installation-9b0":{"__comp":"17896441","content":"7d213ce9"},"/copacetic/website/maintainer-guidelines-7d0":{"__comp":"17896441","content":"4468d236"},"/copacetic/website/output-985":{"__comp":"17896441","content":"683b919f"},"/copacetic/website/quick-start-002":{"__comp":"17896441","content":"84979900"},"/copacetic/website/release-ed0":{"__comp":"17896441","content":"98016c8b"},"/copacetic/website/scanner-plugins-5d7":{"__comp":"17896441","content":"d832efb0"},"/copacetic/website/troubleshooting-236":{"__comp":"17896441","content":"e99d2929"}}')}},e=>{e.O(0,[1869],(()=>{return t=8536,e(e.s=t);var t}));e.O()}]); \ No newline at end of file diff --git a/website/assets/js/main.991a83b1.js.LICENSE.txt b/website/assets/js/main.d9039351.js.LICENSE.txt similarity index 76% rename from website/assets/js/main.991a83b1.js.LICENSE.txt rename to website/assets/js/main.d9039351.js.LICENSE.txt index eb75d691..91dc8949 100644 --- a/website/assets/js/main.991a83b1.js.LICENSE.txt +++ b/website/assets/js/main.d9039351.js.LICENSE.txt @@ -1,15 +1,22 @@ -/* -object-assign -(c) Sindre Sorhus -@license MIT -*/ - /* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress * @license MIT */ +/*! Bundled license information: + +prismjs/prism.js: + (** + * Prism: Lightweight, robust, elegant syntax highlighting + * + * @license MIT <https://opensource.org/licenses/MIT> + * @author Lea Verou <https://lea.verou.me> + * @namespace + * @public + *) +*/ + /** * @license React - * use-sync-external-store-shim.production.min.js + * react-dom.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * @@ -18,16 +25,8 @@ object-assign */ /** - * Prism: Lightweight, robust, elegant syntax highlighting - * - * @license MIT <https://opensource.org/licenses/MIT> - * @author Lea Verou <https://lea.verou.me> - * @namespace - * @public - */ - -/** @license React v0.20.2 - * scheduler.production.min.js + * @license React + * react-jsx-runtime.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * @@ -35,8 +34,9 @@ object-assign * LICENSE file in the root directory of this source tree. */ -/** @license React v16.13.1 - * react-is.production.min.js +/** + * @license React + * react.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * @@ -44,8 +44,9 @@ object-assign * LICENSE file in the root directory of this source tree. */ -/** @license React v17.0.2 - * react-dom.production.min.js +/** + * @license React + * scheduler.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * @@ -53,8 +54,8 @@ object-assign * LICENSE file in the root directory of this source tree. */ -/** @license React v17.0.2 - * react.production.min.js +/** @license React v16.13.1 + * react-is.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * diff --git a/website/assets/js/runtime~main.62889678.js b/website/assets/js/runtime~main.62889678.js new file mode 100644 index 00000000..22af92ca --- /dev/null +++ b/website/assets/js/runtime~main.62889678.js @@ -0,0 +1 @@ +(()=>{"use strict";var e,a,f,b,d,c={},t={};function r(e){var a=t[e];if(void 0!==a)return a.exports;var f=t[e]={id:e,loaded:!1,exports:{}};return c[e].call(f.exports,f,f.exports,r),f.loaded=!0,f.exports}r.m=c,r.c=t,e=[],r.O=(a,f,b,d)=>{if(!f){var c=1/0;for(i=0;i<e.length;i++){f=e[i][0],b=e[i][1],d=e[i][2];for(var t=!0,o=0;o<f.length;o++)(!1&d||c>=d)&&Object.keys(r.O).every((e=>r.O[e](f[o])))?f.splice(o--,1):(t=!1,d<c&&(c=d));if(t){e.splice(i--,1);var n=b();void 0!==n&&(a=n)}}return a}d=d||0;for(var i=e.length;i>0&&e[i-1][2]>d;i--)e[i]=e[i-1];e[i]=[f,b,d]},r.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return r.d(a,{a:a}),a},f=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,r.t=function(e,b){if(1&b&&(e=this(e)),8&b)return e;if("object"==typeof e&&e){if(4&b&&e.__esModule)return e;if(16&b&&"function"==typeof e.then)return e}var d=Object.create(null);r.r(d);var c={};a=a||[null,f({}),f([]),f(f)];for(var t=2&b&&e;"object"==typeof t&&!~a.indexOf(t);t=f(t))Object.getOwnPropertyNames(t).forEach((a=>c[a]=()=>e[a]));return c.default=()=>e,r.d(d,c),d},r.d=(e,a)=>{for(var f in a)r.o(a,f)&&!r.o(e,f)&&Object.defineProperty(e,f,{enumerable:!0,get:a[f]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce(((a,f)=>(r.f[f](e,a),a)),[])),r.u=e=>"assets/js/"+({90:"98016c8b",105:"4d7fa969",621:"c13ba925",782:"e99d2929",864:"e4d3cddf",1157:"804a5934",1268:"d9461b6c",1439:"6173fde8",1459:"4d54d076",1508:"48ae5635",1653:"12042f32",1786:"6bd0979b",1837:"9b66f67b",1842:"299b5310",1866:"2a636ef6",1882:"0d9a188d",1909:"d2339658",1965:"0aeccac2",2150:"0c380bb3",2171:"77a9ed43",2180:"282f11b9",2193:"2133a534",2201:"3fcb412e",2338:"05a474a1",2375:"d832efb0",2676:"4468d236",2728:"c5934ffc",2814:"72e14192",2898:"f3bd9382",3033:"7d213ce9",3105:"040fbf97",3163:"499c1190",3227:"22dce6f4",3379:"9f65b58a",3409:"14aa7e32",3773:"57b2432c",3817:"9e350ec0",3831:"053b5658",3978:"b9421f89",4140:"5fc6e4d2",4269:"84979900",4364:"34f2f592",4519:"7e72de0f",4521:"683b919f",4586:"efdb11b6",4914:"c73303db",4938:"27cf52aa",5336:"4e9dbf89",5368:"fa2b770f",5713:"934782ba",5819:"801664dc",5899:"a09c2993",5911:"5c8ee200",6229:"e8c0720c",6524:"a95b810a",6613:"c66bbf8a",6615:"4cb9f763",6683:"89eebbd3",6803:"3b8c55ea",7098:"a7bd4aaa",7173:"0a7bb60d",7231:"b5d5e16b",7391:"c12dc9fd",7508:"f9459e94",7594:"17e02f37",7973:"11d58a17",8070:"0480b142",8222:"c698fe77",8293:"ff86e818",8313:"1b7d20b2",8355:"14b4e536",8399:"bfa18532",8401:"17896441",8409:"b407a98a",8442:"86b9c768",8581:"935f2afb",9013:"9d9f8394",9048:"a94703ab",9077:"fcc13998",9162:"bab6f5ab",9222:"baa9408d",9228:"dea0f9ea",9516:"512645f2",9590:"ea9eabff",9647:"5e95c892",9702:"22539a87",9727:"9f27d5ef"}[e]||e)+"."+{90:"9efc2921",105:"9b8c384b",621:"e5b48032",782:"a259dc08",864:"23b82d37",1157:"e93947f5",1268:"63467998",1439:"2d07e3f9",1459:"7f68c18d",1508:"51490778",1653:"0e78f076",1786:"ffc56042",1837:"6e821884",1842:"1a9430b9",1866:"29da1826",1882:"5b7da772",1909:"5e176ea4",1965:"02849ec8",2150:"ba7d238d",2171:"c56f8af9",2180:"66f006eb",2193:"8db371e7",2201:"5addcbea",2237:"ede9a640",2338:"a98f3ad4",2375:"70cfe285",2676:"5fa3d39e",2728:"658c31dc",2814:"98634abe",2898:"b63c2bc3",3033:"8520a379",3105:"ec4b04b2",3163:"5345b6eb",3227:"96e84ed3",3379:"37deb826",3409:"2379c39a",3773:"dcb09c4b",3817:"ad02c27c",3831:"2fa8aa35",3978:"6ce1c578",4140:"bc73c3e3",4269:"3a0cf957",4364:"8fe0bd74",4519:"7b9dfc9f",4521:"f74ca5f0",4586:"2663fe04",4914:"5731fdf2",4938:"a3987a1f",5336:"413c7098",5368:"2b296f82",5713:"2d85275c",5819:"a10c85ef",5899:"2acef99e",5911:"faac304c",6229:"4b83c6ce",6524:"dac0b4cb",6613:"9d86a839",6615:"0128fb5a",6683:"287960d1",6803:"995cc6f4",7098:"b82ee0df",7173:"bc8038bc",7231:"8d66db9d",7391:"1489a453",7508:"d7263fc2",7594:"2535a01b",7973:"6adc7a26",8070:"00de69f7",8222:"8fa65e9b",8293:"93b13f6e",8313:"f7af2c6c",8355:"64a562da",8399:"76e61599",8401:"7677fdee",8409:"2829481b",8442:"1fcb11fa",8581:"a5a28739",9013:"6a5641e3",9048:"2668bc5b",9077:"bf8a3793",9162:"0b153f31",9222:"07d2dc63",9228:"c17bbf7a",9516:"65269ba4",9590:"7a3cec22",9647:"b1f5748f",9702:"04ad19a2",9727:"f6738d54"}[e]+".js",r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),b={},d="website:",r.l=(e,a,f,c)=>{if(b[e])b[e].push(a);else{var t,o;if(void 0!==f)for(var n=document.getElementsByTagName("script"),i=0;i<n.length;i++){var u=n[i];if(u.getAttribute("src")==e||u.getAttribute("data-webpack")==d+f){t=u;break}}t||(o=!0,(t=document.createElement("script")).charset="utf-8",t.timeout=120,r.nc&&t.setAttribute("nonce",r.nc),t.setAttribute("data-webpack",d+f),t.src=e),b[e]=[a];var l=(a,f)=>{t.onerror=t.onload=null,clearTimeout(s);var d=b[e];if(delete b[e],t.parentNode&&t.parentNode.removeChild(t),d&&d.forEach((e=>e(f))),a)return a(f)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:t}),12e4);t.onerror=l.bind(null,t.onerror),t.onload=l.bind(null,t.onload),o&&document.head.appendChild(t)}},r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.p="/copacetic/website/",r.gca=function(e){return e={17896441:"8401",84979900:"4269","98016c8b":"90","4d7fa969":"105",c13ba925:"621",e99d2929:"782",e4d3cddf:"864","804a5934":"1157",d9461b6c:"1268","6173fde8":"1439","4d54d076":"1459","48ae5635":"1508","12042f32":"1653","6bd0979b":"1786","9b66f67b":"1837","299b5310":"1842","2a636ef6":"1866","0d9a188d":"1882",d2339658:"1909","0aeccac2":"1965","0c380bb3":"2150","77a9ed43":"2171","282f11b9":"2180","2133a534":"2193","3fcb412e":"2201","05a474a1":"2338",d832efb0:"2375","4468d236":"2676",c5934ffc:"2728","72e14192":"2814",f3bd9382:"2898","7d213ce9":"3033","040fbf97":"3105","499c1190":"3163","22dce6f4":"3227","9f65b58a":"3379","14aa7e32":"3409","57b2432c":"3773","9e350ec0":"3817","053b5658":"3831",b9421f89:"3978","5fc6e4d2":"4140","34f2f592":"4364","7e72de0f":"4519","683b919f":"4521",efdb11b6:"4586",c73303db:"4914","27cf52aa":"4938","4e9dbf89":"5336",fa2b770f:"5368","934782ba":"5713","801664dc":"5819",a09c2993:"5899","5c8ee200":"5911",e8c0720c:"6229",a95b810a:"6524",c66bbf8a:"6613","4cb9f763":"6615","89eebbd3":"6683","3b8c55ea":"6803",a7bd4aaa:"7098","0a7bb60d":"7173",b5d5e16b:"7231",c12dc9fd:"7391",f9459e94:"7508","17e02f37":"7594","11d58a17":"7973","0480b142":"8070",c698fe77:"8222",ff86e818:"8293","1b7d20b2":"8313","14b4e536":"8355",bfa18532:"8399",b407a98a:"8409","86b9c768":"8442","935f2afb":"8581","9d9f8394":"9013",a94703ab:"9048",fcc13998:"9077",bab6f5ab:"9162",baa9408d:"9222",dea0f9ea:"9228","512645f2":"9516",ea9eabff:"9590","5e95c892":"9647","22539a87":"9702","9f27d5ef":"9727"}[e]||e,r.p+r.u(e)},(()=>{var e={5354:0,1869:0};r.f.j=(a,f)=>{var b=r.o(e,a)?e[a]:void 0;if(0!==b)if(b)f.push(b[2]);else if(/^(1869|5354)$/.test(a))e[a]=0;else{var d=new Promise(((f,d)=>b=e[a]=[f,d]));f.push(b[2]=d);var c=r.p+r.u(a),t=new Error;r.l(c,(f=>{if(r.o(e,a)&&(0!==(b=e[a])&&(e[a]=void 0),b)){var d=f&&("load"===f.type?"missing":f.type),c=f&&f.target&&f.target.src;t.message="Loading chunk "+a+" failed.\n("+d+": "+c+")",t.name="ChunkLoadError",t.type=d,t.request=c,b[1](t)}}),"chunk-"+a,a)}},r.O.j=a=>0===e[a];var a=(a,f)=>{var b,d,c=f[0],t=f[1],o=f[2],n=0;if(c.some((a=>0!==e[a]))){for(b in t)r.o(t,b)&&(r.m[b]=t[b]);if(o)var i=o(r)}for(a&&a(f);n<c.length;n++)d=c[n],r.o(e,d)&&e[d]&&e[d][0](),e[d]=0;return r.O(i)},f=self.webpackChunkwebsite=self.webpackChunkwebsite||[];f.forEach(a.bind(null,0)),f.push=a.bind(null,f.push.bind(f))})()})(); \ No newline at end of file diff --git a/website/assets/js/runtime~main.9639751f.js b/website/assets/js/runtime~main.9639751f.js deleted file mode 100644 index 9bc28b34..00000000 --- a/website/assets/js/runtime~main.9639751f.js +++ /dev/null @@ -1 +0,0 @@ -(()=>{"use strict";var e,a,f,b,d,c={},t={};function r(e){var a=t[e];if(void 0!==a)return a.exports;var f=t[e]={id:e,loaded:!1,exports:{}};return c[e].call(f.exports,f,f.exports,r),f.loaded=!0,f.exports}r.m=c,r.c=t,e=[],r.O=(a,f,b,d)=>{if(!f){var c=1/0;for(i=0;i<e.length;i++){f=e[i][0],b=e[i][1],d=e[i][2];for(var t=!0,o=0;o<f.length;o++)(!1&d||c>=d)&&Object.keys(r.O).every((e=>r.O[e](f[o])))?f.splice(o--,1):(t=!1,d<c&&(c=d));if(t){e.splice(i--,1);var n=b();void 0!==n&&(a=n)}}return a}d=d||0;for(var i=e.length;i>0&&e[i-1][2]>d;i--)e[i]=e[i-1];e[i]=[f,b,d]},r.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return r.d(a,{a:a}),a},f=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,r.t=function(e,b){if(1&b&&(e=this(e)),8&b)return e;if("object"==typeof e&&e){if(4&b&&e.__esModule)return e;if(16&b&&"function"==typeof e.then)return e}var d=Object.create(null);r.r(d);var c={};a=a||[null,f({}),f([]),f(f)];for(var t=2&b&&e;"object"==typeof t&&!~a.indexOf(t);t=f(t))Object.getOwnPropertyNames(t).forEach((a=>c[a]=()=>e[a]));return c.default=()=>e,r.d(d,c),d},r.d=(e,a)=>{for(var f in a)r.o(a,f)&&!r.o(e,f)&&Object.defineProperty(e,f,{enumerable:!0,get:a[f]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce(((a,f)=>(r.f[f](e,a),a)),[])),r.u=e=>"assets/js/"+({4:"d9461b6c",8:"17e02f37",53:"935f2afb",138:"27cf52aa",154:"11d58a17",240:"7d213ce9",267:"86b9c768",335:"053b5658",414:"0aeccac2",494:"f3bd9382",568:"c66bbf8a",601:"ff86e818",722:"7e72de0f",836:"0480b142",1031:"98016c8b",1057:"5c8ee200",1087:"fcc13998",1150:"801664dc",1201:"040fbf97",1220:"f9459e94",1802:"12042f32",1865:"0d9a188d",2072:"282f11b9",2088:"4cb9f763",2279:"512645f2",2676:"1b7d20b2",2702:"2a636ef6",2747:"4e9dbf89",2932:"683b919f",2972:"e99d2929",3186:"14b4e536",3200:"bab6f5ab",3217:"3b8c55ea",3242:"3fcb412e",3296:"e8c0720c",3480:"9f65b58a",3602:"5fc6e4d2",3665:"bfa18532",3891:"499c1190",4073:"05a474a1",4111:"efdb11b6",4128:"a09c2993",4789:"84979900",4925:"b407a98a",4993:"c698fe77",5082:"c73303db",5145:"299b5310",5692:"a95b810a",5737:"2133a534",5897:"14aa7e32",5964:"22539a87",6022:"9b66f67b",6148:"fa2b770f",6198:"4468d236",6325:"c12dc9fd",6352:"dea0f9ea",6380:"77a9ed43",6571:"baa9408d",6705:"c5934ffc",6927:"9f27d5ef",6948:"0c380bb3",7059:"b5d5e16b",7071:"804a5934",7080:"4d54d076",7114:"b9421f89",7239:"72e14192",7252:"22dce6f4",7262:"9e350ec0",7451:"6173fde8",7835:"ea9eabff",7845:"c13ba925",7918:"17896441",8163:"934782ba",8202:"e4d3cddf",8644:"d2339658",8765:"57b2432c",8838:"6bd0979b",9070:"d832efb0",9212:"48ae5635",9269:"34f2f592",9360:"9d9f8394",9514:"1be78505",9691:"0a7bb60d",9848:"4d7fa969",9925:"89eebbd3"}[e]||e)+"."+{4:"cfb2e633",8:"860083f8",53:"c1a00716",138:"cace1e2c",154:"31780db1",240:"26e02b59",267:"6a1c76ae",335:"6494c787",414:"920f5236",494:"cfea1d0a",568:"356f0a06",601:"6052e0ec",722:"46d6e17a",836:"7aa8457b",1031:"a456db16",1057:"f51d96f0",1087:"5e4b93fa",1150:"e764cf5c",1201:"7c02e0ce",1220:"d4932d2b",1802:"aeeebb3d",1865:"ace05380",2072:"a334c6b0",2088:"c7fd5bba",2279:"86b67e45",2676:"79e8a116",2702:"df8e9b9f",2747:"f2c2b4d8",2932:"5b96ddd9",2972:"85b262dd",3186:"fee906c1",3200:"1dcf3cff",3217:"034e3387",3242:"58d24b04",3296:"d9edccdb",3480:"3374a93f",3602:"b1cefb99",3665:"f8fc985e",3891:"18188f88",4073:"af8883e8",4111:"1df4609f",4128:"5da876e3",4789:"d30448bf",4925:"7eeb3207",4972:"9374abde",4993:"2ced926f",5082:"a8986184",5145:"a01d158f",5692:"3050b258",5737:"f95c97ab",5897:"4837c057",5964:"bed468c8",6022:"7d9c27c7",6148:"684fae56",6198:"b180c32c",6325:"2e134505",6352:"d742361f",6380:"cdfe4c4f",6571:"c3109501",6705:"0e15e6c8",6927:"f416e8f9",6948:"90242cf8",7059:"0cb143ee",7071:"e1bcf12f",7080:"05bf40fc",7114:"2697740e",7239:"4b528064",7252:"61bd0f54",7262:"c4af5b3c",7451:"55493801",7835:"6d2cdf3d",7845:"cfea7e03",7918:"bd1b9c94",8163:"c1eb8bb2",8202:"34856fe1",8644:"3f5cadcb",8765:"ba8a447b",8838:"2824c577",9070:"23e6d38e",9212:"cd5cfcb1",9269:"0c35ff01",9360:"4bdf9bac",9514:"61acc940",9691:"238534bf",9848:"a26a2980",9925:"f1022e5a"}[e]+".js",r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),b={},d="website:",r.l=(e,a,f,c)=>{if(b[e])b[e].push(a);else{var t,o;if(void 0!==f)for(var n=document.getElementsByTagName("script"),i=0;i<n.length;i++){var u=n[i];if(u.getAttribute("src")==e||u.getAttribute("data-webpack")==d+f){t=u;break}}t||(o=!0,(t=document.createElement("script")).charset="utf-8",t.timeout=120,r.nc&&t.setAttribute("nonce",r.nc),t.setAttribute("data-webpack",d+f),t.src=e),b[e]=[a];var l=(a,f)=>{t.onerror=t.onload=null,clearTimeout(s);var d=b[e];if(delete b[e],t.parentNode&&t.parentNode.removeChild(t),d&&d.forEach((e=>e(f))),a)return a(f)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:t}),12e4);t.onerror=l.bind(null,t.onerror),t.onload=l.bind(null,t.onload),o&&document.head.appendChild(t)}},r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.p="/copacetic/website/",r.gca=function(e){return e={17896441:"7918",84979900:"4789",d9461b6c:"4","17e02f37":"8","935f2afb":"53","27cf52aa":"138","11d58a17":"154","7d213ce9":"240","86b9c768":"267","053b5658":"335","0aeccac2":"414",f3bd9382:"494",c66bbf8a:"568",ff86e818:"601","7e72de0f":"722","0480b142":"836","98016c8b":"1031","5c8ee200":"1057",fcc13998:"1087","801664dc":"1150","040fbf97":"1201",f9459e94:"1220","12042f32":"1802","0d9a188d":"1865","282f11b9":"2072","4cb9f763":"2088","512645f2":"2279","1b7d20b2":"2676","2a636ef6":"2702","4e9dbf89":"2747","683b919f":"2932",e99d2929:"2972","14b4e536":"3186",bab6f5ab:"3200","3b8c55ea":"3217","3fcb412e":"3242",e8c0720c:"3296","9f65b58a":"3480","5fc6e4d2":"3602",bfa18532:"3665","499c1190":"3891","05a474a1":"4073",efdb11b6:"4111",a09c2993:"4128",b407a98a:"4925",c698fe77:"4993",c73303db:"5082","299b5310":"5145",a95b810a:"5692","2133a534":"5737","14aa7e32":"5897","22539a87":"5964","9b66f67b":"6022",fa2b770f:"6148","4468d236":"6198",c12dc9fd:"6325",dea0f9ea:"6352","77a9ed43":"6380",baa9408d:"6571",c5934ffc:"6705","9f27d5ef":"6927","0c380bb3":"6948",b5d5e16b:"7059","804a5934":"7071","4d54d076":"7080",b9421f89:"7114","72e14192":"7239","22dce6f4":"7252","9e350ec0":"7262","6173fde8":"7451",ea9eabff:"7835",c13ba925:"7845","934782ba":"8163",e4d3cddf:"8202",d2339658:"8644","57b2432c":"8765","6bd0979b":"8838",d832efb0:"9070","48ae5635":"9212","34f2f592":"9269","9d9f8394":"9360","1be78505":"9514","0a7bb60d":"9691","4d7fa969":"9848","89eebbd3":"9925"}[e]||e,r.p+r.u(e)},(()=>{var e={1303:0,532:0};r.f.j=(a,f)=>{var b=r.o(e,a)?e[a]:void 0;if(0!==b)if(b)f.push(b[2]);else if(/^(1303|532)$/.test(a))e[a]=0;else{var d=new Promise(((f,d)=>b=e[a]=[f,d]));f.push(b[2]=d);var c=r.p+r.u(a),t=new Error;r.l(c,(f=>{if(r.o(e,a)&&(0!==(b=e[a])&&(e[a]=void 0),b)){var d=f&&("load"===f.type?"missing":f.type),c=f&&f.target&&f.target.src;t.message="Loading chunk "+a+" failed.\n("+d+": "+c+")",t.name="ChunkLoadError",t.type=d,t.request=c,b[1](t)}}),"chunk-"+a,a)}},r.O.j=a=>0===e[a];var a=(a,f)=>{var b,d,c=f[0],t=f[1],o=f[2],n=0;if(c.some((a=>0!==e[a]))){for(b in t)r.o(t,b)&&(r.m[b]=t[b]);if(o)var i=o(r)}for(a&&a(f);n<c.length;n++)d=c[n],r.o(e,d)&&e[d]&&e[d][0](),e[d]=0;return r.O(i)},f=self.webpackChunkwebsite=self.webpackChunkwebsite||[];f.forEach(a.bind(null,0)),f.push=a.bind(null,f.push.bind(f))})()})(); \ No newline at end of file diff --git a/website/best-practices.html b/website/best-practices.html index efd59426..872348e8 100644 --- a/website/best-practices.html +++ b/website/best-practices.html @@ -1,19 +1,26 @@ <!doctype html> -<html lang="en" dir="ltr" class="docs-wrapper docs-doc-page docs-version-v0.6.x plugin-docs plugin-id-default docs-doc-id-best-practices" data-has-hydrated="false"> +<html lang="en" dir="ltr" class="docs-wrapper plugin-docs plugin-id-default docs-version-v0.6.x docs-doc-page docs-doc-id-best-practices" data-has-hydrated="false"> <head> <meta charset="UTF-8"> -<meta name="generator" content="Docusaurus v2.4.3"> -<title data-rh="true">Tagging Guidelines | Copacetic + +Tagging Guidelines | Copacetic - - - + + + -
-
Version: v0.6.x

Tagging Guidelines

There are some patterns and practices you may want to consider when using Copa to patch images. Remember that these are suggestions that may not fit into your workflow, but we think that staying as close as possible to these practices offers the best experience with Copa.

Patch from Unmodified image

When patching vulnerabilities in an image, it helps to always work from the initial unmodified image. For example, say you have an image tagged nginx:1.24.0 that contains a vulnerability. You run Copa to patch the image and produce a new image tagged nginx:1.24.0-1. Then if another vulnerability shows up in your nginx:1.24.0-1 image, you should again patch from the unmodified nginx:1.24.0 image. This will help prevent the buildup of patch layers (discarding subsequent patch layers is a potential future enhancement).

Tagging

There are a couple possible patterns that you could follow when tagging patched images.

Static Incremental Tags

The first approach you could take is incrementing a number you append to the end of an image tag. For example, if you have an image tagged nginx:1.24.0, following patches would be tagged as nginx:1.24.0-1, nginx:1.24.0-2, nginx:1.24.0-3, and so on.

With this pattern you are always explicitly aware of the patch state of the image you are using. The downside is that dependabot is currently unable bump to patched images from unmodified images or bump from one patched image to the next.

Dynamic Tags

Another option is a static tag that is continually reused as new patches are applied. For example, you could have an initial unmodified image that you've tagged nginx:1.24.0-0 (in this case the -0 at the end helps identify the base unpatched image). All following patched images are then tagged as nginx:1.24.0. You then know that the one tagged image always has the latest patches applied.

This method makes it easy to continually consume the latest patched version of an image, but does contain some tradeoffs. First is that without pinning, image digests could change causing unpredictable behavior. Secondly, if an ImagePullPolicy is set to IfNotPresent, newly patched images would not be pulled since the tag hasn't changed.

- - +
Version: v0.6.x

Tagging Guidelines

There are some patterns and practices you may want to consider when using Copa to patch images. Remember that these are suggestions that may not fit into your workflow, but we think that staying as close as possible to these practices offers the best experience with Copa.

+

Patch from Unmodified image

+

When patching vulnerabilities in an image, it helps to always work from the initial unmodified image. For example, say you have an image tagged nginx:1.24.0 that contains a vulnerability. You run Copa to patch the image and produce a new image tagged nginx:1.24.0-1. Then if another vulnerability shows up in your nginx:1.24.0-1 image, you should again patch from the unmodified nginx:1.24.0 image. This will help prevent the buildup of patch layers (discarding subsequent patch layers is a potential future enhancement).

+

Tagging

+

There are a couple possible patterns that you could follow when tagging patched images.

+

Static Incremental Tags

+

The first approach you could take is incrementing a number you append to the end of an image tag. For example, if you have an image tagged nginx:1.24.0, following patches would be tagged as nginx:1.24.0-1, nginx:1.24.0-2, nginx:1.24.0-3, and so on.

+

With this pattern you are always explicitly aware of the patch state of the image you are using. The downside is that dependabot is currently unable bump to patched images from unmodified images or bump from one patched image to the next.

+

Dynamic Tags

+

Another option is a static tag that is continually reused as new patches are applied. For example, you could have an initial unmodified image that you've tagged nginx:1.24.0-0 (in this case the -0 at the end helps identify the base unpatched image). All following patched images are then tagged as nginx:1.24.0. You then know that the one tagged image always has the latest patches applied.

+

This method makes it easy to continually consume the latest patched version of an image, but does contain some tradeoffs. First is that without pinning, image digests could change causing unpredictable behavior. Secondly, if an ImagePullPolicy is set to IfNotPresent, newly patched images would not be pulled since the tag hasn't changed.

\ No newline at end of file diff --git a/website/code-of-conduct.html b/website/code-of-conduct.html index b81f4004..d6fdb8f0 100644 --- a/website/code-of-conduct.html +++ b/website/code-of-conduct.html @@ -1,68 +1,111 @@ - + - -Code of Conduct | Copacetic + +Code of Conduct | Copacetic - - - + + + -
-
Version: v0.6.x

Contributor Covenant Code of Conduct

Our Pledge

We as members, contributors, and leaders pledge to make participation in our +

Version: v0.6.x

Contributor Covenant Code of Conduct

+

Our Pledge

+

We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual -identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community.

Our Standards

Examples of behavior that contributes to a positive environment for our -community include:

  • Demonstrating empathy and kindness toward other people
  • Being respectful of differing opinions, viewpoints, and experiences
  • Giving and gracefully accepting constructive feedback
  • Accepting responsibility and apologizing to those affected by our mistakes, -and learning from the experience
  • Focusing on what is best not just for us as individuals, but for the overall -community

Examples of unacceptable behavior include:

  • The use of sexualized language or imagery, and sexual attention or advances of -any kind
  • Trolling, insulting or derogatory comments, and personal or political attacks
  • Public or private harassment
  • Publishing others' private information, such as a physical or email address, -without their explicit permission
  • Other conduct which could reasonably be considered inappropriate in a -professional setting

Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of +identity and orientation.

+

We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community.

+

Our Standards

+

Examples of behavior that contributes to a positive environment for our +community include:

+
    +
  • Demonstrating empathy and kindness toward other people
  • +
  • Being respectful of differing opinions, viewpoints, and experiences
  • +
  • Giving and gracefully accepting constructive feedback
  • +
  • Accepting responsibility and apologizing to those affected by our mistakes, +and learning from the experience
  • +
  • Focusing on what is best not just for us as individuals, but for the overall +community
  • +
+

Examples of unacceptable behavior include:

+
    +
  • The use of sexualized language or imagery, and sexual attention or advances of +any kind
  • +
  • Trolling, insulting or derogatory comments, and personal or political attacks
  • +
  • Public or private harassment
  • +
  • Publishing others' private information, such as a physical or email address, +without their explicit permission
  • +
  • Other conduct which could reasonably be considered inappropriate in a +professional setting
  • +
+

Enforcement Responsibilities

+

Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, -or harmful.

Community leaders have the right and responsibility to remove, edit, or reject +or harmful.

+

Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate.

Scope

This Code of Conduct applies within all community spaces, and also applies when +decisions when appropriate.

+

Scope

+

This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed -representative at an online or offline event.

Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be +representative at an online or offline event.

+

Enforcement

+

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at project-copacetic@googlegroups.com. -All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the -reporter of any incident.

Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct:

1. Correction

Community Impact: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community.

Consequence: A private, written warning from community leaders, providing +All complaints will be reviewed and investigated promptly and fairly.

+

All community leaders are obligated to respect the privacy and security of the +reporter of any incident.

+

Enforcement Guidelines

+

Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct:

+

1. Correction

+

Community Impact: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community.

+

Consequence: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested.

2. Warning

Community Impact: A violation through a single incident or series of -actions.

Consequence: A warning with consequences for continued behavior. No +behavior was inappropriate. A public apology may be requested.

+

2. Warning

+

Community Impact: A violation through a single incident or series of +actions.

+

Consequence: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent -ban.

3. Temporary Ban

Community Impact: A serious violation of community standards, including -sustained inappropriate behavior.

Consequence: A temporary ban from any sort of interaction or public +ban.

+

3. Temporary Ban

+

Community Impact: A serious violation of community standards, including +sustained inappropriate behavior.

+

Consequence: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban.

4. Permanent Ban

Community Impact: Demonstrating a pattern of violation of community +Violating these terms may lead to a permanent ban.

+

4. Permanent Ban

+

Community Impact: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals.

Consequence: A permanent ban from any sort of public interaction within the -community.

Attribution

This Code of Conduct is adapted from the Contributor Covenant, +individual, or aggression toward or disparagement of classes of individuals.

+

Consequence: A permanent ban from any sort of public interaction within the +community.

+

Attribution

+

This Code of Conduct is adapted from the Contributor Covenant, version 2.1, available at -https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

Community Impact Guidelines were inspired by -Mozilla's code of conduct enforcement ladder.

For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

+

Community Impact Guidelines were inspired by +Mozilla's code of conduct enforcement ladder.

+

For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations.

- - +https://www.contributor-covenant.org/translations.

\ No newline at end of file diff --git a/website/contributing.html b/website/contributing.html index e1068f7c..27b862ff 100644 --- a/website/contributing.html +++ b/website/contributing.html @@ -1,33 +1,127 @@ - + - -Contributing | Copacetic + +Contributing | Copacetic - - - + + + -
-
Version: v0.6.x

Contributing

Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.

Bi-Weekly Community Meeting

A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join here

Slack

To discuss issues with Copa, features, or development, you can join the #copa channel on the OCI Slack.

Contributing Issues

Before opening any new issues, please search our existing GitHub issues to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead.

When opening an issue, please select the most appropriate template for what you're contributing:

  • Bug Report: If you would like to report the project or tool behaving in unexpected ways.
  • Documentation Improvement: If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content.
  • Request: If you have a feature request, suggestion, or a even a design proposal to review.
  • Question: If you would like to ask the maintainers a question about the project.

Contributing Code

Getting Started

Follow the instructions to either:

For an overview of the project components, refer to the copa design document.

Visual Studio Code Development Container

VSCode supports development in a containerized environment through its Remote - Container extension. This folder provides a development container which encapsulates the dependencies specified in the instructions to build and run copa.

Prerequisites

  1. Docker

    For Windows users, enabling WSL2 back-end integration with Docker is recommended.

  2. Visual Studio Code
  3. Visual Studio Code Remote - Containers extension

⚠ If running via Docker Desktop for Windows

Note that the mounted workspace files appear owned by root in the dev container, which will cause git commands to fail with a fatal: detected dubious ownership in a repository error due to safe.directory checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the vscode user:

sudo chown -R vscode:vscode /workspace/copacetic

Personalizing user settings in a dev container

VSCode supports applying your user settings, such as your .gitconfig, to a dev container through the use of dotfiles repositories. This can be done through your own VSCode settings.json file without changing the dev container image or configuration.

Tests

Once you can successfully make the project, any code contributions should also successfully:

  • Pass unit tests via make test.
  • Lint cleanly via make lint.

Pull requests will also be expected to pass the PR functional tests specified by .github/workflows/build.yml.

Pull Requests

If you'd like to start contributing code to the project, you can search for issues with the good first issue label. Other kinds of PR contributions we would look for include:

  • Fixes for bugs and other correctness issues.
  • Docs and other content improvements (e.g. samples).
  • Extensions to support parsing new scanning report formats.
  • Extensions to support patching images based on new distros or using new package managers.

For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:

  • Architectural changes (e.g. breaking interfaces or violations of this project's design tenets).
  • Unsolicited features that significantly expand the functional scope of the tool.

Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the Angular commit message format for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs.

We suggest:

  • Use the standard header format of "<type>: <short summary>" where the <type> is one of the following:
    • build: Changes that affect the build system or external dependencies
    • ci: Changes to the GitHub workflows and configurations
    • docs: Documentation only changes
    • feat: A new feature
    • fix: A bug fix
    • perf: A code change that improves performance
    • refactor: A code change that neither fixes a bug nor adds a feature
    • test: Adding missing tests or correcting existing tests
  • Use a concise, imperative description of the changes included in the <short summary> of the header, the body of the PR, and generally in your commit messages.
  • Use GitHub keywords in the footer of your PR description, such as closes to automatically close issues the PR intends to address.

Developer Certificate of Origin (DCO)

The Developer Certificate of Origin (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the full text of the DCO, reformatted for readability:

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I +

Version: v0.6.x

Contributing

Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.

+

Bi-Weekly Community Meeting

+

A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join here

+

Slack

+

To discuss issues with Copa, features, or development, you can join the #copa channel on the OCI Slack.

+

Contributing Issues

+

Before opening any new issues, please search our existing GitHub issues to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead.

+

When opening an issue, please select the most appropriate template for what you're contributing:

+
    +
  • Bug Report: If you would like to report the project or tool behaving in unexpected ways.
  • +
  • Documentation Improvement: If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content.
  • +
  • Request: If you have a feature request, suggestion, or a even a design proposal to review.
  • +
  • Question: If you would like to ask the maintainers a question about the project.
  • +
+

Contributing Code

+

Getting Started

+

Follow the instructions to either:

+ +

For an overview of the project components, refer to the copa design document.

+

Visual Studio Code Development Container

+

VSCode supports development in a containerized environment through its Remote - Container extension. This folder provides a development container which encapsulates the dependencies specified in the instructions to build and run copa.

+

Prerequisites

+
    +
  1. Docker +
    +

    For Windows users, enabling WSL2 back-end integration with Docker is recommended.

    +
    +
  2. +
  3. Visual Studio Code
  4. +
  5. Visual Studio Code Remote - Containers extension
  6. +
+
+

⚠ If running via Docker Desktop for Windows

+

Note that the mounted workspace files appear owned by root in the dev container, which will cause git commands to fail with a fatal: detected dubious ownership in a repository error due to safe.directory checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the vscode user:

+
sudo chown -R vscode:vscode /workspace/copacetic
+
+

Personalizing user settings in a dev container

+

VSCode supports applying your user settings, such as your .gitconfig, to a dev container through the use of dotfiles repositories. This can be done through your own VSCode settings.json file without changing the dev container image or configuration.

+

Tests

+

Once you can successfully make the project, any code contributions should also successfully:

+
    +
  • Pass unit tests via make test.
  • +
  • Lint cleanly via make lint.
  • +
+

Pull requests will also be expected to pass the PR functional tests specified by .github/workflows/build.yml.

+

Pull Requests

+

If you'd like to start contributing code to the project, you can search for issues with the good first issue label. Other kinds of PR contributions we would look for include:

+
    +
  • Fixes for bugs and other correctness issues.
  • +
  • Docs and other content improvements (e.g. samples).
  • +
  • Extensions to support parsing new scanning report formats.
  • +
  • Extensions to support patching images based on new distros or using new package managers.
  • +
+

For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:

+
    +
  • Architectural changes (e.g. breaking interfaces or violations of this project's design tenets).
  • +
  • Unsolicited features that significantly expand the functional scope of the tool.
  • +
+

Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the Angular commit message format for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs.

+

We suggest:

+
    +
  • Use the standard header format of "<type>: <short summary>" where the <type> is one of the following: +
      +
    • build: Changes that affect the build system or external dependencies
    • +
    • ci: Changes to the GitHub workflows and configurations
    • +
    • docs: Documentation only changes
    • +
    • feat: A new feature
    • +
    • fix: A bug fix
    • +
    • perf: A code change that improves performance
    • +
    • refactor: A code change that neither fixes a bug nor adds a feature
    • +
    • test: Adding missing tests or correcting existing tests
    • +
    +
  • +
  • Use a concise, imperative description of the changes included in the <short summary> of the header, the body of the PR, and generally in your commit messages.
  • +
  • Use GitHub keywords in the footer of your PR description, such as closes to automatically close issues the PR intends to address.
  • +
+

Developer Certificate of Origin (DCO)

+

The Developer Certificate of Origin (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the full text of the DCO, reformatted for readability:

+
+

By making a contribution to this project, I certify that:

+

(a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license -indicated in the file; or

(b) The contribution is based upon previous work that, to the best +indicated in the file; or

+

(b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated -in the file; or

(c) The contribution was provided directly to me by some other +in the file; or

+

(c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified -it.

(d) I understand and agree that this project and the contribution +it.

+

(d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with -this project or the open source license(s) involved.

Contributors sign-off that they adhere to these requirements by adding a Signed-off-by line to commit messages.

This is my commit message

Signed-off-by: Random J Developer <random@developer.example.org>

Git even has a -s command line option to append this automatically to your commit message:

git commit -s -m 'This is my commit message'

Pull requests that do not contain a valid Signed-off-by line cannot be merged.

I didn't sign my commit, now what?

No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:

git switch <branch-name>
git commit --amend --no-edit --signoff
git push --force-with-lease <remote-name> <branch-name>

Code of Conduct

This project has adopted the Contributor Covenant Code of Conduct.

- - +this project or the open source license(s) involved.

+
+

Contributors sign-off that they adhere to these requirements by adding a Signed-off-by line to commit messages.

+
This is my commit message

Signed-off-by: Random J Developer <random@developer.example.org>
+

Git even has a -s command line option to append this automatically to your commit message:

+
git commit -s -m 'This is my commit message'
+

Pull requests that do not contain a valid Signed-off-by line cannot be merged.

+

I didn't sign my commit, now what?

+

No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:

+
git switch <branch-name>
git commit --amend --no-edit --signoff
git push --force-with-lease <remote-name> <branch-name>
+

Code of Conduct

+

This project has adopted the Contributor Covenant Code of Conduct.

\ No newline at end of file diff --git a/website/custom-address.html b/website/custom-address.html index b5f2ce44..bf080983 100644 --- a/website/custom-address.html +++ b/website/custom-address.html @@ -1,19 +1,36 @@ - + - -Custom buildkit addresses | Copacetic + +Custom buildkit addresses | Copacetic - - - + + + -
-
Version: v0.6.x

Custom buildkit addresses

If you need to specify a custom address using the --addr flag. Here are the supported formats:

  • unix:///path/to/buildkit.sock - Connect to buildkit over unix socket.
  • tcp://$BUILDKIT_ADDR:$PORT - Connect to buildkit over TCP. (not recommended for security reasons)
  • docker://<docker connection spec> - Connect to docker, currently only unix sockets are supported, e.g. docker://unix:///var/run/docker.sock (or just docker://).
  • docker-container://my-buildkit-container - Connect to a buildkitd running in a docker container.
  • buildx://my-builder - Connect to a buildx builder (or buildx:// for the currently selected builder). Note: only container-backed buildx instances are currently supported
  • nerdctl-container://my-container-name - Similar to docker-container but uses nerdctl.
  • podman-container://my-container-name - Similar to docker-container but uses podman.
  • ssh://myhost - Connect to a buildkit instance over SSH. Format of the host spec should mimic the SSH command.
  • kubepod://mypod - Connect to buildkit running in a Kubernetes pod. Can also specify kubectl context and pod namespace (kubepod://mypod?context=foo&namespace=notdefault).

Buildkit Connection Examples

Option 1: Connect using defaults

copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched

Option 2: Connect to buildx

docker buildx create --name demo
copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr buildx://demo

Option 3: Buildkit in a container

export BUILDKIT_VERSION=v0.12.4
docker run \
--detach \
--rm \
--privileged \
--name buildkitd \
--entrypoint buildkitd \
"moby/buildkit:$BUILDKIT_VERSION"

copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr docker-container://buildkitd

Option 4: Buildkit over TCP

export BUILDKIT_VERSION=v0.12.4
export BUILDKIT_PORT=8888
docker run \
--detach \
--rm \
--privileged \
-p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \
--name buildkitd \
--entrypoint buildkitd \
"moby/buildkit:$BUILDKIT_VERSION" \
--addr tcp://0.0.0.0:$BUILDKIT_PORT

copa patch \
-i docker.io/library/nginx:1.21.6 \
-r nginx.1.21.6.json \
-t 1.21.6-patched \
-a tcp://0.0.0.0:$BUILDKIT_PORT
- - +
Version: v0.6.x

Custom buildkit addresses

If you need to specify a custom address using the --addr flag. Here are the supported formats:

+
    +
  • unix:///path/to/buildkit.sock - Connect to buildkit over unix socket.
  • +
  • tcp://$BUILDKIT_ADDR:$PORT - Connect to buildkit over TCP. (not recommended for security reasons)
  • +
  • docker://<docker connection spec> - Connect to docker, currently only unix sockets are supported, e.g. docker://unix:///var/run/docker.sock (or just docker://).
  • +
  • docker-container://my-buildkit-container - Connect to a buildkitd running in a docker container.
  • +
  • buildx://my-builder - Connect to a buildx builder (or buildx:// for the currently selected builder). Note: only container-backed buildx instances are currently supported
  • +
  • nerdctl-container://my-container-name - Similar to docker-container but uses nerdctl.
  • +
  • podman-container://my-container-name - Similar to docker-container but uses podman.
  • +
  • ssh://myhost - Connect to a buildkit instance over SSH. Format of the host spec should mimic the SSH command.
  • +
  • kubepod://mypod - Connect to buildkit running in a Kubernetes pod. Can also specify kubectl context and pod namespace (kubepod://mypod?context=foo&namespace=notdefault).
  • +
+

Buildkit Connection Examples

+

Option 1: Connect using defaults

+
copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched
+

Option 2: Connect to buildx

+
docker buildx create --name demo
copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr buildx://demo
+

Option 3: Buildkit in a container

+
export BUILDKIT_VERSION=v0.12.4
docker run \
--detach \
--rm \
--privileged \
--name buildkitd \
--entrypoint buildkitd \
"moby/buildkit:$BUILDKIT_VERSION"

copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr docker-container://buildkitd
+

Option 4: Buildkit over TCP

+
export BUILDKIT_VERSION=v0.12.4
export BUILDKIT_PORT=8888
docker run \
--detach \
--rm \
--privileged \
-p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \
--name buildkitd \
--entrypoint buildkitd \
"moby/buildkit:$BUILDKIT_VERSION" \
--addr tcp://0.0.0.0:$BUILDKIT_PORT

copa patch \
-i docker.io/library/nginx:1.21.6 \
-r nginx.1.21.6.json \
-t 1.21.6-patched \
-a tcp://0.0.0.0:$BUILDKIT_PORT
\ No newline at end of file diff --git a/website/design.html b/website/design.html index 96b33752..6cd0c46f 100644 --- a/website/design.html +++ b/website/design.html @@ -1,19 +1,84 @@ - + - -Design | Copacetic + +Design | Copacetic - - - + + + -
-
Version: v0.6.x

Design

Design Tenets

  • Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update. This is a raison d’etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.

  • Copa is intended to work with the existing ecosystem of container images. The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.

  • Copa is intended to allow parties other than the image authors to address container vulnerabilities. Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.

  • Copa is intended to do one thing well and be composable with other tools and processes. Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane.

Design Reasoning

The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental crane rebase:

  • Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an apt install during image creation will overwrite the dpkg status file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners.

    To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.

  • Rebasing also requires that the user knows a priori what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets.

    While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose.

    The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well.

Architecture

The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:

  • The data schema of various vulnerability scanners producing the input vulnerability report.
  • The state management of various package managers and process for applying patches appropriately through them.

Effectively, copa patch can be considered a command that bridges an extensible Parse action with an extensible Apply action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:

type UpdatePackage struct {
Name string
Version string
}

type UpdateManifest struct {
OSType string
OSVersion string
Arch string
Updates []UpdatePackage
}

type ScanReportParser interface {
Parse(reportPath string) (*UpdateManifest, error)
}

type PackageManager interface {
Apply(imagePath string, report *UpdateManifest) error
}

Implementation

copa is a pseudo-frontend to buildkit implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of LLB graphs for buildkit to execute:

  1. Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.
    • Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions.
  2. Actions to fetch and deploy tools that can be injected into the target image to perform the patching.
    • In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches.
    • In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image.
    • Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well.
  3. Actions to deploy the required patch packages to the target image.
    • copa integrates with buildkit at the API level because it uses the diff and merge graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.

Tradeoffs

  • The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:
    • By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa.
    • copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this.
  • No support for windows containers given the dependency on buildkit.
- - +
Version: v0.6.x

Design

Design Tenets

+
    +
  • +

    Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update. This is a raison d’etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.

    +
  • +
  • +

    Copa is intended to work with the existing ecosystem of container images. The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.

    +
  • +
  • +

    Copa is intended to allow parties other than the image authors to address container vulnerabilities. Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.

    +
  • +
  • +

    Copa is intended to do one thing well and be composable with other tools and processes. Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane.

    +
  • +
+

Design Reasoning

+

The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental crane rebase:

+
    +
  • +

    Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an apt install during image creation will overwrite the dpkg status file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners.

    +

    To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.

    +
  • +
  • +

    Rebasing also requires that the user knows a priori what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets.

    +

    While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose.

    +

    The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well.

    +
  • +
+

Architecture

+ +

The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:

+
    +
  • The data schema of various vulnerability scanners producing the input vulnerability report.
  • +
  • The state management of various package managers and process for applying patches appropriately through them.
  • +
+

Effectively, copa patch can be considered a command that bridges an extensible Parse action with an extensible Apply action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:

+
type UpdatePackage struct {
Name string
Version string
}

type UpdateManifest struct {
OSType string
OSVersion string
Arch string
Updates []UpdatePackage
}

type ScanReportParser interface {
Parse(reportPath string) (*UpdateManifest, error)
}

type PackageManager interface {
Apply(imagePath string, report *UpdateManifest) error
}
+

Implementation

+ +

copa is a pseudo-frontend to buildkit implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of LLB graphs for buildkit to execute:

+
    +
  1. Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image. +
      +
    • Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions.
    • +
    +
  2. +
  3. Actions to fetch and deploy tools that can be injected into the target image to perform the patching. +
      +
    • In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches.
    • +
    • In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image.
    • +
    • Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well.
    • +
    +
  4. +
  5. Actions to deploy the required patch packages to the target image. +
      +
    • copa integrates with buildkit at the API level because it uses the diff and merge graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.
    • +
    +
  6. +
+

Tradeoffs

+
    +
  • The core architectural choice of relying on packages as the unit of patching creates a couple of constraints: +
      +
    • By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa.
    • +
    • copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this.
    • +
    +
  • +
  • No support for windows containers given the dependency on buildkit.
  • +
\ No newline at end of file diff --git a/website/development-tips.html b/website/development-tips.html index 24b6740b..8050124b 100644 --- a/website/development-tips.html +++ b/website/development-tips.html @@ -1,19 +1,72 @@ - + - -Development and Testing Tips | Copacetic + +Development and Testing Tips | Copacetic - - - + + + -
-
Version: v0.6.x

Development and Testing Tips

This document provides some tips and tricks for devs to better understand what is happening under the hood of copa.

Much of the functionality of copa is implemented through the use of the BuildKit library, and in particular, direct building a Low-Level Build (LLB) intermediate representation. Most patching operations are implemented as a series of LLB stages that form a Directed Acyclic Graph (DAG) to produce the final patched image, and we'll walk through some ways to deal with the opaque nature of each operation in that graph which can otherwise make it difficult to debug or test copa.

Use the --debug flag with copa patch

It's always useful to know that code on the copa side is behaving as expected first before diving into the weeds of its interactions with BuildKit. The --debug flag will do two useful things when enabled:

  • Log debug state to stdout with the DEBU tag, including useful information such as the type of image it expects to be operating on, the list of updates and their versions it expects to apply, and any detailed errors.
  • Leave the working folder in place so that you can inspect the contents of the working files copa writes for its own use during the patching process.

For example, if you run copa patch with the --debug flag, you'll see something like the following output:

$ copa patch -i <image> -r <report> --debug
DEBU[0000] updates to apply: ...
...
WARN[0000] --debug specified, working folder at /var/folders/fx/nbhd5jln1qq3t405hz_hl4000000gn/T/copa-806164554 needs to be manually cleaned up

The folder specified defaults to the system temp folder unless the --working-folder option was specified, and you can delete it with rm -r <folder> when you're done. The working folder will usually contain the copa-out directory which contains files depending on the pkgmgr implementation, such as the probed package state or post-patching package state file for the package manager. Searching for SolveToLocal() invocations in the copa codebase will show you where these files are written.

Verify the intermediate stages of building a patched image

It's often useful to be able to inspect what the output of an intermediate LLB stage would look like after it has executed, and you can perform an analog to printf debugging by solving the LLB stage to a Docker image and then inspecting the resulting image:

// DEBUG: Solve the LLB stage to a Docker image.
if err := buildkit.SolveToDocker(ctx, dm.config.Client, &<llb.Stage>, dm.config.ConfigData, dm.config.ImageName+"-<llb.Stage suffix>"); err != nil {
return nil, err
}

For example, if you want to see what the resulting Docker image looks like at the busyBoxApplied stage, you can add the buildkit.SolveToDocker call to the end of the busyBoxApplied stage as follows. The result will be a Docker image with the -busyBoxApplied suffix to the tag that you can inspect with the docker CLI or dive tool:

busyBoxApplied := dm.config.ImageState.File(llb.Copy(toolImage, "/bin/busybox", "/bin/busybox"))
if err := buildkit.SolveToDocker(ctx, dm.config.Client, &busyBoxApplied, dm.config.ConfigData, dm.config.ImageName+"-busyBoxApplied"); err != nil {
return nil, err
}

Inspect a Docker image

Use docker to inspect the metadata of the image

For a quick check of a Docker image, the built in docker CLI commands can be useful:

  • docker inspect can show you the metadata for the image and verifying that the patching process generally preserves the metadata of the original image.
  • docker history can give you a quick overview of the layers in the image, and with the --no-trunc flag, can provide the commands that were run to create each layer.

Use dive to inspect the filesystem differences at each layer of the image

Instructions for installing and using the dive CLI tool are at https://github.com/wagoodman/dive.

dive provides a simple interface for walking the layers of the image and inspecting the files that were added or changed at each layer with your arrow keys.

  • Tab will toggle between navigating the layers and the files in the layer.
  • Filtering out unmodified files with Ctrl+U while in files view will effectively show you the file diff introduced by that layer.

In particular, if you are adding or changing any of the patching functionality, the diff view of the files in the image can be useful to verify that the expected files have actually been written to the target image.

Extract individual files from the image to inspect them

dive won't let you read the contents of the files in the image though; to do that, you can use the docker cp command to copy the files out of the image to a local folder. Note that docker cp only works with containers and not just container images, so you will need to create a container from the image and then copy the files out of the container:

id=$(docker create <image name>:<tag>)
docker cp $id:<filepath> <destination path>
docker rm -v $id

Use crane to manipulate the image or extract the image filesystem

Sometimes it's useful to be able to manipulate the image in ways that docker or dive don't support, such as extracting the entire image filesystem to a local folder. crane can be useful for this and also provides many other convenient utilities for working with container images.

For instance, to extract the filesystem of the image to a local folder, you can use crane export:

crane export <image name>:<tag> - | tar -xvf -

crane is a very flexible tool designed to work well with pipes to existing shell tools. For example, you can also use crane to do a full diff between two images as well:

 diff \
<(crane export image:tag - | tar -tvf - | sort) \
<(crane export image:tag-patched - | tar -tvf - | sort)

Run scripts interactively in an image

Some of the LLB stages effectively run shell scripts defined by copa in the image, and sometimes these need to be debugged or modified. The easiest way to do this is usually just solving the llb.Stage of interest to Docker and then running the image interactively with docker run:

docker run --rm -it --entrypoint sh <image name>:<tag>-<llb.Stage suffix>

One thing to note is that the scripts embedded in the .go files will often have an additional layer of character escapes to make them valid Go strings, so you may need to unescape them before running them interactively.

Dump the LLB Graph

Ultimately, copa is just a tool for building a BuildKit LLB graph, and you may need to understand if the LLB graph being constructed is reasonable or expected. It's helpful to have a basic understanding of how BuildKit and the operations commonly used by copa here, so it's good to be familiar with some key resources here:

The LLB graph up to any llb.Stage can be written out by marshalling it to a Definition and enumerating each of the operations to output. Using the buildctl implementation of dumpLLB as a reference, you can write a function to output the LLB graph as JSON nodes to stdout:

import "github.com/moby/buildkit/solver/pb"

// Definition of the LLB graph node to display, modify as desired.
// This version is what the buildctl tool uses.
type llbOp struct {
Op pb.Op
Digest digest.Digest
OpMetadata pb.OpMetadata
}

func outputLLBGraph(ctx context.Context, llbState *llb.State) error {
// Marshal the llb.State to a LLB definition.
def, err := llbState.Marshal(ctx)
if err != nil {
log.Errorf("Marshal to LLB failed with %s", err)
return err
}

// Format each operation node in the LLB definition into a struct.
var ops []llbOp
for _, dt := range def.Def {
var op pb.Op
if err := (&op).Unmarshal(dt); err != nil {
return errors.Wrap(err, "failed to parse op")
}
hash := digest.FromBytes(dt)
ent := llbOp{Op: op, Digest: hash, OpMetadata: def.Metadata[hash]}
ops = append(ops, ent)
}

// Output the LLB graph as JSON nodes to stdout.
// Modify as desired to output to file or other formats.
enc := json.NewEncoder(os.Stdout)
for _, op := range ops {
if err := enc.Encode(op); err != nil {
return err
}
}
return nil
}

// Within the function (e.g. with the llb.State where you want to dump the LLB graph:
...
// DEBUG: dump the LLB graph to stdout
if err := outputLLBGraph(ctx, &merged); err != nil {
return nil, err
}
...

For the definition of an LLB vertex (an Op node struct enumerated by the code snippet above), refer to https://github.com/moby/buildkit/blob/master/solver/pb/ops.proto.

Following the edges between the LLB nodes is a matter of following the resulting Digest value for the node to where it is consumed as one of the Op.inputs in another node. For example, a pretty-printed version of a LLB graph in json format focusing on a few key nodes might look like:

// Initial target image source node
{
"Op": {
"Op": {
"source": {
"identifier": "docker-image://mcr.microsoft.com/oss/open-policy-agent/opa:0.46.0"
}
},
"platform": {
"Architecture": "amd64",
"OS": "linux"
},
"constraints": {}
},
"Digest": "sha256:a86ddb9065d07c67dc838e11a81ff54020531c4ca2d85fb20574088222da8b30",
"OpMetadata": {
"caps": {
"source.image": true
}
}
}

// ..
// Skipping intermediate graph nodes
// ...

// Diffing out the manifest updates layer
{
"Op": {
"inputs": [
{
"digest": "sha256:cbc31a96266caa8cd5ced38a1f8e97de9f13fafb23dbe9e342125569cd4d5018",
"index": 0
},
{
"digest": "sha256:9f798b2e38e054aadf1ee66c7eb7230c65be324c26d8739a3d5fa2d5da90e5de",
"index": 0
}
],
"Op": {
"diff": {
"lower": {
"input": 0
},
"upper": {
"input": 1
}
}
},
"constraints": {}
},
"Digest": "sha256:f337f99144ab75fee8593ec6531caa9ebace06aaca07614778b7c0ca5c816135",
"OpMetadata": {
"caps": {
"diffop": true
}
}
}

// Merging all the target image with the patch layer and the manifest updates layer
{
"Op": {
"inputs": [
{
"digest": "sha256:a86ddb9065d07c67dc838e11a81ff54020531c4ca2d85fb20574088222da8b30",
"index": 0
},
{
"digest": "sha256:1c3ad84c0de7e1384d727f6168db3f1f8fb632c0086760aff1786a7e89562d13",
"index": 0
},
{
"digest": "sha256:f337f99144ab75fee8593ec6531caa9ebace06aaca07614778b7c0ca5c816135",
"index": 0
}
],
"Op": {
"merge": {
"inputs": [
{
"input": 0
},
{
"input": 1
},
{
"input": 2
}
]
}
},
"constraints": {}
},
"Digest": "sha256:e6a4086e2caf03c8814fc5388dd7d2e45420e1c5281e0df0d3db375d3f00358a",
"OpMetadata": {
"caps": {
"mergeop": true
}
}
}

//...

- - +
Version: v0.6.x

Development and Testing Tips

+

This document provides some tips and tricks for devs to better understand what is happening under the hood of copa.

+

Much of the functionality of copa is implemented through the use of the BuildKit library, and in particular, direct building a Low-Level Build (LLB) intermediate representation. Most patching operations are implemented as a series of LLB stages that form a Directed Acyclic Graph (DAG) to produce the final patched image, and we'll walk through some ways to deal with the opaque nature of each operation in that graph which can otherwise make it difficult to debug or test copa.

+

Use the --debug flag with copa patch

+

It's always useful to know that code on the copa side is behaving as expected first before diving into the weeds of its interactions with BuildKit. The --debug flag will do two useful things when enabled:

+
    +
  • Log debug state to stdout with the DEBU tag, including useful information such as the type of image it expects to be operating on, the list of updates and their versions it expects to apply, and any detailed errors.
  • +
  • Leave the working folder in place so that you can inspect the contents of the working files copa writes for its own use during the patching process.
  • +
+

For example, if you run copa patch with the --debug flag, you'll see something like the following output:

+
$ copa patch -i <image> -r <report> --debug
DEBU[0000] updates to apply: ...
...
WARN[0000] --debug specified, working folder at /var/folders/fx/nbhd5jln1qq3t405hz_hl4000000gn/T/copa-806164554 needs to be manually cleaned up
+

The folder specified defaults to the system temp folder unless the --working-folder option was specified, and you can delete it with rm -r <folder> when you're done. The working folder will usually contain the copa-out directory which contains files depending on the pkgmgr implementation, such as the probed package state or post-patching package state file for the package manager. Searching for SolveToLocal() invocations in the copa codebase will show you where these files are written.

+

Verify the intermediate stages of building a patched image

+

It's often useful to be able to inspect what the output of an intermediate LLB stage would look like after it has executed, and you can perform an analog to printf debugging by solving the LLB stage to a Docker image and then inspecting the resulting image:

+
// DEBUG: Solve the LLB stage to a Docker image.
if err := buildkit.SolveToDocker(ctx, dm.config.Client, &<llb.Stage>, dm.config.ConfigData, dm.config.ImageName+"-<llb.Stage suffix>"); err != nil {
return nil, err
}
+

For example, if you want to see what the resulting Docker image looks like at the busyBoxApplied stage, you can add the buildkit.SolveToDocker call to the end of the busyBoxApplied stage as follows. The result will be a Docker image with the -busyBoxApplied suffix to the tag that you can inspect with the docker CLI or dive tool:

+
busyBoxApplied := dm.config.ImageState.File(llb.Copy(toolImage, "/bin/busybox", "/bin/busybox"))
if err := buildkit.SolveToDocker(ctx, dm.config.Client, &busyBoxApplied, dm.config.ConfigData, dm.config.ImageName+"-busyBoxApplied"); err != nil {
return nil, err
}
+

Inspect a Docker image

+

Use docker to inspect the metadata of the image

+

For a quick check of a Docker image, the built in docker CLI commands can be useful:

+
    +
  • docker inspect can show you the metadata for the image and verifying that the patching process generally preserves the metadata of the original image.
  • +
  • docker history can give you a quick overview of the layers in the image, and with the --no-trunc flag, can provide the commands that were run to create each layer.
  • +
+

Use dive to inspect the filesystem differences at each layer of the image

+

Instructions for installing and using the dive CLI tool are at https://github.com/wagoodman/dive.

+

dive provides a simple interface for walking the layers of the image and inspecting the files that were added or changed at each layer with your arrow keys.

+
    +
  • Tab will toggle between navigating the layers and the files in the layer.
  • +
  • Filtering out unmodified files with Ctrl+U while in files view will effectively show you the file diff introduced by that layer.
  • +
+

In particular, if you are adding or changing any of the patching functionality, the diff view of the files in the image can be useful to verify that the expected files have actually been written to the target image.

+

Extract individual files from the image to inspect them

+

dive won't let you read the contents of the files in the image though; to do that, you can use the docker cp command to copy the files out of the image to a local folder. Note that docker cp only works with containers and not just container images, so you will need to create a container from the image and then copy the files out of the container:

+
id=$(docker create <image name>:<tag>)
docker cp $id:<filepath> <destination path>
docker rm -v $id
+

Use crane to manipulate the image or extract the image filesystem

+

Sometimes it's useful to be able to manipulate the image in ways that docker or dive don't support, such as extracting the entire image filesystem to a local folder. crane can be useful for this and also provides many other convenient utilities for working with container images.

+

For instance, to extract the filesystem of the image to a local folder, you can use crane export:

+
crane export <image name>:<tag> - | tar -xvf -
+

crane is a very flexible tool designed to work well with pipes to existing shell tools. For example, you can also use crane to do a full diff between two images as well:

+
 diff \
<(crane export image:tag - | tar -tvf - | sort) \
<(crane export image:tag-patched - | tar -tvf - | sort)
+

Run scripts interactively in an image

+

Some of the LLB stages effectively run shell scripts defined by copa in the image, and sometimes these need to be debugged or modified. The easiest way to do this is usually just solving the llb.Stage of interest to Docker and then running the image interactively with docker run:

+
docker run --rm -it --entrypoint sh <image name>:<tag>-<llb.Stage suffix>
+

One thing to note is that the scripts embedded in the .go files will often have an additional layer of character escapes to make them valid Go strings, so you may need to unescape them before running them interactively.

+

Dump the LLB Graph

+

Ultimately, copa is just a tool for building a BuildKit LLB graph, and you may need to understand if the LLB graph being constructed is reasonable or expected. It's helpful to have a basic understanding of how BuildKit and the operations commonly used by copa here, so it's good to be familiar with some key resources here:

+ +

The LLB graph up to any llb.Stage can be written out by marshalling it to a Definition and enumerating each of the operations to output. Using the buildctl implementation of dumpLLB as a reference, you can write a function to output the LLB graph as JSON nodes to stdout:

+
import "github.com/moby/buildkit/solver/pb"

// Definition of the LLB graph node to display, modify as desired.
// This version is what the buildctl tool uses.
type llbOp struct {
Op pb.Op
Digest digest.Digest
OpMetadata pb.OpMetadata
}

func outputLLBGraph(ctx context.Context, llbState *llb.State) error {
// Marshal the llb.State to a LLB definition.
def, err := llbState.Marshal(ctx)
if err != nil {
log.Errorf("Marshal to LLB failed with %s", err)
return err
}

// Format each operation node in the LLB definition into a struct.
var ops []llbOp
for _, dt := range def.Def {
var op pb.Op
if err := (&op).Unmarshal(dt); err != nil {
return errors.Wrap(err, "failed to parse op")
}
hash := digest.FromBytes(dt)
ent := llbOp{Op: op, Digest: hash, OpMetadata: def.Metadata[hash]}
ops = append(ops, ent)
}

// Output the LLB graph as JSON nodes to stdout.
// Modify as desired to output to file or other formats.
enc := json.NewEncoder(os.Stdout)
for _, op := range ops {
if err := enc.Encode(op); err != nil {
return err
}
}
return nil
}

// Within the function (e.g. with the llb.State where you want to dump the LLB graph:
...
// DEBUG: dump the LLB graph to stdout
if err := outputLLBGraph(ctx, &merged); err != nil {
return nil, err
}
...
+

For the definition of an LLB vertex (an Op node struct enumerated by the code snippet above), refer to https://github.com/moby/buildkit/blob/master/solver/pb/ops.proto.

+

Following the edges between the LLB nodes is a matter of following the resulting Digest value for the node to where it is consumed as one of the Op.inputs in another node. For example, a pretty-printed version of a LLB graph in json format focusing on a few key nodes might look like:

+
// Initial target image source node
{
"Op": {
"Op": {
"source": {
"identifier": "docker-image://mcr.microsoft.com/oss/open-policy-agent/opa:0.46.0"
}
},
"platform": {
"Architecture": "amd64",
"OS": "linux"
},
"constraints": {}
},
"Digest": "sha256:a86ddb9065d07c67dc838e11a81ff54020531c4ca2d85fb20574088222da8b30",
"OpMetadata": {
"caps": {
"source.image": true
}
}
}

// ..
// Skipping intermediate graph nodes
// ...

// Diffing out the manifest updates layer
{
"Op": {
"inputs": [
{
"digest": "sha256:cbc31a96266caa8cd5ced38a1f8e97de9f13fafb23dbe9e342125569cd4d5018",
"index": 0
},
{
"digest": "sha256:9f798b2e38e054aadf1ee66c7eb7230c65be324c26d8739a3d5fa2d5da90e5de",
"index": 0
}
],
"Op": {
"diff": {
"lower": {
"input": 0
},
"upper": {
"input": 1
}
}
},
"constraints": {}
},
"Digest": "sha256:f337f99144ab75fee8593ec6531caa9ebace06aaca07614778b7c0ca5c816135",
"OpMetadata": {
"caps": {
"diffop": true
}
}
}

// Merging all the target image with the patch layer and the manifest updates layer
{
"Op": {
"inputs": [
{
"digest": "sha256:a86ddb9065d07c67dc838e11a81ff54020531c4ca2d85fb20574088222da8b30",
"index": 0
},
{
"digest": "sha256:1c3ad84c0de7e1384d727f6168db3f1f8fb632c0086760aff1786a7e89562d13",
"index": 0
},
{
"digest": "sha256:f337f99144ab75fee8593ec6531caa9ebace06aaca07614778b7c0ca5c816135",
"index": 0
}
],
"Op": {
"merge": {
"inputs": [
{
"input": 0
},
{
"input": 1
},
{
"input": 2
}
]
}
},
"constraints": {}
},
"Digest": "sha256:e6a4086e2caf03c8814fc5388dd7d2e45420e1c5281e0df0d3db375d3f00358a",
"OpMetadata": {
"caps": {
"mergeop": true
}
}
}

//...

\ No newline at end of file diff --git a/website/faq.html b/website/faq.html index e560b8cc..ad695bb0 100644 --- a/website/faq.html +++ b/website/faq.html @@ -1,19 +1,29 @@ - + - -FAQ | Copacetic + +FAQ | Copacetic - - - + + + -
-
Version: v0.6.x

FAQ

What kind of vulnerabilities can Copa patch?

Copa is capable of patching "OS level" vulnerabilities. This includes packages (like openssl) in the image that are managed by a package manager such as apt or yum. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see below for more details).

What kind of vulnerabilities can Copa not patch?

Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the golang.org/x/net module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level.

To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like http://archive.ubuntu.com/ubuntu/ for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications.

Can I replace the package repositories in the image with my own?

caution

Experimental: This feature might change without preserving backwards compatibility.

Copa does not support replacing the repositories in the package managers with alternatives. Images must already use the intended package repositories. For example, for debian, updating /etc/apt/sources.list from http://archive.ubuntu.com/ubuntu/ to a mirror, such as https://mirrors.wikimedia.org/ubuntu/.

If you need the tooling image to use a different package repository, you can create a source policy to define a replacement image and/or pin to a digest. For example, the following source policy replaces docker.io/library/debian:11-slim image with foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5:

cat <<EOF > source-policy.json
{
"rules": [
{
"action": "CONVERT",
"selector": {
"identifier": "docker-image://docker.io/library/debian:11-slim"
},
"updates": {
"identifier": "docker-image://foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5"
}
}
]
}
EOF

export EXPERIMENTAL_BUILDKIT_SOURCE_POLICY=source-policy.json

Tooling image for Debian-based images are docker.io/library/debian:11-slim and RPM-based repos are mcr.microsoft.com/cbl-mariner/base/core:2.0.

For more information on source policies, see Buildkit Source Policies.

- - +
Version: v0.6.x

FAQ

What kind of vulnerabilities can Copa patch?

+

Copa is capable of patching "OS level" vulnerabilities. This includes packages (like openssl) in the image that are managed by a package manager such as apt or yum. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see below for more details).

+

What kind of vulnerabilities can Copa not patch?

+

Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the golang.org/x/net module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level.

+

To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like http://archive.ubuntu.com/ubuntu/ for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications.

+

Can I replace the package repositories in the image with my own?

+
caution

Experimental: This feature might change without preserving backwards compatibility.

+

Copa does not support replacing the repositories in the package managers with alternatives. Images must already use the intended package repositories. For example, for debian, updating /etc/apt/sources.list from http://archive.ubuntu.com/ubuntu/ to a mirror, such as https://mirrors.wikimedia.org/ubuntu/.

+

If you need the tooling image to use a different package repository, you can create a source policy to define a replacement image and/or pin to a digest. For example, the following source policy replaces docker.io/library/debian:11-slim image with foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5:

+
cat <<EOF > source-policy.json
{
"rules": [
{
"action": "CONVERT",
"selector": {
"identifier": "docker-image://docker.io/library/debian:11-slim"
},
"updates": {
"identifier": "docker-image://foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5"
}
}
]
}
EOF

export EXPERIMENTAL_BUILDKIT_SOURCE_POLICY=source-policy.json
+
+

Tooling image for Debian-based images are docker.io/library/debian:11-slim and RPM-based repos are mcr.microsoft.com/cbl-mariner/base/core:2.0.

+
+

For more information on source policies, see Buildkit Source Policies.

\ No newline at end of file diff --git a/website/github-action.html b/website/github-action.html index e19d70eb..ebc1dade 100644 --- a/website/github-action.html +++ b/website/github-action.html @@ -1,19 +1,17 @@ - + - -Github Action | Copacetic + +Github Action | Copacetic - - - + + + -
-
- - +
\ No newline at end of file diff --git a/website/index.html b/website/index.html index 0aaf0112..6030a54d 100644 --- a/website/index.html +++ b/website/index.html @@ -1,19 +1,59 @@ - + - -Introduction | Copacetic + +Introduction | Copacetic - - - + + + -
-
Version: v0.6.x

Project Copacetic: Directly patch container image vulnerabilities

copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.

Why?

We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between vulnerability disclosure and active exploitation continues to narrow, there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:

  • inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option
  • found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.

In addition to filling the operational gap not met by left-shift security practices and tools, the ability of copa to patch a container without requiring a rebuild of the container image provides other benefits:

  • Allows users other than the image publishers to also patch container images, such as DevSecOps engineers.
  • Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching.
  • Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild.
  • Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.

How?

The copa tool is an extensible engine that:

  1. Parses the needed update packages from the container image’s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats.
  2. Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers.
  3. Applies the resulting update binaries to the container image using buildkit.

This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:

  • Copa supports patching existing container images.
    • Devs don't need to build their images using specific tools or modify them in some way just to support container patching.
  • Copa works with the existing vulnerability scanning and mitigation ecosystems.
    • Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today.
    • Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports.
  • Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image.
    • For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.

For more details, refer to the copa design documentation.

- - +
Version: v0.6.x

Project Copacetic: Directly patch container image vulnerabilities

+

copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.

+

Why?

+

We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between vulnerability disclosure and active exploitation continues to narrow, there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:

+
    +
  • inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option
  • +
  • found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.
  • +
+ +

In addition to filling the operational gap not met by left-shift security practices and tools, the ability of copa to patch a container without requiring a rebuild of the container image provides other benefits:

+
    +
  • Allows users other than the image publishers to also patch container images, such as DevSecOps engineers.
  • +
  • Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching.
  • +
  • Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild.
  • +
  • Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.
  • +
+

How?

+

The copa tool is an extensible engine that:

+
    +
  1. Parses the needed update packages from the container image’s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats.
  2. +
  3. Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers.
  4. +
  5. Applies the resulting update binaries to the container image using buildkit.
  6. +
+ +

This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:

+
    +
  • Copa supports patching existing container images. +
      +
    • Devs don't need to build their images using specific tools or modify them in some way just to support container patching.
    • +
    +
  • +
  • Copa works with the existing vulnerability scanning and mitigation ecosystems. +
      +
    • Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today.
    • +
    • Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports.
    • +
    +
  • +
  • Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image. +
      +
    • For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.
    • +
    +
  • +
+

For more details, refer to the copa design documentation.

\ No newline at end of file diff --git a/website/installation.html b/website/installation.html index d487e1d7..c3837022 100644 --- a/website/installation.html +++ b/website/installation.html @@ -1,19 +1,28 @@ - + - -Installation | Copacetic + +Installation | Copacetic - - - + + + -
-
Version: v0.6.x

Installation

Homebrew

On macOS and Linux, copa can be installed via Homebrew:

brew install copa

GitHub

You can download the latest and previous versions of copa from the GitHub releases page.

Development Setup

Prequisitives

git clone https://github.com/project-copacetic/copacetic
cd copacetic
make
# OPTIONAL: install copa to a pathed folder
sudo mv dist/linux_amd64/release/copa /usr/local/bin/
- - +
Version: v0.6.x

Installation

Homebrew

+

On macOS and Linux, copa can be installed via Homebrew:

+
brew install copa
+

GitHub

+

You can download the latest and previous versions of copa from the GitHub releases page.

+

Development Setup

+

Prequisitives

+ +
git clone https://github.com/project-copacetic/copacetic
cd copacetic
make
# OPTIONAL: install copa to a pathed folder
sudo mv dist/linux_amd64/release/copa /usr/local/bin/
\ No newline at end of file diff --git a/website/maintainer-guidelines.html b/website/maintainer-guidelines.html index 859bdd8a..c0ec5937 100644 --- a/website/maintainer-guidelines.html +++ b/website/maintainer-guidelines.html @@ -1,19 +1,66 @@ - + - -Maintainer Guidelines | Copacetic + +Maintainer Guidelines | Copacetic - - - + + + -
-
Version: v0.6.x

Maintainer Guidelines

Semantic Release Management

This project uses go-semantic-release to automatically generate the appropriate semantic version and changelog for a release based on Angular commit message format. Of note to maintainers is the need to enforce an empty line-separate format:

<HEADER>
<-- blank line -->
<BODY>
<-- blank line -->
<FOOTER>

For contributor PRs, instead of trying to ensure adherence in every commit message, it's easiest to adopt a squash and merge strategy so that the PR description is used as the final commit description with the appropriate semantic release format.

In addition to the semantic release types called out in the contributor pull request guidelines, there are several other categories supported by the default changelog generator that maintainers should be aware of:

  • chore: Reserved for automated maintenance changes, such as minor version go dependency updates initiated by Dependabot.
  • revert: Maintainers should use this to mark commits that revert a previous commit, followed by the header of the reverted commit. The message body should include the SHA of the reverted commit, as well as a clear description of the reason for the revert.
  • style: This is unused for this project.

There are also two special categories to be added to the message footer that maintainers need to pay special attention to when merging changes:

Breaking change

Breaking changes should be described in the footer as follows:

BREAKING CHANGE: <breaking change summary>
<-- blank line -->
<breaking change description & migration instructions>
<-- blank line -->
<-- blank line -->
Closes #<issue number>

Note that this project currently uses the allow-initial-development-versions flag for go-semantic-release, so breaking changes will still be handled as minor releases until the workflow is updated for the v1.0.0 release.

Deprecation

DEPRECATED: <summary of deprecated feature>
<-- blank line -->
<deprecated feature description & migration/workaround instructions>
<-- blank line -->
<-- blank line -->
Closes #<issue number>

Publishing a Release

To avoid inconsistencies in tagging and release branching, this project uses the Publish release GitHub Actions workflow to automate the creation of releases.

Publish a new major/minor version release

  1. Review the main branch to ensure that it has all the desired changes for the new release branch and that there are no PR merge workflows in flight.
  2. Click Run workflow on the Publish release against the main branch. This will:
    1. Create a new tag with the incremented semantic version (e.g. v0.9.0) against the latest commit in main.
    2. Create a new GitHub release against that tag with an automatically generated changelog.
    3. Build and upload the new release version of Copa to the GitHub release.
    4. Create a new release branch if it does not already exist (e.g. release-0.9)
  3. Verify that the workflow ran successfully and review the expected outputs listed above.

Publish a patch revision release

  1. Review the appropriate release branch that the revision patches (e.g. release-0.9 for an anticipated new v0.9.x tag) to ensure that it has all the desired changes for the release and that there are no PR merge workflows in flight.
    1. If there are fixes in main intended for the patch release in the latest release branch, they need to be manually ported to the release branch first and the revision released from there.
  2. Click Run workflow on the Publish release against the target release-x.y branch. This will:
    1. Create a new tag with the incremented semantic version (e.g. v0.9.4) against the latest commit in the release branch.
    2. Create a new GitHub release against that tag with an automatically generated changelog.
    3. Build and upload the new release version of Copa to the GitHub release.
  3. Verify that the workflow ran successfully and review the expected outputs listed above.
- - +
Version: v0.6.x

Maintainer Guidelines

+

Semantic Release Management

+

This project uses go-semantic-release to automatically generate the appropriate semantic version and changelog for a release based on Angular commit message format. Of note to maintainers is the need to enforce an empty line-separate format:

+
<HEADER>
<-- blank line -->
<BODY>
<-- blank line -->
<FOOTER>
+

For contributor PRs, instead of trying to ensure adherence in every commit message, it's easiest to adopt a squash and merge strategy so that the PR description is used as the final commit description with the appropriate semantic release format.

+

In addition to the semantic release types called out in the contributor pull request guidelines, there are several other categories supported by the default changelog generator that maintainers should be aware of:

+
    +
  • chore: Reserved for automated maintenance changes, such as minor version go dependency updates initiated by Dependabot.
  • +
  • revert: Maintainers should use this to mark commits that revert a previous commit, followed by the header of the reverted commit. The message body should include the SHA of the reverted commit, as well as a clear description of the reason for the revert.
  • +
  • style: This is unused for this project.
  • +
+

There are also two special categories to be added to the message footer that maintainers need to pay special attention to when merging changes:

+

Breaking change

+

Breaking changes should be described in the footer as follows:

+
BREAKING CHANGE: <breaking change summary>
<-- blank line -->
<breaking change description & migration instructions>
<-- blank line -->
<-- blank line -->
Closes #<issue number>
+
+

Note that this project currently uses the allow-initial-development-versions flag for go-semantic-release, so breaking changes will still be handled as minor releases until the workflow is updated for the v1.0.0 release.

+
+

Deprecation

+
DEPRECATED: <summary of deprecated feature>
<-- blank line -->
<deprecated feature description & migration/workaround instructions>
<-- blank line -->
<-- blank line -->
Closes #<issue number>
+

Publishing a Release

+

To avoid inconsistencies in tagging and release branching, this project uses the Publish release GitHub Actions workflow to automate the creation of releases.

+

Publish a new major/minor version release

+
    +
  1. Review the main branch to ensure that it has all the desired changes for the new release branch and that there are no PR merge workflows in flight.
  2. +
  3. Click Run workflow on the Publish release against the main branch. This will: +
      +
    1. Create a new tag with the incremented semantic version (e.g. v0.9.0) against the latest commit in main.
    2. +
    3. Create a new GitHub release against that tag with an automatically generated changelog.
    4. +
    5. Build and upload the new release version of Copa to the GitHub release.
    6. +
    7. Create a new release branch if it does not already exist (e.g. release-0.9)
    8. +
    +
  4. +
  5. Verify that the workflow ran successfully and review the expected outputs listed above.
  6. +
+

Publish a patch revision release

+
    +
  1. Review the appropriate release branch that the revision patches (e.g. release-0.9 for an anticipated new v0.9.x tag) to ensure that it has all the desired changes for the release and that there are no PR merge workflows in flight. +
      +
    1. If there are fixes in main intended for the patch release in the latest release branch, they need to be manually ported to the release branch first and the revision released from there.
    2. +
    +
  2. +
  3. Click Run workflow on the Publish release against the target release-x.y branch. This will: +
      +
    1. Create a new tag with the incremented semantic version (e.g. v0.9.4) against the latest commit in the release branch.
    2. +
    3. Create a new GitHub release against that tag with an automatically generated changelog.
    4. +
    5. Build and upload the new release version of Copa to the GitHub release.
    6. +
    +
  4. +
  5. Verify that the workflow ran successfully and review the expected outputs listed above.
  6. +
\ No newline at end of file diff --git a/website/next.html b/website/next.html index fafebced..e3b66cd5 100644 --- a/website/next.html +++ b/website/next.html @@ -1,19 +1,60 @@ - + - -Introduction | Copacetic + +Introduction | Copacetic - - - + + + -
-
Version: Next

Project Copacetic: Directly patch container image vulnerabilities

copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.

Why?

We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between vulnerability disclosure and active exploitation continues to narrow, there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:

  • inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option
  • found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.

In addition to filling the operational gap not met by left-shift security practices and tools, the ability of copa to patch a container without requiring a rebuild of the container image provides other benefits:

  • Allows users other than the image publishers to also patch container images, such as DevSecOps engineers.
  • Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching.
  • Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild.
  • Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.

How?

The copa tool is an extensible engine that:

  1. Parses the needed update packages from the container image’s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats.
  2. Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers.
  3. Applies the resulting update binaries to the container image using buildkit.

This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:

  • Copa supports patching existing container images.
    • Devs don't need to build their images using specific tools or modify them in some way just to support container patching.
  • Copa supports containers without package managers including distroless containers
  • Copa works with the existing vulnerability scanning and mitigation ecosystems.
    • Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today.
    • Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports.
  • Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image.
    • For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.

For more details, refer to the copa design documentation.

- - +
Version: Next

Project Copacetic: Directly patch container image vulnerabilities

+

copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.

+

Why?

+

We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between vulnerability disclosure and active exploitation continues to narrow, there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:

+
    +
  • inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option
  • +
  • found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.
  • +
+ +

In addition to filling the operational gap not met by left-shift security practices and tools, the ability of copa to patch a container without requiring a rebuild of the container image provides other benefits:

+
    +
  • Allows users other than the image publishers to also patch container images, such as DevSecOps engineers.
  • +
  • Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching.
  • +
  • Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild.
  • +
  • Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.
  • +
+

How?

+

The copa tool is an extensible engine that:

+
    +
  1. Parses the needed update packages from the container image’s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats.
  2. +
  3. Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers.
  4. +
  5. Applies the resulting update binaries to the container image using buildkit.
  6. +
+ +

This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:

+
    +
  • Copa supports patching existing container images. +
      +
    • Devs don't need to build their images using specific tools or modify them in some way just to support container patching.
    • +
    +
  • +
  • Copa supports containers without package managers including distroless containers
  • +
  • Copa works with the existing vulnerability scanning and mitigation ecosystems. +
      +
    • Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today.
    • +
    • Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports.
    • +
    +
  • +
  • Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image. +
      +
    • For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.
    • +
    +
  • +
+

For more details, refer to the copa design documentation.

\ No newline at end of file diff --git a/website/next/best-practices.html b/website/next/best-practices.html index ba1c089c..a3f08c89 100644 --- a/website/next/best-practices.html +++ b/website/next/best-practices.html @@ -1,19 +1,26 @@ - + - -Tagging Guidelines | Copacetic + +Tagging Guidelines | Copacetic - - - + + + -
-
Version: Next

Tagging Guidelines

There are some patterns and practices you may want to consider when using Copa to patch images. Remember that these are suggestions that may not fit into your workflow, but we think that staying as close as possible to these practices offers the best experience with Copa.

Patch from Unmodified image

When patching vulnerabilities in an image, it helps to always work from the initial unmodified image. For example, say you have an image tagged nginx:1.24.0 that contains a vulnerability. You run Copa to patch the image and produce a new image tagged nginx:1.24.0-1. Then if another vulnerability shows up in your nginx:1.24.0-1 image, you should again patch from the unmodified nginx:1.24.0 image. This will help prevent the buildup of patch layers (discarding subsequent patch layers is a potential future enhancement).

Tagging

There are a couple possible patterns that you could follow when tagging patched images.

Static Incremental Tags

The first approach you could take is incrementing a number you append to the end of an image tag. For example, if you have an image tagged nginx:1.24.0, following patches would be tagged as nginx:1.24.0-1, nginx:1.24.0-2, nginx:1.24.0-3, and so on.

With this pattern you are always explicitly aware of the patch state of the image you are using. The downside is that dependabot is currently unable bump to patched images from unmodified images or bump from one patched image to the next.

Dynamic Tags

Another option is a static tag that is continually reused as new patches are applied. For example, you could have an initial unmodified image that you've tagged nginx:1.24.0-0 (in this case the -0 at the end helps identify the base unpatched image). All following patched images are then tagged as nginx:1.24.0. You then know that the one tagged image always has the latest patches applied.

This method makes it easy to continually consume the latest patched version of an image, but does contain some tradeoffs. First is that without pinning, image digests could change causing unpredictable behavior. Secondly, if an ImagePullPolicy is set to IfNotPresent, newly patched images would not be pulled since the tag hasn't changed.

- - +
Version: Next

Tagging Guidelines

There are some patterns and practices you may want to consider when using Copa to patch images. Remember that these are suggestions that may not fit into your workflow, but we think that staying as close as possible to these practices offers the best experience with Copa.

+

Patch from Unmodified image

+

When patching vulnerabilities in an image, it helps to always work from the initial unmodified image. For example, say you have an image tagged nginx:1.24.0 that contains a vulnerability. You run Copa to patch the image and produce a new image tagged nginx:1.24.0-1. Then if another vulnerability shows up in your nginx:1.24.0-1 image, you should again patch from the unmodified nginx:1.24.0 image. This will help prevent the buildup of patch layers (discarding subsequent patch layers is a potential future enhancement).

+

Tagging

+

There are a couple possible patterns that you could follow when tagging patched images.

+

Static Incremental Tags

+

The first approach you could take is incrementing a number you append to the end of an image tag. For example, if you have an image tagged nginx:1.24.0, following patches would be tagged as nginx:1.24.0-1, nginx:1.24.0-2, nginx:1.24.0-3, and so on.

+

With this pattern you are always explicitly aware of the patch state of the image you are using. The downside is that dependabot is currently unable bump to patched images from unmodified images or bump from one patched image to the next.

+

Dynamic Tags

+

Another option is a static tag that is continually reused as new patches are applied. For example, you could have an initial unmodified image that you've tagged nginx:1.24.0-0 (in this case the -0 at the end helps identify the base unpatched image). All following patched images are then tagged as nginx:1.24.0. You then know that the one tagged image always has the latest patches applied.

+

This method makes it easy to continually consume the latest patched version of an image, but does contain some tradeoffs. First is that without pinning, image digests could change causing unpredictable behavior. Secondly, if an ImagePullPolicy is set to IfNotPresent, newly patched images would not be pulled since the tag hasn't changed.

\ No newline at end of file diff --git a/website/next/code-of-conduct.html b/website/next/code-of-conduct.html index d1aa6f06..7a7df102 100644 --- a/website/next/code-of-conduct.html +++ b/website/next/code-of-conduct.html @@ -1,68 +1,111 @@ - + - -Code of Conduct | Copacetic + +Code of Conduct | Copacetic - - - + + + -
-
Version: Next

Contributor Covenant Code of Conduct

Our Pledge

We as members, contributors, and leaders pledge to make participation in our +

Version: Next

Contributor Covenant Code of Conduct

+

Our Pledge

+

We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual -identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community.

Our Standards

Examples of behavior that contributes to a positive environment for our -community include:

  • Demonstrating empathy and kindness toward other people
  • Being respectful of differing opinions, viewpoints, and experiences
  • Giving and gracefully accepting constructive feedback
  • Accepting responsibility and apologizing to those affected by our mistakes, -and learning from the experience
  • Focusing on what is best not just for us as individuals, but for the overall -community

Examples of unacceptable behavior include:

  • The use of sexualized language or imagery, and sexual attention or advances of -any kind
  • Trolling, insulting or derogatory comments, and personal or political attacks
  • Public or private harassment
  • Publishing others' private information, such as a physical or email address, -without their explicit permission
  • Other conduct which could reasonably be considered inappropriate in a -professional setting

Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of +identity and orientation.

+

We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community.

+

Our Standards

+

Examples of behavior that contributes to a positive environment for our +community include:

+
    +
  • Demonstrating empathy and kindness toward other people
  • +
  • Being respectful of differing opinions, viewpoints, and experiences
  • +
  • Giving and gracefully accepting constructive feedback
  • +
  • Accepting responsibility and apologizing to those affected by our mistakes, +and learning from the experience
  • +
  • Focusing on what is best not just for us as individuals, but for the overall +community
  • +
+

Examples of unacceptable behavior include:

+
    +
  • The use of sexualized language or imagery, and sexual attention or advances of +any kind
  • +
  • Trolling, insulting or derogatory comments, and personal or political attacks
  • +
  • Public or private harassment
  • +
  • Publishing others' private information, such as a physical or email address, +without their explicit permission
  • +
  • Other conduct which could reasonably be considered inappropriate in a +professional setting
  • +
+

Enforcement Responsibilities

+

Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, -or harmful.

Community leaders have the right and responsibility to remove, edit, or reject +or harmful.

+

Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate.

Scope

This Code of Conduct applies within all community spaces, and also applies when +decisions when appropriate.

+

Scope

+

This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed -representative at an online or offline event.

Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be +representative at an online or offline event.

+

Enforcement

+

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at project-copacetic@googlegroups.com. -All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the -reporter of any incident.

Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct:

1. Correction

Community Impact: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community.

Consequence: A private, written warning from community leaders, providing +All complaints will be reviewed and investigated promptly and fairly.

+

All community leaders are obligated to respect the privacy and security of the +reporter of any incident.

+

Enforcement Guidelines

+

Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct:

+

1. Correction

+

Community Impact: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community.

+

Consequence: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested.

2. Warning

Community Impact: A violation through a single incident or series of -actions.

Consequence: A warning with consequences for continued behavior. No +behavior was inappropriate. A public apology may be requested.

+

2. Warning

+

Community Impact: A violation through a single incident or series of +actions.

+

Consequence: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent -ban.

3. Temporary Ban

Community Impact: A serious violation of community standards, including -sustained inappropriate behavior.

Consequence: A temporary ban from any sort of interaction or public +ban.

+

3. Temporary Ban

+

Community Impact: A serious violation of community standards, including +sustained inappropriate behavior.

+

Consequence: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban.

4. Permanent Ban

Community Impact: Demonstrating a pattern of violation of community +Violating these terms may lead to a permanent ban.

+

4. Permanent Ban

+

Community Impact: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals.

Consequence: A permanent ban from any sort of public interaction within the -community.

Attribution

This Code of Conduct is adapted from the Contributor Covenant, +individual, or aggression toward or disparagement of classes of individuals.

+

Consequence: A permanent ban from any sort of public interaction within the +community.

+

Attribution

+

This Code of Conduct is adapted from the Contributor Covenant, version 2.1, available at -https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

Community Impact Guidelines were inspired by -Mozilla's code of conduct enforcement ladder.

For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

+

Community Impact Guidelines were inspired by +Mozilla's code of conduct enforcement ladder.

+

For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations.

- - +https://www.contributor-covenant.org/translations.

\ No newline at end of file diff --git a/website/next/contributing.html b/website/next/contributing.html index ec3f441a..fa7755e5 100644 --- a/website/next/contributing.html +++ b/website/next/contributing.html @@ -1,33 +1,127 @@ - + - -Contributing | Copacetic + +Contributing | Copacetic - - - + + + -
-
Version: Next

Contributing

Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.

Bi-Weekly Community Meeting

A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join here

Slack

To discuss issues with Copa, features, or development, you can join the #copa channel on the OCI Slack.

Contributing Issues

Before opening any new issues, please search our existing GitHub issues to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead.

When opening an issue, please select the most appropriate template for what you're contributing:

  • Bug Report: If you would like to report the project or tool behaving in unexpected ways.
  • Documentation Improvement: If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content.
  • Request: If you have a feature request, suggestion, or a even a design proposal to review.
  • Question: If you would like to ask the maintainers a question about the project.

Contributing Code

Getting Started

Follow the instructions to either:

For an overview of the project components, refer to the copa design document.

Visual Studio Code Development Container

VSCode supports development in a containerized environment through its Remote - Container extension. This folder provides a development container which encapsulates the dependencies specified in the instructions to build and run copa.

Prerequisites

  1. Docker

    For Windows users, enabling WSL2 back-end integration with Docker is recommended.

  2. Visual Studio Code
  3. Visual Studio Code Remote - Containers extension

⚠ If running via Docker Desktop for Windows

Note that the mounted workspace files appear owned by root in the dev container, which will cause git commands to fail with a fatal: detected dubious ownership in a repository error due to safe.directory checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the vscode user:

sudo chown -R vscode:vscode /workspace/copacetic

Personalizing user settings in a dev container

VSCode supports applying your user settings, such as your .gitconfig, to a dev container through the use of dotfiles repositories. This can be done through your own VSCode settings.json file without changing the dev container image or configuration.

Tests

Once you can successfully make the project, any code contributions should also successfully:

  • Pass unit tests via make test.
  • Lint cleanly via make lint.

Pull requests will also be expected to pass the PR functional tests specified by .github/workflows/build.yml.

Pull Requests

If you'd like to start contributing code to the project, you can search for issues with the good first issue label. Other kinds of PR contributions we would look for include:

  • Fixes for bugs and other correctness issues.
  • Docs and other content improvements (e.g. samples).
  • Extensions to support parsing new scanning report formats.
  • Extensions to support patching images based on new distros or using new package managers.

For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:

  • Architectural changes (e.g. breaking interfaces or violations of this project's design tenets).
  • Unsolicited features that significantly expand the functional scope of the tool.

Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the Angular commit message format for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs.

We suggest:

  • Use the standard header format of "<type>: <short summary>" where the <type> is one of the following:
    • build: Changes that affect the build system or external dependencies
    • ci: Changes to the GitHub workflows and configurations
    • docs: Documentation only changes
    • feat: A new feature
    • fix: A bug fix
    • perf: A code change that improves performance
    • refactor: A code change that neither fixes a bug nor adds a feature
    • test: Adding missing tests or correcting existing tests
  • Use a concise, imperative description of the changes included in the <short summary> of the header, the body of the PR, and generally in your commit messages.
  • Use GitHub keywords in the footer of your PR description, such as closes to automatically close issues the PR intends to address.

Developer Certificate of Origin (DCO)

The Developer Certificate of Origin (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the full text of the DCO, reformatted for readability:

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I +

Version: Next

Contributing

Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.

+

Bi-Weekly Community Meeting

+

A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join here

+

Slack

+

To discuss issues with Copa, features, or development, you can join the #copa channel on the OCI Slack.

+

Contributing Issues

+

Before opening any new issues, please search our existing GitHub issues to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead.

+

When opening an issue, please select the most appropriate template for what you're contributing:

+
    +
  • Bug Report: If you would like to report the project or tool behaving in unexpected ways.
  • +
  • Documentation Improvement: If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content.
  • +
  • Request: If you have a feature request, suggestion, or a even a design proposal to review.
  • +
  • Question: If you would like to ask the maintainers a question about the project.
  • +
+

Contributing Code

+

Getting Started

+

Follow the instructions to either:

+ +

For an overview of the project components, refer to the copa design document.

+

Visual Studio Code Development Container

+

VSCode supports development in a containerized environment through its Remote - Container extension. This folder provides a development container which encapsulates the dependencies specified in the instructions to build and run copa.

+

Prerequisites

+
    +
  1. Docker +
    +

    For Windows users, enabling WSL2 back-end integration with Docker is recommended.

    +
    +
  2. +
  3. Visual Studio Code
  4. +
  5. Visual Studio Code Remote - Containers extension
  6. +
+
+

⚠ If running via Docker Desktop for Windows

+

Note that the mounted workspace files appear owned by root in the dev container, which will cause git commands to fail with a fatal: detected dubious ownership in a repository error due to safe.directory checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the vscode user:

+
sudo chown -R vscode:vscode /workspace/copacetic
+
+

Personalizing user settings in a dev container

+

VSCode supports applying your user settings, such as your .gitconfig, to a dev container through the use of dotfiles repositories. This can be done through your own VSCode settings.json file without changing the dev container image or configuration.

+

Tests

+

Once you can successfully make the project, any code contributions should also successfully:

+
    +
  • Pass unit tests via make test.
  • +
  • Lint cleanly via make lint.
  • +
+

Pull requests will also be expected to pass the PR functional tests specified by .github/workflows/build.yml.

+

Pull Requests

+

If you'd like to start contributing code to the project, you can search for issues with the good first issue label. Other kinds of PR contributions we would look for include:

+
    +
  • Fixes for bugs and other correctness issues.
  • +
  • Docs and other content improvements (e.g. samples).
  • +
  • Extensions to support parsing new scanning report formats.
  • +
  • Extensions to support patching images based on new distros or using new package managers.
  • +
+

For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:

+
    +
  • Architectural changes (e.g. breaking interfaces or violations of this project's design tenets).
  • +
  • Unsolicited features that significantly expand the functional scope of the tool.
  • +
+

Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the Angular commit message format for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs.

+

We suggest:

+
    +
  • Use the standard header format of "<type>: <short summary>" where the <type> is one of the following: +
      +
    • build: Changes that affect the build system or external dependencies
    • +
    • ci: Changes to the GitHub workflows and configurations
    • +
    • docs: Documentation only changes
    • +
    • feat: A new feature
    • +
    • fix: A bug fix
    • +
    • perf: A code change that improves performance
    • +
    • refactor: A code change that neither fixes a bug nor adds a feature
    • +
    • test: Adding missing tests or correcting existing tests
    • +
    +
  • +
  • Use a concise, imperative description of the changes included in the <short summary> of the header, the body of the PR, and generally in your commit messages.
  • +
  • Use GitHub keywords in the footer of your PR description, such as closes to automatically close issues the PR intends to address.
  • +
+

Developer Certificate of Origin (DCO)

+

The Developer Certificate of Origin (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the full text of the DCO, reformatted for readability:

+
+

By making a contribution to this project, I certify that:

+

(a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license -indicated in the file; or

(b) The contribution is based upon previous work that, to the best +indicated in the file; or

+

(b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated -in the file; or

(c) The contribution was provided directly to me by some other +in the file; or

+

(c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified -it.

(d) I understand and agree that this project and the contribution +it.

+

(d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with -this project or the open source license(s) involved.

Contributors sign-off that they adhere to these requirements by adding a Signed-off-by line to commit messages.

This is my commit message

Signed-off-by: Random J Developer <random@developer.example.org>

Git even has a -s command line option to append this automatically to your commit message:

git commit -s -m 'This is my commit message'

Pull requests that do not contain a valid Signed-off-by line cannot be merged.

I didn't sign my commit, now what?

No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:

git switch <branch-name>
git commit --amend --no-edit --signoff
git push --force-with-lease <remote-name> <branch-name>

Code of Conduct

This project has adopted the Contributor Covenant Code of Conduct.

- - +this project or the open source license(s) involved.

+
+

Contributors sign-off that they adhere to these requirements by adding a Signed-off-by line to commit messages.

+
This is my commit message

Signed-off-by: Random J Developer <random@developer.example.org>
+

Git even has a -s command line option to append this automatically to your commit message:

+
git commit -s -m 'This is my commit message'
+

Pull requests that do not contain a valid Signed-off-by line cannot be merged.

+

I didn't sign my commit, now what?

+

No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:

+
git switch <branch-name>
git commit --amend --no-edit --signoff
git push --force-with-lease <remote-name> <branch-name>
+

Code of Conduct

+

This project has adopted the Contributor Covenant Code of Conduct.

\ No newline at end of file diff --git a/website/next/custom-address.html b/website/next/custom-address.html index 6ab4dd34..ef02f79b 100644 --- a/website/next/custom-address.html +++ b/website/next/custom-address.html @@ -1,19 +1,36 @@ - + - -Custom buildkit addresses | Copacetic + +Custom buildkit addresses | Copacetic - - - + + + -
-
Version: Next

Custom buildkit addresses

You may need to specify a custom address using the --addr flag. Here are the supported formats:

  • unix:///path/to/buildkit.sock - Connect to buildkit over unix socket.
  • tcp://$BUILDKIT_ADDR:$PORT - Connect to buildkit over TCP. (not recommended for security reasons)
  • docker://<docker connection spec> - Connect to docker, currently only unix sockets are supported, e.g. docker://unix:///var/run/docker.sock (or just docker://).
  • docker-container://my-buildkit-container - Connect to a buildkitd running in a docker container.
  • buildx://my-builder - Connect to a buildx builder (or buildx:// for the currently selected builder). Note: only container-backed buildx instances are currently supported
  • nerdctl-container://my-container-name - Similar to docker-container but uses nerdctl.
  • podman-container://my-container-name - Similar to docker-container but uses podman.
  • ssh://myhost - Connect to a buildkit instance over SSH. Format of the host spec should mimic the SSH command.
  • kubepod://mypod - Connect to buildkit running in a Kubernetes pod. Can also specify kubectl context and pod namespace (kubepod://mypod?context=foo&namespace=notdefault).

Buildkit Connection Examples

Option 1: Connect using defaults

copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched

Option 2: Connect to buildx

docker buildx create --name demo
copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr buildx://demo

Option 3: Buildkit in a container

export BUILDKIT_VERSION=v0.12.4
docker run \
--detach \
--rm \
--privileged \
--name buildkitd \
--entrypoint buildkitd \
"moby/buildkit:$BUILDKIT_VERSION"

copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr docker-container://buildkitd

Option 4: Buildkit over TCP

export BUILDKIT_VERSION=v0.12.4
export BUILDKIT_PORT=8888
docker run \
--detach \
--rm \
--privileged \
-p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \
--name buildkitd \
--entrypoint buildkitd \
"moby/buildkit:$BUILDKIT_VERSION" \
--addr tcp://0.0.0.0:$BUILDKIT_PORT

copa patch \
-i docker.io/library/nginx:1.21.6 \
-r nginx.1.21.6.json \
-t 1.21.6-patched \
-a tcp://0.0.0.0:$BUILDKIT_PORT
- - +
Version: Next

Custom buildkit addresses

You may need to specify a custom address using the --addr flag. Here are the supported formats:

+
    +
  • unix:///path/to/buildkit.sock - Connect to buildkit over unix socket.
  • +
  • tcp://$BUILDKIT_ADDR:$PORT - Connect to buildkit over TCP. (not recommended for security reasons)
  • +
  • docker://<docker connection spec> - Connect to docker, currently only unix sockets are supported, e.g. docker://unix:///var/run/docker.sock (or just docker://).
  • +
  • docker-container://my-buildkit-container - Connect to a buildkitd running in a docker container.
  • +
  • buildx://my-builder - Connect to a buildx builder (or buildx:// for the currently selected builder). Note: only container-backed buildx instances are currently supported
  • +
  • nerdctl-container://my-container-name - Similar to docker-container but uses nerdctl.
  • +
  • podman-container://my-container-name - Similar to docker-container but uses podman.
  • +
  • ssh://myhost - Connect to a buildkit instance over SSH. Format of the host spec should mimic the SSH command.
  • +
  • kubepod://mypod - Connect to buildkit running in a Kubernetes pod. Can also specify kubectl context and pod namespace (kubepod://mypod?context=foo&namespace=notdefault).
  • +
+

Buildkit Connection Examples

+

Option 1: Connect using defaults

+
copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched
+

Option 2: Connect to buildx

+
docker buildx create --name demo
copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr buildx://demo
+

Option 3: Buildkit in a container

+
export BUILDKIT_VERSION=v0.12.4
docker run \
--detach \
--rm \
--privileged \
--name buildkitd \
--entrypoint buildkitd \
"moby/buildkit:$BUILDKIT_VERSION"

copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr docker-container://buildkitd
+

Option 4: Buildkit over TCP

+
export BUILDKIT_VERSION=v0.12.4
export BUILDKIT_PORT=8888
docker run \
--detach \
--rm \
--privileged \
-p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \
--name buildkitd \
--entrypoint buildkitd \
"moby/buildkit:$BUILDKIT_VERSION" \
--addr tcp://0.0.0.0:$BUILDKIT_PORT

copa patch \
-i docker.io/library/nginx:1.21.6 \
-r nginx.1.21.6.json \
-t 1.21.6-patched \
-a tcp://0.0.0.0:$BUILDKIT_PORT
\ No newline at end of file diff --git a/website/next/design.html b/website/next/design.html index 3e57cf0f..2aa7b7fd 100644 --- a/website/next/design.html +++ b/website/next/design.html @@ -1,19 +1,84 @@ - + - -Design | Copacetic + +Design | Copacetic - - - + + + -
-
Version: Next

Design

Design Tenets

  • Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update. This is a raison d’etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.

  • Copa is intended to work with the existing ecosystem of container images. The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.

  • Copa is intended to allow parties other than the image authors to address container vulnerabilities. Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.

  • Copa is intended to do one thing well and be composable with other tools and processes. Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane.

Design Reasoning

The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental crane rebase:

  • Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an apt install during image creation will overwrite the dpkg status file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners.

    To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.

  • Rebasing also requires that the user knows a priori what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets.

    While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose.

    The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well.

Architecture

The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:

  • The data schema of various vulnerability scanners producing the input vulnerability report.
  • The state management of various package managers and process for applying patches appropriately through them.

Effectively, copa patch can be considered a command that bridges an extensible Parse action with an extensible Apply action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:

type UpdatePackage struct {
Name string
Version string
}

type UpdateManifest struct {
OSType string
OSVersion string
Arch string
Updates []UpdatePackage
}

type ScanReportParser interface {
Parse(reportPath string) (*UpdateManifest, error)
}

type PackageManager interface {
Apply(imagePath string, report *UpdateManifest) error
}

Implementation

copa is a pseudo-frontend to buildkit implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of LLB graphs for buildkit to execute:

  1. Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.
    • Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions.
  2. Actions to fetch and deploy tools that can be injected into the target image to perform the patching.
    • In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches.
    • In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image.
    • Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well.
  3. Actions to deploy the required patch packages to the target image.
    • copa integrates with buildkit at the API level because it uses the diff and merge graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.

Tradeoffs

  • The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:
    • By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa.
    • copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this.
  • No support for windows containers given the dependency on buildkit.
- - +
Version: Next

Design

Design Tenets

+
    +
  • +

    Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update. This is a raison d’etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.

    +
  • +
  • +

    Copa is intended to work with the existing ecosystem of container images. The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.

    +
  • +
  • +

    Copa is intended to allow parties other than the image authors to address container vulnerabilities. Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.

    +
  • +
  • +

    Copa is intended to do one thing well and be composable with other tools and processes. Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane.

    +
  • +
+

Design Reasoning

+

The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental crane rebase:

+
    +
  • +

    Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an apt install during image creation will overwrite the dpkg status file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners.

    +

    To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.

    +
  • +
  • +

    Rebasing also requires that the user knows a priori what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets.

    +

    While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose.

    +

    The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well.

    +
  • +
+

Architecture

+ +

The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:

+
    +
  • The data schema of various vulnerability scanners producing the input vulnerability report.
  • +
  • The state management of various package managers and process for applying patches appropriately through them.
  • +
+

Effectively, copa patch can be considered a command that bridges an extensible Parse action with an extensible Apply action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:

+
type UpdatePackage struct {
Name string
Version string
}

type UpdateManifest struct {
OSType string
OSVersion string
Arch string
Updates []UpdatePackage
}

type ScanReportParser interface {
Parse(reportPath string) (*UpdateManifest, error)
}

type PackageManager interface {
Apply(imagePath string, report *UpdateManifest) error
}
+

Implementation

+ +

copa is a pseudo-frontend to buildkit implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of LLB graphs for buildkit to execute:

+
    +
  1. Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image. +
      +
    • Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions.
    • +
    +
  2. +
  3. Actions to fetch and deploy tools that can be injected into the target image to perform the patching. +
      +
    • In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches.
    • +
    • In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image.
    • +
    • Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well.
    • +
    +
  4. +
  5. Actions to deploy the required patch packages to the target image. +
      +
    • copa integrates with buildkit at the API level because it uses the diff and merge graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.
    • +
    +
  6. +
+

Tradeoffs

+
    +
  • The core architectural choice of relying on packages as the unit of patching creates a couple of constraints: +
      +
    • By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa.
    • +
    • copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this.
    • +
    +
  • +
  • No support for windows containers given the dependency on buildkit.
  • +
\ No newline at end of file diff --git a/website/next/development-tips.html b/website/next/development-tips.html index fa5fdd16..03451320 100644 --- a/website/next/development-tips.html +++ b/website/next/development-tips.html @@ -1,19 +1,72 @@ - + - -Development and Testing Tips | Copacetic + +Development and Testing Tips | Copacetic - - - + + + -
-
Version: Next

Development and Testing Tips

This document provides some tips and tricks for devs to better understand what is happening under the hood of copa.

Much of the functionality of copa is implemented through the use of the BuildKit library, and in particular, direct building a Low-Level Build (LLB) intermediate representation. Most patching operations are implemented as a series of LLB stages that form a Directed Acyclic Graph (DAG) to produce the final patched image, and we'll walk through some ways to deal with the opaque nature of each operation in that graph which can otherwise make it difficult to debug or test copa.

Use the --debug flag with copa patch

It's always useful to know that code on the copa side is behaving as expected first before diving into the weeds of its interactions with BuildKit. The --debug flag will do two useful things when enabled:

  • Log debug state to stdout with the DEBU tag, including useful information such as the type of image it expects to be operating on, the list of updates and their versions it expects to apply, and any detailed errors.
  • Leave the working folder in place so that you can inspect the contents of the working files copa writes for its own use during the patching process.

For example, if you run copa patch with the --debug flag, you'll see something like the following output:

$ copa patch -i <image> -r <report> --debug
DEBU[0000] updates to apply: ...
...
WARN[0000] --debug specified, working folder at /var/folders/fx/nbhd5jln1qq3t405hz_hl4000000gn/T/copa-806164554 needs to be manually cleaned up

The folder specified defaults to the system temp folder unless the --working-folder option was specified, and you can delete it with rm -r <folder> when you're done. The working folder will usually contain the copa-out directory which contains files depending on the pkgmgr implementation, such as the probed package state or post-patching package state file for the package manager. Searching for SolveToLocal() invocations in the copa codebase will show you where these files are written.

Verify the intermediate stages of building a patched image

It's often useful to be able to inspect what the output of an intermediate LLB stage would look like after it has executed, and you can perform an analog to printf debugging by solving the LLB stage to a Docker image and then inspecting the resulting image:

// DEBUG: Solve the LLB stage to a Docker image.
if err := buildkit.SolveToDocker(ctx, dm.config.Client, &<llb.Stage>, dm.config.ConfigData, dm.config.ImageName+"-<llb.Stage suffix>"); err != nil {
return nil, err
}

For example, if you want to see what the resulting Docker image looks like at the busyBoxApplied stage, you can add the buildkit.SolveToDocker call to the end of the busyBoxApplied stage as follows. The result will be a Docker image with the -busyBoxApplied suffix to the tag that you can inspect with the docker CLI or dive tool:

busyBoxApplied := dm.config.ImageState.File(llb.Copy(toolImage, "/bin/busybox", "/bin/busybox"))
if err := buildkit.SolveToDocker(ctx, dm.config.Client, &busyBoxApplied, dm.config.ConfigData, dm.config.ImageName+"-busyBoxApplied"); err != nil {
return nil, err
}

Inspect a Docker image

Use docker to inspect the metadata of the image

For a quick check of a Docker image, the built in docker CLI commands can be useful:

  • docker inspect can show you the metadata for the image and verifying that the patching process generally preserves the metadata of the original image.
  • docker history can give you a quick overview of the layers in the image, and with the --no-trunc flag, can provide the commands that were run to create each layer.

Use dive to inspect the filesystem differences at each layer of the image

Instructions for installing and using the dive CLI tool are at https://github.com/wagoodman/dive.

dive provides a simple interface for walking the layers of the image and inspecting the files that were added or changed at each layer with your arrow keys.

  • Tab will toggle between navigating the layers and the files in the layer.
  • Filtering out unmodified files with Ctrl+U while in files view will effectively show you the file diff introduced by that layer.

In particular, if you are adding or changing any of the patching functionality, the diff view of the files in the image can be useful to verify that the expected files have actually been written to the target image.

Extract individual files from the image to inspect them

dive won't let you read the contents of the files in the image though; to do that, you can use the docker cp command to copy the files out of the image to a local folder. Note that docker cp only works with containers and not just container images, so you will need to create a container from the image and then copy the files out of the container:

id=$(docker create <image name>:<tag>)
docker cp $id:<filepath> <destination path>
docker rm -v $id

Use crane to manipulate the image or extract the image filesystem

Sometimes it's useful to be able to manipulate the image in ways that docker or dive don't support, such as extracting the entire image filesystem to a local folder. crane can be useful for this and also provides many other convenient utilities for working with container images.

For instance, to extract the filesystem of the image to a local folder, you can use crane export:

crane export <image name>:<tag> - | tar -xvf -

crane is a very flexible tool designed to work well with pipes to existing shell tools. For example, you can also use crane to do a full diff between two images as well:

 diff \
<(crane export image:tag - | tar -tvf - | sort) \
<(crane export image:tag-patched - | tar -tvf - | sort)

Run scripts interactively in an image

Some of the LLB stages effectively run shell scripts defined by copa in the image, and sometimes these need to be debugged or modified. The easiest way to do this is usually just solving the llb.Stage of interest to Docker and then running the image interactively with docker run:

docker run --rm -it --entrypoint sh <image name>:<tag>-<llb.Stage suffix>

One thing to note is that the scripts embedded in the .go files will often have an additional layer of character escapes to make them valid Go strings, so you may need to unescape them before running them interactively.

Dump the LLB Graph

Ultimately, copa is just a tool for building a BuildKit LLB graph, and you may need to understand if the LLB graph being constructed is reasonable or expected. It's helpful to have a basic understanding of how BuildKit and the operations commonly used by copa here, so it's good to be familiar with some key resources here:

The LLB graph up to any llb.Stage can be written out by marshalling it to a Definition and enumerating each of the operations to output. Using the buildctl implementation of dumpLLB as a reference, you can write a function to output the LLB graph as JSON nodes to stdout:

import "github.com/moby/buildkit/solver/pb"

// Definition of the LLB graph node to display, modify as desired.
// This version is what the buildctl tool uses.
type llbOp struct {
Op pb.Op
Digest digest.Digest
OpMetadata pb.OpMetadata
}

func outputLLBGraph(ctx context.Context, llbState *llb.State) error {
// Marshal the llb.State to a LLB definition.
def, err := llbState.Marshal(ctx)
if err != nil {
log.Errorf("Marshal to LLB failed with %s", err)
return err
}

// Format each operation node in the LLB definition into a struct.
var ops []llbOp
for _, dt := range def.Def {
var op pb.Op
if err := (&op).Unmarshal(dt); err != nil {
return errors.Wrap(err, "failed to parse op")
}
hash := digest.FromBytes(dt)
ent := llbOp{Op: op, Digest: hash, OpMetadata: def.Metadata[hash]}
ops = append(ops, ent)
}

// Output the LLB graph as JSON nodes to stdout.
// Modify as desired to output to file or other formats.
enc := json.NewEncoder(os.Stdout)
for _, op := range ops {
if err := enc.Encode(op); err != nil {
return err
}
}
return nil
}

// Within the function (e.g. with the llb.State where you want to dump the LLB graph:
...
// DEBUG: dump the LLB graph to stdout
if err := outputLLBGraph(ctx, &merged); err != nil {
return nil, err
}
...

For the definition of an LLB vertex (an Op node struct enumerated by the code snippet above), refer to https://github.com/moby/buildkit/blob/master/solver/pb/ops.proto.

Following the edges between the LLB nodes is a matter of following the resulting Digest value for the node to where it is consumed as one of the Op.inputs in another node. For example, a pretty-printed version of a LLB graph in json format focusing on a few key nodes might look like:

// Initial target image source node
{
"Op": {
"Op": {
"source": {
"identifier": "docker-image://mcr.microsoft.com/oss/open-policy-agent/opa:0.46.0"
}
},
"platform": {
"Architecture": "amd64",
"OS": "linux"
},
"constraints": {}
},
"Digest": "sha256:a86ddb9065d07c67dc838e11a81ff54020531c4ca2d85fb20574088222da8b30",
"OpMetadata": {
"caps": {
"source.image": true
}
}
}

// ..
// Skipping intermediate graph nodes
// ...

// Diffing out the manifest updates layer
{
"Op": {
"inputs": [
{
"digest": "sha256:cbc31a96266caa8cd5ced38a1f8e97de9f13fafb23dbe9e342125569cd4d5018",
"index": 0
},
{
"digest": "sha256:9f798b2e38e054aadf1ee66c7eb7230c65be324c26d8739a3d5fa2d5da90e5de",
"index": 0
}
],
"Op": {
"diff": {
"lower": {
"input": 0
},
"upper": {
"input": 1
}
}
},
"constraints": {}
},
"Digest": "sha256:f337f99144ab75fee8593ec6531caa9ebace06aaca07614778b7c0ca5c816135",
"OpMetadata": {
"caps": {
"diffop": true
}
}
}

// Merging all the target image with the patch layer and the manifest updates layer
{
"Op": {
"inputs": [
{
"digest": "sha256:a86ddb9065d07c67dc838e11a81ff54020531c4ca2d85fb20574088222da8b30",
"index": 0
},
{
"digest": "sha256:1c3ad84c0de7e1384d727f6168db3f1f8fb632c0086760aff1786a7e89562d13",
"index": 0
},
{
"digest": "sha256:f337f99144ab75fee8593ec6531caa9ebace06aaca07614778b7c0ca5c816135",
"index": 0
}
],
"Op": {
"merge": {
"inputs": [
{
"input": 0
},
{
"input": 1
},
{
"input": 2
}
]
}
},
"constraints": {}
},
"Digest": "sha256:e6a4086e2caf03c8814fc5388dd7d2e45420e1c5281e0df0d3db375d3f00358a",
"OpMetadata": {
"caps": {
"mergeop": true
}
}
}

//...

- - +
Version: Next

Development and Testing Tips

+

This document provides some tips and tricks for devs to better understand what is happening under the hood of copa.

+

Much of the functionality of copa is implemented through the use of the BuildKit library, and in particular, direct building a Low-Level Build (LLB) intermediate representation. Most patching operations are implemented as a series of LLB stages that form a Directed Acyclic Graph (DAG) to produce the final patched image, and we'll walk through some ways to deal with the opaque nature of each operation in that graph which can otherwise make it difficult to debug or test copa.

+

Use the --debug flag with copa patch

+

It's always useful to know that code on the copa side is behaving as expected first before diving into the weeds of its interactions with BuildKit. The --debug flag will do two useful things when enabled:

+
    +
  • Log debug state to stdout with the DEBU tag, including useful information such as the type of image it expects to be operating on, the list of updates and their versions it expects to apply, and any detailed errors.
  • +
  • Leave the working folder in place so that you can inspect the contents of the working files copa writes for its own use during the patching process.
  • +
+

For example, if you run copa patch with the --debug flag, you'll see something like the following output:

+
$ copa patch -i <image> -r <report> --debug
DEBU[0000] updates to apply: ...
...
WARN[0000] --debug specified, working folder at /var/folders/fx/nbhd5jln1qq3t405hz_hl4000000gn/T/copa-806164554 needs to be manually cleaned up
+

The folder specified defaults to the system temp folder unless the --working-folder option was specified, and you can delete it with rm -r <folder> when you're done. The working folder will usually contain the copa-out directory which contains files depending on the pkgmgr implementation, such as the probed package state or post-patching package state file for the package manager. Searching for SolveToLocal() invocations in the copa codebase will show you where these files are written.

+

Verify the intermediate stages of building a patched image

+

It's often useful to be able to inspect what the output of an intermediate LLB stage would look like after it has executed, and you can perform an analog to printf debugging by solving the LLB stage to a Docker image and then inspecting the resulting image:

+
// DEBUG: Solve the LLB stage to a Docker image.
if err := buildkit.SolveToDocker(ctx, dm.config.Client, &<llb.Stage>, dm.config.ConfigData, dm.config.ImageName+"-<llb.Stage suffix>"); err != nil {
return nil, err
}
+

For example, if you want to see what the resulting Docker image looks like at the busyBoxApplied stage, you can add the buildkit.SolveToDocker call to the end of the busyBoxApplied stage as follows. The result will be a Docker image with the -busyBoxApplied suffix to the tag that you can inspect with the docker CLI or dive tool:

+
busyBoxApplied := dm.config.ImageState.File(llb.Copy(toolImage, "/bin/busybox", "/bin/busybox"))
if err := buildkit.SolveToDocker(ctx, dm.config.Client, &busyBoxApplied, dm.config.ConfigData, dm.config.ImageName+"-busyBoxApplied"); err != nil {
return nil, err
}
+

Inspect a Docker image

+

Use docker to inspect the metadata of the image

+

For a quick check of a Docker image, the built in docker CLI commands can be useful:

+
    +
  • docker inspect can show you the metadata for the image and verifying that the patching process generally preserves the metadata of the original image.
  • +
  • docker history can give you a quick overview of the layers in the image, and with the --no-trunc flag, can provide the commands that were run to create each layer.
  • +
+

Use dive to inspect the filesystem differences at each layer of the image

+

Instructions for installing and using the dive CLI tool are at https://github.com/wagoodman/dive.

+

dive provides a simple interface for walking the layers of the image and inspecting the files that were added or changed at each layer with your arrow keys.

+
    +
  • Tab will toggle between navigating the layers and the files in the layer.
  • +
  • Filtering out unmodified files with Ctrl+U while in files view will effectively show you the file diff introduced by that layer.
  • +
+

In particular, if you are adding or changing any of the patching functionality, the diff view of the files in the image can be useful to verify that the expected files have actually been written to the target image.

+

Extract individual files from the image to inspect them

+

dive won't let you read the contents of the files in the image though; to do that, you can use the docker cp command to copy the files out of the image to a local folder. Note that docker cp only works with containers and not just container images, so you will need to create a container from the image and then copy the files out of the container:

+
id=$(docker create <image name>:<tag>)
docker cp $id:<filepath> <destination path>
docker rm -v $id
+

Use crane to manipulate the image or extract the image filesystem

+

Sometimes it's useful to be able to manipulate the image in ways that docker or dive don't support, such as extracting the entire image filesystem to a local folder. crane can be useful for this and also provides many other convenient utilities for working with container images.

+

For instance, to extract the filesystem of the image to a local folder, you can use crane export:

+
crane export <image name>:<tag> - | tar -xvf -
+

crane is a very flexible tool designed to work well with pipes to existing shell tools. For example, you can also use crane to do a full diff between two images as well:

+
 diff \
<(crane export image:tag - | tar -tvf - | sort) \
<(crane export image:tag-patched - | tar -tvf - | sort)
+

Run scripts interactively in an image

+

Some of the LLB stages effectively run shell scripts defined by copa in the image, and sometimes these need to be debugged or modified. The easiest way to do this is usually just solving the llb.Stage of interest to Docker and then running the image interactively with docker run:

+
docker run --rm -it --entrypoint sh <image name>:<tag>-<llb.Stage suffix>
+

One thing to note is that the scripts embedded in the .go files will often have an additional layer of character escapes to make them valid Go strings, so you may need to unescape them before running them interactively.

+

Dump the LLB Graph

+

Ultimately, copa is just a tool for building a BuildKit LLB graph, and you may need to understand if the LLB graph being constructed is reasonable or expected. It's helpful to have a basic understanding of how BuildKit and the operations commonly used by copa here, so it's good to be familiar with some key resources here:

+ +

The LLB graph up to any llb.Stage can be written out by marshalling it to a Definition and enumerating each of the operations to output. Using the buildctl implementation of dumpLLB as a reference, you can write a function to output the LLB graph as JSON nodes to stdout:

+
import "github.com/moby/buildkit/solver/pb"

// Definition of the LLB graph node to display, modify as desired.
// This version is what the buildctl tool uses.
type llbOp struct {
Op pb.Op
Digest digest.Digest
OpMetadata pb.OpMetadata
}

func outputLLBGraph(ctx context.Context, llbState *llb.State) error {
// Marshal the llb.State to a LLB definition.
def, err := llbState.Marshal(ctx)
if err != nil {
log.Errorf("Marshal to LLB failed with %s", err)
return err
}

// Format each operation node in the LLB definition into a struct.
var ops []llbOp
for _, dt := range def.Def {
var op pb.Op
if err := (&op).Unmarshal(dt); err != nil {
return errors.Wrap(err, "failed to parse op")
}
hash := digest.FromBytes(dt)
ent := llbOp{Op: op, Digest: hash, OpMetadata: def.Metadata[hash]}
ops = append(ops, ent)
}

// Output the LLB graph as JSON nodes to stdout.
// Modify as desired to output to file or other formats.
enc := json.NewEncoder(os.Stdout)
for _, op := range ops {
if err := enc.Encode(op); err != nil {
return err
}
}
return nil
}

// Within the function (e.g. with the llb.State where you want to dump the LLB graph:
...
// DEBUG: dump the LLB graph to stdout
if err := outputLLBGraph(ctx, &merged); err != nil {
return nil, err
}
...
+

For the definition of an LLB vertex (an Op node struct enumerated by the code snippet above), refer to https://github.com/moby/buildkit/blob/master/solver/pb/ops.proto.

+

Following the edges between the LLB nodes is a matter of following the resulting Digest value for the node to where it is consumed as one of the Op.inputs in another node. For example, a pretty-printed version of a LLB graph in json format focusing on a few key nodes might look like:

+
// Initial target image source node
{
"Op": {
"Op": {
"source": {
"identifier": "docker-image://mcr.microsoft.com/oss/open-policy-agent/opa:0.46.0"
}
},
"platform": {
"Architecture": "amd64",
"OS": "linux"
},
"constraints": {}
},
"Digest": "sha256:a86ddb9065d07c67dc838e11a81ff54020531c4ca2d85fb20574088222da8b30",
"OpMetadata": {
"caps": {
"source.image": true
}
}
}

// ..
// Skipping intermediate graph nodes
// ...

// Diffing out the manifest updates layer
{
"Op": {
"inputs": [
{
"digest": "sha256:cbc31a96266caa8cd5ced38a1f8e97de9f13fafb23dbe9e342125569cd4d5018",
"index": 0
},
{
"digest": "sha256:9f798b2e38e054aadf1ee66c7eb7230c65be324c26d8739a3d5fa2d5da90e5de",
"index": 0
}
],
"Op": {
"diff": {
"lower": {
"input": 0
},
"upper": {
"input": 1
}
}
},
"constraints": {}
},
"Digest": "sha256:f337f99144ab75fee8593ec6531caa9ebace06aaca07614778b7c0ca5c816135",
"OpMetadata": {
"caps": {
"diffop": true
}
}
}

// Merging all the target image with the patch layer and the manifest updates layer
{
"Op": {
"inputs": [
{
"digest": "sha256:a86ddb9065d07c67dc838e11a81ff54020531c4ca2d85fb20574088222da8b30",
"index": 0
},
{
"digest": "sha256:1c3ad84c0de7e1384d727f6168db3f1f8fb632c0086760aff1786a7e89562d13",
"index": 0
},
{
"digest": "sha256:f337f99144ab75fee8593ec6531caa9ebace06aaca07614778b7c0ca5c816135",
"index": 0
}
],
"Op": {
"merge": {
"inputs": [
{
"input": 0
},
{
"input": 1
},
{
"input": 2
}
]
}
},
"constraints": {}
},
"Digest": "sha256:e6a4086e2caf03c8814fc5388dd7d2e45420e1c5281e0df0d3db375d3f00358a",
"OpMetadata": {
"caps": {
"mergeop": true
}
}
}

//...

\ No newline at end of file diff --git a/website/next/faq.html b/website/next/faq.html index 07e6219a..3f22859f 100644 --- a/website/next/faq.html +++ b/website/next/faq.html @@ -1,19 +1,29 @@ - + - -FAQ | Copacetic + +FAQ | Copacetic - - - + + + -
-
Version: Next

FAQ

What kind of vulnerabilities can Copa patch?

Copa is capable of patching "OS level" vulnerabilities. This includes packages (like openssl) in the image that are managed by a package manager such as apt or yum. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see below for more details).

What kind of vulnerabilities can Copa not patch?

Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the golang.org/x/net module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level.

To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like http://archive.ubuntu.com/ubuntu/ for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications.

Can I replace the package repositories in the image with my own?

caution

Experimental: This feature might change without preserving backwards compatibility.

Copa does not support replacing the repositories in the package managers with alternatives. Images must already use the intended package repositories. For example, for debian, updating /etc/apt/sources.list from http://archive.ubuntu.com/ubuntu/ to a mirror, such as https://mirrors.wikimedia.org/ubuntu/.

If you need the tooling image to use a different package repository, you can create a source policy to define a replacement image and/or pin to a digest. For example, the following source policy replaces docker.io/library/debian:11-slim image with foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5:

cat <<EOF > source-policy.json
{
"rules": [
{
"action": "CONVERT",
"selector": {
"identifier": "docker-image://docker.io/library/debian:11-slim"
},
"updates": {
"identifier": "docker-image://foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5"
}
}
]
}
EOF

export EXPERIMENTAL_BUILDKIT_SOURCE_POLICY=source-policy.json

Tooling image for Debian-based images are docker.io/library/debian:11-slim and RPM-based repos are mcr.microsoft.com/cbl-mariner/base/core:2.0.

For more information on source policies, see Buildkit Source Policies.

- - +
Version: Next

FAQ

What kind of vulnerabilities can Copa patch?

+

Copa is capable of patching "OS level" vulnerabilities. This includes packages (like openssl) in the image that are managed by a package manager such as apt or yum. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see below for more details).

+

What kind of vulnerabilities can Copa not patch?

+

Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the golang.org/x/net module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level.

+

To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like http://archive.ubuntu.com/ubuntu/ for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications.

+

Can I replace the package repositories in the image with my own?

+
caution

Experimental: This feature might change without preserving backwards compatibility.

+

Copa does not support replacing the repositories in the package managers with alternatives. Images must already use the intended package repositories. For example, for debian, updating /etc/apt/sources.list from http://archive.ubuntu.com/ubuntu/ to a mirror, such as https://mirrors.wikimedia.org/ubuntu/.

+

If you need the tooling image to use a different package repository, you can create a source policy to define a replacement image and/or pin to a digest. For example, the following source policy replaces docker.io/library/debian:11-slim image with foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5:

+
cat <<EOF > source-policy.json
{
"rules": [
{
"action": "CONVERT",
"selector": {
"identifier": "docker-image://docker.io/library/debian:11-slim"
},
"updates": {
"identifier": "docker-image://foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5"
}
}
]
}
EOF

export EXPERIMENTAL_BUILDKIT_SOURCE_POLICY=source-policy.json
+
+

Tooling image for Debian-based images are docker.io/library/debian:11-slim and RPM-based repos are mcr.microsoft.com/cbl-mariner/base/core:2.0.

+
+

For more information on source policies, see Buildkit Source Policies.

\ No newline at end of file diff --git a/website/next/github-action.html b/website/next/github-action.html index 466514e1..50aa788b 100644 --- a/website/next/github-action.html +++ b/website/next/github-action.html @@ -1,19 +1,17 @@ - + - -Github Action | Copacetic + +Github Action | Copacetic - - - + + + -
-
Version: Next

Github Action

The Copa Github Action allows you patch vulnerable containers in your GitHub Actions workflows using Copa.

Please refer to Copa Github Action for more details on how to use it.

- - +
Version: Next

Github Action

The Copa Github Action allows you patch vulnerable containers in your GitHub Actions workflows using Copa.

+

Please refer to Copa Github Action for more details on how to use it.

\ No newline at end of file diff --git a/website/next/installation.html b/website/next/installation.html index 2fc33280..e05fbfbc 100644 --- a/website/next/installation.html +++ b/website/next/installation.html @@ -1,19 +1,28 @@ - + - -Installation | Copacetic + +Installation | Copacetic - - - + + + -
-
Version: Next

Installation

Homebrew

On macOS and Linux, copa can be installed via Homebrew:

brew install copa

GitHub

You can download the latest and previous versions of copa from the GitHub releases page.

Development Setup

Prequisitives

git clone https://github.com/project-copacetic/copacetic
cd copacetic
make
# OPTIONAL: install copa to a pathed folder
sudo mv dist/linux_amd64/release/copa /usr/local/bin/
- - +
Version: Next

Installation

Homebrew

+

On macOS and Linux, copa can be installed via Homebrew:

+
brew install copa
+

GitHub

+

You can download the latest and previous versions of copa from the GitHub releases page.

+

Development Setup

+

Prequisitives

+ +
git clone https://github.com/project-copacetic/copacetic
cd copacetic
make
# OPTIONAL: install copa to a pathed folder
sudo mv dist/linux_amd64/release/copa /usr/local/bin/
\ No newline at end of file diff --git a/website/next/maintainer-guidelines.html b/website/next/maintainer-guidelines.html index 6560d38e..f1e2c188 100644 --- a/website/next/maintainer-guidelines.html +++ b/website/next/maintainer-guidelines.html @@ -1,19 +1,66 @@ - + - -Maintainer Guidelines | Copacetic + +Maintainer Guidelines | Copacetic - - - + + + -
-
Version: Next

Maintainer Guidelines

Semantic Release Management

This project uses go-semantic-release to automatically generate the appropriate semantic version and changelog for a release based on Angular commit message format. Of note to maintainers is the need to enforce an empty line-separate format:

<HEADER>
<-- blank line -->
<BODY>
<-- blank line -->
<FOOTER>

For contributor PRs, instead of trying to ensure adherence in every commit message, it's easiest to adopt a squash and merge strategy so that the PR description is used as the final commit description with the appropriate semantic release format.

In addition to the semantic release types called out in the contributor pull request guidelines, there are several other categories supported by the default changelog generator that maintainers should be aware of:

  • chore: Reserved for automated maintenance changes, such as minor version go dependency updates initiated by Dependabot.
  • revert: Maintainers should use this to mark commits that revert a previous commit, followed by the header of the reverted commit. The message body should include the SHA of the reverted commit, as well as a clear description of the reason for the revert.
  • style: This is unused for this project.

There are also two special categories to be added to the message footer that maintainers need to pay special attention to when merging changes:

Breaking change

Breaking changes should be described in the footer as follows:

BREAKING CHANGE: <breaking change summary>
<-- blank line -->
<breaking change description & migration instructions>
<-- blank line -->
<-- blank line -->
Closes #<issue number>

Note that this project currently uses the allow-initial-development-versions flag for go-semantic-release, so breaking changes will still be handled as minor releases until the workflow is updated for the v1.0.0 release.

Deprecation

DEPRECATED: <summary of deprecated feature>
<-- blank line -->
<deprecated feature description & migration/workaround instructions>
<-- blank line -->
<-- blank line -->
Closes #<issue number>

Publishing a Release

To avoid inconsistencies in tagging and release branching, this project uses the Publish release GitHub Actions workflow to automate the creation of releases.

Publish a new major/minor version release

  1. Review the main branch to ensure that it has all the desired changes for the new release branch and that there are no PR merge workflows in flight.
  2. Click Run workflow on the Publish release against the main branch. This will:
    1. Create a new tag with the incremented semantic version (e.g. v0.9.0) against the latest commit in main.
    2. Create a new GitHub release against that tag with an automatically generated changelog.
    3. Build and upload the new release version of Copa to the GitHub release.
    4. Create a new release branch if it does not already exist (e.g. release-0.9)
  3. Verify that the workflow ran successfully and review the expected outputs listed above.

Publish a patch revision release

  1. Review the appropriate release branch that the revision patches (e.g. release-0.9 for an anticipated new v0.9.x tag) to ensure that it has all the desired changes for the release and that there are no PR merge workflows in flight.
    1. If there are fixes in main intended for the patch release in the latest release branch, they need to be manually ported to the release branch first and the revision released from there.
  2. Click Run workflow on the Publish release against the target release-x.y branch. This will:
    1. Create a new tag with the incremented semantic version (e.g. v0.9.4) against the latest commit in the release branch.
    2. Create a new GitHub release against that tag with an automatically generated changelog.
    3. Build and upload the new release version of Copa to the GitHub release.
  3. Verify that the workflow ran successfully and review the expected outputs listed above.
- - +
Version: Next

Maintainer Guidelines

+

Semantic Release Management

+

This project uses go-semantic-release to automatically generate the appropriate semantic version and changelog for a release based on Angular commit message format. Of note to maintainers is the need to enforce an empty line-separate format:

+
<HEADER>
<-- blank line -->
<BODY>
<-- blank line -->
<FOOTER>
+

For contributor PRs, instead of trying to ensure adherence in every commit message, it's easiest to adopt a squash and merge strategy so that the PR description is used as the final commit description with the appropriate semantic release format.

+

In addition to the semantic release types called out in the contributor pull request guidelines, there are several other categories supported by the default changelog generator that maintainers should be aware of:

+
    +
  • chore: Reserved for automated maintenance changes, such as minor version go dependency updates initiated by Dependabot.
  • +
  • revert: Maintainers should use this to mark commits that revert a previous commit, followed by the header of the reverted commit. The message body should include the SHA of the reverted commit, as well as a clear description of the reason for the revert.
  • +
  • style: This is unused for this project.
  • +
+

There are also two special categories to be added to the message footer that maintainers need to pay special attention to when merging changes:

+

Breaking change

+

Breaking changes should be described in the footer as follows:

+
BREAKING CHANGE: <breaking change summary>
<-- blank line -->
<breaking change description & migration instructions>
<-- blank line -->
<-- blank line -->
Closes #<issue number>
+
+

Note that this project currently uses the allow-initial-development-versions flag for go-semantic-release, so breaking changes will still be handled as minor releases until the workflow is updated for the v1.0.0 release.

+
+

Deprecation

+
DEPRECATED: <summary of deprecated feature>
<-- blank line -->
<deprecated feature description & migration/workaround instructions>
<-- blank line -->
<-- blank line -->
Closes #<issue number>
+

Publishing a Release

+

To avoid inconsistencies in tagging and release branching, this project uses the Publish release GitHub Actions workflow to automate the creation of releases.

+

Publish a new major/minor version release

+
    +
  1. Review the main branch to ensure that it has all the desired changes for the new release branch and that there are no PR merge workflows in flight.
  2. +
  3. Click Run workflow on the Publish release against the main branch. This will: +
      +
    1. Create a new tag with the incremented semantic version (e.g. v0.9.0) against the latest commit in main.
    2. +
    3. Create a new GitHub release against that tag with an automatically generated changelog.
    4. +
    5. Build and upload the new release version of Copa to the GitHub release.
    6. +
    7. Create a new release branch if it does not already exist (e.g. release-0.9)
    8. +
    +
  4. +
  5. Verify that the workflow ran successfully and review the expected outputs listed above.
  6. +
+

Publish a patch revision release

+
    +
  1. Review the appropriate release branch that the revision patches (e.g. release-0.9 for an anticipated new v0.9.x tag) to ensure that it has all the desired changes for the release and that there are no PR merge workflows in flight. +
      +
    1. If there are fixes in main intended for the patch release in the latest release branch, they need to be manually ported to the release branch first and the revision released from there.
    2. +
    +
  2. +
  3. Click Run workflow on the Publish release against the target release-x.y branch. This will: +
      +
    1. Create a new tag with the incremented semantic version (e.g. v0.9.4) against the latest commit in the release branch.
    2. +
    3. Create a new GitHub release against that tag with an automatically generated changelog.
    4. +
    5. Build and upload the new release version of Copa to the GitHub release.
    6. +
    +
  4. +
  5. Verify that the workflow ran successfully and review the expected outputs listed above.
  6. +
\ No newline at end of file diff --git a/website/next/output.html b/website/next/output.html index d7cd257b..b08392b4 100644 --- a/website/next/output.html +++ b/website/next/output.html @@ -1,19 +1,32 @@ - + - -Output | Copacetic + +Output | Copacetic - - - + + + -
-
Version: Next

Output

caution

Experimental: This feature might change without preserving backwards compatibility.

Copa optionally outputs a Vulnerability Exploitability eXchange (VEX) file as a result of the patching process to surface the vulnerabilities and packages that were patched.

Currently, Copa supports the OpenVEX format, but it can be extended to support other formats.

OpenVEX

OpenVEX is an implementation of Vulnerability Exploitability eXchange (VEX) format. For more information, see OpenVEX specification.

tip
  • Use COPA_VEX_AUTHOR environment variable to set the author of the VEX document. If it's not set, the author will default to Project Copacetic.

  • A VEX document must contain at least one VEX statement. If there are no fixed vulnerabilities, Copa will not generate a VEX document.

To generate a VEX document using OpenVEX, use --format="openvex" flag, and use --output to specify a file path. For example:

copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --format="openvex" --output "nginx.1.21.6-vex.json"

This will generate a VEX Document that looks like:

{
"@context": "https://openvex.dev/ns",
"@id": "https://openvex.dev/docs/public/vex-a6c44ec1d79e9dd4190dc01b4ecf7527ebb26bd37c01e32e6efcd203ae00d2a5",
"author": "Project Copacetic",
"timestamp": "2023-10-11T00:15:00.114768055Z",
"version": 1,
"tooling": "Project Copacetic",
"statements": [
{
"vulnerability": {
"@id": "CVE-2021-22945"
},
"products": [
{
"@id": "pkg:oci/docker.io/library/nginx:1.21.6-patched",
"subcomponents": [
{
"@id": "pkg:deb/debian/curl@7.74.0-1.3+deb11u2?arch=amd64"
},
{
"@id": "pkg:deb/debian/libcurl4@7.74.0-1.3+deb11u2?arch=amd64"
}
]
}
],
"status": "fixed"
},
...
}
- - +
Version: Next

Output

caution

Experimental: This feature might change without preserving backwards compatibility.

+

Copa optionally outputs a Vulnerability Exploitability eXchange (VEX) file as a result of the patching process to surface the vulnerabilities and packages that were patched.

+

Currently, Copa supports the OpenVEX format, but it can be extended to support other formats.

+

OpenVEX

+

OpenVEX is an implementation of Vulnerability Exploitability eXchange (VEX) format. For more information, see OpenVEX specification.

+
tip
    +
  • +

    Use COPA_VEX_AUTHOR environment variable to set the author of the VEX document. If it's not set, the author will default to Project Copacetic.

    +
  • +
  • +

    A VEX document must contain at least one VEX statement. If there are no fixed vulnerabilities, Copa will not generate a VEX document.

    +
  • +
+

To generate a VEX document using OpenVEX, use --format="openvex" flag, and use --output to specify a file path. For example:

+
copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --format="openvex" --output "nginx.1.21.6-vex.json"
+

This will generate a VEX Document that looks like:

+
{
"@context": "https://openvex.dev/ns",
"@id": "https://openvex.dev/docs/public/vex-a6c44ec1d79e9dd4190dc01b4ecf7527ebb26bd37c01e32e6efcd203ae00d2a5",
"author": "Project Copacetic",
"timestamp": "2023-10-11T00:15:00.114768055Z",
"version": 1,
"tooling": "Project Copacetic",
"statements": [
{
"vulnerability": {
"@id": "CVE-2021-22945"
},
"products": [
{
"@id": "pkg:oci/docker.io/library/nginx:1.21.6-patched",
"subcomponents": [
{
"@id": "pkg:deb/debian/curl@7.74.0-1.3+deb11u2?arch=amd64"
},
{
"@id": "pkg:deb/debian/libcurl4@7.74.0-1.3+deb11u2?arch=amd64"
}
]
}
],
"status": "fixed"
},
...
}
\ No newline at end of file diff --git a/website/next/quick-start.html b/website/next/quick-start.html index fb891764..8bdcc28f 100644 --- a/website/next/quick-start.html +++ b/website/next/quick-start.html @@ -1,19 +1,71 @@ - + - -Quick Start | Copacetic + +Quick Start | Copacetic - - - + + + -
-
Version: Next

Quick Start

This sample illustrates how to patch containers using vulnerability reports with copa.

Prerequisites

  • Linux or macOS configured through the setup instructions. This includes:
    • copa tool built & pathed.
    • buildkit daemon installed & pathed. Examples
      • The docker daemon runs a buildkit service in-process. If you are using this for your buildkit instance, Docker must have the containerd image store feature enabled.
      • If you are using a buildx instance, or using buildkitd directly, there is no need to enable the containerd image store. However, only images in a remote registry can be patched using these methods.
    • docker daemon running and CLI installed & pathed.
    • trivy CLI installed & pathed.

Sample Steps

  1. Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:

    trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6

    You can also see the existing patchable vulnerabilities in table form on the shell with:

    trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6
  2. To patch the image, use the Trivy report and specify a buildkit instance to connect to:

    By default copa will attempt to auto-connect to an instance in order:

    1. Default docker buildkit endpoint (requires at least docker v24.0 with containerd image store support enabled)
    2. Currently selected buildx builder (see: docker buildx --help)
    3. buildkit daemon at the default address /run/buildkit/buildkitd.sock

    If an instance doesn't exist or that instance doesn't support all the features copa needs the next will be attempted. Please see custom buildkit addresses for more information.

    After setting up the buildkit instance, run the following command to patch the image:

    copa patch -r nginx.1.21.6.json -i docker.io/library/nginx:1.21.6

    In any of these cases, copa is non-destructive and exports a new image with the specified 1.21.6-patched label to the local Docker daemon.

    note

    If you're running this sample against an image from a private registry instead,ensure that the credentials are configured in the default Docker config.json before running copa patch, for example, via docker login -u <user> -p <password> <registry>.

    note

    If you're scanning and patching an image that is local-only (i.e. built or tagged locally but not pushed to a registry), copa is limited to using docker's built-in buildkit service, and must use the containerd image store feature. This is because only docker's built-in buildkit service has access to the docker image store (see Prerequisites for more information.)

  3. Scan the patched image and verify that the vulnerabilities have been patched:

    trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched

    You can also inspect the structure of the patched image with docker history to see the new patch layer appended to the image:

    $ docker history docker.io/library/nginx:1.21.6-patched
    IMAGE CREATED CREATED BY SIZE COMMENT
    262dacfeb193 About a minute ago mount / from exec sh -c apt install --no-ins… 41.1MB buildkit.exporter.image.v0
    <missing> 20 months ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
    <missing> 20 months ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B
    <missing> 20 months ago /bin/sh -c #(nop) EXPOSE 80 0B
    <missing> 20 months ago /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr… 0B
    <missing> 20 months ago /bin/sh -c #(nop) COPY file:09a214a3e07c919a… 16.4kB
    <missing> 20 months ago /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7… 12.3kB
    <missing> 20 months ago /bin/sh -c #(nop) COPY file:0b866ff3fc1ef5b0… 12.3kB
    <missing> 20 months ago /bin/sh -c #(nop) COPY file:65504f71f5855ca0… 8.19kB
    <missing> 20 months ago /bin/sh -c set -x && addgroup --system -… 64.5MB
    <missing> 20 months ago /bin/sh -c #(nop) ENV PKG_RELEASE=1~bullseye 0B
    <missing> 20 months ago /bin/sh -c #(nop) ENV NJS_VERSION=0.7.3 0B
    <missing> 20 months ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.21.6 0B
    <missing> 20 months ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do… 0B
    <missing> 20 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
    <missing> 20 months ago /bin/sh -c #(nop) ADD file:134f25aec8adf83cb… 91.8MB
  4. Run the container to verify that the image has no regressions:

    $ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched
    /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
    /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
    10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
    10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
    /docker-entrypoint.sh: Configuration complete; ready for start up
    2024/01/22 23:32:54 [notice] 1#1: using the "epoll" event method
    2024/01/22 23:32:54 [notice] 1#1: nginx/1.21.6
    2024/01/22 23:32:54 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
    2024/01/22 23:32:54 [notice] 1#1: OS: Linux 6.2.0-1018-azure
    2024/01/22 23:32:54 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
    2024/01/22 23:32:54 [notice] 1#1: start worker processes

    You can stop the container by opening a new shell instance and running: docker stop nginx-test

- - +
Version: Next

Quick Start

This sample illustrates how to patch containers using vulnerability reports with copa.

+

Prerequisites

+
    +
  • Linux or macOS configured through the setup instructions. This includes: +
      +
    • copa tool built & pathed.
    • +
    • buildkit daemon installed & pathed. Examples +
        +
      • The docker daemon runs a buildkit service in-process. If you are using this for your buildkit instance, Docker must have the containerd image store feature enabled.
      • +
      • If you are using a buildx instance, or using buildkitd directly, there is no need to enable the containerd image store. However, only images in a remote registry can be patched using these methods.
      • +
      +
    • +
    • docker daemon running and CLI installed & pathed.
    • +
    • trivy CLI installed & pathed. + +
    • +
    +
  • +
+

Sample Steps

+
    +
  1. +

    Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:

    +
    trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6
    +

    You can also see the existing patchable vulnerabilities in table form on the shell with:

    +
    trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6
    +
  2. +
  3. +

    To patch the image, use the Trivy report and specify a buildkit instance to connect to:

    +

    By default copa will attempt to auto-connect to an instance in order:

    +
      +
    1. Default docker buildkit endpoint (requires at least docker v24.0 with containerd image store support enabled)
    2. +
    3. Currently selected buildx builder (see: docker buildx --help)
    4. +
    5. buildkit daemon at the default address /run/buildkit/buildkitd.sock
    6. +
    +

    If an instance doesn't exist or that instance doesn't support all the features copa needs the next will be attempted. Please see custom buildkit addresses for more information.

    +

    After setting up the buildkit instance, run the following command to patch the image:

    +
    copa patch -r nginx.1.21.6.json -i docker.io/library/nginx:1.21.6
    +

    In any of these cases, copa is non-destructive and exports a new image with the specified 1.21.6-patched label to the local Docker daemon.

    +
    note

    If you're running this sample against an image from a private registry instead,ensure that the credentials are configured in the default Docker config.json before running copa patch, for example, via docker login -u <user> -p <password> <registry>.

    +
    note

    If you're scanning and patching an image that is local-only (i.e. built or tagged locally but not pushed to a registry), copa is limited to using docker's built-in buildkit service, and must use the containerd image store feature. This is because only docker's built-in buildkit service has access to the docker image store (see Prerequisites for more information.)

    +
  4. +
  5. +

    Scan the patched image and verify that the vulnerabilities have been patched:

    +
    trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched
    +

    You can also inspect the structure of the patched image with docker history to see the new patch layer appended to the image:

    +
    $ docker history docker.io/library/nginx:1.21.6-patched
    IMAGE CREATED CREATED BY SIZE COMMENT
    262dacfeb193 About a minute ago mount / from exec sh -c apt install --no-ins… 41.1MB buildkit.exporter.image.v0
    <missing> 20 months ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
    <missing> 20 months ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B
    <missing> 20 months ago /bin/sh -c #(nop) EXPOSE 80 0B
    <missing> 20 months ago /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr… 0B
    <missing> 20 months ago /bin/sh -c #(nop) COPY file:09a214a3e07c919a… 16.4kB
    <missing> 20 months ago /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7… 12.3kB
    <missing> 20 months ago /bin/sh -c #(nop) COPY file:0b866ff3fc1ef5b0… 12.3kB
    <missing> 20 months ago /bin/sh -c #(nop) COPY file:65504f71f5855ca0… 8.19kB
    <missing> 20 months ago /bin/sh -c set -x && addgroup --system -… 64.5MB
    <missing> 20 months ago /bin/sh -c #(nop) ENV PKG_RELEASE=1~bullseye 0B
    <missing> 20 months ago /bin/sh -c #(nop) ENV NJS_VERSION=0.7.3 0B
    <missing> 20 months ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.21.6 0B
    <missing> 20 months ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do… 0B
    <missing> 20 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
    <missing> 20 months ago /bin/sh -c #(nop) ADD file:134f25aec8adf83cb… 91.8MB
    +
  6. +
  7. +

    Run the container to verify that the image has no regressions:

    +
    $ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched
    /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
    /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
    10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
    10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
    /docker-entrypoint.sh: Configuration complete; ready for start up
    2024/01/22 23:32:54 [notice] 1#1: using the "epoll" event method
    2024/01/22 23:32:54 [notice] 1#1: nginx/1.21.6
    2024/01/22 23:32:54 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
    2024/01/22 23:32:54 [notice] 1#1: OS: Linux 6.2.0-1018-azure
    2024/01/22 23:32:54 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
    2024/01/22 23:32:54 [notice] 1#1: start worker processes
    +

    You can stop the container by opening a new shell instance and running: docker stop nginx-test

    +
  8. +
\ No newline at end of file diff --git a/website/next/release.html b/website/next/release.html index 606d8728..42edb5d0 100644 --- a/website/next/release.html +++ b/website/next/release.html @@ -1,19 +1,24 @@ - + - -Release Process | Copacetic + +Release Process | Copacetic - - - + + + -
-
Version: Next

Release Process

Overview

The release process for Copacetic uses GoReleaser.

Once you are ready to cut a new release, checkout the release branch and tag it with the respective version.

```
git checkout <BRANCH NAME>
git pull origin <BRANCH NAME>
git tag -a <NEW VERSION> -m '<NEW VERSION>'
git push origin <NEW VERSION>
```

Publishing

  1. GoReleaser will create a new release, review and edit it at https://github.com/project-copacetic/copacetic/releases
  2. Review the respective copa-action image at: https://github.com/orgs/project-copacetic/packages/container/package/copa-action
- - +
Version: Next

Release Process

Overview

+

The release process for Copacetic uses GoReleaser.

+

Once you are ready to cut a new release, checkout the release branch and tag it with the respective version.

+
git checkout <BRANCH NAME>
git pull origin <BRANCH NAME>
git tag -a <NEW VERSION> -m '<NEW VERSION>'
git push origin <NEW VERSION>
+

Publishing

+
    +
  1. GoReleaser will create a new release, review and edit it at https://github.com/project-copacetic/copacetic/releases
  2. +
  3. Review the respective copa-action image at: https://github.com/orgs/project-copacetic/packages/container/package/copa-action
  4. +
\ No newline at end of file diff --git a/website/next/scanner-plugins.html b/website/next/scanner-plugins.html index d6e70194..4d5c4775 100644 --- a/website/next/scanner-plugins.html +++ b/website/next/scanner-plugins.html @@ -1,19 +1,37 @@ - + - -Scanner Plugins | Copacetic + +Scanner Plugins | Copacetic - - - + + + -
-
Version: Next

Motivation

By default, copa uses Trivy to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners.

Starting with v0.5.0 and later, copa offers extensibility to support different vulnerability scanners. Plugin architecture allows users to use the vulnerability scanner of their choice to patch container images without having to modify copa's core codebase.

Usage

Scanner plugin binaries must be in $PATH, and should be prefixed with copa- and have executable permissions. Copa will automatically detect and use the scanner plugin if it is in $PATH.

For example, if you have a scanner plugin binary called copa-foo in $PATH, you can run copa with the following command:

copa patch --scanner foo --image $IMAGE ...

Scanner Plugins from the Community

If you have built a scanner plugin and would like to add it to this list, please submit a PR to update this section with your plugin.

note

If you have any issues with a specific plugin, please open an issue in the applicable plugin's repository.

Writing a Scanner Plugin

Please see instructions at Scanner Plugin Template for a template to get started with writing a scanner plugin.

Scanner Plugin Interface

note

alpha versions of the API are not guarenteed to be backwards compatible. Once the API graduates to beta and stable, it will be backwards compatible.

Scanner plugins must implement the following interface:

v1alpha1

type UpdateManifest struct {
// API version of the interface (e.g. v1alpha1)
APIVersion string `json:"apiVersion"`
// Metadata contains information about the OS and config
Metadata Metadata `json:"metadata"`
// Updates is a list of UpdatePackage that contains information about the package updates
Updates UpdatePackages `json:"updates"`
}

// UpdatePackages is a list of UpdatePackage
type UpdatePackages []UpdatePackage

// Metadata contains information about the OS and config
type Metadata struct {
OS OS `json:"os"`
Config Config `json:"config"`
}

type OS struct {
// OS Type (e.g. debian, alpine, etc.)
Type string `json:"type"`
// OS Version (e.g. 11.3)
Version string `json:"version"`
}

// Config contains information about the config
type Config struct {
// OS Architecture (e.g. amd64, arm64)
Arch string `json:"arch"`
}

// UpdatePackage contains information about the package update
type UpdatePackage struct {
// Package name
Name string `json:"name"`
// Installed version
InstalledVersion string `json:"installedVersion"`
// Fixed version
FixedVersion string `json:"fixedVersion"`
// Vulnerability ID
VulnerabilityID string `json:"vulnerabilityID"`
}

From the above, we can see that the plugin must return a JSON object via standard out with the following fields. For example:

{
"apiVersion": "v1alpha1",
"metadata": {
"os": {
"type": "debian",
"version": "11.3",
},
"config": {
"arch": "amd64"
}
},
"updates": [
{
"name": "libcurl4",
"installedVersion": "7.74.0-1.3+deb11u1",
"fixedVersion": "7.74.0-1.3+deb11u2",
"vulnerabilityID": "CVE-2021-22945"
}
]
}
- - +
Version: Next

Motivation

+

By default, copa uses Trivy to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners.

+

Starting with v0.5.0 and later, copa offers extensibility to support different vulnerability scanners. Plugin architecture allows users to use the vulnerability scanner of their choice to patch container images without having to modify copa's core codebase.

+

Usage

+

Scanner plugin binaries must be in $PATH, and should be prefixed with copa- and have executable permissions. Copa will automatically detect and use the scanner plugin if it is in $PATH.

+

For example, if you have a scanner plugin binary called copa-foo in $PATH, you can run copa with the following command:

+
copa patch --scanner foo --image $IMAGE ...
+

Scanner Plugins from the Community

+

If you have built a scanner plugin and would like to add it to this list, please submit a PR to update this section with your plugin.

+
note

If you have any issues with a specific plugin, please open an issue in the applicable plugin's repository.

+ +

Writing a Scanner Plugin

+

Please see instructions at Scanner Plugin Template for a template to get started with writing a scanner plugin.

+

Scanner Plugin Interface

+
note

alpha versions of the API are not guarenteed to be backwards compatible. Once the API graduates to beta and stable, it will be backwards compatible.

+

Scanner plugins must implement the following interface:

+

v1alpha1

+
type UpdateManifest struct {
// API version of the interface (e.g. v1alpha1)
APIVersion string `json:"apiVersion"`
// Metadata contains information about the OS and config
Metadata Metadata `json:"metadata"`
// Updates is a list of UpdatePackage that contains information about the package updates
Updates UpdatePackages `json:"updates"`
}

// UpdatePackages is a list of UpdatePackage
type UpdatePackages []UpdatePackage

// Metadata contains information about the OS and config
type Metadata struct {
OS OS `json:"os"`
Config Config `json:"config"`
}

type OS struct {
// OS Type (e.g. debian, alpine, etc.)
Type string `json:"type"`
// OS Version (e.g. 11.3)
Version string `json:"version"`
}

// Config contains information about the config
type Config struct {
// OS Architecture (e.g. amd64, arm64)
Arch string `json:"arch"`
}

// UpdatePackage contains information about the package update
type UpdatePackage struct {
// Package name
Name string `json:"name"`
// Installed version
InstalledVersion string `json:"installedVersion"`
// Fixed version
FixedVersion string `json:"fixedVersion"`
// Vulnerability ID
VulnerabilityID string `json:"vulnerabilityID"`
}
+

From the above, we can see that the plugin must return a JSON object via standard out with the following fields. For example:

+
{
"apiVersion": "v1alpha1",
"metadata": {
"os": {
"type": "debian",
"version": "11.3",
},
"config": {
"arch": "amd64"
}
},
"updates": [
{
"name": "libcurl4",
"installedVersion": "7.74.0-1.3+deb11u1",
"fixedVersion": "7.74.0-1.3+deb11u2",
"vulnerabilityID": "CVE-2021-22945"
}
]
}
\ No newline at end of file diff --git a/website/next/troubleshooting.html b/website/next/troubleshooting.html index f8cf90d4..eb95b582 100644 --- a/website/next/troubleshooting.html +++ b/website/next/troubleshooting.html @@ -1,19 +1,30 @@ - + - -Troubleshooting | Copacetic + +Troubleshooting | Copacetic - - - + + + -
-
Version: Next

Troubleshooting

Filtering Vulnerabilities

You might want to filter/ignore some of the vulnerabilities while patching. To do so, you need to first filter those undesired vulnerabilities from your scanner output.

For Trivy, vulnerabilities can be filtered by the following 2 ways:

Rego Policy

An example rego file which demonstrates how to ignore certain Vulnerability IDs or Package Names:

$ cat trivy_ignore.rego

package trivy

import data.lib.trivy

default ignore = false


# Ignore the following Vulnerability IDs
ignore_vulnerability_ids := {
"CVE-2018-14618"
}
# Ignore the following Package Names
ignore_pkgs := {"bash", "vim"}


# For ignoring vulnID
ignore {
input.VulnerabilityID == ignore_vulnerability_ids[_]
}
# For ignoring pkgName
ignore {
input.PkgName == ignore_pkgs[_]
}

After adding the above rego file, run the image scan with the --ignore-policy flag followed by the file name to ignore them while scanning:

trivy image --ignore-policy trivy_ignore.rego ruby:2.4.0

In the above example, the vulnerability "CVE-2018-14618" and the packages "bash" & "vim" are ignored while scanning, and hence patching the image.

Ignore File

Use a .trivyignore file to list all the vulnerabilities you want to ignore.

Example:

$ cat .trivyignore

# Accept the risk
CVE-2018-14618

In the above example, the vulnerability CVE-2018-14618 is ignored while scanning, and hence while patching the image.

For a more detailed explanation on how to ignore certain vulnerabilities with Trivy, please refer to the official documentation here.

- - +
Version: Next

Troubleshooting

Filtering Vulnerabilities

+

You might want to filter/ignore some of the vulnerabilities while patching. To do so, you need to first filter those undesired vulnerabilities from your scanner output.

+

For Trivy, vulnerabilities can be filtered by the following 2 ways:

+

Rego Policy

+

An example rego file which demonstrates how to ignore certain Vulnerability IDs or Package Names:

+
$ cat trivy_ignore.rego

package trivy

import data.lib.trivy

default ignore = false


# Ignore the following Vulnerability IDs
ignore_vulnerability_ids := {
"CVE-2018-14618"
}
# Ignore the following Package Names
ignore_pkgs := {"bash", "vim"}


# For ignoring vulnID
ignore {
input.VulnerabilityID == ignore_vulnerability_ids[_]
}
# For ignoring pkgName
ignore {
input.PkgName == ignore_pkgs[_]
}

+

After adding the above rego file, run the image scan with the --ignore-policy flag followed by the file name to ignore them while scanning:

+
trivy image --ignore-policy trivy_ignore.rego ruby:2.4.0
+

In the above example, the vulnerability "CVE-2018-14618" and the packages "bash" & "vim" are ignored while scanning, and hence patching the image.

+

Ignore File

+

Use a .trivyignore file to list all the vulnerabilities you want to ignore.

+

Example:

+
$ cat .trivyignore

# Accept the risk
CVE-2018-14618
+

In the above example, the vulnerability CVE-2018-14618 is ignored while scanning, and hence while patching the image.

+

For a more detailed explanation on how to ignore certain vulnerabilities with Trivy, please refer to the official documentation here.

\ No newline at end of file diff --git a/website/output.html b/website/output.html index b3ff22bc..61f86bae 100644 --- a/website/output.html +++ b/website/output.html @@ -1,19 +1,32 @@ - + - -Output | Copacetic + +Output | Copacetic - - - + + + -
-
Version: v0.6.x

Output

caution

Experimental: This feature might change without preserving backwards compatibility.

Copa optionally outputs a Vulnerability Exploitability eXchange (VEX) file as a result of the patching process to surface the vulnerabilities and packages that were patched.

Currently, Copa supports the OpenVEX format, but it can be extended to support other formats.

OpenVEX

OpenVEX is an implementation of Vulnerability Exploitability eXchange (VEX) format. For more information, see OpenVEX specification.

tip
  • Use COPA_VEX_AUTHOR environment variable to set the author of the VEX document. If it's not set, the author will default to Project Copacetic.

  • A VEX document must contain at least one VEX statement. If there are no fixed vulnerabilities, Copa will not generate a VEX document.

To generate a VEX document using OpenVEX, use --format="openvex" flag, and use --output to specify a file path. For example:

copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --format="openvex" --output "nginx.1.21.6-vex.json"

This will generate a VEX Document that looks like:

{
"@context": "https://openvex.dev/ns",
"@id": "https://openvex.dev/docs/public/vex-a6c44ec1d79e9dd4190dc01b4ecf7527ebb26bd37c01e32e6efcd203ae00d2a5",
"author": "Project Copacetic",
"timestamp": "2023-10-11T00:15:00.114768055Z",
"version": 1,
"tooling": "Project Copacetic",
"statements": [
{
"vulnerability": {
"@id": "CVE-2021-22945"
},
"products": [
{
"@id": "pkg:oci/docker.io/library/nginx:1.21.6-patched",
"subcomponents": [
{
"@id": "pkg:deb/debian/curl@7.74.0-1.3+deb11u2?arch=amd64"
},
{
"@id": "pkg:deb/debian/libcurl4@7.74.0-1.3+deb11u2?arch=amd64"
}
]
}
],
"status": "fixed"
},
...
}
- - +
Version: v0.6.x

Output

caution

Experimental: This feature might change without preserving backwards compatibility.

+

Copa optionally outputs a Vulnerability Exploitability eXchange (VEX) file as a result of the patching process to surface the vulnerabilities and packages that were patched.

+

Currently, Copa supports the OpenVEX format, but it can be extended to support other formats.

+

OpenVEX

+

OpenVEX is an implementation of Vulnerability Exploitability eXchange (VEX) format. For more information, see OpenVEX specification.

+
tip
    +
  • +

    Use COPA_VEX_AUTHOR environment variable to set the author of the VEX document. If it's not set, the author will default to Project Copacetic.

    +
  • +
  • +

    A VEX document must contain at least one VEX statement. If there are no fixed vulnerabilities, Copa will not generate a VEX document.

    +
  • +
+

To generate a VEX document using OpenVEX, use --format="openvex" flag, and use --output to specify a file path. For example:

+
copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --format="openvex" --output "nginx.1.21.6-vex.json"
+

This will generate a VEX Document that looks like:

+
{
"@context": "https://openvex.dev/ns",
"@id": "https://openvex.dev/docs/public/vex-a6c44ec1d79e9dd4190dc01b4ecf7527ebb26bd37c01e32e6efcd203ae00d2a5",
"author": "Project Copacetic",
"timestamp": "2023-10-11T00:15:00.114768055Z",
"version": 1,
"tooling": "Project Copacetic",
"statements": [
{
"vulnerability": {
"@id": "CVE-2021-22945"
},
"products": [
{
"@id": "pkg:oci/docker.io/library/nginx:1.21.6-patched",
"subcomponents": [
{
"@id": "pkg:deb/debian/curl@7.74.0-1.3+deb11u2?arch=amd64"
},
{
"@id": "pkg:deb/debian/libcurl4@7.74.0-1.3+deb11u2?arch=amd64"
}
]
}
],
"status": "fixed"
},
...
}
\ No newline at end of file diff --git a/website/quick-start.html b/website/quick-start.html index c251bfbb..e75975b8 100644 --- a/website/quick-start.html +++ b/website/quick-start.html @@ -1,19 +1,71 @@ - + - -Quick Start | Copacetic + +Quick Start | Copacetic - - - + + + -
-
Version: v0.6.x

Quick Start

This sample illustrates how to patch containers using vulnerability reports with copa.

Prerequisites

  • Linux or macOS configured through the setup instructions. This includes:
    • copa tool built & pathed.
    • buildkit daemon installed & pathed. Examples
      • The docker daemon runs a buildkit service in-process. If you are using this for your buildkit instance, Docker must have the containerd image store feature enabled.
      • If you are using a buildx instance, or using buildkitd directly, there is no need to enable the containerd image store. However, only images in a remote registry can be patched using these methods.
    • docker daemon running and CLI installed & pathed.
    • trivy CLI installed & pathed.

Sample Steps

  1. Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:

    trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6

    You can also see the existing patchable vulnerabilities in table form on the shell with:

    trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6
  2. To patch the image, use the Trivy report and specify a buildkit instance to connect to:

    By default copa will attempt to auto-connect to an instance in order:

    1. Default docker buildkit endpoint (requires at least docker v24.0 with containerd image store support enabled)
    2. Currently selected buildx builder (see: docker buildx --help)
    3. buildkit daemon at the default address /run/buildkit/buildkitd.sock

    If an instance doesn't exist or that instance doesn't support all the features copa needs the next will be attempted. Please see custom buildkit addresses for more information.

    After setting up the buildkit instance, run the following command to patch the image:

    copa patch -r nginx.1.21.6.json -i docker.io/library/nginx:1.21.6

    In any of these cases, copa is non-destructive and exports a new image with the specified 1.21.6-patched label to the local Docker daemon.

    note

    If you're running this sample against an image from a private registry instead,ensure that the credentials are configured in the default Docker config.json before running copa patch, for example, via docker login -u <user> -p <password> <registry>.

    note

    If you're scanning and patching an image that is local-only (i.e. built or tagged locally but not pushed to a registry), copa is limited to using docker's built-in buildkit service, and must use the containerd image store feature. This is because only docker's built-in buildkit service has access to the docker image store (see Prerequisites for more information.)

  3. Scan the patched image and verify that the vulnerabilities have been patched:

    trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched

    You can also inspect the structure of the patched image with docker history to see the new patch layer appended to the image:

    $ docker history docker.io/library/nginx:1.21.6-patched
    IMAGE CREATED CREATED BY SIZE COMMENT
    262dacfeb193 About a minute ago mount / from exec sh -c apt install --no-ins… 41.1MB buildkit.exporter.image.v0
    <missing> 20 months ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
    <missing> 20 months ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B
    <missing> 20 months ago /bin/sh -c #(nop) EXPOSE 80 0B
    <missing> 20 months ago /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr… 0B
    <missing> 20 months ago /bin/sh -c #(nop) COPY file:09a214a3e07c919a… 16.4kB
    <missing> 20 months ago /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7… 12.3kB
    <missing> 20 months ago /bin/sh -c #(nop) COPY file:0b866ff3fc1ef5b0… 12.3kB
    <missing> 20 months ago /bin/sh -c #(nop) COPY file:65504f71f5855ca0… 8.19kB
    <missing> 20 months ago /bin/sh -c set -x && addgroup --system -… 64.5MB
    <missing> 20 months ago /bin/sh -c #(nop) ENV PKG_RELEASE=1~bullseye 0B
    <missing> 20 months ago /bin/sh -c #(nop) ENV NJS_VERSION=0.7.3 0B
    <missing> 20 months ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.21.6 0B
    <missing> 20 months ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do… 0B
    <missing> 20 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
    <missing> 20 months ago /bin/sh -c #(nop) ADD file:134f25aec8adf83cb… 91.8MB
  4. Run the container to verify that the image has no regressions:

    $ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched
    /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
    /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
    10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
    10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
    /docker-entrypoint.sh: Configuration complete; ready for start up
    2024/01/22 23:32:54 [notice] 1#1: using the "epoll" event method
    2024/01/22 23:32:54 [notice] 1#1: nginx/1.21.6
    2024/01/22 23:32:54 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
    2024/01/22 23:32:54 [notice] 1#1: OS: Linux 6.2.0-1018-azure
    2024/01/22 23:32:54 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
    2024/01/22 23:32:54 [notice] 1#1: start worker processes

    You can stop the container by opening a new shell instance and running: docker stop nginx-test

- - +
Version: v0.6.x

Quick Start

This sample illustrates how to patch containers using vulnerability reports with copa.

+

Prerequisites

+
    +
  • Linux or macOS configured through the setup instructions. This includes: +
      +
    • copa tool built & pathed.
    • +
    • buildkit daemon installed & pathed. Examples +
        +
      • The docker daemon runs a buildkit service in-process. If you are using this for your buildkit instance, Docker must have the containerd image store feature enabled.
      • +
      • If you are using a buildx instance, or using buildkitd directly, there is no need to enable the containerd image store. However, only images in a remote registry can be patched using these methods.
      • +
      +
    • +
    • docker daemon running and CLI installed & pathed.
    • +
    • trivy CLI installed & pathed. + +
    • +
    +
  • +
+

Sample Steps

+
    +
  1. +

    Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:

    +
    trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6
    +

    You can also see the existing patchable vulnerabilities in table form on the shell with:

    +
    trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6
    +
  2. +
  3. +

    To patch the image, use the Trivy report and specify a buildkit instance to connect to:

    +

    By default copa will attempt to auto-connect to an instance in order:

    +
      +
    1. Default docker buildkit endpoint (requires at least docker v24.0 with containerd image store support enabled)
    2. +
    3. Currently selected buildx builder (see: docker buildx --help)
    4. +
    5. buildkit daemon at the default address /run/buildkit/buildkitd.sock
    6. +
    +

    If an instance doesn't exist or that instance doesn't support all the features copa needs the next will be attempted. Please see custom buildkit addresses for more information.

    +

    After setting up the buildkit instance, run the following command to patch the image:

    +
    copa patch -r nginx.1.21.6.json -i docker.io/library/nginx:1.21.6
    +

    In any of these cases, copa is non-destructive and exports a new image with the specified 1.21.6-patched label to the local Docker daemon.

    +
    note

    If you're running this sample against an image from a private registry instead,ensure that the credentials are configured in the default Docker config.json before running copa patch, for example, via docker login -u <user> -p <password> <registry>.

    +
    note

    If you're scanning and patching an image that is local-only (i.e. built or tagged locally but not pushed to a registry), copa is limited to using docker's built-in buildkit service, and must use the containerd image store feature. This is because only docker's built-in buildkit service has access to the docker image store (see Prerequisites for more information.)

    +
  4. +
  5. +

    Scan the patched image and verify that the vulnerabilities have been patched:

    +
    trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched
    +

    You can also inspect the structure of the patched image with docker history to see the new patch layer appended to the image:

    +
    $ docker history docker.io/library/nginx:1.21.6-patched
    IMAGE CREATED CREATED BY SIZE COMMENT
    262dacfeb193 About a minute ago mount / from exec sh -c apt install --no-ins… 41.1MB buildkit.exporter.image.v0
    <missing> 20 months ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
    <missing> 20 months ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B
    <missing> 20 months ago /bin/sh -c #(nop) EXPOSE 80 0B
    <missing> 20 months ago /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr… 0B
    <missing> 20 months ago /bin/sh -c #(nop) COPY file:09a214a3e07c919a… 16.4kB
    <missing> 20 months ago /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7… 12.3kB
    <missing> 20 months ago /bin/sh -c #(nop) COPY file:0b866ff3fc1ef5b0… 12.3kB
    <missing> 20 months ago /bin/sh -c #(nop) COPY file:65504f71f5855ca0… 8.19kB
    <missing> 20 months ago /bin/sh -c set -x && addgroup --system -… 64.5MB
    <missing> 20 months ago /bin/sh -c #(nop) ENV PKG_RELEASE=1~bullseye 0B
    <missing> 20 months ago /bin/sh -c #(nop) ENV NJS_VERSION=0.7.3 0B
    <missing> 20 months ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.21.6 0B
    <missing> 20 months ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do… 0B
    <missing> 20 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
    <missing> 20 months ago /bin/sh -c #(nop) ADD file:134f25aec8adf83cb… 91.8MB
    +
  6. +
  7. +

    Run the container to verify that the image has no regressions:

    +
    $ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched
    /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
    /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
    10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
    10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
    /docker-entrypoint.sh: Configuration complete; ready for start up
    2024/01/22 23:32:54 [notice] 1#1: using the "epoll" event method
    2024/01/22 23:32:54 [notice] 1#1: nginx/1.21.6
    2024/01/22 23:32:54 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
    2024/01/22 23:32:54 [notice] 1#1: OS: Linux 6.2.0-1018-azure
    2024/01/22 23:32:54 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
    2024/01/22 23:32:54 [notice] 1#1: start worker processes
    +

    You can stop the container by opening a new shell instance and running: docker stop nginx-test

    +
  8. +
\ No newline at end of file diff --git a/website/release.html b/website/release.html index deae4428..62df703d 100644 --- a/website/release.html +++ b/website/release.html @@ -1,19 +1,24 @@ - + - -Release Process | Copacetic + +Release Process | Copacetic - - - + + + -
-
Version: v0.6.x

Release Process

Overview

The release process for Copacetic uses GoReleaser.

Once you are ready to cut a new release, checkout the release branch and tag it with the respective version.

```
git checkout <BRANCH NAME>
git pull origin <BRANCH NAME>
git tag -a <NEW VERSION> -m '<NEW VERSION>'
git push origin <NEW VERSION>
```

Publishing

  1. GoReleaser will create a new release, review and edit it at https://github.com/project-copacetic/copacetic/releases
  2. Review the respective copa-action image at: https://github.com/orgs/project-copacetic/packages/container/package/copa-action
- - +
Version: v0.6.x

Release Process

Overview

+

The release process for Copacetic uses GoReleaser.

+

Once you are ready to cut a new release, checkout the release branch and tag it with the respective version.

+
git checkout <BRANCH NAME>
git pull origin <BRANCH NAME>
git tag -a <NEW VERSION> -m '<NEW VERSION>'
git push origin <NEW VERSION>
+

Publishing

+
    +
  1. GoReleaser will create a new release, review and edit it at https://github.com/project-copacetic/copacetic/releases
  2. +
  3. Review the respective copa-action image at: https://github.com/orgs/project-copacetic/packages/container/package/copa-action
  4. +
\ No newline at end of file diff --git a/website/scanner-plugins.html b/website/scanner-plugins.html index 5a0d15b7..8cfc358b 100644 --- a/website/scanner-plugins.html +++ b/website/scanner-plugins.html @@ -1,19 +1,37 @@ - + - -Scanner Plugins | Copacetic + +Scanner Plugins | Copacetic - - - + + + -
-
Version: v0.6.x

Motivation

By default, copa uses Trivy to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners.

Starting with v0.5.0 and later, copa offers extensibility to support different vulnerability scanners. Plugin architecture allows users to use the vulnerability scanner of their choice to patch container images without having to modify copa's core codebase.

Usage

Scanner plugin binaries must be in $PATH, and should be prefixed with copa- and have executable permissions. Copa will automatically detect and use the scanner plugin if it is in $PATH.

For example, if you have a scanner plugin binary called copa-foo in $PATH, you can run copa with the following command:

copa patch --scanner foo --image $IMAGE ...

Scanner Plugins from the Community

If you have built a scanner plugin and would like to add it to this list, please submit a PR to update this section with your plugin.

note

If you have any issues with a specific plugin, please open an issue in the applicable plugin's repository.

Writing a Scanner Plugin

Please see instructions at Scanner Plugin Template for a template to get started with writing a scanner plugin.

Scanner Plugin Interface

note

alpha versions of the API are not guarenteed to be backwards compatible. Once the API graduates to beta and stable, it will be backwards compatible.

Scanner plugins must implement the following interface:

v1alpha1

type UpdateManifest struct {
// API version of the interface (e.g. v1alpha1)
APIVersion string `json:"apiVersion"`
// Metadata contains information about the OS and config
Metadata Metadata `json:"metadata"`
// Updates is a list of UpdatePackage that contains information about the package updates
Updates UpdatePackages `json:"updates"`
}

// UpdatePackages is a list of UpdatePackage
type UpdatePackages []UpdatePackage

// Metadata contains information about the OS and config
type Metadata struct {
OS OS `json:"os"`
Config Config `json:"config"`
}

type OS struct {
// OS Type (e.g. debian, alpine, etc.)
Type string `json:"type"`
// OS Version (e.g. 11.3)
Version string `json:"version"`
}

// Config contains information about the config
type Config struct {
// OS Architecture (e.g. amd64, arm64)
Arch string `json:"arch"`
}

// UpdatePackage contains information about the package update
type UpdatePackage struct {
// Package name
Name string `json:"name"`
// Installed version
InstalledVersion string `json:"installedVersion"`
// Fixed version
FixedVersion string `json:"fixedVersion"`
// Vulnerability ID
VulnerabilityID string `json:"vulnerabilityID"`
}

From the above, we can see that the plugin must return a JSON object via standard out with the following fields. For example:

{
"apiVersion": "v1alpha1",
"metadata": {
"os": {
"type": "debian",
"version": "11.3",
},
"config": {
"arch": "amd64"
}
},
"updates": [
{
"name": "libcurl4",
"installedVersion": "7.74.0-1.3+deb11u1",
"fixedVersion": "7.74.0-1.3+deb11u2",
"vulnerabilityID": "CVE-2021-22945"
}
]
}
- - +
Version: v0.6.x

Motivation

+

By default, copa uses Trivy to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners.

+

Starting with v0.5.0 and later, copa offers extensibility to support different vulnerability scanners. Plugin architecture allows users to use the vulnerability scanner of their choice to patch container images without having to modify copa's core codebase.

+

Usage

+

Scanner plugin binaries must be in $PATH, and should be prefixed with copa- and have executable permissions. Copa will automatically detect and use the scanner plugin if it is in $PATH.

+

For example, if you have a scanner plugin binary called copa-foo in $PATH, you can run copa with the following command:

+
copa patch --scanner foo --image $IMAGE ...
+

Scanner Plugins from the Community

+

If you have built a scanner plugin and would like to add it to this list, please submit a PR to update this section with your plugin.

+
note

If you have any issues with a specific plugin, please open an issue in the applicable plugin's repository.

+ +

Writing a Scanner Plugin

+

Please see instructions at Scanner Plugin Template for a template to get started with writing a scanner plugin.

+

Scanner Plugin Interface

+
note

alpha versions of the API are not guarenteed to be backwards compatible. Once the API graduates to beta and stable, it will be backwards compatible.

+

Scanner plugins must implement the following interface:

+

v1alpha1

+
type UpdateManifest struct {
// API version of the interface (e.g. v1alpha1)
APIVersion string `json:"apiVersion"`
// Metadata contains information about the OS and config
Metadata Metadata `json:"metadata"`
// Updates is a list of UpdatePackage that contains information about the package updates
Updates UpdatePackages `json:"updates"`
}

// UpdatePackages is a list of UpdatePackage
type UpdatePackages []UpdatePackage

// Metadata contains information about the OS and config
type Metadata struct {
OS OS `json:"os"`
Config Config `json:"config"`
}

type OS struct {
// OS Type (e.g. debian, alpine, etc.)
Type string `json:"type"`
// OS Version (e.g. 11.3)
Version string `json:"version"`
}

// Config contains information about the config
type Config struct {
// OS Architecture (e.g. amd64, arm64)
Arch string `json:"arch"`
}

// UpdatePackage contains information about the package update
type UpdatePackage struct {
// Package name
Name string `json:"name"`
// Installed version
InstalledVersion string `json:"installedVersion"`
// Fixed version
FixedVersion string `json:"fixedVersion"`
// Vulnerability ID
VulnerabilityID string `json:"vulnerabilityID"`
}
+

From the above, we can see that the plugin must return a JSON object via standard out with the following fields. For example:

+
{
"apiVersion": "v1alpha1",
"metadata": {
"os": {
"type": "debian",
"version": "11.3",
},
"config": {
"arch": "amd64"
}
},
"updates": [
{
"name": "libcurl4",
"installedVersion": "7.74.0-1.3+deb11u1",
"fixedVersion": "7.74.0-1.3+deb11u2",
"vulnerabilityID": "CVE-2021-22945"
}
]
}
\ No newline at end of file diff --git a/website/troubleshooting.html b/website/troubleshooting.html index 4e8dfb4e..43031b85 100644 --- a/website/troubleshooting.html +++ b/website/troubleshooting.html @@ -1,19 +1,30 @@ - + - -Troubleshooting | Copacetic + +Troubleshooting | Copacetic - - - + + + -
-
Version: v0.6.x

Troubleshooting

Filtering Vulnerabilities

You might want to filter/ignore some of the vulnerabilities while patching. To do so, you need to first filter those undesired vulnerabilities from your scanner output.

For Trivy, vulnerabilities can be filtered by the following 2 ways:

Rego Policy

An example rego file which demonstrates how to ignore certain Vulnerability IDs or Package Names:

$ cat trivy_ignore.rego

package trivy

import data.lib.trivy

default ignore = false


# Ignore the following Vulnerability IDs
ignore_vulnerability_ids := {
"CVE-2018-14618"
}
# Ignore the following Package Names
ignore_pkgs := {"bash", "vim"}


# For ignoring vulnID
ignore {
input.VulnerabilityID == ignore_vulnerability_ids[_]
}
# For ignoring pkgName
ignore {
input.PkgName == ignore_pkgs[_]
}

After adding the above rego file, run the image scan with the --ignore-policy flag followed by the file name to ignore them while scanning:

trivy image --ignore-policy trivy_ignore.rego ruby:2.4.0

In the above example, the vulnerability "CVE-2018-14618" and the packages "bash" & "vim" are ignored while scanning, and hence patching the image.

Ignore File

Use a .trivyignore file to list all the vulnerabilities you want to ignore.

Example:

$ cat .trivyignore

# Accept the risk
CVE-2018-14618

In the above example, the vulnerability CVE-2018-14618 is ignored while scanning, and hence while patching the image.

For a more detailed explanation on how to ignore certain vulnerabilities with Trivy, please refer to the official documentation here.

- - +
Version: v0.6.x

Troubleshooting

Filtering Vulnerabilities

+

You might want to filter/ignore some of the vulnerabilities while patching. To do so, you need to first filter those undesired vulnerabilities from your scanner output.

+

For Trivy, vulnerabilities can be filtered by the following 2 ways:

+

Rego Policy

+

An example rego file which demonstrates how to ignore certain Vulnerability IDs or Package Names:

+
$ cat trivy_ignore.rego

package trivy

import data.lib.trivy

default ignore = false


# Ignore the following Vulnerability IDs
ignore_vulnerability_ids := {
"CVE-2018-14618"
}
# Ignore the following Package Names
ignore_pkgs := {"bash", "vim"}


# For ignoring vulnID
ignore {
input.VulnerabilityID == ignore_vulnerability_ids[_]
}
# For ignoring pkgName
ignore {
input.PkgName == ignore_pkgs[_]
}

+

After adding the above rego file, run the image scan with the --ignore-policy flag followed by the file name to ignore them while scanning:

+
trivy image --ignore-policy trivy_ignore.rego ruby:2.4.0
+

In the above example, the vulnerability "CVE-2018-14618" and the packages "bash" & "vim" are ignored while scanning, and hence patching the image.

+

Ignore File

+

Use a .trivyignore file to list all the vulnerabilities you want to ignore.

+

Example:

+
$ cat .trivyignore

# Accept the risk
CVE-2018-14618
+

In the above example, the vulnerability CVE-2018-14618 is ignored while scanning, and hence while patching the image.

+

For a more detailed explanation on how to ignore certain vulnerabilities with Trivy, please refer to the official documentation here.

\ No newline at end of file diff --git a/website/v0.1.x.html b/website/v0.1.x.html index 544d3ad6..c331b8b6 100644 --- a/website/v0.1.x.html +++ b/website/v0.1.x.html @@ -1,19 +1,59 @@ - + - -Introduction | Copacetic + +Introduction | Copacetic - - - + + + -
-
Version: v0.1.x

Project Copacetic: Directly patch container image vulnerabilities

copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.

Why?

We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between vulnerability disclosure and active exploitation continues to narrow, there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:

  • inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option
  • found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.

In addition to filling the operational gap not met by left-shift security practices and tools, the ability of copa to patch a container without requiring a rebuild of the container image provides other benefits:

  • Allows users other than the image publishers to also patch container images, such as DevSecOps engineers.
  • Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching.
  • Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild.
  • Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.

How?

The copa tool is an extensible engine that:

  1. Parses the needed update packages from the container image’s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats.
  2. Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers.
  3. Applies the resulting update binaries to the container image using buildkit.

This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:

  • Copa supports patching existing container images.
    • Devs don't need to build their images using specific tools or modify them in some way just to support container patching.
  • Copa works with the existing vulnerability scanning and mitigation ecosystems.
    • Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today.
    • Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports.
  • Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image.
    • For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.

For more details, refer to the copa design documentation.

- - +
Version: v0.1.x

Project Copacetic: Directly patch container image vulnerabilities

+

copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.

+

Why?

+

We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between vulnerability disclosure and active exploitation continues to narrow, there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:

+
    +
  • inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option
  • +
  • found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.
  • +
+ +

In addition to filling the operational gap not met by left-shift security practices and tools, the ability of copa to patch a container without requiring a rebuild of the container image provides other benefits:

+
    +
  • Allows users other than the image publishers to also patch container images, such as DevSecOps engineers.
  • +
  • Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching.
  • +
  • Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild.
  • +
  • Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.
  • +
+

How?

+

The copa tool is an extensible engine that:

+
    +
  1. Parses the needed update packages from the container image’s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats.
  2. +
  3. Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers.
  4. +
  5. Applies the resulting update binaries to the container image using buildkit.
  6. +
+ +

This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:

+
    +
  • Copa supports patching existing container images. +
      +
    • Devs don't need to build their images using specific tools or modify them in some way just to support container patching.
    • +
    +
  • +
  • Copa works with the existing vulnerability scanning and mitigation ecosystems. +
      +
    • Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today.
    • +
    • Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports.
    • +
    +
  • +
  • Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image. +
      +
    • For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.
    • +
    +
  • +
+

For more details, refer to the copa design documentation.

\ No newline at end of file diff --git a/website/v0.1.x/code-of-conduct.html b/website/v0.1.x/code-of-conduct.html index 2059ea1b..48a8118c 100644 --- a/website/v0.1.x/code-of-conduct.html +++ b/website/v0.1.x/code-of-conduct.html @@ -1,68 +1,111 @@ - + - -Code of Conduct | Copacetic + +Code of Conduct | Copacetic - - - + + + -
-
Version: v0.1.x

Contributor Covenant Code of Conduct

Our Pledge

We as members, contributors, and leaders pledge to make participation in our +

Version: v0.1.x

Contributor Covenant Code of Conduct

+

Our Pledge

+

We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual -identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community.

Our Standards

Examples of behavior that contributes to a positive environment for our -community include:

  • Demonstrating empathy and kindness toward other people
  • Being respectful of differing opinions, viewpoints, and experiences
  • Giving and gracefully accepting constructive feedback
  • Accepting responsibility and apologizing to those affected by our mistakes, -and learning from the experience
  • Focusing on what is best not just for us as individuals, but for the overall -community

Examples of unacceptable behavior include:

  • The use of sexualized language or imagery, and sexual attention or advances of -any kind
  • Trolling, insulting or derogatory comments, and personal or political attacks
  • Public or private harassment
  • Publishing others' private information, such as a physical or email address, -without their explicit permission
  • Other conduct which could reasonably be considered inappropriate in a -professional setting

Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of +identity and orientation.

+

We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community.

+

Our Standards

+

Examples of behavior that contributes to a positive environment for our +community include:

+
    +
  • Demonstrating empathy and kindness toward other people
  • +
  • Being respectful of differing opinions, viewpoints, and experiences
  • +
  • Giving and gracefully accepting constructive feedback
  • +
  • Accepting responsibility and apologizing to those affected by our mistakes, +and learning from the experience
  • +
  • Focusing on what is best not just for us as individuals, but for the overall +community
  • +
+

Examples of unacceptable behavior include:

+
    +
  • The use of sexualized language or imagery, and sexual attention or advances of +any kind
  • +
  • Trolling, insulting or derogatory comments, and personal or political attacks
  • +
  • Public or private harassment
  • +
  • Publishing others' private information, such as a physical or email address, +without their explicit permission
  • +
  • Other conduct which could reasonably be considered inappropriate in a +professional setting
  • +
+

Enforcement Responsibilities

+

Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, -or harmful.

Community leaders have the right and responsibility to remove, edit, or reject +or harmful.

+

Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate.

Scope

This Code of Conduct applies within all community spaces, and also applies when +decisions when appropriate.

+

Scope

+

This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed -representative at an online or offline event.

Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be +representative at an online or offline event.

+

Enforcement

+

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at project-copacetic@googlegroups.com. -All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the -reporter of any incident.

Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct:

1. Correction

Community Impact: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community.

Consequence: A private, written warning from community leaders, providing +All complaints will be reviewed and investigated promptly and fairly.

+

All community leaders are obligated to respect the privacy and security of the +reporter of any incident.

+

Enforcement Guidelines

+

Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct:

+

1. Correction

+

Community Impact: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community.

+

Consequence: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested.

2. Warning

Community Impact: A violation through a single incident or series of -actions.

Consequence: A warning with consequences for continued behavior. No +behavior was inappropriate. A public apology may be requested.

+

2. Warning

+

Community Impact: A violation through a single incident or series of +actions.

+

Consequence: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent -ban.

3. Temporary Ban

Community Impact: A serious violation of community standards, including -sustained inappropriate behavior.

Consequence: A temporary ban from any sort of interaction or public +ban.

+

3. Temporary Ban

+

Community Impact: A serious violation of community standards, including +sustained inappropriate behavior.

+

Consequence: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban.

4. Permanent Ban

Community Impact: Demonstrating a pattern of violation of community +Violating these terms may lead to a permanent ban.

+

4. Permanent Ban

+

Community Impact: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals.

Consequence: A permanent ban from any sort of public interaction within the -community.

Attribution

This Code of Conduct is adapted from the Contributor Covenant, +individual, or aggression toward or disparagement of classes of individuals.

+

Consequence: A permanent ban from any sort of public interaction within the +community.

+

Attribution

+

This Code of Conduct is adapted from the Contributor Covenant, version 2.1, available at -https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

Community Impact Guidelines were inspired by -Mozilla's code of conduct enforcement ladder.

For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

+

Community Impact Guidelines were inspired by +Mozilla's code of conduct enforcement ladder.

+

For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations.

- - +https://www.contributor-covenant.org/translations.

\ No newline at end of file diff --git a/website/v0.1.x/contributing.html b/website/v0.1.x/contributing.html index afb30d52..8ab81f28 100644 --- a/website/v0.1.x/contributing.html +++ b/website/v0.1.x/contributing.html @@ -1,33 +1,133 @@ - + - -Contributing | Copacetic + +Contributing | Copacetic - - - + + + -
-
Version: v0.1.x

Contributing

Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.

Contributing Issues

Before opening any new issues, please search our existing GitHub issues to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead.

When opening an issue, please select the most appropriate template for what you're contributing:

  • Bug Report: If you would like to report the project or tool behaving in unexpected ways.
  • Documentation Improvement: If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content.
  • Request: If you have a feature request, suggestion, or a even a design proposal to review.
  • Question: If you would like to ask the maintainers a question about the project.

Contributing Code

Getting Started

Follow the instructions to either:

For an overview of the project components, refer to the copa design document.

Visual Studio Code Development Container

VSCode supports development in a containerized environment through its Remote - Container extension. This folder provides a development container which encapsulates the dependencies specified in the instructions to build and run copa.

Prerequisites

  1. Docker

    For Windows users, enabling WSL2 back-end integration with Docker is recommended.

  2. Visual Studio Code
  3. Visual Studio Code Remote - Containers extension

Using the dev container

  1. After you have cloned this repo locally, open the repo folder in VSCode. VSCode will detect the presence of this .devcontainer subfolder and will prompt you to reopen the project in a container.

    Alternatively, you can open the command palette and use the Remote-Containers: Reopen in Container command.

  2. Once the container is loaded, open an integrated terminal in VSCode and you can start running the demo instructions.

⚠ If running via Docker Desktop for Windows

Note that the mounted workspace files appear owned by root in the dev container, which will cause git commands to fail with a fatal: detected dubious ownership in a repository error due to safe.directory checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the vscode user:

sudo chown -R vscode:vscode /workspace/copacetic

Personalizing user settings in a dev container

VSCode supports applying your user settings, such as your .gitconfig, to a dev container through the use of dotfiles repositories. This can be done through your own VSCode settings.json file without changing the dev container image or configuration.

Tests

Once you can successfully make the project, any code contributions should also successfully:

  • Pass unit tests via make test.
  • Lint cleanly via make lint.

Pull requests will also be expected to pass the PR functional tests specified by .github/workflows/build.yml.

Pull Requests

If you'd like to start contributing code to the project, you can search for issues with the good first issue label. Other kinds of PR contributions we would look for include:

  • Fixes for bugs and other correctness issues.
  • Docs and other content improvements (e.g. samples).
  • Extensions to support parsing new scanning report formats.
  • Extensions to support patching images based on new distros or using new package managers.

For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:

  • Architectural changes (e.g. breaking interfaces or violations of this project's design tenets).
  • Unsolicited features that significantly expand the functional scope of the tool.

Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the Angular commit message format for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs.

We suggest:

  • Use the standard header format of "<type>: <short summary>" where the <type> is one of the following:
    • build: Changes that affect the build system or external dependencies
    • ci: Changes to the GitHub workflows and configurations
    • docs: Documentation only changes
    • feat: A new feature
    • fix: A bug fix
    • perf: A code change that improves performance
    • refactor: A code change that neither fixes a bug nor adds a feature
    • test: Adding missing tests or correcting existing tests
  • Use a concise, imperative description of the changes included in the <short summary> of the header, the body of the PR, and generally in your commit messages.
  • Use GitHub keywords in the footer of your PR description, such as closes to automatically close issues the PR intends to address.

Developer Certificate of Origin (DCO)

The Developer Certificate of Origin (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the full text of the DCO, reformatted for readability:

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I +

Version: v0.1.x

Contributing

Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.

+

Contributing Issues

+

Before opening any new issues, please search our existing GitHub issues to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead.

+

When opening an issue, please select the most appropriate template for what you're contributing:

+
    +
  • Bug Report: If you would like to report the project or tool behaving in unexpected ways.
  • +
  • Documentation Improvement: If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content.
  • +
  • Request: If you have a feature request, suggestion, or a even a design proposal to review.
  • +
  • Question: If you would like to ask the maintainers a question about the project.
  • +
+

Contributing Code

+

Getting Started

+

Follow the instructions to either:

+ +

For an overview of the project components, refer to the copa design document.

+

Visual Studio Code Development Container

+

VSCode supports development in a containerized environment through its Remote - Container extension. This folder provides a development container which encapsulates the dependencies specified in the instructions to build and run copa.

+

Prerequisites

+
    +
  1. Docker +
    +

    For Windows users, enabling WSL2 back-end integration with Docker is recommended.

    +
    +
  2. +
  3. Visual Studio Code
  4. +
  5. Visual Studio Code Remote - Containers extension
  6. +
+

Using the dev container

+
    +
  1. +

    After you have cloned this repo locally, open the repo folder in VSCode. VSCode will detect the presence of this .devcontainer subfolder and will prompt you to reopen the project in a container.

    +

    Alternatively, you can open the command palette and use the Remote-Containers: Reopen in Container command.

    +
  2. +
  3. +

    Once the container is loaded, open an integrated terminal in VSCode and you can start running the demo instructions.

    +
  4. +
+
+

⚠ If running via Docker Desktop for Windows

+

Note that the mounted workspace files appear owned by root in the dev container, which will cause git commands to fail with a fatal: detected dubious ownership in a repository error due to safe.directory checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the vscode user:

+
sudo chown -R vscode:vscode /workspace/copacetic
+
+

Personalizing user settings in a dev container

+

VSCode supports applying your user settings, such as your .gitconfig, to a dev container through the use of dotfiles repositories. This can be done through your own VSCode settings.json file without changing the dev container image or configuration.

+

Tests

+

Once you can successfully make the project, any code contributions should also successfully:

+
    +
  • Pass unit tests via make test.
  • +
  • Lint cleanly via make lint.
  • +
+

Pull requests will also be expected to pass the PR functional tests specified by .github/workflows/build.yml.

+

Pull Requests

+

If you'd like to start contributing code to the project, you can search for issues with the good first issue label. Other kinds of PR contributions we would look for include:

+
    +
  • Fixes for bugs and other correctness issues.
  • +
  • Docs and other content improvements (e.g. samples).
  • +
  • Extensions to support parsing new scanning report formats.
  • +
  • Extensions to support patching images based on new distros or using new package managers.
  • +
+

For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:

+
    +
  • Architectural changes (e.g. breaking interfaces or violations of this project's design tenets).
  • +
  • Unsolicited features that significantly expand the functional scope of the tool.
  • +
+

Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the Angular commit message format for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs.

+

We suggest:

+
    +
  • Use the standard header format of "<type>: <short summary>" where the <type> is one of the following: +
      +
    • build: Changes that affect the build system or external dependencies
    • +
    • ci: Changes to the GitHub workflows and configurations
    • +
    • docs: Documentation only changes
    • +
    • feat: A new feature
    • +
    • fix: A bug fix
    • +
    • perf: A code change that improves performance
    • +
    • refactor: A code change that neither fixes a bug nor adds a feature
    • +
    • test: Adding missing tests or correcting existing tests
    • +
    +
  • +
  • Use a concise, imperative description of the changes included in the <short summary> of the header, the body of the PR, and generally in your commit messages.
  • +
  • Use GitHub keywords in the footer of your PR description, such as closes to automatically close issues the PR intends to address.
  • +
+

Developer Certificate of Origin (DCO)

+

The Developer Certificate of Origin (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the full text of the DCO, reformatted for readability:

+
+

By making a contribution to this project, I certify that:

+

(a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license -indicated in the file; or

(b) The contribution is based upon previous work that, to the best +indicated in the file; or

+

(b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated -in the file; or

(c) The contribution was provided directly to me by some other +in the file; or

+

(c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified -it.

(d) I understand and agree that this project and the contribution +it.

+

(d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with -this project or the open source license(s) involved.

Contributors sign-off that they adhere to these requirements by adding a Signed-off-by line to commit messages.

This is my commit message

Signed-off-by: Random J Developer <random@developer.example.org>

Git even has a -s command line option to append this automatically to your commit message:

git commit -s -m 'This is my commit message'

Pull requests that do not contain a valid Signed-off-by line cannot be merged.

I didn't sign my commit, now what?

No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:

git switch <branch-name>
git commit --amend --no-edit --signoff
git push --force-with-lease <remote-name> <branch-name>

Code of Conduct

This project has adopted the Contributor Covenant Code of Conduct.

- - +this project or the open source license(s) involved.

+
+

Contributors sign-off that they adhere to these requirements by adding a Signed-off-by line to commit messages.

+
This is my commit message

Signed-off-by: Random J Developer <random@developer.example.org>
+

Git even has a -s command line option to append this automatically to your commit message:

+
git commit -s -m 'This is my commit message'
+

Pull requests that do not contain a valid Signed-off-by line cannot be merged.

+

I didn't sign my commit, now what?

+

No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:

+
git switch <branch-name>
git commit --amend --no-edit --signoff
git push --force-with-lease <remote-name> <branch-name>
+

Code of Conduct

+

This project has adopted the Contributor Covenant Code of Conduct.

\ No newline at end of file diff --git a/website/v0.1.x/design.html b/website/v0.1.x/design.html index 23948b9b..ac960cc4 100644 --- a/website/v0.1.x/design.html +++ b/website/v0.1.x/design.html @@ -1,19 +1,84 @@ - + - -Design | Copacetic + +Design | Copacetic - - - + + + -
-
Version: v0.1.x

Design

Design Tenets

  • Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update. This is a raison d’etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.

  • Copa is intended to work with the existing ecosystem of container images. The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.

  • Copa is intended to allow parties other than the image authors to address container vulnerabilities. Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.

  • Copa is intended to do one thing well and be composable with other tools and processes. Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane.

Design Reasoning

The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental crane rebase:

  • Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an apt install during image creation will overwrite the dpkg status file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners.

    To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.

  • Rebasing also requires that the user knows a priori what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets.

    While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose.

    The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well.

Architecture

The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:

  • The data schema of various vulnerability scanners producing the input vulnerability report.
  • The state management of various package managers and process for applying patches appropriately through them.

Effectively, copa patch can be considered a command that bridges an extensible Parse action with an extensible Apply action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:

type UpdatePackage struct {
Name string
Version string
}

type UpdateManifest struct {
OSType string
OSVersion string
Arch string
Updates []UpdatePackage
}

type ScanReportParser interface {
Parse(reportPath string) (*UpdateManifest, error)
}

type PackageManager interface {
Apply(imagePath string, report *UpdateManifest) error
}

Implementation

copa is a pseudo-frontend to buildkit implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of LLB graphs for buildkit to execute:

  1. Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.
    • Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions.
  2. Actions to fetch and deploy tools that can be injected into the target image to perform the patching.
    • In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches.
    • In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image.
    • Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well.
  3. Actions to deploy the required patch packages to the target image.
    • copa integrates with buildkit at the API level because it uses the diff and merge graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.

Tradeoffs

  • The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:
    • By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa.
    • copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this.
  • No support for windows containers given the dependency on buildkit.
- - +
Version: v0.1.x

Design

Design Tenets

+
    +
  • +

    Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update. This is a raison d’etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.

    +
  • +
  • +

    Copa is intended to work with the existing ecosystem of container images. The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.

    +
  • +
  • +

    Copa is intended to allow parties other than the image authors to address container vulnerabilities. Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.

    +
  • +
  • +

    Copa is intended to do one thing well and be composable with other tools and processes. Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane.

    +
  • +
+

Design Reasoning

+

The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental crane rebase:

+
    +
  • +

    Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an apt install during image creation will overwrite the dpkg status file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners.

    +

    To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.

    +
  • +
  • +

    Rebasing also requires that the user knows a priori what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets.

    +

    While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose.

    +

    The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well.

    +
  • +
+

Architecture

+ +

The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:

+
    +
  • The data schema of various vulnerability scanners producing the input vulnerability report.
  • +
  • The state management of various package managers and process for applying patches appropriately through them.
  • +
+

Effectively, copa patch can be considered a command that bridges an extensible Parse action with an extensible Apply action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:

+
type UpdatePackage struct {
Name string
Version string
}

type UpdateManifest struct {
OSType string
OSVersion string
Arch string
Updates []UpdatePackage
}

type ScanReportParser interface {
Parse(reportPath string) (*UpdateManifest, error)
}

type PackageManager interface {
Apply(imagePath string, report *UpdateManifest) error
}
+

Implementation

+ +

copa is a pseudo-frontend to buildkit implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of LLB graphs for buildkit to execute:

+
    +
  1. Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image. +
      +
    • Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions.
    • +
    +
  2. +
  3. Actions to fetch and deploy tools that can be injected into the target image to perform the patching. +
      +
    • In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches.
    • +
    • In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image.
    • +
    • Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well.
    • +
    +
  4. +
  5. Actions to deploy the required patch packages to the target image. +
      +
    • copa integrates with buildkit at the API level because it uses the diff and merge graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.
    • +
    +
  6. +
+

Tradeoffs

+
    +
  • The core architectural choice of relying on packages as the unit of patching creates a couple of constraints: +
      +
    • By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa.
    • +
    • copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this.
    • +
    +
  • +
  • No support for windows containers given the dependency on buildkit.
  • +
\ No newline at end of file diff --git a/website/v0.1.x/faq.html b/website/v0.1.x/faq.html index 72a5ffc0..10fe811f 100644 --- a/website/v0.1.x/faq.html +++ b/website/v0.1.x/faq.html @@ -1,19 +1,20 @@ - + - -FAQ | Copacetic + +FAQ | Copacetic - - - + + + -
-
Version: v0.1.x

FAQ

What kind of vulnerabilities can Copa patch?

Copa is capable of patching "OS level" vulnerabilities. This includes packages (like openssl) in the image that are managed by a package manager such as apt or yum. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see below for more details).

What kind of vulnerabilities can Copa not patch?

Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the golang.org/x/net module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level.

To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like http://archive.ubuntu.com/ubuntu/ for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications.

- - +
Version: v0.1.x

FAQ

What kind of vulnerabilities can Copa patch?

+

Copa is capable of patching "OS level" vulnerabilities. This includes packages (like openssl) in the image that are managed by a package manager such as apt or yum. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see below for more details).

+

What kind of vulnerabilities can Copa not patch?

+

Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the golang.org/x/net module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level.

+

To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like http://archive.ubuntu.com/ubuntu/ for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications.

\ No newline at end of file diff --git a/website/v0.1.x/installation.html b/website/v0.1.x/installation.html index 04118d10..79c14ac3 100644 --- a/website/v0.1.x/installation.html +++ b/website/v0.1.x/installation.html @@ -1,19 +1,23 @@ - + - -Installation | Copacetic + +Installation | Copacetic - - - + + + -
-
Version: v0.1.x

Installation

Homebrew

On macOS and Linux, copa can be installed via Homebrew:

brew install copa

GitHub

You can download the latest and previous versions of copa from the GitHub releases page.

Development Setup

The following instructions are for Ubuntu 22.04 with the dependency versions supported as part of the dev container environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead.

git clone https://github.com/project-copacetic/copacetic
cd copacetic
make
# OPTIONAL: install copa to a pathed folder
sudo mv dist/linux_amd64/release/copa /usr/local/bin/
- - +
Version: v0.1.x

Installation

Homebrew

+

On macOS and Linux, copa can be installed via Homebrew:

+
brew install copa
+

GitHub

+

You can download the latest and previous versions of copa from the GitHub releases page.

+

Development Setup

+

The following instructions are for Ubuntu 22.04 with the dependency versions supported as part of the dev container environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead.

+
git clone https://github.com/project-copacetic/copacetic
cd copacetic
make
# OPTIONAL: install copa to a pathed folder
sudo mv dist/linux_amd64/release/copa /usr/local/bin/
\ No newline at end of file diff --git a/website/v0.1.x/quick-start.html b/website/v0.1.x/quick-start.html index bac42892..ff21c62b 100644 --- a/website/v0.1.x/quick-start.html +++ b/website/v0.1.x/quick-start.html @@ -1,21 +1,59 @@ - + - -Quick Start | Copacetic + +Quick Start | Copacetic - - - + + + -
-
Version: v0.1.x

Quick Start

This sample illustrates how to patch containers using vulnerability reports with copa.

Prerequisites

Sample Steps

  1. Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:

    trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6

    You can also see the existing patchable vulnerabilities in table form on the shell with:

    trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6

  2. Patch the image using the Trivy report. You will need to start buildkitd if it is not already running:

    sudo buildkitd &
    sudo copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched

    Alternatively, you can run buildkitd in a container, which allows copa to be run without root access to the local buildkit socket:

    export BUILDKIT_VERSION=v0.11.4
    export BUILDKIT_PORT=8888
    docker run \
    --detach \
    --rm \
    --privileged \
    -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \
    --name buildkitd \
    --entrypoint buildkitd \
    "moby/buildkit:$BUILDKIT_VERSION" \
    --addr tcp://0.0.0.0:$BUILDKIT_PORT
    copa patch \
    -i docker.io/library/nginx:1.21.6 \
    -r nginx.1.21.6.json \
    -t 1.21.6-patched \
    -a tcp://0.0.0.0:$BUILDKIT_PORT

    In either case, copa is non-destructive and exports a new image with the specified 1.21.6-patched label to the local Docker daemon.

    NOTE: if you're running this sample against an image from a private registry instead, +

    Version: v0.1.x

    Quick Start

    This sample illustrates how to patch containers using vulnerability reports with copa.

    +

    Prerequisites

    + +

    Sample Steps

    +
      +
    1. +

      Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:

      +
      trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6
      +

      You can also see the existing patchable vulnerabilities in table form on the shell with:

      +
      trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6

      +
    2. +
    3. +

      Patch the image using the Trivy report. You will need to start buildkitd if it is not already running:

      +
      sudo buildkitd &
      sudo copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched
      +

      Alternatively, you can run buildkitd in a container, which allows copa to be run without root access to the local buildkit socket:

      +
      export BUILDKIT_VERSION=v0.11.4
      export BUILDKIT_PORT=8888
      docker run \
      --detach \
      --rm \
      --privileged \
      -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \
      --name buildkitd \
      --entrypoint buildkitd \
      "moby/buildkit:$BUILDKIT_VERSION" \
      --addr tcp://0.0.0.0:$BUILDKIT_PORT
      copa patch \
      -i docker.io/library/nginx:1.21.6 \
      -r nginx.1.21.6.json \
      -t 1.21.6-patched \
      -a tcp://0.0.0.0:$BUILDKIT_PORT
      +

      In either case, copa is non-destructive and exports a new image with the specified 1.21.6-patched label to the local Docker daemon.

      +
      +

      NOTE: if you're running this sample against an image from a private registry instead, ensure that the credentials are configured in the default Docker config.json before running copa patch, -for example, via sudo docker login -u <user> -p <password> <registry>.

    4. Scan the patched image and verify that the vulnerabilities have been patched:

      trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched

      You can also inspect the structure of the patched image with docker history to see the new patch layer appended to the image:

      $ docker history docker.io/library/nginx:1.21.6-patched
      IMAGE CREATED CREATED BY SIZE COMMENT
      a372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins… 26.1MB buildkit.exporter.image.v0
      <missing> 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0
      <missing> 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0
      <missing> 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0
      <missing> 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0
      <missing> 3 months ago COPY 30-tune-worker-processes.sh /docker-ent… 4.61kB buildkit.dockerfile.v0
      <missing> 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent… 1.04kB buildkit.dockerfile.v0
      <missing> 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker… 1.96kB buildkit.dockerfile.v0
      <missing> 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0
      <missing> 3 months ago RUN /bin/sh -c set -x && addgroup --syst… 61.1MB buildkit.dockerfile.v0
      <missing> 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0
      <missing> 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0
      <missing> 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0
      <missing> 3 months ago LABEL maintainer=NGINX Docker Maintainers <d… 0B buildkit.dockerfile.v0
      <missing> 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
      <missing> 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55… 80.4MB
    5. Run the container to verify that the image has no regressions:

      $ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched
      /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
      /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
      /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
      10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
      10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
      /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
      /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
      /docker-entrypoint.sh: Configuration complete; ready for start up
      2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method
      2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2
      2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
      2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2
      2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
      2022/05/16 18:00:17 [notice] 1#1: start worker processes
      2022/05/16 18:00:17 [notice] 1#1: start worker process 31
      2022/05/16 18:00:17 [notice] 1#1: start worker process 32
      2022/05/16 18:00:17 [notice] 1#1: start worker process 33
      2022/05/16 18:00:17 [notice] 1#1: start worker process 34
      2022/05/16 18:00:17 [notice] 1#1: start worker process 35
      2022/05/16 18:00:17 [notice] 1#1: start worker process 36
      2022/05/16 18:00:17 [notice] 1#1: start worker process 37
      2022/05/16 18:00:17 [notice] 1#1: start worker process 38
      2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received

      You can stop the container by opening a new shell instance and running: docker stop nginx-test

    - - +for example, via sudo docker login -u <user> -p <password> <registry>.

    +
    +
  3. +
  4. +

    Scan the patched image and verify that the vulnerabilities have been patched:

    +
    trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched
    +

    You can also inspect the structure of the patched image with docker history to see the new patch layer appended to the image:

    +
    $ docker history docker.io/library/nginx:1.21.6-patched
    IMAGE CREATED CREATED BY SIZE COMMENT
    a372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins… 26.1MB buildkit.exporter.image.v0
    <missing> 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0
    <missing> 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0
    <missing> 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0
    <missing> 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0
    <missing> 3 months ago COPY 30-tune-worker-processes.sh /docker-ent… 4.61kB buildkit.dockerfile.v0
    <missing> 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent… 1.04kB buildkit.dockerfile.v0
    <missing> 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker… 1.96kB buildkit.dockerfile.v0
    <missing> 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0
    <missing> 3 months ago RUN /bin/sh -c set -x && addgroup --syst… 61.1MB buildkit.dockerfile.v0
    <missing> 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0
    <missing> 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0
    <missing> 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0
    <missing> 3 months ago LABEL maintainer=NGINX Docker Maintainers <d… 0B buildkit.dockerfile.v0
    <missing> 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
    <missing> 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55… 80.4MB
    +
  5. +
  6. +

    Run the container to verify that the image has no regressions:

    +
    $ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched
    /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
    /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
    10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
    10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
    /docker-entrypoint.sh: Configuration complete; ready for start up
    2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method
    2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2
    2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
    2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2
    2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
    2022/05/16 18:00:17 [notice] 1#1: start worker processes
    2022/05/16 18:00:17 [notice] 1#1: start worker process 31
    2022/05/16 18:00:17 [notice] 1#1: start worker process 32
    2022/05/16 18:00:17 [notice] 1#1: start worker process 33
    2022/05/16 18:00:17 [notice] 1#1: start worker process 34
    2022/05/16 18:00:17 [notice] 1#1: start worker process 35
    2022/05/16 18:00:17 [notice] 1#1: start worker process 36
    2022/05/16 18:00:17 [notice] 1#1: start worker process 37
    2022/05/16 18:00:17 [notice] 1#1: start worker process 38
    2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received
    +

    You can stop the container by opening a new shell instance and running: docker stop nginx-test

    +
  7. +
\ No newline at end of file diff --git a/website/v0.2.x.html b/website/v0.2.x.html index 3009118c..fd7682d7 100644 --- a/website/v0.2.x.html +++ b/website/v0.2.x.html @@ -1,19 +1,59 @@ - + - -Introduction | Copacetic + +Introduction | Copacetic - - - + + + -
-
Version: v0.2.x

Project Copacetic: Directly patch container image vulnerabilities

copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.

Why?

We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between vulnerability disclosure and active exploitation continues to narrow, there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:

  • inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option
  • found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.

In addition to filling the operational gap not met by left-shift security practices and tools, the ability of copa to patch a container without requiring a rebuild of the container image provides other benefits:

  • Allows users other than the image publishers to also patch container images, such as DevSecOps engineers.
  • Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching.
  • Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild.
  • Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.

How?

The copa tool is an extensible engine that:

  1. Parses the needed update packages from the container image’s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats.
  2. Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers.
  3. Applies the resulting update binaries to the container image using buildkit.

This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:

  • Copa supports patching existing container images.
    • Devs don't need to build their images using specific tools or modify them in some way just to support container patching.
  • Copa works with the existing vulnerability scanning and mitigation ecosystems.
    • Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today.
    • Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports.
  • Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image.
    • For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.

For more details, refer to the copa design documentation.

- - +
Version: v0.2.x

Project Copacetic: Directly patch container image vulnerabilities

+

copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.

+

Why?

+

We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between vulnerability disclosure and active exploitation continues to narrow, there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:

+
    +
  • inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option
  • +
  • found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.
  • +
+ +

In addition to filling the operational gap not met by left-shift security practices and tools, the ability of copa to patch a container without requiring a rebuild of the container image provides other benefits:

+
    +
  • Allows users other than the image publishers to also patch container images, such as DevSecOps engineers.
  • +
  • Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching.
  • +
  • Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild.
  • +
  • Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.
  • +
+

How?

+

The copa tool is an extensible engine that:

+
    +
  1. Parses the needed update packages from the container image’s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats.
  2. +
  3. Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers.
  4. +
  5. Applies the resulting update binaries to the container image using buildkit.
  6. +
+ +

This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:

+
    +
  • Copa supports patching existing container images. +
      +
    • Devs don't need to build their images using specific tools or modify them in some way just to support container patching.
    • +
    +
  • +
  • Copa works with the existing vulnerability scanning and mitigation ecosystems. +
      +
    • Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today.
    • +
    • Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports.
    • +
    +
  • +
  • Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image. +
      +
    • For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.
    • +
    +
  • +
+

For more details, refer to the copa design documentation.

\ No newline at end of file diff --git a/website/v0.2.x/code-of-conduct.html b/website/v0.2.x/code-of-conduct.html index de4e0ee8..1b8c8f8d 100644 --- a/website/v0.2.x/code-of-conduct.html +++ b/website/v0.2.x/code-of-conduct.html @@ -1,68 +1,111 @@ - + - -Code of Conduct | Copacetic + +Code of Conduct | Copacetic - - - + + + -
-
Version: v0.2.x

Contributor Covenant Code of Conduct

Our Pledge

We as members, contributors, and leaders pledge to make participation in our +

Version: v0.2.x

Contributor Covenant Code of Conduct

+

Our Pledge

+

We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual -identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community.

Our Standards

Examples of behavior that contributes to a positive environment for our -community include:

  • Demonstrating empathy and kindness toward other people
  • Being respectful of differing opinions, viewpoints, and experiences
  • Giving and gracefully accepting constructive feedback
  • Accepting responsibility and apologizing to those affected by our mistakes, -and learning from the experience
  • Focusing on what is best not just for us as individuals, but for the overall -community

Examples of unacceptable behavior include:

  • The use of sexualized language or imagery, and sexual attention or advances of -any kind
  • Trolling, insulting or derogatory comments, and personal or political attacks
  • Public or private harassment
  • Publishing others' private information, such as a physical or email address, -without their explicit permission
  • Other conduct which could reasonably be considered inappropriate in a -professional setting

Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of +identity and orientation.

+

We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community.

+

Our Standards

+

Examples of behavior that contributes to a positive environment for our +community include:

+
    +
  • Demonstrating empathy and kindness toward other people
  • +
  • Being respectful of differing opinions, viewpoints, and experiences
  • +
  • Giving and gracefully accepting constructive feedback
  • +
  • Accepting responsibility and apologizing to those affected by our mistakes, +and learning from the experience
  • +
  • Focusing on what is best not just for us as individuals, but for the overall +community
  • +
+

Examples of unacceptable behavior include:

+
    +
  • The use of sexualized language or imagery, and sexual attention or advances of +any kind
  • +
  • Trolling, insulting or derogatory comments, and personal or political attacks
  • +
  • Public or private harassment
  • +
  • Publishing others' private information, such as a physical or email address, +without their explicit permission
  • +
  • Other conduct which could reasonably be considered inappropriate in a +professional setting
  • +
+

Enforcement Responsibilities

+

Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, -or harmful.

Community leaders have the right and responsibility to remove, edit, or reject +or harmful.

+

Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate.

Scope

This Code of Conduct applies within all community spaces, and also applies when +decisions when appropriate.

+

Scope

+

This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed -representative at an online or offline event.

Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be +representative at an online or offline event.

+

Enforcement

+

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at project-copacetic@googlegroups.com. -All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the -reporter of any incident.

Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct:

1. Correction

Community Impact: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community.

Consequence: A private, written warning from community leaders, providing +All complaints will be reviewed and investigated promptly and fairly.

+

All community leaders are obligated to respect the privacy and security of the +reporter of any incident.

+

Enforcement Guidelines

+

Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct:

+

1. Correction

+

Community Impact: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community.

+

Consequence: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested.

2. Warning

Community Impact: A violation through a single incident or series of -actions.

Consequence: A warning with consequences for continued behavior. No +behavior was inappropriate. A public apology may be requested.

+

2. Warning

+

Community Impact: A violation through a single incident or series of +actions.

+

Consequence: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent -ban.

3. Temporary Ban

Community Impact: A serious violation of community standards, including -sustained inappropriate behavior.

Consequence: A temporary ban from any sort of interaction or public +ban.

+

3. Temporary Ban

+

Community Impact: A serious violation of community standards, including +sustained inappropriate behavior.

+

Consequence: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban.

4. Permanent Ban

Community Impact: Demonstrating a pattern of violation of community +Violating these terms may lead to a permanent ban.

+

4. Permanent Ban

+

Community Impact: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals.

Consequence: A permanent ban from any sort of public interaction within the -community.

Attribution

This Code of Conduct is adapted from the Contributor Covenant, +individual, or aggression toward or disparagement of classes of individuals.

+

Consequence: A permanent ban from any sort of public interaction within the +community.

+

Attribution

+

This Code of Conduct is adapted from the Contributor Covenant, version 2.1, available at -https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

Community Impact Guidelines were inspired by -Mozilla's code of conduct enforcement ladder.

For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

+

Community Impact Guidelines were inspired by +Mozilla's code of conduct enforcement ladder.

+

For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations.

- - +https://www.contributor-covenant.org/translations.

\ No newline at end of file diff --git a/website/v0.2.x/contributing.html b/website/v0.2.x/contributing.html index b422ed62..4a5a9f35 100644 --- a/website/v0.2.x/contributing.html +++ b/website/v0.2.x/contributing.html @@ -1,33 +1,133 @@ - + - -Contributing | Copacetic + +Contributing | Copacetic - - - + + + -
-
Version: v0.2.x

Contributing

Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.

Contributing Issues

Before opening any new issues, please search our existing GitHub issues to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead.

When opening an issue, please select the most appropriate template for what you're contributing:

  • Bug Report: If you would like to report the project or tool behaving in unexpected ways.
  • Documentation Improvement: If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content.
  • Request: If you have a feature request, suggestion, or a even a design proposal to review.
  • Question: If you would like to ask the maintainers a question about the project.

Contributing Code

Getting Started

Follow the instructions to either:

For an overview of the project components, refer to the copa design document.

Visual Studio Code Development Container

VSCode supports development in a containerized environment through its Remote - Container extension. This folder provides a development container which encapsulates the dependencies specified in the instructions to build and run copa.

Prerequisites

  1. Docker

    For Windows users, enabling WSL2 back-end integration with Docker is recommended.

  2. Visual Studio Code
  3. Visual Studio Code Remote - Containers extension

Using the dev container

  1. After you have cloned this repo locally, open the repo folder in VSCode. VSCode will detect the presence of this .devcontainer subfolder and will prompt you to reopen the project in a container.

    Alternatively, you can open the command palette and use the Remote-Containers: Reopen in Container command.

  2. Once the container is loaded, open an integrated terminal in VSCode and you can start running the demo instructions.

⚠ If running via Docker Desktop for Windows

Note that the mounted workspace files appear owned by root in the dev container, which will cause git commands to fail with a fatal: detected dubious ownership in a repository error due to safe.directory checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the vscode user:

sudo chown -R vscode:vscode /workspace/copacetic

Personalizing user settings in a dev container

VSCode supports applying your user settings, such as your .gitconfig, to a dev container through the use of dotfiles repositories. This can be done through your own VSCode settings.json file without changing the dev container image or configuration.

Tests

Once you can successfully make the project, any code contributions should also successfully:

  • Pass unit tests via make test.
  • Lint cleanly via make lint.

Pull requests will also be expected to pass the PR functional tests specified by .github/workflows/build.yml.

Pull Requests

If you'd like to start contributing code to the project, you can search for issues with the good first issue label. Other kinds of PR contributions we would look for include:

  • Fixes for bugs and other correctness issues.
  • Docs and other content improvements (e.g. samples).
  • Extensions to support parsing new scanning report formats.
  • Extensions to support patching images based on new distros or using new package managers.

For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:

  • Architectural changes (e.g. breaking interfaces or violations of this project's design tenets).
  • Unsolicited features that significantly expand the functional scope of the tool.

Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the Angular commit message format for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs.

We suggest:

  • Use the standard header format of "<type>: <short summary>" where the <type> is one of the following:
    • build: Changes that affect the build system or external dependencies
    • ci: Changes to the GitHub workflows and configurations
    • docs: Documentation only changes
    • feat: A new feature
    • fix: A bug fix
    • perf: A code change that improves performance
    • refactor: A code change that neither fixes a bug nor adds a feature
    • test: Adding missing tests or correcting existing tests
  • Use a concise, imperative description of the changes included in the <short summary> of the header, the body of the PR, and generally in your commit messages.
  • Use GitHub keywords in the footer of your PR description, such as closes to automatically close issues the PR intends to address.

Developer Certificate of Origin (DCO)

The Developer Certificate of Origin (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the full text of the DCO, reformatted for readability:

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I +

Version: v0.2.x

Contributing

Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.

+

Contributing Issues

+

Before opening any new issues, please search our existing GitHub issues to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead.

+

When opening an issue, please select the most appropriate template for what you're contributing:

+
    +
  • Bug Report: If you would like to report the project or tool behaving in unexpected ways.
  • +
  • Documentation Improvement: If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content.
  • +
  • Request: If you have a feature request, suggestion, or a even a design proposal to review.
  • +
  • Question: If you would like to ask the maintainers a question about the project.
  • +
+

Contributing Code

+

Getting Started

+

Follow the instructions to either:

+ +

For an overview of the project components, refer to the copa design document.

+

Visual Studio Code Development Container

+

VSCode supports development in a containerized environment through its Remote - Container extension. This folder provides a development container which encapsulates the dependencies specified in the instructions to build and run copa.

+

Prerequisites

+
    +
  1. Docker +
    +

    For Windows users, enabling WSL2 back-end integration with Docker is recommended.

    +
    +
  2. +
  3. Visual Studio Code
  4. +
  5. Visual Studio Code Remote - Containers extension
  6. +
+

Using the dev container

+
    +
  1. +

    After you have cloned this repo locally, open the repo folder in VSCode. VSCode will detect the presence of this .devcontainer subfolder and will prompt you to reopen the project in a container.

    +

    Alternatively, you can open the command palette and use the Remote-Containers: Reopen in Container command.

    +
  2. +
  3. +

    Once the container is loaded, open an integrated terminal in VSCode and you can start running the demo instructions.

    +
  4. +
+
+

⚠ If running via Docker Desktop for Windows

+

Note that the mounted workspace files appear owned by root in the dev container, which will cause git commands to fail with a fatal: detected dubious ownership in a repository error due to safe.directory checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the vscode user:

+
sudo chown -R vscode:vscode /workspace/copacetic
+
+

Personalizing user settings in a dev container

+

VSCode supports applying your user settings, such as your .gitconfig, to a dev container through the use of dotfiles repositories. This can be done through your own VSCode settings.json file without changing the dev container image or configuration.

+

Tests

+

Once you can successfully make the project, any code contributions should also successfully:

+
    +
  • Pass unit tests via make test.
  • +
  • Lint cleanly via make lint.
  • +
+

Pull requests will also be expected to pass the PR functional tests specified by .github/workflows/build.yml.

+

Pull Requests

+

If you'd like to start contributing code to the project, you can search for issues with the good first issue label. Other kinds of PR contributions we would look for include:

+
    +
  • Fixes for bugs and other correctness issues.
  • +
  • Docs and other content improvements (e.g. samples).
  • +
  • Extensions to support parsing new scanning report formats.
  • +
  • Extensions to support patching images based on new distros or using new package managers.
  • +
+

For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:

+
    +
  • Architectural changes (e.g. breaking interfaces or violations of this project's design tenets).
  • +
  • Unsolicited features that significantly expand the functional scope of the tool.
  • +
+

Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the Angular commit message format for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs.

+

We suggest:

+
    +
  • Use the standard header format of "<type>: <short summary>" where the <type> is one of the following: +
      +
    • build: Changes that affect the build system or external dependencies
    • +
    • ci: Changes to the GitHub workflows and configurations
    • +
    • docs: Documentation only changes
    • +
    • feat: A new feature
    • +
    • fix: A bug fix
    • +
    • perf: A code change that improves performance
    • +
    • refactor: A code change that neither fixes a bug nor adds a feature
    • +
    • test: Adding missing tests or correcting existing tests
    • +
    +
  • +
  • Use a concise, imperative description of the changes included in the <short summary> of the header, the body of the PR, and generally in your commit messages.
  • +
  • Use GitHub keywords in the footer of your PR description, such as closes to automatically close issues the PR intends to address.
  • +
+

Developer Certificate of Origin (DCO)

+

The Developer Certificate of Origin (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the full text of the DCO, reformatted for readability:

+
+

By making a contribution to this project, I certify that:

+

(a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license -indicated in the file; or

(b) The contribution is based upon previous work that, to the best +indicated in the file; or

+

(b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated -in the file; or

(c) The contribution was provided directly to me by some other +in the file; or

+

(c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified -it.

(d) I understand and agree that this project and the contribution +it.

+

(d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with -this project or the open source license(s) involved.

Contributors sign-off that they adhere to these requirements by adding a Signed-off-by line to commit messages.

This is my commit message

Signed-off-by: Random J Developer <random@developer.example.org>

Git even has a -s command line option to append this automatically to your commit message:

git commit -s -m 'This is my commit message'

Pull requests that do not contain a valid Signed-off-by line cannot be merged.

I didn't sign my commit, now what?

No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:

git switch <branch-name>
git commit --amend --no-edit --signoff
git push --force-with-lease <remote-name> <branch-name>

Code of Conduct

This project has adopted the Contributor Covenant Code of Conduct.

- - +this project or the open source license(s) involved.

+
+

Contributors sign-off that they adhere to these requirements by adding a Signed-off-by line to commit messages.

+
This is my commit message

Signed-off-by: Random J Developer <random@developer.example.org>
+

Git even has a -s command line option to append this automatically to your commit message:

+
git commit -s -m 'This is my commit message'
+

Pull requests that do not contain a valid Signed-off-by line cannot be merged.

+

I didn't sign my commit, now what?

+

No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:

+
git switch <branch-name>
git commit --amend --no-edit --signoff
git push --force-with-lease <remote-name> <branch-name>
+

Code of Conduct

+

This project has adopted the Contributor Covenant Code of Conduct.

\ No newline at end of file diff --git a/website/v0.2.x/design.html b/website/v0.2.x/design.html index 6d50a1a6..e45333d4 100644 --- a/website/v0.2.x/design.html +++ b/website/v0.2.x/design.html @@ -1,19 +1,84 @@ - + - -Design | Copacetic + +Design | Copacetic - - - + + + -
-
Version: v0.2.x

Design

Design Tenets

  • Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update. This is a raison d’etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.

  • Copa is intended to work with the existing ecosystem of container images. The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.

  • Copa is intended to allow parties other than the image authors to address container vulnerabilities. Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.

  • Copa is intended to do one thing well and be composable with other tools and processes. Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane.

Design Reasoning

The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental crane rebase:

  • Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an apt install during image creation will overwrite the dpkg status file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners.

    To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.

  • Rebasing also requires that the user knows a priori what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets.

    While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose.

    The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well.

Architecture

The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:

  • The data schema of various vulnerability scanners producing the input vulnerability report.
  • The state management of various package managers and process for applying patches appropriately through them.

Effectively, copa patch can be considered a command that bridges an extensible Parse action with an extensible Apply action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:

type UpdatePackage struct {
Name string
Version string
}

type UpdateManifest struct {
OSType string
OSVersion string
Arch string
Updates []UpdatePackage
}

type ScanReportParser interface {
Parse(reportPath string) (*UpdateManifest, error)
}

type PackageManager interface {
Apply(imagePath string, report *UpdateManifest) error
}

Implementation

copa is a pseudo-frontend to buildkit implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of LLB graphs for buildkit to execute:

  1. Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.
    • Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions.
  2. Actions to fetch and deploy tools that can be injected into the target image to perform the patching.
    • In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches.
    • In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image.
    • Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well.
  3. Actions to deploy the required patch packages to the target image.
    • copa integrates with buildkit at the API level because it uses the diff and merge graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.

Tradeoffs

  • The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:
    • By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa.
    • copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this.
  • No support for windows containers given the dependency on buildkit.
- - +
Version: v0.2.x

Design

Design Tenets

+
    +
  • +

    Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update. This is a raison d’etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.

    +
  • +
  • +

    Copa is intended to work with the existing ecosystem of container images. The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.

    +
  • +
  • +

    Copa is intended to allow parties other than the image authors to address container vulnerabilities. Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.

    +
  • +
  • +

    Copa is intended to do one thing well and be composable with other tools and processes. Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane.

    +
  • +
+

Design Reasoning

+

The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental crane rebase:

+
    +
  • +

    Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an apt install during image creation will overwrite the dpkg status file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners.

    +

    To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.

    +
  • +
  • +

    Rebasing also requires that the user knows a priori what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets.

    +

    While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose.

    +

    The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well.

    +
  • +
+

Architecture

+ +

The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:

+
    +
  • The data schema of various vulnerability scanners producing the input vulnerability report.
  • +
  • The state management of various package managers and process for applying patches appropriately through them.
  • +
+

Effectively, copa patch can be considered a command that bridges an extensible Parse action with an extensible Apply action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:

+
type UpdatePackage struct {
Name string
Version string
}

type UpdateManifest struct {
OSType string
OSVersion string
Arch string
Updates []UpdatePackage
}

type ScanReportParser interface {
Parse(reportPath string) (*UpdateManifest, error)
}

type PackageManager interface {
Apply(imagePath string, report *UpdateManifest) error
}
+

Implementation

+ +

copa is a pseudo-frontend to buildkit implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of LLB graphs for buildkit to execute:

+
    +
  1. Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image. +
      +
    • Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions.
    • +
    +
  2. +
  3. Actions to fetch and deploy tools that can be injected into the target image to perform the patching. +
      +
    • In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches.
    • +
    • In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image.
    • +
    • Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well.
    • +
    +
  4. +
  5. Actions to deploy the required patch packages to the target image. +
      +
    • copa integrates with buildkit at the API level because it uses the diff and merge graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.
    • +
    +
  6. +
+

Tradeoffs

+
    +
  • The core architectural choice of relying on packages as the unit of patching creates a couple of constraints: +
      +
    • By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa.
    • +
    • copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this.
    • +
    +
  • +
  • No support for windows containers given the dependency on buildkit.
  • +
\ No newline at end of file diff --git a/website/v0.2.x/faq.html b/website/v0.2.x/faq.html index e13b0f5d..4003c5c8 100644 --- a/website/v0.2.x/faq.html +++ b/website/v0.2.x/faq.html @@ -1,19 +1,20 @@ - + - -FAQ | Copacetic + +FAQ | Copacetic - - - + + + -
-
Version: v0.2.x

FAQ

What kind of vulnerabilities can Copa patch?

Copa is capable of patching "OS level" vulnerabilities. This includes packages (like openssl) in the image that are managed by a package manager such as apt or yum. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see below for more details).

What kind of vulnerabilities can Copa not patch?

Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the golang.org/x/net module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level.

To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like http://archive.ubuntu.com/ubuntu/ for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications.

- - +
Version: v0.2.x

FAQ

What kind of vulnerabilities can Copa patch?

+

Copa is capable of patching "OS level" vulnerabilities. This includes packages (like openssl) in the image that are managed by a package manager such as apt or yum. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see below for more details).

+

What kind of vulnerabilities can Copa not patch?

+

Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the golang.org/x/net module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level.

+

To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like http://archive.ubuntu.com/ubuntu/ for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications.

\ No newline at end of file diff --git a/website/v0.2.x/installation.html b/website/v0.2.x/installation.html index fa910f5f..8ebd42d6 100644 --- a/website/v0.2.x/installation.html +++ b/website/v0.2.x/installation.html @@ -1,19 +1,23 @@ - + - -Installation | Copacetic + +Installation | Copacetic - - - + + + -
-
Version: v0.2.x

Installation

Homebrew

On macOS and Linux, copa can be installed via Homebrew:

brew install copa

GitHub

You can download the latest and previous versions of copa from the GitHub releases page.

Development Setup

The following instructions are for Ubuntu 22.04 with the dependency versions supported as part of the dev container environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead.

git clone https://github.com/project-copacetic/copacetic
cd copacetic
make
# OPTIONAL: install copa to a pathed folder
sudo mv dist/linux_amd64/release/copa /usr/local/bin/
- - +
Version: v0.2.x

Installation

Homebrew

+

On macOS and Linux, copa can be installed via Homebrew:

+
brew install copa
+

GitHub

+

You can download the latest and previous versions of copa from the GitHub releases page.

+

Development Setup

+

The following instructions are for Ubuntu 22.04 with the dependency versions supported as part of the dev container environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead.

+
git clone https://github.com/project-copacetic/copacetic
cd copacetic
make
# OPTIONAL: install copa to a pathed folder
sudo mv dist/linux_amd64/release/copa /usr/local/bin/
\ No newline at end of file diff --git a/website/v0.2.x/quick-start.html b/website/v0.2.x/quick-start.html index 4ae330cf..80cfdf08 100644 --- a/website/v0.2.x/quick-start.html +++ b/website/v0.2.x/quick-start.html @@ -1,21 +1,59 @@ - + - -Quick Start | Copacetic + +Quick Start | Copacetic - - - + + + -
-
Version: v0.2.x

Quick Start

This sample illustrates how to patch containers using vulnerability reports with copa.

Prerequisites

Sample Steps

  1. Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:

    trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6

    You can also see the existing patchable vulnerabilities in table form on the shell with:

    trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6

  2. Patch the image using the Trivy report. You will need to start buildkitd if it is not already running:

    sudo buildkitd &
    sudo copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched

    Alternatively, you can run buildkitd in a container, which allows copa to be run without root access to the local buildkit socket:

    export BUILDKIT_VERSION=v0.11.4
    export BUILDKIT_PORT=8888
    docker run \
    --detach \
    --rm \
    --privileged \
    -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \
    --name buildkitd \
    --entrypoint buildkitd \
    "moby/buildkit:$BUILDKIT_VERSION" \
    --addr tcp://0.0.0.0:$BUILDKIT_PORT
    copa patch \
    -i docker.io/library/nginx:1.21.6 \
    -r nginx.1.21.6.json \
    -t 1.21.6-patched \
    -a tcp://0.0.0.0:$BUILDKIT_PORT

    In either case, copa is non-destructive and exports a new image with the specified 1.21.6-patched label to the local Docker daemon.

    NOTE: if you're running this sample against an image from a private registry instead, +

    Version: v0.2.x

    Quick Start

    This sample illustrates how to patch containers using vulnerability reports with copa.

    +

    Prerequisites

    + +

    Sample Steps

    +
      +
    1. +

      Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:

      +
      trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6
      +

      You can also see the existing patchable vulnerabilities in table form on the shell with:

      +
      trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6

      +
    2. +
    3. +

      Patch the image using the Trivy report. You will need to start buildkitd if it is not already running:

      +
      sudo buildkitd &
      sudo copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched
      +

      Alternatively, you can run buildkitd in a container, which allows copa to be run without root access to the local buildkit socket:

      +
      export BUILDKIT_VERSION=v0.11.4
      export BUILDKIT_PORT=8888
      docker run \
      --detach \
      --rm \
      --privileged \
      -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \
      --name buildkitd \
      --entrypoint buildkitd \
      "moby/buildkit:$BUILDKIT_VERSION" \
      --addr tcp://0.0.0.0:$BUILDKIT_PORT
      copa patch \
      -i docker.io/library/nginx:1.21.6 \
      -r nginx.1.21.6.json \
      -t 1.21.6-patched \
      -a tcp://0.0.0.0:$BUILDKIT_PORT
      +

      In either case, copa is non-destructive and exports a new image with the specified 1.21.6-patched label to the local Docker daemon.

      +
      +

      NOTE: if you're running this sample against an image from a private registry instead, ensure that the credentials are configured in the default Docker config.json before running copa patch, -for example, via sudo docker login -u <user> -p <password> <registry>.

    4. Scan the patched image and verify that the vulnerabilities have been patched:

      trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched

      You can also inspect the structure of the patched image with docker history to see the new patch layer appended to the image:

      $ docker history docker.io/library/nginx:1.21.6-patched
      IMAGE CREATED CREATED BY SIZE COMMENT
      a372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins… 26.1MB buildkit.exporter.image.v0
      <missing> 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0
      <missing> 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0
      <missing> 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0
      <missing> 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0
      <missing> 3 months ago COPY 30-tune-worker-processes.sh /docker-ent… 4.61kB buildkit.dockerfile.v0
      <missing> 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent… 1.04kB buildkit.dockerfile.v0
      <missing> 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker… 1.96kB buildkit.dockerfile.v0
      <missing> 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0
      <missing> 3 months ago RUN /bin/sh -c set -x && addgroup --syst… 61.1MB buildkit.dockerfile.v0
      <missing> 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0
      <missing> 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0
      <missing> 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0
      <missing> 3 months ago LABEL maintainer=NGINX Docker Maintainers <d… 0B buildkit.dockerfile.v0
      <missing> 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
      <missing> 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55… 80.4MB
    5. Run the container to verify that the image has no regressions:

      $ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched
      /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
      /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
      /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
      10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
      10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
      /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
      /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
      /docker-entrypoint.sh: Configuration complete; ready for start up
      2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method
      2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2
      2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
      2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2
      2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
      2022/05/16 18:00:17 [notice] 1#1: start worker processes
      2022/05/16 18:00:17 [notice] 1#1: start worker process 31
      2022/05/16 18:00:17 [notice] 1#1: start worker process 32
      2022/05/16 18:00:17 [notice] 1#1: start worker process 33
      2022/05/16 18:00:17 [notice] 1#1: start worker process 34
      2022/05/16 18:00:17 [notice] 1#1: start worker process 35
      2022/05/16 18:00:17 [notice] 1#1: start worker process 36
      2022/05/16 18:00:17 [notice] 1#1: start worker process 37
      2022/05/16 18:00:17 [notice] 1#1: start worker process 38
      2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received

      You can stop the container by opening a new shell instance and running: docker stop nginx-test

    - - +for example, via sudo docker login -u <user> -p <password> <registry>.

    +
    +
  3. +
  4. +

    Scan the patched image and verify that the vulnerabilities have been patched:

    +
    trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched
    +

    You can also inspect the structure of the patched image with docker history to see the new patch layer appended to the image:

    +
    $ docker history docker.io/library/nginx:1.21.6-patched
    IMAGE CREATED CREATED BY SIZE COMMENT
    a372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins… 26.1MB buildkit.exporter.image.v0
    <missing> 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0
    <missing> 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0
    <missing> 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0
    <missing> 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0
    <missing> 3 months ago COPY 30-tune-worker-processes.sh /docker-ent… 4.61kB buildkit.dockerfile.v0
    <missing> 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent… 1.04kB buildkit.dockerfile.v0
    <missing> 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker… 1.96kB buildkit.dockerfile.v0
    <missing> 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0
    <missing> 3 months ago RUN /bin/sh -c set -x && addgroup --syst… 61.1MB buildkit.dockerfile.v0
    <missing> 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0
    <missing> 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0
    <missing> 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0
    <missing> 3 months ago LABEL maintainer=NGINX Docker Maintainers <d… 0B buildkit.dockerfile.v0
    <missing> 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
    <missing> 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55… 80.4MB
    +
  5. +
  6. +

    Run the container to verify that the image has no regressions:

    +
    $ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched
    /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
    /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
    10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
    10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
    /docker-entrypoint.sh: Configuration complete; ready for start up
    2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method
    2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2
    2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
    2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2
    2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
    2022/05/16 18:00:17 [notice] 1#1: start worker processes
    2022/05/16 18:00:17 [notice] 1#1: start worker process 31
    2022/05/16 18:00:17 [notice] 1#1: start worker process 32
    2022/05/16 18:00:17 [notice] 1#1: start worker process 33
    2022/05/16 18:00:17 [notice] 1#1: start worker process 34
    2022/05/16 18:00:17 [notice] 1#1: start worker process 35
    2022/05/16 18:00:17 [notice] 1#1: start worker process 36
    2022/05/16 18:00:17 [notice] 1#1: start worker process 37
    2022/05/16 18:00:17 [notice] 1#1: start worker process 38
    2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received
    +

    You can stop the container by opening a new shell instance and running: docker stop nginx-test

    +
  7. +
\ No newline at end of file diff --git a/website/v0.3.x.html b/website/v0.3.x.html index 1ab60919..a1f8cfa3 100644 --- a/website/v0.3.x.html +++ b/website/v0.3.x.html @@ -1,19 +1,59 @@ - + - -Introduction | Copacetic + +Introduction | Copacetic - - - + + + -
-
Version: v0.3.x

Project Copacetic: Directly patch container image vulnerabilities

copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.

Why?

We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between vulnerability disclosure and active exploitation continues to narrow, there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:

  • inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option
  • found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.

In addition to filling the operational gap not met by left-shift security practices and tools, the ability of copa to patch a container without requiring a rebuild of the container image provides other benefits:

  • Allows users other than the image publishers to also patch container images, such as DevSecOps engineers.
  • Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching.
  • Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild.
  • Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.

How?

The copa tool is an extensible engine that:

  1. Parses the needed update packages from the container image’s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats.
  2. Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers.
  3. Applies the resulting update binaries to the container image using buildkit.

This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:

  • Copa supports patching existing container images.
    • Devs don't need to build their images using specific tools or modify them in some way just to support container patching.
  • Copa works with the existing vulnerability scanning and mitigation ecosystems.
    • Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today.
    • Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports.
  • Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image.
    • For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.

For more details, refer to the copa design documentation.

- - +
Version: v0.3.x

Project Copacetic: Directly patch container image vulnerabilities

+

copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.

+

Why?

+

We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between vulnerability disclosure and active exploitation continues to narrow, there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:

+
    +
  • inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option
  • +
  • found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.
  • +
+ +

In addition to filling the operational gap not met by left-shift security practices and tools, the ability of copa to patch a container without requiring a rebuild of the container image provides other benefits:

+
    +
  • Allows users other than the image publishers to also patch container images, such as DevSecOps engineers.
  • +
  • Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching.
  • +
  • Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild.
  • +
  • Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.
  • +
+

How?

+

The copa tool is an extensible engine that:

+
    +
  1. Parses the needed update packages from the container image’s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats.
  2. +
  3. Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers.
  4. +
  5. Applies the resulting update binaries to the container image using buildkit.
  6. +
+ +

This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:

+
    +
  • Copa supports patching existing container images. +
      +
    • Devs don't need to build their images using specific tools or modify them in some way just to support container patching.
    • +
    +
  • +
  • Copa works with the existing vulnerability scanning and mitigation ecosystems. +
      +
    • Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today.
    • +
    • Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports.
    • +
    +
  • +
  • Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image. +
      +
    • For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.
    • +
    +
  • +
+

For more details, refer to the copa design documentation.

\ No newline at end of file diff --git a/website/v0.3.x/code-of-conduct.html b/website/v0.3.x/code-of-conduct.html index 4f58591e..58a452f1 100644 --- a/website/v0.3.x/code-of-conduct.html +++ b/website/v0.3.x/code-of-conduct.html @@ -1,68 +1,111 @@ - + - -Code of Conduct | Copacetic + +Code of Conduct | Copacetic - - - + + + -
-
Version: v0.3.x

Contributor Covenant Code of Conduct

Our Pledge

We as members, contributors, and leaders pledge to make participation in our +

Version: v0.3.x

Contributor Covenant Code of Conduct

+

Our Pledge

+

We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual -identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community.

Our Standards

Examples of behavior that contributes to a positive environment for our -community include:

  • Demonstrating empathy and kindness toward other people
  • Being respectful of differing opinions, viewpoints, and experiences
  • Giving and gracefully accepting constructive feedback
  • Accepting responsibility and apologizing to those affected by our mistakes, -and learning from the experience
  • Focusing on what is best not just for us as individuals, but for the overall -community

Examples of unacceptable behavior include:

  • The use of sexualized language or imagery, and sexual attention or advances of -any kind
  • Trolling, insulting or derogatory comments, and personal or political attacks
  • Public or private harassment
  • Publishing others' private information, such as a physical or email address, -without their explicit permission
  • Other conduct which could reasonably be considered inappropriate in a -professional setting

Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of +identity and orientation.

+

We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community.

+

Our Standards

+

Examples of behavior that contributes to a positive environment for our +community include:

+
    +
  • Demonstrating empathy and kindness toward other people
  • +
  • Being respectful of differing opinions, viewpoints, and experiences
  • +
  • Giving and gracefully accepting constructive feedback
  • +
  • Accepting responsibility and apologizing to those affected by our mistakes, +and learning from the experience
  • +
  • Focusing on what is best not just for us as individuals, but for the overall +community
  • +
+

Examples of unacceptable behavior include:

+
    +
  • The use of sexualized language or imagery, and sexual attention or advances of +any kind
  • +
  • Trolling, insulting or derogatory comments, and personal or political attacks
  • +
  • Public or private harassment
  • +
  • Publishing others' private information, such as a physical or email address, +without their explicit permission
  • +
  • Other conduct which could reasonably be considered inappropriate in a +professional setting
  • +
+

Enforcement Responsibilities

+

Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, -or harmful.

Community leaders have the right and responsibility to remove, edit, or reject +or harmful.

+

Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate.

Scope

This Code of Conduct applies within all community spaces, and also applies when +decisions when appropriate.

+

Scope

+

This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed -representative at an online or offline event.

Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be +representative at an online or offline event.

+

Enforcement

+

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at project-copacetic@googlegroups.com. -All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the -reporter of any incident.

Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct:

1. Correction

Community Impact: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community.

Consequence: A private, written warning from community leaders, providing +All complaints will be reviewed and investigated promptly and fairly.

+

All community leaders are obligated to respect the privacy and security of the +reporter of any incident.

+

Enforcement Guidelines

+

Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct:

+

1. Correction

+

Community Impact: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community.

+

Consequence: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested.

2. Warning

Community Impact: A violation through a single incident or series of -actions.

Consequence: A warning with consequences for continued behavior. No +behavior was inappropriate. A public apology may be requested.

+

2. Warning

+

Community Impact: A violation through a single incident or series of +actions.

+

Consequence: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent -ban.

3. Temporary Ban

Community Impact: A serious violation of community standards, including -sustained inappropriate behavior.

Consequence: A temporary ban from any sort of interaction or public +ban.

+

3. Temporary Ban

+

Community Impact: A serious violation of community standards, including +sustained inappropriate behavior.

+

Consequence: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban.

4. Permanent Ban

Community Impact: Demonstrating a pattern of violation of community +Violating these terms may lead to a permanent ban.

+

4. Permanent Ban

+

Community Impact: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals.

Consequence: A permanent ban from any sort of public interaction within the -community.

Attribution

This Code of Conduct is adapted from the Contributor Covenant, +individual, or aggression toward or disparagement of classes of individuals.

+

Consequence: A permanent ban from any sort of public interaction within the +community.

+

Attribution

+

This Code of Conduct is adapted from the Contributor Covenant, version 2.1, available at -https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

Community Impact Guidelines were inspired by -Mozilla's code of conduct enforcement ladder.

For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

+

Community Impact Guidelines were inspired by +Mozilla's code of conduct enforcement ladder.

+

For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations.

- - +https://www.contributor-covenant.org/translations.

\ No newline at end of file diff --git a/website/v0.3.x/contributing.html b/website/v0.3.x/contributing.html index 465fa63a..21f0e1e0 100644 --- a/website/v0.3.x/contributing.html +++ b/website/v0.3.x/contributing.html @@ -1,33 +1,137 @@ - + - -Contributing | Copacetic + +Contributing | Copacetic - - - + + + -
-
Version: v0.3.x

Contributing

Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.

Bi-Weekly Community Meeting

A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join here

Slack

To discuss issues with Copa, features, or development, you can join the #copa channel on the OCI Slack.

Contributing Issues

Before opening any new issues, please search our existing GitHub issues to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead.

When opening an issue, please select the most appropriate template for what you're contributing:

  • Bug Report: If you would like to report the project or tool behaving in unexpected ways.
  • Documentation Improvement: If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content.
  • Request: If you have a feature request, suggestion, or a even a design proposal to review.
  • Question: If you would like to ask the maintainers a question about the project.

Contributing Code

Getting Started

Follow the instructions to either:

For an overview of the project components, refer to the copa design document.

Visual Studio Code Development Container

VSCode supports development in a containerized environment through its Remote - Container extension. This folder provides a development container which encapsulates the dependencies specified in the instructions to build and run copa.

Prerequisites

  1. Docker

    For Windows users, enabling WSL2 back-end integration with Docker is recommended.

  2. Visual Studio Code
  3. Visual Studio Code Remote - Containers extension

Using the dev container

  1. After you have cloned this repo locally, open the repo folder in VSCode. VSCode will detect the presence of this .devcontainer subfolder and will prompt you to reopen the project in a container.

    Alternatively, you can open the command palette and use the Remote-Containers: Reopen in Container command.

  2. Once the container is loaded, open an integrated terminal in VSCode and you can start running the demo instructions.

⚠ If running via Docker Desktop for Windows

Note that the mounted workspace files appear owned by root in the dev container, which will cause git commands to fail with a fatal: detected dubious ownership in a repository error due to safe.directory checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the vscode user:

sudo chown -R vscode:vscode /workspace/copacetic

Personalizing user settings in a dev container

VSCode supports applying your user settings, such as your .gitconfig, to a dev container through the use of dotfiles repositories. This can be done through your own VSCode settings.json file without changing the dev container image or configuration.

Tests

Once you can successfully make the project, any code contributions should also successfully:

  • Pass unit tests via make test.
  • Lint cleanly via make lint.

Pull requests will also be expected to pass the PR functional tests specified by .github/workflows/build.yml.

Pull Requests

If you'd like to start contributing code to the project, you can search for issues with the good first issue label. Other kinds of PR contributions we would look for include:

  • Fixes for bugs and other correctness issues.
  • Docs and other content improvements (e.g. samples).
  • Extensions to support parsing new scanning report formats.
  • Extensions to support patching images based on new distros or using new package managers.

For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:

  • Architectural changes (e.g. breaking interfaces or violations of this project's design tenets).
  • Unsolicited features that significantly expand the functional scope of the tool.

Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the Angular commit message format for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs.

We suggest:

  • Use the standard header format of "<type>: <short summary>" where the <type> is one of the following:
    • build: Changes that affect the build system or external dependencies
    • ci: Changes to the GitHub workflows and configurations
    • docs: Documentation only changes
    • feat: A new feature
    • fix: A bug fix
    • perf: A code change that improves performance
    • refactor: A code change that neither fixes a bug nor adds a feature
    • test: Adding missing tests or correcting existing tests
  • Use a concise, imperative description of the changes included in the <short summary> of the header, the body of the PR, and generally in your commit messages.
  • Use GitHub keywords in the footer of your PR description, such as closes to automatically close issues the PR intends to address.

Developer Certificate of Origin (DCO)

The Developer Certificate of Origin (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the full text of the DCO, reformatted for readability:

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I +

Version: v0.3.x

Contributing

Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.

+

Bi-Weekly Community Meeting

+

A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join here

+

Slack

+

To discuss issues with Copa, features, or development, you can join the #copa channel on the OCI Slack.

+

Contributing Issues

+

Before opening any new issues, please search our existing GitHub issues to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead.

+

When opening an issue, please select the most appropriate template for what you're contributing:

+
    +
  • Bug Report: If you would like to report the project or tool behaving in unexpected ways.
  • +
  • Documentation Improvement: If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content.
  • +
  • Request: If you have a feature request, suggestion, or a even a design proposal to review.
  • +
  • Question: If you would like to ask the maintainers a question about the project.
  • +
+

Contributing Code

+

Getting Started

+

Follow the instructions to either:

+ +

For an overview of the project components, refer to the copa design document.

+

Visual Studio Code Development Container

+

VSCode supports development in a containerized environment through its Remote - Container extension. This folder provides a development container which encapsulates the dependencies specified in the instructions to build and run copa.

+

Prerequisites

+
    +
  1. Docker +
    +

    For Windows users, enabling WSL2 back-end integration with Docker is recommended.

    +
    +
  2. +
  3. Visual Studio Code
  4. +
  5. Visual Studio Code Remote - Containers extension
  6. +
+

Using the dev container

+
    +
  1. +

    After you have cloned this repo locally, open the repo folder in VSCode. VSCode will detect the presence of this .devcontainer subfolder and will prompt you to reopen the project in a container.

    +

    Alternatively, you can open the command palette and use the Remote-Containers: Reopen in Container command.

    +
  2. +
  3. +

    Once the container is loaded, open an integrated terminal in VSCode and you can start running the demo instructions.

    +
  4. +
+
+

⚠ If running via Docker Desktop for Windows

+

Note that the mounted workspace files appear owned by root in the dev container, which will cause git commands to fail with a fatal: detected dubious ownership in a repository error due to safe.directory checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the vscode user:

+
sudo chown -R vscode:vscode /workspace/copacetic
+
+

Personalizing user settings in a dev container

+

VSCode supports applying your user settings, such as your .gitconfig, to a dev container through the use of dotfiles repositories. This can be done through your own VSCode settings.json file without changing the dev container image or configuration.

+

Tests

+

Once you can successfully make the project, any code contributions should also successfully:

+
    +
  • Pass unit tests via make test.
  • +
  • Lint cleanly via make lint.
  • +
+

Pull requests will also be expected to pass the PR functional tests specified by .github/workflows/build.yml.

+

Pull Requests

+

If you'd like to start contributing code to the project, you can search for issues with the good first issue label. Other kinds of PR contributions we would look for include:

+
    +
  • Fixes for bugs and other correctness issues.
  • +
  • Docs and other content improvements (e.g. samples).
  • +
  • Extensions to support parsing new scanning report formats.
  • +
  • Extensions to support patching images based on new distros or using new package managers.
  • +
+

For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:

+
    +
  • Architectural changes (e.g. breaking interfaces or violations of this project's design tenets).
  • +
  • Unsolicited features that significantly expand the functional scope of the tool.
  • +
+

Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the Angular commit message format for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs.

+

We suggest:

+
    +
  • Use the standard header format of "<type>: <short summary>" where the <type> is one of the following: +
      +
    • build: Changes that affect the build system or external dependencies
    • +
    • ci: Changes to the GitHub workflows and configurations
    • +
    • docs: Documentation only changes
    • +
    • feat: A new feature
    • +
    • fix: A bug fix
    • +
    • perf: A code change that improves performance
    • +
    • refactor: A code change that neither fixes a bug nor adds a feature
    • +
    • test: Adding missing tests or correcting existing tests
    • +
    +
  • +
  • Use a concise, imperative description of the changes included in the <short summary> of the header, the body of the PR, and generally in your commit messages.
  • +
  • Use GitHub keywords in the footer of your PR description, such as closes to automatically close issues the PR intends to address.
  • +
+

Developer Certificate of Origin (DCO)

+

The Developer Certificate of Origin (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the full text of the DCO, reformatted for readability:

+
+

By making a contribution to this project, I certify that:

+

(a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license -indicated in the file; or

(b) The contribution is based upon previous work that, to the best +indicated in the file; or

+

(b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated -in the file; or

(c) The contribution was provided directly to me by some other +in the file; or

+

(c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified -it.

(d) I understand and agree that this project and the contribution +it.

+

(d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with -this project or the open source license(s) involved.

Contributors sign-off that they adhere to these requirements by adding a Signed-off-by line to commit messages.

This is my commit message

Signed-off-by: Random J Developer <random@developer.example.org>

Git even has a -s command line option to append this automatically to your commit message:

git commit -s -m 'This is my commit message'

Pull requests that do not contain a valid Signed-off-by line cannot be merged.

I didn't sign my commit, now what?

No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:

git switch <branch-name>
git commit --amend --no-edit --signoff
git push --force-with-lease <remote-name> <branch-name>

Code of Conduct

This project has adopted the Contributor Covenant Code of Conduct.

- - +this project or the open source license(s) involved.

+
+

Contributors sign-off that they adhere to these requirements by adding a Signed-off-by line to commit messages.

+
This is my commit message

Signed-off-by: Random J Developer <random@developer.example.org>
+

Git even has a -s command line option to append this automatically to your commit message:

+
git commit -s -m 'This is my commit message'
+

Pull requests that do not contain a valid Signed-off-by line cannot be merged.

+

I didn't sign my commit, now what?

+

No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:

+
git switch <branch-name>
git commit --amend --no-edit --signoff
git push --force-with-lease <remote-name> <branch-name>
+

Code of Conduct

+

This project has adopted the Contributor Covenant Code of Conduct.

\ No newline at end of file diff --git a/website/v0.3.x/design.html b/website/v0.3.x/design.html index 6c712bca..f2c9c58c 100644 --- a/website/v0.3.x/design.html +++ b/website/v0.3.x/design.html @@ -1,19 +1,84 @@ - + - -Design | Copacetic + +Design | Copacetic - - - + + + -
-
Version: v0.3.x

Design

Design Tenets

  • Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update. This is a raison d’etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.

  • Copa is intended to work with the existing ecosystem of container images. The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.

  • Copa is intended to allow parties other than the image authors to address container vulnerabilities. Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.

  • Copa is intended to do one thing well and be composable with other tools and processes. Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane.

Design Reasoning

The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental crane rebase:

  • Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an apt install during image creation will overwrite the dpkg status file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners.

    To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.

  • Rebasing also requires that the user knows a priori what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets.

    While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose.

    The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well.

Architecture

The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:

  • The data schema of various vulnerability scanners producing the input vulnerability report.
  • The state management of various package managers and process for applying patches appropriately through them.

Effectively, copa patch can be considered a command that bridges an extensible Parse action with an extensible Apply action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:

type UpdatePackage struct {
Name string
Version string
}

type UpdateManifest struct {
OSType string
OSVersion string
Arch string
Updates []UpdatePackage
}

type ScanReportParser interface {
Parse(reportPath string) (*UpdateManifest, error)
}

type PackageManager interface {
Apply(imagePath string, report *UpdateManifest) error
}

Implementation

copa is a pseudo-frontend to buildkit implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of LLB graphs for buildkit to execute:

  1. Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.
    • Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions.
  2. Actions to fetch and deploy tools that can be injected into the target image to perform the patching.
    • In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches.
    • In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image.
    • Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well.
  3. Actions to deploy the required patch packages to the target image.
    • copa integrates with buildkit at the API level because it uses the diff and merge graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.

Tradeoffs

  • The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:
    • By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa.
    • copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this.
  • No support for windows containers given the dependency on buildkit.
- - +
Version: v0.3.x

Design

Design Tenets

+
    +
  • +

    Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update. This is a raison d’etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.

    +
  • +
  • +

    Copa is intended to work with the existing ecosystem of container images. The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.

    +
  • +
  • +

    Copa is intended to allow parties other than the image authors to address container vulnerabilities. Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.

    +
  • +
  • +

    Copa is intended to do one thing well and be composable with other tools and processes. Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane.

    +
  • +
+

Design Reasoning

+

The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental crane rebase:

+
    +
  • +

    Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an apt install during image creation will overwrite the dpkg status file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners.

    +

    To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.

    +
  • +
  • +

    Rebasing also requires that the user knows a priori what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets.

    +

    While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose.

    +

    The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well.

    +
  • +
+

Architecture

+ +

The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:

+
    +
  • The data schema of various vulnerability scanners producing the input vulnerability report.
  • +
  • The state management of various package managers and process for applying patches appropriately through them.
  • +
+

Effectively, copa patch can be considered a command that bridges an extensible Parse action with an extensible Apply action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:

+
type UpdatePackage struct {
Name string
Version string
}

type UpdateManifest struct {
OSType string
OSVersion string
Arch string
Updates []UpdatePackage
}

type ScanReportParser interface {
Parse(reportPath string) (*UpdateManifest, error)
}

type PackageManager interface {
Apply(imagePath string, report *UpdateManifest) error
}
+

Implementation

+ +

copa is a pseudo-frontend to buildkit implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of LLB graphs for buildkit to execute:

+
    +
  1. Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image. +
      +
    • Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions.
    • +
    +
  2. +
  3. Actions to fetch and deploy tools that can be injected into the target image to perform the patching. +
      +
    • In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches.
    • +
    • In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image.
    • +
    • Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well.
    • +
    +
  4. +
  5. Actions to deploy the required patch packages to the target image. +
      +
    • copa integrates with buildkit at the API level because it uses the diff and merge graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.
    • +
    +
  6. +
+

Tradeoffs

+
    +
  • The core architectural choice of relying on packages as the unit of patching creates a couple of constraints: +
      +
    • By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa.
    • +
    • copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this.
    • +
    +
  • +
  • No support for windows containers given the dependency on buildkit.
  • +
\ No newline at end of file diff --git a/website/v0.3.x/faq.html b/website/v0.3.x/faq.html index a5251ee7..3c342fc4 100644 --- a/website/v0.3.x/faq.html +++ b/website/v0.3.x/faq.html @@ -1,19 +1,20 @@ - + - -FAQ | Copacetic + +FAQ | Copacetic - - - + + + -
-
Version: v0.3.x

FAQ

What kind of vulnerabilities can Copa patch?

Copa is capable of patching "OS level" vulnerabilities. This includes packages (like openssl) in the image that are managed by a package manager such as apt or yum. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see below for more details).

What kind of vulnerabilities can Copa not patch?

Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the golang.org/x/net module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level.

To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like http://archive.ubuntu.com/ubuntu/ for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications.

- - +
Version: v0.3.x

FAQ

What kind of vulnerabilities can Copa patch?

+

Copa is capable of patching "OS level" vulnerabilities. This includes packages (like openssl) in the image that are managed by a package manager such as apt or yum. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see below for more details).

+

What kind of vulnerabilities can Copa not patch?

+

Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the golang.org/x/net module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level.

+

To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like http://archive.ubuntu.com/ubuntu/ for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications.

\ No newline at end of file diff --git a/website/v0.3.x/installation.html b/website/v0.3.x/installation.html index ddb9c26a..c57b3f9c 100644 --- a/website/v0.3.x/installation.html +++ b/website/v0.3.x/installation.html @@ -1,19 +1,23 @@ - + - -Installation | Copacetic + +Installation | Copacetic - - - + + + -
-
Version: v0.3.x

Installation

Homebrew

On macOS and Linux, copa can be installed via Homebrew:

brew install copa

GitHub

You can download the latest and previous versions of copa from the GitHub releases page.

Development Setup

The following instructions are for Ubuntu 22.04 with the dependency versions supported as part of the dev container environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead.

git clone https://github.com/project-copacetic/copacetic
cd copacetic
make
# OPTIONAL: install copa to a pathed folder
sudo mv dist/linux_amd64/release/copa /usr/local/bin/
- - +
Version: v0.3.x

Installation

Homebrew

+

On macOS and Linux, copa can be installed via Homebrew:

+
brew install copa
+

GitHub

+

You can download the latest and previous versions of copa from the GitHub releases page.

+

Development Setup

+

The following instructions are for Ubuntu 22.04 with the dependency versions supported as part of the dev container environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead.

+
git clone https://github.com/project-copacetic/copacetic
cd copacetic
make
# OPTIONAL: install copa to a pathed folder
sudo mv dist/linux_amd64/release/copa /usr/local/bin/
\ No newline at end of file diff --git a/website/v0.3.x/quick-start.html b/website/v0.3.x/quick-start.html index ed0e9122..11785874 100644 --- a/website/v0.3.x/quick-start.html +++ b/website/v0.3.x/quick-start.html @@ -1,21 +1,59 @@ - + - -Quick Start | Copacetic + +Quick Start | Copacetic - - - + + + -
-
Version: v0.3.x

Quick Start

This sample illustrates how to patch containers using vulnerability reports with copa.

Prerequisites

Sample Steps

  1. Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:

    trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6

    You can also see the existing patchable vulnerabilities in table form on the shell with:

    trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6

  2. Patch the image using the Trivy report. You will need to start buildkitd if it is not already running:

    sudo buildkitd &
    sudo copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched

    Alternatively, you can run buildkitd in a container, which allows copa to be run without root access to the local buildkit socket:

    export BUILDKIT_VERSION=v0.11.4
    export BUILDKIT_PORT=8888
    docker run \
    --detach \
    --rm \
    --privileged \
    -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \
    --name buildkitd \
    --entrypoint buildkitd \
    "moby/buildkit:$BUILDKIT_VERSION" \
    --addr tcp://0.0.0.0:$BUILDKIT_PORT
    copa patch \
    -i docker.io/library/nginx:1.21.6 \
    -r nginx.1.21.6.json \
    -t 1.21.6-patched \
    -a tcp://0.0.0.0:$BUILDKIT_PORT

    In either case, copa is non-destructive and exports a new image with the specified 1.21.6-patched label to the local Docker daemon.

    NOTE: if you're running this sample against an image from a private registry instead, +

    Version: v0.3.x

    Quick Start

    This sample illustrates how to patch containers using vulnerability reports with copa.

    +

    Prerequisites

    + +

    Sample Steps

    +
      +
    1. +

      Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:

      +
      trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6
      +

      You can also see the existing patchable vulnerabilities in table form on the shell with:

      +
      trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6

      +
    2. +
    3. +

      Patch the image using the Trivy report. You will need to start buildkitd if it is not already running:

      +
      sudo buildkitd &
      sudo copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched
      +

      Alternatively, you can run buildkitd in a container, which allows copa to be run without root access to the local buildkit socket:

      +
      export BUILDKIT_VERSION=v0.11.4
      export BUILDKIT_PORT=8888
      docker run \
      --detach \
      --rm \
      --privileged \
      -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \
      --name buildkitd \
      --entrypoint buildkitd \
      "moby/buildkit:$BUILDKIT_VERSION" \
      --addr tcp://0.0.0.0:$BUILDKIT_PORT
      copa patch \
      -i docker.io/library/nginx:1.21.6 \
      -r nginx.1.21.6.json \
      -t 1.21.6-patched \
      -a tcp://0.0.0.0:$BUILDKIT_PORT
      +

      In either case, copa is non-destructive and exports a new image with the specified 1.21.6-patched label to the local Docker daemon.

      +
      +

      NOTE: if you're running this sample against an image from a private registry instead, ensure that the credentials are configured in the default Docker config.json before running copa patch, -for example, via sudo docker login -u <user> -p <password> <registry>.

    4. Scan the patched image and verify that the vulnerabilities have been patched:

      trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched

      You can also inspect the structure of the patched image with docker history to see the new patch layer appended to the image:

      $ docker history docker.io/library/nginx:1.21.6-patched
      IMAGE CREATED CREATED BY SIZE COMMENT
      a372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins… 26.1MB buildkit.exporter.image.v0
      <missing> 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0
      <missing> 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0
      <missing> 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0
      <missing> 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0
      <missing> 3 months ago COPY 30-tune-worker-processes.sh /docker-ent… 4.61kB buildkit.dockerfile.v0
      <missing> 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent… 1.04kB buildkit.dockerfile.v0
      <missing> 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker… 1.96kB buildkit.dockerfile.v0
      <missing> 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0
      <missing> 3 months ago RUN /bin/sh -c set -x && addgroup --syst… 61.1MB buildkit.dockerfile.v0
      <missing> 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0
      <missing> 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0
      <missing> 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0
      <missing> 3 months ago LABEL maintainer=NGINX Docker Maintainers <d… 0B buildkit.dockerfile.v0
      <missing> 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
      <missing> 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55… 80.4MB
    5. Run the container to verify that the image has no regressions:

      $ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched
      /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
      /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
      /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
      10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
      10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
      /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
      /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
      /docker-entrypoint.sh: Configuration complete; ready for start up
      2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method
      2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2
      2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
      2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2
      2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
      2022/05/16 18:00:17 [notice] 1#1: start worker processes
      2022/05/16 18:00:17 [notice] 1#1: start worker process 31
      2022/05/16 18:00:17 [notice] 1#1: start worker process 32
      2022/05/16 18:00:17 [notice] 1#1: start worker process 33
      2022/05/16 18:00:17 [notice] 1#1: start worker process 34
      2022/05/16 18:00:17 [notice] 1#1: start worker process 35
      2022/05/16 18:00:17 [notice] 1#1: start worker process 36
      2022/05/16 18:00:17 [notice] 1#1: start worker process 37
      2022/05/16 18:00:17 [notice] 1#1: start worker process 38
      2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received

      You can stop the container by opening a new shell instance and running: docker stop nginx-test

    - - +for example, via sudo docker login -u <user> -p <password> <registry>.

    +
    +
  3. +
  4. +

    Scan the patched image and verify that the vulnerabilities have been patched:

    +
    trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched
    +

    You can also inspect the structure of the patched image with docker history to see the new patch layer appended to the image:

    +
    $ docker history docker.io/library/nginx:1.21.6-patched
    IMAGE CREATED CREATED BY SIZE COMMENT
    a372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins… 26.1MB buildkit.exporter.image.v0
    <missing> 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0
    <missing> 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0
    <missing> 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0
    <missing> 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0
    <missing> 3 months ago COPY 30-tune-worker-processes.sh /docker-ent… 4.61kB buildkit.dockerfile.v0
    <missing> 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent… 1.04kB buildkit.dockerfile.v0
    <missing> 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker… 1.96kB buildkit.dockerfile.v0
    <missing> 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0
    <missing> 3 months ago RUN /bin/sh -c set -x && addgroup --syst… 61.1MB buildkit.dockerfile.v0
    <missing> 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0
    <missing> 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0
    <missing> 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0
    <missing> 3 months ago LABEL maintainer=NGINX Docker Maintainers <d… 0B buildkit.dockerfile.v0
    <missing> 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
    <missing> 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55… 80.4MB
    +
  5. +
  6. +

    Run the container to verify that the image has no regressions:

    +
    $ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched
    /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
    /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
    10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
    10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
    /docker-entrypoint.sh: Configuration complete; ready for start up
    2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method
    2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2
    2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
    2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2
    2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
    2022/05/16 18:00:17 [notice] 1#1: start worker processes
    2022/05/16 18:00:17 [notice] 1#1: start worker process 31
    2022/05/16 18:00:17 [notice] 1#1: start worker process 32
    2022/05/16 18:00:17 [notice] 1#1: start worker process 33
    2022/05/16 18:00:17 [notice] 1#1: start worker process 34
    2022/05/16 18:00:17 [notice] 1#1: start worker process 35
    2022/05/16 18:00:17 [notice] 1#1: start worker process 36
    2022/05/16 18:00:17 [notice] 1#1: start worker process 37
    2022/05/16 18:00:17 [notice] 1#1: start worker process 38
    2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received
    +

    You can stop the container by opening a new shell instance and running: docker stop nginx-test

    +
  7. +
\ No newline at end of file diff --git a/website/v0.4.x.html b/website/v0.4.x.html index 8a3aa42b..f4eaaefc 100644 --- a/website/v0.4.x.html +++ b/website/v0.4.x.html @@ -1,19 +1,59 @@ - + - -Introduction | Copacetic + +Introduction | Copacetic - - - + + + -
-
Version: v0.4.x

Project Copacetic: Directly patch container image vulnerabilities

copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.

Why?

We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between vulnerability disclosure and active exploitation continues to narrow, there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:

  • inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option
  • found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.

In addition to filling the operational gap not met by left-shift security practices and tools, the ability of copa to patch a container without requiring a rebuild of the container image provides other benefits:

  • Allows users other than the image publishers to also patch container images, such as DevSecOps engineers.
  • Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching.
  • Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild.
  • Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.

How?

The copa tool is an extensible engine that:

  1. Parses the needed update packages from the container image’s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats.
  2. Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers.
  3. Applies the resulting update binaries to the container image using buildkit.

This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:

  • Copa supports patching existing container images.
    • Devs don't need to build their images using specific tools or modify them in some way just to support container patching.
  • Copa works with the existing vulnerability scanning and mitigation ecosystems.
    • Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today.
    • Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports.
  • Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image.
    • For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.

For more details, refer to the copa design documentation.

- - +
Version: v0.4.x

Project Copacetic: Directly patch container image vulnerabilities

+

copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.

+

Why?

+

We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between vulnerability disclosure and active exploitation continues to narrow, there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:

+
    +
  • inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option
  • +
  • found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.
  • +
+ +

In addition to filling the operational gap not met by left-shift security practices and tools, the ability of copa to patch a container without requiring a rebuild of the container image provides other benefits:

+
    +
  • Allows users other than the image publishers to also patch container images, such as DevSecOps engineers.
  • +
  • Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching.
  • +
  • Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild.
  • +
  • Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.
  • +
+

How?

+

The copa tool is an extensible engine that:

+
    +
  1. Parses the needed update packages from the container image’s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats.
  2. +
  3. Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers.
  4. +
  5. Applies the resulting update binaries to the container image using buildkit.
  6. +
+ +

This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:

+
    +
  • Copa supports patching existing container images. +
      +
    • Devs don't need to build their images using specific tools or modify them in some way just to support container patching.
    • +
    +
  • +
  • Copa works with the existing vulnerability scanning and mitigation ecosystems. +
      +
    • Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today.
    • +
    • Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports.
    • +
    +
  • +
  • Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image. +
      +
    • For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.
    • +
    +
  • +
+

For more details, refer to the copa design documentation.

\ No newline at end of file diff --git a/website/v0.4.x/code-of-conduct.html b/website/v0.4.x/code-of-conduct.html index a447a856..ba68e306 100644 --- a/website/v0.4.x/code-of-conduct.html +++ b/website/v0.4.x/code-of-conduct.html @@ -1,68 +1,111 @@ - + - -Code of Conduct | Copacetic + +Code of Conduct | Copacetic - - - + + + -
-
Version: v0.4.x

Contributor Covenant Code of Conduct

Our Pledge

We as members, contributors, and leaders pledge to make participation in our +

Version: v0.4.x

Contributor Covenant Code of Conduct

+

Our Pledge

+

We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual -identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community.

Our Standards

Examples of behavior that contributes to a positive environment for our -community include:

  • Demonstrating empathy and kindness toward other people
  • Being respectful of differing opinions, viewpoints, and experiences
  • Giving and gracefully accepting constructive feedback
  • Accepting responsibility and apologizing to those affected by our mistakes, -and learning from the experience
  • Focusing on what is best not just for us as individuals, but for the overall -community

Examples of unacceptable behavior include:

  • The use of sexualized language or imagery, and sexual attention or advances of -any kind
  • Trolling, insulting or derogatory comments, and personal or political attacks
  • Public or private harassment
  • Publishing others' private information, such as a physical or email address, -without their explicit permission
  • Other conduct which could reasonably be considered inappropriate in a -professional setting

Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of +identity and orientation.

+

We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community.

+

Our Standards

+

Examples of behavior that contributes to a positive environment for our +community include:

+
    +
  • Demonstrating empathy and kindness toward other people
  • +
  • Being respectful of differing opinions, viewpoints, and experiences
  • +
  • Giving and gracefully accepting constructive feedback
  • +
  • Accepting responsibility and apologizing to those affected by our mistakes, +and learning from the experience
  • +
  • Focusing on what is best not just for us as individuals, but for the overall +community
  • +
+

Examples of unacceptable behavior include:

+
    +
  • The use of sexualized language or imagery, and sexual attention or advances of +any kind
  • +
  • Trolling, insulting or derogatory comments, and personal or political attacks
  • +
  • Public or private harassment
  • +
  • Publishing others' private information, such as a physical or email address, +without their explicit permission
  • +
  • Other conduct which could reasonably be considered inappropriate in a +professional setting
  • +
+

Enforcement Responsibilities

+

Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, -or harmful.

Community leaders have the right and responsibility to remove, edit, or reject +or harmful.

+

Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate.

Scope

This Code of Conduct applies within all community spaces, and also applies when +decisions when appropriate.

+

Scope

+

This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed -representative at an online or offline event.

Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be +representative at an online or offline event.

+

Enforcement

+

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at project-copacetic@googlegroups.com. -All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the -reporter of any incident.

Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct:

1. Correction

Community Impact: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community.

Consequence: A private, written warning from community leaders, providing +All complaints will be reviewed and investigated promptly and fairly.

+

All community leaders are obligated to respect the privacy and security of the +reporter of any incident.

+

Enforcement Guidelines

+

Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct:

+

1. Correction

+

Community Impact: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community.

+

Consequence: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested.

2. Warning

Community Impact: A violation through a single incident or series of -actions.

Consequence: A warning with consequences for continued behavior. No +behavior was inappropriate. A public apology may be requested.

+

2. Warning

+

Community Impact: A violation through a single incident or series of +actions.

+

Consequence: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent -ban.

3. Temporary Ban

Community Impact: A serious violation of community standards, including -sustained inappropriate behavior.

Consequence: A temporary ban from any sort of interaction or public +ban.

+

3. Temporary Ban

+

Community Impact: A serious violation of community standards, including +sustained inappropriate behavior.

+

Consequence: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban.

4. Permanent Ban

Community Impact: Demonstrating a pattern of violation of community +Violating these terms may lead to a permanent ban.

+

4. Permanent Ban

+

Community Impact: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals.

Consequence: A permanent ban from any sort of public interaction within the -community.

Attribution

This Code of Conduct is adapted from the Contributor Covenant, +individual, or aggression toward or disparagement of classes of individuals.

+

Consequence: A permanent ban from any sort of public interaction within the +community.

+

Attribution

+

This Code of Conduct is adapted from the Contributor Covenant, version 2.1, available at -https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

Community Impact Guidelines were inspired by -Mozilla's code of conduct enforcement ladder.

For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

+

Community Impact Guidelines were inspired by +Mozilla's code of conduct enforcement ladder.

+

For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations.

- - +https://www.contributor-covenant.org/translations.

\ No newline at end of file diff --git a/website/v0.4.x/contributing.html b/website/v0.4.x/contributing.html index 181893b4..f58ecab8 100644 --- a/website/v0.4.x/contributing.html +++ b/website/v0.4.x/contributing.html @@ -1,33 +1,127 @@ - + - -Contributing | Copacetic + +Contributing | Copacetic - - - + + + -
-
Version: v0.4.x

Contributing

Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.

Bi-Weekly Community Meeting

A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join here

Slack

To discuss issues with Copa, features, or development, you can join the #copa channel on the OCI Slack.

Contributing Issues

Before opening any new issues, please search our existing GitHub issues to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead.

When opening an issue, please select the most appropriate template for what you're contributing:

  • Bug Report: If you would like to report the project or tool behaving in unexpected ways.
  • Documentation Improvement: If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content.
  • Request: If you have a feature request, suggestion, or a even a design proposal to review.
  • Question: If you would like to ask the maintainers a question about the project.

Contributing Code

Getting Started

Follow the instructions to either:

For an overview of the project components, refer to the copa design document.

Visual Studio Code Development Container

VSCode supports development in a containerized environment through its Remote - Container extension. This folder provides a development container which encapsulates the dependencies specified in the instructions to build and run copa.

Prerequisites

  1. Docker

    For Windows users, enabling WSL2 back-end integration with Docker is recommended.

  2. Visual Studio Code
  3. Visual Studio Code Remote - Containers extension

⚠ If running via Docker Desktop for Windows

Note that the mounted workspace files appear owned by root in the dev container, which will cause git commands to fail with a fatal: detected dubious ownership in a repository error due to safe.directory checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the vscode user:

sudo chown -R vscode:vscode /workspace/copacetic

Personalizing user settings in a dev container

VSCode supports applying your user settings, such as your .gitconfig, to a dev container through the use of dotfiles repositories. This can be done through your own VSCode settings.json file without changing the dev container image or configuration.

Tests

Once you can successfully make the project, any code contributions should also successfully:

  • Pass unit tests via make test.
  • Lint cleanly via make lint.

Pull requests will also be expected to pass the PR functional tests specified by .github/workflows/build.yml.

Pull Requests

If you'd like to start contributing code to the project, you can search for issues with the good first issue label. Other kinds of PR contributions we would look for include:

  • Fixes for bugs and other correctness issues.
  • Docs and other content improvements (e.g. samples).
  • Extensions to support parsing new scanning report formats.
  • Extensions to support patching images based on new distros or using new package managers.

For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:

  • Architectural changes (e.g. breaking interfaces or violations of this project's design tenets).
  • Unsolicited features that significantly expand the functional scope of the tool.

Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the Angular commit message format for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs.

We suggest:

  • Use the standard header format of "<type>: <short summary>" where the <type> is one of the following:
    • build: Changes that affect the build system or external dependencies
    • ci: Changes to the GitHub workflows and configurations
    • docs: Documentation only changes
    • feat: A new feature
    • fix: A bug fix
    • perf: A code change that improves performance
    • refactor: A code change that neither fixes a bug nor adds a feature
    • test: Adding missing tests or correcting existing tests
  • Use a concise, imperative description of the changes included in the <short summary> of the header, the body of the PR, and generally in your commit messages.
  • Use GitHub keywords in the footer of your PR description, such as closes to automatically close issues the PR intends to address.

Developer Certificate of Origin (DCO)

The Developer Certificate of Origin (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the full text of the DCO, reformatted for readability:

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I +

Version: v0.4.x

Contributing

Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.

+

Bi-Weekly Community Meeting

+

A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join here

+

Slack

+

To discuss issues with Copa, features, or development, you can join the #copa channel on the OCI Slack.

+

Contributing Issues

+

Before opening any new issues, please search our existing GitHub issues to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead.

+

When opening an issue, please select the most appropriate template for what you're contributing:

+
    +
  • Bug Report: If you would like to report the project or tool behaving in unexpected ways.
  • +
  • Documentation Improvement: If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content.
  • +
  • Request: If you have a feature request, suggestion, or a even a design proposal to review.
  • +
  • Question: If you would like to ask the maintainers a question about the project.
  • +
+

Contributing Code

+

Getting Started

+

Follow the instructions to either:

+ +

For an overview of the project components, refer to the copa design document.

+

Visual Studio Code Development Container

+

VSCode supports development in a containerized environment through its Remote - Container extension. This folder provides a development container which encapsulates the dependencies specified in the instructions to build and run copa.

+

Prerequisites

+
    +
  1. Docker +
    +

    For Windows users, enabling WSL2 back-end integration with Docker is recommended.

    +
    +
  2. +
  3. Visual Studio Code
  4. +
  5. Visual Studio Code Remote - Containers extension
  6. +
+
+

⚠ If running via Docker Desktop for Windows

+

Note that the mounted workspace files appear owned by root in the dev container, which will cause git commands to fail with a fatal: detected dubious ownership in a repository error due to safe.directory checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the vscode user:

+
sudo chown -R vscode:vscode /workspace/copacetic
+
+

Personalizing user settings in a dev container

+

VSCode supports applying your user settings, such as your .gitconfig, to a dev container through the use of dotfiles repositories. This can be done through your own VSCode settings.json file without changing the dev container image or configuration.

+

Tests

+

Once you can successfully make the project, any code contributions should also successfully:

+
    +
  • Pass unit tests via make test.
  • +
  • Lint cleanly via make lint.
  • +
+

Pull requests will also be expected to pass the PR functional tests specified by .github/workflows/build.yml.

+

Pull Requests

+

If you'd like to start contributing code to the project, you can search for issues with the good first issue label. Other kinds of PR contributions we would look for include:

+
    +
  • Fixes for bugs and other correctness issues.
  • +
  • Docs and other content improvements (e.g. samples).
  • +
  • Extensions to support parsing new scanning report formats.
  • +
  • Extensions to support patching images based on new distros or using new package managers.
  • +
+

For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:

+
    +
  • Architectural changes (e.g. breaking interfaces or violations of this project's design tenets).
  • +
  • Unsolicited features that significantly expand the functional scope of the tool.
  • +
+

Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the Angular commit message format for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs.

+

We suggest:

+
    +
  • Use the standard header format of "<type>: <short summary>" where the <type> is one of the following: +
      +
    • build: Changes that affect the build system or external dependencies
    • +
    • ci: Changes to the GitHub workflows and configurations
    • +
    • docs: Documentation only changes
    • +
    • feat: A new feature
    • +
    • fix: A bug fix
    • +
    • perf: A code change that improves performance
    • +
    • refactor: A code change that neither fixes a bug nor adds a feature
    • +
    • test: Adding missing tests or correcting existing tests
    • +
    +
  • +
  • Use a concise, imperative description of the changes included in the <short summary> of the header, the body of the PR, and generally in your commit messages.
  • +
  • Use GitHub keywords in the footer of your PR description, such as closes to automatically close issues the PR intends to address.
  • +
+

Developer Certificate of Origin (DCO)

+

The Developer Certificate of Origin (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the full text of the DCO, reformatted for readability:

+
+

By making a contribution to this project, I certify that:

+

(a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license -indicated in the file; or

(b) The contribution is based upon previous work that, to the best +indicated in the file; or

+

(b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated -in the file; or

(c) The contribution was provided directly to me by some other +in the file; or

+

(c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified -it.

(d) I understand and agree that this project and the contribution +it.

+

(d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with -this project or the open source license(s) involved.

Contributors sign-off that they adhere to these requirements by adding a Signed-off-by line to commit messages.

This is my commit message

Signed-off-by: Random J Developer <random@developer.example.org>

Git even has a -s command line option to append this automatically to your commit message:

git commit -s -m 'This is my commit message'

Pull requests that do not contain a valid Signed-off-by line cannot be merged.

I didn't sign my commit, now what?

No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:

git switch <branch-name>
git commit --amend --no-edit --signoff
git push --force-with-lease <remote-name> <branch-name>

Code of Conduct

This project has adopted the Contributor Covenant Code of Conduct.

- - +this project or the open source license(s) involved.

+
+

Contributors sign-off that they adhere to these requirements by adding a Signed-off-by line to commit messages.

+
This is my commit message

Signed-off-by: Random J Developer <random@developer.example.org>
+

Git even has a -s command line option to append this automatically to your commit message:

+
git commit -s -m 'This is my commit message'
+

Pull requests that do not contain a valid Signed-off-by line cannot be merged.

+

I didn't sign my commit, now what?

+

No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:

+
git switch <branch-name>
git commit --amend --no-edit --signoff
git push --force-with-lease <remote-name> <branch-name>
+

Code of Conduct

+

This project has adopted the Contributor Covenant Code of Conduct.

\ No newline at end of file diff --git a/website/v0.4.x/design.html b/website/v0.4.x/design.html index 3150acb0..1b25d0b7 100644 --- a/website/v0.4.x/design.html +++ b/website/v0.4.x/design.html @@ -1,19 +1,84 @@ - + - -Design | Copacetic + +Design | Copacetic - - - + + + -
-
Version: v0.4.x

Design

Design Tenets

  • Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update. This is a raison d’etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.

  • Copa is intended to work with the existing ecosystem of container images. The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.

  • Copa is intended to allow parties other than the image authors to address container vulnerabilities. Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.

  • Copa is intended to do one thing well and be composable with other tools and processes. Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane.

Design Reasoning

The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental crane rebase:

  • Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an apt install during image creation will overwrite the dpkg status file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners.

    To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.

  • Rebasing also requires that the user knows a priori what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets.

    While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose.

    The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well.

Architecture

The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:

  • The data schema of various vulnerability scanners producing the input vulnerability report.
  • The state management of various package managers and process for applying patches appropriately through them.

Effectively, copa patch can be considered a command that bridges an extensible Parse action with an extensible Apply action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:

type UpdatePackage struct {
Name string
Version string
}

type UpdateManifest struct {
OSType string
OSVersion string
Arch string
Updates []UpdatePackage
}

type ScanReportParser interface {
Parse(reportPath string) (*UpdateManifest, error)
}

type PackageManager interface {
Apply(imagePath string, report *UpdateManifest) error
}

Implementation

copa is a pseudo-frontend to buildkit implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of LLB graphs for buildkit to execute:

  1. Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.
    • Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions.
  2. Actions to fetch and deploy tools that can be injected into the target image to perform the patching.
    • In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches.
    • In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image.
    • Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well.
  3. Actions to deploy the required patch packages to the target image.
    • copa integrates with buildkit at the API level because it uses the diff and merge graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.

Tradeoffs

  • The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:
    • By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa.
    • copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this.
  • No support for windows containers given the dependency on buildkit.
- - +
Version: v0.4.x

Design

Design Tenets

+
    +
  • +

    Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update. This is a raison d’etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.

    +
  • +
  • +

    Copa is intended to work with the existing ecosystem of container images. The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.

    +
  • +
  • +

    Copa is intended to allow parties other than the image authors to address container vulnerabilities. Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.

    +
  • +
  • +

    Copa is intended to do one thing well and be composable with other tools and processes. Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane.

    +
  • +
+

Design Reasoning

+

The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental crane rebase:

+
    +
  • +

    Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an apt install during image creation will overwrite the dpkg status file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners.

    +

    To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.

    +
  • +
  • +

    Rebasing also requires that the user knows a priori what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets.

    +

    While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose.

    +

    The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well.

    +
  • +
+

Architecture

+ +

The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:

+
    +
  • The data schema of various vulnerability scanners producing the input vulnerability report.
  • +
  • The state management of various package managers and process for applying patches appropriately through them.
  • +
+

Effectively, copa patch can be considered a command that bridges an extensible Parse action with an extensible Apply action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:

+
type UpdatePackage struct {
Name string
Version string
}

type UpdateManifest struct {
OSType string
OSVersion string
Arch string
Updates []UpdatePackage
}

type ScanReportParser interface {
Parse(reportPath string) (*UpdateManifest, error)
}

type PackageManager interface {
Apply(imagePath string, report *UpdateManifest) error
}
+

Implementation

+ +

copa is a pseudo-frontend to buildkit implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of LLB graphs for buildkit to execute:

+
    +
  1. Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image. +
      +
    • Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions.
    • +
    +
  2. +
  3. Actions to fetch and deploy tools that can be injected into the target image to perform the patching. +
      +
    • In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches.
    • +
    • In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image.
    • +
    • Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well.
    • +
    +
  4. +
  5. Actions to deploy the required patch packages to the target image. +
      +
    • copa integrates with buildkit at the API level because it uses the diff and merge graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.
    • +
    +
  6. +
+

Tradeoffs

+
    +
  • The core architectural choice of relying on packages as the unit of patching creates a couple of constraints: +
      +
    • By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa.
    • +
    • copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this.
    • +
    +
  • +
  • No support for windows containers given the dependency on buildkit.
  • +
\ No newline at end of file diff --git a/website/v0.4.x/faq.html b/website/v0.4.x/faq.html index f7c283e7..4820e4dd 100644 --- a/website/v0.4.x/faq.html +++ b/website/v0.4.x/faq.html @@ -1,19 +1,20 @@ - + - -FAQ | Copacetic + +FAQ | Copacetic - - - + + + -
-
Version: v0.4.x

FAQ

What kind of vulnerabilities can Copa patch?

Copa is capable of patching "OS level" vulnerabilities. This includes packages (like openssl) in the image that are managed by a package manager such as apt or yum. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see below for more details).

What kind of vulnerabilities can Copa not patch?

Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the golang.org/x/net module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level.

To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like http://archive.ubuntu.com/ubuntu/ for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications.

- - +
Version: v0.4.x

FAQ

What kind of vulnerabilities can Copa patch?

+

Copa is capable of patching "OS level" vulnerabilities. This includes packages (like openssl) in the image that are managed by a package manager such as apt or yum. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see below for more details).

+

What kind of vulnerabilities can Copa not patch?

+

Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the golang.org/x/net module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level.

+

To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like http://archive.ubuntu.com/ubuntu/ for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications.

\ No newline at end of file diff --git a/website/v0.4.x/github-action.html b/website/v0.4.x/github-action.html index db57ed44..889d8fad 100644 --- a/website/v0.4.x/github-action.html +++ b/website/v0.4.x/github-action.html @@ -1,19 +1,33 @@ - + - -Copa Github Action | Copacetic + +Copa Github Action | Copacetic - - - + + + -
-
Version: v0.4.x

Copa Github Action

Overview

The Copa Github Action allows you patch vulnerable containers in your workflows using Copa.

Inputs

image

Required The image reference to patch.

image-report

Required The trivy json vulnerability report of the image to patch.

patched-tag

Required The new patched image tag.

buildkit-version

Optional The buildkit version used in the action, default is latest.

copa-version

Optional The Copa version used in the action, default is latest.

Output

patched-image

Image reference of the resulting patched image.

Example Workflow

on: [push]

jobs:
test:
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
# provide relevant list of images to scan on each run
images: ['docker.io/library/nginx:1.21.6', 'docker.io/openpolicyagent/opa:0.46.0', 'docker.io/library/hello-world:latest']

steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@dedd61cf5d839122591f5027c89bf3ad27691d18

- name: Generate Trivy Report
uses: aquasecurity/trivy-action@69cbbc0cbbf6a2b0bab8dcf0e9f2d7ead08e87e4
with:
scan-type: 'image'
format: 'json'
output: 'report.json'
ignore-unfixed: true
vuln-type: 'os'
image-ref: ${{ matrix.images }}

- name: Check Vuln Count
id: vuln_count
run: |
report_file="report.json"
vuln_count=$(jq '.Results | length' "$report_file")
echo "vuln_count=$vuln_count" >> $GITHUB_OUTPUT

- name: Copa Action
if: steps.vuln_count.outputs.vuln_count != '0'
id: copa
uses: project-copacetic/copa-action@v1
with:
image: ${{ matrix.images }}
image-report: 'report.json'
patched-tag: 'patched'
buildkit-version: 'v0.11.6'
# optional, default is latest
copa-version: '0.4.1'

- name: Login to Docker Hub
if: steps.copa.conclusion == 'success'
id: login
uses: docker/login-action@b4bedf8053341df3b5a9f9e0f2cf4e79e27360c6
with:
username: 'user'
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Docker Push Patched Image
if: steps.login.conclusion == 'success'
run: |
docker push ${{ steps.copa.outputs.patched-image }}

- - +
Version: v0.4.x

Copa Github Action

Overview

+

The Copa Github Action allows you patch vulnerable containers in your workflows using Copa.

+

Inputs

+

image

+

Required The image reference to patch.

+

image-report

+

Required The trivy json vulnerability report of the image to patch.

+

patched-tag

+

Required The new patched image tag.

+

buildkit-version

+

Optional The buildkit version used in the action, default is latest.

+

copa-version

+

Optional The Copa version used in the action, default is latest.

+

Output

+

patched-image

+

Image reference of the resulting patched image.

+

Example Workflow

+
on: [push]

jobs:
test:
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
# provide relevant list of images to scan on each run
images: ['docker.io/library/nginx:1.21.6', 'docker.io/openpolicyagent/opa:0.46.0', 'docker.io/library/hello-world:latest']

steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@dedd61cf5d839122591f5027c89bf3ad27691d18

- name: Generate Trivy Report
uses: aquasecurity/trivy-action@69cbbc0cbbf6a2b0bab8dcf0e9f2d7ead08e87e4
with:
scan-type: 'image'
format: 'json'
output: 'report.json'
ignore-unfixed: true
vuln-type: 'os'
image-ref: ${{ matrix.images }}

- name: Check Vuln Count
id: vuln_count
run: |
report_file="report.json"
vuln_count=$(jq '.Results | length' "$report_file")
echo "vuln_count=$vuln_count" >> $GITHUB_OUTPUT

- name: Copa Action
if: steps.vuln_count.outputs.vuln_count != '0'
id: copa
uses: project-copacetic/copa-action@v1
with:
image: ${{ matrix.images }}
image-report: 'report.json'
patched-tag: 'patched'
buildkit-version: 'v0.11.6'
# optional, default is latest
copa-version: '0.4.1'

- name: Login to Docker Hub
if: steps.copa.conclusion == 'success'
id: login
uses: docker/login-action@b4bedf8053341df3b5a9f9e0f2cf4e79e27360c6
with:
username: 'user'
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Docker Push Patched Image
if: steps.login.conclusion == 'success'
run: |
docker push ${{ steps.copa.outputs.patched-image }}

\ No newline at end of file diff --git a/website/v0.4.x/installation.html b/website/v0.4.x/installation.html index 58bddd35..59512bab 100644 --- a/website/v0.4.x/installation.html +++ b/website/v0.4.x/installation.html @@ -1,19 +1,23 @@ - + - -Installation | Copacetic + +Installation | Copacetic - - - + + + -
-
Version: v0.4.x

Installation

Homebrew

On macOS and Linux, copa can be installed via Homebrew:

brew install copa

GitHub

You can download the latest and previous versions of copa from the GitHub releases page.

Development Setup

The following instructions are for Ubuntu 22.04 with the dependency versions supported as part of the dev container environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead.

git clone https://github.com/project-copacetic/copacetic
cd copacetic
make
# OPTIONAL: install copa to a pathed folder
sudo mv dist/linux_amd64/release/copa /usr/local/bin/
- - +
Version: v0.4.x

Installation

Homebrew

+

On macOS and Linux, copa can be installed via Homebrew:

+
brew install copa
+

GitHub

+

You can download the latest and previous versions of copa from the GitHub releases page.

+

Development Setup

+

The following instructions are for Ubuntu 22.04 with the dependency versions supported as part of the dev container environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead.

+
git clone https://github.com/project-copacetic/copacetic
cd copacetic
make
# OPTIONAL: install copa to a pathed folder
sudo mv dist/linux_amd64/release/copa /usr/local/bin/
\ No newline at end of file diff --git a/website/v0.4.x/quick-start.html b/website/v0.4.x/quick-start.html index 69f87af1..d146144c 100644 --- a/website/v0.4.x/quick-start.html +++ b/website/v0.4.x/quick-start.html @@ -1,22 +1,88 @@ - + - -Quick Start | Copacetic + +Quick Start | Copacetic - - - + + + -
-
Version: v0.4.x

Quick Start

This sample illustrates how to patch containers using vulnerability reports with copa.

Prerequisites

Sample Steps

  1. Download the target container to scan and patch:

    docker pull mcr.microsoft.com/oss/nginx/nginx:1.21.6
  2. Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:

    trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json mcr.microsoft.com/oss/nginx/nginx:1.21.6

    You can also see the existing patchable vulnerabilities in table form on the shell with:

    trivy image --vuln-type os --ignore-unfixed mcr.microsoft.com/oss/nginx/nginx:1.21.6

  3. To patch the image, use the Trivy report and specify a buildkit instance to connect to:

    By default copa will attempt to auto-connect to an instance in order:

    1. Default docker buildkit endpoint (requires at least docker v24.0 with containerd snapshotter support enabled)
    2. Currently selected buildx builder (see: docker buildx --help)
    3. buildkit daemon at the default address /run/buildkit/buildkitd.sock

    If an instance doesn't exist or that instance doesn't support all the features copa needs the next will be attempted. -You may need to specify a custom address using the --addr flag. Here are the supported formats:

    • unix:///path/to/buildkit.sock - Connect to buildkit over unix socket.
    • tcp://$BUILDKIT_ADDR:$PORT - Connec to buildkit over TCP. (not recommended for security reasons)
    • docker://<docker connection spec> - Connect to docker, currently only unix sockets are supported, e.g. docker://unix:///var/run/docker.sock (or just docker://).
    • docker-container://my-buildkit-container - Connect to a buildkitd running in a docker container.
    • buildx://my-builder - Connect to a buildx builder (or buildx:// for the currently selected builder). Note: only container-backed buildx instances are currently supported
    • nerdctl-container://my-container-name - Similar to docker-container but uses nerdctl.
    • podman-container://my-container-name - Similar to docker-container but uses podman.
    • ssh://myhost - Connect to a buildkit instance over SSH. Format of the host spec should mimic the SSH command.
    • kubepod://mypod - Connect to buildkit running in a Kubernetes pod. Can also specify kubectl context and pod namespace (kubepod://mypod?context=foo&namespace=notdefault).

    Buildkit Connection Examples

    Example: Connect using defaults:

    copa patch -i mcr.microsoft.com/oss/nginx/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched

    Example: Connect to buildx

    docker buildx create --name demo
    copa patch -i mcr.microsoft.com/oss/nginx/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr buildx://demo

    Example: Buildkit in a container

    export BUILDKIT_VERSION=v0.12.0
    docker run \
    --detach \
    --rm \
    --privileged \
    --name buildkitd \
    --entrypoint buildkitd \
    "moby/buildkit:$BUILDKIT_VERSION"

    copa patch -i mcr.microsoft.com/oss/nginx/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr docker-container://buildkitd

    Example: Buildkit over TCP

    export BUILDKIT_VERSION=v0.12.0
    export BUILDKIT_PORT=8888
    docker run \
    --detach \
    --rm \
    --privileged \
    -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \
    --name buildkitd \
    --entrypoint buildkitd \
    "moby/buildkit:$BUILDKIT_VERSION" \
    --addr tcp://0.0.0.0:$BUILDKIT_PORT
    copa patch \
    -i mcr.microsoft.com/oss/nginx/nginx:1.21.6 \
    -r nginx.1.21.6.json \
    -t 1.21.6-patched \
    -a tcp://0.0.0.0:$BUILDKIT_PORT

    In either case, copa is non-destructive and exports a new image with the specified 1.21.6-patched label to the local Docker daemon.

    NOTE: if you're running this sample against an image from a private registry instead, +

    Version: v0.4.x

    Quick Start

    This sample illustrates how to patch containers using vulnerability reports with copa.

    +

    Prerequisites

    + +

    Sample Steps

    +
      +
    1. +

      Download the target container to scan and patch:

      +
      docker pull mcr.microsoft.com/oss/nginx/nginx:1.21.6
      +
    2. +
    3. +

      Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:

      +
      trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json mcr.microsoft.com/oss/nginx/nginx:1.21.6
      +

      You can also see the existing patchable vulnerabilities in table form on the shell with:

      +
      trivy image --vuln-type os --ignore-unfixed mcr.microsoft.com/oss/nginx/nginx:1.21.6

      +
    4. +
    5. +

      To patch the image, use the Trivy report and specify a buildkit instance to connect to:

      +

      By default copa will attempt to auto-connect to an instance in order:

      +
        +
      1. Default docker buildkit endpoint (requires at least docker v24.0 with containerd snapshotter support enabled)
      2. +
      3. Currently selected buildx builder (see: docker buildx --help)
      4. +
      5. buildkit daemon at the default address /run/buildkit/buildkitd.sock
      6. +
      +

      If an instance doesn't exist or that instance doesn't support all the features copa needs the next will be attempted. +You may need to specify a custom address using the --addr flag. Here are the supported formats:

      +
        +
      • unix:///path/to/buildkit.sock - Connect to buildkit over unix socket.
      • +
      • tcp://$BUILDKIT_ADDR:$PORT - Connec to buildkit over TCP. (not recommended for security reasons)
      • +
      • docker://<docker connection spec> - Connect to docker, currently only unix sockets are supported, e.g. docker://unix:///var/run/docker.sock (or just docker://).
      • +
      • docker-container://my-buildkit-container - Connect to a buildkitd running in a docker container.
      • +
      • buildx://my-builder - Connect to a buildx builder (or buildx:// for the currently selected builder). Note: only container-backed buildx instances are currently supported
      • +
      • nerdctl-container://my-container-name - Similar to docker-container but uses nerdctl.
      • +
      • podman-container://my-container-name - Similar to docker-container but uses podman.
      • +
      • ssh://myhost - Connect to a buildkit instance over SSH. Format of the host spec should mimic the SSH command.
      • +
      • kubepod://mypod - Connect to buildkit running in a Kubernetes pod. Can also specify kubectl context and pod namespace (kubepod://mypod?context=foo&namespace=notdefault).
      • +
      +

      Buildkit Connection Examples

      +

      Example: Connect using defaults:

      +
      copa patch -i mcr.microsoft.com/oss/nginx/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched
      +

      Example: Connect to buildx

      +
      docker buildx create --name demo
      copa patch -i mcr.microsoft.com/oss/nginx/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr buildx://demo
      +

      Example: Buildkit in a container

      +
      export BUILDKIT_VERSION=v0.12.0
      docker run \
      --detach \
      --rm \
      --privileged \
      --name buildkitd \
      --entrypoint buildkitd \
      "moby/buildkit:$BUILDKIT_VERSION"

      copa patch -i mcr.microsoft.com/oss/nginx/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr docker-container://buildkitd
      +

      Example: Buildkit over TCP

      +
      export BUILDKIT_VERSION=v0.12.0
      export BUILDKIT_PORT=8888
      docker run \
      --detach \
      --rm \
      --privileged \
      -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \
      --name buildkitd \
      --entrypoint buildkitd \
      "moby/buildkit:$BUILDKIT_VERSION" \
      --addr tcp://0.0.0.0:$BUILDKIT_PORT
      copa patch \
      -i mcr.microsoft.com/oss/nginx/nginx:1.21.6 \
      -r nginx.1.21.6.json \
      -t 1.21.6-patched \
      -a tcp://0.0.0.0:$BUILDKIT_PORT
      +

      In either case, copa is non-destructive and exports a new image with the specified 1.21.6-patched label to the local Docker daemon.

      +
      +

      NOTE: if you're running this sample against an image from a private registry instead, ensure that the credentials are configured in the default Docker config.json before running copa patch, -for example, via sudo docker login -u <user> -p <password> <registry>.

    6. Scan the patched image and verify that the vulnerabilities have been patched:

      trivy image --vuln-type os --ignore-unfixed mcr.microsoft.com/oss/nginx/nginx:1.21.6-patched

      You can also inspect the structure of the patched image with docker history to see the new patch layer appended to the image:

      $ docker history mcr.microsoft.com/oss/nginx/nginx:1.21.6-patched
      IMAGE CREATED CREATED BY SIZE COMMENT
      a372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins… 26.1MB buildkit.exporter.image.v0
      <missing> 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0
      <missing> 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0
      <missing> 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0
      <missing> 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0
      <missing> 3 months ago COPY 30-tune-worker-processes.sh /docker-ent… 4.61kB buildkit.dockerfile.v0
      <missing> 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent… 1.04kB buildkit.dockerfile.v0
      <missing> 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker… 1.96kB buildkit.dockerfile.v0
      <missing> 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0
      <missing> 3 months ago RUN /bin/sh -c set -x && addgroup --syst… 61.1MB buildkit.dockerfile.v0
      <missing> 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0
      <missing> 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0
      <missing> 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0
      <missing> 3 months ago LABEL maintainer=NGINX Docker Maintainers <d… 0B buildkit.dockerfile.v0
      <missing> 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
      <missing> 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55… 80.4MB
    7. Run the container to verify that the image has no regressions:

      $ docker run -it --rm --name nginx-test mcr.microsoft.com/oss/nginx/nginx:1.21.6-patched
      /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
      /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
      /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
      10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
      10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
      /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
      /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
      /docker-entrypoint.sh: Configuration complete; ready for start up
      2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method
      2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2
      2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
      2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2
      2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
      2022/05/16 18:00:17 [notice] 1#1: start worker processes
      2022/05/16 18:00:17 [notice] 1#1: start worker process 31
      2022/05/16 18:00:17 [notice] 1#1: start worker process 32
      2022/05/16 18:00:17 [notice] 1#1: start worker process 33
      2022/05/16 18:00:17 [notice] 1#1: start worker process 34
      2022/05/16 18:00:17 [notice] 1#1: start worker process 35
      2022/05/16 18:00:17 [notice] 1#1: start worker process 36
      2022/05/16 18:00:17 [notice] 1#1: start worker process 37
      2022/05/16 18:00:17 [notice] 1#1: start worker process 38
      2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received

      You can stop the container by opening a new shell instance and running: docker stop nginx-test

    - - +for example, via sudo docker login -u <user> -p <password> <registry>.

    +
    +
  4. +
  5. +

    Scan the patched image and verify that the vulnerabilities have been patched:

    +
    trivy image --vuln-type os --ignore-unfixed mcr.microsoft.com/oss/nginx/nginx:1.21.6-patched
    +

    You can also inspect the structure of the patched image with docker history to see the new patch layer appended to the image:

    +
    $ docker history mcr.microsoft.com/oss/nginx/nginx:1.21.6-patched
    IMAGE CREATED CREATED BY SIZE COMMENT
    a372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins… 26.1MB buildkit.exporter.image.v0
    <missing> 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0
    <missing> 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0
    <missing> 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0
    <missing> 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0
    <missing> 3 months ago COPY 30-tune-worker-processes.sh /docker-ent… 4.61kB buildkit.dockerfile.v0
    <missing> 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent… 1.04kB buildkit.dockerfile.v0
    <missing> 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker… 1.96kB buildkit.dockerfile.v0
    <missing> 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0
    <missing> 3 months ago RUN /bin/sh -c set -x && addgroup --syst… 61.1MB buildkit.dockerfile.v0
    <missing> 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0
    <missing> 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0
    <missing> 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0
    <missing> 3 months ago LABEL maintainer=NGINX Docker Maintainers <d… 0B buildkit.dockerfile.v0
    <missing> 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
    <missing> 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55… 80.4MB
    +
  6. +
  7. +

    Run the container to verify that the image has no regressions:

    +
    $ docker run -it --rm --name nginx-test mcr.microsoft.com/oss/nginx/nginx:1.21.6-patched
    /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
    /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
    10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
    10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
    /docker-entrypoint.sh: Configuration complete; ready for start up
    2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method
    2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2
    2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
    2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2
    2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
    2022/05/16 18:00:17 [notice] 1#1: start worker processes
    2022/05/16 18:00:17 [notice] 1#1: start worker process 31
    2022/05/16 18:00:17 [notice] 1#1: start worker process 32
    2022/05/16 18:00:17 [notice] 1#1: start worker process 33
    2022/05/16 18:00:17 [notice] 1#1: start worker process 34
    2022/05/16 18:00:17 [notice] 1#1: start worker process 35
    2022/05/16 18:00:17 [notice] 1#1: start worker process 36
    2022/05/16 18:00:17 [notice] 1#1: start worker process 37
    2022/05/16 18:00:17 [notice] 1#1: start worker process 38
    2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received
    +

    You can stop the container by opening a new shell instance and running: docker stop nginx-test

    +
  8. +
\ No newline at end of file diff --git a/website/v0.4.x/release.html b/website/v0.4.x/release.html index 38f39b7d..16738333 100644 --- a/website/v0.4.x/release.html +++ b/website/v0.4.x/release.html @@ -1,19 +1,24 @@ - + - -Release Process | Copacetic + +Release Process | Copacetic - - - + + + -
-
Version: v0.4.x

Release Process

Overview

The release process for Copacetic uses GoReleaser.

Once you are ready to cut a new release, checkout the release branch and tag it with the respective version.

```
git checkout <BRANCH NAME>
git pull origin <BRANCH NAME>
git tag -a <NEW VERSION> -m '<NEW VERSION>'
git push origin <NEW VERSION>
```

Publishing

  1. GoReleaser will create a new release, review and edit it at https://github.com/project-copacetic/copacetic/releases
  2. Review the respective copa-action image at: https://github.com/orgs/project-copacetic/packages/container/package/copa-action
- - +
Version: v0.4.x

Release Process

Overview

+

The release process for Copacetic uses GoReleaser.

+

Once you are ready to cut a new release, checkout the release branch and tag it with the respective version.

+
git checkout <BRANCH NAME>
git pull origin <BRANCH NAME>
git tag -a <NEW VERSION> -m '<NEW VERSION>'
git push origin <NEW VERSION>
+

Publishing

+
    +
  1. GoReleaser will create a new release, review and edit it at https://github.com/project-copacetic/copacetic/releases
  2. +
  3. Review the respective copa-action image at: https://github.com/orgs/project-copacetic/packages/container/package/copa-action
  4. +
\ No newline at end of file diff --git a/website/v0.4.x/troubleshooting.html b/website/v0.4.x/troubleshooting.html index bb65188e..9583a8fa 100644 --- a/website/v0.4.x/troubleshooting.html +++ b/website/v0.4.x/troubleshooting.html @@ -1,19 +1,30 @@ - + - -Troubleshooting | Copacetic + +Troubleshooting | Copacetic - - - + + + -
-
Version: v0.4.x

Troubleshooting

Filtering Vulnerabilities

You might want to filter/ignore some of the vulnerabilities while patching. To do so, you need to first filter those undesired vulnerabilities from your scanner output.

For Trivy, vulnerabilities can be filtered by the following 2 ways:

Rego Policy

An example rego file which demonstrates how to ignore certain Vulnerability IDs or Package Names:

$ cat trivy_ignore.rego

package trivy

import data.lib.trivy

default ignore = false


# Ignore the following Vulnerability IDs
ignore_vulnerability_ids := {
"CVE-2018-14618"
}
# Ignore the following Package Names
ignore_pkgs := {"bash", "vim"}


# For ignoring vulnID
ignore {
input.VulnerabilityID == ignore_vulnerability_ids[_]
}
# For ignoring pkgName
ignore {
input.PkgName == ignore_pkgs[_]
}

After adding the above rego file, run the image scan with the --ignore-policy flag followed by the file name to ignore them while scanning:

trivy image --ignore-policy trivy_ignore.rego ruby:2.4.0

In the above example, the vulnerability "CVE-2018-14618" and the packages "bash" & "vim" are ignored while scanning, and hence patching the image.

Ignore File

Use a .trivyignore file to list all the vulnerabilities you want to ignore.

Example:

$ cat .trivyignore

# Accept the risk
CVE-2018-14618

In the above example, the vulnerability CVE-2018-14618 is ignored while scanning, and hence while patching the image.

For a more detailed explanation on how to ignore certain vulnerabilities with Trivy, please refer to the official documentation here.

- - +
Version: v0.4.x

Troubleshooting

Filtering Vulnerabilities

+

You might want to filter/ignore some of the vulnerabilities while patching. To do so, you need to first filter those undesired vulnerabilities from your scanner output.

+

For Trivy, vulnerabilities can be filtered by the following 2 ways:

+

Rego Policy

+

An example rego file which demonstrates how to ignore certain Vulnerability IDs or Package Names:

+
$ cat trivy_ignore.rego

package trivy

import data.lib.trivy

default ignore = false


# Ignore the following Vulnerability IDs
ignore_vulnerability_ids := {
"CVE-2018-14618"
}
# Ignore the following Package Names
ignore_pkgs := {"bash", "vim"}


# For ignoring vulnID
ignore {
input.VulnerabilityID == ignore_vulnerability_ids[_]
}
# For ignoring pkgName
ignore {
input.PkgName == ignore_pkgs[_]
}

+

After adding the above rego file, run the image scan with the --ignore-policy flag followed by the file name to ignore them while scanning:

+
trivy image --ignore-policy trivy_ignore.rego ruby:2.4.0
+

In the above example, the vulnerability "CVE-2018-14618" and the packages "bash" & "vim" are ignored while scanning, and hence patching the image.

+

Ignore File

+

Use a .trivyignore file to list all the vulnerabilities you want to ignore.

+

Example:

+
$ cat .trivyignore

# Accept the risk
CVE-2018-14618
+

In the above example, the vulnerability CVE-2018-14618 is ignored while scanning, and hence while patching the image.

+

For a more detailed explanation on how to ignore certain vulnerabilities with Trivy, please refer to the official documentation here.

\ No newline at end of file diff --git a/website/v0.5.x.html b/website/v0.5.x.html index c96bc4c0..44dbe8be 100644 --- a/website/v0.5.x.html +++ b/website/v0.5.x.html @@ -1,19 +1,59 @@ - + - -Introduction | Copacetic + +Introduction | Copacetic - - - + + + -
-
Version: v0.5.x

Project Copacetic: Directly patch container image vulnerabilities

copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.

Why?

We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between vulnerability disclosure and active exploitation continues to narrow, there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:

  • inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option
  • found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.

In addition to filling the operational gap not met by left-shift security practices and tools, the ability of copa to patch a container without requiring a rebuild of the container image provides other benefits:

  • Allows users other than the image publishers to also patch container images, such as DevSecOps engineers.
  • Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching.
  • Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild.
  • Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.

How?

The copa tool is an extensible engine that:

  1. Parses the needed update packages from the container image’s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats.
  2. Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers.
  3. Applies the resulting update binaries to the container image using buildkit.

This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:

  • Copa supports patching existing container images.
    • Devs don't need to build their images using specific tools or modify them in some way just to support container patching.
  • Copa works with the existing vulnerability scanning and mitigation ecosystems.
    • Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today.
    • Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports.
  • Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image.
    • For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.

For more details, refer to the copa design documentation.

- - +
Version: v0.5.x

Project Copacetic: Directly patch container image vulnerabilities

+

copa is a CLI tool written in Go and based on buildkit that can be used to directly patch container images given the vulnerability scanning results from popular tools like Trivy.

+

Why?

+

We needed the ability to patch containers quickly without going upstream for a full rebuild. As the window between vulnerability disclosure and active exploitation continues to narrow, there is a growing operational need to patch critical security vulnerabilities in container images so they can be quickly redeployed into production. The need is especially acute when those vulnerabilities are:

+
    +
  • inherited from base images several levels deep and waiting on updated releases to percolate through the supply chain is not an option
  • +
  • found in 3rd party app images you don't maintain with update cadences that don't meet your security SLAs.
  • +
+ +

In addition to filling the operational gap not met by left-shift security practices and tools, the ability of copa to patch a container without requiring a rebuild of the container image provides other benefits:

+
    +
  • Allows users other than the image publishers to also patch container images, such as DevSecOps engineers.
  • +
  • Reduces the storage and transmission costs of redistributing patched images by only creating an additional patch layer, instead of rebuilding the entire image which usually results in different layer hashes that break layer caching.
  • +
  • Reduces the turnaround time for patching a container image by not having to wait for base image updates and being a faster operation than a full image rebuild.
  • +
  • Reduces the complexity of patching the image from running a rebuild pipeline to running a single tool on the image.
  • +
+

How?

+

The copa tool is an extensible engine that:

+
    +
  1. Parses the needed update packages from the container image’s vulnerability report produced by a scanner like Trivy. New adapters can be written to accommodate more report formats.
  2. +
  3. Obtains and processes the needed update packages using the appropriate package manager tools such as apt, apk, etc. New adapters can be written to support more package managers.
  4. +
  5. Applies the resulting update binaries to the container image using buildkit.
  6. +
+ +

This approach is motivated by the core principles of making direct container patching broadly applicable and accessible:

+
    +
  • Copa supports patching existing container images. +
      +
    • Devs don't need to build their images using specific tools or modify them in some way just to support container patching.
    • +
    +
  • +
  • Copa works with the existing vulnerability scanning and mitigation ecosystems. +
      +
    • Image publishers don't need to create new workflows for container patching since Copa supports patching container images using the security update packages already being published today.
    • +
    • Consumers do not need to migrate to a new and potentially more limited support ecosystem for custom distros or change their container vulnerability scanning pipelines to include remediation, since Copa can be integrated seamlessly as an extra step to patch containers based on those scanning reports.
    • +
    +
  • +
  • Copa reduces the technical expertise needed and waiting on dependencies needed to patch an image. +
      +
    • For OS package vulnerabilities, no specialized knowledge about a specific image is needed to be patch it as Copa relies on the vulnerability remediation knowledge already embedded in the reports produced by popular container scanning tools today.
    • +
    +
  • +
+

For more details, refer to the copa design documentation.

\ No newline at end of file diff --git a/website/v0.5.x/code-of-conduct.html b/website/v0.5.x/code-of-conduct.html index e23cb9b5..fff0da91 100644 --- a/website/v0.5.x/code-of-conduct.html +++ b/website/v0.5.x/code-of-conduct.html @@ -1,68 +1,111 @@ - + - -Code of Conduct | Copacetic + +Code of Conduct | Copacetic - - - + + + -
-
Version: v0.5.x

Contributor Covenant Code of Conduct

Our Pledge

We as members, contributors, and leaders pledge to make participation in our +

Version: v0.5.x

Contributor Covenant Code of Conduct

+

Our Pledge

+

We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual -identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community.

Our Standards

Examples of behavior that contributes to a positive environment for our -community include:

  • Demonstrating empathy and kindness toward other people
  • Being respectful of differing opinions, viewpoints, and experiences
  • Giving and gracefully accepting constructive feedback
  • Accepting responsibility and apologizing to those affected by our mistakes, -and learning from the experience
  • Focusing on what is best not just for us as individuals, but for the overall -community

Examples of unacceptable behavior include:

  • The use of sexualized language or imagery, and sexual attention or advances of -any kind
  • Trolling, insulting or derogatory comments, and personal or political attacks
  • Public or private harassment
  • Publishing others' private information, such as a physical or email address, -without their explicit permission
  • Other conduct which could reasonably be considered inappropriate in a -professional setting

Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of +identity and orientation.

+

We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community.

+

Our Standards

+

Examples of behavior that contributes to a positive environment for our +community include:

+
    +
  • Demonstrating empathy and kindness toward other people
  • +
  • Being respectful of differing opinions, viewpoints, and experiences
  • +
  • Giving and gracefully accepting constructive feedback
  • +
  • Accepting responsibility and apologizing to those affected by our mistakes, +and learning from the experience
  • +
  • Focusing on what is best not just for us as individuals, but for the overall +community
  • +
+

Examples of unacceptable behavior include:

+
    +
  • The use of sexualized language or imagery, and sexual attention or advances of +any kind
  • +
  • Trolling, insulting or derogatory comments, and personal or political attacks
  • +
  • Public or private harassment
  • +
  • Publishing others' private information, such as a physical or email address, +without their explicit permission
  • +
  • Other conduct which could reasonably be considered inappropriate in a +professional setting
  • +
+

Enforcement Responsibilities

+

Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, -or harmful.

Community leaders have the right and responsibility to remove, edit, or reject +or harmful.

+

Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate.

Scope

This Code of Conduct applies within all community spaces, and also applies when +decisions when appropriate.

+

Scope

+

This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed -representative at an online or offline event.

Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be +representative at an online or offline event.

+

Enforcement

+

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at project-copacetic@googlegroups.com. -All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the -reporter of any incident.

Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct:

1. Correction

Community Impact: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community.

Consequence: A private, written warning from community leaders, providing +All complaints will be reviewed and investigated promptly and fairly.

+

All community leaders are obligated to respect the privacy and security of the +reporter of any incident.

+

Enforcement Guidelines

+

Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct:

+

1. Correction

+

Community Impact: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community.

+

Consequence: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested.

2. Warning

Community Impact: A violation through a single incident or series of -actions.

Consequence: A warning with consequences for continued behavior. No +behavior was inappropriate. A public apology may be requested.

+

2. Warning

+

Community Impact: A violation through a single incident or series of +actions.

+

Consequence: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent -ban.

3. Temporary Ban

Community Impact: A serious violation of community standards, including -sustained inappropriate behavior.

Consequence: A temporary ban from any sort of interaction or public +ban.

+

3. Temporary Ban

+

Community Impact: A serious violation of community standards, including +sustained inappropriate behavior.

+

Consequence: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban.

4. Permanent Ban

Community Impact: Demonstrating a pattern of violation of community +Violating these terms may lead to a permanent ban.

+

4. Permanent Ban

+

Community Impact: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals.

Consequence: A permanent ban from any sort of public interaction within the -community.

Attribution

This Code of Conduct is adapted from the Contributor Covenant, +individual, or aggression toward or disparagement of classes of individuals.

+

Consequence: A permanent ban from any sort of public interaction within the +community.

+

Attribution

+

This Code of Conduct is adapted from the Contributor Covenant, version 2.1, available at -https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

Community Impact Guidelines were inspired by -Mozilla's code of conduct enforcement ladder.

For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

+

Community Impact Guidelines were inspired by +Mozilla's code of conduct enforcement ladder.

+

For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations.

- - +https://www.contributor-covenant.org/translations.

\ No newline at end of file diff --git a/website/v0.5.x/contributing.html b/website/v0.5.x/contributing.html index 082090c8..939157b8 100644 --- a/website/v0.5.x/contributing.html +++ b/website/v0.5.x/contributing.html @@ -1,33 +1,127 @@ - + - -Contributing | Copacetic + +Contributing | Copacetic - - - + + + -
-
Version: v0.5.x

Contributing

Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.

Bi-Weekly Community Meeting

A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join here

Slack

To discuss issues with Copa, features, or development, you can join the #copa channel on the OCI Slack.

Contributing Issues

Before opening any new issues, please search our existing GitHub issues to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead.

When opening an issue, please select the most appropriate template for what you're contributing:

  • Bug Report: If you would like to report the project or tool behaving in unexpected ways.
  • Documentation Improvement: If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content.
  • Request: If you have a feature request, suggestion, or a even a design proposal to review.
  • Question: If you would like to ask the maintainers a question about the project.

Contributing Code

Getting Started

Follow the instructions to either:

For an overview of the project components, refer to the copa design document.

Visual Studio Code Development Container

VSCode supports development in a containerized environment through its Remote - Container extension. This folder provides a development container which encapsulates the dependencies specified in the instructions to build and run copa.

Prerequisites

  1. Docker

    For Windows users, enabling WSL2 back-end integration with Docker is recommended.

  2. Visual Studio Code
  3. Visual Studio Code Remote - Containers extension

⚠ If running via Docker Desktop for Windows

Note that the mounted workspace files appear owned by root in the dev container, which will cause git commands to fail with a fatal: detected dubious ownership in a repository error due to safe.directory checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the vscode user:

sudo chown -R vscode:vscode /workspace/copacetic

Personalizing user settings in a dev container

VSCode supports applying your user settings, such as your .gitconfig, to a dev container through the use of dotfiles repositories. This can be done through your own VSCode settings.json file without changing the dev container image or configuration.

Tests

Once you can successfully make the project, any code contributions should also successfully:

  • Pass unit tests via make test.
  • Lint cleanly via make lint.

Pull requests will also be expected to pass the PR functional tests specified by .github/workflows/build.yml.

Pull Requests

If you'd like to start contributing code to the project, you can search for issues with the good first issue label. Other kinds of PR contributions we would look for include:

  • Fixes for bugs and other correctness issues.
  • Docs and other content improvements (e.g. samples).
  • Extensions to support parsing new scanning report formats.
  • Extensions to support patching images based on new distros or using new package managers.

For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:

  • Architectural changes (e.g. breaking interfaces or violations of this project's design tenets).
  • Unsolicited features that significantly expand the functional scope of the tool.

Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the Angular commit message format for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs.

We suggest:

  • Use the standard header format of "<type>: <short summary>" where the <type> is one of the following:
    • build: Changes that affect the build system or external dependencies
    • ci: Changes to the GitHub workflows and configurations
    • docs: Documentation only changes
    • feat: A new feature
    • fix: A bug fix
    • perf: A code change that improves performance
    • refactor: A code change that neither fixes a bug nor adds a feature
    • test: Adding missing tests or correcting existing tests
  • Use a concise, imperative description of the changes included in the <short summary> of the header, the body of the PR, and generally in your commit messages.
  • Use GitHub keywords in the footer of your PR description, such as closes to automatically close issues the PR intends to address.

Developer Certificate of Origin (DCO)

The Developer Certificate of Origin (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the full text of the DCO, reformatted for readability:

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I +

Version: v0.5.x

Contributing

Welcome! We are very happy to accept community contributions to the project, whether through filing issues or code in the form of Pull Requests. Please note that by participating in this project, you agree to abide by the Code of Conduct, as well as the terms of the Developer Certificate of Origin.

+

Bi-Weekly Community Meeting

+

A great way to get started is to join our bi-weekly community meeting. The meeting is held every other Monday from 1:30pm PT - 2:15pm PT. You can find the agenda and links to join here

+

Slack

+

To discuss issues with Copa, features, or development, you can join the #copa channel on the OCI Slack.

+

Contributing Issues

+

Before opening any new issues, please search our existing GitHub issues to check if your bug or suggestion has already been filed. If such an issue already exists, we recommend adding your comments and perspective to that existing issue instead.

+

When opening an issue, please select the most appropriate template for what you're contributing:

+
    +
  • Bug Report: If you would like to report the project or tool behaving in unexpected ways.
  • +
  • Documentation Improvement: If you have corrections or improvements to the project's documents, be they typos, factual errors, or missing content.
  • +
  • Request: If you have a feature request, suggestion, or a even a design proposal to review.
  • +
  • Question: If you would like to ask the maintainers a question about the project.
  • +
+

Contributing Code

+

Getting Started

+

Follow the instructions to either:

+ +

For an overview of the project components, refer to the copa design document.

+

Visual Studio Code Development Container

+

VSCode supports development in a containerized environment through its Remote - Container extension. This folder provides a development container which encapsulates the dependencies specified in the instructions to build and run copa.

+

Prerequisites

+
    +
  1. Docker +
    +

    For Windows users, enabling WSL2 back-end integration with Docker is recommended.

    +
    +
  2. +
  3. Visual Studio Code
  4. +
  5. Visual Studio Code Remote - Containers extension
  6. +
+
+

⚠ If running via Docker Desktop for Windows

+

Note that the mounted workspace files appear owned by root in the dev container, which will cause git commands to fail with a fatal: detected dubious ownership in a repository error due to safe.directory checks. This can be addressed by changing the mapped ownership of the workspace files in the dev container to the vscode user:

+
sudo chown -R vscode:vscode /workspace/copacetic
+
+

Personalizing user settings in a dev container

+

VSCode supports applying your user settings, such as your .gitconfig, to a dev container through the use of dotfiles repositories. This can be done through your own VSCode settings.json file without changing the dev container image or configuration.

+

Tests

+

Once you can successfully make the project, any code contributions should also successfully:

+
    +
  • Pass unit tests via make test.
  • +
  • Lint cleanly via make lint.
  • +
+

Pull requests will also be expected to pass the PR functional tests specified by .github/workflows/build.yml.

+

Pull Requests

+

If you'd like to start contributing code to the project, you can search for issues with the good first issue label. Other kinds of PR contributions we would look for include:

+
    +
  • Fixes for bugs and other correctness issues.
  • +
  • Docs and other content improvements (e.g. samples).
  • +
  • Extensions to support parsing new scanning report formats.
  • +
  • Extensions to support patching images based on new distros or using new package managers.
  • +
+

For any changes that may involve significant refactoring or development effort, we suggest that you file an issue to discuss the proposal with the maintainers first as it is unlikely that we will accept large PRs without prior discussion that have:

+
    +
  • Architectural changes (e.g. breaking interfaces or violations of this project's design tenets).
  • +
  • Unsolicited features that significantly expand the functional scope of the tool.
  • +
+

Pull requests should be submitted from your fork of the project with the PR template filled out. This project uses the Angular commit message format for automated changelog generation, so it's helpful to be familiar with it as the maintainers will need to ensure adherence to it on accepting PRs.

+

We suggest:

+
    +
  • Use the standard header format of "<type>: <short summary>" where the <type> is one of the following: +
      +
    • build: Changes that affect the build system or external dependencies
    • +
    • ci: Changes to the GitHub workflows and configurations
    • +
    • docs: Documentation only changes
    • +
    • feat: A new feature
    • +
    • fix: A bug fix
    • +
    • perf: A code change that improves performance
    • +
    • refactor: A code change that neither fixes a bug nor adds a feature
    • +
    • test: Adding missing tests or correcting existing tests
    • +
    +
  • +
  • Use a concise, imperative description of the changes included in the <short summary> of the header, the body of the PR, and generally in your commit messages.
  • +
  • Use GitHub keywords in the footer of your PR description, such as closes to automatically close issues the PR intends to address.
  • +
+

Developer Certificate of Origin (DCO)

+

The Developer Certificate of Origin (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the full text of the DCO, reformatted for readability:

+
+

By making a contribution to this project, I certify that:

+

(a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license -indicated in the file; or

(b) The contribution is based upon previous work that, to the best +indicated in the file; or

+

(b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated -in the file; or

(c) The contribution was provided directly to me by some other +in the file; or

+

(c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified -it.

(d) I understand and agree that this project and the contribution +it.

+

(d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with -this project or the open source license(s) involved.

Contributors sign-off that they adhere to these requirements by adding a Signed-off-by line to commit messages.

This is my commit message

Signed-off-by: Random J Developer <random@developer.example.org>

Git even has a -s command line option to append this automatically to your commit message:

git commit -s -m 'This is my commit message'

Pull requests that do not contain a valid Signed-off-by line cannot be merged.

I didn't sign my commit, now what?

No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:

git switch <branch-name>
git commit --amend --no-edit --signoff
git push --force-with-lease <remote-name> <branch-name>

Code of Conduct

This project has adopted the Contributor Covenant Code of Conduct.

- - +this project or the open source license(s) involved.

+
+

Contributors sign-off that they adhere to these requirements by adding a Signed-off-by line to commit messages.

+
This is my commit message

Signed-off-by: Random J Developer <random@developer.example.org>
+

Git even has a -s command line option to append this automatically to your commit message:

+
git commit -s -m 'This is my commit message'
+

Pull requests that do not contain a valid Signed-off-by line cannot be merged.

+

I didn't sign my commit, now what?

+

No worries - You can easily amend your commit with a sign-off and force push the change to your submitting branch:

+
git switch <branch-name>
git commit --amend --no-edit --signoff
git push --force-with-lease <remote-name> <branch-name>
+

Code of Conduct

+

This project has adopted the Contributor Covenant Code of Conduct.

\ No newline at end of file diff --git a/website/v0.5.x/design.html b/website/v0.5.x/design.html index b2330871..5f2698b8 100644 --- a/website/v0.5.x/design.html +++ b/website/v0.5.x/design.html @@ -1,19 +1,84 @@ - + - -Design | Copacetic + +Design | Copacetic - - - + + + -
-
Version: v0.5.x

Design

Design Tenets

  • Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update. This is a raison d’etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.

  • Copa is intended to work with the existing ecosystem of container images. The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.

  • Copa is intended to allow parties other than the image authors to address container vulnerabilities. Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.

  • Copa is intended to do one thing well and be composable with other tools and processes. Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane.

Design Reasoning

The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental crane rebase:

  • Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an apt install during image creation will overwrite the dpkg status file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners.

    To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.

  • Rebasing also requires that the user knows a priori what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets.

    While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose.

    The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well.

Architecture

The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:

  • The data schema of various vulnerability scanners producing the input vulnerability report.
  • The state management of various package managers and process for applying patches appropriately through them.

Effectively, copa patch can be considered a command that bridges an extensible Parse action with an extensible Apply action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:

type UpdatePackage struct {
Name string
Version string
}

type UpdateManifest struct {
OSType string
OSVersion string
Arch string
Updates []UpdatePackage
}

type ScanReportParser interface {
Parse(reportPath string) (*UpdateManifest, error)
}

type PackageManager interface {
Apply(imagePath string, report *UpdateManifest) error
}

Implementation

copa is a pseudo-frontend to buildkit implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of LLB graphs for buildkit to execute:

  1. Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image.
    • Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions.
  2. Actions to fetch and deploy tools that can be injected into the target image to perform the patching.
    • In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches.
    • In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image.
    • Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well.
  3. Actions to deploy the required patch packages to the target image.
    • copa integrates with buildkit at the API level because it uses the diff and merge graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.

Tradeoffs

  • The core architectural choice of relying on packages as the unit of patching creates a couple of constraints:
    • By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa.
    • copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this.
  • No support for windows containers given the dependency on buildkit.
- - +
Version: v0.5.x

Design

Design Tenets

+
    +
  • +

    Copa is intended to accelerate container patching by eliminating waiting on base image dependency chains to update. This is a raison d’etre for the Copa project, so if we figured out a different way to patch containers that still relied on waiting for base images to be rebuilt and republished, we would consider spinning that off into a different project instead of making it part of Copa.

    +
  • +
  • +

    Copa is intended to work with the existing ecosystem of container images. The project should have a strong preference for solutions that do not require image producers to create or modify their images in special ways to use Copa.

    +
  • +
  • +

    Copa is intended to allow parties other than the image authors to address container vulnerabilities. Copa should require a minimum of special knowledge about the lineage and construction of an image from the user to patch it successfully.

    +
  • +
  • +

    Copa is intended to do one thing well and be composable with other tools and processes. Copa does not have to be a universal multitool for container patching. For example, it is preferable that it integrates with popular container scanning tools rather than incorporating custom container scanning into the project itself. Similarly, it does not need to become a general container manipulation tool in the vein of crane.

    +
  • +
+

Design Reasoning

+

The design of copa arises from the application of those tenets to the observed issues in previous efforts directly update container images via rebasing, for example, the experimental crane rebase:

+
    +
  • +

    Rebasing requires that all actors involved in creation of the image are coordinated so that some layers can be switched out without breaking the image. Attempting to switch out layers in the container overlay structure is brittle because most existing containers are created by writing over shared configuration files and data stores in base images. For example, an apt install during image creation will overwrite the dpkg status file in the base image, which will mask any package updates in a rebased layer. Since many existing container scanners rely on the reported package status to find vulnerable package versions, this can cause new vulnerabilities to not be reported or for patched binaries not to be recognized by the scanners.

    +

    To avoid breaking integration with the existing container ecosystem, copa patches the filesystem bundle as a whole instead of as a collection of layers so that the resulting image state is consistent. This strategy also allows copa to patch vulnerabilties introduced at any layer in the image, including OS packages added in the app layers that is not addressed by a simple rebase. It also supports the core tenet of supporting patching without requiring coordination with all the publishers of the base images that a given image transitively depends on.

    +
  • +
  • +

    Rebasing also requires that the user knows a priori what base image (or transitive base image) is in the target image to determine which appropriate rebase image to use. This makes it very difficult for anyone not intimately involved with authoring the image from being able to remediate it, which is one of our tenets.

    +

    While it is possible to embed extra metadata or annotations into the target image to facilitate this base image (or transitive base image) lookup, that would require that the images to be patched be modified or created especially to support updates, which goes against another of our tenets to be able to patch images without requiring them to be customized explicitly for that purpose.

    +

    The design of copa addresses this by reframing the problem of updating containers and understanding the structure or lineage of a container image to the more specific problem of what packages in a given container image need to be updated. This allows copa to tap into the expertise embedded in the much more robust ecosystem for detecting and remediating vulnerabilities at the package level that already exists today. By making copa an additional remediation step that can be run after a container scan in existing workflows, we avoid both of those issues with an additional benefit: it incurs no additional work on the part of base image publishers to support patching of images based on their base images, the existing channels for publishing update packages is sufficient to service those container images as well.

    +
  • +
+

Architecture

+ +

The requirements presented encourage an extensible model in order to support broad applicability. Specifically, there are two areas that the tool will need to accommodate multiple implementations to support more use cases:

+
    +
  • The data schema of various vulnerability scanners producing the input vulnerability report.
  • +
  • The state management of various package managers and process for applying patches appropriately through them.
  • +
+

Effectively, copa patch can be considered a command that bridges an extensible Parse action with an extensible Apply action as illustrated in the diagram; the implementation can be thought of as an engine that uses this abstract Go interface to apply security update packages:

+
type UpdatePackage struct {
Name string
Version string
}

type UpdateManifest struct {
OSType string
OSVersion string
Arch string
Updates []UpdatePackage
}

type ScanReportParser interface {
Parse(reportPath string) (*UpdateManifest, error)
}

type PackageManager interface {
Apply(imagePath string, report *UpdateManifest) error
}
+

Implementation

+ +

copa is a pseudo-frontend to buildkit implemented as a CLI tool. Effectively, instead of taking a container definition to create from scratch, it takes the reference to the target image to patch and a container scan report and builds a series of LLB graphs for buildkit to execute:

+
    +
  1. Actions to probe the image as a filesystem bundle, for example, retrieving the package manager status in the image. +
      +
    • Within each distribution type identified by the scanner report (e.g. Debian) there can be different ways of applying patches to the target image (e.g. distroless), which can be differentiated through these actions.
    • +
    +
  2. +
  3. Actions to fetch and deploy tools that can be injected into the target image to perform the patching. +
      +
    • In cases where the package tools are not available in the target image, a standard version of the OS container matching the target image's is used to stage the necessary tooling for patches.
    • +
    • In the case of distroless images for example, where there is no valid package status file in the target image, the tooling container is also used to pull down and process the necessary package updates for copy to the target image.
    • +
    • Although not pictured, this can also be used to obtain tools (e.g. busybox) to be used in the image probing stage as well.
    • +
    +
  4. +
  5. Actions to deploy the required patch packages to the target image. +
      +
    • copa integrates with buildkit at the API level because it uses the diff and merge graph operations directly so that it can stage all the necessary tooling in the target image while producing a resulting image that only contains the original image plus a new layer with all the deployed patches.
    • +
    +
  6. +
+

Tradeoffs

+
    +
  • The core architectural choice of relying on packages as the unit of patching creates a couple of constraints: +
      +
    • By relying on existing vulnerability scanner behavior that only detects vulnerabilities via presence/absence of vulnerable packages, copa is limited in the kinds of vulnerabilities it can address and false positive/negatives from scanners flow downstream to copa.
    • +
    • copa depends on individual package manager adapters to correctly deploy patches to the target images, but there is a long tail of compatibility issues that arise depending on the target image itself (e.g. outdated package manager config/keys, invalid/missing package graph, etc.). Overall, the maintenance cost of the project is expected to be non-trivial to address this.
    • +
    +
  • +
  • No support for windows containers given the dependency on buildkit.
  • +
\ No newline at end of file diff --git a/website/v0.5.x/faq.html b/website/v0.5.x/faq.html index 5c9ced9d..8df5be04 100644 --- a/website/v0.5.x/faq.html +++ b/website/v0.5.x/faq.html @@ -1,19 +1,29 @@ - + - -FAQ | Copacetic + +FAQ | Copacetic - - - + + + -
-
Version: v0.5.x

FAQ

What kind of vulnerabilities can Copa patch?

Copa is capable of patching "OS level" vulnerabilities. This includes packages (like openssl) in the image that are managed by a package manager such as apt or yum. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see below for more details).

What kind of vulnerabilities can Copa not patch?

Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the golang.org/x/net module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level.

To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like http://archive.ubuntu.com/ubuntu/ for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications.

Can I replace the package repositories in the image with my own?

caution

Experimental: This feature might change without preserving backwards compatibility.

Copa does not support replacing the repositories in the package managers with alternatives. Images must already use the intended package repositories. For example, for debian, updating /etc/apt/sources.list from http://archive.ubuntu.com/ubuntu/ to a mirror, such as https://mirrors.wikimedia.org/ubuntu/.

If you need the tooling image to use a different package repository, you can create a source policy to define a replacement image and/or pin to a digest. For example, the following source policy replaces docker.io/library/debian:11-slim image with foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5:

cat <<EOF > source-policy.json
{
"rules": [
{
"action": "CONVERT",
"selector": {
"identifier": "docker-image://docker.io/library/debian:11-slim"
},
"updates": {
"identifier": "docker-image://foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5"
}
}
]
}
EOF

export EXPERIMENTAL_BUILDKIT_SOURCE_POLICY=source-policy.json

Tooling image for Debian-based images are docker.io/library/debian:11-slim and RPM-based repos are mcr.microsoft.com/cbl-mariner/base/core:2.0.

For more information on source policies, see Buildkit Source Policies.

- - +
Version: v0.5.x

FAQ

What kind of vulnerabilities can Copa patch?

+

Copa is capable of patching "OS level" vulnerabilities. This includes packages (like openssl) in the image that are managed by a package manager such as apt or yum. Copa is not currently capable of patching vulnerabilities at the "application level" such as Python packages or Go modules (see below for more details).

+

What kind of vulnerabilities can Copa not patch?

+

Copa is not capable of patching vulnerabilities for compiled languages, like Go, at the "application level", for instance, Go modules. If your application uses a vulnerable version of the golang.org/x/net module, Copa will be unable to patch it. This is because Copa doesn't have access to the application's source code or the knowledge of how to build it, such as compiler flags, preventing it from patching vulnerabilities at the application level.

+

To patch vulnerabilities for applications, you can package these applications and consume them from package repositories, like http://archive.ubuntu.com/ubuntu/ for Ubuntu, and ensure Trivy can scan and report vulnerabilities for these packages. This way, Copa can patch the applications as a whole, though it cannot patch specific modules within the applications.

+

Can I replace the package repositories in the image with my own?

+
caution

Experimental: This feature might change without preserving backwards compatibility.

+

Copa does not support replacing the repositories in the package managers with alternatives. Images must already use the intended package repositories. For example, for debian, updating /etc/apt/sources.list from http://archive.ubuntu.com/ubuntu/ to a mirror, such as https://mirrors.wikimedia.org/ubuntu/.

+

If you need the tooling image to use a different package repository, you can create a source policy to define a replacement image and/or pin to a digest. For example, the following source policy replaces docker.io/library/debian:11-slim image with foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5:

+
cat <<EOF > source-policy.json
{
"rules": [
{
"action": "CONVERT",
"selector": {
"identifier": "docker-image://docker.io/library/debian:11-slim"
},
"updates": {
"identifier": "docker-image://foo.io/bar/baz:latest@sha256:42d3e6bc186572245aded5a0be381012adba6d89355fa9486dd81b0c634695b5"
}
}
]
}
EOF

export EXPERIMENTAL_BUILDKIT_SOURCE_POLICY=source-policy.json
+
+

Tooling image for Debian-based images are docker.io/library/debian:11-slim and RPM-based repos are mcr.microsoft.com/cbl-mariner/base/core:2.0.

+
+

For more information on source policies, see Buildkit Source Policies.

\ No newline at end of file diff --git a/website/v0.5.x/github-action.html b/website/v0.5.x/github-action.html index 17f530b1..a9f8b7b7 100644 --- a/website/v0.5.x/github-action.html +++ b/website/v0.5.x/github-action.html @@ -1,19 +1,33 @@ - + - -Copa Github Action | Copacetic + +Copa Github Action | Copacetic - - - + + + -
-
Version: v0.5.x

Copa Github Action

Overview

The Copa Github Action allows you patch vulnerable containers in your workflows using Copa.

Inputs

image

Required The image reference to patch.

image-report

Required The trivy json vulnerability report of the image to patch.

patched-tag

Required The new patched image tag.

buildkit-version

Optional The buildkit version used in the action, default is latest.

copa-version

Optional The Copa version used in the action, default is latest.

Output

patched-image

Image reference of the resulting patched image.

Example Workflow

on: [push]

jobs:
test:
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
# provide relevant list of images to scan on each run
images: ['docker.io/library/nginx:1.21.6', 'docker.io/openpolicyagent/opa:0.46.0', 'docker.io/library/hello-world:latest']

steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@dedd61cf5d839122591f5027c89bf3ad27691d18

- name: Generate Trivy Report
uses: aquasecurity/trivy-action@69cbbc0cbbf6a2b0bab8dcf0e9f2d7ead08e87e4
with:
scan-type: 'image'
format: 'json'
output: 'report.json'
ignore-unfixed: true
vuln-type: 'os'
image-ref: ${{ matrix.images }}

- name: Check Vuln Count
id: vuln_count
run: |
report_file="report.json"
vuln_count=$(jq '.Results | length' "$report_file")
echo "vuln_count=$vuln_count" >> $GITHUB_OUTPUT

- name: Copa Action
if: steps.vuln_count.outputs.vuln_count != '0'
id: copa
uses: project-copacetic/copa-action@v1
with:
image: ${{ matrix.images }}
image-report: 'report.json'
patched-tag: 'patched'
buildkit-version: 'v0.11.6'
# optional, default is latest
copa-version: '0.4.1'

- name: Login to Docker Hub
if: steps.copa.conclusion == 'success'
id: login
uses: docker/login-action@b4bedf8053341df3b5a9f9e0f2cf4e79e27360c6
with:
username: 'user'
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Docker Push Patched Image
if: steps.login.conclusion == 'success'
run: |
docker push ${{ steps.copa.outputs.patched-image }}

- - +
Version: v0.5.x

Copa Github Action

Overview

+

The Copa Github Action allows you patch vulnerable containers in your workflows using Copa.

+

Inputs

+

image

+

Required The image reference to patch.

+

image-report

+

Required The trivy json vulnerability report of the image to patch.

+

patched-tag

+

Required The new patched image tag.

+

buildkit-version

+

Optional The buildkit version used in the action, default is latest.

+

copa-version

+

Optional The Copa version used in the action, default is latest.

+

Output

+

patched-image

+

Image reference of the resulting patched image.

+

Example Workflow

+
on: [push]

jobs:
test:
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
# provide relevant list of images to scan on each run
images: ['docker.io/library/nginx:1.21.6', 'docker.io/openpolicyagent/opa:0.46.0', 'docker.io/library/hello-world:latest']

steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@dedd61cf5d839122591f5027c89bf3ad27691d18

- name: Generate Trivy Report
uses: aquasecurity/trivy-action@69cbbc0cbbf6a2b0bab8dcf0e9f2d7ead08e87e4
with:
scan-type: 'image'
format: 'json'
output: 'report.json'
ignore-unfixed: true
vuln-type: 'os'
image-ref: ${{ matrix.images }}

- name: Check Vuln Count
id: vuln_count
run: |
report_file="report.json"
vuln_count=$(jq '.Results | length' "$report_file")
echo "vuln_count=$vuln_count" >> $GITHUB_OUTPUT

- name: Copa Action
if: steps.vuln_count.outputs.vuln_count != '0'
id: copa
uses: project-copacetic/copa-action@v1
with:
image: ${{ matrix.images }}
image-report: 'report.json'
patched-tag: 'patched'
buildkit-version: 'v0.11.6'
# optional, default is latest
copa-version: '0.4.1'

- name: Login to Docker Hub
if: steps.copa.conclusion == 'success'
id: login
uses: docker/login-action@b4bedf8053341df3b5a9f9e0f2cf4e79e27360c6
with:
username: 'user'
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Docker Push Patched Image
if: steps.login.conclusion == 'success'
run: |
docker push ${{ steps.copa.outputs.patched-image }}

\ No newline at end of file diff --git a/website/v0.5.x/installation.html b/website/v0.5.x/installation.html index 6b73a4a7..b91de755 100644 --- a/website/v0.5.x/installation.html +++ b/website/v0.5.x/installation.html @@ -1,19 +1,23 @@ - + - -Installation | Copacetic + +Installation | Copacetic - - - + + + -
-
Version: v0.5.x

Installation

Homebrew

On macOS and Linux, copa can be installed via Homebrew:

brew install copa

GitHub

You can download the latest and previous versions of copa from the GitHub releases page.

Development Setup

The following instructions are for Ubuntu 22.04 with the dependency versions supported as part of the dev container environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead.

git clone https://github.com/project-copacetic/copacetic
cd copacetic
make
# OPTIONAL: install copa to a pathed folder
sudo mv dist/linux_amd64/release/copa /usr/local/bin/
- - +
Version: v0.5.x

Installation

Homebrew

+

On macOS and Linux, copa can be installed via Homebrew:

+
brew install copa
+

GitHub

+

You can download the latest and previous versions of copa from the GitHub releases page.

+

Development Setup

+

The following instructions are for Ubuntu 22.04 with the dependency versions supported as part of the dev container environment we use for builds and tests. For other distributions and OS, refer to the appropriate installation instructions for each of the components instead.

+
git clone https://github.com/project-copacetic/copacetic
cd copacetic
make
# OPTIONAL: install copa to a pathed folder
sudo mv dist/linux_amd64/release/copa /usr/local/bin/
\ No newline at end of file diff --git a/website/v0.5.x/output.html b/website/v0.5.x/output.html index 86fdaed2..6f31f708 100644 --- a/website/v0.5.x/output.html +++ b/website/v0.5.x/output.html @@ -1,19 +1,32 @@ - + - -Output | Copacetic + +Output | Copacetic - - - + + + -
-
Version: v0.5.x

Output

caution

Experimental: This feature might change without preserving backwards compatibility.

Copa optionally outputs a Vulnerability Exploitability eXchange (VEX) file as a result of the patching process to surface the vulnerabilities and packages that were patched.

Currently, Copa supports the OpenVEX format, but it can be extended to support other formats.

OpenVEX

OpenVEX is an implementation of Vulnerability Exploitability eXchange (VEX) format. For more information, see OpenVEX specification.

tip
  • Use COPA_VEX_AUTHOR environment variable to set the author of the VEX document. If it's not set, the author will default to Project Copacetic.

  • A VEX document must contain at least one VEX statement. If there are no fixed vulnerabilities, Copa will not generate a VEX document.

To generate a VEX document using OpenVEX, use --format="openvex" flag, and use --output to specify a file path. For example:

copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --format="openvex" --output "nginx.1.21.6-vex.json"

This will generate a VEX Document that looks like:

{
"@context": "https://openvex.dev/ns",
"@id": "https://openvex.dev/docs/public/vex-a6c44ec1d79e9dd4190dc01b4ecf7527ebb26bd37c01e32e6efcd203ae00d2a5",
"author": "Project Copacetic",
"timestamp": "2023-10-11T00:15:00.114768055Z",
"version": 1,
"tooling": "Project Copacetic",
"statements": [
{
"vulnerability": {
"@id": "CVE-2021-22945"
},
"products": [
{
"@id": "pkg:oci/docker.io/library/nginx:1.21.6-patched",
"subcomponents": [
{
"@id": "pkg:deb/debian/curl@7.74.0-1.3+deb11u2?arch=amd64"
},
{
"@id": "pkg:deb/debian/libcurl4@7.74.0-1.3+deb11u2?arch=amd64"
}
]
}
],
"status": "fixed"
},
...
}
- - +
Version: v0.5.x

Output

caution

Experimental: This feature might change without preserving backwards compatibility.

+

Copa optionally outputs a Vulnerability Exploitability eXchange (VEX) file as a result of the patching process to surface the vulnerabilities and packages that were patched.

+

Currently, Copa supports the OpenVEX format, but it can be extended to support other formats.

+

OpenVEX

+

OpenVEX is an implementation of Vulnerability Exploitability eXchange (VEX) format. For more information, see OpenVEX specification.

+
tip
    +
  • +

    Use COPA_VEX_AUTHOR environment variable to set the author of the VEX document. If it's not set, the author will default to Project Copacetic.

    +
  • +
  • +

    A VEX document must contain at least one VEX statement. If there are no fixed vulnerabilities, Copa will not generate a VEX document.

    +
  • +
+

To generate a VEX document using OpenVEX, use --format="openvex" flag, and use --output to specify a file path. For example:

+
copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --format="openvex" --output "nginx.1.21.6-vex.json"
+

This will generate a VEX Document that looks like:

+
{
"@context": "https://openvex.dev/ns",
"@id": "https://openvex.dev/docs/public/vex-a6c44ec1d79e9dd4190dc01b4ecf7527ebb26bd37c01e32e6efcd203ae00d2a5",
"author": "Project Copacetic",
"timestamp": "2023-10-11T00:15:00.114768055Z",
"version": 1,
"tooling": "Project Copacetic",
"statements": [
{
"vulnerability": {
"@id": "CVE-2021-22945"
},
"products": [
{
"@id": "pkg:oci/docker.io/library/nginx:1.21.6-patched",
"subcomponents": [
{
"@id": "pkg:deb/debian/curl@7.74.0-1.3+deb11u2?arch=amd64"
},
{
"@id": "pkg:deb/debian/libcurl4@7.74.0-1.3+deb11u2?arch=amd64"
}
]
}
],
"status": "fixed"
},
...
}
\ No newline at end of file diff --git a/website/v0.5.x/quick-start.html b/website/v0.5.x/quick-start.html index 250c74ec..8bdd22be 100644 --- a/website/v0.5.x/quick-start.html +++ b/website/v0.5.x/quick-start.html @@ -1,22 +1,84 @@ - + - -Quick Start | Copacetic + +Quick Start | Copacetic - - - + + + -
-
Version: v0.5.x

Quick Start

This sample illustrates how to patch containers using vulnerability reports with copa.

Prerequisites

Sample Steps

  1. Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:

    trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6

    You can also see the existing patchable vulnerabilities in table form on the shell with:

    trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6

  2. To patch the image, use the Trivy report and specify a buildkit instance to connect to:

    By default copa will attempt to auto-connect to an instance in order:

    1. Default docker buildkit endpoint (requires at least docker v24.0 with containerd snapshotter support enabled)
    2. Currently selected buildx builder (see: docker buildx --help)
    3. buildkit daemon at the default address /run/buildkit/buildkitd.sock

    If an instance doesn't exist or that instance doesn't support all the features copa needs the next will be attempted. -You may need to specify a custom address using the --addr flag. Here are the supported formats:

    • unix:///path/to/buildkit.sock - Connect to buildkit over unix socket.
    • tcp://$BUILDKIT_ADDR:$PORT - Connect to buildkit over TCP. (not recommended for security reasons)
    • docker://<docker connection spec> - Connect to docker, currently only unix sockets are supported, e.g. docker://unix:///var/run/docker.sock (or just docker://).
    • docker-container://my-buildkit-container - Connect to a buildkitd running in a docker container.
    • buildx://my-builder - Connect to a buildx builder (or buildx:// for the currently selected builder). Note: only container-backed buildx instances are currently supported
    • nerdctl-container://my-container-name - Similar to docker-container but uses nerdctl.
    • podman-container://my-container-name - Similar to docker-container but uses podman.
    • ssh://myhost - Connect to a buildkit instance over SSH. Format of the host spec should mimic the SSH command.
    • kubepod://mypod - Connect to buildkit running in a Kubernetes pod. Can also specify kubectl context and pod namespace (kubepod://mypod?context=foo&namespace=notdefault).

    Buildkit Connection Examples

    Example: Connect using defaults:

    copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched

    Example: Connect to buildx

    docker buildx create --name demo
    copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr buildx://demo

    Example: Buildkit in a container

    export BUILDKIT_VERSION=v0.12.0
    docker run \
    --detach \
    --rm \
    --privileged \
    --name buildkitd \
    --entrypoint buildkitd \
    "moby/buildkit:$BUILDKIT_VERSION"

    copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr docker-container://buildkitd

    Example: Buildkit over TCP

    export BUILDKIT_VERSION=v0.12.0
    export BUILDKIT_PORT=8888
    docker run \
    --detach \
    --rm \
    --privileged \
    -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \
    --name buildkitd \
    --entrypoint buildkitd \
    "moby/buildkit:$BUILDKIT_VERSION" \
    --addr tcp://0.0.0.0:$BUILDKIT_PORT
    copa patch \
    -i docker.io/library/nginx:1.21.6 \
    -r nginx.1.21.6.json \
    -t 1.21.6-patched \
    -a tcp://0.0.0.0:$BUILDKIT_PORT

    In either case, copa is non-destructive and exports a new image with the specified 1.21.6-patched label to the local Docker daemon.

    NOTE: if you're running this sample against an image from a private registry instead, +

    Version: v0.5.x

    Quick Start

    This sample illustrates how to patch containers using vulnerability reports with copa.

    +

    Prerequisites

    + +

    Sample Steps

    +
      +
    1. +

      Scan the container image for patchable OS vulnerabilities, outputting the results to a JSON file:

      +
      trivy image --vuln-type os --ignore-unfixed -f json -o nginx.1.21.6.json docker.io/library/nginx:1.21.6
      +

      You can also see the existing patchable vulnerabilities in table form on the shell with:

      +
      trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6

      +
    2. +
    3. +

      To patch the image, use the Trivy report and specify a buildkit instance to connect to:

      +

      By default copa will attempt to auto-connect to an instance in order:

      +
        +
      1. Default docker buildkit endpoint (requires at least docker v24.0 with containerd snapshotter support enabled)
      2. +
      3. Currently selected buildx builder (see: docker buildx --help)
      4. +
      5. buildkit daemon at the default address /run/buildkit/buildkitd.sock
      6. +
      +

      If an instance doesn't exist or that instance doesn't support all the features copa needs the next will be attempted. +You may need to specify a custom address using the --addr flag. Here are the supported formats:

      +
        +
      • unix:///path/to/buildkit.sock - Connect to buildkit over unix socket.
      • +
      • tcp://$BUILDKIT_ADDR:$PORT - Connect to buildkit over TCP. (not recommended for security reasons)
      • +
      • docker://<docker connection spec> - Connect to docker, currently only unix sockets are supported, e.g. docker://unix:///var/run/docker.sock (or just docker://).
      • +
      • docker-container://my-buildkit-container - Connect to a buildkitd running in a docker container.
      • +
      • buildx://my-builder - Connect to a buildx builder (or buildx:// for the currently selected builder). Note: only container-backed buildx instances are currently supported
      • +
      • nerdctl-container://my-container-name - Similar to docker-container but uses nerdctl.
      • +
      • podman-container://my-container-name - Similar to docker-container but uses podman.
      • +
      • ssh://myhost - Connect to a buildkit instance over SSH. Format of the host spec should mimic the SSH command.
      • +
      • kubepod://mypod - Connect to buildkit running in a Kubernetes pod. Can also specify kubectl context and pod namespace (kubepod://mypod?context=foo&namespace=notdefault).
      • +
      +

      Buildkit Connection Examples

      +

      Example: Connect using defaults:

      +
      copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched
      +

      Example: Connect to buildx

      +
      docker buildx create --name demo
      copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr buildx://demo
      +

      Example: Buildkit in a container

      +
      export BUILDKIT_VERSION=v0.12.0
      docker run \
      --detach \
      --rm \
      --privileged \
      --name buildkitd \
      --entrypoint buildkitd \
      "moby/buildkit:$BUILDKIT_VERSION"

      copa patch -i docker.io/library/nginx:1.21.6 -r nginx.1.21.6.json -t 1.21.6-patched --addr docker-container://buildkitd
      +

      Example: Buildkit over TCP

      +
      export BUILDKIT_VERSION=v0.12.0
      export BUILDKIT_PORT=8888
      docker run \
      --detach \
      --rm \
      --privileged \
      -p 127.0.0.1:$BUILDKIT_PORT:$BUILDKIT_PORT/tcp \
      --name buildkitd \
      --entrypoint buildkitd \
      "moby/buildkit:$BUILDKIT_VERSION" \
      --addr tcp://0.0.0.0:$BUILDKIT_PORT
      copa patch \
      -i docker.io/library/nginx:1.21.6 \
      -r nginx.1.21.6.json \
      -t 1.21.6-patched \
      -a tcp://0.0.0.0:$BUILDKIT_PORT
      +

      In either case, copa is non-destructive and exports a new image with the specified 1.21.6-patched label to the local Docker daemon.

      +
      +

      NOTE: if you're running this sample against an image from a private registry instead, ensure that the credentials are configured in the default Docker config.json before running copa patch, -for example, via sudo docker login -u <user> -p <password> <registry>.

    4. Scan the patched image and verify that the vulnerabilities have been patched:

      trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched

      You can also inspect the structure of the patched image with docker history to see the new patch layer appended to the image:

      $ docker history docker.io/library/nginx:1.21.6-patched
      IMAGE CREATED CREATED BY SIZE COMMENT
      a372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins… 26.1MB buildkit.exporter.image.v0
      <missing> 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0
      <missing> 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0
      <missing> 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0
      <missing> 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0
      <missing> 3 months ago COPY 30-tune-worker-processes.sh /docker-ent… 4.61kB buildkit.dockerfile.v0
      <missing> 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent… 1.04kB buildkit.dockerfile.v0
      <missing> 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker… 1.96kB buildkit.dockerfile.v0
      <missing> 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0
      <missing> 3 months ago RUN /bin/sh -c set -x && addgroup --syst… 61.1MB buildkit.dockerfile.v0
      <missing> 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0
      <missing> 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0
      <missing> 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0
      <missing> 3 months ago LABEL maintainer=NGINX Docker Maintainers <d… 0B buildkit.dockerfile.v0
      <missing> 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
      <missing> 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55… 80.4MB
    5. Run the container to verify that the image has no regressions:

      $ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched
      /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
      /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
      /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
      10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
      10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
      /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
      /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
      /docker-entrypoint.sh: Configuration complete; ready for start up
      2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method
      2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2
      2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
      2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2
      2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
      2022/05/16 18:00:17 [notice] 1#1: start worker processes
      2022/05/16 18:00:17 [notice] 1#1: start worker process 31
      2022/05/16 18:00:17 [notice] 1#1: start worker process 32
      2022/05/16 18:00:17 [notice] 1#1: start worker process 33
      2022/05/16 18:00:17 [notice] 1#1: start worker process 34
      2022/05/16 18:00:17 [notice] 1#1: start worker process 35
      2022/05/16 18:00:17 [notice] 1#1: start worker process 36
      2022/05/16 18:00:17 [notice] 1#1: start worker process 37
      2022/05/16 18:00:17 [notice] 1#1: start worker process 38
      2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received
      2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received

      You can stop the container by opening a new shell instance and running: docker stop nginx-test

    - - +for example, via sudo docker login -u <user> -p <password> <registry>.

    +
    +
  3. +
  4. +

    Scan the patched image and verify that the vulnerabilities have been patched:

    +
    trivy image --vuln-type os --ignore-unfixed docker.io/library/nginx:1.21.6-patched
    +

    You can also inspect the structure of the patched image with docker history to see the new patch layer appended to the image:

    +
    $ docker history docker.io/library/nginx:1.21.6-patched
    IMAGE CREATED CREATED BY SIZE COMMENT
    a372df41e06d 1 minute ago mount / from exec sh -c apt install --no-ins… 26.1MB buildkit.exporter.image.v0
    <missing> 3 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0
    <missing> 3 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0
    <missing> 3 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0
    <missing> 3 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0
    <missing> 3 months ago COPY 30-tune-worker-processes.sh /docker-ent… 4.61kB buildkit.dockerfile.v0
    <missing> 3 months ago COPY 20-envsubst-on-templates.sh /docker-ent… 1.04kB buildkit.dockerfile.v0
    <missing> 3 months ago COPY 10-listen-on-ipv6-by-default.sh /docker… 1.96kB buildkit.dockerfile.v0
    <missing> 3 months ago COPY docker-entrypoint.sh / # buildkit 1.2kB buildkit.dockerfile.v0
    <missing> 3 months ago RUN /bin/sh -c set -x && addgroup --syst… 61.1MB buildkit.dockerfile.v0
    <missing> 3 months ago ENV PKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0
    <missing> 3 months ago ENV NJS_VERSION=0.7.0 0B buildkit.dockerfile.v0
    <missing> 3 months ago ENV NGINX_VERSION=1.20.2 0B buildkit.dockerfile.v0
    <missing> 3 months ago LABEL maintainer=NGINX Docker Maintainers <d… 0B buildkit.dockerfile.v0
    <missing> 4 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
    <missing> 4 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55… 80.4MB
    +
  5. +
  6. +

    Run the container to verify that the image has no regressions:

    +
    $ docker run -it --rm --name nginx-test docker.io/library/nginx:1.21.6-patched
    /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
    /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
    10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
    10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
    /docker-entrypoint.sh: Configuration complete; ready for start up
    2022/05/16 18:00:17 [notice] 1#1: using the "epoll" event method
    2022/05/16 18:00:17 [notice] 1#1: nginx/1.20.2
    2022/05/16 18:00:17 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
    2022/05/16 18:00:17 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2
    2022/05/16 18:00:17 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
    2022/05/16 18:00:17 [notice] 1#1: start worker processes
    2022/05/16 18:00:17 [notice] 1#1: start worker process 31
    2022/05/16 18:00:17 [notice] 1#1: start worker process 32
    2022/05/16 18:00:17 [notice] 1#1: start worker process 33
    2022/05/16 18:00:17 [notice] 1#1: start worker process 34
    2022/05/16 18:00:17 [notice] 1#1: start worker process 35
    2022/05/16 18:00:17 [notice] 1#1: start worker process 36
    2022/05/16 18:00:17 [notice] 1#1: start worker process 37
    2022/05/16 18:00:17 [notice] 1#1: start worker process 38
    2022/05/16 18:00:17 [notice] 38#38: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 36#36: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 33#33: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 32#32: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 34#34: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 35#35: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 37#37: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 1#1: signal 28 (SIGWINCH) received
    2022/05/16 18:00:17 [notice] 31#31: signal 28 (SIGWINCH) received
    +

    You can stop the container by opening a new shell instance and running: docker stop nginx-test

    +
  7. +
\ No newline at end of file diff --git a/website/v0.5.x/release.html b/website/v0.5.x/release.html index b22fff12..4519db41 100644 --- a/website/v0.5.x/release.html +++ b/website/v0.5.x/release.html @@ -1,19 +1,24 @@ - + - -Release Process | Copacetic + +Release Process | Copacetic - - - + + + -
-
Version: v0.5.x

Release Process

Overview

The release process for Copacetic uses GoReleaser.

Once you are ready to cut a new release, checkout the release branch and tag it with the respective version.

```
git checkout <BRANCH NAME>
git pull origin <BRANCH NAME>
git tag -a <NEW VERSION> -m '<NEW VERSION>'
git push origin <NEW VERSION>
```

Publishing

  1. GoReleaser will create a new release, review and edit it at https://github.com/project-copacetic/copacetic/releases
  2. Review the respective copa-action image at: https://github.com/orgs/project-copacetic/packages/container/package/copa-action
- - +
Version: v0.5.x

Release Process

Overview

+

The release process for Copacetic uses GoReleaser.

+

Once you are ready to cut a new release, checkout the release branch and tag it with the respective version.

+
git checkout <BRANCH NAME>
git pull origin <BRANCH NAME>
git tag -a <NEW VERSION> -m '<NEW VERSION>'
git push origin <NEW VERSION>
+

Publishing

+
    +
  1. GoReleaser will create a new release, review and edit it at https://github.com/project-copacetic/copacetic/releases
  2. +
  3. Review the respective copa-action image at: https://github.com/orgs/project-copacetic/packages/container/package/copa-action
  4. +
\ No newline at end of file diff --git a/website/v0.5.x/scanner-plugins.html b/website/v0.5.x/scanner-plugins.html index 4453593d..60b89fa1 100644 --- a/website/v0.5.x/scanner-plugins.html +++ b/website/v0.5.x/scanner-plugins.html @@ -1,19 +1,37 @@ - + - -Scanner Plugins | Copacetic + +Scanner Plugins | Copacetic - - - + + + -
-
Version: v0.5.x

Motivation

By default, copa uses Trivy to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners.

Starting with v0.5.0 and later, copa offers extensibility to support different vulnerability scanners. Plugin architecture allows users to use the vulnerability scanner of their choice to patch container images without having to modify copa's core codebase.

Usage

Scanner plugin binaries must be in $PATH, and should be prefixed with copa- and have executable permissions. Copa will automatically detect and use the scanner plugin if it is in $PATH.

For example, if you have a scanner plugin binary called copa-foo in $PATH, you can run copa with the following command:

copa patch --scanner foo --image $IMAGE ...

Scanner Plugins from the Community

If you have built a scanner plugin and would like to add it to this list, please submit a PR to update this section with your plugin.

note

If you have any issues with a specific plugin, please open an issue in the applicable plugin's repository.

Writing a Scanner Plugin

Please see instructions at Scanner Plugin Template for a template to get started with writing a scanner plugin.

Scanner Plugin Interface

note

alpha versions of the API are not guarenteed to be backwards compatible. Once the API graduates to beta and stable, it will be backwards compatible.

Scanner plugins must implement the following interface:

v1alpha1

type UpdateManifest struct {
// API version of the interface (e.g. v1alpha1)
APIVersion string `json:"apiVersion"`
// Metadata contains information about the OS and config
Metadata Metadata `json:"metadata"`
// Updates is a list of UpdatePackage that contains information about the package updates
Updates UpdatePackages `json:"updates"`
}

// UpdatePackages is a list of UpdatePackage
type UpdatePackages []UpdatePackage

// Metadata contains information about the OS and config
type Metadata struct {
OS OS `json:"os"`
Config Config `json:"config"`
}

type OS struct {
// OS Type (e.g. debian, alpine, etc.)
Type string `json:"type"`
// OS Version (e.g. 11.3)
Version string `json:"version"`
}

// Config contains information about the config
type Config struct {
// OS Architecture (e.g. amd64, arm64)
Arch string `json:"arch"`
}

// UpdatePackage contains information about the package update
type UpdatePackage struct {
// Package name
Name string `json:"name"`
// Installed version
InstalledVersion string `json:"installedVersion"`
// Fixed version
FixedVersion string `json:"fixedVersion"`
// Vulnerability ID
VulnerabilityID string `json:"vulnerabilityID"`
}

From the above, we can see that the plugin must return a JSON object via standard out with the following fields. For example:

{
"apiVersion": "v1alpha1",
"metadata": {
"os": {
"type": "debian",
"version": "11.3",
},
"config": {
"arch": "amd64"
}
},
"updates": [
{
"name": "libcurl4",
"installedVersion": "7.74.0-1.3+deb11u1",
"fixedVersion": "7.74.0-1.3+deb11u2",
"vulnerabilityID": "CVE-2021-22945"
}
]
}
- - +
Version: v0.5.x

Motivation

+

By default, copa uses Trivy to scan container images for vulnerabilities. However, we understand that different organizations have different requirements and may want to use different vulnerability scanners.

+

Starting with v0.5.0 and later, copa offers extensibility to support different vulnerability scanners. Plugin architecture allows users to use the vulnerability scanner of their choice to patch container images without having to modify copa's core codebase.

+

Usage

+

Scanner plugin binaries must be in $PATH, and should be prefixed with copa- and have executable permissions. Copa will automatically detect and use the scanner plugin if it is in $PATH.

+

For example, if you have a scanner plugin binary called copa-foo in $PATH, you can run copa with the following command:

+
copa patch --scanner foo --image $IMAGE ...
+

Scanner Plugins from the Community

+

If you have built a scanner plugin and would like to add it to this list, please submit a PR to update this section with your plugin.

+
note

If you have any issues with a specific plugin, please open an issue in the applicable plugin's repository.

+ +

Writing a Scanner Plugin

+

Please see instructions at Scanner Plugin Template for a template to get started with writing a scanner plugin.

+

Scanner Plugin Interface

+
note

alpha versions of the API are not guarenteed to be backwards compatible. Once the API graduates to beta and stable, it will be backwards compatible.

+

Scanner plugins must implement the following interface:

+

v1alpha1

+
type UpdateManifest struct {
// API version of the interface (e.g. v1alpha1)
APIVersion string `json:"apiVersion"`
// Metadata contains information about the OS and config
Metadata Metadata `json:"metadata"`
// Updates is a list of UpdatePackage that contains information about the package updates
Updates UpdatePackages `json:"updates"`
}

// UpdatePackages is a list of UpdatePackage
type UpdatePackages []UpdatePackage

// Metadata contains information about the OS and config
type Metadata struct {
OS OS `json:"os"`
Config Config `json:"config"`
}

type OS struct {
// OS Type (e.g. debian, alpine, etc.)
Type string `json:"type"`
// OS Version (e.g. 11.3)
Version string `json:"version"`
}

// Config contains information about the config
type Config struct {
// OS Architecture (e.g. amd64, arm64)
Arch string `json:"arch"`
}

// UpdatePackage contains information about the package update
type UpdatePackage struct {
// Package name
Name string `json:"name"`
// Installed version
InstalledVersion string `json:"installedVersion"`
// Fixed version
FixedVersion string `json:"fixedVersion"`
// Vulnerability ID
VulnerabilityID string `json:"vulnerabilityID"`
}
+

From the above, we can see that the plugin must return a JSON object via standard out with the following fields. For example:

+
{
"apiVersion": "v1alpha1",
"metadata": {
"os": {
"type": "debian",
"version": "11.3",
},
"config": {
"arch": "amd64"
}
},
"updates": [
{
"name": "libcurl4",
"installedVersion": "7.74.0-1.3+deb11u1",
"fixedVersion": "7.74.0-1.3+deb11u2",
"vulnerabilityID": "CVE-2021-22945"
}
]
}
\ No newline at end of file diff --git a/website/v0.5.x/troubleshooting.html b/website/v0.5.x/troubleshooting.html index 441c63aa..b1adbfaf 100644 --- a/website/v0.5.x/troubleshooting.html +++ b/website/v0.5.x/troubleshooting.html @@ -1,19 +1,30 @@ - + - -Troubleshooting | Copacetic + +Troubleshooting | Copacetic - - - + + + -
-
Version: v0.5.x

Troubleshooting

Filtering Vulnerabilities

You might want to filter/ignore some of the vulnerabilities while patching. To do so, you need to first filter those undesired vulnerabilities from your scanner output.

For Trivy, vulnerabilities can be filtered by the following 2 ways:

Rego Policy

An example rego file which demonstrates how to ignore certain Vulnerability IDs or Package Names:

$ cat trivy_ignore.rego

package trivy

import data.lib.trivy

default ignore = false


# Ignore the following Vulnerability IDs
ignore_vulnerability_ids := {
"CVE-2018-14618"
}
# Ignore the following Package Names
ignore_pkgs := {"bash", "vim"}


# For ignoring vulnID
ignore {
input.VulnerabilityID == ignore_vulnerability_ids[_]
}
# For ignoring pkgName
ignore {
input.PkgName == ignore_pkgs[_]
}

After adding the above rego file, run the image scan with the --ignore-policy flag followed by the file name to ignore them while scanning:

trivy image --ignore-policy trivy_ignore.rego ruby:2.4.0

In the above example, the vulnerability "CVE-2018-14618" and the packages "bash" & "vim" are ignored while scanning, and hence patching the image.

Ignore File

Use a .trivyignore file to list all the vulnerabilities you want to ignore.

Example:

$ cat .trivyignore

# Accept the risk
CVE-2018-14618

In the above example, the vulnerability CVE-2018-14618 is ignored while scanning, and hence while patching the image.

For a more detailed explanation on how to ignore certain vulnerabilities with Trivy, please refer to the official documentation here.

- - +
Version: v0.5.x

Troubleshooting

Filtering Vulnerabilities

+

You might want to filter/ignore some of the vulnerabilities while patching. To do so, you need to first filter those undesired vulnerabilities from your scanner output.

+

For Trivy, vulnerabilities can be filtered by the following 2 ways:

+

Rego Policy

+

An example rego file which demonstrates how to ignore certain Vulnerability IDs or Package Names:

+
$ cat trivy_ignore.rego

package trivy

import data.lib.trivy

default ignore = false


# Ignore the following Vulnerability IDs
ignore_vulnerability_ids := {
"CVE-2018-14618"
}
# Ignore the following Package Names
ignore_pkgs := {"bash", "vim"}


# For ignoring vulnID
ignore {
input.VulnerabilityID == ignore_vulnerability_ids[_]
}
# For ignoring pkgName
ignore {
input.PkgName == ignore_pkgs[_]
}

+

After adding the above rego file, run the image scan with the --ignore-policy flag followed by the file name to ignore them while scanning:

+
trivy image --ignore-policy trivy_ignore.rego ruby:2.4.0
+

In the above example, the vulnerability "CVE-2018-14618" and the packages "bash" & "vim" are ignored while scanning, and hence patching the image.

+

Ignore File

+

Use a .trivyignore file to list all the vulnerabilities you want to ignore.

+

Example:

+
$ cat .trivyignore

# Accept the risk
CVE-2018-14618
+

In the above example, the vulnerability CVE-2018-14618 is ignored while scanning, and hence while patching the image.

+

For a more detailed explanation on how to ignore certain vulnerabilities with Trivy, please refer to the official documentation here.

\ No newline at end of file