Skip to content

Commit b7aab37

Browse files
authored
feat: add nonce placeholder option to cli insert command (#105)
1 parent 12e7ac5 commit b7aab37

File tree

10 files changed

+1652
-1960
lines changed

10 files changed

+1652
-1960
lines changed

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,11 +200,12 @@ given directory. Directory defaults to current working directory.
200200

201201
Usage: ngssc insert [options] [directory]
202202

203-
| Options | Description |
204-
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
205-
| `--recursive, -r` | Recursively searches for ngssc.json files and applies the contained configuration. |
206-
| `--nginx` | Applies default configuration for ngssc insert to work with nginx. Sets working directory to /usr/share/nginx/html/ and recursive to true. |
207-
| `--dry` | Perform the insert without actually inserting the variables. |
203+
| Options | Description |
204+
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
205+
| `--recursive, -r` | Recursively searches for ngssc.json files and applies the contained configuration. |
206+
| `--nginx` | Applies default configuration for ngssc insert to work with nginx. Sets working directory to /usr/share/nginx/html/ and recursive to true. |
207+
| `--dry` | Perform the insert without actually inserting the variables. |
208+
| `--nonce="example"` | Generates a nonce in the script tag with the given placeholder. |
208209

209210
#### ngssc substitute
210211

cli/insert.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
func InsertCommand(c *cli.Context) error {
1313
// Init Flags
1414
nginxFlag := c.Bool("nginx")
15+
noncePlaceholder := c.String("nonce")
1516
dryRunFlag := c.Bool("dry")
1617
recursive := c.Bool("recursive")
1718
if !recursive && nginxFlag {
@@ -50,8 +51,9 @@ func InsertCommand(c *cli.Context) error {
5051
}
5152

5253
task := InsertionTask{
53-
path: workingDirectory,
54-
dryRun: dryRunFlag,
54+
path: workingDirectory,
55+
noncePlaceholder: noncePlaceholder,
56+
dryRun: dryRunFlag,
5557
}
5658
if recursive {
5759
return task.Recursive()

cli/insert_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,17 @@ func TestNgsscProcessWithNgsw(t *testing.T) {
189189
"Expected ngsw.json to be updated:\n "+result.Stdout())
190190
}
191191

192+
func TestNonce(t *testing.T) {
193+
context := newTestDir(t)
194+
context.CreateFile("index.html", configHTMLTemplate)
195+
context.CreateFile("ngssc.json", `{"variant":"process","environmentVariables":["TEST_VALUE"]}`)
196+
197+
result := runWithArgs("insert", context.path, "--nonce=CSP_NONCE")
198+
assertSuccess(t, result)
199+
expect := `<script nonce="CSP_NONCE">(function(self){self.process={"env":{"TEST_VALUE":null}};})(window)</script>`
200+
assertContains(t, context.ReadFile("index.html"), expect, "Expected html to contain iife.")
201+
}
202+
192203
var configHTMLTemplate = `<!DOCTYPE html>
193204
<html lang="en">
194205

cli/insertion_target.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package main
33
import (
44
"crypto/sha1"
55
"fmt"
6-
"io/ioutil"
76
"os"
87
"path/filepath"
98
"regexp"
@@ -12,20 +11,29 @@ import (
1211

1312
// InsertionTarget represents an html file target
1413
type InsertionTarget struct {
15-
filePath string
16-
ngsscConfig NgsscConfig
14+
filePath string
15+
noncePlaceholder string
16+
ngsscConfig NgsscConfig
1717
}
1818

1919
// Insert the environment variables into the targeted file
2020
func (target InsertionTarget) Insert() error {
21-
htmlBytes, err := ioutil.ReadFile(target.filePath)
21+
htmlBytes, err := os.ReadFile(target.filePath)
2222
if err != nil {
2323
return fmt.Errorf("failed to read %v\n%v", target.filePath, err)
2424
}
2525

26+
nonce := ""
27+
if target.noncePlaceholder != "" {
28+
nonce = fmt.Sprintf(
29+
" nonce=\"%v\"",
30+
target.noncePlaceholder)
31+
}
32+
2633
html := string(htmlBytes)
2734
iifeScript := fmt.Sprintf(
28-
"<!--ngssc--><script>%v</script><!--/ngssc-->",
35+
"<!--ngssc--><script%v>%v</script><!--/ngssc-->",
36+
nonce,
2937
target.ngsscConfig.BuildIifeScriptContent())
3038
var newHTML string
3139
ngsscRegex := regexp.MustCompile(`<!--ngssc-->[\w\W]*<!--/ngssc-->`)
@@ -41,7 +49,7 @@ func (target InsertionTarget) Insert() error {
4149
}
4250

4351
newHTMLBytes := []byte(newHTML)
44-
err = ioutil.WriteFile(target.filePath, newHTMLBytes, 0644)
52+
err = os.WriteFile(target.filePath, newHTMLBytes, 0644)
4553
if err != nil {
4654
return fmt.Errorf("failed to update %v\n%v", target.filePath, err)
4755
}
@@ -58,7 +66,7 @@ func replaceIndexHashInNgsw(target InsertionTarget, originalHash []byte, replace
5866
return
5967
}
6068

61-
ngswBytes, err := ioutil.ReadFile(filePath)
69+
ngswBytes, err := os.ReadFile(filePath)
6270
if err != nil {
6371
fmt.Printf("Detected ngsw.json, but failed to read it at %v\n", filePath)
6472
return
@@ -73,7 +81,7 @@ func replaceIndexHashInNgsw(target InsertionTarget, originalHash []byte, replace
7381

7482
replacedWrappedHexHash := createQuotedHash(replacedHash)
7583
replacedNgswContent := strings.Replace(ngswContent, wrappedHexHash, replacedWrappedHexHash, 1)
76-
err = ioutil.WriteFile(filePath, []byte(replacedNgswContent), info.Mode())
84+
err = os.WriteFile(filePath, []byte(replacedNgswContent), info.Mode())
7785
if err != nil {
7886
fmt.Printf("Detected ngsw.json, but failed to update it at %v\n", filePath)
7987
return

cli/insertion_task.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import (
99

1010
// InsertionTask represents an insertion task
1111
type InsertionTask struct {
12-
path string
13-
dryRun bool
12+
path string
13+
noncePlaceholder string
14+
dryRun bool
1415
}
1516

1617
// Single will perform the insertion for a single ngssc.json
@@ -53,8 +54,9 @@ func (task InsertionTask) insertWithNgssc(ngsscConfig NgsscConfig) error {
5354
if !task.dryRun {
5455
for _, insertionFile := range files {
5556
target := InsertionTarget{
56-
filePath: insertionFile,
57-
ngsscConfig: ngsscConfig,
57+
filePath: insertionFile,
58+
noncePlaceholder: task.noncePlaceholder,
59+
ngsscConfig: ngsscConfig,
5860
}
5961
target.Insert()
6062
}

cli/main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ func run(args []string) error {
4848
Name: "dry",
4949
Usage: "Perform the insert without actually inserting the variables.",
5050
},
51+
cli.StringFlag{
52+
Name: "nonce",
53+
Usage: "Generates a nonce in the script tag with the given placeholder.",
54+
},
5155
},
5256
Action: InsertCommand,
5357
},

package.json

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -45,35 +45,35 @@
4545
"homepage": "https://github.com/kyubisation/angular-server-side-configuration#readme",
4646
"private": true,
4747
"dependencies": {
48-
"@angular/animations": "^18.0.6",
49-
"@angular/common": "^18.0.6",
50-
"@angular/compiler": "^18.0.6",
51-
"@angular/core": "^18.0.6",
52-
"@angular/forms": "^18.0.6",
53-
"@angular/platform-browser": "^18.0.6",
54-
"@angular/platform-browser-dynamic": "^18.0.6",
55-
"@angular/platform-server": "^18.0.6",
56-
"@angular/router": "^18.0.6",
57-
"@angular/ssr": "^18.0.7",
48+
"@angular/animations": "^18.2.2",
49+
"@angular/common": "^18.2.2",
50+
"@angular/compiler": "^18.2.2",
51+
"@angular/core": "^18.2.2",
52+
"@angular/forms": "^18.2.2",
53+
"@angular/platform-browser": "^18.2.2",
54+
"@angular/platform-browser-dynamic": "^18.2.2",
55+
"@angular/platform-server": "^18.2.2",
56+
"@angular/router": "^18.2.2",
57+
"@angular/ssr": "^18.2.2",
5858
"rxjs": "7.8.1",
5959
"tslib": "^2.6.3",
60-
"zone.js": "~0.14.6"
60+
"zone.js": "~0.14.10"
6161
},
6262
"devDependencies": {
63-
"@angular-devkit/architect": "^0.1800.7",
64-
"@angular-devkit/build-angular": "^18.0.7",
65-
"@angular-devkit/core": "^18.0.7",
66-
"@angular-devkit/schematics": "^18.0.7",
67-
"@angular-eslint/builder": "18.1.0",
68-
"@angular-eslint/eslint-plugin": "18.1.0",
69-
"@angular-eslint/eslint-plugin-template": "18.1.0",
70-
"@angular-eslint/schematics": "18.1.0",
71-
"@angular-eslint/template-parser": "18.1.0",
72-
"@angular/build": "^18.0.7",
73-
"@angular/cli": "^18.0.7",
74-
"@angular/compiler-cli": "^18.0.6",
75-
"@angular/localize": "18.0.6",
76-
"@schematics/angular": "^18.0.7",
63+
"@angular-devkit/architect": "^0.1802.2",
64+
"@angular-devkit/build-angular": "^18.2.2",
65+
"@angular-devkit/core": "^18.2.2",
66+
"@angular-devkit/schematics": "^18.2.2",
67+
"@angular-eslint/builder": "18.3.0",
68+
"@angular-eslint/eslint-plugin": "18.3.0",
69+
"@angular-eslint/eslint-plugin-template": "18.3.0",
70+
"@angular-eslint/schematics": "18.3.0",
71+
"@angular-eslint/template-parser": "18.3.0",
72+
"@angular/build": "^18.2.2",
73+
"@angular/cli": "^18.2.2",
74+
"@angular/compiler-cli": "^18.2.2",
75+
"@angular/localize": "18.2.2",
76+
"@schematics/angular": "^18.2.2",
7777
"@types/jasmine": "~5.1.4",
7878
"@types/node": "^20.12.12",
7979
"@typescript-eslint/eslint-plugin": "^7.16.0",

0 commit comments

Comments
 (0)