@@ -437,46 +437,93 @@ func (e ExtensionsCmd) BuildWebBotAuth(ctx context.Context, in ExtensionsBuildWe
437437 return fmt .Errorf ("npm run build failed: %w" , err )
438438 }
439439
440- // Run npm run build :chrome in the browser-extension directory
440+ // Run npm run bundle :chrome in the browser-extension directory (builds and packs as CRX)
441441 extDir := filepath .Join (repoDir , "examples" , "browser-extension" )
442- pterm .Info .Println ("Building extension (npm run build :chrome)..." )
443- npmBuild := exec .CommandContext (ctx , "npm" , "run" , "build :chrome" )
444- npmBuild .Dir = extDir
445- npmBuild .Stdout = os .Stdout
446- npmBuild .Stderr = os .Stderr
447- if err := npmBuild .Run (); err != nil {
448- return fmt .Errorf ("npm run build :chrome failed: %w" , err )
442+ pterm .Info .Println ("Building and bundling extension (npm run bundle :chrome)..." )
443+ npmBundle := exec .CommandContext (ctx , "npm" , "run" , "bundle :chrome" )
444+ npmBundle .Dir = extDir
445+ npmBundle .Stdout = os .Stdout
446+ npmBundle .Stderr = os .Stderr
447+ if err := npmBundle .Run (); err != nil {
448+ return fmt .Errorf ("npm run bundle :chrome failed: %w" , err )
449449 }
450450
451- // Copy built extension to output directory
451+ // Create unpacked subdirectory for the extension files (used for upload)
452+ unpackedDir := filepath .Join (outDir , "unpacked" )
453+ if err := os .MkdirAll (unpackedDir , 0o755 ); err != nil {
454+ return fmt .Errorf ("failed to create unpacked directory: %w" , err )
455+ }
456+
457+ // Copy built extension files to unpacked/
452458 builtDir := filepath .Join (extDir , "dist" , "mv3" , "chromium" )
453- manifestSrc := filepath .Join (extDir , "platform" , "mv3" , "chromium" , "manifest.json" )
454459
455460 // Copy background.mjs
456461 bgSrc := filepath .Join (builtDir , "background.mjs" )
457- if err := copyFile (bgSrc , filepath .Join (outDir , "background.mjs" )); err != nil {
462+ if err := copyFile (bgSrc , filepath .Join (unpackedDir , "background.mjs" )); err != nil {
458463 return fmt .Errorf ("failed to copy background.mjs: %w" , err )
459464 }
460465
461- // Read manifest and add version (the build script doesn't add it, only bundle does)
462- manifestData , err := os .ReadFile (manifestSrc )
463- if err != nil {
464- return fmt .Errorf ("failed to read manifest.json: %w" , err )
466+ // Copy manifest.json (bundle:chrome already adds version)
467+ manifestSrc := filepath .Join (builtDir , "manifest.json" )
468+ if err := copyFile (manifestSrc , filepath .Join (unpackedDir , "manifest.json" )); err != nil {
469+ return fmt .Errorf ("failed to copy manifest.json: %w" , err )
470+ }
471+
472+ // Copy CRX bundle artifacts
473+ artifactsDir := filepath .Join (extDir , "dist" , "web-ext-artifacts" )
474+ crxSrc := filepath .Join (artifactsDir , "http-message-signatures-extension.crx" )
475+ if err := copyFile (crxSrc , filepath .Join (outDir , "extension.crx" )); err != nil {
476+ return fmt .Errorf ("failed to copy extension.crx: %w" , err )
465477 }
466- var manifest map [string ]interface {}
467- if err := json .Unmarshal (manifestData , & manifest ); err != nil {
468- return fmt .Errorf ("failed to parse manifest.json: %w" , err )
478+
479+ updateXMLSrc := filepath .Join (artifactsDir , "update.xml" )
480+ if err := copyFile (updateXMLSrc , filepath .Join (outDir , "update.xml" )); err != nil {
481+ return fmt .Errorf ("failed to copy update.xml: %w" , err )
469482 }
470- manifest ["version" ] = "1.0.0"
471- manifestOut , err := json .MarshalIndent (manifest , "" , " " )
472- if err != nil {
473- return fmt .Errorf ("failed to marshal manifest.json: %w" , err )
483+
484+ // Copy policy files
485+ policyDir := filepath .Join (extDir , "policy" )
486+ policyJSONSrc := filepath .Join (policyDir , "policy.json" )
487+ if err := copyFile (policyJSONSrc , filepath .Join (outDir , "policy.json" )); err != nil {
488+ return fmt .Errorf ("failed to copy policy.json: %w" , err )
474489 }
475- if err := os .WriteFile (filepath .Join (outDir , "manifest.json" ), manifestOut , 0o644 ); err != nil {
476- return fmt .Errorf ("failed to write manifest.json: %w" , err )
490+
491+ plistSrc := filepath .Join (policyDir , "com.google.Chrome.managed.plist" )
492+ if err := copyFile (plistSrc , filepath .Join (outDir , "com.google.Chrome.managed.plist" )); err != nil {
493+ return fmt .Errorf ("failed to copy plist: %w" , err )
494+ }
495+
496+ // Copy RSA private key (useful for re-signing later)
497+ privateKeySrc := filepath .Join (extDir , "private_key.pem" )
498+ if err := copyFile (privateKeySrc , filepath .Join (outDir , "private_key.pem" )); err != nil {
499+ return fmt .Errorf ("failed to copy private_key.pem: %w" , err )
500+ }
501+
502+ // Extract extension ID from update.xml and save it
503+ updateXMLData , err := os .ReadFile (updateXMLSrc )
504+ if err == nil {
505+ // Parse extension ID from update.xml (it's in the appid attribute)
506+ xmlStr := string (updateXMLData )
507+ if idx := strings .Index (xmlStr , `appid="` ); idx != - 1 {
508+ start := idx + 7
509+ if end := strings .Index (xmlStr [start :], `"` ); end != - 1 {
510+ extID := xmlStr [start : start + end ]
511+ if err := os .WriteFile (filepath .Join (outDir , "extension-id.txt" ), []byte (extID ), 0o644 ); err != nil {
512+ pterm .Warning .Printf ("Failed to write extension-id.txt: %v\n " , err )
513+ }
514+ }
515+ }
477516 }
478517
479- pterm .Success .Printf ("Built extension to: %s\n " , outDir )
518+ pterm .Success .Printf ("Built extension bundle to: %s\n " , outDir )
519+ pterm .Info .Println ("Bundle contents:" )
520+ pterm .Info .Println (" - extension.crx (packed extension for policy installation)" )
521+ pterm .Info .Println (" - update.xml (Chrome update manifest)" )
522+ pterm .Info .Println (" - policy.json (Linux/Chrome OS policy file)" )
523+ pterm .Info .Println (" - com.google.Chrome.managed.plist (macOS policy file)" )
524+ pterm .Info .Println (" - private_key.pem (RSA key for re-signing)" )
525+ pterm .Info .Println (" - extension-id.txt (Chrome extension ID)" )
526+ pterm .Info .Println (" - unpacked/ (unpacked extension files)" )
480527
481528 // Optionally upload
482529 if in .Upload {
@@ -485,7 +532,7 @@ func (e ExtensionsCmd) BuildWebBotAuth(ctx context.Context, in ExtensionsBuildWe
485532 name = "web-bot-auth"
486533 }
487534 pterm .Info .Printf ("Uploading extension as '%s'...\n " , name )
488- if err := e .Upload (ctx , ExtensionsUploadInput {Dir : outDir , Name : name }); err != nil {
535+ if err := e .Upload (ctx , ExtensionsUploadInput {Dir : unpackedDir , Name : name }); err != nil {
489536 return err
490537 }
491538 }
@@ -641,6 +688,15 @@ var extensionsBuildWebBotAuthCmd = &cobra.Command{
641688This command downloads and builds Cloudflare's web-bot-auth browser extension,
642689which adds RFC 9421 HTTP Message Signatures to all outgoing requests.
643690
691+ The output includes:
692+ - extension.crx (packed extension for policy installation)
693+ - update.xml (Chrome update manifest)
694+ - policy.json (Linux/Chrome OS policy file)
695+ - com.google.Chrome.managed.plist (macOS policy file)
696+ - private_key.pem (RSA key for re-signing)
697+ - extension-id.txt (Chrome extension ID)
698+ - unpacked/ (unpacked extension files for Kernel upload)
699+
644700By default, it uses the RFC9421 test key that works with Cloudflare's test site
645701at https://http-message-signatures-example.research.cloudflare.com/
646702
0 commit comments