Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

https://developer.android.com/reference/android/security/NetworkSecurityPolicy#isCleartextTrafficPermitted() #706

Open
wants to merge 45 commits into
base: DragonRider0o0-patch-1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
801a4e5
Add DartPad.dev to the iframe allow list
domesticmouse Oct 5, 2021
178baa7
install go module with `go install` instead of `go get`
j143 Dec 25, 2021
d323995
Snake case metadata table field names (#698)
DragonRider0o0 Dec 30, 2021
cbe08dc
Merge pull request #654 from googlecodelabs/domesticmouse-patch-1
domesticmouse Jan 5, 2022
821748c
Merge pull request #693 from j143/patch-1
cassierecher Mar 8, 2022
fff60fd
Fix byte index out of range panic for likely error responses.
shawnbuso Mar 9, 2022
85498c4
Merge pull request #713 from shawnbuso/imglen
shawnbuso Mar 9, 2022
79e16b8
Use `eventHandler. listen`. (#718)
DragonRider0o0 Mar 22, 2022
0b0a6c2
Fix bug with image bytes length detection.
shawnbuso Mar 22, 2022
21e8ad6
Add `disconnectedCallback`. (#722)
DragonRider0o0 Mar 29, 2022
50f2fc3
Simplify page URL hash logic (#725)
DragonRider0o0 Apr 1, 2022
c3b83b4
Merge pull request #719 from shawnbuso/imglen
shawnbuso Apr 5, 2022
d977ead
Improve mobile codelab title.
DragonRider0o0 Apr 7, 2022
538f6f7
Fix image extension parsing. (#729)
DragonRider0o0 Apr 13, 2022
7b0573f
Prevent reloads when setting hash. (#731)
DragonRider0o0 Apr 14, 2022
ec84552
Update export.go
DragonRider0o0 Oct 21, 2022
f8529ef
Update fetch.go
DragonRider0o0 Oct 21, 2022
3b2249d
Merge pull request #792 from googlecodelabs/DragonRider0o0-patch-4
cassierecher Oct 21, 2022
966d39d
fix oob oauth to use localhost and update elements bucket (#798)
mco-gh Nov 9, 2022
6d81f90
Use exact match with IframeAllowlist (#799)
DragonRider0o0 Nov 9, 2022
228dc9c
update version string (#802)
mco-gh Dec 5, 2022
8b5107f
Add demo.arcade.software to the whitelist (#830)
rmanalan Apr 18, 2023
50becb1
Update parse.go
DragonRider0o0 May 25, 2023
976cba9
Update parse.go
DragonRider0o0 May 25, 2023
3d4299e
Update parse_test.go
DragonRider0o0 May 25, 2023
6d7e1d3
Update image_test.go
DragonRider0o0 May 25, 2023
a62f6c7
Update fetch.go
DragonRider0o0 May 25, 2023
9fff679
Update image.go
DragonRider0o0 May 25, 2023
872cbcf
Merge pull request #835 from googlecodelabs/image-urls
cassierecher May 25, 2023
1f31303
Update google_codelab.js
JasonCent Jun 22, 2023
4c7c18d
Update google_codelab_analytics.js
JasonCent Jun 22, 2023
4d5855f
Update google_codelab_analytics.js
JasonCent Jun 22, 2023
0df4e0f
Update google_codelab_analytics.js
JasonCent Jun 22, 2023
60399ab
Update parse.go
JasonCent Jun 22, 2023
633b390
Update parse_test.go
JasonCent Jun 22, 2023
61b07e9
Update template.go
JasonCent Jun 22, 2023
2584e92
Update template.html
JasonCent Jun 22, 2023
0f43cf7
Update codelab.go
JasonCent Jun 22, 2023
b293135
Update google_codelab_analytics.js
JasonCent Jun 22, 2023
57b79d1
Merge pull request #842 from googlecodelabs/Add-GA4
JasonCent Jun 22, 2023
4c98edf
Merge pull request #843 from googlecodelabs/Add-GA4-1
JasonCent Jun 22, 2023
27f42e2
feat: add stackblitz.com in iframe whitelist (#787)
jfgreffier Aug 29, 2023
dfb356f
Update README.md
mco-gh Sep 27, 2023
9f2b585
Merge pull request #855 from googlecodelabs/mco-gh-patch-2
cassierecher Oct 9, 2023
873fe39
add vimeo (#825)
rossbeale Feb 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Tools for authoring and serving codelabs

[![Demo](https://storage.googleapis.com/claat/demo.png)](https://storage.googleapis.com/claat/demo.mp4)

Codelabs are interactive instructional tutorials, which can be authored in Google Docs
using some simple formatting conventions. You can also author codelabs using markdown syntax.
This repo contains all the tools and documentation you’ll need for building and publishing
Expand Down
2 changes: 1 addition & 1 deletion claat/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The binaries, as well as their checksums are available at the

Alternatively, if you have [Go installed](https://golang.org/doc/install):

go get github.com/googlecodelabs/tools/claat
go install github.com/googlecodelabs/tools/claat@latest

If none of the above works, compile the tool from source following Dev workflow
instructions below.
Expand Down
2 changes: 1 addition & 1 deletion claat/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.0.4
2.2.5
15 changes: 7 additions & 8 deletions claat/cmd/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,20 +111,19 @@ func ExportCodelab(src string, rt http.RoundTripper, opts CmdExportOptions) (*ty
lastmod := types.ContextTime(clab.Mod)
clab.Meta.Source = src
meta := &clab.Meta
ctx := &types.Context{
Env: opts.Expenv,
Format: opts.Tmplout,
Prefix: opts.Prefix,
MainGA: opts.GlobalGA,
Updated: &lastmod,
}

dir := opts.Output // output dir or stdout
if !isStdout(dir) {
dir = codelabDir(dir, meta)
}
// write codelab and its metadata to disk
return meta, writeCodelab(dir, clab.Codelab, opts.ExtraVars, ctx)
return meta, writeCodelab(dir, clab.Codelab, opts.ExtraVars, &types.Context{
Env: opts.Expenv,
Format: opts.Tmplout,
Prefix: opts.Prefix,
MainGA: opts.GlobalGA,
Updated: &lastmod,
})
}

func ExportCodelabMemory(src io.ReadCloser, w io.Writer, opts CmdExportOptions) (*types.Meta, error) {
Expand Down
30 changes: 25 additions & 5 deletions claat/fetch/drive/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"path"
Expand All @@ -43,14 +44,33 @@ var (
ClientID: googClient,
ClientSecret: googSecret,
Scopes: []string{scopeDriveReadOnly},
RedirectURL: "urn:ietf:wg:oauth:2.0:oob",
RedirectURL: "http://localhost:8091",
Endpoint: oauth2.Endpoint{
AuthURL: "https://accounts.google.com/o/oauth2/auth",
TokenURL: "https://accounts.google.com/o/oauth2/token",
},
}
)

// The webserver waits for an oauth code in the three-legged auth flow.
func startWebServer() (code string, err error) {
listener, err := net.Listen("tcp", "localhost:8091")
if err != nil {
return "", err
}
codeCh := make(chan string)

go http.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
code := r.FormValue("code")
codeCh <- code // send code to OAuth flow
listener.Close()
w.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(w, "Received oauth code\r\nYou can now safely close this browser window.")
}))
code = <- codeCh
return code, nil
}

type authorizationHandler func(conf *oauth2.Config) (*oauth2.Token, error)

type internalOptions struct {
Expand Down Expand Up @@ -214,10 +234,10 @@ func (c *cachedTokenSource) Token() (*oauth2.Token, error) {

// authorize performs user authorization flow, asking for permissions grant.
func authorize(conf *oauth2.Config) (*oauth2.Token, error) {
aurl := conf.AuthCodeURL("unused", oauth2.AccessTypeOffline)
fmt.Printf("Authorize me at following URL, please:\n\n%s\n\nCode: ", aurl)
var code string
if _, err := fmt.Scan(&code); err != nil {
aURL := conf.AuthCodeURL("unused", oauth2.AccessTypeOffline)
fmt.Printf("Authorize me at following URL, please:\n\n%s\n", aURL)
code, err := startWebServer()
if err != nil {
return nil, err
}
return conf.Exchange(context.Background(), code)
Expand Down
88 changes: 59 additions & 29 deletions claat/fetch/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ const (

// driveAPI is a base URL for Drive API
driveAPI = "https://www.googleapis.com/drive/v3"

// Minimum image size in bytes for extension detection.
minImageSize = 11
)

// TODO: create an enum for use with "nometa" for readability's sake
Expand Down Expand Up @@ -111,6 +114,7 @@ type Fetcher struct {
roundTripper http.RoundTripper
}

// NewFetcher creates an instance of Fetcher.
func NewFetcher(at string, pm map[string]bool, rt http.RoundTripper) (*Fetcher, error) {
return &Fetcher{
authHelper: nil,
Expand Down Expand Up @@ -225,7 +229,7 @@ func (f *Fetcher) SlurpImages(src, dir string, n []nodes.Node, images map[string
for _, imageNode := range imageNodes {
go func(imageNode *nodes.ImageNode) {
url := imageNode.Src
file, err := f.slurpBytes(src, dir, url)
file, err := f.slurpBytes(src, dir, url, imageNode.Bytes)
if err == nil {
imageNode.Src = filepath.Join(util.ImgDirname, file)
}
Expand All @@ -247,43 +251,52 @@ func (f *Fetcher) SlurpImages(src, dir string, n []nodes.Node, images map[string
return nil
}

func (f *Fetcher) slurpBytes(codelabSrc, dir, imgURL string) (string, error) {
// images can be local in Markdown cases or remote.
func (f *Fetcher) slurpBytes(codelabSrc, dir, imgURL string, imgBytes []byte) (string, error) {
// images can be data URLs, local in Markdown cases or remote.
// Only proceed a simple copy on local reference.
var b []byte
var ext string
u, err := url.Parse(imgURL)
if err != nil {
return "", err
}
var err error

// If the codelab source is being downloaded from the network, then we should interpret
// the image URL in the same way.
srcUrl, err := url.Parse(codelabSrc)
if err == nil && srcUrl.Host != "" {
u = srcUrl.ResolveReference(u)
}

if u.Host == "" {
if imgURL, err = restrictPathToParent(imgURL, filepath.Dir(codelabSrc)); err != nil {
return "", err
if len(imgBytes) > 0 {
// Slurp bytes from image URL data.
b = imgBytes
if ext, err = imgExtFromBytes(b); err != nil {
return "", fmt.Errorf("Error reading image type: %v", err)
}
b, err = ioutil.ReadFile(imgURL)
ext = filepath.Ext(imgURL)
} else {
b, err = f.slurpRemoteBytes(u.String(), 5)
if string(b[6:10]) == "JFIF" {
ext = ".jpeg"
} else if string(b[0:3]) == "GIF" {
ext = ".gif"
// Slurp bytes from local or remote URL.
u, err := url.Parse(imgURL)
if err != nil {
return "", err
}

// If the codelab source is being downloaded from the network, then we should interpret
// the image URL in the same way.
srcURL, err := url.Parse(codelabSrc)
if err == nil && srcURL.Host != "" {
u = srcURL.ResolveReference(u)
}

if u.Host == "" {
if imgURL, err = restrictPathToParent(imgURL, filepath.Dir(codelabSrc)); err != nil {
return "", err
}
if b, err = ioutil.ReadFile(imgURL); err != nil {
return "", err
}
ext = filepath.Ext(imgURL)
} else {
ext = ".png"
if b, err = f.slurpRemoteBytes(u.String(), 5); err != nil {
return "", fmt.Errorf("Error downloading image at %s: %v", u.String(), err)
}
if ext, err = imgExtFromBytes(b); err != nil {
return "", fmt.Errorf("Error reading image type at %s: %v", u.String(), err)
}
}
}
if err != nil {
return "", err
}

// Generate image file from slurped bytes.
crc := crc64.Checksum(b, f.crcTable)
file := fmt.Sprintf("%x%s", crc, ext)
dst := filepath.Join(dir, file)
Expand Down Expand Up @@ -479,7 +492,10 @@ func gdocID(url string) string {
}

func gdocExportURL(id string) string {
return fmt.Sprintf("%s/files/%s/export?mimeType=text/html", driveAPI, id)
q := url.Values{
"mimeType": {"text/html"},
}
return fmt.Sprintf("%s/files/%s/export?%s", driveAPI, id, q.Encode())
}

// restrictPathToParent will ensure that assetPath is in parent.
Expand Down Expand Up @@ -509,3 +525,17 @@ func isStdout(filename string) bool {
func codelabDir(base string, m *types.Meta) string {
return filepath.Join(base, m.ID)
}

func imgExtFromBytes(b []byte) (string, error) {
if len(b) < minImageSize {
return "", fmt.Errorf("error parsing image - response \"%s\" is too small (< %d bytes)", b, minImageSize)
}
ext := ".png"
switch {
case string(b[6:10]) == "JFIF":
ext = ".jpeg"
case string(b[0:3]) == "GIF":
ext = ".gif"
}
return ext, nil
}
28 changes: 28 additions & 0 deletions claat/fetch/fetch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,34 @@ func TestFuzzRestrictPathToParent(t *testing.T) {
}
}

func TestImgExtFromBytes(t *testing.T) {
tests := []struct {
bytes []byte

wantExt string
wantErr bool
}{
{[]byte("012345JFIF0"), ".jpeg", false},
{[]byte("GIF34567890"), ".gif", false},
{[]byte("SOMETHINGELSE"), ".png", false},
{[]byte("GIF345JFIF0"), ".jpeg", false},
{[]byte("toosmall"), "", true},
}
for _, tc := range tests {
t.Run(fmt.Sprintf("bytes: %s", tc.bytes), func(t *testing.T) {
ext, err := imgExtFromBytes(tc.bytes)

if err != nil != tc.wantErr {
t.Errorf("imgExtFromBytes() error = %v, wantErr %v", err, tc.wantErr)
return
}
if ext != tc.wantExt {
t.Errorf("imgExtFromBytes() return: got %s, wanted %s", ext, tc.wantExt)
}
})
}
}

// safeAbs compute Abs of p and fail the test if not valid.
// Empty string return empty path.
func safeAbs(t *testing.T, p string) string {
Expand Down
1 change: 1 addition & 0 deletions claat/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.16

require (
github.com/google/go-cmp v0.5.6
github.com/stoewer/go-strcase v1.2.0 // indirect
github.com/x1ddos/csslex v0.0.0-20160125172232-7894d8ab8bfe
github.com/yuin/goldmark v1.3.7
golang.org/x/net v0.0.0-20210525063256-abc453219eb5
Expand Down
3 changes: 3 additions & 0 deletions claat/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,11 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/x1ddos/csslex v0.0.0-20160125172232-7894d8ab8bfe h1:SX7lFdwn40ahL78CxofAh548P+dcWjdRNpirU7+sKiE=
github.com/x1ddos/csslex v0.0.0-20160125172232-7894d8ab8bfe/go.mod h1:SwmD4V+Y0RjNqvt8hW2FpZNkQnoFVNtBF9qEnevUueU=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down
4 changes: 4 additions & 0 deletions claat/nodes/iframe.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ var IframeAllowlist = []string{
"carto.com",
"codepen.io",
"dartlang.org",
"dartpad.dev",
"demo.arcade.software",
"github.com",
"glitch.com",
"google.com",
"google.dev",
"observablehq.com",
"repl.it",
"stackblitz.com",
"vimeo.com",
"web.dev",
}

Expand Down
5 changes: 4 additions & 1 deletion claat/nodes/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ type NewImageNodeOptions struct {
Width float32
Alt string
Title string
Bytes []byte
}

// NewImageNode creates a new ImageNode with the given options.
Expand All @@ -18,6 +19,7 @@ func NewImageNode(opts NewImageNodeOptions) *ImageNode {
Width: opts.Width,
Alt: opts.Alt,
Title: opts.Title,
Bytes: opts.Bytes,
}
}

Expand All @@ -28,11 +30,12 @@ type ImageNode struct {
Width float32
Alt string
Title string
Bytes []byte
}

// Empty returns true if its Src is zero, excluding space runes.
func (in *ImageNode) Empty() bool {
return strings.TrimSpace(in.Src) == ""
return strings.TrimSpace(in.Src) == "" && len(in.Bytes) == 0
}

// ImageNodes extracts everything except NodeImage nodes, recursively.
Expand Down
21 changes: 20 additions & 1 deletion claat/nodes/image_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package nodes

import (
"encoding/base64"
"testing"

"github.com/google/go-cmp/cmp"
)

var testBytes, _ = base64.StdEncoding.DecodeString("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")

func TestNewImageNode(t *testing.T) {
tests := []struct {
name string
Expand All @@ -19,7 +22,7 @@ func TestNewImageNode(t *testing.T) {
},
},
{
name: "NonEmpty",
name: "StandardURL",
inOpts: NewImageNodeOptions{
Src: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
Width: 1.0,
Expand All @@ -34,6 +37,22 @@ func TestNewImageNode(t *testing.T) {
Alt: "bar",
},
},
{
name: "DataURL",
inOpts: NewImageNodeOptions{
Width: 1.0,
Title: "foo",
Alt: "bar",
Bytes: testBytes,
},
out: &ImageNode{
node: node{typ: NodeImage},
Width: 1.0,
Title: "foo",
Alt: "bar",
Bytes: testBytes,
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
Expand Down
Loading
Loading