From 49ef020c6cf104f7696bc964e94a5d8583558065 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 10 Sep 2020 18:10:08 +0300 Subject: [PATCH 01/14] WIP - started to work on the page --- submit-flags/submit-flags-app/Cargo.toml | 14 +++++ submit-flags/submit-flags-app/src/Level.rs | 62 ++++++++++++++++++++++ submit-flags/submit-flags-app/src/lib.rs | 57 ++++++++++++++++++++ 3 files changed, 133 insertions(+) create mode 100644 submit-flags/submit-flags-app/Cargo.toml create mode 100644 submit-flags/submit-flags-app/src/Level.rs create mode 100644 submit-flags/submit-flags-app/src/lib.rs diff --git a/submit-flags/submit-flags-app/Cargo.toml b/submit-flags/submit-flags-app/Cargo.toml new file mode 100644 index 0000000..9d20351 --- /dev/null +++ b/submit-flags/submit-flags-app/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "submit-flags-app" +version = "0.1.0" +authors = ["Shay Nehmad "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +yew = "0.17" +wasm-bindgen = "0.2.67" diff --git a/submit-flags/submit-flags-app/src/Level.rs b/submit-flags/submit-flags-app/src/Level.rs new file mode 100644 index 0000000..55b429a --- /dev/null +++ b/submit-flags/submit-flags-app/src/Level.rs @@ -0,0 +1,62 @@ +use yew::prelude::{Component, ComponentLink, Properties, html, Html, ShouldRender}; + +pub struct LevelComponent { + link: ComponentLink, + name: String, + flag: String, + flagCorrect: bool, +} + +pub enum LevelMsg { + Clicked, +} + +#[derive(Clone, PartialEq, Properties)] +pub struct LevelProps { + #[prop_or_default] + pub name: String, + pub flag: String, +} + +impl Component for LevelComponent { + type Properties = LevelProps; + type Message = LevelMsg; + + fn create(props: Self::Properties, link: ComponentLink) -> Self { + Self { + link: link, + name: props.name, + flag: props.flag, + flagCorrect: false, + } + } + + fn update(&mut self, _msg: Self::Message) -> ShouldRender { + true + } + + fn change(&mut self, _props: Self::Properties) -> ShouldRender{ + // Should only return "true" if new properties are different to + // previously received properties. + // This component has no properties so we will always return "false". + false + } + + fn view(&self) -> Html { + html! { + <> +

{ "Level Component is here!" }

+

{"Name: "}{self.name.clone()}{" | Flag: "}{self.flag.clone()}{" | Status: "}{get_correct_emoji(self.flagCorrect)}

+ + + } + } +} + +fn get_correct_emoji(correct: bool) -> String { + if correct { + return "✔".to_string(); + } else { + return "❌".to_string(); + } +} diff --git a/submit-flags/submit-flags-app/src/lib.rs b/submit-flags/submit-flags-app/src/lib.rs new file mode 100644 index 0000000..f63d6a7 --- /dev/null +++ b/submit-flags/submit-flags-app/src/lib.rs @@ -0,0 +1,57 @@ +mod Level; + +use wasm_bindgen::prelude::{wasm_bindgen}; +use yew::prelude::{Component, ComponentLink, ShouldRender, Html, html, App}; +use Level::LevelComponent; + +struct Model { + link: ComponentLink, + value: i64, +} + +enum Msg { + AddOne, +} + +impl Component for Model { + type Message = Msg; + type Properties = (); + fn create(_: Self::Properties, link: ComponentLink) -> Self { + Self { + link, + value: 0, + } + } + + fn update(&mut self, msg: Self::Message) -> ShouldRender { + match msg { + Msg::AddOne => self.value += 1 + } + true + } + + fn change(&mut self, _props: Self::Properties) -> ShouldRender { + // Should only return "true" if new properties are different to + // previously received properties. + // This component has no properties so we will always return "false". + false + } + + fn view(&self) -> Html { + html! { + <> +
+

{ "Make Git Better CTF - Submit Flags" }

+
+ +
+
+ + } + } +} + +#[wasm_bindgen(start)] +pub fn run_app() { + App::::new().mount_to_body(); +} From f1b30459d8b3b92ecc15738cffdbd4c5ee01ce5c Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 10 Sep 2020 18:11:44 +0300 Subject: [PATCH 02/14] Added static HTML file --- submit-flags/submit-flags-app/static/index.html | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 submit-flags/submit-flags-app/static/index.html diff --git a/submit-flags/submit-flags-app/static/index.html b/submit-flags/submit-flags-app/static/index.html new file mode 100644 index 0000000..28d5af1 --- /dev/null +++ b/submit-flags/submit-flags-app/static/index.html @@ -0,0 +1,14 @@ + + + + + + 🚩 Make Git Better CTF 🚩 Submit Flags 🚩 + + + + + From 11fd910447709ee07edf5218db67721dc9f7ada9 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 10 Sep 2020 18:12:31 +0300 Subject: [PATCH 03/14] added static css as well --- submit-flags/submit-flags-app/static/css/style.css | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 submit-flags/submit-flags-app/static/css/style.css diff --git a/submit-flags/submit-flags-app/static/css/style.css b/submit-flags/submit-flags-app/static/css/style.css new file mode 100644 index 0000000..09d3928 --- /dev/null +++ b/submit-flags/submit-flags-app/static/css/style.css @@ -0,0 +1,5 @@ +@charset "UTF-8";/*!normalize.css v8.0.0 | MIT License | github.com/necolas/normalize.css*/html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}.chroma{color:#eee;background-color:#2c3e50}.chroma .err{color:#960050;background-color:#1e0010}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block}.chroma .hl{display:block;width:100%;background-color:#ffc}.chroma .lnt{margin-right:.4em;padding:0 .4em}.chroma .ln{margin-right:.4em;padding:0 .4em}.chroma .k{color:#66d9ef}.chroma .kc{color:#66d9ef}.chroma .kd{color:#66d9ef}.chroma .kn{color:#f92672}.chroma .kp{color:#66d9ef}.chroma .kr{color:#66d9ef}.chroma .kt{color:#66d9ef}.chroma .na{color:#a6e22e}.chroma .nc{color:#a6e22e}.chroma .no{color:#66d9ef}.chroma .nd{color:#a6e22e}.chroma .ne{color:#a6e22e}.chroma .nf{color:#a6e22e}.chroma .nx{color:#a6e22e}.chroma .nt{color:#f92672}.chroma .l{color:#ae81ff}.chroma .ld{color:#e6db74}.chroma .s{color:#e6db74}.chroma .sa{color:#e6db74}.chroma .sb{color:#e6db74}.chroma .sc{color:#e6db74}.chroma .dl{color:#e6db74}.chroma .sd{color:#e6db74}.chroma .s2{color:#e6db74}.chroma .se{color:#ae81ff}.chroma .sh{color:#e6db74}.chroma .si{color:#e6db74}.chroma .sx{color:#e6db74}.chroma .sr{color:#e6db74}.chroma .s1{color:#e6db74}.chroma .ss{color:#e6db74}.chroma .m{color:#ae81ff}.chroma .mb{color:#ae81ff}.chroma .mf{color:#ae81ff}.chroma .mh{color:#ae81ff}.chroma .mi{color:#ae81ff}.chroma .il{color:#ae81ff}.chroma .mo{color:#ae81ff}.chroma .o{color:#f92672}.chroma .ow{color:#f92672}.chroma .c{color:#75715e}.chroma .ch{color:#75715e}.chroma .cm{color:#75715e}.chroma .c1{color:#75715e}.chroma .cs{color:#75715e}.chroma .cp{color:#75715e}.chroma .cpf{color:#75715e}.chroma .gd{color:#f92672}.chroma .ge{font-style:italic}.chroma .gi{color:#a6e22e}.chroma .gs{font-weight:700}.chroma .gu{color:#75715e}/*!* animate.css - https://animate.style/ +* Version - 4.1.0 +* Licensed under the MIT license - http://opensource.org/licenses/MIT +* +* Copyright (c) 2020 Animate.css*/:root{--animate-duration:1s;--animate-delay:1s;--animate-repeat:1}.animated{animation-duration:1s;animation-duration:var(--animate-duration);animation-fill-mode:both}.animated.infinite{animation-iteration-count:infinite}.animated.repeat-1{animation-iteration-count:1;animation-iteration-count:var(--animate-repeat)}.animated.repeat-2{animation-iteration-count:2;animation-iteration-count:calc(var(--animate-repeat)*2)}.animated.repeat-3{animation-iteration-count:3;animation-iteration-count:calc(var(--animate-repeat)*3)}.animated.delay-1s{animation-delay:1s;animation-delay:var(--animate-delay)}.animated.delay-2s{animation-delay:2s;animation-delay:calc(var(--animate-delay)*2)}.animated.delay-3s{animation-delay:3s;animation-delay:calc(var(--animate-delay)*3)}.animated.delay-4s{animation-delay:4s;animation-delay:calc(var(--animate-delay)*4)}.animated.delay-5s{animation-delay:5s;animation-delay:calc(var(--animate-delay)*5)}.animated.faster{animation-duration:.5s;animation-duration:calc(var(--animate-duration)/2)}.animated.fast{animation-duration:.8s;animation-duration:calc(var(--animate-duration)*0.8)}.animated.slow{animation-duration:2s;animation-duration:calc(var(--animate-duration)*2)}.animated.slower{animation-duration:3s;animation-duration:calc(var(--animate-duration)*3)}@media(prefers-reduced-motion:reduce),print{.animated{animation-duration:1ms!important;transition-duration:1ms!important;animation-iteration-count:1!important}.animated[class*=Out]{opacity:0}}@keyframes bounce{0%,20%,53%,to{animation-timing-function:cubic-bezier(0.215,0.61,0.355,1);transform:translateZ(0)}40%,43%{animation-timing-function:cubic-bezier(0.755,0.05,0.855,0.06);transform:translate3d(0,-30px,0)scaleY(1.1)}70%{animation-timing-function:cubic-bezier(0.755,0.05,0.855,0.06);transform:translate3d(0,-15px,0)scaleY(1.05)}80%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1);transform:translateZ(0)scaleY(0.95)}90%{transform:translate3d(0,-4px,0)scaleY(1.02)}}.bounce{animation-name:bounce;transform-origin:center bottom}@keyframes bounceInRight{0%,60%,75%,90%,to{animation-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;transform:translate3d(3000px,0,0)scaleX(3)}60%{opacity:1;transform:translate3d(-25px,0,0)scaleX(1)}75%{transform:translate3d(10px,0,0)scaleX(0.98)}90%{transform:translate3d(-5px,0,0)scaleX(0.995)}to{transform:translateZ(0)}}.bounceInRight{animation-name:bounceInRight}@keyframes bounceOutRight{20%{opacity:1;transform:translate3d(-20px,0,0)scaleX(0.9)}to{opacity:0;transform:translate3d(2000px,0,0)scaleX(2)}}.bounceOutRight{animation-name:bounceOutRight}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.fadeIn{animation-name:fadeIn}@keyframes slideInUp{0%{transform:translate3d(0,100%,0);visibility:visible}to{transform:translateZ(0)}}.slideInUp{animation-name:slideInUp}@keyframes slideOutDown{0%{transform:translateZ(0)}to{visibility:hidden;transform:translate3d(0,100%,0)}}.slideOutDown{animation-name:slideOutDown}::-webkit-scrollbar{width:8px;height:8px;background:#2c3e50}::-webkit-scrollbar-thumb{background:#888}::-webkit-scrollbar-thumb:hover{background:#e8eef2}html{background:#494f5c;line-height:1.6;letter-spacing:.06em;scroll-behavior:smooth}body,button,input,select,textarea{color:#e8eef2;font-family:trebuchet ms,Verdana,verdana ref,segoe ui,Candara,lucida grande,lucida sans unicode,lucida sans,Tahoma,sans-serif}pre,code,pre tt{font-family:Consolas,andale mono wt,andale mono,Menlo,Monaco,lucida console,lucida sans typewriter,dejavu sans mono,bitstream vera sans mono,liberation mono,nimbus mono l,courier new,Courier,yahei consolas hybrid,monospace,segoe ui emoji,pingfang sc,microsoft yahei}pre{padding:.7em 1.1em;overflow:auto;font-size:.9em;line-height:1.5;letter-spacing:normal;white-space:pre;color:#eee;background:#2c3e50;border-radius:4px}pre code{padding:0;margin:0;background:#2c3e50}code{color:#eee;background:#7d828a;border-radius:3px;padding:0 3px;margin:0 4px;word-wrap:break-word;letter-spacing:normal}blockquote{border-left:.25em solid;margin:1em;padding:0 1em;font-style:italic}blockquote cite{font-weight:700;font-style:normal}blockquote cite::before{content:"—— "}a{color:#e8eef2;text-decoration:none;border:none;transition-property:color;transition-duration:.4s;transition-timing-function:ease-out}a:hover{color:#fff;text-shadow:0 0 1px #fff}hr{opacity:.2;border-width:0 0 5px;border-style:dashed;background:0 0;width:50%;margin:1.8em auto}table{border-collapse:collapse;border-spacing:0;empty-cells:show;width:100%;max-width:100%}table th,table td{padding:1.5%;border:1px solid}table th{font-weight:700;vertical-align:bottom}.section-inner{margin:0 auto;max-width:1200px;width:93%}.thin{max-width:720px;margin:auto}.feather{display:inline-block;vertical-align:-.125em;width:1em;height:1em}.sub-menu{font-size:.7em}.desktop-only,.desktop-only-ib{display:none}.highlight{position:relative}.highlight pre{padding-right:75px}.highlight-copy-btn{position:absolute;bottom:7px;right:7px;border:0;border-radius:4px;padding:1px;font-size:.7em;line-height:1.8;color:#fff;background-color:#777;opacity:.6;min-width:55px;text-align:center}.highlight-copy-btn:hover{background-color:#666}.screen-reader-text{border:0;clip:rect(1px,1px,1px,1px);clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute!important;width:1px;word-wrap:normal!important}.screen-reader-text:focus{background-color:#f1f1f1;border-radius:3px;box-shadow:0 0 2px 2px rgba(0,0,0,.6);clip:auto!important;clip-path:none;color:#21759b;display:block;font-size:14px;font-size:.875rem;font-weight:700;height:auto;left:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}#site-header{position:fixed;z-index:1;bottom:0;width:100%;box-sizing:border-box;box-shadow:-1px -2px 3px rgba(0,0,0,.45);background-color:#3b3e48;animation-duration:.3s}.hdr-wrapper{display:flex;justify-content:space-between;align-items:center;padding:.5em 0;font-size:1.2rem}.hdr-wrapper .site-branding{display:inline-block;margin-right:.8em;font-size:1.2em}.hdr-wrapper .site-nav{display:inline-block;font-size:1.1em;opacity:.8}.hdr-wrapper .site-nav .has-children{padding-right:.5em;border-right:2px solid #7d828a}.hdr-wrapper .site-nav .sub-menu>a{margin-left:.3em}.hdr-wrapper .site-nav a{margin-left:.8em}.hdr-icons{font-size:1.2em}.hdr-social{display:inline-block;margin-left:.6em}.hdr-social>a{margin-left:.4em}.hdr-btn{border:none;background:0 0;padding:0;margin-left:.4em;cursor:pointer}#menu-btn{display:none;margin-left:.6em;cursor:pointer}#mobile-menu{position:fixed;bottom:4.8em;right:1.5em;display:none;padding:.6em 1.8em;z-index:1;box-sizing:border-box;box-shadow:-1px -2px 3px 0 rgba(0,0,0,.45);background-color:#3b3e48}#mobile-menu ul{list-style:none;margin:0;padding:0;line-height:2;font-size:1.2em}#site-footer{text-align:center;font-size:.9em;margin-bottom:96px;margin-top:64px}#site-footer p{margin:0}#spotlight{display:flex;min-height:100vh;flex-direction:column;align-items:center;justify-content:center;max-width:93%;margin:auto;font-size:1.5rem}#spotlight.error-404{flex-direction:row;line-height:normal}p.img-404{margin:0}p.img-404 svg{width:180px;max-width:100%;height:auto}.banner-404{margin-left:2em}.banner-404 h1{font-size:3em;margin:.5rem 0}.banner-404 p{margin-top:0}.banner-404 .btn-404{font-size:.8em}.banner-404 .btn-404 a{display:inline-block;border:2px solid #e8eef2;border-radius:5px;padding:5px;transition-property:color,border-color}.banner-404 .btn-404 a:first-child{margin-right:1em}.banner-404 .btn-404 a:hover{border-color:#fff}.banner-404 .btn-404 a svg{margin-right:.5em}#home-center{display:flex;flex-grow:1;flex-direction:column;justify-content:center}#home-title{margin:0;text-align:center}#home-subtitle{margin-top:0;margin-bottom:1.5em;text-align:center;line-height:normal;font-size:.7em;font-style:italic;opacity:.9}#home-social{font-size:1.4em;text-align:center;opacity:.8}#home-social a{margin:0 .2em}#home-nav{opacity:.8}#home-nav a{display:block;text-align:center;margin-top:.5em}#home-footer{text-align:center;font-size:.6em;line-height:normal;opacity:.6}#home-footer p{margin-top:0}.posts-group{display:flex;margin-bottom:1.9em;line-height:normal}.posts-group .post-year{padding-top:6px;margin-right:1.8em;font-size:1.6em;opacity:.6}.posts-group .post-year:hover{text-decoration:underline;cursor:pointer}.posts-group .posts-list{flex-grow:1;margin:0;padding:0;list-style:none}.posts-group .post-item{border-bottom:1px #7d828a dashed}.posts-group .post-item a{display:flex;justify-content:space-between;align-items:baseline;padding:12px 0}.posts-group .post-day{flex-shrink:0;margin-left:1em;opacity:.6}.bg-img{width:100vw;height:100vh;opacity:.03;z-index:-1;position:fixed;top:0;background-attachment:fixed;background-repeat:no-repeat;background-size:cover;background-position:50%;transition:opacity .5s}.show-bg-img{z-index:100;opacity:1;cursor:pointer}.post-header{margin-top:1.2em;line-height:normal}.post-header .post-meta{font-size:.9em;letter-spacing:normal;opacity:.6}.post-header h1{margin-top:.1em}hr.post-end{width:50%;margin-top:1.6em;margin-bottom:.8em;margin-left:0;border-style:solid;border-bottom-width:4px}.content a{word-wrap:break-word;border:none;box-shadow:inset 0 -4px 0 #018574;transition-property:box-shadow;transition-duration:.1s}.content a:hover{box-shadow:inset 0 -1em 0 #018574}.content figure{max-width:100%;height:auto;margin:0;text-align:center}.content figure p{font-size:.8em;font-style:italic;opacity:.6}.content figure.left{float:left;margin-right:1.5em;max-width:50%}.content figure.right{float:right;margin-left:1.5em;max-width:50%}.content figure.big{max-width:100vw}.content img{display:block;max-width:100%;height:auto;margin:auto;border-radius:4px}.content ul,.content ol{padding:0;margin-left:1.8em}.content a.anchor{float:left;margin-left:-20px;padding-right:6px;box-shadow:none;opacity:.8}.content a.anchor:hover{background:0 0;color:#018574;opacity:1}.content a.anchor svg{display:inline-block;width:14px;height:14px;vertical-align:baseline;visibility:hidden}.content a.anchor:focus svg{visibility:visible}.content h1:hover a.anchor svg,.content h2:hover a.anchor svg,.content h3:hover a.anchor svg,.content h4:hover a.anchor svg,.content h5:hover a.anchor svg,.content h6:hover a.anchor svg{visibility:visible}.footnotes{font-size:.85em}.footnotes a{box-shadow:none;text-decoration:underline;transition-property:color}.footnotes a:hover{background:0 0}.footnotes a.footnote-backref{text-decoration:none}.footnotes ol{line-height:1.8}a.footnote-ref{box-shadow:none;text-decoration:none;padding:2px;border-radius:2px;background-color:#2c3e50}a.footnote-ref:hover{box-shadow:none;background-color:#018574;transition-property:background-color}.post-info{font-size:.8rem;line-height:normal;opacity:.6}.post-info p{margin:.8em 0}.post-info a:hover{border-bottom:1px solid #018574}.post-info svg{margin-right:.8em}.post-info .tag{margin-right:.5em}.post-info .tag::before{content:"#"}#toc{position:fixed;left:50%;top:0;display:none}.toc-title{margin-left:1em;margin-bottom:.5em;font-size:.8em;font-weight:700}#TableOfContents{font-size:.8em;opacity:.6}#TableOfContents ul{padding-left:1em;margin:0}#TableOfContents>ul{list-style-type:none}#TableOfContents>ul ul ul{font-size:.9em}#TableOfContents a:hover{border-bottom:#018574 1px solid}.post-nav{display:flex;justify-content:space-between;margin-top:1.5em;margin-bottom:2.5em;font-size:1.2em}.post-nav a{flex-basis:50%;flex-grow:1}.post-nav .next-post{text-align:left;padding-right:5px}.post-nav .prev-post{text-align:right;padding-left:5px}.post-nav .post-nav-label{font-size:.8em;opacity:.8;text-transform:uppercase}.related-posts{padding:.8em;margin-top:1.5em;font-size:.8rem;border:3px dashed rgba(255,255,255,.2);border-radius:5px}.related-posts h2{margin:0;line-height:normal}.related-posts ul{margin-top:.5em;margin-bottom:0}@media(min-width:800px){.site-main{margin-top:3em}hr.post-end{width:40%}}@media(min-width:960px){.site-main{margin-top:6em}}@media(min-width:1300px){.site-main{margin-top:8em}.desktop-only,#toc.show-toc{display:block}.desktop-only-ib{display:inline-block}figure.left{margin-left:-240px}figure.left p{text-align:left}figure.right{margin-right:-240px}figure.right p{text-align:right}figure.big{width:1200px;margin-left:-240px}hr.post-end{width:30%}#toc{top:13em;margin-left:370px;max-width:220px}}@media(min-width:1800px){.site-main{margin-top:10em}.section-inner{max-width:1600px}.thin{max-width:960px}figure.left{max-width:75%;margin-left:-320px}figure.right{max-width:75%;margin-right:-320px}figure.big{width:1600px;margin-left:-320px}hr.post-end{width:30%}#toc{top:15em;margin-left:490px;max-width:300px}}@media(max-width:760px){.hide-in-mobile,.site-nav.hide-in-mobile{display:none}#menu-btn{display:inline-block}.posts-group{display:block}.posts-group .post-year{margin:-6px 0 4px}#spotlight.error-404{flex-direction:column;text-align:center}#spotlight.error-404 .banner-404{margin:0}}@media(max-width:520px){.content figure.left,.content figure.right{float:unset;max-width:100%;margin:0}hr.post-end{width:60%}#mobile-menu{right:1.2em}} From cd0b513c1951ee440b600f39b3e33742720ac2cc Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Fri, 18 Sep 2020 17:59:48 +0300 Subject: [PATCH 04/14] WIP on the level component, still a few changes left --- submit-flags/submit-flags-app/Cargo.toml | 2 + submit-flags/submit-flags-app/src/Level.rs | 75 +- submit-flags/submit-flags-app/src/lib.rs | 13 +- .../submit-flags-app/static/css/style.css | 1577 ++++++++++++++++- 4 files changed, 1653 insertions(+), 14 deletions(-) diff --git a/submit-flags/submit-flags-app/Cargo.toml b/submit-flags/submit-flags-app/Cargo.toml index 9d20351..ade6f2f 100644 --- a/submit-flags/submit-flags-app/Cargo.toml +++ b/submit-flags/submit-flags-app/Cargo.toml @@ -12,3 +12,5 @@ crate-type = ["cdylib", "rlib"] [dependencies] yew = "0.17" wasm-bindgen = "0.2.67" +log = "0.4.6" +wasm-logger = "0.2.0" diff --git a/submit-flags/submit-flags-app/src/Level.rs b/submit-flags/submit-flags-app/src/Level.rs index 55b429a..60ba090 100644 --- a/submit-flags/submit-flags-app/src/Level.rs +++ b/submit-flags/submit-flags-app/src/Level.rs @@ -1,37 +1,79 @@ +use log; + use yew::prelude::{Component, ComponentLink, Properties, html, Html, ShouldRender}; +use yew::html::InputData; pub struct LevelComponent { + // The link enables us to interact (i.e. reqister callbacks and send messages) with the component itself. See https://yew.rs/docs/en/concepts/components/#create link: ComponentLink, + // The level's name. This is so the user knows which flag belongs where name: String, + // The flag itself. In the future this will become a hash so that the users can't get the flags using devtools. flag: String, - flagCorrect: bool, + // The user's guess for the flag, that they are typing + user_flag: String, + // Whether the correct flag has been entered. + flag_correct: bool, } +// These are the messages (think "events") that can happen in this component. pub enum LevelMsg { - Clicked, + // This message indicates that it's time to check the user flag to see if it's the correct one. + CheckFlag, + // This message indicates that the user changed the flg they're guessing (when they're typing). Since we need to pass a value, this message has a parameter - see the `view` and `update` methods to see how this is used. + UserFlagChanged(String), } +// See https://yew.rs/docs/en/concepts/components/properties/ +// The properties allow enable child and parent components to communicate with each other. +// The parent of a level component is the page itself. #[derive(Clone, PartialEq, Properties)] pub struct LevelProps { - #[prop_or_default] + // This prop is the level's name. Passed from parent and won't change pub name: String, + // This prop is the level's flag. Passed from parent and won't change pub flag: String, + // This prop indicates whether the user's flag is correct. Not passed from parent, but rather used to communicate back to it from the level. + #[prop_or(false)] + pub flag_correct: bool, } +// See https://yew.rs/docs/en/concepts/components/ +// `Component` is a Trait (see https://doc.rust-lang.org/book/ch10-02-traits.html), defined here: https://github.com/yewstack/yew/blob/master/yew/src/html/mod.rs#L30 impl Component for LevelComponent { + // Overriding properties since we have our own. type Properties = LevelProps; + // Overriding `Message` since we have our own messages. type Message = LevelMsg; + // See https://yew.rs/docs/en/concepts/components/#create fn create(props: Self::Properties, link: ComponentLink) -> Self { + log::debug!("Creating level {} component", props.name.clone()); Self { link: link, + // Pass the name from the parent component name: props.name, + // Pass the flag from the parent component flag: props.flag, - flagCorrect: false, + // The initial user flag is empty + user_flag: "".to_string(), + // This is passed from the parent component as well, but has a default value of `false`. + flag_correct: props.flag_correct, } } - fn update(&mut self, _msg: Self::Message) -> ShouldRender { + fn update(&mut self, msg: Self::Message) -> ShouldRender { + match msg { + LevelMsg::CheckFlag => { + // TODO - hash + self.flag_correct = self.user_flag == self.flag + } + LevelMsg::UserFlagChanged(value) => { + log::debug!("In level {}, User flag changed from {} to {}", self.name.clone(), self.user_flag.clone(), value.clone()); + self.user_flag = value; + self.update(LevelMsg::CheckFlag); + } + } true } @@ -43,11 +85,28 @@ impl Component for LevelComponent { } fn view(&self) -> Html { + let label_text = self.name.clone() + "'s flag goes here 🚩"; html! { <> -

