diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..7ac963bd --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,25 @@ +FROM mcr.microsoft.com/devcontainers/base:bullseye + +# Install basic software that enables other software to be installed +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get install -y --no-install-recommends \ + curl apt-transport-https ca-certificates \ + gnupg \ + pkg-config \ + zip unzip \ + python \ + git + +# Install Bazel +ARG BAZELISK_VERSION=v1.10.1 +ARG BAZELISK_DOWNLOAD_SHA=dev-mode +RUN curl -fSsL -o /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/${BAZELISK_VERSION}/bazelisk-linux-amd64 \ + && ([ "${BAZELISK_DOWNLOAD_SHA}" = "dev-mode" ] || echo "${BAZELISK_DOWNLOAD_SHA} */usr/local/bin/bazelisk" | sha256sum --check - ) \ + && chmod 0755 /usr/local/bin/bazelisk \ + && ln -sf /usr/local/bin/bazelisk /usr/local/bin/bazel + +# Clean extraneous packages and files +RUN apt-get autoremove -y \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* + diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..879de62f --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,33 @@ +{ + "name": "Bazel (Community)", + "build": { + "dockerfile": "Dockerfile", + "args": { + "BAZELISK_VERSION": "v1.10.1", + "BAZELISK_DOWNLOAD_SHA": "4cb534c52cdd47a6223d4596d530e7c9c785438ab3b0a49ff347e991c210b2cd" + } + }, + "remoteUser": "vscode", + "features": { + "ghcr.io/devcontainers/features/common-utils:1": { + "installZsh": false, + "installOhMyZsh": false, + "upgradePackages": true + }, + "ghcr.io/devcontainers/features/go:1": {}, + "ghcr.io/devcontainers/features/node:1": {} + }, + + "settings": { + }, + + "customizations": { + "vscode": { + "extensions": [ + "dbaeumer.vscode-eslint", + "bazelbuild.vscode-bazel", + "golang.go" + ] + } + } +} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e2dbe2ac..b592d4e3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,7 +29,7 @@ jobs: files: | bazel-bin/chrome-ssh-agent.zip - name: Publish to Webstore - uses: mnao305/chrome-extension-upload@3.0.0 + uses: mnao305/chrome-extension-upload@v4.0.0 with: file-path;: bazel-bin/chrome-ssh-agent.zip extension-id: eechpbnaifiimgajnomdipfaamobdfha diff --git a/WORKSPACE b/WORKSPACE index e8607cbb..55b2143f 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -8,18 +8,14 @@ load( http_archive( name = "io_bazel_rules_go", - patch_args = ["-p1"], - patches = [ - "//:patches/rules_go-wasm.patch", - ], - sha256 = "16e9fca53ed6bd4ff4ad76facc9b7b651a89db1689a2877d6fd7b82aa824e366", - urls = ["https://github.com/bazelbuild/rules_go/releases/download/v0.34.0/rules_go-v0.34.0.zip"], + sha256 = "56d8c5a5c91e1af73eca71a6fab2ced959b67c86d12ba37feedb0a2dfea441a6", + urls = ["https://github.com/bazelbuild/rules_go/releases/download/v0.37.0/rules_go-v0.37.0.zip"], ) http_archive( name = "bazel_gazelle", - sha256 = "501deb3d5695ab658e82f6f6f549ba681ea3ca2a5fb7911154b5aa45596183fa", - urls = ["https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.26.0/bazel-gazelle-v0.26.0.tar.gz"], + sha256 = "448e37e0dbf61d6fa8f00aaa12d191745e14f07c31cabfa731f0c8e8a4f41b97", + urls = ["https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.28.0/bazel-gazelle-v0.28.0.tar.gz"], ) http_archive( @@ -30,22 +26,22 @@ http_archive( http_archive( name = "bazel_skylib", - sha256 = "f7be3474d42aae265405a592bb7da8e171919d74c16f082a5457840f06054728", - urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz"], + sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506", + urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz"], ) http_archive( name = "rules_pkg", - sha256 = "8a298e832762eda1830597d64fe7db58178aa84cd5926d76d5b744d6558941c2", - urls = ["https://github.com/bazelbuild/rules_pkg/releases/download/0.7.0/rules_pkg-0.7.0.tar.gz"], + sha256 = "eea0f59c28a9241156a47d7a8e32db9122f3d50b505fae0f33de6ce4d9b61834", + urls = ["https://github.com/bazelbuild/rules_pkg/releases/download/0.8.0/rules_pkg-0.8.0.tar.gz"], ) http_archive( name = "rules_proto", - sha256 = "e017528fd1c91c5a33f15493e3a398181a9e821a804eb7ff5acdd1d2d6c2b18d", - strip_prefix = "rules_proto-4.0.0-3.20.0", + sha256 = "80d3a4ec17354cccc898bfe32118edd934f851b03029d63ef3fc7c8663a7415c", + strip_prefix = "rules_proto-5.3.0-21.5", urls = [ - "https://github.com/bazelbuild/rules_proto/archive/refs/tags/4.0.0-3.20.0.tar.gz", + "https://github.com/bazelbuild/rules_proto/archive/refs/tags/5.3.0-21.5.tar.gz", ], ) @@ -113,11 +109,11 @@ go_rules_dependencies() go_register_toolchains( nogo = "@//:chrome_ssh_agent_nogo", - version = "1.18", + version = "1.19", ) # Gazelle dependency management support -load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies") +load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository") # Pull in external dependencies: # gazelle:repository_macro go_deps.bzl%go_dependencies diff --git a/go.mod b/go.mod index a7587a2f..c9254ee6 100644 --- a/go.mod +++ b/go.mod @@ -3,23 +3,24 @@ module github.com/google/chrome-ssh-agent go 1.18 require ( - github.com/google/go-cmp v0.5.6 + github.com/google/go-cmp v0.5.9 // https://github.com/tebeka/selenium/commit/e617f9870cec59a6f6e234017e45d36ef0444a04 required to support CRX3 format github.com/tebeka/selenium v0.9.10-0.20211105214847-e9100b7f5ac1 github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a - golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d + golang.org/x/crypto v0.4.0 ) require ( - github.com/bazelbuild/rules_go v0.34.0 - github.com/norunners/vert v0.0.0-20211229045251-b4c39e2856da - golang.org/x/tools v0.0.0-20190624190245-7f2218787638 + github.com/bazelbuild/rules_go v0.37.0 + github.com/norunners/vert v0.0.0-20221203075838-106a353d42dd + golang.org/x/tools v0.4.0 ) require ( github.com/blang/semver v3.5.1+incompatible // indirect - github.com/golang/protobuf v1.3.4 // indirect + github.com/golang/protobuf v1.5.2 // indirect github.com/mediabuyerbot/go-crx3 v1.3.1 // indirect - golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect + golang.org/x/sys v0.3.0 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/protobuf v1.28.1 // indirect ) diff --git a/go.sum b/go.sum index 9b0e2551..a43d4460 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/bazelbuild/rules_go v0.34.0 h1:cmObMtgIOaEU944SqXtJ9DnlS8IPGGa7pdRnsrpQzXM= github.com/bazelbuild/rules_go v0.34.0/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M= +github.com/bazelbuild/rules_go v0.37.0 h1:vbnESGv/t2WgGEbXatwbXAS95dTx93Lv6Uh5QkVF13s= +github.com/bazelbuild/rules_go v0.37.0/go.mod h1:TMHmtfpvyfsxaqfL9WnahCsXMWDMICTw7XeK9yVb+YU= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= @@ -50,12 +52,18 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v27 v27.0.4/go.mod h1:/0Gr8pJ55COkmv+S/yPKCczSkUPIM/LnFyubufRNIS0= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -92,6 +100,8 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/norunners/vert v0.0.0-20211229045251-b4c39e2856da h1:wowEKpELVGcI7utqSzrCbdbSfq4BSfY1wLeQsW6R/as= github.com/norunners/vert v0.0.0-20211229045251-b4c39e2856da/go.mod h1:8iuQLyTSvuzwy6R6l6w6J+i9c/6xPEVoVdcMz9E8FEw= +github.com/norunners/vert v0.0.0-20221203075838-106a353d42dd h1:tHn7K76q9eJ2rXLH/OoxHkdprM3l2A+0kdxOrKYcV7U= +github.com/norunners/vert v0.0.0-20221203075838-106a353d42dd/go.mod h1:8iuQLyTSvuzwy6R6l6w6J+i9c/6xPEVoVdcMz9E8FEw= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -141,6 +151,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= +golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= @@ -164,6 +176,7 @@ golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= +golang.org/x/net v0.3.0 h1:VWL6FNY2bEEmsGVKabSlHu5Irp34xmMRoqb/9lF9lxk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -185,7 +198,10 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -203,6 +219,8 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624190245-7f2218787638 h1:uIfBkD8gLczr4XDgYpt/qJYds2YJwZRNw4zs7wSnNhk= golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -222,6 +240,10 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/go/background/main.go b/go/background/main.go index d4ceeca8..c569707f 100644 --- a/go/background/main.go +++ b/go/background/main.go @@ -55,12 +55,9 @@ func (a *background) Name() string { } func (a *background) Init(ctx jsutil.AsyncContext, cleanup *jsutil.CleanupFuncs) error { - jsutil.Log("Cleaning up old data") - a.manager.CleanupOldData(ctx) - - jsutil.Log("Loading keys from session") - if err := a.manager.LoadFromSession(ctx); err != nil { - jsutil.LogError("failed to load keys into agent: %v", err) + jsutil.Log("Initializing manager") + if err := a.manager.Init(ctx); err != nil { + jsutil.LogError("failed to initialize manager: %v", err) } jsutil.LogDebug("Attaching event handlers") diff --git a/go/dom/dom.go b/go/dom/dom.go index b8a47670..ade5b383 100644 --- a/go/dom/dom.go +++ b/go/dom/dom.go @@ -172,6 +172,11 @@ func TextContent(o js.Value) string { return o.Get("textContent").String() } +// Checked returns true if a checkbox is checked, and false otherwise. +func Checked(o js.Value) bool { + return o.Get("checked").Truthy() +} + // AppendChild adds the child object. If non-nil, the populate() function is // invoked on the child to initialize it. func AppendChild(parent, child js.Value, populate func(child js.Value)) { diff --git a/go/keys/client.go b/go/keys/client.go index 7ef4390b..4f97a1e1 100644 --- a/go/keys/client.go +++ b/go/keys/client.go @@ -85,9 +85,10 @@ type rspLoaded struct { } type msgAdd struct { - Type int `js:"type"` - Name string `js:"name"` - PEMPrivateKey string `js:"pemPrivateKey"` + Type int `js:"type"` + Name string `js:"name"` + PEMPrivateKey string `js:"pemPrivateKey"` + Options KeyOptions `js:"options"` } type rspAdd struct { @@ -198,7 +199,7 @@ func (s *Server) OnMessage(ctx jsutil.AsyncContext, headerObj js.Value, sender j return s.makeErrorResponse(fmt.Errorf("failed to parse Add message: %v", err)) } jsutil.LogDebug("Server.OnMessage(Add req): name=%s", m.Name) - err := s.mgr.Add(ctx, m.Name, m.PEMPrivateKey) + err := s.mgr.Add(ctx, m.Name, m.PEMPrivateKey, m.Options) rsp := rspAdd{ Type: msgTypeAddRsp, Err: makeErrStr(err), @@ -294,11 +295,12 @@ func (c *client) Loaded(ctx jsutil.AsyncContext) ([]*LoadedKey, error) { } // Add implements Manager.Add. -func (c *client) Add(ctx jsutil.AsyncContext, name string, pemPrivateKey string) error { +func (c *client) Add(ctx jsutil.AsyncContext, name string, pemPrivateKey string, options KeyOptions) error { var msg msgAdd msg.Type = msgTypeAdd msg.Name = name msg.PEMPrivateKey = pemPrivateKey + msg.Options = options jsutil.LogDebug("Client.Add(req): name=%s", msg.Name) rspObj, err := c.msg.Send(ctx, vert.ValueOf(msg).JSValue()) jsutil.LogDebug("Client.Add(rsp)") diff --git a/go/keys/client_test.go b/go/keys/client_test.go index b92049d3..fb994e72 100644 --- a/go/keys/client_test.go +++ b/go/keys/client_test.go @@ -28,6 +28,7 @@ type dummyManager struct { ID ID Name string PEMPrivateKey string + Options KeyOptions Passphrase string ConfiguredKeys []*ConfiguredKey LoadedKeys []*LoadedKey @@ -39,9 +40,10 @@ func (m *dummyManager) Configured(ctx jsutil.AsyncContext) ([]*ConfiguredKey, er return m.ConfiguredKeys, m.Err } -func (m *dummyManager) Add(ctx jsutil.AsyncContext, name string, pemPrivateKey string) error { +func (m *dummyManager) Add(ctx jsutil.AsyncContext, name string, pemPrivateKey string, options KeyOptions) error { m.Name = name m.PEMPrivateKey = pemPrivateKey + m.Options = options return m.Err } @@ -112,7 +114,7 @@ func TestClientServerAdd(t *testing.T) { mgr.Err = wantErr - err := cli.Add(ctx, wantName, wantPrivateKey) + err := cli.Add(ctx, wantName, wantPrivateKey, KeyOptions{}) if diff := cmp.Diff(mgr.Name, wantName); diff != "" { t.Errorf("incorrect name; -got +want: %s", diff) } diff --git a/go/keys/manager.go b/go/keys/manager.go index 5ce9ddeb..43b55cea 100644 --- a/go/keys/manager.go +++ b/go/keys/manager.go @@ -54,6 +54,9 @@ type ConfiguredKey struct { // Encrypted indicates if the key is encrypted and requires a passphrase // to load. Encrypted bool `js:"encrypted"` + // AutoLoad indicates if the key will be automatically loaded on startup. + // Only applies to unencrypted keys. + AutoLoad bool `js:"autoload"` } // LoadedKey is a key loaded into the agent. @@ -102,6 +105,13 @@ func (k *LoadedKey) ID() ID { return ID(strings.TrimPrefix(k.Comment, commentPrefix)) } +// KeyOptions are additional options that can be configured for a key. +type KeyOptions struct { + // AutoLoad indicates that the key will be automatically loaded at + // startup. Only applies to unencrypted keys. + AutoLoad bool `js:"autoLoad"` +} + // Manager provides an API for managing configured keys and loading them into // an SSH agent. type Manager interface { @@ -110,7 +120,7 @@ type Manager interface { // Add configures a new key. name is a human-readable name describing // the key, and pemPrivateKey is the PEM-encoded private key. - Add(ctx jsutil.AsyncContext, name string, pemPrivateKey string) error + Add(ctx jsutil.AsyncContext, name string, pemPrivateKey string, options KeyOptions) error // Remove removes the key with the specified ID. // @@ -143,6 +153,7 @@ func NewManager(agt agent.Agent, syncStorage, sessionStorage storage.Area) *Defa sessionStorage: sessionStorage, storedKeys: storage.NewTyped[storedKey](syncStorage, storedKeyPrefixes), sessionKeys: storage.NewTyped[sessionKey](sessionStorage, sessionKeyPrefixes), + lifecycleState: storage.NewValue[lifecycleState](sessionStorage, lifecycleStatePrefixes), } } @@ -153,6 +164,7 @@ type DefaultManager struct { sessionStorage storage.Area storedKeys *storage.Typed[storedKey] sessionKeys *storage.Typed[sessionKey] + lifecycleState *storage.Value[lifecycleState] } // storedKey is the raw object stored in persistent storage for a configured @@ -161,6 +173,7 @@ type storedKey struct { ID string `js:"id"` Name string `js:"name"` PEMPrivateKey string `js:"pemPrivateKey"` + AutoLoad bool `js:"autoLoad"` } // EncryptedPKCS8 determines if the private key is an encrypted PKCS#8 formatted @@ -220,7 +233,17 @@ type sessionKey struct { PrivateKey string `js:"privateKey"` } +// lifecycleState is the raw object stored in persistent storage to keep track +// of the extension's lifecycle. +type lifecycleState struct { + sessionStarted bool `js:"sessionStarted"` +} + var ( + // lifecycleStatePrefixes are the prefixes used to store lifecycle + // state in storage. + lifecycleStatePrefixes = []string{"managerLifecycle"} + // storedKeyPrefix is the prefix for keys stored in persistent storage. storedKeyPrefixes = []string{"key"} // sessionKeyPrefix is the prefix for key material stored in-memory @@ -289,7 +312,7 @@ var ( ) // Add implements Manager.Add. -func (m *DefaultManager) Add(ctx jsutil.AsyncContext, name string, pemPrivateKey string) error { +func (m *DefaultManager) Add(ctx jsutil.AsyncContext, name string, pemPrivateKey string, options KeyOptions) error { if name == "" { return fmt.Errorf("%w: name must not be empty", errInvalidName) } @@ -303,6 +326,7 @@ func (m *DefaultManager) Add(ctx jsutil.AsyncContext, name string, pemPrivateKey ID: i.String(), Name: name, PEMPrivateKey: pemPrivateKey, + AutoLoad: options.AutoLoad, } return m.storedKeys.Write(ctx, sk) } @@ -339,9 +363,42 @@ var ( errMarshalFailed = errors.New("key marshalling failed") ) -// CleanupOldData removes storage data that is no longer required. -func (m *DefaultManager) CleanupOldData(ctx jsutil.AsyncContext) { - jsutil.LogDebug("DefaultManager.CleanupOldData: Cleaning up stored keys") +// Init initializes the manager state from storage. +func (m *DefaultManager) Init(ctx jsutil.AsyncContext) error { + jsutil.Log("DefaultManager.Init: cleaning up old data") + m.cleanupOldData(ctx) + + lifecycle, err := m.lifecycleState.Get(ctx) + if err != nil { + return fmt.Errorf("failed to read current lifecycle state: %v", err) + } + + if !lifecycle.sessionStarted { + jsutil.Log("DefaultManager.Init: auto-loading keys") + if err := m.autoLoadKeys(ctx); err != nil { + // Continue on error. + jsutil.LogError("failed to auto-load keys: %v", err) + } + + // Startup successful; update lifecycle. + lifecycle.sessionStarted = true + if err := m.lifecycleState.Set(ctx, lifecycle); err != nil { + return fmt.Errorf("failed to update lifecycle state: %v", err) + } + } + + jsutil.Log("DefaultManager.Init: loading keys from session") + if err := m.loadFromSession(ctx); err != nil { + // Continue on error. + jsutil.LogError("failed to load keys into agent: %v", err) + } + + return nil +} + +// cleanupOldData removes storage data that is no longer required. +func (m *DefaultManager) cleanupOldData(ctx jsutil.AsyncContext) { + jsutil.LogDebug("DefaultManager.cleanupOldData: Cleaning up stored keys") areas := []storage.Area{ m.syncStorage, @@ -360,8 +417,13 @@ func (m *DefaultManager) CleanupOldData(ctx jsutil.AsyncContext) { } } -// LoadFromSession loads all keys for the current session into the agent. -func (m *DefaultManager) LoadFromSession(ctx jsutil.AsyncContext) error { +// autoLoadKeys loads all keys that were configured to auto-load. +func (m *DefaultManager) autoLoadKeys(ctx jsutil.AsyncContext) error { + +} + +// loadFromSession loads all keys for the current session into the agent. +func (m *DefaultManager) loadFromSession(ctx jsutil.AsyncContext) error { // Read session keys. We'll load these into the agent. jsutil.LogDebug("DefaultManager.LoadFromSession: Read session keys") sessionKeys, err := m.sessionKeys.ReadAll(ctx) diff --git a/go/keys/manager_test.go b/go/keys/manager_test.go index 99cdac8b..a7bf4611 100644 --- a/go/keys/manager_test.go +++ b/go/keys/manager_test.go @@ -48,7 +48,7 @@ var ( func newTestManager(ctx jsutil.AsyncContext, agent agent.Agent, syncStorage, sessionStorage storage.Area, keys []*initialKey) (*DefaultManager, error) { mgr := NewManager(agent, syncStorage, sessionStorage) for _, k := range keys { - if err := mgr.Add(ctx, k.Name, k.PEMPrivateKey); err != nil { + if err := mgr.Add(ctx, k.Name, k.PEMPrivateKey, KeyOptions{}); err != nil { return nil, err } @@ -125,7 +125,7 @@ func TestAdd(t *testing.T) { } // Add the key. - err = mgr.Add(ctx, tc.name, tc.pemPrivateKey) + err = mgr.Add(ctx, tc.name, tc.pemPrivateKey, KeyOptions{}) if diff := cmp.Diff(err, tc.wantErr, cmpopts.EquateErrors()); diff != "" { t.Errorf("incorrect error; -got +want: %s", diff) } diff --git a/go/optionsui/ui.go b/go/optionsui/ui.go index 27e58eb6..609efa0f 100644 --- a/go/optionsui/ui.go +++ b/go/optionsui/ui.go @@ -120,12 +120,12 @@ func (u *UI) setError(err error) { // and the corresponding private key. If the user continues, the key is // added to the manager. func (u *UI) add(ctx jsutil.AsyncContext, evt dom.Event) { - ok, name, privateKey := u.promptAdd(ctx) + ok, name, privateKey, autoLoad := u.promptAdd(ctx) if !ok { return } - if err := u.mgr.Add(ctx, name, privateKey); err != nil { + if err := u.mgr.Add(ctx, name, privateKey, autoLoad); err != nil { u.setError(fmt.Errorf("failed to add key: %v", err)) return } @@ -135,11 +135,12 @@ func (u *UI) add(ctx jsutil.AsyncContext, evt dom.Event) { } // promptAdd displays a dialog prompting the user for a name and private key. -func (u *UI) promptAdd(ctx jsutil.AsyncContext) (ok bool, name, privateKey string) { +func (u *UI) promptAdd(ctx jsutil.AsyncContext) (ok bool, name, privateKey string, options keys.KeyOptions) { dialog := dom.NewDialog(u.dom.GetElement("addDialog")) form := u.dom.GetElement("addForm") nameField := u.dom.GetElement("addName") keyField := u.dom.GetElement("addKey") + autoLoadField := u.dom.GetElement("addAutoLoad") cancel := u.dom.GetElement("addCancel") sig := newSignal() @@ -148,6 +149,7 @@ func (u *UI) promptAdd(ctx jsutil.AsyncContext) (ok bool, name, privateKey strin ok = true name = dom.Value(nameField) privateKey = dom.Value(keyField) + options.AutoLoad = dom.Checked(autoLoadField) dialog.Close() sig.Notify() })) diff --git a/go/storage/BUILD.bazel b/go/storage/BUILD.bazel index 8c4d3293..4a127a2c 100644 --- a/go/storage/BUILD.bazel +++ b/go/storage/BUILD.bazel @@ -10,6 +10,7 @@ go_library( "raw.go", "typed.go", "view.go", + "value.go", ], importpath = "github.com/google/chrome-ssh-agent/go/storage", visibility = ["//visibility:public"], @@ -27,6 +28,7 @@ go_wasm_test( "raw_test.go", "typed_test.go", "view_test.go", + "value_test.go", ], embed = [":storage"], node_deps = [ diff --git a/go/storage/value.go b/go/storage/value.go new file mode 100644 index 00000000..f2b476c7 --- /dev/null +++ b/go/storage/value.go @@ -0,0 +1,79 @@ +//go:build js + +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package storage + +import ( + "errors" + "syscall/js" + + "github.com/google/chrome-ssh-agent/go/jsutil" + "github.com/norunners/vert" +) + +const ( + // valueKey is the key under which the singular value is stored. + valueKey = "current" +) + +var ( + errParse = errors.New("parse failed") +) + +// Value reads and writes a singular value. +type Value[V any] struct { + store Area +} + +// NewValue returns a new Value using the underlying persistent store. +// keyPrefix is the prefix used to distinguish values from others in the same +// underlying store; multiple may be supplied to support migration scenarios. +func NewValue[V any](store Area, keyPrefix []string) *Value[V] { + return &Value[V]{ + store: NewView(keyPrefix, store), + } +} + +// Get reads the current value from storage. If it doesn't exist, the zero +// value is returned. +func (v *Value[V]) Get(ctx jsutil.AsyncContext) (V, error) { + var zero V + + data, err := v.store.Get(ctx) + if err != nil { + return zero, err + } + + val, present := data[valueKey] + if !present { + return zero, nil + } + + var tv V + if err := vert.ValueOf(val).AssignTo(&tv); err != nil { + return zero, errParse + } + + return tv, nil +} + +// Set writes a new value to storage. +func (v *Value[V]) Set(ctx jsutil.AsyncContext, val V) error { + data := map[string]js.Value{ + valueKey: vert.ValueOf(val).JSValue(), + } + return v.store.Set(ctx, data) +} diff --git a/go/storage/value_test.go b/go/storage/value_test.go new file mode 100644 index 00000000..9986e2a6 --- /dev/null +++ b/go/storage/value_test.go @@ -0,0 +1,124 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package storage + +import ( + "syscall/js" + "testing" + + "github.com/google/chrome-ssh-agent/go/jsutil" + jut "github.com/google/chrome-ssh-agent/go/jsutil/testing" + st "github.com/google/chrome-ssh-agent/go/storage/testing" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/norunners/vert" +) + +func TestValueGet(t *testing.T) { + testcases := []struct { + description string + init map[string]js.Value + want myStruct + wantErr error + }{ + { + description: "no value present", + want: myStruct{}, + }, + { + description: "parse values", + init: map[string]js.Value{ + testKeyPrefix + "." + valueKey: vert.ValueOf(&myStruct{IntField: 42}).JSValue(), + }, + want: myStruct{IntField: 42}, + }, + { + description: "error on unparseable value", + init: map[string]js.Value{ + testKeyPrefix + "." + valueKey: js.ValueOf(42), + }, + wantErr: errParse, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + jut.DoSync(func(ctx jsutil.AsyncContext) { + store := NewRaw(st.NewMemArea()) + if err := store.Set(ctx, tc.init); err != nil { + t.Fatalf("Set failed: %v", err) + } + + vs := NewValue[myStruct](store, testKeyPrefixes) + + got, err := vs.Get(ctx) + if diff := cmp.Diff(got, tc.want); diff != "" { + t.Errorf("incorrect result: -got +want: %s", diff) + } + if diff := cmp.Diff(err, tc.wantErr, cmpopts.EquateErrors()); diff != "" { + t.Errorf("incorrect error: -got +want: %s", diff) + } + }) + }) + } +} + +func TestValueSet(t *testing.T) { + testcases := []struct { + description string + init map[string]js.Value + write myStruct + want myStruct + }{ + { + description: "write initial value", + write: myStruct{IntField: 100}, + want: myStruct{IntField: 100}, + }, + { + description: "overwrite previous value", + init: map[string]js.Value{ + testKeyPrefix + "." + valueKey: vert.ValueOf(&myStruct{StringField: "foo"}).JSValue(), + }, + write: myStruct{IntField: 42}, + want: myStruct{IntField: 42}, + }, + } + + for _, tc := range testcases { + t.Run(tc.description, func(t *testing.T) { + jut.DoSync(func(ctx jsutil.AsyncContext) { + store := NewRaw(st.NewMemArea()) + if err := store.Set(ctx, tc.init); err != nil { + t.Fatalf("Set failed: %v", err) + } + + vs := NewValue[myStruct](store, testKeyPrefixes) + + if err := vs.Set(ctx, tc.write); err != nil { + t.Fatalf("Value.Set failed: %v", err) + } + + got, err := vs.Get(ctx) + if err != nil { + t.Fatalf("Value.Get failed: %v", err) + } + if diff := cmp.Diff(got, tc.want); diff != "" { + t.Errorf("incorrect result: -got +want: %s", diff) + } + }) + }) + } +} diff --git a/go_deps.bzl b/go_deps.bzl index f26415ff..8b1effac 100644 --- a/go_deps.bzl +++ b/go_deps.bzl @@ -1,6 +1,24 @@ load("@bazel_gazelle//:deps.bzl", "go_repository") def go_dependencies(): + go_repository( + name = "com_github_yuin_goldmark", + importpath = "github.com/yuin/goldmark", + sum = "h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=", + version = "v1.4.13", + ) + go_repository( + name = "org_golang_google_protobuf", + importpath = "google.golang.org/protobuf", + sum = "h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=", + version = "v1.28.1", + ) + go_repository( + name = "org_golang_x_mod", + importpath = "golang.org/x/mod", + sum = "h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=", + version = "v0.7.0", + ) go_repository( name = "co_honnef_go_tools", importpath = "honnef.co/go/tools", @@ -35,8 +53,8 @@ def go_dependencies(): go_repository( name = "com_github_bazelbuild_rules_go", importpath = "github.com/bazelbuild/rules_go", - sum = "h1:cmObMtgIOaEU944SqXtJ9DnlS8IPGGa7pdRnsrpQzXM=", - version = "v0.34.0", + sum = "h1:vbnESGv/t2WgGEbXatwbXAS95dTx93Lv6Uh5QkVF13s=", + version = "v0.37.0", ) go_repository( @@ -199,14 +217,14 @@ def go_dependencies(): go_repository( name = "com_github_golang_mock", importpath = "github.com/golang/mock", - sum = "h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=", - version = "v1.3.1", + sum = "h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=", + version = "v1.6.0", ) go_repository( name = "com_github_golang_protobuf", importpath = "github.com/golang/protobuf", - sum = "h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=", - version = "v1.3.4", + sum = "h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=", + version = "v1.5.2", ) go_repository( name = "com_github_google_btree", @@ -218,8 +236,8 @@ def go_dependencies(): go_repository( name = "com_github_google_go_cmp", importpath = "github.com/google/go-cmp", - sum = "h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=", - version = "v0.5.6", + sum = "h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=", + version = "v0.5.9", ) go_repository( name = "com_github_google_go_github_v27", @@ -410,11 +428,8 @@ def go_dependencies(): name = "com_github_norunners_vert", importpath = "github.com/norunners/vert", patch_args = ["-p1"], - patches = [ - "//:patches/norunners-vert-go1.18.patch", - ], - sum = "h1:wowEKpELVGcI7utqSzrCbdbSfq4BSfY1wLeQsW6R/as=", - version = "v0.0.0-20211229045251-b4c39e2856da", + sum = "h1:tHn7K76q9eJ2rXLH/OoxHkdprM3l2A+0kdxOrKYcV7U=", + version = "v0.0.0-20221203075838-106a353d42dd", ) go_repository( @@ -671,21 +686,21 @@ def go_dependencies(): go_repository( name = "org_golang_google_genproto", importpath = "google.golang.org/genproto", - sum = "h1:UsSJe9fhWNSz6emfIGPpH5DF23t7ALo2Pf3sC+/hsdg=", - version = "v0.0.0-20190626174449-989357319d63", + sum = "h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=", + version = "v0.0.0-20200526211855-cb27e3aa2013", ) go_repository( name = "org_golang_google_grpc", importpath = "google.golang.org/grpc", - sum = "h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=", - version = "v1.21.1", + sum = "h1:fPVVDxY9w++VjTZsYvXWqEf9Rqar/e+9zYfxKK+W+YU=", + version = "v1.50.0", ) go_repository( name = "org_golang_x_crypto", importpath = "golang.org/x/crypto", - sum = "h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=", - version = "v0.0.0-20220622213112-05595931fe9d", + sum = "h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=", + version = "v0.4.0", ) go_repository( name = "org_golang_x_exp", @@ -715,8 +730,8 @@ def go_dependencies(): go_repository( name = "org_golang_x_net", importpath = "golang.org/x/net", - sum = "h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=", - version = "v0.0.0-20211112202133-69e39bad7dc2", + sum = "h1:VWL6FNY2bEEmsGVKabSlHu5Irp34xmMRoqb/9lF9lxk=", + version = "v0.3.0", ) go_repository( name = "org_golang_x_oauth2", @@ -728,26 +743,26 @@ def go_dependencies(): go_repository( name = "org_golang_x_sync", importpath = "golang.org/x/sync", - sum = "h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=", - version = "v0.0.0-20190423024810-112230192c58", + sum = "h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=", + version = "v0.1.0", ) go_repository( name = "org_golang_x_sys", importpath = "golang.org/x/sys", - sum = "h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=", - version = "v0.0.0-20210630005230-0f9fa26af87c", + sum = "h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=", + version = "v0.3.0", ) go_repository( name = "org_golang_x_term", importpath = "golang.org/x/term", - sum = "h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=", - version = "v0.0.0-20201126162022-7de9c90e9dd1", + sum = "h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=", + version = "v0.3.0", ) go_repository( name = "org_golang_x_text", importpath = "golang.org/x/text", - sum = "h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=", - version = "v0.3.6", + sum = "h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=", + version = "v0.5.0", ) go_repository( name = "org_golang_x_time", @@ -759,8 +774,8 @@ def go_dependencies(): go_repository( name = "org_golang_x_tools", importpath = "golang.org/x/tools", - sum = "h1:uIfBkD8gLczr4XDgYpt/qJYds2YJwZRNw4zs7wSnNhk=", - version = "v0.0.0-20190624190245-7f2218787638", + sum = "h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4=", + version = "v0.4.0", ) go_repository( name = "org_golang_x_xerrors", diff --git a/html/options.html b/html/options.html index 3a67c276..5fd65b44 100644 --- a/html/options.html +++ b/html/options.html @@ -53,6 +53,10 @@