From 9caa5131e09e586cd48bb63a52430cbf10e5b530 Mon Sep 17 00:00:00 2001 From: Wagner Abrantes Date: Tue, 11 Aug 2020 16:37:48 -0300 Subject: [PATCH] =?UTF-8?q?Valida=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wiki.go | 67 ++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/wiki.go b/wiki.go index a75ad63..95160c2 100644 --- a/wiki.go +++ b/wiki.go @@ -1,10 +1,15 @@ package main import ( + // Pacote para auxilio no tratamento e criação de erros + "errors" "html/template" "io/ioutil" "log" "net/http" + + // pacote de expressões regulares + "regexp" ) // Pagina é um struct com a estrutura que as páginas da aplicação terão @@ -13,30 +18,37 @@ type Pagina struct { Corpo []byte } -// Há uma ineficiência neste código: renderizaTemplate chama ParseFiles sempre que uma página é -// renderizada. Uma abordagem melhor seria chamar ParseFiles uma vez na inicialização do programa, -// analisando todos os templates em um único *Template. Então, podemos usar o método ExecuteTemplate para -// renderizar um template específico. +// Como você deve ter observado, este programa tem uma falha de segurança séria: um usuário pode fornecer um +// caminho arbitrário para ser lido/escrito no servidor. Para atenuar isso, podemos escrever uma função para +// validar o título com uma expressão regular. -// Primeiro, criamos uma variável global chamada templates e a inicializamos com ParseFiles. +// Primeiro, adicione "regexp" à lista import. Então, podemos criar uma variável global para armazenar nossa +// expressão de validação: -// A função template.Must é um invólucro que entra em pânico quando é passado um valor error -// não nulo (nil) e, caso contrário, retorna o *Template inalterado. O panic é apropriado aqui; -// se os templates não puderem ser carregados, a única coisa sensata a fazer é sair do programa. +// A função regexp.MustCompile irá analisar e compilar a expressão regular e retornar um regexp.Regexp. +// MustCompile é diferente de Compile porque entrará em panic se a compilação da expressão falhar, enquanto +// Compile retorna um error como um segundo parâmetro. -// A função ParseFiles recebe qualquer número de argumentos de string que identificam nossos arquivos de -// template e os parseam (não sei se existe essa palavra haha) em templates que são nomeados -// após o nome base do arquivo. Se adicionássemos mais modelos ao nosso programa, adicionaríamos -// seus nomes aos argumentos da chamada ParseFiles. +var caminhovalido = regexp.MustCompile("^/(edit|save|view)/([a-zA-Z0-9]+)$") // templates faz o cacheamento dos templates var templates = template.Must(template.ParseFiles("edit.html", "view.html")) -// Em seguida, modificamos a função renderizaTemplate para chamar o método templates.ExecuteTemplate -// com o nome do template apropriado: +// Agora, vamos escrever uma função que usa a expressão caminhovalido para validar o caminho e extrair +// o título da página: + +// Se o título coincide, ele será retornado junto com um valor nil de erro. Se o título for inválido, +// a função gravará um erro "404 Not Found" na conexão HTTP e retornará um erro ao handler. +// Para criar um novo erro customizado, temos que importar o pacote errors. -// Observe que o nome do template é o nome do arquivo do template, portanto, devemos anexar -// ".html" ao argumento tmpl. +func obtemTitulo(escrever http.ResponseWriter, ler *http.Request) (string, error) { + coincide := caminhovalido.FindStringSubmatch(ler.URL.Path) + if coincide == nil { + http.NotFound(escrever, ler) + return "", errors.New("Titulo da página inválido") + } + return coincide[2], nil // The title is the second subexpression. +} // renderizaTemplate faz o parse dos arquivos tratados pelos handlers func renderizaTemplate(escrever http.ResponseWriter, tmpl string, pagina *Pagina) { @@ -62,9 +74,14 @@ func carregaPagina(titulo string) (*Pagina, error) { return &Pagina{Titulo: titulo, Corpo: corpo}, nil } +// Vamos colocar uma chamada para obtemTitulo em cada um dos handlers: + // viewHandler r o titulo e corpo da pagina em html formatado func viewHandler(escrever http.ResponseWriter, ler *http.Request) { - titulo := ler.URL.Path[len("/view/"):] + titulo, err := obtemTitulo(escrever, ler) + if err != nil { + return + } pagina, err := carregaPagina(titulo) if err != nil { http.Redirect(escrever, ler, "/edit/"+titulo, http.StatusFound) @@ -73,9 +90,14 @@ func viewHandler(escrever http.ResponseWriter, ler *http.Request) { renderizaTemplate(escrever, "view", pagina) } +// Vamos colocar uma chamada para obtemTitulo em cada um dos handlers: + // editHandler carrega um formulário de edição func editHandler(escrever http.ResponseWriter, ler *http.Request) { - titulo := ler.URL.Path[len("/edit/"):] + titulo, err := obtemTitulo(escrever, ler) + if err != nil { + return + } pagina, err := carregaPagina(titulo) if err != nil { pagina = &Pagina{Titulo: titulo} @@ -83,13 +105,18 @@ func editHandler(escrever http.ResponseWriter, ler *http.Request) { renderizaTemplate(escrever, "edit", pagina) } +// Vamos colocar uma chamada para obtemTitulo em cada um dos handlers: + // A função saveHandler tratará do envio de formulários localizados nas páginas de edição func saveHandler(escrever http.ResponseWriter, ler *http.Request) { - titulo := ler.URL.Path[len("/save/"):] + titulo, err := obtemTitulo(escrever, ler) + if err != nil { + return + } corpo := ler.FormValue("body") pagina := &Pagina{Titulo: titulo, Corpo: []byte(corpo)} pagina.salvar() - err := pagina.salvar() + err = pagina.salvar() if err != nil { http.Error(escrever, err.Error(), http.StatusInternalServerError) return