{ "Level Component is here!" }

-

{"Name: "}{self.name.clone()}{" | Flag: "}{self.flag.clone()}{" | Status: "}{get_correct_emoji(self.flagCorrect)}

- + +
+                        {"DEBUG: I am a level component! Name: "}{self.name.clone()}
+                        {" | Flag: "}{self.flag.clone()}
+                        {" | Status: "}{get_correct_emoji(self.flag_correct)}
+                    
+
+
+
+ + + + + +
+ // TODO - delete button + +
+
} } diff --git a/submit-flags/submit-flags-app/src/lib.rs b/submit-flags/submit-flags-app/src/lib.rs index f63d6a7..f445dd5 100644 --- a/submit-flags/submit-flags-app/src/lib.rs +++ b/submit-flags/submit-flags-app/src/lib.rs @@ -1,8 +1,12 @@ -mod Level; +// See https://github.com/yewstack/yew/issues/97 +#![recursion_limit="256"] + +mod level; use wasm_bindgen::prelude::{wasm_bindgen}; use yew::prelude::{Component, ComponentLink, ShouldRender, Html, html, App}; -use Level::LevelComponent; +use level::LevelComponent; +use wasm_logger; struct Model { link: ComponentLink, @@ -41,8 +45,8 @@ impl Component for Model { html! { <>
-

{ "Make Git Better CTF - Submit Flags" }

-
+

{ "Make Git Better CTF - Submit Flags" }

