From e842aa5f10fc8731c3a45ef50e79dd4b08c93458 Mon Sep 17 00:00:00 2001
From: Marcin Nowak-Liebiediew <marcin.liebiediew@dfinity.org>
Date: Tue, 9 Aug 2022 14:52:27 +0200
Subject: [PATCH] RUSTSEC-2020-0071: drop `chrono` dependency in `icx-asset`

this required a small rework, because it's not possible to obtain
completely sound local time offset value in multithreaded environment
- https://github.com/time-rs/time/issues/427
- https://github.com/time-rs/time/issues/500
- https://github.com/time-rs/time/issues/493
---
 Cargo.lock                                    | 42 +++----------------
 src/assets_canister/icx-asset/Cargo.toml      |  2 +-
 .../icx-asset/src/commands/list.rs            | 18 ++++----
 src/assets_canister/icx-asset/src/main.rs     | 41 +++++++++++++-----
 4 files changed, 47 insertions(+), 56 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 58956de1ca..86986a5ef7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -444,19 +444,6 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
-[[package]]
-name = "chrono"
-version = "0.4.20"
-source = "git+https://github.com/chronotope/chrono#0b7feacb5482076b4efe1b6bcf720abfc82eb476"
-dependencies = [
- "js-sys",
- "num-integer",
- "num-traits",
- "time 0.1.44",
- "wasm-bindgen",
- "winapi",
-]
-
 [[package]]
 name = "cipher"
 version = "0.3.0"
