Repository: HapticX/happyx (language: python)

Issue:
Attributes and text not escaped in buildHtml (ssr) 🐛
**Describe the bug** 🐛
There is a difference between the rendering in spa and what ssr produce using buildHtml, in the ssr case html code can be injected both in attributes and in text strings.

**To Reproduce** 👨‍🔬
```nim

import ../src/happyx

appRoutes("app"):
  "/":
    tDiv(class="hello\" style=\"color:green"):
      "hello <b>world</b>"
```
escapes the quotes in the class attribute when run as a spa. The html produced is
```html
<div class="hello&quot; style=&quot;color:green">hello &lt;b&gt;world&lt;/b&gt;</div>
```

For ssr:
```nim
import
  ../src/happyx

serve("127.0.0.1", 5000):
  get "/":
    buildHtml:
      tDiv(class="hello\" style=\"color:green"):
        "hello <b>world</b>"
```
does not escape the `class` attribute or the `<b>` in the text and gives
```html
<div class="hello" style="color:green">
hello <b>world</b>
</div>
```

**Expected behavior** 🤔
I would expect the behavior of the spa where the attributes and the text strings are escaped.


Discussion / hints:

Fix patch:
diff --git a/src/happyx/spa/tag.nim b/src/happyx/spa/tag.nim
index b9afeb03..b46b12d3 100644
--- a/src/happyx/spa/tag.nim
+++ b/src/happyx/spa/tag.nim
@@ -102,8 +102,11 @@ when defined(js):
   const _originCloneNode = Node.prototype.cloneNode;
 
   Node.prototype.__getEventIndex = function(target, targetArgs) {
-    if (!this._eventListeners)
+    if (!this._eventListeners) {
       this._eventListeners = [];
+      return -1;
+    }
+    if (!targetArgs) return -1;
     return this._eventListeners.findIndex(args => {
       for (let i = 0; i < args.length; i++)
         if (targetArgs[i] !== args[i]) return false;
@@ -598,12 +601,12 @@ func `$`*(self: TagRef): string =
       argsStr = self.args.join(" ")
 
     if self.isText:
-      return self.name
+      return xmltree.escape(self.name)
     
     var attrs = ""
     for key, value in self.attrs.pairs():
       if value.len > 0:
-        attrs &= " " & key & "=" & "\"" & value & "\""
+        attrs &= " " & key & "=" & "\"" & value.replace("\"", "&quot;") & "\""
       else:
         attrs &= " " & key
 
@@ -932,12 +935,12 @@ when defined(js):
       argsStr = self.args.join(" ")
 
     if self.isText:
-      return self.name
+      return xmltree.escape(self.name)
     
     var attrs = ""
     for key, value in self.attrs.pairs():
       if value.len > 0:
-        attrs &= " " & key & "=" & "\"" & value & "\""
+        attrs &= " " & key & "=" & "\"" & value.replace("\"", "&quot;") & "\""
       else:
         attrs &= " " & key
 
diff --git a/src/happyx/ssr/cors.nim b/src/happyx/ssr/cors.nim
index 4c11e557..34768e58 100644
--- a/src/happyx/ssr/cors.nim
+++ b/src/happyx/ssr/cors.nim
@@ -45,7 +45,13 @@ when not defined(js) and not (exportJvm or exportPython or defined(napibuild)):
       if `allowMethods`.len > 0:
         `headers`["Access-Control-Allow-Methods"] = `allowMethods`
       if `allowOrigins`.len > 0:
-        `headers`["Access-Control-Allow-Origin"] = `allowOrigins`
+        if `allowOrigins` == @["*"]:
+          when not enableHttpx and not enableHttpBeast:
+            `headers`["Access-Control-Allow-Origin"] = req.hostname
+          else:
+            `headers`["Access-Control-Allow-Origin"] = req.ip
+        else:
+          `headers`["Access-Control-Allow-Origin"] = `allowOrigins`
   
   macro regCORS*(body: untyped): untyped =
     ## Register CORS
@@ -148,7 +154,7 @@ else:
       if cors.allowOrigins.len > 0:
         for origin in cors.allowOrigins:
           if origin == "*":
-            `headers`["Access-Control-Allow-Origin"] = "*"
+            `headers`["Access-Control-Allow-Origin"] = host
             break
           elif origin == `host`:
             `headers`["Access-Control-Allow-Origin"] = origin


Test patch:
diff --git a/tests/testc20.nim b/tests/testc20.nim
index 25d8d795..b58791b4 100644
--- a/tests/testc20.nim
+++ b/tests/testc20.nim
@@ -13,6 +13,11 @@ serve("127.0.0.1", 5000):
   post "/[data:Obj:json]":
     echo data
     return ""
+  
+  get "/":
+    buildHtml:
+      tDiv(class="hello\" style=\"color:green"):
+        "hello <b>world</b>"
 
   get "/issue287":
     statusCode = 200
diff --git a/tests/testc6.nim b/tests/testc6.nim
index 5dc3e65b..e92da8a2 100644
--- a/tests/testc6.nim
+++ b/tests/testc6.nim
@@ -5,7 +5,7 @@ import
 
 regCORS:
   credentials: true
-  origins: "https://www.google.com"  # You can send request from this address
+  origins: "*" # "https://www.google.com"  # You can send request from this address
   methods: ["GET", "POST", "PUT"]
   headers: "*"
 
diff --git a/tests/testjs18.nim b/tests/testjs18.nim
index 7353c284..c89ec471 100644
--- a/tests/testjs18.nim
+++ b/tests/testjs18.nim
@@ -86,8 +86,25 @@ let C = buildHtml:
     text(x="35",y="20", style= "fill: black;"): "C: Anyone Here?"
 
 
+component Hello:
+  html:
+    tDiv:
+      "hello"
+
+let b = buildHtml:
+  Hello
+
+let c = buildHtml:
+  tDiv:
+    "hello"
+
+
 appRoutes("app"):
   "/":
+    tDiv: {$b}
+    tDiv:
+      {$c}
+      c
     for i in 1..5:
       tDiv:
         {$htmlProcs[0]()}