+
@@ -53,5 +57,6 @@ impl Component for Model { #[wasm_bindgen(start)] pub fn run_app() { + wasm_logger::init(wasm_logger::Config::default()); App::::new().mount_to_body(); } diff --git a/submit-flags/submit-flags-app/static/css/style.css b/submit-flags/submit-flags-app/static/css/style.css index 09d3928..b3af66c 100644 --- a/submit-flags/submit-flags-app/static/css/style.css +++ b/submit-flags/submit-flags-app/static/css/style.css @@ -1,5 +1,1578 @@ -@charset "UTF-8";/*!normalize.css v8.0.0 | MIT License | github.com/necolas/normalize.css*/html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}.chroma{color:#eee;background-color:#2c3e50}.chroma .err{color:#960050;background-color:#1e0010}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block}.chroma .hl{display:block;width:100%;background-color:#ffc}.chroma .lnt{margin-right:.4em;padding:0 .4em}.chroma .ln{margin-right:.4em;padding:0 .4em}.chroma .k{color:#66d9ef}.chroma .kc{color:#66d9ef}.chroma .kd{color:#66d9ef}.chroma .kn{color:#f92672}.chroma .kp{color:#66d9ef}.chroma .kr{color:#66d9ef}.chroma .kt{color:#66d9ef}.chroma .na{color:#a6e22e}.chroma .nc{color:#a6e22e}.chroma .no{color:#66d9ef}.chroma .nd{color:#a6e22e}.chroma .ne{color:#a6e22e}.chroma .nf{color:#a6e22e}.chroma .nx{color:#a6e22e}.chroma .nt{color:#f92672}.chroma .l{color:#ae81ff}.chroma .ld{color:#e6db74}.chroma .s{color:#e6db74}.chroma .sa{color:#e6db74}.chroma .sb{color:#e6db74}.chroma .sc{color:#e6db74}.chroma .dl{color:#e6db74}.chroma .sd{color:#e6db74}.chroma .s2{color:#e6db74}.chroma .se{color:#ae81ff}.chroma .sh{color:#e6db74}.chroma .si{color:#e6db74}.chroma .sx{color:#e6db74}.chroma .sr{color:#e6db74}.chroma .s1{color:#e6db74}.chroma .ss{color:#e6db74}.chroma .m{color:#ae81ff}.chroma .mb{color:#ae81ff}.chroma .mf{color:#ae81ff}.chroma .mh{color:#ae81ff}.chroma .mi{color:#ae81ff}.chroma .il{color:#ae81ff}.chroma .mo{color:#ae81ff}.chroma .o{color:#f92672}.chroma .ow{color:#f92672}.chroma .c{color:#75715e}.chroma .ch{color:#75715e}.chroma .cm{color:#75715e}.chroma .c1{color:#75715e}.chroma .cs{color:#75715e}.chroma .cp{color:#75715e}.chroma .cpf{color:#75715e}.chroma .gd{color:#f92672}.chroma .ge{font-style:italic}.chroma .gi{color:#a6e22e}.chroma .gs{font-weight:700}.chroma .gu{color:#75715e}/*!* animate.css - https://animate.style/ +@charset "UTF-8";/*!normalize.css v8.0.0 | MIT License | github.com/necolas/normalize.css*/ +html { + line-height: 1.15; + -webkit-text-size-adjust: 100% +} + +body { + margin: 0 +} + +h1 { + font-size: 2em; + margin: .67em 0 +} + +hr { + box-sizing: content-box; + height: 0; + overflow: visible +} + +pre { + font-family: monospace,monospace; + font-size: 1em +} + +a { + background-color: transparent +} + +abbr[title] { + border-bottom: none; + text-decoration: underline; + text-decoration: underline dotted +} + +b,strong { + font-weight: bolder +} + +code,kbd,samp { + font-family: monospace,monospace; + font-size: 1em +} + +small { + font-size: 80% +} + +sub,sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline +} + +sub { + bottom: -.25em +} + +sup { + top: -.5em +} + +img { + border-style: none +} + +button,input,optgroup,select,textarea { + font-family: inherit; + font-size: 100%; + line-height: 1.15; + margin: 0 +} + +button,input { + overflow: visible +} + +button,select { + text-transform: none +} + +button,[type=button],[type=reset],[type=submit] { + -webkit-appearance: button +} + +button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner { + border-style: none; + padding: 0 +} + +button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring { + outline: 1px dotted ButtonText +} + +fieldset { + padding: .35em .75em .625em +} + +legend { + box-sizing: border-box; + color: inherit; + display: table; + max-width: 100%; + padding: 0; + white-space: normal +} + +progress { + vertical-align: baseline +} + +textarea { + overflow: auto +} + +[type=checkbox],[type=radio] { + box-sizing: border-box; + padding: 0 +} + +[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button { + height: auto +} + +[type=search] { + -webkit-appearance: textfield; + outline-offset: -2px +} + +[type=search]::-webkit-search-decoration { + -webkit-appearance: none +} + +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit +} + +details { + display: block +} + +summary { + display: list-item +} + +template { + display: none +} + +[hidden] { + display: none +} + +.chroma { + color: #eee; + background-color: #2c3e50 +} + +.chroma .err { + color: #960050; + background-color: #1e0010 +} + +.chroma .lntd { + vertical-align: top; + padding: 0; + margin: 0; + border: 0 +} + +.chroma .lntable { + border-spacing: 0; + padding: 0; + margin: 0; + border: 0; + width: auto; + overflow: auto; + display: block +} + +.chroma .hl { + display: block; + width: 100%; + background-color: #ffc +} + +.chroma .lnt { + margin-right: .4em; + padding: 0 .4em +} + +.chroma .ln { + margin-right: .4em; + padding: 0 .4em +} + +.chroma .k { + color: #66d9ef +} + +.chroma .kc { + color: #66d9ef +} + +.chroma .kd { + color: #66d9ef +} + +.chroma .kn { + color: #f92672 +} + +.chroma .kp { + color: #66d9ef +} + +.chroma .kr { + color: #66d9ef +} + +.chroma .kt { + color: #66d9ef +} + +.chroma .na { + color: #a6e22e +} + +.chroma .nc { + color: #a6e22e +} + +.chroma .no { + color: #66d9ef +} + +.chroma .nd { + color: #a6e22e +} + +.chroma .ne { + color: #a6e22e +} + +.chroma .nf { + color: #a6e22e +} + +.chroma .nx { + color: #a6e22e +} + +.chroma .nt { + color: #f92672 +} + +.chroma .l { + color: #ae81ff +} + +.chroma .ld { + color: #e6db74 +} + +.chroma .s { + color: #e6db74 +} + +.chroma .sa { + color: #e6db74 +} + +.chroma .sb { + color: #e6db74 +} + +.chroma .sc { + color: #e6db74 +} + +.chroma .dl { + color: #e6db74 +} + +.chroma .sd { + color: #e6db74 +} + +.chroma .s2 { + color: #e6db74 +} + +.chroma .se { + color: #ae81ff +} + +.chroma .sh { + color: #e6db74 +} + +.chroma .si { + color: #e6db74 +} + +.chroma .sx { + color: #e6db74 +} + +.chroma .sr { + color: #e6db74 +} + +.chroma .s1 { + color: #e6db74 +} + +.chroma .ss { + color: #e6db74 +} + +.chroma .m { + color: #ae81ff +} + +.chroma .mb { + color: #ae81ff +} + +.chroma .mf { + color: #ae81ff +} + +.chroma .mh { + color: #ae81ff +} + +.chroma .mi { + color: #ae81ff +} + +.chroma .il { + color: #ae81ff +} + +.chroma .mo { + color: #ae81ff +} + +.chroma .o { + color: #f92672 +} + +.chroma .ow { + color: #f92672 +} + +.chroma .c { + color: #75715e +} + +.chroma .ch { + color: #75715e +} + +.chroma .cm { + color: #75715e +} + +.chroma .c1 { + color: #75715e +} + +.chroma .cs { + color: #75715e +} + +.chroma .cp { + color: #75715e +} + +.chroma .cpf { + color: #75715e +} + +.chroma .gd { + color: #f92672 +} + +.chroma .ge { + font-style: italic +} + +.chroma .gi { + color: #a6e22e +} + +.chroma .gs { + font-weight: 700 +} + +.chroma .gu { + color: #75715e +} + +/*!* animate.css - https://animate.style/ * Version - 4.1.0 * Licensed under the MIT license - http://opensource.org/licenses/MIT * -* Copyright (c) 2020 Animate.css*/:root{--animate-duration:1s;--animate-delay:1s;--animate-repeat:1}.animated{animation-duration:1s;animation-duration:var(--animate-duration);animation-fill-mode:both}.animated.infinite{animation-iteration-count:infinite}.animated.repeat-1{animation-iteration-count:1;animation-iteration-count:var(--animate-repeat)}.animated.repeat-2{animation-iteration-count:2;animation-iteration-count:calc(var(--animate-repeat)*2)}.animated.repeat-3{animation-iteration-count:3;animation-iteration-count:calc(var(--animate-repeat)*3)}.animated.delay-1s{animation-delay:1s;animation-delay:var(--animate-delay)}.animated.delay-2s{animation-delay:2s;animation-delay:calc(var(--animate-delay)*2)}.animated.delay-3s{animation-delay:3s;animation-delay:calc(var(--animate-delay)*3)}.animated.delay-4s{animation-delay:4s;animation-delay:calc(var(--animate-delay)*4)}.animated.delay-5s{animation-delay:5s;animation-delay:calc(var(--animate-delay)*5)}.animated.faster{animation-duration:.5s;animation-duration:calc(var(--animate-duration)/2)}.animated.fast{animation-duration:.8s;animation-duration:calc(var(--animate-duration)*0.8)}.animated.slow{animation-duration:2s;animation-duration:calc(var(--animate-duration)*2)}.animated.slower{animation-duration:3s;animation-duration:calc(var(--animate-duration)*3)}@media(prefers-reduced-motion:reduce),print{.animated{animation-duration:1ms!important;transition-duration:1ms!important;animation-iteration-count:1!important}.animated[class*=Out]{opacity:0}}@keyframes bounce{0%,20%,53%,to{animation-timing-function:cubic-bezier(0.215,0.61,0.355,1);transform:translateZ(0)}40%,43%{animation-timing-function:cubic-bezier(0.755,0.05,0.855,0.06);transform:translate3d(0,-30px,0)scaleY(1.1)}70%{animation-timing-function:cubic-bezier(0.755,0.05,0.855,0.06);transform:translate3d(0,-15px,0)scaleY(1.05)}80%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1);transform:translateZ(0)scaleY(0.95)}90%{transform:translate3d(0,-4px,0)scaleY(1.02)}}.bounce{animation-name:bounce;transform-origin:center bottom}@keyframes bounceInRight{0%,60%,75%,90%,to{animation-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;transform:translate3d(3000px,0,0)scaleX(3)}60%{opacity:1;transform:translate3d(-25px,0,0)scaleX(1)}75%{transform:translate3d(10px,0,0)scaleX(0.98)}90%{transform:translate3d(-5px,0,0)scaleX(0.995)}to{transform:translateZ(0)}}.bounceInRight{animation-name:bounceInRight}@keyframes bounceOutRight{20%{opacity:1;transform:translate3d(-20px,0,0)scaleX(0.9)}to{opacity:0;transform:translate3d(2000px,0,0)scaleX(2)}}.bounceOutRight{animation-name:bounceOutRight}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.fadeIn{animation-name:fadeIn}@keyframes slideInUp{0%{transform:translate3d(0,100%,0);visibility:visible}to{transform:translateZ(0)}}.slideInUp{animation-name:slideInUp}@keyframes slideOutDown{0%{transform:translateZ(0)}to{visibility:hidden;transform:translate3d(0,100%,0)}}.slideOutDown{animation-name:slideOutDown}::-webkit-scrollbar{width:8px;height:8px;background:#2c3e50}::-webkit-scrollbar-thumb{background:#888}::-webkit-scrollbar-thumb:hover{background:#e8eef2}html{background:#494f5c;line-height:1.6;letter-spacing:.06em;scroll-behavior:smooth}body,button,input,select,textarea{color:#e8eef2;font-family:trebuchet ms,Verdana,verdana ref,segoe ui,Candara,lucida grande,lucida sans unicode,lucida sans,Tahoma,sans-serif}pre,code,pre tt{font-family:Consolas,andale mono wt,andale mono,Menlo,Monaco,lucida console,lucida sans typewriter,dejavu sans mono,bitstream vera sans mono,liberation mono,nimbus mono l,courier new,Courier,yahei consolas hybrid,monospace,segoe ui emoji,pingfang sc,microsoft yahei}pre{padding:.7em 1.1em;overflow:auto;font-size:.9em;line-height:1.5;letter-spacing:normal;white-space:pre;color:#eee;background:#2c3e50;border-radius:4px}pre code{padding:0;margin:0;background:#2c3e50}code{color:#eee;background:#7d828a;border-radius:3px;padding:0 3px;margin:0 4px;word-wrap:break-word;letter-spacing:normal}blockquote{border-left:.25em solid;margin:1em;padding:0 1em;font-style:italic}blockquote cite{font-weight:700;font-style:normal}blockquote cite::before{content:"—— "}a{color:#e8eef2;text-decoration:none;border:none;transition-property:color;transition-duration:.4s;transition-timing-function:ease-out}a:hover{color:#fff;text-shadow:0 0 1px #fff}hr{opacity:.2;border-width:0 0 5px;border-style:dashed;background:0 0;width:50%;margin:1.8em auto}table{border-collapse:collapse;border-spacing:0;empty-cells:show;width:100%;max-width:100%}table th,table td{padding:1.5%;border:1px solid}table th{font-weight:700;vertical-align:bottom}.section-inner{margin:0 auto;max-width:1200px;width:93%}.thin{max-width:720px;margin:auto}.feather{display:inline-block;vertical-align:-.125em;width:1em;height:1em}.sub-menu{font-size:.7em}.desktop-only,.desktop-only-ib{display:none}.highlight{position:relative}.highlight pre{padding-right:75px}.highlight-copy-btn{position:absolute;bottom:7px;right:7px;border:0;border-radius:4px;padding:1px;font-size:.7em;line-height:1.8;color:#fff;background-color:#777;opacity:.6;min-width:55px;text-align:center}.highlight-copy-btn:hover{background-color:#666}.screen-reader-text{border:0;clip:rect(1px,1px,1px,1px);clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute!important;width:1px;word-wrap:normal!important}.screen-reader-text:focus{background-color:#f1f1f1;border-radius:3px;box-shadow:0 0 2px 2px rgba(0,0,0,.6);clip:auto!important;clip-path:none;color:#21759b;display:block;font-size:14px;font-size:.875rem;font-weight:700;height:auto;left:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}#site-header{position:fixed;z-index:1;bottom:0;width:100%;box-sizing:border-box;box-shadow:-1px -2px 3px rgba(0,0,0,.45);background-color:#3b3e48;animation-duration:.3s}.hdr-wrapper{display:flex;justify-content:space-between;align-items:center;padding:.5em 0;font-size:1.2rem}.hdr-wrapper .site-branding{display:inline-block;margin-right:.8em;font-size:1.2em}.hdr-wrapper .site-nav{display:inline-block;font-size:1.1em;opacity:.8}.hdr-wrapper .site-nav .has-children{padding-right:.5em;border-right:2px solid #7d828a}.hdr-wrapper .site-nav .sub-menu>a{margin-left:.3em}.hdr-wrapper .site-nav a{margin-left:.8em}.hdr-icons{font-size:1.2em}.hdr-social{display:inline-block;margin-left:.6em}.hdr-social>a{margin-left:.4em}.hdr-btn{border:none;background:0 0;padding:0;margin-left:.4em;cursor:pointer}#menu-btn{display:none;margin-left:.6em;cursor:pointer}#mobile-menu{position:fixed;bottom:4.8em;right:1.5em;display:none;padding:.6em 1.8em;z-index:1;box-sizing:border-box;box-shadow:-1px -2px 3px 0 rgba(0,0,0,.45);background-color:#3b3e48}#mobile-menu ul{list-style:none;margin:0;padding:0;line-height:2;font-size:1.2em}#site-footer{text-align:center;font-size:.9em;margin-bottom:96px;margin-top:64px}#site-footer p{margin:0}#spotlight{display:flex;min-height:100vh;flex-direction:column;align-items:center;justify-content:center;max-width:93%;margin:auto;font-size:1.5rem}#spotlight.error-404{flex-direction:row;line-height:normal}p.img-404{margin:0}p.img-404 svg{width:180px;max-width:100%;height:auto}.banner-404{margin-left:2em}.banner-404 h1{font-size:3em;margin:.5rem 0}.banner-404 p{margin-top:0}.banner-404 .btn-404{font-size:.8em}.banner-404 .btn-404 a{display:inline-block;border:2px solid #e8eef2;border-radius:5px;padding:5px;transition-property:color,border-color}.banner-404 .btn-404 a:first-child{margin-right:1em}.banner-404 .btn-404 a:hover{border-color:#fff}.banner-404 .btn-404 a svg{margin-right:.5em}#home-center{display:flex;flex-grow:1;flex-direction:column;justify-content:center}#home-title{margin:0;text-align:center}#home-subtitle{margin-top:0;margin-bottom:1.5em;text-align:center;line-height:normal;font-size:.7em;font-style:italic;opacity:.9}#home-social{font-size:1.4em;text-align:center;opacity:.8}#home-social a{margin:0 .2em}#home-nav{opacity:.8}#home-nav a{display:block;text-align:center;margin-top:.5em}#home-footer{text-align:center;font-size:.6em;line-height:normal;opacity:.6}#home-footer p{margin-top:0}.posts-group{display:flex;margin-bottom:1.9em;line-height:normal}.posts-group .post-year{padding-top:6px;margin-right:1.8em;font-size:1.6em;opacity:.6}.posts-group .post-year:hover{text-decoration:underline;cursor:pointer}.posts-group .posts-list{flex-grow:1;margin:0;padding:0;list-style:none}.posts-group .post-item{border-bottom:1px #7d828a dashed}.posts-group .post-item a{display:flex;justify-content:space-between;align-items:baseline;padding:12px 0}.posts-group .post-day{flex-shrink:0;margin-left:1em;opacity:.6}.bg-img{width:100vw;height:100vh;opacity:.03;z-index:-1;position:fixed;top:0;background-attachment:fixed;background-repeat:no-repeat;background-size:cover;background-position:50%;transition:opacity .5s}.show-bg-img{z-index:100;opacity:1;cursor:pointer}.post-header{margin-top:1.2em;line-height:normal}.post-header .post-meta{font-size:.9em;letter-spacing:normal;opacity:.6}.post-header h1{margin-top:.1em}hr.post-end{width:50%;margin-top:1.6em;margin-bottom:.8em;margin-left:0;border-style:solid;border-bottom-width:4px}.content a{word-wrap:break-word;border:none;box-shadow:inset 0 -4px 0 #018574;transition-property:box-shadow;transition-duration:.1s}.content a:hover{box-shadow:inset 0 -1em 0 #018574}.content figure{max-width:100%;height:auto;margin:0;text-align:center}.content figure p{font-size:.8em;font-style:italic;opacity:.6}.content figure.left{float:left;margin-right:1.5em;max-width:50%}.content figure.right{float:right;margin-left:1.5em;max-width:50%}.content figure.big{max-width:100vw}.content img{display:block;max-width:100%;height:auto;margin:auto;border-radius:4px}.content ul,.content ol{padding:0;margin-left:1.8em}.content a.anchor{float:left;margin-left:-20px;padding-right:6px;box-shadow:none;opacity:.8}.content a.anchor:hover{background:0 0;color:#018574;opacity:1}.content a.anchor svg{display:inline-block;width:14px;height:14px;vertical-align:baseline;visibility:hidden}.content a.anchor:focus svg{visibility:visible}.content h1:hover a.anchor svg,.content h2:hover a.anchor svg,.content h3:hover a.anchor svg,.content h4:hover a.anchor svg,.content h5:hover a.anchor svg,.content h6:hover a.anchor svg{visibility:visible}.footnotes{font-size:.85em}.footnotes a{box-shadow:none;text-decoration:underline;transition-property:color}.footnotes a:hover{background:0 0}.footnotes a.footnote-backref{text-decoration:none}.footnotes ol{line-height:1.8}a.footnote-ref{box-shadow:none;text-decoration:none;padding:2px;border-radius:2px;background-color:#2c3e50}a.footnote-ref:hover{box-shadow:none;background-color:#018574;transition-property:background-color}.post-info{font-size:.8rem;line-height:normal;opacity:.6}.post-info p{margin:.8em 0}.post-info a:hover{border-bottom:1px solid #018574}.post-info svg{margin-right:.8em}.post-info .tag{margin-right:.5em}.post-info .tag::before{content:"#"}#toc{position:fixed;left:50%;top:0;display:none}.toc-title{margin-left:1em;margin-bottom:.5em;font-size:.8em;font-weight:700}#TableOfContents{font-size:.8em;opacity:.6}#TableOfContents ul{padding-left:1em;margin:0}#TableOfContents>ul{list-style-type:none}#TableOfContents>ul ul ul{font-size:.9em}#TableOfContents a:hover{border-bottom:#018574 1px solid}.post-nav{display:flex;justify-content:space-between;margin-top:1.5em;margin-bottom:2.5em;font-size:1.2em}.post-nav a{flex-basis:50%;flex-grow:1}.post-nav .next-post{text-align:left;padding-right:5px}.post-nav .prev-post{text-align:right;padding-left:5px}.post-nav .post-nav-label{font-size:.8em;opacity:.8;text-transform:uppercase}.related-posts{padding:.8em;margin-top:1.5em;font-size:.8rem;border:3px dashed rgba(255,255,255,.2);border-radius:5px}.related-posts h2{margin:0;line-height:normal}.related-posts ul{margin-top:.5em;margin-bottom:0}@media(min-width:800px){.site-main{margin-top:3em}hr.post-end{width:40%}}@media(min-width:960px){.site-main{margin-top:6em}}@media(min-width:1300px){.site-main{margin-top:8em}.desktop-only,#toc.show-toc{display:block}.desktop-only-ib{display:inline-block}figure.left{margin-left:-240px}figure.left p{text-align:left}figure.right{margin-right:-240px}figure.right p{text-align:right}figure.big{width:1200px;margin-left:-240px}hr.post-end{width:30%}#toc{top:13em;margin-left:370px;max-width:220px}}@media(min-width:1800px){.site-main{margin-top:10em}.section-inner{max-width:1600px}.thin{max-width:960px}figure.left{max-width:75%;margin-left:-320px}figure.right{max-width:75%;margin-right:-320px}figure.big{width:1600px;margin-left:-320px}hr.post-end{width:30%}#toc{top:15em;margin-left:490px;max-width:300px}}@media(max-width:760px){.hide-in-mobile,.site-nav.hide-in-mobile{display:none}#menu-btn{display:inline-block}.posts-group{display:block}.posts-group .post-year{margin:-6px 0 4px}#spotlight.error-404{flex-direction:column;text-align:center}#spotlight.error-404 .banner-404{margin:0}}@media(max-width:520px){.content figure.left,.content figure.right{float:unset;max-width:100%;margin:0}hr.post-end{width:60%}#mobile-menu{right:1.2em}} +* Copyright (c) 2020 Animate.css*/ +:root { + --animate-duration:1s;--animate-delay:1s;--animate-repeat:1} + +.animated { + animation-duration: 1s; + animation-duration: var(--animate-duration); + animation-fill-mode: both +} + +.animated.infinite { + animation-iteration-count: infinite +} + +.animated.repeat-1 { + animation-iteration-count: 1; + animation-iteration-count: var(--animate-repeat) +} + +.animated.repeat-2 { + animation-iteration-count: 2; + animation-iteration-count: calc(var(--animate-repeat)*2) +} + +.animated.repeat-3 { + animation-iteration-count: 3; + animation-iteration-count: calc(var(--animate-repeat)*3) +} + +.animated.delay-1s { + animation-delay: 1s; + animation-delay: var(--animate-delay) +} + +.animated.delay-2s { + animation-delay: 2s; + animation-delay: calc(var(--animate-delay)*2) +} + +.animated.delay-3s { + animation-delay: 3s; + animation-delay: calc(var(--animate-delay)*3) +} + +.animated.delay-4s { + animation-delay: 4s; + animation-delay: calc(var(--animate-delay)*4) +} + +.animated.delay-5s { + animation-delay: 5s; + animation-delay: calc(var(--animate-delay)*5) +} + +.animated.faster { + animation-duration: .5s; + animation-duration: calc(var(--animate-duration)/2) +} + +.animated.fast { + animation-duration: .8s; + animation-duration: calc(var(--animate-duration)*0.8) +} + +.animated.slow { + animation-duration: 2s; + animation-duration: calc(var(--animate-duration)*2) +} + +.animated.slower { + animation-duration: 3s; + animation-duration: calc(var(--animate-duration)*3) +} + +@media(prefers-reduced-motion:reduce),print { + .animated { + animation-duration: 1ms!important; + transition-duration: 1ms!important; + animation-iteration-count: 1!important + } + + .animated[class*=Out] { + opacity: 0 + } +} + +@keyframes bounce { + 0%,20%,53%,to { + animation-timing-function: cubic-bezier(0.215,0.61,0.355,1); + transform: translateZ(0) + } + + 40%,43% { + animation-timing-function: cubic-bezier(0.755,0.05,0.855,0.06); + transform: translate3d(0,-30px,0)scaleY(1.1) + } + + 70% { + animation-timing-function: cubic-bezier(0.755,0.05,0.855,0.06); + transform: translate3d(0,-15px,0)scaleY(1.05) + } + + 80% { + transition-timing-function: cubic-bezier(0.215,0.61,0.355,1); + transform: translateZ(0)scaleY(0.95) + } + + 90% { + transform: translate3d(0,-4px,0)scaleY(1.02) + } +} + +.bounce { + animation-name: bounce; + transform-origin: center bottom +} + +@keyframes bounceInRight { + 0%,60%,75%,90%,to { + animation-timing-function: cubic-bezier(0.215,0.61,0.355,1) + } + + 0% { + opacity: 0; + transform: translate3d(3000px,0,0)scaleX(3) + } + + 60% { + opacity: 1; + transform: translate3d(-25px,0,0)scaleX(1) + } + + 75% { + transform: translate3d(10px,0,0)scaleX(0.98) + } + + 90% { + transform: translate3d(-5px,0,0)scaleX(0.995) + } + + to { + transform: translateZ(0) + } +} + +.bounceInRight { + animation-name: bounceInRight +} + +@keyframes bounceOutRight { + 20% { + opacity: 1; + transform: translate3d(-20px,0,0)scaleX(0.9) + } + + to { + opacity: 0; + transform: translate3d(2000px,0,0)scaleX(2) + } +} + +.bounceOutRight { + animation-name: bounceOutRight +} + +@keyframes fadeIn { + 0% { + opacity: 0 + } + + to { + opacity: 1 + } +} + +.fadeIn { + animation-name: fadeIn +} + +@keyframes slideInUp { + 0% { + transform: translate3d(0,100%,0); + visibility: visible + } + + to { + transform: translateZ(0) + } +} + +.slideInUp { + animation-name: slideInUp +} + +@keyframes slideOutDown { + 0% { + transform: translateZ(0) + } + + to { + visibility: hidden; + transform: translate3d(0,100%,0) + } +} + +.slideOutDown { + animation-name: slideOutDown +} + +::-webkit-scrollbar { + width: 8px; + height: 8px; + background: #2c3e50 +} + +::-webkit-scrollbar-thumb { + background: #888 +} + +::-webkit-scrollbar-thumb:hover { + background: #e8eef2 +} + +html { + background: #494f5c; + line-height: 1.6; + letter-spacing: .06em; + scroll-behavior: smooth +} + +body,button,input,select,textarea { + color: #e8eef2; + font-family: trebuchet ms,Verdana,verdana ref,segoe ui,Candara,lucida grande,lucida sans unicode,lucida sans,Tahoma,sans-serif +} + +pre,code,pre tt { + font-family: Consolas,andale mono wt,andale mono,Menlo,Monaco,lucida console,lucida sans typewriter,dejavu sans mono,bitstream vera sans mono,liberation mono,nimbus mono l,courier new,Courier,yahei consolas hybrid,monospace,segoe ui emoji,pingfang sc,microsoft yahei +} + +pre { + padding: .7em 1.1em; + overflow: auto; + font-size: .9em; + line-height: 1.5; + letter-spacing: normal; + white-space: pre; + color: #eee; + background: #2c3e50; + border-radius: 4px +} + +pre code { + padding: 0; + margin: 0; + background: #2c3e50 +} + +code { + color: #eee; + background: #7d828a; + border-radius: 3px; + padding: 0 3px; + margin: 0 4px; + word-wrap: break-word; + letter-spacing: normal +} + +blockquote { + border-left: .25em solid; + margin: 1em; + padding: 0 1em; + font-style: italic +} + +blockquote cite { + font-weight: 700; + font-style: normal +} + +blockquote cite::before { + content: "—— " +} + +a { + color: #e8eef2; + text-decoration: none; + border: none; + transition-property: color; + transition-duration: .4s; + transition-timing-function: ease-out +} + +a:hover { + color: #fff; + text-shadow: 0 0 1px #fff +} + +hr { + opacity: .2; + border-width: 0 0 5px; + border-style: dashed; + background: 0 0; + width: 50%; + margin: 1.8em auto +} + +table { + border-collapse: collapse; + border-spacing: 0; + empty-cells: show; + width: 100%; + max-width: 100% +} + +table th,table td { + padding: 1.5%; + border: 1px solid +} + +table th { + font-weight: 700; + vertical-align: bottom +} + +.section-inner { + margin: 0 auto; + max-width: 1200px; + width: 93% +} + +.thin { + max-width: 720px; + margin: auto +} + +.feather { + display: inline-block; + vertical-align: -.125em; + width: 1em; + height: 1em +} + +.sub-menu { + font-size: .7em +} + +.desktop-only,.desktop-only-ib { + display: none +} + +.highlight { + position: relative +} + +.highlight pre { + padding-right: 75px +} + +.highlight-copy-btn { + position: absolute; + bottom: 7px; + right: 7px; + border: 0; + border-radius: 4px; + padding: 1px; + font-size: .7em; + line-height: 1.8; + color: #fff; + background-color: #777; + opacity: .6; + min-width: 55px; + text-align: center +} + +.highlight-copy-btn:hover { + background-color: #666 +} + +.screen-reader-text { + border: 0; + clip: rect(1px,1px,1px,1px); + clip-path: inset(50%); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute!important; + width: 1px; + word-wrap: normal!important +} + +.screen-reader-text:focus { + background-color: #f1f1f1; + border-radius: 3px; + box-shadow: 0 0 2px 2px rgba(0,0,0,.6); + clip: auto!important; + clip-path: none; + color: #21759b; + display: block; + font-size: 14px; + font-size: .875rem; + font-weight: 700; + height: auto; + left: 5px; + line-height: normal; + padding: 15px 23px 14px; + text-decoration: none; + top: 5px; + width: auto; + z-index: 100000 +} + +#site-header { + position: fixed; + z-index: 1; + bottom: 0; + width: 100%; + box-sizing: border-box; + box-shadow: -1px -2px 3px rgba(0,0,0,.45); + background-color: #3b3e48; + animation-duration: .3s +} + +.hdr-wrapper { + display: flex; + justify-content: space-between; + align-items: center; + padding: .5em 0; + font-size: 1.2rem +} + +.hdr-wrapper .site-branding { + display: inline-block; + margin-right: .8em; + font-size: 1.2em +} + +.hdr-wrapper .site-nav { + display: inline-block; + font-size: 1.1em; + opacity: .8 +} + +.hdr-wrapper .site-nav .has-children { + padding-right: .5em; + border-right: 2px solid #7d828a +} + +.hdr-wrapper .site-nav .sub-menu>a { + margin-left: .3em +} + +.hdr-wrapper .site-nav a { + margin-left: .8em +} + +.hdr-icons { + font-size: 1.2em +} + +.hdr-social { + display: inline-block; + margin-left: .6em +} + +.hdr-social>a { + margin-left: .4em +} + +.hdr-btn { + border: none; + background: 0 0; + padding: 0; + margin-left: .4em; + cursor: pointer +} + +#menu-btn { + display: none; + margin-left: .6em; + cursor: pointer +} + +#mobile-menu { + position: fixed; + bottom: 4.8em; + right: 1.5em; + display: none; + padding: .6em 1.8em; + z-index: 1; + box-sizing: border-box; + box-shadow: -1px -2px 3px 0 rgba(0,0,0,.45); + background-color: #3b3e48 +} + +#mobile-menu ul { + list-style: none; + margin: 0; + padding: 0; + line-height: 2; + font-size: 1.2em +} + +#site-footer { + text-align: center; + font-size: .9em; + margin-bottom: 96px; + margin-top: 64px +} + +#site-footer p { + margin: 0 +} + +#spotlight { + display: flex; + min-height: 100vh; + flex-direction: column; + align-items: center; + justify-content: center; + max-width: 93%; + margin: auto; + font-size: 1.5rem +} + +#spotlight.error-404 { + flex-direction: row; + line-height: normal +} + +p.img-404 { + margin: 0 +} + +p.img-404 svg { + width: 180px; + max-width: 100%; + height: auto +} + +.banner-404 { + margin-left: 2em +} + +.banner-404 h1 { + font-size: 3em; + margin: .5rem 0 +} + +.banner-404 p { + margin-top: 0 +} + +.banner-404 .btn-404 { + font-size: .8em +} + +.banner-404 .btn-404 a { + display: inline-block; + border: 2px solid #e8eef2; + border-radius: 5px; + padding: 5px; + transition-property: color,border-color +} + +.banner-404 .btn-404 a:first-child { + margin-right: 1em +} + +.banner-404 .btn-404 a:hover { + border-color: #fff +} + +.banner-404 .btn-404 a svg { + margin-right: .5em +} + +#home-center { + display: flex; + flex-grow: 1; + flex-direction: column; + justify-content: center +} + +#home-title { + margin: 0; + text-align: center +} + +#home-subtitle { + margin-top: 0; + margin-bottom: 1.5em; + text-align: center; + line-height: normal; + font-size: .7em; + font-style: italic; + opacity: .9 +} + +#home-social { + font-size: 1.4em; + text-align: center; + opacity: .8 +} + +#home-social a { + margin: 0 .2em +} + +#home-nav { + opacity: .8 +} + +#home-nav a { + display: block; + text-align: center; + margin-top: .5em +} + +#home-footer { + text-align: center; + font-size: .6em; + line-height: normal; + opacity: .6 +} + +#home-footer p { + margin-top: 0 +} + +.posts-group { + display: flex; + margin-bottom: 1.9em; + line-height: normal +} + +.posts-group .post-year { + padding-top: 6px; + margin-right: 1.8em; + font-size: 1.6em; + opacity: .6 +} + +.posts-group .post-year:hover { + text-decoration: underline; + cursor: pointer +} + +.posts-group .posts-list { + flex-grow: 1; + margin: 0; + padding: 0; + list-style: none +} + +.posts-group .post-item { + border-bottom: 1px #7d828a dashed +} + +.posts-group .post-item a { + display: flex; + justify-content: space-between; + align-items: baseline; + padding: 12px 0 +} + +.posts-group .post-day { + flex-shrink: 0; + margin-left: 1em; + opacity: .6 +} + +.bg-img { + width: 100vw; + height: 100vh; + opacity: .03; + z-index: -1; + position: fixed; + top: 0; + background-attachment: fixed; + background-repeat: no-repeat; + background-size: cover; + background-position: 50%; + transition: opacity .5s +} + +.show-bg-img { + z-index: 100; + opacity: 1; + cursor: pointer +} + +.post-header { + margin-top: 1.2em; + line-height: normal +} + +.post-header .post-meta { + font-size: .9em; + letter-spacing: normal; + opacity: .6 +} + +.post-header h1 { + margin-top: .1em +} + +hr.post-end { + width: 50%; + margin-top: 1.6em; + margin-bottom: .8em; + margin-left: 0; + border-style: solid; + border-bottom-width: 4px +} + +.content a { + word-wrap: break-word; + border: none; + box-shadow: inset 0 -4px 0 #018574; + transition-property: box-shadow; + transition-duration: .1s +} + +.content a:hover { + box-shadow: inset 0 -1em 0 #018574 +} + +.content figure { + max-width: 100%; + height: auto; + margin: 0; + text-align: center +} + +.content figure p { + font-size: .8em; + font-style: italic; + opacity: .6 +} + +.content figure.left { + float: left; + margin-right: 1.5em; + max-width: 50% +} + +.content figure.right { + float: right; + margin-left: 1.5em; + max-width: 50% +} + +.content figure.big { + max-width: 100vw +} + +.content img { + display: block; + max-width: 100%; + height: auto; + margin: auto; + border-radius: 4px +} + +.content ul,.content ol { + padding: 0; + margin-left: 1.8em +} + +.content a.anchor { + float: left; + margin-left: -20px; + padding-right: 6px; + box-shadow: none; + opacity: .8 +} + +.content a.anchor:hover { + background: 0 0; + color: #018574; + opacity: 1 +} + +.content a.anchor svg { + display: inline-block; + width: 14px; + height: 14px; + vertical-align: baseline; + visibility: hidden +} + +.content a.anchor:focus svg { + visibility: visible +} + +.content h1:hover a.anchor svg,.content h2:hover a.anchor svg,.content h3:hover a.anchor svg,.content h4:hover a.anchor svg,.content h5:hover a.anchor svg,.content h6:hover a.anchor svg { + visibility: visible +} + +.footnotes { + font-size: .85em +} + +.footnotes a { + box-shadow: none; + text-decoration: underline; + transition-property: color +} + +.footnotes a:hover { + background: 0 0 +} + +.footnotes a.footnote-backref { + text-decoration: none +} + +.footnotes ol { + line-height: 1.8 +} + +a.footnote-ref { + box-shadow: none; + text-decoration: none; + padding: 2px; + border-radius: 2px; + background-color: #2c3e50 +} + +a.footnote-ref:hover { + box-shadow: none; + background-color: #018574; + transition-property: background-color +} + +.post-info { + font-size: .8rem; + line-height: normal; + opacity: .6 +} + +.post-info p { + margin: .8em 0 +} + +.post-info a:hover { + border-bottom: 1px solid #018574 +} + +.post-info svg { + margin-right: .8em +} + +.post-info .tag { + margin-right: .5em +} + +.post-info .tag::before { + content: "#" +} + +#toc { + position: fixed; + left: 50%; + top: 0; + display: none +} + +.toc-title { + margin-left: 1em; + margin-bottom: .5em; + font-size: .8em; + font-weight: 700 +} + +#TableOfContents { + font-size: .8em; + opacity: .6 +} + +#TableOfContents ul { + padding-left: 1em; + margin: 0 +} + +#TableOfContents>ul { + list-style-type: none +} + +#TableOfContents>ul ul ul { + font-size: .9em +} + +#TableOfContents a:hover { + border-bottom: #018574 1px solid +} + +.post-nav { + display: flex; + justify-content: space-between; + margin-top: 1.5em; + margin-bottom: 2.5em; + font-size: 1.2em +} + +.post-nav a { + flex-basis: 50%; + flex-grow: 1 +} + +.post-nav .next-post { + text-align: left; + padding-right: 5px +} + +.post-nav .prev-post { + text-align: right; + padding-left: 5px +} + +.post-nav .post-nav-label { + font-size: .8em; + opacity: .8; + text-transform: uppercase +} + +.related-posts { + padding: .8em; + margin-top: 1.5em; + font-size: .8rem; + border: 3px dashed rgba(255,255,255,.2); + border-radius: 5px +} + +.related-posts h2 { + margin: 0; + line-height: normal +} + +.related-posts ul { + margin-top: .5em; + margin-bottom: 0 +} + +@media(min-width: 800px) { + .site-main { + margin-top:3em + } + + hr.post-end { + width: 40% + } +} + +@media(min-width: 960px) { + .site-main { + margin-top:6em + } +} + +@media(min-width: 1300px) { + .site-main { + margin-top:8em + } + + .desktop-only,#toc.show-toc { + display: block + } + + .desktop-only-ib { + display: inline-block + } + + figure.left { + margin-left: -240px + } + + figure.left p { + text-align: left + } + + figure.right { + margin-right: -240px + } + + figure.right p { + text-align: right + } + + figure.big { + width: 1200px; + margin-left: -240px + } + + hr.post-end { + width: 30% + } + + #toc { + top: 13em; + margin-left: 370px; + max-width: 220px + } +} + +@media(min-width: 1800px) { + .site-main { + margin-top:10em + } + + .section-inner { + max-width: 1600px + } + + .thin { + max-width: 960px + } + + figure.left { + max-width: 75%; + margin-left: -320px + } + + figure.right { + max-width: 75%; + margin-right: -320px + } + + figure.big { + width: 1600px; + margin-left: -320px + } + + hr.post-end { + width: 30% + } + + #toc { + top: 15em; + margin-left: 490px; + max-width: 300px + } +} + +@media(max-width: 760px) { + .hide-in-mobile,.site-nav.hide-in-mobile { + display:none + } + + #menu-btn { + display: inline-block + } + + .posts-group { + display: block + } + + .posts-group .post-year { + margin: -6px 0 4px + } + + #spotlight.error-404 { + flex-direction: column; + text-align: center + } + + #spotlight.error-404 .banner-404 { + margin: 0 + } +} + +@media(max-width: 520px) { + .content figure.left,.content figure.right { + float:unset; + max-width: 100%; + margin: 0 + } + + hr.post-end { + width: 60% + } + + #mobile-menu { + right: 1.2em + } +} + +#check-flag-btn { + float: right; + padding: 10px 15px; + cursor: pointer; + text-align: center; + text-decoration: none; + outline: none; + color: #fff; + background-color: #999; + border: none; + border-radius: 15px; + box-shadow: 0 9px #444; +} + +#check-flag-btn:hover { + background-color: #3e8e41 +} + +#check-flag-btn:active { + background-color: #3e8e41; + box-shadow: 0 5px #666; + transform: translateY(4px); +} + + +#check-flag-btn span { + cursor: pointer; + display: inline-block; + position: relative; + transition: 0.5s; +} + +#check-flag-btn span:after { + content: '\00bb'; + position: absolute; + opacity: 0; + top: 0; + right: -20px; + transition: 0.5s; +} + +#check-flag-btn:hover span { + padding-right: 25px; +} + +#check-flag-btn:hover span:after { + opacity: 1; + right: 0; +} + +#single-level-checker { + clear: both; +} +/* necessary to give position: relative to parent. */ +input[type="text"]{font: 15px/24px 'Muli', sans-serif; color: #ededed; width: 100%; box-sizing: border-box; letter-spacing: 1px;} +input[type="text"]::placeholder{color: #ccc;} + +:focus{outline: none;} + +.input-effect{float: left; width: 70%; position: relative;} /* necessary to give position: relative to parent. */ + +.effect-8 ~ .focus-border:before, +.effect-8 ~ .focus-border:after{content: ""; position: absolute; top: 0; left: 0; width: 0; height: 2px; background-color: #eee; transition: 0.3s;} +.effect-8 ~ .focus-border:after{top: auto; bottom: 0; left: auto; right: 0;} +.effect-8 ~ .focus-border i:before, +.effect-8 ~ .focus-border i:after{content: ""; position: absolute; top: 0; left: 0; width: 2px; height: 0; background-color: #eee; transition: 0.4s;} +.effect-8 ~ .focus-border i:after{left: auto; right: 0; top: auto; bottom: 0;} +.effect-8:focus ~ .focus-border:before, +.effect-8:focus ~ .focus-border:after{width: 100%; transition: 0.3s;} +.effect-8:focus ~ .focus-border i:before, +.effect-8:focus ~ .focus-border i:after{height: 100%; transition: 0.4s;} + +.effect-10-bad, +.effect-10-good{border: 0; padding: 7px 15px; border: 1px solid #ccc; position: relative; background: transparent;} + +.effect-10-bad ~ .focus-bg{position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-color: red; opacity: 0; transition: 0.5s; z-index: -1;} +.effect-10-bad:focus ~ .focus-bg{transition: 0.5s; opacity: 0.2;} + +.effect-10-good ~ .focus-bg{position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-color: #4caf50; opacity: 0.2; transition: 0.5s; z-index: -1;} From 8026129095218b66b410a01871f3a9d4c75c37ee Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sat, 19 Sep 2020 17:37:36 +0300 Subject: [PATCH 05/14] Finished with level component for now and creating it from a list --- submit-flags/submit-flags-app/src/Level.rs | 101 ++++++++++++------ submit-flags/submit-flags-app/src/lib.rs | 39 +++++-- .../submit-flags-app/static/css/style.css | 13 ++- .../submit-flags-app/static/favicon.ico | Bin 0 -> 15086 bytes 4 files changed, 111 insertions(+), 42 deletions(-) create mode 100644 submit-flags/submit-flags-app/static/favicon.ico diff --git a/submit-flags/submit-flags-app/src/Level.rs b/submit-flags/submit-flags-app/src/Level.rs index 60ba090..9758209 100644 --- a/submit-flags/submit-flags-app/src/Level.rs +++ b/submit-flags/submit-flags-app/src/Level.rs @@ -57,57 +57,96 @@ impl Component for LevelComponent { flag: props.flag, // The initial user flag is empty user_flag: "".to_string(), - // This is passed from the parent component as well, but has a default value of `false`. + // This has a default value of `false`. Not passed from parent flag_correct: props.flag_correct, } } + // See https://yew.rs/docs/en/concepts/components/#update fn update(&mut self, msg: Self::Message) -> ShouldRender { + log::debug!("Updating level {} component", self.name.clone()); + // Do something different depending on the update message. match msg { LevelMsg::CheckFlag => { - // TODO - hash - self.flag_correct = self.user_flag == self.flag + log::debug!("In level {}, checking flag", self.name.clone()); + // TODO - Change this to hash instead of flag + self.flag_correct = self.user_flag == self.flag; + true // Re-render } - LevelMsg::UserFlagChanged(value) => { - log::debug!("In level {}, User flag changed from {} to {}", self.name.clone(), self.user_flag.clone(), value.clone()); - self.user_flag = value; + LevelMsg::UserFlagChanged(new_user_flag) => { + log::debug!("In level {}, User flag changed from {} to {}", self.name.clone(), self.user_flag.clone(), new_user_flag.clone()); + self.user_flag = new_user_flag; self.update(LevelMsg::CheckFlag); + true // Re-render } } - true } + // See https://yew.rs/docs/en/concepts/components/#change + // We're not using "change" fn change(&mut self, _props: Self::Properties) -> ShouldRender{ - // Should only return "true" if new properties are different to - // previously received properties. - // This component has no properties so we will always return "false". + log::debug!("Changing level {} component", self.name.clone()); false } + // See https://yew.rs/docs/en/concepts/components/#view + // In this method we're declaring what the element looks like. This is very reminiscent of JSX and React. fn view(&self) -> Html { + log::debug!("Viewing level {} component", self.name.clone()); + + // TODO - move to "create" let label_text = self.name.clone() + "'s flag goes here 🚩"; + let input_id = self.name.clone() + "-id"; + + // Creating the element as variables makes it clearer - similar to functional elements in react + + // This element just prints the component info to make it easier to develop. Will delete soon :) + let debug_info_element = html! { +
+                { 
+                    format!("DEBUG: I am a level component! Name: {} | Flag: {} | Status: {}", 
+                        self.name.clone(),
+                        self.flag.clone(),
+                        self.flag_correct) 
+                }
+                
+
+ }; + + // This element is the input for the flag. + let input_element = html! { +
+ + // Cosmetics + +
+ }; + + // This element is for a11y - don't indicate status with color only, but with an emoji as well. + let status_element = html! { +
 { get_correct_emoji(self.flag_correct) }
