Browse Source

Works again

Has a go library, better testing, statsd, etc.

Next step: go mod/sum
master
Teran McKinney 2 years ago
parent
commit
48b141932b
  1. 17
      README.md
  2. 2
      burnpaste.go
  3. 13
      burnpaste.service
  4. 103
      cmd/burnpaste/main.go
  5. 61
      test.sh

17
README.md

@ -1,12 +1,21 @@
# burnpaste
golang pastebin for burn-on-reading pastes.
Burn-on-reading pastebin. Also has a client library.
## Install
* `go get github.com/teran-mckinney/burnpaste/...`
## Usage
* Run: `go run burnpaste`
* Write: `curl --data-urlencode data@burnpaste.go localhost:4444/write # Grab the returned hash from this.`
* Read: `curl localhost:4444/read/(hash)`
* run: `burnpaste`
* Write: `curl --data-urlencode data@burnpaste.go localhost:2323/write # Grab the returned hash from this.`
* Read: `curl localhost:2323/read/(hash)`
* Try another read: `curl localhost:2323/read/(hash)` (Nope, it was deleted)
## Note
Listens globally on port 2323 by default, can be overridden.
## License

2
burnpaste.go

@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"strings"
@ -40,7 +39,6 @@ func Write(endpoint string, paste []byte) (string, error) {
func Read(burnpasteURL string) ([]byte, error) {
urlParts := strings.Split(burnpasteURL, "/")
urlHash := urlParts[len(urlParts)-1]
log.Printf("urlHash: %s", urlHash)
response, err := http.Get(burnpasteURL)
if err != nil {
return []byte(""), err

13
burnpaste.service

@ -0,0 +1,13 @@
[Unit]
Description=burnpaste burn-on-reading pastebin
[Service]
DynamicUser=yes
ExecStart=/usr/local/bin/burnpaste
RuntimeDirectory=burnpaste
WorkingDirectory=/run/burnpaste
ProtectSystem=strict
NoNewPrivileges=yes
UMask=0077
Restart=on-failure
[Install]
WantedBy=multi-user.target

103
cmd/burnpaste/main.go

@ -2,6 +2,7 @@
package main
import (
"errors"
"fmt"
"io/ioutil"
"log"
@ -10,13 +11,80 @@ import (
"strings"
"github.com/teran-mckinney/burnpaste"
"gopkg.in/alexcesaro/statsd.v2"
)
// 8197 works out to be the same thing as 4096 y's. 4096 regular letters will be larger, but roughly gives us 4KiB max file size or less.
const MAX_POST = 8197
// Max length of de-URL encoded paste. Max possible length of a file is this plus MAX_PAGE_LENGTH, plus two bytes.
const MAX_LENGTH = 4096
// Max number of unread pastes
const MAX_FILES = 128
func dataTooLong(data []byte) error {
// Return error string if data is too long, nil if not.
var err error
length := len(data)
if length == 0 {
message := "No form data found."
log.Print(message)
err = errors.New(message)
return err
} else if length > MAX_LENGTH {
log.Printf("This is strange, data is %d bytes long but we allowed the POST?", length)
err = errors.New("Your paste was too long.")
return err
}
return err
}
func countPastes() (int, error) {
var err error
var pastes []os.FileInfo
pastes, err = ioutil.ReadDir(".")
if err != nil {
log.Print("EXCEPTION")
log.Print(err)
err = errors.New("Issue totaling up pastes. Please contact the administrator.")
return 9000, err
}
return len(pastes), err
}
func tooManyPastes(numberOfPastes int) error {
// Make sure we don't fill up disk with too many pastes.
// Return error string if too many pastes, nil if not.
var err error
if numberOfPastes >= MAX_FILES {
log.Print("EXCEPTION")
log.Printf("%d pastes detected, max is %d", numberOfPastes, MAX_FILES)
err = errors.New("Too many pastes. Please contact the administrator.")
}
return err
}
func main() {
/* Statsd statistics. This works fine with or without. */
s, err := statsd.New(statsd.Prefix("burnpaste"))
if err != nil {
log.Print("burnpaste connection to statsd failed. This is not a problem unless you want statsd.")
// This should be non-fatal.
log.Print(err)
} else {
log.Print("burnpaste connected to statsd.")
}
defer s.Close()
http.HandleFunc("/read/", func(w http.ResponseWriter, r *http.Request) {
var err error
path_parts := strings.Split(r.URL.Path, "/")
path := path_parts[len(path_parts)-1]
s.Increment("read.hit")
pathParts := strings.Split(r.URL.Path, "/")
path := pathParts[len(pathParts)-1]
if len(path) != 64 {
log.Println("Error: Path too short.")
goto error
@ -32,6 +100,10 @@ func main() {
err = os.Remove(path)
if err == nil {
log.Printf("read: %s\n", path)
numberOfPastes, err := countPastes()
if err == nil {
s.Gauge("paste_count", numberOfPastes)
}
}
return
error:
@ -40,11 +112,38 @@ func main() {
})
http.HandleFunc("/write", func(w http.ResponseWriter, r *http.Request) {
s.Increment("write.hit")
// https://stackoverflow.com/questions/28282370/is-it-advisable-to-further-limit-the-size-of-forms-when-using-golang
r.Body = http.MaxBytesReader(w, r.Body, MAX_POST)
err = r.ParseForm()
if err != nil {
log.Print("POST too long.")
http.Error(w, "Your POST request was too long.", 400)
return
}
// data is paste text.
data := []byte(r.FormValue("data"))
if err = dataTooLong(data); err != nil {
http.Error(w, err.Error(), 400)
return
}
numberOfPastes, err := countPastes()
if err != nil {
http.Error(w, err.Error(), 500)
}
if err = tooManyPastes(numberOfPastes); err != nil {
http.Error(w, err.Error(), 500)
return
}
path := burnpaste.Hash(data)
ioutil.WriteFile(path, []byte(data), 0600)
fmt.Fprintf(w, path)
log.Printf("write: %s\n", path)
s.Gauge("paste_count", numberOfPastes+1)
})
var port = ":2323"
if len(os.Args) == 2 {

61
test.sh

@ -9,17 +9,31 @@ shellcheck "$0"
# Before we build...
go fmt
go doc
go test
# This comes later because it requires a running daemon
# go test
go fmt ./cmd/burnpaste/
go test ./cmd/burnpaste/ -v
go doc ./cmd/burnpaste/
go build
go build ./cmd/burnpaste/
strip -s burnpaste
./burnpaste &
mkdir test_dir
cd test_dir
../burnpaste &
PID=$!
# So, there's a bug in shellcheck where disabling SC2103 doesn't work.
# shellcheck disable=SC2103
# cd ..
cd "$(echo .. | grep .)"
cleanup() {
echo "Cleaning up."
kill "$PID"
exit 1
rm -r test_dir
}
trap cleanup $(seq 1 64)
@ -37,43 +51,66 @@ sha256() {
# Be sure we are running.
sleep 2
go test
# Test security, slightly.
echo 'This should give a 400.'
output=$(curl -s --show-error --fail "http://localhost:4444/read/README.md" || true)
output=$(curl -s --show-error --fail "http://localhost:2323/read/README.md" || true)
[ ! -f 'README.md' ] && fail 'Whoops, we deleted README.md'
# Functional tests.
pasteid=$(echo test | curl -s --show-error --fail --data-urlencode data@- "http://localhost:4444"/write)
pasteid=$(echo test | curl -s --show-error --fail --data-urlencode data@- "http://localhost:2323"/write)
[ -z "$pasteid" ] && fail 'pasteid empty.'
[ "$pasteid" != "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2" ] && fail 'pasteid does not match.'
output=$(curl -s --show-error --fail "http://localhost:4444/read/$pasteid")
output=$(curl -s --show-error --fail "http://localhost:2323/read/$pasteid")
[ "$output" != 'test' ] && fail 'Paste output inconsistent.'
echo 'This should give a 404.'
output=$(curl -s --show-error --fail "http://localhost:4444/read/$pasteid" || true)
output=$(curl -s --show-error --fail "http://localhost:2323/read/$pasteid" || true)
[ "$output" = 'test' ] && fail 'Paste not deleted.'
# Now test with burnpaste.go
pasteid=$(curl -s --show-error --fail --data-urlencode data@- "http://localhost:4444"/write < burnpaste.go)
pasteid=$(curl -s --show-error --fail --data-urlencode data@- "http://localhost:2323"/write < burnpaste.go)
[ "$pasteid" != "$(sha256 burnpaste.go)" ] && fail 'pasteid does not match for burnpaste.go.'
output_checksum=$(curl -s --show-error --fail "http://localhost:4444/read/$pasteid" | sha256 -)
output_checksum=$(curl -s --show-error --fail "http://localhost:2323/read/$pasteid" | sha256 -)
[ "$pasteid" != "$output_checksum" ] && fail 'output does not match for burnpaste.go.'
kill "$PID"
echo -n | curl -s --show-error --fail --data-urlencode data@- "http://localhost:2323"/write && fail "Should error on empty input."
# This is.. approximate because it's URL encoded.
yes | head -c 4096 | curl -s --show-error --fail --data-urlencode data@- "http://localhost:2323"/write || fail "Should allow a 4096 byte post."
yes | head -c 4097 | curl -s --show-error --fail --data-urlencode data@- "http://localhost:2323"/write && fail "Should fail on anything greater than 4096 bytes."
[ "$(wc -l test_dir/309a1668b23adc98b0ec1b67d55bdca1e89e9d81c0930d5baf9b85df85d76ee0)" != "4096" ] || fail "4096 byte file wrong length or checksum???"
output_checksum=$(curl -s --show-error --fail "http://localhost:2323/read/309a1668b23adc98b0ec1b67d55bdca1e89e9d81c0930d5baf9b85df85d76ee0" | sha256 -)
[ "309a1668b23adc98b0ec1b67d55bdca1e89e9d81c0930d5baf9b85df85d76ee0" != "$output_checksum" ] && fail 'output does not match for 4096 byte paste'
for i in $(seq 1 128); do
yes | head -c "$i" | curl -s --show-error --fail --data-urlencode data@- "http://localhost:2323"/write || fail "Should be able to add paste number $i"
done
echo test | curl -s --show-error --fail --data-urlencode data@- "http://localhost:2323"/feedback && fail "Should not be able to make more than 128 feedbacks."
[ "$(find test_dir/ -type f | wc -l)" -ne 128 ] && fail "We don't have exactly 128 pastes??"
cleanup
sleep 0.1
# FIXME: Eventually look for 64 character length files in this directory as a sign of issues. For now, just ls.
ls -1
echo "burnpaste.go sha256: $pasteid"
echo
echo Success.
Loading…
Cancel
Save