Serving my blog posts as Linux manual pages
jamesg.blog
jamesg.blog
So `apt update` will pull in all blog posts, `man your-blog` will show the latest post with links to the index of all your other posts.
I think I'd be too chicken to subscribe.
Edit: and navigate like so: `docker image myblog.com/recipes` to list all recipes. God I love APIs.
Or maybe this is a microcosm of why docker and kuberwhatever happens so much.
I'll bite: with GP's take, docker-compose would make for a good webring/blogroll. Or use it as bookmarking system.
cd blogs/cooking
docker-compose up
# 20 different recipe blogs available as man pagesTIL there's no `ls -al /usr/share/man/** | man --html`; though it would be easy to build one with Python's http.server, or bottlepy, or bash,.
Prompt: An http server ( with bash and /dev/tcp/ ) that serves HTML versions of local manpages with linkification of URLs and references to other manpages
It doesn't since file:/// is just a uri and not part of the http protocol
If so, Isn't it thus probably better to run an HTTP server over a permissioned socket than to serve static HTML [manpages] from file URLs [in a [DEB] package]?
https://github.com/capjamesg/jamesg.blog.deb has all you need to build a man-page-only deb file using:
git clone https://github.com/capjamesg/jamesg.blog.deb cd jamesg.blog.deb dpkg-deb --build --root-owner-group jamesg.blog sudo dpkg -i jamesg.blog.deb
You should see:
... Processing triggers for man-db (2.9.1-1) ...
Which indicates the man page for `man jamesg.blog` is available. There is just a placeholder in there for now. I will perhaps finish this tomorrow!
NB: This may become a blog post soon :D
<https://packages.debian.org/bookworm/dwww>
(Joerg Jaspert was the former maintainer of Linux Gazette packages: <https://people.debian.org/~joerg/> (2002).)
This remains to me one of the best examples of integrating information delivery and documentation on an operating system I've ever encountered. Notably, it makes both manual and info documents more useful and usable than their traditional terminal-based interfaces.
There are Debian-associated blogs (Debian Planet), though I don't believe that was ever packaged for Debian itself.
Frankly, RSS is probably a better option for blog subscription.
I remember getting all of those with the Debian Sarge DVD's, good stuff, among the Anarchist Faq, the 'Derivations' math book, and tons offline documentation when I didn't have internet at home.
Of course, all of the documentation that's exposed via dwww is accessible on that host's filesystem. But dwww also provides a few additional affordances: Web-formatted output, a directory of installed documentation (organised by source / type), and (if you have swish++ installed), full-text search.
`curl -sL -H "Accept: text/roff" https://jamesg.blog/2024/02/28/programming-projects/ | man -l -`
already taken.
Friends don't let friends pipe streams into commands
https://news.ycombinator.com/item?id=39554044You might say, yes, but it's still a good idea to review the file before you open it. OK sure, but that isn't going to work for binary files anyway, so "Friends don't let friends pipe streams into commands" should not be the general rule. "Friends don't let friends pipe streams into shells" is certainly a good enough general rule.
This should be disabled by default since groff v1.17 (released in 2001):
https://git.savannah.gnu.org/cgit/groff.git/commit/?id=7b3f5...
2. The person replying to me mentioned ffmpeg and well... see the reply to them. Parsers may not seem like a big deal, but see this about `cat`[0] or this about `less`[1] (a quick search shows a lot of pipe and pager type of vulnerabilities, including privilege escalation). Programs that look simple and a non-risk are probably actually prime targets for hackers because it can lull someone into a false sense of security.
3. You can detect bash piping server side. There's been a bunch of HN and reddit posts, several have been shared already so I won't repeat.
I'm sticking to:
Friends don't let friends pipe streams into commands
It's just safer. Risk is pretty low, but does using `&&` or a `;` instead really create a lot more work? How about curl -sL -H "Accept: text/roff" https://jamesg.blog/2024/02/28/programming-projects/ > post.page && man ./post.page && rm post.page
This at least guarantees you don't get a truncated execution.You'll delete the file after you close man. This at least guarantees you don't get a truncated execution. Better if we send to /tmp/post.page and not delete, in case something is fuzzy. Both of these also prevent server side detection and possible tomfoolery. /tmp will be cleared on reboot anyways and it's better to have the file in case something DOES happen. curl -sL -H "Accept: text/roff" https://jamesg.blog/2024/02/28/programming-projects/ > /tmp/post.page && man /tmp/post.page
[0] https://security.stackexchange.com/questions/56307/can-cat-i...[1] https://ubuntu.com/security/notices/USN-6664-1
Edit:
groff vulns: https://www.cvedetails.com/vulnerability-list/vendor_id-72/p...
What if I have a post.page in my current directory?
> curl -sL -H "Accept: text/roff" https://jamesg.blog/2024/02/28/programming-projects/ > /tmp/post.page && man /tmp/post.page
What if another user runs the command at the same time, then?
Or what if a malicious user creates a 666 mode /tmp/post.page file beforehand, detects when you finish writing to it, then attaches a payload right before `man` reads it?
Unfortunately, there is no perfect solution for this problem; I run arbitrary html, css, and javascript every day by browsing the web. It's debatable whether switching to command chains instead of piping results in overall benefits. Of course, the same goes for vice versa as well.
curl -sL -H "Accept: text/roff" https://jamesg.blog/2024/02/28/programming-projects/ | man /dev/stdinFortunately, macOS has ZSH as the default shell so the following does work:
man =(curl -sL -H "Accept: text/roff" https://jamesg.blog/2024/02/28/programming-projects/)[1] https://zsh.sourceforge.io/Doc/Release/Expansion.html#Proces...
man <(curl ...) ends up working on Linux but for whatever reason still yields an error on macOS:
No manual entry for /dev/fd/11 curl -sL -H "Accept: text/roff" https://jamesg.blog/2024/02/28/programming-projects/ | mandoc -a
works on macOS (and should also work on (Free|Net|Open)BSD, which ship similar man toolchains).It's essentially running the process in another shell and sending its output to a tempfile. The name of that file is then substituted in the original command. So, in theory, equivalent to `inner > /tmp/file; outer /tmp/file`. This works with programs that don't read from stdin, or need two or more inputs, for example.
man -l <(curl -sL -H "Accept: text/roff" https://jamesg.blog/2024/02/28/programming-projects/)For me, I'm all for this. Anyone that is aware of the security implications is extremely likely aware of how to make the conversion like you've provided. In that case, no reason to tell people, because they already know. But it's not a great thing to tell noobies to do because they will get burned. So don't tell them. As they advance they'll naturally learn about this feature. And hopefully they have learned the implications by the time they learn how to do this.
(not me): https://www.seancassidy.me/dont-pipe-to-your-shell.html
The really dangerous thing is copy/pasting from a browser to your terminal. Always do Ctrl-X Ctrl-E to open an editor, paste it in there, then inspect it before saving/closing the editor to run the command.
For zsh users see here: https://nuclearsquid.com/writings/edit-long-commands/
Note: this can be tricky depending on where it goes in your zshrc. If you use a plugin manager (like Sheldon) then this should be above that. I ended up with this and it works well on OSX and linux
autoload -U edit-command-line
# Emacs style (<C-x><C-e>)
zle -N edit-command-line
# make sure `set -o vi` is above this line
bindkey '^xe' edit-command-line
bindkey '^x^e' edit-command-line
# (VIM) Use visual mode
bindkey -M vicmd v edit-command-lineThe two things you need to be able to say you trust are your CA store, and the source of your curl -> shell.
There is no practical difference. "Nobody" will inspect the man page using a different viewer first. So if I download to disk and then view via man or directly via man is no difference.
A shell script one might inspect first using some viewer. While only few probably do.
This can be mitigated by wrapping the script, but clearly no one is looking at the code so this isn't really verified anyways. And it's not like we see that all the time.
Edit:
But my main point is about habits. There's the concern mananaysiempre brings up[0], but either way, it is best to be in good habits.
curl -sL -H "Accept: text/roff" https://jamesg.blog/2024/02/28/programming-projects/ > post.page && man ./post.page
If curl's process is interupted, it'll generate a non-0 exit code and the man command won't be exectued. That's how double ampersands work in shell.
Nobody cares about losing a system. This is the data it is hosting that is valuable and takes the most time to recover from a backup.
https://www.gnu.org/software/coreutils/manual/html_node/Trea...
[1] https://www.idontplaydarts.com/2016/04/detecting-curl-pipe-b...
[2] https://web.archive.org/web/20240228190305/https://www.idont...
Interesting vector if you're worried about people piping into man from curl, but there you go.
It’s generally considered bad practice only for bash and similar commands that execute their input. It’s not a bad practice at all for commands that just display or transform their input, like `man`, `less`, `ffmpeg`, etc.
(Also, I don’t know about ffmpeg as it is somewhat more rare to have it be public-facing, but there have definitely been exploits against ImageMagick, usually targeted at websites using it to process user input.)
> ffmpeg There is certainly a few hundered exploitable vectors in that program alone... to say nothing of the rest.
When in doubt, spin up a VM to run the random untrusted thing -- And then go read its mailing list/issue tracker for known VM escaping exploits. I have a machine setup to test malware, so I just hit my "airgap" switch to isolate the system from my network once the questionable code is in place and ready to run (potentially amok). Study-up about ARP-poison attacks, and remember ARP does not transit to upstream routers/switches (Y "combinate" your network for fun and profit).
Before you assume non malicious simple text output, consider "ANSI" escape code complexity as an intrusion vector for whatever terminal you run. I've got "0-days" for this going back to MSDOS: ANSI Bomb => arbitrary CMD entry. You don't have to take my word for it, your terminal of choice is most certainly vulnerable to some ANSI/escape code related exploit, look it up.
Wait a minute I just realized there could be a zero day in the VM hypervisor too. I guess I'll just have to buy a fresh Raspberry Pi for each file I want to open.
/s
What a terrible argument. Not it's trivial to resolve. Why not just fix things that we know are problems or can lead to serious problems instead of waiting for it to become a problem where it'll then be FAR more work to clean it up?
Seriously, you're a human, not a bug. You have the ability to solve things before they become problems. Use it.
While I'm not sure of a specific example, I feel quite confident in saying that this has been done before.
You're right, piping to sh is definitely a risk, and we should do better as a community, and not make users normalized to piping to sh.
But as you can see elsewhere, we shouldn't pipe streams into anything. It's just not hard to avoid this. A few extra characters and you're good to go. Let's take rust for example. Users are copy pasting anyways
They give
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
But why not curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > /tmp/rust_install.sh && sh /tmp/rust_install.sh
For user experience, it is till a one liner, but it is infinitely better. Still has some risks, but a lot less. (at least rust does wrap the install so you don't run the risk of partial line execution) And it gives the user a way to verify that the file was correct if they provide a checksum.There's just no good reason to not do this. We especially shouldn't be teaching noobies to pipe streams into any command. Let's be real, most people don't know linux well, even if they use linux.
Firstly, I'll assume you mean untrusted streams since if you didn't mean that, we might as well throw out UNIX entirely.
Even given that caveat though, I disagree. Downloading an image and then converting that image into another format is not a case where there's a material improvement in security when making temporary files instead of using pipes. I'd argue in some cases it may have the opposite effect.
It's indicative of the lack of rationale on this more general case that you selected an example where the piping is into sh here.
I agree with all of your points about piping into sh, just not that we can turn that into a general principal. It's OK if we have different best practices between these two cases.
This kind of advice taken by a laymen or junior dev can cause problems, because the second you put the file on disk there's a risk you won't clean up that content, which for an automated system will ultimately bring it down when the disk fills up. In addition, if the information downloaded is sensitive, you are creating a security problem if you are intentionally writing it on disk even if you do clean it up later, as filesystem data remains persistent after unlink.
This is to all the people saying no difference between downloading and running right away
If the download gets interrupted bash will execute the partial line. That means `rm -r /tmp/foo.ext` or `rm -r ${HOME}/.tmp_config` can execute as `rm -r /`. This can be mitigated by wrapping the script. Best way to do this is wrap the whole script into a function and then execute at the last line[0]. Oh, and you can detect `curl|bash` server side[1][0] https://archive.is/20160603044800/https://sandstorm.io/news/...
[1] https://archive.is/20230325190353/https://www.idontplaydarts...
Edit: as an example, rust does the warpping. But they still place this stupid shit on in their install instructions. Bad Rust! Bad!
It's not the best way. If your function is named `lsp_init` and your last line is `lsp_init`, a partial line can result in the execution of `ls`.
AFAIK the best way is to just wrap your script in `()`.
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > /tmp/rust_install.sh && sh /tmp/rust_install.sh
(obviously doesn't solve the problem of inspection, but I think we all know that's not going to happen anyways)curl --limit-rate 1000 http://textfiles.com/sf/STARTREK/trek.vt && reset
(the reset is there because it might mess up your terminal)
Other terminal-powered URIs:
curl cheat.sh/tar (gets examples on how to use the program after the /)
curl wttr.in/berlin (gets weather info with terminal formatting)
It’s actually quite simple; the hardest part is to generate the frames but that can be done with ffmpeg+img2txt.py: https://github.com/bfontaine/RickASCIIRoll/tree/master/movie....
curl ansi.hrtk.in/ungenannt_1453.ans
https://github.com/postmodern/kramdown-man
[0]: https://pandoc.org/
Simply type M-x package-install sicp RET
That gave me the idea that one could install a whole bookshelf of blog archives through some modified feed reader. Reading Info in Emacs, you even get bookmarks.
chicken-install srfi-203 chicken-install srtfi216
~/.csirc for sicp:
(import scheme)
(import (srfi 203))
(import (srfi 216))
(define (inc x)
(+ x 1))
(define (dec x)
(- x 1))
Then user geiser and geiser for chicken as usual.pg was an AT&T thing, more was a BSD thing, less was a GNU thing. With all 3, / starts a regular expression search so you could use that to search for anything, underlined or not.
Less also supports tag files so you could use 't' to jump to the next tag.
In any case, a few pointless things that I seem compelled to say:
* It's not returning roff, as such. Those things like `.TH` are not part of roff, they are part of the macro package for writing man pages. * I was disappointed that there was no markdown-to-roff conversion, which seemed like it was going to be the interesting part of this post. At least use one of the existing ones. * On a similar note, this means that the text isn't really formatted correctly. roff is meant for one sentence per line of input, to distinguish between `.` to end a sentence vs. other uses. * Also also wik, this means that any line starting with a `.` will be interpreted as a command, potentially wreaking havoc.
Or maybe I'm just a grumpy old man.
As for markdown to roff, I thought about it as a v2. As I started to think about implementing a parser, someone shared https://github.com/sunaku/md2man with me, which appears to solve the problem.
I'd need to figure out how to integrate this into my (Python) site that is built on GitHub Pages; a bit of tinkering would be required :D
I found this rather surprising too. Pandoc can trivially convert markdown to man-page roff. Insert that into the given template and it looks like more like an actual man page.
Context: All man pages are generated on the fly on GitHub Pages. My site generates ~2500 pages, for which 826 are eligible for a man page. I didn't want to introduce another parser since I just got my site build times down :D
I can counter increased build times with caching, but it gets a bit icky since some blog pages are evergreen (i.e. my blogroll). [insert cache invalidation complaint here] But there's certainly a way!
Btw, in Bash, you can use process substitution to avoid littering your folder with files, if you don't want to save them:
$ man <(curl ...)
The post includes the template he used. You can adapt it to any templating engine or put some placeholders in and use sed or whatever.
Anyone doing this but with Vim help files?
Those are orthogonal subjects; you could generate your own static page with your generator AND also serve them as manpages. The linked article does not suggest to server manpages to everyone, just to user agents that request them.
I noticed the "written by human, not AI" logo, a little too cute but the sentiment is good. I had been thinking of putting something like "this page created by natural stupidity" in mine.
| soelim | tbl | eqn | nroff -man - | $PAGER
| soelim | tbl | eqn | nroff -man -Tpost - | /usr/lib/postscript/bin/dpost | ps2pdf - > ~/Desktop/blog.PDF
| soelim | tbl | eqn | groff -Tps -man - | ps2pdf - > ~/Desktop/blog.PDFjust run
| groff -Tpdf -step -k > ~/Desktop/blog.pdf