+ }; + + // This is the complete HTML component we're returning from `view`. html! { - <> - -
-                        {"DEBUG: I am a level component! Name: "}{self.name.clone()}
-                        {" | Flag: "}{self.flag.clone()}
-                        {" | Status: "}{get_correct_emoji(self.flag_correct)}
-                    
-
-
-
- - - - - -
- // TODO - delete button - -
-
- + + // TODO - delete this + { debug_info_element } +
+ { input_element } + { status_element } +
+
} } } diff --git a/submit-flags/submit-flags-app/src/lib.rs b/submit-flags/submit-flags-app/src/lib.rs index f445dd5..4a6545d 100644 --- a/submit-flags/submit-flags-app/src/lib.rs +++ b/submit-flags/submit-flags-app/src/lib.rs @@ -8,28 +8,41 @@ use yew::prelude::{Component, ComponentLink, ShouldRender, Html, html, App}; use level::LevelComponent; use wasm_logger; -struct Model { +struct LevelInfo { + name: String, + flag: String, +} + +struct SubmitFlagsPage { link: ComponentLink, - value: i64, + levels: Vec, + all_flags_done: bool, } enum Msg { - AddOne, + CheckAllFlags, } -impl Component for Model { +impl Component for SubmitFlagsPage { type Message = Msg; type Properties = (); fn create(_: Self::Properties, link: ComponentLink) -> Self { + // TODO change to read from file + let const_level_1 = LevelInfo {name: "name1".to_string(), flag: "flag1".to_string()}; + let const_level_2 = LevelInfo {name: "name2".to_string(), flag: "flag2".to_string()}; + + let levels_info_vector = vec![const_level_1, const_level_2]; + Self { - link, - value: 0, + link: link, + levels: levels_info_vector, + all_flags_done: false, } } fn update(&mut self, msg: Self::Message) -> ShouldRender { match msg { - Msg::AddOne => self.value += 1 + Msg::CheckAllFlags => self.all_flags_done = !self.all_flags_done } true } @@ -46,8 +59,8 @@ impl Component for Model { <>

{ "Make Git Better CTF - Submit Flags" }

-
- +
+ { for self.levels.iter().map(create_component_from_level_info) }
@@ -55,8 +68,14 @@ impl Component for Model { } } +fn create_component_from_level_info(level_info: &LevelInfo) -> Html { + html! { + + } +} + #[wasm_bindgen(start)] pub fn run_app() { wasm_logger::init(wasm_logger::Config::default()); - App::::new().mount_to_body(); + App::::new().mount_to_body(); } diff --git a/submit-flags/submit-flags-app/static/css/style.css b/submit-flags/submit-flags-app/static/css/style.css index b3af66c..cf4031b 100644 --- a/submit-flags/submit-flags-app/static/css/style.css +++ b/submit-flags/submit-flags-app/static/css/style.css @@ -1556,7 +1556,13 @@ input[type="text"]::placeholder{color: #ccc;} :focus{outline: none;} -.input-effect{float: left; width: 70%; position: relative;} /* necessary to give position: relative to parent. */ +.input-effect{ + float: left; + width: 70%; + margin-left: 3%; + margin-right: 3%; + position: relative; +} .effect-8 ~ .focus-border:before, .effect-8 ~ .focus-border:after{content: ""; position: absolute; top: 0; left: 0; width: 0; height: 2px; background-color: #eee; transition: 0.3s;} @@ -1576,3 +1582,8 @@ input[type="text"]::placeholder{color: #ccc;} .effect-10-bad:focus ~ .focus-bg{transition: 0.5s; opacity: 0.2;} .effect-10-good ~ .focus-bg{position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-color: #4caf50; opacity: 0.2; transition: 0.5s; z-index: -1;} + +.status { + width: max-content; + text-align: center; +} diff --git a/submit-flags/submit-flags-app/static/favicon.ico b/submit-flags/submit-flags-app/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..ec3f6270a2f0bbeee124763f754c755dd4c50f9b GIT binary patch literal 15086 zcmcJW2Y6IP*T-)X2t`WhMM`MWyVOYUARy8#SSSi2hzROyL4u%QL5(0_0Z|cAX##eH zC`F|RNRc8CI)tEsK=g%>dH=sjCM?S)tbTm^JU6p@@7$Sl&Ybc;XC|H(;)Qv2>Ue4+ zy?K>9uc7C8k&)@YBRp@P?nS8O`H*a$_q;k(Q6IhH-K&fH_ERyfxmJIR=F1sUD0lW^ z`E!JH&YRtvls`OVWufb`A1P7bx|FiT3YdzeiTJ@dzdG!jjdga&Vi_bnav)`I(#y&C1+ZJGV2_UVG8}^7AT_aO{9N^T%;>;>dopXUArHZ%yns zW|e4Pw`Qf;zGb~RynmNTOgL(b?`|E<*#A6YmMr+l#Qna*oIZ8TY}@p+d2Z|zreUorNd;a)lYm&`6$_ zU%DsSOo5!CwvTkjMAffpX1zJZtX~^z$7GEjdw3|k?-39EG*DRb=L@8*znV8KW4nTH=3Xz9GIM;FtjZF5sBe=Z|i2eZFHuS>=YenT(`QZ zU*iTdO!F2O?Utq>=+`>hwUTTs&-dQmOF9;zbqO;yE0r~`OnTPD%Z7Gt-DvK)t>*>x zo0dD&lXngNBROUl?N7ICQvYvk@Q?VzcHBy3iW}y?tIsW_VyU9$p&= z|7SP1YhfyvEn$k~%VqBD)yfb3e;(96!9zj2d>oDISsA>g^5Up>lge3<85)gJO>^IA(EkA3&m5=%S& zjd>jn_>@}mFC!#-Xms?_2VT&HR!wiT>r&zRBBoc@4p!f|Pha}*K03%d#5d8FF^Bw| zDP1_fY1z1*`OhQ6%m&%Skpp|oy|?#Fl3e>`wFY^@yb_9=tBEuCM*NDSoz~HKYnOJ` z4v?kq{W?|3TYrQ-7M2{L7kyy8?(Egw@{#*g+bn(WpBRL_hD+0e7nH|xtjeD_Vo{Csx39gWPvn1lP@nWnS)%`@)f z_XphBTkqbFO6L#%_Ui`o!-}uW5Yb3Hx~V~JBRpe$ zH)CS2jE66Gd?8D%ujNU2$j*yD?R!gS8!wI>+G~>jJYmkAK53HABxGFJ*3f}>**H+^ zhDd8;=nr(ev280^JoETS>qnj#HT+CI#c97a2$6kGB_1)?EuGp}zH3x0WAN{T?!L{m zkUpXd;@|Y_(%#O&JlJu+-nZDf`CTa82A+?8(vAha(2oE0zj*fMl*v}s$mTMd)Af~m zjEio4`^94O>&73UWr^s|z)OOxRpkc`9^CtzdHao5(_#_&D_JrwII>UoPFBYF6Kt|{ zk^H89^-A_0>tkuxJghyK2wZSBG5!jgeR*I#@XzH%8}DaEKljliXaHZmIN_Ny^6#zO z8tD0`*I$0#926$Nclg?+;~!pby0MnUj>x=mooc3!^aDSR%$Xld@ahW_EbW=e+?Rg% z0|P~AT=XU@`Wass++thoxp||yQ_|_r+o*Q6tJQCXT$El*ZQqsXonB_5gTt4o)%Wfl771e>VqFUabzF9b-U;<6XJOd9vIN?1nUA8 z17BTD4*15l%^I2hcl5IBz&rR(C5%P_%Ufd%SYz}(SV%50O0Y( zpMIDuEZkEuaM;Z0F)7$ZweqE`&Ifwe_a5Vbi?MTZ5oC9da2VL$(RUf$k~MS^Q;;`y z8!vhN@ZFd89c;kGY2SGQeMgU9ePO)8PYQ>`$ZySF`~A1pHxthTy%XpjJYcH>`}H*? zBCaz}Jvt&&*~1gQqwg(UuswsW&Yk_1jTz7m&XGP}VRwP%Wv%gv#Dh`82d`1Q`1yC` zLZcrVa#h{&9}gb-3Cj^rCr=z_VJ)}O|#e&eTlbZ%>!D6e`;eiyxP^h3AvpU@AELf60r z;16ORJh3h-zFumG)5s&M`FQ5meU)PD!jC_=_~?iS&X9|e+xf=StBu2R=x^P$f$7k) zshK0Z;plg?BU|D)x%=1q%+8!{I*8LvNop#M~@V)TBv+|^MuUw%GX-|Lz-Q4K9NK%auuA?C&2SUZiA2*yT6!Se5T zU>q(84DF5ZgVV>HGxnOD5o!Sbq=?!7GtmHt9ad8GZ>Q$>~ z?VlLJS`gda9GSJePcEUl%i*B|?|zeYz=t?}&Fnq@`}n_(Eu*XtB_G5Olc!v59LEFw zIw}8sKsi7Y=?`@-|2eMsExCc?zFN8fJ6j?74cxL}?Rj#xU@#QpA$#)H<_+sw-$(5Y z8z82?F#ee<>6c8gT`OO`U$S6cQ|pFub{uL0Jhj&aE}WUu{&) zQaSo0;fGqPwH^jP2kU=Xcl5rsur@j4JJVh>jTAqrBTkQb`7-^1SFjR&S+5&aGlCcL zbMgvmF~qj_r4R2+kGZNJWX(FaZf5rF+L|OE-9;EQr((}iVjy@XSo@&{GGstMyAJph zY?w8rc1Awq^cY-+DCq@reEJ*FpA~+jB{>U-<$F`@v8M@S`V|jqqU?Taa?|S614Ma-y$kfwW{4%jB+)o-wR<8ZR$f!|=0sQSvw)sG8ah`ab4@HRCJFc0w<8>bJk z3|nxvpRwN3S8Rp4hQ@wdaVB6LbjX0aBDA(k*~6kHPK`cei~#mPF2r66I^$?}E%(^# zLWi)qI#nttR^4RtOkxeSE$X1q&2#JnJ0h0*&&wF;@o}dxVpS*aK&^6ux~jeJ8K`)2 zwVDPxbf0Ql(2VXo+G)X}#5ME>jDsDwR<1xT5j{ZnYs#*n8Nd6R>iy(l)T5!-$qoL% zDaw`4{`cIUS=MhIKf=?u3X>+ZYu5O(vD}!hW$Xqut5_WFzXs3`7J?S~Ia`2dep5rj z_R%%^x)xh^^us$b>HT-6rD*(#>Jw0J9Nn%hu05{e{`Ih9hjdz7V1{;?B1c}dit@4t^Z(LY(aff<(FrkPfpC|=nJM*J{xxY z@ZKR`FJ15l^|8!&K(5Ra^U`y+?&iA=@WB29wdT3+y=}f;w$ReeeJ-bOe5aO+Z$ht_ zix{Waa#4BOyBGhPR45pl{rfCgFgH{+n?Axv`@sjSC-nNtpYPlQC)tyRap9g+xo`%YCmTS-DB610| z7Vv;xgLSA4`09o)y=N0I)!nFPvG+Yt_y@aS3~;<^YjIkWLB^zBQRfdtUoiJl&z~(U z%<<%`8LuBChU1SdAAb1;J_|ptezq>aR)57R~$o&xE7hG)G2V|C%$9KA`3Z{p)OE6~*e&%KyKU9OH#^QqV2d z1wFuj5Z{n3bfO=82m6!jN>7r6^R_IRKlioxqX$}P%-sK9^ky}#=FPiz*S3bAd^l^k zFwgs{53bX?99GOpQa+ffc$%uZaB|c7HIECgZk4ZH^w!i@qLn{&N;#igLhq{t{_j7= z#D*E;c_!8K#?t=nd1Yziv`I!A>v>_c(VBx+jW^cw)ZO!1cwP!^S?6Ym$jlin*07O_pb3LI(&ISu>A6(SYOGrun{j@M zb4Z-0;;akt^xZe7qz>ud@1%U?g1q5j>}P5V_Md-rc&x1O#T&|j6RCYMr)=8p@00VB zyHV?4e}kL|EWo}Md5m&Kd$voM<80lk72lIQSdO4SIv3JR@oGJL(yE17pHF=dY!B9V zzQ_6_>yy&X06AYozOFq8i{p@&a_H@a^5pd2+YLxhzVOfveY$tvPpy?)jx#*qTUug? z_2=oB-_7rE1O5;HPJetRds3XAq7JNmwd19VMDz_fY5L9ULcJ)-a4&UNas+qq327wfSQu3+2m94yCmRu~xp{kf(zy@%7*? zYKa*&7>!4MPCg6%B-W94u_ovP`>)Ujos2;~Ngr}#bo8+iL$~J&y+R*Cr6-fwPaq~z z!*H>aoROLozllNA#KAK3VNTX${6=2nH`2%Xnp^J0tEQUEe&sSYhC?U(fr*_Akq7x5@+Z!7h9R>(QRJW; z$DX|ilxMPLoW)a3AYS`0QL1l0sQsiA_7344I^E^u%zCqTz}(c9IB&;1zWCM2f%}|M zxJPU4Kc168UD?RWZu5~n!Cm;{x4z~B5n>})BOynx8-o9!#zaL@f))(wA4U77t^H^0j>$gw!rC7f&bV5ds| zq+@C8=_5}!E`D;Pa!dL31f7#i9HTQZneEH)4lV8<#3!+#80pf z%lTLEZ6J9v9y)-XDPE<{`(S2DFg5m4I`hQ*)D-?oI%Q@3h-&22s>sb3iC@kyQKMte z5`A^P(edqCd-H%-UcpRm+vKc_MP4-bW1) z+5*Xdci7kA{Im8R|KN;4R`W9k@q`>f@!jTT=quwuE6?!>)KF=;s2kyX{cA?_=RNBB zlK;g|K74l@wePIv=N;+`yo2qtUy3dI%L#d~w&*H0OAZu>fBLl1**VGoxMJ*wjOlps zAJg$pU50aek7-XCT!1ffeVtz-CLkyLDErXpSRfsMSM+q#`ZX(FpFDApXgCk8E>Cmg z(n1&h0v|Y3`x&gSWM*R=v@s{o(0%*^wNiXHafrQC>XQC4fd6q%KAO6F`=&9&?(bhw zI=!118b}7{7J9^aHhds9j*fFSkN(68?3dU_+=pLc6LEvR7-F*T{8paoBmZzrXOg@Cp>w|?k+`O*k9qL!u9T&bH+o3-?&Y4rXciEOFu_1Eo%lZ;6iw3?&PF|3_YY?jK8DCTCZ9qt0$Z# zTBbcO@EAOCRf)VFf4AMF{v8mHYmce+$8%9 zM63c2gN37?7!|McHbK>yHEvz$`bzoRRAkTPXh1g9k2xm-9qu9~!ZR)SgS}I1lbWCQ zu6G?d@O$@vQd78iKDoGX(IlNiibuxq4;{3`B77mX=x_+wgFHj=T(#joc3#EHbK+xe z;;G{`0?GPn_e5{lp5L}K(jMas`JOn{OH%N6PA23V_v_3L7yI?ty7V+v{x4oNmxb%r z#CFvf|K~hdV`SU5d3{yUF|7Zcz2~=TQh%p(AyMmkQS_v0pDIbdZ=cSQex-WanAI!4 zX&mglCNYOG(IK`>quN6Y$_Xkh6dr4k5Au_*!1_~G!|nrHgnjL zkmfXr`3;6hSt3$bMx8F|=TGp*Y>s~p+0@oiZ zl_^}K;rJWo!~WSHXP=oGFn$7z4L#b2I-|JIOXsmZ!Z#C_ zzzF11(7^sJ?^!!m{>E8p@=bgX7dS>|1eQ*EcFZA{BcT6`O+H30r9BjT=7=-a^WJ~Q zo_(-*O8uBi>ysdPULYnpzs?x^pAz+rAC;3*CtxoYAB!)?&gl!L;vAi1e%=>z@jmAP zRFhYIpo#6rnJ@k)fiq9M$NThCo|>w2?g#LLc7Ek}&_dj1UjC1e{)~?v5TD@>JjA+c z?s)C_eLzm@_Db+c)D_5wSOajt&23v)tVI2tG2ww$XPOr4Oh-G#&;-j@`uyx8^8a3( zqo=0C83u3=Sd6%d{3VOit7E_Efe(-_yh3bc4Cvsj!1=#UTR%_CXRX*vW#5AT8zTo6 ze>3%NPV`v&kVRF8Sctx{KFEr_H1^EUwMV53_)zvfz`OFJD|YYLqAKjA&(=-riplO` zWHWyv8)Q!$PFj{BKbaCqsx;Au>87?YcDL<=@C literal 0 HcmV?d00001 From 42cfeb126be89113e333fe232c4f122f3d5f7dc4 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 20 Sep 2020 01:47:35 +0300 Subject: [PATCH 06/14] Added hashing of flags as an anti-cheating mechanism --- submit-flags/submit-flags-app/Cargo.toml | 2 ++ submit-flags/submit-flags-app/src/Level.rs | 32 ++++++++++++++-------- submit-flags/submit-flags-app/src/lib.rs | 12 ++++++-- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/submit-flags/submit-flags-app/Cargo.toml b/submit-flags/submit-flags-app/Cargo.toml index ade6f2f..ceec3ba 100644 --- a/submit-flags/submit-flags-app/Cargo.toml +++ b/submit-flags/submit-flags-app/Cargo.toml @@ -14,3 +14,5 @@ yew = "0.17" wasm-bindgen = "0.2.67" log = "0.4.6" wasm-logger = "0.2.0" +sha2 = "0.9.1" +console_error_panic_hook = "0.1.6" diff --git a/submit-flags/submit-flags-app/src/Level.rs b/submit-flags/submit-flags-app/src/Level.rs index 9758209..388177f 100644 --- a/submit-flags/submit-flags-app/src/Level.rs +++ b/submit-flags/submit-flags-app/src/Level.rs @@ -1,4 +1,7 @@ use log; +use std::str; + +use sha2::{Sha256, Digest}; use yew::prelude::{Component, ComponentLink, Properties, html, Html, ShouldRender}; use yew::html::InputData; @@ -13,7 +16,7 @@ pub struct LevelComponent { // The user's guess for the flag, that they are typing user_flag: String, // Whether the correct flag has been entered. - flag_correct: bool, + is_flag_correct: bool, } // These are the messages (think "events") that can happen in this component. @@ -35,7 +38,7 @@ pub struct LevelProps { pub flag: String, // This prop indicates whether the user's flag is correct. Not passed from parent, but rather used to communicate back to it from the level. #[prop_or(false)] - pub flag_correct: bool, + pub is_flag_correct: bool, } // See https://yew.rs/docs/en/concepts/components/ @@ -58,23 +61,30 @@ impl Component for LevelComponent { // The initial user flag is empty user_flag: "".to_string(), // This has a default value of `false`. Not passed from parent - flag_correct: props.flag_correct, + is_flag_correct: props.is_flag_correct, } } // See https://yew.rs/docs/en/concepts/components/#update fn update(&mut self, msg: Self::Message) -> ShouldRender { - log::debug!("Updating level {} component", self.name.clone()); // Do something different depending on the update message. match msg { LevelMsg::CheckFlag => { - log::debug!("In level {}, checking flag", self.name.clone()); - // TODO - Change this to hash instead of flag - self.flag_correct = self.user_flag == self.flag; + // Hash the user flag to an array + let hashed_user_flag_arr = Sha256::digest(self.user_flag.as_bytes()); + // Cast the array to a string + let hashed_user_flag_str: String = format!("{:x}", hashed_user_flag_arr); + log::debug!("update::{}, user flag {}, user hash {}, actual flag hash {}", + self.name.clone(), + self.user_flag.clone(), + hashed_user_flag_str.clone(), + self.flag.clone()); + // Compare user hash to our hash + self.is_flag_correct = hashed_user_flag_str == self.flag; true // Re-render } LevelMsg::UserFlagChanged(new_user_flag) => { - log::debug!("In level {}, User flag changed from {} to {}", self.name.clone(), self.user_flag.clone(), new_user_flag.clone()); + log::debug!("update::{}, User flag changed from {} to {}", self.name.clone(), self.user_flag.clone(), new_user_flag.clone()); self.user_flag = new_user_flag; self.update(LevelMsg::CheckFlag); true // Re-render @@ -107,7 +117,7 @@ impl Component for LevelComponent { format!("DEBUG: I am a level component! Name: {} | Flag: {} | Status: {}", self.name.clone(), self.flag.clone(), - self.flag_correct) + self.is_flag_correct) }
@@ -121,7 +131,7 @@ impl Component for LevelComponent { /* Change the background colour effect according to the status. If the flag is correct, the class will be "effect-8 effect-10-good", * which paints the BG of the text box green (and stays). Otherwise, paint it in red (as long as it's in focus). */ - class={ format!("effect-8 effect-10-{}", if self.flag_correct { "good" } else { "bad" }) } + class={ format!("effect-8 effect-10-{}", if self.is_flag_correct { "good" } else { "bad" }) } type="text" placeholder={label_text.clone()} // Whenever the user inputs something into the box, notify this LevelComponent that the user flag has changed. @@ -134,7 +144,7 @@ impl Component for LevelComponent { // This element is for a11y - don't indicate status with color only, but with an emoji as well. let status_element = html! { -
 { get_correct_emoji(self.flag_correct) }
+
 { get_correct_emoji(self.is_flag_correct) }
}; // This is the complete HTML component we're returning from `view`. diff --git a/submit-flags/submit-flags-app/src/lib.rs b/submit-flags/submit-flags-app/src/lib.rs index 4a6545d..c5dd0d3 100644 --- a/submit-flags/submit-flags-app/src/lib.rs +++ b/submit-flags/submit-flags-app/src/lib.rs @@ -1,5 +1,7 @@ // See https://github.com/yewstack/yew/issues/97 #![recursion_limit="256"] +use console_error_panic_hook; +use std::panic; mod level; @@ -28,8 +30,8 @@ impl Component for SubmitFlagsPage { type Properties = (); fn create(_: Self::Properties, link: ComponentLink) -> Self { // TODO change to read from file - let const_level_1 = LevelInfo {name: "name1".to_string(), flag: "flag1".to_string()}; - let const_level_2 = LevelInfo {name: "name2".to_string(), flag: "flag2".to_string()}; + let const_level_1 = LevelInfo {name: "name1".to_string(), flag: "bfebba9e53b0108063c9c9e5828c0907337aeeed4363b1aac4da791d9593cec2".to_string()}; + let const_level_2 = LevelInfo {name: "name2".to_string(), flag: "e647a1ad81540b0c4e11048cba1eeae8a9993052a1186a6dd9acf575c834ba83".to_string()}; let levels_info_vector = vec![const_level_1, const_level_2]; @@ -76,6 +78,12 @@ fn create_component_from_level_info(level_info: &LevelInfo) -> Html { #[wasm_bindgen(start)] pub fn run_app() { + // Logging to console - see https://yew.rs/docs/en/more/debugging/ wasm_logger::init(wasm_logger::Config::default()); + + // Stacktrace to console on panics - see https://yew.rs/docs/en/more/debugging/ + panic::set_hook(Box::new(console_error_panic_hook::hook)); + + // Actually start the wasm app App::::new().mount_to_body(); } From 1ad2f6e3f3ff2aa23c1854ee56b706b769bc7a9c Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Sun, 20 Sep 2020 13:34:05 +0300 Subject: [PATCH 07/14] added all flags to game-config.toml for scripting later on --- levels/game-config.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/levels/game-config.toml b/levels/game-config.toml index 4d013a1..d0ce8a8 100644 --- a/levels/game-config.toml +++ b/levels/game-config.toml @@ -65,7 +65,7 @@ flags = ['log-5'] title = 'log-5' branch = 'pamphletary-harnessing-petticoaterie' solution_checker = 'hooks/checkers/log-5.sh' -flags = [] +flags = ['log-flag-meteorical'] [[levels]] title = 'merge-3' @@ -107,7 +107,7 @@ flags = [ title = 'tag-2' branch = 'individually-nonintroversive-chalcomancy' solution_checker = 'hooks/checkers/tag-2.sh' -flags = [] +flags = ['individually-nonintroversive-chalcomancy'] [[levels]] title = 'hooks-1' From ede10ea40ac0fd490ebe6d7a0a98634ecc29a151 Mon Sep 17 00:00:00 2001 From: Barak Nehmad Date: Sun, 20 Sep 2020 13:56:50 +0300 Subject: [PATCH 08/14] Renamed level.rs to be lowercase. --- submit-flags/submit-flags-app/src/{Level.rs => level.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename submit-flags/submit-flags-app/src/{Level.rs => level.rs} (100%) diff --git a/submit-flags/submit-flags-app/src/Level.rs b/submit-flags/submit-flags-app/src/level.rs similarity index 100% rename from submit-flags/submit-flags-app/src/Level.rs rename to submit-flags/submit-flags-app/src/level.rs From 173f2b54534ba1069dcc1ee51540c0cce3736106 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 24 Sep 2020 11:36:12 +0300 Subject: [PATCH 09/14] Created components dir to make stuff a little more orderly. --- submit-flags/submit-flags-app/Cargo.toml | 4 + .../src/{ => components}/level.rs | 1 - .../src/components/mainpage.rs | 72 ++++++++++++++++++ .../submit-flags-app/src/components/mod.rs | 2 + submit-flags/submit-flags-app/src/lib.rs | 76 +------------------ 5 files changed, 82 insertions(+), 73 deletions(-) rename submit-flags/submit-flags-app/src/{ => components}/level.rs (99%) create mode 100644 submit-flags/submit-flags-app/src/components/mainpage.rs create mode 100644 submit-flags/submit-flags-app/src/components/mod.rs diff --git a/submit-flags/submit-flags-app/Cargo.toml b/submit-flags/submit-flags-app/Cargo.toml index ceec3ba..337801b 100644 --- a/submit-flags/submit-flags-app/Cargo.toml +++ b/submit-flags/submit-flags-app/Cargo.toml @@ -16,3 +16,7 @@ log = "0.4.6" wasm-logger = "0.2.0" sha2 = "0.9.1" console_error_panic_hook = "0.1.6" +wasm-bindgen-futures = "0.4.18" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + diff --git a/submit-flags/submit-flags-app/src/level.rs b/submit-flags/submit-flags-app/src/components/level.rs similarity index 99% rename from submit-flags/submit-flags-app/src/level.rs rename to submit-flags/submit-flags-app/src/components/level.rs index 388177f..e910be6 100644 --- a/submit-flags/submit-flags-app/src/level.rs +++ b/submit-flags/submit-flags-app/src/components/level.rs @@ -1,5 +1,4 @@ use log; -use std::str; use sha2::{Sha256, Digest}; diff --git a/submit-flags/submit-flags-app/src/components/mainpage.rs b/submit-flags/submit-flags-app/src/components/mainpage.rs new file mode 100644 index 0000000..f078c13 --- /dev/null +++ b/submit-flags/submit-flags-app/src/components/mainpage.rs @@ -0,0 +1,72 @@ +use yew::prelude::{Component, ComponentLink, Properties, html, Html, ShouldRender}; +use serde::{Serialize, Deserialize}; + +use super::level::LevelComponent; + +// todo move to same lib as scripts +#[derive(Serialize, Deserialize, Debug)] +struct LevelInfo { + name: String, + flag: String, +} + +pub struct MainPage { + link: ComponentLink, + levels: Vec, + all_flags_done: bool, +} + +pub enum Msg { + CheckAllFlags, +} + +// TODO mainpage props + +impl Component for MainPage { + type Message = Msg; + type Properties = (); + fn create(_: Self::Properties, link: ComponentLink) -> Self { + let const_level_1 = LevelInfo {name: "name1".to_string(), flag: "bfebba9e53b0108063c9c9e5828c0907337aeeed4363b1aac4da791d9593cec2".to_string()}; + let const_level_2 = LevelInfo {name: "name2".to_string(), flag: "e647a1ad81540b0c4e11048cba1eeae8a9993052a1186a6dd9acf575c834ba83".to_string()}; + let levels_info_vector = vec![const_level_1, const_level_2]; + + Self { + link: link, + levels: levels_info_vector, + all_flags_done: false, + } + } + + fn update(&mut self, msg: Self::Message) -> ShouldRender { + match msg { + Msg::CheckAllFlags => self.all_flags_done = !self.all_flags_done + } + true + } + + fn change(&mut self, _props: Self::Properties) -> ShouldRender { + // Should only return "true" if new properties are different to + // previously received properties. + // This component has no properties so we will always return "false". + false + } + + fn view(&self) -> Html { + html! { + <> +
+

{ "Make Git Better CTF - Submit Flags" }

+
+ { for self.levels.iter().map(create_component_from_level_info) } +
+
+ + } + } +} + +fn create_component_from_level_info(level_info: &LevelInfo) -> Html { + html! { + + } +} diff --git a/submit-flags/submit-flags-app/src/components/mod.rs b/submit-flags/submit-flags-app/src/components/mod.rs new file mode 100644 index 0000000..9f3f996 --- /dev/null +++ b/submit-flags/submit-flags-app/src/components/mod.rs @@ -0,0 +1,2 @@ +pub mod mainpage; +pub mod level; diff --git a/submit-flags/submit-flags-app/src/lib.rs b/submit-flags/submit-flags-app/src/lib.rs index c5dd0d3..57c418d 100644 --- a/submit-flags/submit-flags-app/src/lib.rs +++ b/submit-flags/submit-flags-app/src/lib.rs @@ -1,80 +1,12 @@ // See https://github.com/yewstack/yew/issues/97 #![recursion_limit="256"] +use yew::App; +use wasm_bindgen::prelude::*; use console_error_panic_hook; use std::panic; - -mod level; - -use wasm_bindgen::prelude::{wasm_bindgen}; -use yew::prelude::{Component, ComponentLink, ShouldRender, Html, html, App}; -use level::LevelComponent; use wasm_logger; -struct LevelInfo { - name: String, - flag: String, -} - -struct SubmitFlagsPage { - link: ComponentLink, - levels: Vec, - all_flags_done: bool, -} - -enum Msg { - CheckAllFlags, -} - -impl Component for SubmitFlagsPage { - type Message = Msg; - type Properties = (); - fn create(_: Self::Properties, link: ComponentLink) -> Self { - // TODO change to read from file - let const_level_1 = LevelInfo {name: "name1".to_string(), flag: "bfebba9e53b0108063c9c9e5828c0907337aeeed4363b1aac4da791d9593cec2".to_string()}; - let const_level_2 = LevelInfo {name: "name2".to_string(), flag: "e647a1ad81540b0c4e11048cba1eeae8a9993052a1186a6dd9acf575c834ba83".to_string()}; - - let levels_info_vector = vec![const_level_1, const_level_2]; - - Self { - link: link, - levels: levels_info_vector, - all_flags_done: false, - } - } - - fn update(&mut self, msg: Self::Message) -> ShouldRender { - match msg { - Msg::CheckAllFlags => self.all_flags_done = !self.all_flags_done - } - true - } - - fn change(&mut self, _props: Self::Properties) -> ShouldRender { - // Should only return "true" if new properties are different to - // previously received properties. - // This component has no properties so we will always return "false". - false - } - - fn view(&self) -> Html { - html! { - <> -
-