@@ -808,7 +795,7 @@ dependencies = [
  "tempfile",
  "term",
  "thiserror",
- "time 0.3.12",
+ "time",
  "tokio",
  "toml",
  "url",
@@ -1269,7 +1256,7 @@ checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
 dependencies = [
  "cfg-if 1.0.0",
  "libc",
- "wasi 0.11.0+wasi-snapshot-preview1",
+ "wasi",
 ]
 
 [[package]]
@@ -1706,7 +1693,6 @@ version = "0.20.0"
 dependencies = [
  "anyhow",
  "candid",
- "chrono",
  "clap",
  "delay",
  "garcon",
@@ -1721,6 +1707,7 @@ dependencies = [
  "serde_bytes",
  "serde_json",
  "thiserror",
+ "time",
  "tokio",
  "walkdir",
 ]
@@ -2019,7 +2006,7 @@ checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf"
 dependencies = [
  "libc",
  "log",
- "wasi 0.11.0+wasi-snapshot-preview1",
+ "wasi",
  "windows-sys",
 ]
 
@@ -3219,7 +3206,7 @@ dependencies = [
  "num-bigint 0.4.3",
  "num-traits",
  "thiserror",
- "time 0.3.12",
+ "time",
 ]
 
 [[package]]
@@ -3265,7 +3252,7 @@ dependencies = [
  "slog",
  "term",
  "thread_local",
- "time 0.3.12",
+ "time",
 ]
 
 [[package]]
@@ -3484,17 +3471,6 @@ dependencies = [
  "once_cell",
 ]
 
-[[package]]
-name = "time"
-version = "0.1.44"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
-dependencies = [
- "libc",
- "wasi 0.10.0+wasi-snapshot-preview1",
- "winapi",
-]
-
 [[package]]
 name = "time"
 version = "0.3.12"
@@ -3844,12 +3820,6 @@ dependencies = [
  "try-lock",
 ]
 
-[[package]]
-name = "wasi"
-version = "0.10.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
-
 [[package]]
 name = "wasi"
 version = "0.11.0+wasi-snapshot-preview1"
diff --git a/src/assets_canister/icx-asset/Cargo.toml b/src/assets_canister/icx-asset/Cargo.toml
index cce66bbeb2..9056202939 100644
--- a/src/assets_canister/icx-asset/Cargo.toml
+++ b/src/assets_canister/icx-asset/Cargo.toml
@@ -19,7 +19,6 @@ rust-version = "1.60.0"
 [dependencies]
 anyhow = "1.0.34"
 candid = "0.7.15"
-chrono = "0.4.19"
 clap = { version = "3.0.14", features = ["derive", "cargo"] }
 delay = "0.3.1"
 garcon = "0.2.2"
@@ -33,6 +32,7 @@ pem = "1.0.1"
 serde = "1.0.115"
 serde_bytes = "0.11.5"
 serde_json = "1.0.57"
+time = { version = "0.3.9", features = ["formatting", "local-offset"] }
 tokio = { version = "1.2.0", features = ["full", "rt"] }
 thiserror = "1.0.24"
 walkdir = "2.3.1"
diff --git a/src/assets_canister/icx-asset/src/commands/list.rs b/src/assets_canister/icx-asset/src/commands/list.rs
index 7b60850328..bc16fc85ff 100644
--- a/src/assets_canister/icx-asset/src/commands/list.rs
+++ b/src/assets_canister/icx-asset/src/commands/list.rs
@@ -5,9 +5,9 @@ use ic_utils::Canister;
 
 use num_traits::ToPrimitive;
 use serde::Deserialize;
-use std::time::SystemTime;
+use time::{format_description, Duration, OffsetDateTime, UtcOffset};
 
-pub async fn list(canister: &Canister<'_>) -> Result {
+pub async fn list(canister: &Canister<'_>, local_offset: UtcOffset) -> Result {
     #[derive(CandidType, Deserialize)]
     struct Encoding {
         modified: Int,
@@ -33,18 +33,20 @@ pub async fn list(canister: &Canister<'_>) -> Result {
         .call()
         .await?;
 
-    use chrono::offset::Local;
-    use chrono::DateTime;
-
     for entry in entries {
         for encoding in entry.encodings {
             let modified = encoding.modified;
-            let modified = SystemTime::UNIX_EPOCH
-                + std::time::Duration::from_nanos(modified.0.to_u64().unwrap());
+            let modified =
+                OffsetDateTime::from_unix_timestamp_nanos(modified.0.to_i128().unwrap())?;
+
+            let offset = Duration::new(local_offset.whole_seconds().into(), 0);
+            let timestamp = modified.checked_add(offset).unwrap_or(modified);
+            let timestamp_format =
+                format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second]")?;
 
             eprintln!(
                 "{:>20} {:>15} {:50} ({}, {})",
-                DateTime::<Local>::from(modified).format("%F %X"),
+                timestamp.format(&timestamp_format)?,
                 encoding.length.0,
                 entry.key,
                 entry.content_type,
diff --git a/src/assets_canister/icx-asset/src/main.rs b/src/assets_canister/icx-asset/src/main.rs
index 20414140d3..2b842710e3 100644
--- a/src/assets_canister/icx-asset/src/main.rs
+++ b/src/assets_canister/icx-asset/src/main.rs
@@ -7,6 +7,7 @@ use candid::Principal;
 use clap::{crate_authors, crate_version, Parser};
 use ic_agent::identity::{AnonymousIdentity, BasicIdentity, Secp256k1Identity};
 use ic_agent::{agent, Agent, Identity};
+use time::UtcOffset;
 
 use crate::commands::upload::upload;
 use std::path::PathBuf;
@@ -93,8 +94,7 @@ fn create_identity(maybe_pem: Option<PathBuf>) -> Box<dyn Identity + Sync + Send
     }
 }
 
-#[tokio::main(flavor = "multi_thread", worker_threads = 10)]
-async fn main() -> support::Result {
+fn main() -> support::Result {
     let opts: Opts = Opts::parse();
 
     let ttl: std::time::Duration = opts
@@ -110,33 +110,52 @@ async fn main() -> support::Result {
         .build()?;
 
     let normalized_replica = opts.replica.strip_suffix('/').unwrap_or(&opts.replica);
-    if normalized_replica != DEFAULT_IC_GATEWAY {
-        agent.fetch_root_key().await?;
-    }
 
-    match &opts.subcommand {
+    let local_offset = time::OffsetDateTime::now_local().map_or(UtcOffset::UTC, |v| v.offset());
+
+    let result = tokio::runtime::Builder::new_multi_thread()
+        .worker_threads(10)
+        .enable_all()
+        .build()
+        .unwrap()
+        .block_on(async {
+            if normalized_replica != DEFAULT_IC_GATEWAY {
+                agent.fetch_root_key().await?;
+            }
+
+            dispatch(&opts.subcommand, agent, ttl, local_offset).await
+        });
+
+    result
+}
+
+async fn dispatch(
+    subcommand: &SubCommand,
+    agent: Agent,
+    ttl: std::time::Duration,
+    local_offset: UtcOffset,
+) -> support::Result {
+    match subcommand {
         SubCommand::List(o) => {
             let canister = ic_utils::Canister::builder()
                 .with_agent(&agent)
                 .with_canister_id(Principal::from_text(&o.canister_id)?)
                 .build()?;
-            list(&canister).await?;
+            list(&canister, local_offset).await
         }
         SubCommand::Sync(o) => {
             let canister = ic_utils::Canister::builder()
                 .with_agent(&agent)
                 .with_canister_id(Principal::from_text(&o.canister_id)?)
                 .build()?;
-            sync(&canister, ttl, o).await?;
+            sync(&canister, ttl, o).await
         }
         SubCommand::Upload(o) => {
             let canister = ic_utils::Canister::builder()
                 .with_agent(&agent)
                 .with_canister_id(Principal::from_text(&o.canister_id)?)
                 .build()?;
-            upload(&canister, o).await?;
+            upload(&canister, o).await
         }
     }
-
-    Ok(())
 }