{ "Make Git Better CTF - Submit Flags" }

-
- { for self.levels.iter().map(create_component_from_level_info) } -
-
- - } - } -} - -fn create_component_from_level_info(level_info: &LevelInfo) -> Html { - html! { - - } -} +mod components; #[wasm_bindgen(start)] pub fn run_app() { @@ -85,5 +17,5 @@ pub fn run_app() { panic::set_hook(Box::new(console_error_panic_hook::hook)); // Actually start the wasm app - App::::new().mount_to_body(); + App::::new().mount_to_body(); } From 44a738a4c1cad4e475a8717b9d4dcb48503f7057 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 24 Sep 2020 15:24:30 +0300 Subject: [PATCH 10/14] Added fetch for the levels file. --- submit-flags/submit-flags-app/Cargo.toml | 2 +- .../submit-flags-app/src/components/common.rs | 13 +++ .../src/components/fetchflags.rs | 46 ++++++++ .../src/components/mainpage.rs | 103 ++++++++++++++---- .../submit-flags-app/src/components/mod.rs | 5 + 5 files changed, 148 insertions(+), 21 deletions(-) create mode 100644 submit-flags/submit-flags-app/src/components/common.rs create mode 100644 submit-flags/submit-flags-app/src/components/fetchflags.rs diff --git a/submit-flags/submit-flags-app/Cargo.toml b/submit-flags/submit-flags-app/Cargo.toml index 337801b..336325c 100644 --- a/submit-flags/submit-flags-app/Cargo.toml +++ b/submit-flags/submit-flags-app/Cargo.toml @@ -19,4 +19,4 @@ console_error_panic_hook = "0.1.6" wasm-bindgen-futures = "0.4.18" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" - +anyhow = "1.0.32" diff --git a/submit-flags/submit-flags-app/src/components/common.rs b/submit-flags/submit-flags-app/src/components/common.rs new file mode 100644 index 0000000..3247a5b --- /dev/null +++ b/submit-flags/submit-flags-app/src/components/common.rs @@ -0,0 +1,13 @@ + +use serde::{Serialize, Deserialize}; +// todo move to same lib as scripts +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct LevelInfo { + pub name: String, + pub flag: String, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct LevelsInfo { + pub levels: Vec, +} diff --git a/submit-flags/submit-flags-app/src/components/fetchflags.rs b/submit-flags/submit-flags-app/src/components/fetchflags.rs new file mode 100644 index 0000000..cafa9e4 --- /dev/null +++ b/submit-flags/submit-flags-app/src/components/fetchflags.rs @@ -0,0 +1,46 @@ +use anyhow::{anyhow, Error}; + +use yew::callback::Callback; +use yew::format::{Nothing}; +use yew::services::fetch::{FetchService, FetchTask, Request, Response}; + +use super::common::LevelsInfo; + +#[derive(Default)] +pub struct GetFlagsService { + file_path: &'static str, +} + +impl GetFlagsService { + pub fn new(file_path: &'static str) -> Self { + Self { + file_path, + } + } + + pub fn get_response(&mut self, callback: Callback>) -> FetchTask { + let handler = move |response: Response>| { + let (head, body) = response.into_parts(); + if head.status.is_success() { + log::debug!("Response is a success"); + let body_value = body.unwrap(); + log::debug!("here's the body: {}", body_value); + let parsed: LevelsInfo = serde_json::from_str(&body_value).unwrap(); + log::debug!("JSON conversion went well! Found {} levels", parsed.levels.len()); + callback.emit(Ok(parsed)) + } else { + callback.emit(Err(anyhow!( + "{}: error getting levels from server", + head.status + ))) + } + }; + + // Local server + let url = format!("/{}", self.file_path); + + let request = Request::get(url.clone().as_str()).body(Nothing).unwrap(); + log::debug!("Created get request to URI {}", request.uri()); + FetchService::fetch(request, handler.into()).unwrap() + } +} diff --git a/submit-flags/submit-flags-app/src/components/mainpage.rs b/submit-flags/submit-flags-app/src/components/mainpage.rs index f078c13..769a7ae 100644 --- a/submit-flags/submit-flags-app/src/components/mainpage.rs +++ b/submit-flags/submit-flags-app/src/components/mainpage.rs @@ -1,45 +1,82 @@ -use yew::prelude::{Component, ComponentLink, Properties, html, Html, ShouldRender}; -use serde::{Serialize, Deserialize}; +use yew::prelude::{Component, ComponentLink, html, Html, ShouldRender}; + +use anyhow::Error; +use yew::format::Json; +use yew::prelude::*; +use yew::services::fetch::FetchTask; use super::level::LevelComponent; +use super::common::{LevelInfo, LevelsInfo}; -// todo move to same lib as scripts -#[derive(Serialize, Deserialize, Debug)] -struct LevelInfo { - name: String, - flag: String, -} +use super::fetchflags::GetFlagsService; pub struct MainPage { link: ComponentLink, - levels: Vec, + levels: Option>, all_flags_done: bool, + + // Fetch-related members + flags_service: GetFlagsService, + flags_service_response: Option, + flags_service_callback: Callback>, + flags_service_task: Option, + requested_flags: bool, } -pub enum Msg { +#[derive(Debug)] +pub enum MainPageMsg { CheckAllFlags, + // Fetch-related messages + GetFlagsResponse, + FlagsResponseReady(Result), } -// TODO mainpage props - impl Component for MainPage { - type Message = Msg; + type Message = MainPageMsg; type Properties = (); fn create(_: Self::Properties, link: ComponentLink) -> Self { - let const_level_1 = LevelInfo {name: "name1".to_string(), flag: "bfebba9e53b0108063c9c9e5828c0907337aeeed4363b1aac4da791d9593cec2".to_string()}; + /*let const_level_1 = LevelInfo {name: "name1".to_string(), flag: "bfebba9e53b0108063c9c9e5828c0907337aeeed4363b1aac4da791d9593cec2".to_string()}; let const_level_2 = LevelInfo {name: "name2".to_string(), flag: "e647a1ad81540b0c4e11048cba1eeae8a9993052a1186a6dd9acf575c834ba83".to_string()}; let levels_info_vector = vec![const_level_1, const_level_2]; + let levels = LevelsInfo { levels: levels_info_vector };*/ + Self { - link: link, - levels: levels_info_vector, + link: link.clone(), + levels: None, all_flags_done: false, + + flags_service: GetFlagsService::new("levels_info.json"), + flags_service_response: None, + flags_service_callback: link.callback(MainPageMsg::FlagsResponseReady), + flags_service_task: None, + requested_flags: false } } fn update(&mut self, msg: Self::Message) -> ShouldRender { + log::debug!("MainPage: Update message {:?}", msg); match msg { - Msg::CheckAllFlags => self.all_flags_done = !self.all_flags_done + MainPageMsg::CheckAllFlags => { + // TODO: impl this + self.all_flags_done = !self.all_flags_done; + } + MainPageMsg::GetFlagsResponse => { + log::debug!("Sending a get response"); + let task = self.flags_service.get_response(self.flags_service_callback.clone()); + log::debug!("Sent a get response"); + self.flags_service_task = Some(task); + self.requested_flags = true; + } + MainPageMsg::FlagsResponseReady(Ok(response)) => { + self.flags_service_response = Some(response); + log::debug!("Got response: {:?}", Json(self.flags_service_response.clone())); + // Finally, get the levels from the response. Phew! + self.levels = Some(self.flags_service_response.as_mut().unwrap().levels.clone()); + } + MainPageMsg::FlagsResponseReady(Err(err)) => { + log::error!("Error while trying to fetch flags: {:?}", err); + } } true } @@ -52,19 +89,45 @@ impl Component for MainPage { } fn view(&self) -> Html { + // If you didn't request flags yet, try to. + if !self.requested_flags { + log::debug!("Requesting flags for the first time."); + self.link.send_message(MainPageMsg::GetFlagsResponse); + } + html! { <>

{ "Make Git Better CTF - Submit Flags" }

-
- { for self.levels.iter().map(create_component_from_level_info) } -
+ { self.levels_comp() }
} } } +// Extra, non-component functions for MainPage +impl MainPage { + fn levels_comp(&self) -> Html { + match &self.levels { + None => { + html! { +

{ "No levels yet. :( Check console logs." }

+ } + } + Some(levels) => { + html! { +
+ { + for levels.iter().map(create_component_from_level_info) + } +
+ } + } + } + } +} + fn create_component_from_level_info(level_info: &LevelInfo) -> Html { html! { diff --git a/submit-flags/submit-flags-app/src/components/mod.rs b/submit-flags/submit-flags-app/src/components/mod.rs index 9f3f996..eb20dbc 100644 --- a/submit-flags/submit-flags-app/src/components/mod.rs +++ b/submit-flags/submit-flags-app/src/components/mod.rs @@ -1,2 +1,7 @@ +// Public pub mod mainpage; pub mod level; + +// Internal +mod common; +mod fetchflags; From 3c64f29c5ba42b10154430021cd538dcdd183572 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 24 Sep 2020 16:26:33 +0300 Subject: [PATCH 11/14] Added error handling for fetching --- .../src/components/fetchflags.rs | 20 ++++++++--- .../src/components/mainpage.rs | 35 ++++++++++++------- .../submit-flags-app/static/css/style.css | 29 +++++++++++++++ 3 files changed, 68 insertions(+), 16 deletions(-) diff --git a/submit-flags/submit-flags-app/src/components/fetchflags.rs b/submit-flags/submit-flags-app/src/components/fetchflags.rs index cafa9e4..af50425 100644 --- a/submit-flags/submit-flags-app/src/components/fetchflags.rs +++ b/submit-flags/submit-flags-app/src/components/fetchflags.rs @@ -25,9 +25,16 @@ impl GetFlagsService { log::debug!("Response is a success"); let body_value = body.unwrap(); log::debug!("here's the body: {}", body_value); - let parsed: LevelsInfo = serde_json::from_str(&body_value).unwrap(); - log::debug!("JSON conversion went well! Found {} levels", parsed.levels.len()); - callback.emit(Ok(parsed)) + let parsed = try_to_parse_levels_json(&body_value); + match parsed { + Ok(v) => { + log::debug!("JSON conversion went well! Found {} levels", v.levels.len()); + callback.emit(Ok(v)) + } + Err(e) => { + callback.emit(Err(anyhow!("{:?}", e))); + } + } } else { callback.emit(Err(anyhow!( "{}: error getting levels from server", @@ -39,8 +46,13 @@ impl GetFlagsService { // Local server let url = format!("/{}", self.file_path); - let request = Request::get(url.clone().as_str()).body(Nothing).unwrap(); + let request = Request::get(url.clone().as_str()).header("Cache-Control", "no-cache").body(Nothing).unwrap(); log::debug!("Created get request to URI {}", request.uri()); FetchService::fetch(request, handler.into()).unwrap() } } + +fn try_to_parse_levels_json(data: &str) -> Result { + let parsed: LevelsInfo = serde_json::from_str(data)?; + Ok(parsed) +} diff --git a/submit-flags/submit-flags-app/src/components/mainpage.rs b/submit-flags/submit-flags-app/src/components/mainpage.rs index 769a7ae..7058eea 100644 --- a/submit-flags/submit-flags-app/src/components/mainpage.rs +++ b/submit-flags/submit-flags-app/src/components/mainpage.rs @@ -1,6 +1,5 @@ -use yew::prelude::{Component, ComponentLink, html, Html, ShouldRender}; - use anyhow::Error; +use yew::prelude::{Component, ComponentLink, html, Html, ShouldRender}; use yew::format::Json; use yew::prelude::*; use yew::services::fetch::FetchTask; @@ -14,6 +13,7 @@ pub struct MainPage { link: ComponentLink, levels: Option>, all_flags_done: bool, + error: String, // Fetch-related members flags_service: GetFlagsService, @@ -35,16 +35,11 @@ impl Component for MainPage { type Message = MainPageMsg; type Properties = (); fn create(_: Self::Properties, link: ComponentLink) -> Self { - /*let const_level_1 = LevelInfo {name: "name1".to_string(), flag: "bfebba9e53b0108063c9c9e5828c0907337aeeed4363b1aac4da791d9593cec2".to_string()}; - let const_level_2 = LevelInfo {name: "name2".to_string(), flag: "e647a1ad81540b0c4e11048cba1eeae8a9993052a1186a6dd9acf575c834ba83".to_string()}; - let levels_info_vector = vec![const_level_1, const_level_2]; - - let levels = LevelsInfo { levels: levels_info_vector };*/ - Self { link: link.clone(), levels: None, all_flags_done: false, + error: "".to_string(), flags_service: GetFlagsService::new("levels_info.json"), flags_service_response: None, @@ -76,6 +71,7 @@ impl Component for MainPage { } MainPageMsg::FlagsResponseReady(Err(err)) => { log::error!("Error while trying to fetch flags: {:?}", err); + self.error = format!("{:?}", err); } } true @@ -99,7 +95,7 @@ impl Component for MainPage { <>

{ "Make Git Better CTF - Submit Flags" }

- { self.levels_comp() } + { self.get_levels_comp() }
} @@ -108,11 +104,26 @@ impl Component for MainPage { // Extra, non-component functions for MainPage impl MainPage { - fn levels_comp(&self) -> Html { + fn get_levels_comp(&self) -> Html { match &self.levels { None => { - html! { -

{ "No levels yet. :( Check console logs." }

+ // Check if still laoding, or an actual error + if self.error.is_empty() { // Still loading + html! { + <> +
+

{ "No levels yet. Loading from server. If this is taking more than a few seconds, check the console logs." }

+ + } + } else { // Error state + html! { + <> +
+

{ format!("An error has occured! Details:") }

+
 { self.error.clone() } 
+

{ "Please reach out to Shay Nehmad (@ShayNehmad on Twitter) with the details!" }

+ + } } } Some(levels) => { diff --git a/submit-flags/submit-flags-app/static/css/style.css b/submit-flags/submit-flags-app/static/css/style.css index cf4031b..049d5eb 100644 --- a/submit-flags/submit-flags-app/static/css/style.css +++ b/submit-flags/submit-flags-app/static/css/style.css @@ -1587,3 +1587,32 @@ input[type="text"]::placeholder{color: #ccc;} width: max-content; text-align: center; } + +.spinner { + width: 40px; + height: 40px; + background-color: #333; + + margin: 100px auto; + -webkit-animation: sk-rotateplane 1.2s infinite ease-in-out; + animation: sk-rotateplane 1.2s infinite ease-in-out; +} + +@-webkit-keyframes sk-rotateplane { + 0% { -webkit-transform: perspective(120px) } + 50% { -webkit-transform: perspective(120px) rotateY(180deg) } + 100% { -webkit-transform: perspective(120px) rotateY(180deg) rotateX(180deg) } +} + +@keyframes sk-rotateplane { + 0% { + transform: perspective(120px) rotateX(0deg) rotateY(0deg); + -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg) + } 50% { + transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg); + -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) + } 100% { + transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); + -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); + } +} From f4fa96150e43584f0374802d0448c7c96cb2a8da Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Fri, 25 Sep 2020 09:42:17 +0300 Subject: [PATCH 12/14] Added check win state --- .../submit-flags-app/src/components/common.rs | 19 +- .../submit-flags-app/src/components/level.rs | 25 +- .../src/components/mainpage.rs | 100 +++++-- .../submit-flags-app/static/css/style.css | 245 ++++++++++++++++++ 4 files changed, 355 insertions(+), 34 deletions(-) diff --git a/submit-flags/submit-flags-app/src/components/common.rs b/submit-flags/submit-flags-app/src/components/common.rs index 3247a5b..8df557a 100644 --- a/submit-flags/submit-flags-app/src/components/common.rs +++ b/submit-flags/submit-flags-app/src/components/common.rs @@ -1,4 +1,5 @@ - +use yew::callback::Callback; +use anyhow::Error; use serde::{Serialize, Deserialize}; // todo move to same lib as scripts #[derive(Serialize, Deserialize, Clone, Debug)] @@ -11,3 +12,19 @@ pub struct LevelInfo { pub struct LevelsInfo { pub levels: Vec, } + +#[derive(Debug)] +pub struct SingleFlagStatus { + pub level_name: String, + pub is_correct: bool +} + +#[derive(Debug)] +pub enum MainPageMsg { + CheckSingleFlag(SingleFlagStatus), + // Fetch-related messages + GetFlagsResponse, + FlagsResponseReady(Result), +} + +pub type CheckFlagCallback = Callback; diff --git a/submit-flags/submit-flags-app/src/components/level.rs b/submit-flags/submit-flags-app/src/components/level.rs index e910be6..c96d0c6 100644 --- a/submit-flags/submit-flags-app/src/components/level.rs +++ b/submit-flags/submit-flags-app/src/components/level.rs @@ -5,6 +5,9 @@ use sha2::{Sha256, Digest}; use yew::prelude::{Component, ComponentLink, Properties, html, Html, ShouldRender}; use yew::html::InputData; +use super::common::{SingleFlagStatus, CheckFlagCallback}; + + pub struct LevelComponent { // The link enables us to interact (i.e. reqister callbacks and send messages) with the component itself. See https://yew.rs/docs/en/concepts/components/#create link: ComponentLink, @@ -16,6 +19,8 @@ pub struct LevelComponent { user_flag: String, // Whether the correct flag has been entered. is_flag_correct: bool, + // Callback to update parent that flag has been solved + check_callback: CheckFlagCallback, } // These are the messages (think "events") that can happen in this component. @@ -38,6 +43,8 @@ pub struct LevelProps { // This prop indicates whether the user's flag is correct. Not passed from parent, but rather used to communicate back to it from the level. #[prop_or(false)] pub is_flag_correct: bool, + // Callback to update parent that flag has been solved + pub check_callback: CheckFlagCallback, } // See https://yew.rs/docs/en/concepts/components/ @@ -61,6 +68,8 @@ impl Component for LevelComponent { user_flag: "".to_string(), // This has a default value of `false`. Not passed from parent is_flag_correct: props.is_flag_correct, + // parent has to pass the callback + check_callback: props.check_callback, } } @@ -73,13 +82,13 @@ impl Component for LevelComponent { let hashed_user_flag_arr = Sha256::digest(self.user_flag.as_bytes()); // Cast the array to a string let hashed_user_flag_str: String = format!("{:x}", hashed_user_flag_arr); - log::debug!("update::{}, user flag {}, user hash {}, actual flag hash {}", - self.name.clone(), - self.user_flag.clone(), - hashed_user_flag_str.clone(), - self.flag.clone()); // Compare user hash to our hash self.is_flag_correct = hashed_user_flag_str == self.flag; + + // update parent via callback + let status = SingleFlagStatus { level_name: self.name.clone(), is_correct: self.is_flag_correct }; + self.check_callback.emit(status); + true // Re-render } LevelMsg::UserFlagChanged(new_user_flag) => { @@ -109,8 +118,8 @@ impl Component for LevelComponent { // Creating the element as variables makes it clearer - similar to functional elements in react - // This element just prints the component info to make it easier to develop. Will delete soon :) - let debug_info_element = html! { + // This element just prints the component info to make it easier to develop. + let _debug_info_element = html! {
                 { 
                     format!("DEBUG: I am a level component! Name: {} | Flag: {} | Status: {}", 
@@ -149,8 +158,6 @@ impl Component for LevelComponent {
         // This is the complete HTML component we're returning from `view`.
         html! {
             
-                // TODO - delete this
-                { debug_info_element }
                 
{ input_element } { status_element } diff --git a/submit-flags/submit-flags-app/src/components/mainpage.rs b/submit-flags/submit-flags-app/src/components/mainpage.rs index 7058eea..155d80c 100644 --- a/submit-flags/submit-flags-app/src/components/mainpage.rs +++ b/submit-flags/submit-flags-app/src/components/mainpage.rs @@ -1,18 +1,25 @@ +use std::collections::HashMap; use anyhow::Error; use yew::prelude::{Component, ComponentLink, html, Html, ShouldRender}; +use yew::callback::Callback; use yew::format::Json; -use yew::prelude::*; use yew::services::fetch::FetchTask; use super::level::LevelComponent; -use super::common::{LevelInfo, LevelsInfo}; +use super::common::{LevelInfo, LevelsInfo, MainPageMsg, CheckFlagCallback}; use super::fetchflags::GetFlagsService; pub struct MainPage { link: ComponentLink, + + // All the level information - used to create the level components levels: Option>, - all_flags_done: bool, + + // The status of each level component (name to is_correct) + levels_status: HashMap, + + // If there's an error in any of the states, shove it here so the user can see it. error: String, // Fetch-related members @@ -23,14 +30,6 @@ pub struct MainPage { requested_flags: bool, } -#[derive(Debug)] -pub enum MainPageMsg { - CheckAllFlags, - // Fetch-related messages - GetFlagsResponse, - FlagsResponseReady(Result), -} - impl Component for MainPage { type Message = MainPageMsg; type Properties = (); @@ -38,24 +37,21 @@ impl Component for MainPage { Self { link: link.clone(), levels: None, - all_flags_done: false, + levels_status: HashMap::new(), error: "".to_string(), flags_service: GetFlagsService::new("levels_info.json"), flags_service_response: None, flags_service_callback: link.callback(MainPageMsg::FlagsResponseReady), flags_service_task: None, - requested_flags: false + requested_flags: false, } } fn update(&mut self, msg: Self::Message) -> ShouldRender { log::debug!("MainPage: Update message {:?}", msg); match msg { - MainPageMsg::CheckAllFlags => { - // TODO: impl this - self.all_flags_done = !self.all_flags_done; - } + // Fetch Messages MainPageMsg::GetFlagsResponse => { log::debug!("Sending a get response"); let task = self.flags_service.get_response(self.flags_service_callback.clone()); @@ -68,11 +64,19 @@ impl Component for MainPage { log::debug!("Got response: {:?}", Json(self.flags_service_response.clone())); // Finally, get the levels from the response. Phew! self.levels = Some(self.flags_service_response.as_mut().unwrap().levels.clone()); + for level in self.levels.as_ref().unwrap().iter() { + self.levels_status.insert(level.name.clone(), false); + } } MainPageMsg::FlagsResponseReady(Err(err)) => { log::error!("Error while trying to fetch flags: {:?}", err); self.error = format!("{:?}", err); } + // Callback for level component + MainPageMsg::CheckSingleFlag(status) => { + *self.levels_status.get_mut(&status.level_name).unwrap() = status.is_correct; + log::debug!("map {:?}", self.levels_status); + } } true } @@ -96,14 +100,57 @@ impl Component for MainPage {

{ "Make Git Better CTF - Submit Flags" }

{ self.get_levels_comp() } + { self.get_totals_comp() }
} } } -// Extra, non-component functions for MainPage +// Extra, non-component methods for MainPage impl MainPage { + fn get_totals_comp(&self) -> Html { + if self.levels_status.is_empty() { + html! {} + } else { + let mut len = 0; + let mut counter = 0; + for (_, is_correct) in self.levels_status.iter() { + len += 1; + if *is_correct { + counter += 1; + } + }; + + let victory_comp: Html; + + if len == counter { + victory_comp = html! { + <> + // fireworks! +
+
+

{ "You win! 🏆" }

+

+ { "Screenshot this and send it to me to get into the " } + {"make-git-better Hall of Fame"}{"! "} + {"Here's a list of ways to contact me."} +

+

{ "Thanks for playing! 😀" }

+
+ + }; + } else { victory_comp = html! { }; } + + html! { +
+
{ format!("{} / {}", counter, len) }
+ { victory_comp } +
+ } + } + } + fn get_levels_comp(&self) -> Html { match &self.levels { None => { @@ -127,20 +174,25 @@ impl MainPage { } } Some(levels) => { + let render_level_component = |level| { + self.create_component_from_level_info(level) + }; + html! {
- { - for levels.iter().map(create_component_from_level_info) + { + for levels.iter().map(render_level_component) }
} } } } -} -fn create_component_from_level_info(level_info: &LevelInfo) -> Html { - html! { - + fn create_component_from_level_info(&self, level_info: &LevelInfo) -> Html { + let callback: CheckFlagCallback = self.link.clone().callback(MainPageMsg::CheckSingleFlag); + html! { + + } } } diff --git a/submit-flags/submit-flags-app/static/css/style.css b/submit-flags/submit-flags-app/static/css/style.css index 049d5eb..54725fa 100644 --- a/submit-flags/submit-flags-app/static/css/style.css +++ b/submit-flags/submit-flags-app/static/css/style.css @@ -1616,3 +1616,248 @@ input[type="text"]::placeholder{color: #ccc;} -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); } } + +.pyro { + position: absolute; + left: 40%; + top: 50%; + width: 20%; +} + +.pyro > .before, +.pyro > .after { +position: absolute; +width: 5px; +height: 5px; +border-radius: 50%; +box-shadow: 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff; +-moz-animation: 1s bang ease-out infinite backwards, 1s gravity ease-in infinite backwards, 5s position linear infinite backwards; +-webkit-animation: 1s bang ease-out infinite backwards, 1s gravity ease-in infinite backwards, 5s position linear infinite backwards; +-o-animation: 1s bang ease-out infinite backwards, 1s gravity ease-in infinite backwards, 5s position linear infinite backwards; +-ms-animation: 1s bang ease-out infinite backwards, 1s gravity ease-in infinite backwards, 5s position linear infinite backwards; +animation: 1s bang ease-out infinite backwards, 1s gravity ease-in infinite backwards, 5s position linear infinite backwards; +} + +.pyro > .after { +-moz-animation-delay: 1.25s, 1.25s, 1.25s; +-webkit-animation-delay: 1.25s, 1.25s, 1.25s; +-o-animation-delay: 1.25s, 1.25s, 1.25s; +-ms-animation-delay: 1.25s, 1.25s, 1.25s; +animation-delay: 1.25s, 1.25s, 1.25s; +-moz-animation-duration: 1.25s, 1.25s, 6.25s; +-webkit-animation-duration: 1.25s, 1.25s, 6.25s; +-o-animation-duration: 1.25s, 1.25s, 6.25s; +-ms-animation-duration: 1.25s, 1.25s, 6.25s; +animation-duration: 1.25s, 1.25s, 6.25s; +} + +@-webkit-keyframes bang { +to { + box-shadow: -231px 31.3333333333px #8cff00, -155px -46.6666666667px #002fff, 186px -322.6666666667px #fb00ff, 159px -224.6666666667px #f200ff, -159px -268.6666666667px #ff5900, 129px 12.3333333333px #00ddff, 16px -157.6666666667px #00ffbb, -38px -289.6666666667px #ff00c4, -169px -367.6666666667px #fbff00, -212px -47.6666666667px #ffdd00, -101px -122.6666666667px #00ff80, 139px -359.6666666667px #00ff88, 21px -58.6666666667px #ff001a, 206px 82.3333333333px #0dff00, 21px -98.6666666667px #66ff00, 79px -292.6666666667px #b700ff, 209px -183.6666666667px #7300ff, 107px -203.6666666667px #ff1e00, -79px 50.3333333333px #ff00bf, -162px -13.6666666667px #ff00d5, 181px -6.6666666667px #0022ff, 131px -128.6666666667px #ff00d0, 136px 11.3333333333px #ff006a, 0px -79.6666666667px #ffcc00, -162px -19.6666666667px #00e6ff, -18px -56.6666666667px #0011ff, 48px -353.6666666667px #44ff00, -61px -130.6666666667px #d000ff, -211px -348.6666666667px #00ff91, -106px -100.6666666667px #b7ff00, -89px -20.6666666667px #11ff00, 108px -101.6666666667px #ff0022, -175px 17.3333333333px #00ff88, 149px -342.6666666667px #00e6ff, 60px -11.6666666667px #6aff00, -26px -267.6666666667px #ff0004, 183px -338.6666666667px #1aff00, 0px -221.6666666667px #00ffcc, 222px -239.6666666667px #ff2600, -53px -88.6666666667px #ff0040, -73px 7.3333333333px #ff1100, 101px -121.6666666667px #ff6600, 70px -245.6666666667px #ff0080, -78px -42.6666666667px #ffaa00, -28px -192.6666666667px #d900ff, -159px -386.6666666667px #ff002b, 167px 3.3333333333px #ff4d00, -246px -6.6666666667px #ffee00, -56px 61.3333333333px #ff2200, -175px -74.6666666667px #ff0011, 214px 39.3333333333px #ff006a; +} +} +@-moz-keyframes bang { +to { + box-shadow: -231px 31.3333333333px #8cff00, -155px -46.6666666667px #002fff, 186px -322.6666666667px #fb00ff, 159px -224.6666666667px #f200ff, -159px -268.6666666667px #ff5900, 129px 12.3333333333px #00ddff, 16px -157.6666666667px #00ffbb, -38px -289.6666666667px #ff00c4, -169px -367.6666666667px #fbff00, -212px -47.6666666667px #ffdd00, -101px -122.6666666667px #00ff80, 139px -359.6666666667px #00ff88, 21px -58.6666666667px #ff001a, 206px 82.3333333333px #0dff00, 21px -98.6666666667px #66ff00, 79px -292.6666666667px #b700ff, 209px -183.6666666667px #7300ff, 107px -203.6666666667px #ff1e00, -79px 50.3333333333px #ff00bf, -162px -13.6666666667px #ff00d5, 181px -6.6666666667px #0022ff, 131px -128.6666666667px #ff00d0, 136px 11.3333333333px #ff006a, 0px -79.6666666667px #ffcc00, -162px -19.6666666667px #00e6ff, -18px -56.6666666667px #0011ff, 48px -353.6666666667px #44ff00, -61px -130.6666666667px #d000ff, -211px -348.6666666667px #00ff91, -106px -100.6666666667px #b7ff00, -89px -20.6666666667px #11ff00, 108px -101.6666666667px #ff0022, -175px 17.3333333333px #00ff88, 149px -342.6666666667px #00e6ff, 60px -11.6666666667px #6aff00, -26px -267.6666666667px #ff0004, 183px -338.6666666667px #1aff00, 0px -221.6666666667px #00ffcc, 222px -239.6666666667px #ff2600, -53px -88.6666666667px #ff0040, -73px 7.3333333333px #ff1100, 101px -121.6666666667px #ff6600, 70px -245.6666666667px #ff0080, -78px -42.6666666667px #ffaa00, -28px -192.6666666667px #d900ff, -159px -386.6666666667px #ff002b, 167px 3.3333333333px #ff4d00, -246px -6.6666666667px #ffee00, -56px 61.3333333333px #ff2200, -175px -74.6666666667px #ff0011, 214px 39.3333333333px #ff006a; +} +} +@-o-keyframes bang { +to { + box-shadow: -231px 31.3333333333px #8cff00, -155px -46.6666666667px #002fff, 186px -322.6666666667px #fb00ff, 159px -224.6666666667px #f200ff, -159px -268.6666666667px #ff5900, 129px 12.3333333333px #00ddff, 16px -157.6666666667px #00ffbb, -38px -289.6666666667px #ff00c4, -169px -367.6666666667px #fbff00, -212px -47.6666666667px #ffdd00, -101px -122.6666666667px #00ff80, 139px -359.6666666667px #00ff88, 21px -58.6666666667px #ff001a, 206px 82.3333333333px #0dff00, 21px -98.6666666667px #66ff00, 79px -292.6666666667px #b700ff, 209px -183.6666666667px #7300ff, 107px -203.6666666667px #ff1e00, -79px 50.3333333333px #ff00bf, -162px -13.6666666667px #ff00d5, 181px -6.6666666667px #0022ff, 131px -128.6666666667px #ff00d0, 136px 11.3333333333px #ff006a, 0px -79.6666666667px #ffcc00, -162px -19.6666666667px #00e6ff, -18px -56.6666666667px #0011ff, 48px -353.6666666667px #44ff00, -61px -130.6666666667px #d000ff, -211px -348.6666666667px #00ff91, -106px -100.6666666667px #b7ff00, -89px -20.6666666667px #11ff00, 108px -101.6666666667px #ff0022, -175px 17.3333333333px #00ff88, 149px -342.6666666667px #00e6ff, 60px -11.6666666667px #6aff00, -26px -267.6666666667px #ff0004, 183px -338.6666666667px #1aff00, 0px -221.6666666667px #00ffcc, 222px -239.6666666667px #ff2600, -53px -88.6666666667px #ff0040, -73px 7.3333333333px #ff1100, 101px -121.6666666667px #ff6600, 70px -245.6666666667px #ff0080, -78px -42.6666666667px #ffaa00, -28px -192.6666666667px #d900ff, -159px -386.6666666667px #ff002b, 167px 3.3333333333px #ff4d00, -246px -6.6666666667px #ffee00, -56px 61.3333333333px #ff2200, -175px -74.6666666667px #ff0011, 214px 39.3333333333px #ff006a; +} +} +@-ms-keyframes bang { +to { + box-shadow: -231px 31.3333333333px #8cff00, -155px -46.6666666667px #002fff, 186px -322.6666666667px #fb00ff, 159px -224.6666666667px #f200ff, -159px -268.6666666667px #ff5900, 129px 12.3333333333px #00ddff, 16px -157.6666666667px #00ffbb, -38px -289.6666666667px #ff00c4, -169px -367.6666666667px #fbff00, -212px -47.6666666667px #ffdd00, -101px -122.6666666667px #00ff80, 139px -359.6666666667px #00ff88, 21px -58.6666666667px #ff001a, 206px 82.3333333333px #0dff00, 21px -98.6666666667px #66ff00, 79px -292.6666666667px #b700ff, 209px -183.6666666667px #7300ff, 107px -203.6666666667px #ff1e00, -79px 50.3333333333px #ff00bf, -162px -13.6666666667px #ff00d5, 181px -6.6666666667px #0022ff, 131px -128.6666666667px #ff00d0, 136px 11.3333333333px #ff006a, 0px -79.6666666667px #ffcc00, -162px -19.6666666667px #00e6ff, -18px -56.6666666667px #0011ff, 48px -353.6666666667px #44ff00, -61px -130.6666666667px #d000ff, -211px -348.6666666667px #00ff91, -106px -100.6666666667px #b7ff00, -89px -20.6666666667px #11ff00, 108px -101.6666666667px #ff0022, -175px 17.3333333333px #00ff88, 149px -342.6666666667px #00e6ff, 60px -11.6666666667px #6aff00, -26px -267.6666666667px #ff0004, 183px -338.6666666667px #1aff00, 0px -221.6666666667px #00ffcc, 222px -239.6666666667px #ff2600, -53px -88.6666666667px #ff0040, -73px 7.3333333333px #ff1100, 101px -121.6666666667px #ff6600, 70px -245.6666666667px #ff0080, -78px -42.6666666667px #ffaa00, -28px -192.6666666667px #d900ff, -159px -386.6666666667px #ff002b, 167px 3.3333333333px #ff4d00, -246px -6.6666666667px #ffee00, -56px 61.3333333333px #ff2200, -175px -74.6666666667px #ff0011, 214px 39.3333333333px #ff006a; +} +} +@keyframes bang { +to { + box-shadow: -231px 31.3333333333px #8cff00, -155px -46.6666666667px #002fff, 186px -322.6666666667px #fb00ff, 159px -224.6666666667px #f200ff, -159px -268.6666666667px #ff5900, 129px 12.3333333333px #00ddff, 16px -157.6666666667px #00ffbb, -38px -289.6666666667px #ff00c4, -169px -367.6666666667px #fbff00, -212px -47.6666666667px #ffdd00, -101px -122.6666666667px #00ff80, 139px -359.6666666667px #00ff88, 21px -58.6666666667px #ff001a, 206px 82.3333333333px #0dff00, 21px -98.6666666667px #66ff00, 79px -292.6666666667px #b700ff, 209px -183.6666666667px #7300ff, 107px -203.6666666667px #ff1e00, -79px 50.3333333333px #ff00bf, -162px -13.6666666667px #ff00d5, 181px -6.6666666667px #0022ff, 131px -128.6666666667px #ff00d0, 136px 11.3333333333px #ff006a, 0px -79.6666666667px #ffcc00, -162px -19.6666666667px #00e6ff, -18px -56.6666666667px #0011ff, 48px -353.6666666667px #44ff00, -61px -130.6666666667px #d000ff, -211px -348.6666666667px #00ff91, -106px -100.6666666667px #b7ff00, -89px -20.6666666667px #11ff00, 108px -101.6666666667px #ff0022, -175px 17.3333333333px #00ff88, 149px -342.6666666667px #00e6ff, 60px -11.6666666667px #6aff00, -26px -267.6666666667px #ff0004, 183px -338.6666666667px #1aff00, 0px -221.6666666667px #00ffcc, 222px -239.6666666667px #ff2600, -53px -88.6666666667px #ff0040, -73px 7.3333333333px #ff1100, 101px -121.6666666667px #ff6600, 70px -245.6666666667px #ff0080, -78px -42.6666666667px #ffaa00, -28px -192.6666666667px #d900ff, -159px -386.6666666667px #ff002b, 167px 3.3333333333px #ff4d00, -246px -6.6666666667px #ffee00, -56px 61.3333333333px #ff2200, -175px -74.6666666667px #ff0011, 214px 39.3333333333px #ff006a; +} +} +@-webkit-keyframes gravity { +to { + transform: translateY(200px); + -moz-transform: translateY(200px); + -webkit-transform: translateY(200px); + -o-transform: translateY(200px); + -ms-transform: translateY(200px); + opacity: 0; +} +} +@-moz-keyframes gravity { +to { + transform: translateY(200px); + -moz-transform: translateY(200px); + -webkit-transform: translateY(200px); + -o-transform: translateY(200px); + -ms-transform: translateY(200px); + opacity: 0; +} +} +@-o-keyframes gravity { +to { + transform: translateY(200px); + -moz-transform: translateY(200px); + -webkit-transform: translateY(200px); + -o-transform: translateY(200px); + -ms-transform: translateY(200px); + opacity: 0; +} +} +@-ms-keyframes gravity { +to { + transform: translateY(200px); + -moz-transform: translateY(200px); + -webkit-transform: translateY(200px); + -o-transform: translateY(200px); + -ms-transform: translateY(200px); + opacity: 0; +} +} +@keyframes gravity { +to { + transform: translateY(200px); + -moz-transform: translateY(200px); + -webkit-transform: translateY(200px); + -o-transform: translateY(200px); + -ms-transform: translateY(200px); + opacity: 0; +} +} +@-webkit-keyframes position { +0%, +19.9% { + margin-top: 10%; + margin-left: 40%; +} +20%, +39.9% { + margin-top: 40%; + margin-left: 30%; +} +40%, +59.9% { + margin-top: 20%; + margin-left: 70%; +} +60%, +79.9% { + margin-top: 30%; + margin-left: 20%; +} +80%, +99.9% { + margin-top: 30%; + margin-left: 80%; +} +} +@-moz-keyframes position { +0%, +19.9% { + margin-top: 10%; + margin-left: 40%; +} +20%, +39.9% { + margin-top: 40%; + margin-left: 30%; +} +40%, +59.9% { + margin-top: 20%; + margin-left: 70%; +} +60%, +79.9% { + margin-top: 30%; + margin-left: 20%; +} +80%, +99.9% { + margin-top: 30%; + margin-left: 80%; +} +} +@-o-keyframes position { +0%, +19.9% { + margin-top: 10%; + margin-left: 40%; +} +20%, +39.9% { + margin-top: 40%; + margin-left: 30%; +} +40%, +59.9% { + margin-top: 20%; + margin-left: 70%; +} +60%, +79.9% { + margin-top: 30%; + margin-left: 20%; +} +80%, +99.9% { + margin-top: 30%; + margin-left: 80%; +} +} +@-ms-keyframes position { +0%, +19.9% { + margin-top: 10%; + margin-left: 40%; +} +20%, +39.9% { + margin-top: 40%; + margin-left: 30%; +} +40%, +59.9% { + margin-top: 20%; + margin-left: 70%; +} +60%, +79.9% { + margin-top: 30%; + margin-left: 20%; +} +80%, +99.9% { + margin-top: 30%; + margin-left: 80%; +} +} +@keyframes position { +0%, +19.9% { + margin-top: 10%; + margin-left: 40%; +} +20%, +39.9% { + margin-top: 40%; + margin-left: 30%; +} +40%, +59.9% { + margin-top: 20%; + margin-left: 70%; +} +60%, +79.9% { + margin-top: 30%; + margin-left: 20%; +} +80%, +99.9% { + margin-top: 30%; + margin-left: 80%; +} +} From 2b449a440a24088336a4805a766a59fa557ed8f4 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Fri, 25 Sep 2020 13:04:43 +0300 Subject: [PATCH 13/14] Ansible isn't working yet, uploading WIP for testing --- build/ansible/build.yaml | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/build/ansible/build.yaml b/build/ansible/build.yaml index 7bc3e69..aaf4835 100644 --- a/build/ansible/build.yaml +++ b/build/ansible/build.yaml @@ -4,7 +4,7 @@ git: repo: "https://github.com/TheCoreMan/make-git-better-2.git" dest: /home/{{ ansible_facts['user_id'] }}/make-git-better-2 - version: dev + version: 26/auto-hof-page accept_hostkey: yes - name: Compile rust @@ -16,6 +16,13 @@ shell: docker build --tag mgb:0.1 --build-arg CACHE_DATE=$(date +%Y-%m-%d:%H:%M:%S) . args: chdir: /home/{{ ansible_facts['user_id'] }}/make-git-better-2 + become: yes + + - name: Docker housekeeping, delete untagged built docker images to save space + shell: docker rmi $(docker images | grep "^" | awk '{ print $3 }') + # Can ignore errors if no such images exist + ignore_errors: true + become: yes - name: Clone docker-tcp-switchboard git: @@ -53,3 +60,25 @@ # when it finishes running. The nohup is there to prevent it, and the redirections # prevent breaking the process. become: yes + + - name: Submit Flags - Install wasm requirements 1 + yum: + name: + - openssl-devel + - pkgconfig + become: yes + + - name: Submit Flags - Install wasm-pack + command: /home/{{ ansible_facts['user_id'] }}/.cargo/bin/cargo install wasm-pack + + - name: Submit Flags - Build wasm + shell: /home/{{ ansible_facts['user_id'] }}/.cargo/bin/wasm-pack build --target web --out-name wasm --out-dir ./static + args: + chdir: /home/{{ ansible_facts['user_id'] }}/make-git-better-2/submit-flags/submit-flags-app + + - name: Submit Flags - Install HTTP server + command: /home/{{ ansible_facts['user_id'] }}/.cargo/bin/cargo install simple-http-server + + - name: Submit Flags - Run HTTP server + shell: nohup /home/{{ ansible_facts['user_id'] }}/.cargo/bin/simple-http-server --index --nocache --port 1337 /home/{{ ansible_facts['user_id'] }}/make-git-better-2/submit-flags/submit-flags-app/static /dev/null 2>&1 & + become: yes From fbbf75bba4e3273779a85cdb77e84c93d45db451 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Fri, 25 Sep 2020 16:18:56 +0300 Subject: [PATCH 14/14] Added the levels info json --- .../submit-flags-app/static/levels_info.json | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 submit-flags/submit-flags-app/static/levels_info.json diff --git a/submit-flags/submit-flags-app/static/levels_info.json b/submit-flags/submit-flags-app/static/levels_info.json new file mode 100644 index 0000000..51b1cf2 --- /dev/null +++ b/submit-flags/submit-flags-app/static/levels_info.json @@ -0,0 +1,25 @@ +{ + "levels": + [ + { + "name": "remote-1", + "flag": "cb045a9406baa9354fa33cff3fdd27f48165b07cc5a9da025eb4f52ead3d483f" + }, + { + "name": "log-5", + "flag": "cd8a9375f7d4e9c37e1d402f55ac13fd7762d48d975b6ff6b84b136e4dd8f96f" + }, + { + "name": "merge-5", + "flag": "b1a4235065bbb7ac9ba90106d56c2d7fa691524621d1c5067920b04b291031e2" + }, + { + "name": "hooks-2", + "flag": "2ac12517dba3277afbffeece186e2884e5cecf90e970ce9a7c8f550f12c5ef60" + }, + { + "name": "tag-2", + "flag": "7ca275600a4d349e3ba7517d0ca4a9f5e0347f4b7a3fb90e3aa102a82db607fe" + } + ] +}