Initial commit
10
.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
db.json
|
||||||
|
*.log
|
||||||
|
node_modules/
|
||||||
|
public/
|
||||||
|
.deploy*/
|
||||||
|
_multiconfig.yml
|
||||||
|
svrjs/
|
||||||
|
google.json
|
0
_config.landscape.yml
Normal file
151
_config.yml
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
# Hexo Configuration
|
||||||
|
## Docs: https://hexo.io/docs/configuration.html
|
||||||
|
## Source: https://github.com/hexojs/hexo/
|
||||||
|
|
||||||
|
# Site
|
||||||
|
title: SVR.JS Blog
|
||||||
|
subtitle: ''
|
||||||
|
description: 'Welcome to the SVR.JS Blog! Explore our latest blog posts featuring programming tips, web development insights, and server setup guides. Stay tuned for the latest updates and helpful insights to enhance your knowledge in the field.'
|
||||||
|
keywords:
|
||||||
|
- web server
|
||||||
|
- svr.js
|
||||||
|
- programming
|
||||||
|
- tips
|
||||||
|
- web
|
||||||
|
- server setup
|
||||||
|
- web development
|
||||||
|
- http
|
||||||
|
author: SVR.JS
|
||||||
|
language: en-US
|
||||||
|
timezone: ''
|
||||||
|
|
||||||
|
# URL
|
||||||
|
## Set your site url here. For example, if you use GitHub Page, set url as 'https://username.github.io/project'
|
||||||
|
url: https://blog.svrjs.org
|
||||||
|
permalink: :year/:month/:day/:title/
|
||||||
|
permalink_defaults:
|
||||||
|
pretty_urls:
|
||||||
|
trailing_index: false # Set to false to remove trailing 'index.html' from permalinks
|
||||||
|
trailing_html: false # Set to false to remove trailing '.html' from permalinks
|
||||||
|
|
||||||
|
# Directory
|
||||||
|
source_dir: source
|
||||||
|
public_dir: public
|
||||||
|
tag_dir: tags
|
||||||
|
archive_dir: archives
|
||||||
|
category_dir: categories
|
||||||
|
code_dir: downloads/code
|
||||||
|
i18n_dir: :lang
|
||||||
|
skip_render:
|
||||||
|
|
||||||
|
# Writing
|
||||||
|
new_post_name: :title.md # File name of new posts
|
||||||
|
default_layout: post
|
||||||
|
titlecase: false # Transform title into titlecase
|
||||||
|
external_link:
|
||||||
|
enable: false # Open external links in new tab
|
||||||
|
field: site # Apply to the whole site
|
||||||
|
exclude: ''
|
||||||
|
filename_case: 0
|
||||||
|
render_drafts: false
|
||||||
|
post_asset_folder: false
|
||||||
|
relative_link: false
|
||||||
|
future: true
|
||||||
|
highlight:
|
||||||
|
enable: true
|
||||||
|
line_number: true
|
||||||
|
auto_detect: false
|
||||||
|
tab_replace: ''
|
||||||
|
wrap: true
|
||||||
|
hljs: false
|
||||||
|
prismjs:
|
||||||
|
enable: false
|
||||||
|
preprocess: true
|
||||||
|
line_number: true
|
||||||
|
tab_replace: ''
|
||||||
|
|
||||||
|
# Home page setting
|
||||||
|
# path: Root path for your blogs index page. (default = '')
|
||||||
|
# per_page: Posts displayed per page. (0 = disable pagination)
|
||||||
|
# order_by: Posts order. (Order by date descending by default)
|
||||||
|
index_generator:
|
||||||
|
path: ''
|
||||||
|
per_page: 10
|
||||||
|
order_by: -date
|
||||||
|
|
||||||
|
# Category & Tag
|
||||||
|
default_category: uncategorized
|
||||||
|
category_map:
|
||||||
|
tag_map:
|
||||||
|
|
||||||
|
# Metadata elements
|
||||||
|
## https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta
|
||||||
|
meta_generator: true
|
||||||
|
|
||||||
|
# Date / Time format
|
||||||
|
## Hexo uses Moment.js to parse and display date
|
||||||
|
## You can customize the date format as defined in
|
||||||
|
## http://momentjs.com/docs/#/displaying/format/
|
||||||
|
date_format: DD.MM.YYYY
|
||||||
|
time_format: HH:mm:ss
|
||||||
|
## updated_option supports 'mtime', 'date', 'empty'
|
||||||
|
updated_option: 'mtime'
|
||||||
|
|
||||||
|
# Pagination
|
||||||
|
## Set per_page to 0 to disable pagination
|
||||||
|
per_page: 10
|
||||||
|
pagination_dir: page
|
||||||
|
|
||||||
|
# Include / Exclude file(s)
|
||||||
|
## include:/exclude: options only apply to the 'source/' folder
|
||||||
|
include:
|
||||||
|
exclude:
|
||||||
|
ignore:
|
||||||
|
|
||||||
|
# Extensions
|
||||||
|
## Plugins: https://hexo.io/plugins/
|
||||||
|
## Themes: https://hexo.io/themes/
|
||||||
|
theme: svrjs
|
||||||
|
|
||||||
|
# Deployment
|
||||||
|
## Docs: https://hexo.io/docs/one-command-deployment
|
||||||
|
deploy:
|
||||||
|
- type: url_submission
|
||||||
|
|
||||||
|
# FEEDS
|
||||||
|
|
||||||
|
feed:
|
||||||
|
enable: true
|
||||||
|
type: atom
|
||||||
|
path: atom.xml
|
||||||
|
limit: 20
|
||||||
|
content: true
|
||||||
|
order_by: -date
|
||||||
|
autodiscovery: true
|
||||||
|
|
||||||
|
sitemap:
|
||||||
|
path:
|
||||||
|
- sitemap.xml
|
||||||
|
rel: true
|
||||||
|
|
||||||
|
robotstxt:
|
||||||
|
useragent: "*"
|
||||||
|
allow:
|
||||||
|
- /
|
||||||
|
sitemap: https://blog.svrjs.org/sitemap.xml
|
||||||
|
|
||||||
|
excerpt:
|
||||||
|
depth: 5
|
||||||
|
|
||||||
|
url_submission:
|
||||||
|
enable: true
|
||||||
|
type: 'latest'
|
||||||
|
channels:
|
||||||
|
google:
|
||||||
|
key: "google.json"
|
||||||
|
bing:
|
||||||
|
token: ""
|
||||||
|
count: 10
|
||||||
|
prefix: ['/']
|
||||||
|
urls_path: 'submit_url.txt'
|
||||||
|
sitemap: 'https://blog.svrjs.org/sitemap.xml'
|
2949
package-lock.json
generated
Normal file
31
package.json
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"name": "hexo-site",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build": "hexo generate",
|
||||||
|
"clean": "hexo clean",
|
||||||
|
"deploy": "hexo deploy",
|
||||||
|
"server": "hexo server"
|
||||||
|
},
|
||||||
|
"hexo": {
|
||||||
|
"version": "7.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"hexo": "^7.0.0",
|
||||||
|
"hexo-excerpt": "^1.3.0",
|
||||||
|
"hexo-generator-archive": "^2.0.0",
|
||||||
|
"hexo-generator-category": "^2.0.0",
|
||||||
|
"hexo-generator-feed": "^3.0.0",
|
||||||
|
"hexo-generator-index": "^3.0.0",
|
||||||
|
"hexo-generator-robotstxt": "^0.2.0",
|
||||||
|
"hexo-generator-sitemap": "^3.0.1",
|
||||||
|
"hexo-generator-tag": "^2.0.0",
|
||||||
|
"hexo-renderer-ejs": "^2.0.0",
|
||||||
|
"hexo-renderer-marked": "^6.0.0",
|
||||||
|
"hexo-renderer-stylus": "^3.0.0",
|
||||||
|
"hexo-server": "^3.0.0",
|
||||||
|
"hexo-theme-landscape": "^1.0.0",
|
||||||
|
"hexo-url-submission": "^2.0.0"
|
||||||
|
}
|
||||||
|
}
|
4
preparetest.sh
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
git clone -b stable https://git.svrjs.org/git/svrjs.git
|
||||||
|
cp svrjs-config.json svrjs/config.json
|
4
scaffolds/draft.md
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
title: {{ title }}
|
||||||
|
tags:
|
||||||
|
---
|
4
scaffolds/page.md
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
title: {{ title }}
|
||||||
|
date: {{ date }}
|
||||||
|
---
|
5
scaffolds/post.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: {{ title }}
|
||||||
|
date: {{ date }}
|
||||||
|
tags:
|
||||||
|
---
|
9
source/404.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
title: 404 Not Found
|
||||||
|
date: 2018-05-31 00:00:00
|
||||||
|
permalink: /404.html
|
||||||
|
sitemap: false
|
||||||
|
---
|
||||||
|
The requested file doesn't exist. If you have typed the URL manually, then please check the spelling.
|
||||||
|
|
||||||
|
**Or maybe go back to [blog home page](/), read [The Book of ZSOiE](https://svrjs.org/zsoiebook.svr) or browse our [SVR.JS documentation](https://svrjs.org/docs)? You decide!**
|
|
@ -0,0 +1,28 @@
|
||||||
|
---
|
||||||
|
title: Attention! Upgrade RedBrick, OrangeCircle, YellowSquare and reverse-proxy-mod!
|
||||||
|
date: 2023-08-15 14:41:13
|
||||||
|
tags:
|
||||||
|
- cybersecurity
|
||||||
|
categories: Notices
|
||||||
|
---
|
||||||
|
|
||||||
|
We have discovered security vulnerabilites in those SVR.JS mods. Fortunately we have patched those mods. **But it's recommended to upgrade these mods immediately.**
|
||||||
|
|
||||||
|
**Patched versions**:
|
||||||
|
- <del>**RedBrick 2.3.3** and newer</del>
|
||||||
|
- <del>**reverse-proxy-mod 1.0.4** and newer</del>
|
||||||
|
- <del>**OrangeCircle 1.0.2** and newer</del>
|
||||||
|
- <del>**YellowSquare 1.0.1** and newer</del>
|
||||||
|
|
||||||
|
Unpatched versions have various configuration file and source code leakage vulnerabilities. You can view [our security advisory](https://svrjs.org/advisories/svrjs-00001.pdf).
|
||||||
|
|
||||||
|
**UPDATE**: We discovered this vulnerability: "An attacker could hack the upstream server, replace the web server or application with one that sends an invalid HTTP response code, and make a request to the hacked server through the reverse proxy to crash the reverse proxy server". The vulnerability is patched in **reverse-proxy-mod 1.1.2** and newer.
|
||||||
|
|
||||||
|
**UPDATE 2**: We have discovered and mitigated even more security vulnerabilites in RedBrick, OrangeCircle and YellowSquare. **We recommend to upgrade your SVR.JS mods to patched versions immediately.**
|
||||||
|
|
||||||
|
**Patched versions**:
|
||||||
|
- **RedBrick 2.5.4** and newer
|
||||||
|
- **OrangeCircle 1.0.4** and newer
|
||||||
|
- **YellowSquare 1.0.4** and newer
|
||||||
|
|
||||||
|
With unpatched versions, an attacker could add HTTP authentication header to the HTTP request when not required to enable web application functionality normally disabled on unauthenticated requests. You can view our [security advisory](https://svrjs.org/advisories/svrjs-00005.pdf).
|
|
@ -0,0 +1,62 @@
|
||||||
|
---
|
||||||
|
title: Attention! Upgrade SVR.JS to 3.13.0 or 3.4.41 LTS and newer!
|
||||||
|
date: 2023-09-12 23:26:12
|
||||||
|
tags:
|
||||||
|
- cybersecurity
|
||||||
|
categories: Notices
|
||||||
|
---
|
||||||
|
|
||||||
|
We have discovered more security vulnerabilites, this time in SVR.JS itself. We have patched SVR.JS, but **we recommend to upgrade your SVR.JS web server to patched versions immediately.**
|
||||||
|
|
||||||
|
**Patched versions:**
|
||||||
|
|
||||||
|
<del>
|
||||||
|
|
||||||
|
- **SVR.JS 3.9.2** and newer
|
||||||
|
- **SVR.JS 3.4.30 LTS** and newer
|
||||||
|
|
||||||
|
</del>
|
||||||
|
|
||||||
|
Unpatched versions didn't properly sanitize URLs for SVR.JS mods and server-side JavaScript, leaving them vulnerable. You can view our [security advisory](https://svrjs.org/advisories/svrjs-00002.pdf).
|
||||||
|
|
||||||
|
**UPDATE**: We have discovered and mitigated even more security vulnerabilites in SVR.JS itself. **We recommend to upgrade your SVR.JS web server to patched versions immediately.**
|
||||||
|
|
||||||
|
**Patched versions:**
|
||||||
|
|
||||||
|
<del>
|
||||||
|
|
||||||
|
- **SVR.JS 3.9.3** and newer
|
||||||
|
- **SVR.JS 3.4.31 LTS** and newer
|
||||||
|
|
||||||
|
</del>
|
||||||
|
|
||||||
|
Unpatched versions didn't properly enforce access control for non-proxy SVR.JS mods and server-side JavaScript, leaving them vulnerable. You can view our [security advisory](https://svrjs.org/advisories/svrjs-00003.pdf).
|
||||||
|
|
||||||
|
**UPDATE 2**: We have discovered and mitigated security vulnerabilites in SVR.JS itself even further. **We strongly recommend to upgrade your SVR.JS web server to patched versions immediately.**
|
||||||
|
|
||||||
|
**Patched versions:**
|
||||||
|
|
||||||
|
<del>
|
||||||
|
|
||||||
|
- **SVR.JS 3.9.6** and newer
|
||||||
|
- **SVR.JS 3.4.34 LTS** and newer
|
||||||
|
|
||||||
|
</del>
|
||||||
|
|
||||||
|
Unpatched versions did allow access of *temp* directory inside SVR.JS installation directory to the public, leading to information leakage. You can view our [security advisory](https://svrjs.org/advisories/svrjs-00004.pdf).
|
||||||
|
|
||||||
|
**UPDATE 3**: SVR.JS versions from 3.9.6 to 3.10.1 had a bug with wrong mod loading order. The bug is related to mod access control vulnerability mitigation. The bug didn't affect LTS versions. The bug is fixed in **SVR.JS 3.10.2** and newer.
|
||||||
|
|
||||||
|
**UPDATE 4**: It seems like we had patched a lot more vulnerabilities in SVR.JS. There is a list of them:
|
||||||
|
|
||||||
|
**Fixed in SVR.JS 3.13.0 and in SVR.JS 3.4.41 LTS:**
|
||||||
|
|
||||||
|
* An attacker could use user name with newlines on HTTP authentication to inject false log entries. (introduced in SVR.JS 3.0.0)
|
||||||
|
* An attacker could install problematic SVR.JS mod with newlines in its filename to inject false log entries. (introduced in SVR.JS 3.0.0)
|
||||||
|
|
||||||
|
**Fixed in SVR.JS 3.12.1 and in SVR.JS 3.4.39 LTS:**
|
||||||
|
|
||||||
|
* An attacker could inject HTML code into the extName parameter of the callServerError method to perform XSS attack.
|
||||||
|
* An attacker could inject HTML code into the serverAdministratorEmail config.json property and cause the server to return 500, 502, 503, 504, 506 or 509 error code using a default error page or an error page with a _{contact}_ placeholder to perform XSS attack.
|
||||||
|
|
||||||
|
We're feeling like we're doing too many updates. You can [track latest discovered SVR.JS vulnerabilites](https://svrjs.org/vulnerabilities).
|
|
@ -0,0 +1,43 @@
|
||||||
|
---
|
||||||
|
title: Bun 1.0 released! SVR.JS can run on it! (at least partially)
|
||||||
|
date: 2023-09-08 21:36:33
|
||||||
|
tags:
|
||||||
|
- bun
|
||||||
|
- node.js
|
||||||
|
categories: News
|
||||||
|
---
|
||||||
|
|
||||||
|
**[Bun](https://bun.sh)** - a Node.JS-compatible JavaScript runtime (using JavaScriptCore), package manager, bundler, test runner and all-in-one toolkit has finally a production-ready version - **[Bun 1.0](https://bun.sh/blog/bun-v1.0)!**
|
||||||
|
|
||||||
|
We did get SVR.JS working in Bun, beginning with SVR.JS 3.0.0. But Bun changed fast, and so older SVR.JS versions won't work properly with newest versions of Bun. We have tested Bun 1.0 (it introduced native IPC not present in Bun 0.x), patched SVR.JS and released **SVR.JS 3.9.4** and **SVR.JS 3.4.32 LTS** <!--<s>**SVR.JS 3.9.6**</s> and **SVR.JS 3.4.34 LTS**--> (older versions displayed *TypeError*s with Bun 1.0).
|
||||||
|
|
||||||
|
**Bun support is still experimental** though, so you may run into problems with SVR.JS startup ("*There was an unknown error with the server.*" errors; master will shut down but worker will be active) or see "*There was a problem when handling SVR.JS worker! (from master process side)*" warnings, because [Bun doesn't support native clustering yet](https://github.com/oven-sh/bun/issues/2428) (SVR.JS shims missing *cluster* module; the shim may break with newer versions of Bun making SVR.JS reliability suffer).
|
||||||
|
|
||||||
|
If you run into issues running SVR.JS on Bun 1.0, try using **Bun 0.8.1** (latest developement version; it works with clustered SVR.JS and it's shim) or **Node.JS** (SVR.JS is designed for it).
|
||||||
|
|
||||||
|
You may also **create this startup script** for SVR.JS running on Bun 1.0 like this (multiple clustering will not work on SVR.JS running on Bun 1.0, so SVR.JS will fail to start new worker in case the previous ones hung up, but SVR.JS will at least start new worker in case if all of the workers had crashed):
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
while ! bun svr.js; do
|
||||||
|
echo "Restarting SVR.JS, because it crashed/failed to start..."
|
||||||
|
pkill -9 -f 'bun .*svr\.js'
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
It is also possible to run SVR.JS single-threaded on Bun 1.0, although reliability of SVR.JS may suffer. For that, you can create this startup script:
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
while ! bun svr.js --single-threaded; do
|
||||||
|
echo "Restarting SVR.JS, because it crashed/failed to start..."
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
**Here are screenshots of SVR.JS running on Bun 1.0:**
|
||||||
|
|
||||||
|
![Console of SVR.JS running on Bun 1.0](/images/svrjs-on-bun-console.png)
|
||||||
|
![Default server page served by SVR.JS running on Bun 1.0](/images/svrjs-on-bun-startpage.png)
|
||||||
|
![404 Not Found page generated by SVR.JS running on Bun 1.0](/images/svrjs-on-bun-404.png)
|
||||||
|
|
||||||
|
Maybe we will replace Node.JS with Bun on our own servers, when Bun begins to support clustering and maybe HTTP/2...
|
||||||
|
|
||||||
|
**UPDATE:** **SVR.JS 3.10.0**, **SVR.JS 3.4.35 LTS** and newer has limited the number of workers on Bun 1.0 with shimmed (not native) cluster module to one, because of above mentioned problems.
|
12
source/_posts/Exploring-The-Book-of-ZSOiE.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
title: Exploring The Book of ZSOiE...
|
||||||
|
date: 2023-07-06 13:59:00
|
||||||
|
---
|
||||||
|
|
||||||
|
![The Book of ZSOiE](/images/zsoiebook.png)
|
||||||
|
|
||||||
|
The Book of ZSOiE reflects what students at ZSOiE (or rather its CyberSkiller Challenge Poland participants) has done, and also history of SVR.JS 3.x release.
|
||||||
|
|
||||||
|
In this quote shown in the picture, one of three CyberSkiller Challenge Poland participants used SVR.JS (more specifically SVR.JS 3.0.0-beta4) along with Node.JS to solve some challenges. Two other participants has helped him.
|
||||||
|
|
||||||
|
You can visit ZSOiE article [here](http://zsoie.lubsko.pl/index.php/1121-cyberskiller-challenge-poland-edycja-iii-mamy-4-miejsce-w-polsce) (it's in Polish!). You can access The Book of ZSOiE by going to */zsoiebook.svr* in web server running SVR.JS. SVR.JS even hosts The Book of ZSOiE [here](https://svrjs.org/zsoiebook.svr)!
|
306
source/_posts/How-to-create-static-HTTP-server-in-Node-JS.md
Normal file
|
@ -0,0 +1,306 @@
|
||||||
|
---
|
||||||
|
title: How to create static HTTP server in Node.JS?
|
||||||
|
date: 2023-07-10 02:53:17
|
||||||
|
tags:
|
||||||
|
- http
|
||||||
|
- node.js
|
||||||
|
- javascript
|
||||||
|
- server
|
||||||
|
categories: Tips
|
||||||
|
---
|
||||||
|
|
||||||
|
Node.JS is a event driven server-side JavaScript runtime, that uses V8 JS Engine (same as Chromium) and executes code outside a web browser.
|
||||||
|
In this article we will cover basics of implementation of HTTP server in Node.JS and build a simple static HTTP server.
|
||||||
|
## Prerequisites
|
||||||
|
- Node.JS installed on your developement machine
|
||||||
|
## Built-in HTTP module
|
||||||
|
Node.JS has built-in `http` module, which allows Node.JS to communicate over HyperText Transfer Protocol (HTTP).
|
||||||
|
To include that module, we use `require()` method:
|
||||||
|
|
||||||
|
var http = require("http");
|
||||||
|
## "Hello, World" server
|
||||||
|
In this example, the server sends "Hello, World" response. This server is listening at port 8080:
|
||||||
|
{% codeblock server.js lang:javascript %}
|
||||||
|
//Hello World server
|
||||||
|
var http = require("http");
|
||||||
|
var port = 8080;
|
||||||
|
var server = http.createServer(function (req, res) {
|
||||||
|
res.writeHead(200, "OK", {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
});
|
||||||
|
res.write("Hello, World!");
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
server.listen(port, function() {
|
||||||
|
console.log("Started server at port " + port + ".");
|
||||||
|
});
|
||||||
|
{% endcodeblock %}
|
||||||
|
For creating HTTP server object we use `http.createServer()` method. Server will then listen at port 8080. This server writes header indicating, that we're sending plain text. Then it sends "Hello, World" message and ends the connection.
|
||||||
|
Save the code above in a file called *server.js* and start the server using `node server.js`.
|
||||||
|
|
||||||
|
If you visit *localhost:8080* in your web browser, the result will look like this:
|
||||||
|
![Hello World server](/images/hello-server.png)
|
||||||
|
## Serving files using Node.JS
|
||||||
|
For reading files, we're using `fs` module and `fs.readFile()` method.
|
||||||
|
The code will look like this:
|
||||||
|
{% codeblock server.js lang:javascript %}
|
||||||
|
//Serving index...
|
||||||
|
var http = require("http");
|
||||||
|
var fs = require("fs");
|
||||||
|
var port = 8080;
|
||||||
|
var server = http.createServer(function (req, res) {
|
||||||
|
fs.readFile("index.html", function(err, data) {
|
||||||
|
if(err) {
|
||||||
|
res.writeHead(500, "Internal Server Error", {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
});
|
||||||
|
res.end("500 Internal Server Error! Reason: " + err.message);
|
||||||
|
} else {
|
||||||
|
res.writeHead(200, "OK", {
|
||||||
|
"Content-Type": "text/html"
|
||||||
|
});
|
||||||
|
res.end(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
server.listen(port, function() {
|
||||||
|
console.log("Started server at port " + port + ".");
|
||||||
|
});
|
||||||
|
{% endcodeblock %}
|
||||||
|
This code will serve *index.html* file, and returns 500 error if there was a problem reading that file.
|
||||||
|
But what if that index file doesn't exist? We will then need to serve 404 error page:
|
||||||
|
{% codeblock server.js lang:javascript %}
|
||||||
|
//Serving 404...
|
||||||
|
var http = require("http");
|
||||||
|
var fs = require("fs");
|
||||||
|
var port = 8080;
|
||||||
|
var server = http.createServer(function (req, res) {
|
||||||
|
fs.readFile("index.html", function(err, data) {
|
||||||
|
if(err) {
|
||||||
|
if(err.code == "ENOENT") {
|
||||||
|
//ENOENT means "File doesn't exist"
|
||||||
|
res.writeHead(404, "Not Found", {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
});
|
||||||
|
res.end("404 Not Found");
|
||||||
|
} else {
|
||||||
|
res.writeHead(500, "Internal Server Error", {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
});
|
||||||
|
res.end("500 Internal Server Error! Reason: " + err.message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.writeHead(200, "OK", {
|
||||||
|
"Content-Type": "text/html"
|
||||||
|
});
|
||||||
|
res.end(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
server.listen(port, function() {
|
||||||
|
console.log("Started server at port " + port + ".");
|
||||||
|
});
|
||||||
|
{% endcodeblock %}
|
||||||
|
Note, that in static HTTPS servers, files are determined from resource URL.
|
||||||
|
{% codeblock server.js lang:javascript %}
|
||||||
|
//WARNING!!! PATH TRAVERSAL
|
||||||
|
var http = require("http");
|
||||||
|
var fs = require("fs");
|
||||||
|
var port = 8080;
|
||||||
|
var server = http.createServer(function (req, res) {
|
||||||
|
var filename = "." + req.url;
|
||||||
|
if(req.url == "/") filename = "./index.html";
|
||||||
|
fs.readFile(filename, function(err, data) {
|
||||||
|
if(err) {
|
||||||
|
if(err.code == "ENOENT") {
|
||||||
|
//ENOENT means "File doesn't exist"
|
||||||
|
res.writeHead(404, "Not Found", {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
});
|
||||||
|
res.end("404 Not Found");
|
||||||
|
} else {
|
||||||
|
res.writeHead(500, "Internal Server Error", {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
});
|
||||||
|
res.end("500 Internal Server Error! Reason: " + err.message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.writeHead(200, "OK", {
|
||||||
|
"Content-Type": "text/html"
|
||||||
|
});
|
||||||
|
res.end(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
server.listen(port, function() {
|
||||||
|
console.log("Started server at port " + port + ".");
|
||||||
|
});
|
||||||
|
{% endcodeblock %}
|
||||||
|
But we have introduced path traversal vulnerability! (being able to access file outside the web root) To mitigate that, we'll use a regular expression, that removes all dot-dot-slash sequences from file name:
|
||||||
|
{% codeblock server.js lang:javascript %}
|
||||||
|
//Path traversal mitigated
|
||||||
|
var http = require("http");
|
||||||
|
var fs = require("fs");
|
||||||
|
var port = 8080;
|
||||||
|
var server = http.createServer(function (req, res) {
|
||||||
|
var filename = "." + req.url;
|
||||||
|
if(req.url == "/") filename = "./index.html";
|
||||||
|
filename = filename.replace(/\\/g,"/").replace(/(?:\/|^)\.\.(?=(\/|$))/g,"$1").replace(/\/+/g,"/"); //Poor mans URL sanitizer
|
||||||
|
fs.readFile(filename, function(err, data) {
|
||||||
|
if(err) {
|
||||||
|
if(err.code == "ENOENT") {
|
||||||
|
//ENOENT means "File doesn't exist"
|
||||||
|
res.writeHead(404, "Not Found", {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
});
|
||||||
|
res.end("404 Not Found");
|
||||||
|
} else {
|
||||||
|
res.writeHead(500, "Internal Server Error", {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
});
|
||||||
|
res.end("500 Internal Server Error! Reason: " + err.message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.writeHead(200, "OK", {
|
||||||
|
"Content-Type": "text/html"
|
||||||
|
});
|
||||||
|
res.end(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
server.listen(port, function() {
|
||||||
|
console.log("Started server at port " + port + ".");
|
||||||
|
});
|
||||||
|
{% endcodeblock %}
|
||||||
|
That might work fine for HTML files, but if you try other files, there will be content type mismatch. To get MIME types, we use `mime-types` package, that you can install using `npm install mime-types`. We will then use that module, along with `path` module to get file extension. The code will look like this:
|
||||||
|
{% codeblock server.js lang:javascript %}
|
||||||
|
//Adding MIME type support...
|
||||||
|
var http = require("http");
|
||||||
|
var fs = require("fs");
|
||||||
|
var mime = require("mime-types");
|
||||||
|
var path = require("path");
|
||||||
|
var port = 8080;
|
||||||
|
var server = http.createServer(function (req, res) {
|
||||||
|
var filename = "." + req.url;
|
||||||
|
if(req.url == "/") filename = "./index.html";
|
||||||
|
filename = filename.replace(/\\/g,"/").replace(/(?:\/|^)\.\.(?=(\/|$))/g,"$1").replace(/\/+/g,"/"); //Poor mans URL sanitizer
|
||||||
|
var ext = path.extname(filename).substr(1); //path.extname gives "." character, so we're using substr(1) method.
|
||||||
|
fs.readFile(filename, function(err, data) {
|
||||||
|
if(err) {
|
||||||
|
if(err.code == "ENOENT") {
|
||||||
|
//ENOENT means "File doesn't exist"
|
||||||
|
res.writeHead(404, "Not Found", {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
});
|
||||||
|
res.end("404 Not Found");
|
||||||
|
} else {
|
||||||
|
res.writeHead(500, "Internal Server Error", {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
});
|
||||||
|
res.end("500 Internal Server Error! Reason: " + err.message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.writeHead(200, "OK", {
|
||||||
|
"Content-Type": mime.lookup(ext) || undefined
|
||||||
|
});
|
||||||
|
res.end(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
server.listen(port, function() {
|
||||||
|
console.log("Started server at port " + port + ".");
|
||||||
|
});
|
||||||
|
{% endcodeblock %}
|
||||||
|
But with query strings, it will fail. To prevent that, we'll be using WHATWG URL parser (`url.parse` is now deprecated):
|
||||||
|
{% codeblock server.js lang:javascript %}
|
||||||
|
//And URL query...
|
||||||
|
var http = require("http");
|
||||||
|
var fs = require("fs");
|
||||||
|
var mime = require("mime-types");
|
||||||
|
var path = require("path");
|
||||||
|
var port = 8080;
|
||||||
|
var server = http.createServer(function (req, res) {
|
||||||
|
var urlObject = new URL(req.url, "http://localhost");
|
||||||
|
var filename = "." + urlObject.pathname;
|
||||||
|
if(req.url == "/") filename = "./index.html";
|
||||||
|
filename = filename.replace(/\\/g,"/").replace(/(?:\/|^)\.\.(?=(\/|$))/g,"$1").replace(/\/+/g,"/"); //Poor mans URL sanitizer
|
||||||
|
var ext = path.extname(filename).substr(1); //path.extname gives "." character, so we're using substr(1) method.
|
||||||
|
fs.readFile(filename, function(err, data) {
|
||||||
|
if(err) {
|
||||||
|
if(err.code == "ENOENT") {
|
||||||
|
//ENOENT means "File doesn't exist"
|
||||||
|
res.writeHead(404, "Not Found", {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
});
|
||||||
|
res.end("404 Not Found");
|
||||||
|
} else {
|
||||||
|
res.writeHead(500, "Internal Server Error", {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
});
|
||||||
|
res.end("500 Internal Server Error! Reason: " + err.message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.writeHead(200, "OK", {
|
||||||
|
"Content-Type": mime.lookup(ext) || undefined
|
||||||
|
});
|
||||||
|
res.end(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
server.listen(port, function() {
|
||||||
|
console.log("Started server at port " + port + ".");
|
||||||
|
});
|
||||||
|
{% endcodeblock %}
|
||||||
|
It's nearly finished! But encoded URLs will not work. To fix that, we will use `decodeURIComponent()` method:
|
||||||
|
{% codeblock server.js lang:javascript %}
|
||||||
|
//And URL decoding...
|
||||||
|
var http = require("http");
|
||||||
|
var fs = require("fs");
|
||||||
|
var mime = require("mime-types");
|
||||||
|
var path = require("path");
|
||||||
|
var port = 8080;
|
||||||
|
var server = http.createServer(function (req, res) {
|
||||||
|
var urlObject = new URL(req.url, "http://localhost");
|
||||||
|
var filename = "";
|
||||||
|
try {
|
||||||
|
filename = "." + decodeURIComponent(urlObject.pathname);
|
||||||
|
} catch(ex) {
|
||||||
|
//Malformed URI means bad request.
|
||||||
|
res.writeHead(400, "Bad Request", {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
});
|
||||||
|
res.end("400 Bad Request");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(req.url == "/") filename = "./index.html";
|
||||||
|
filename = filename.replace(/\\/g,"/").replace(/(?:\/|^)\.\.(?=(\/|$))/g,"$1").replace(/\/+/g,"/"); //Poor mans URL sanitizer
|
||||||
|
var ext = path.extname(filename).substr(1); //path.extname gives "." character, so we're using substr(1) method.
|
||||||
|
fs.readFile(filename, function(err, data) {
|
||||||
|
if(err) {
|
||||||
|
if(err.code == "ENOENT") {
|
||||||
|
//ENOENT means "File doesn't exist"
|
||||||
|
res.writeHead(404, "Not Found", {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
});
|
||||||
|
res.end("404 Not Found");
|
||||||
|
} else {
|
||||||
|
res.writeHead(500, "Internal Server Error", {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
});
|
||||||
|
res.end("500 Internal Server Error! Reason: " + err.message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.writeHead(200, "OK", {
|
||||||
|
"Content-Type": mime.lookup(ext) || undefined
|
||||||
|
});
|
||||||
|
res.end(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
server.listen(port, function() {
|
||||||
|
console.log("Started server at port " + port + ".");
|
||||||
|
});
|
||||||
|
{% endcodeblock %}
|
||||||
|
We have now very simple HTTP static server, serving at *localhost:8080*.
|
||||||
|
|
||||||
|
**Wait... Did we forget about [SVR.JS](https://svrjs.duckdns.org)?** SVR.JS is a web server running on Node.JS, that supports not only static file serving, but also directory listings, path rewriting, complete URL sanitation, HTTPS, HTTP/2.0, expandability via mods and server-side JavaScript, and it's configurable.
|
375
source/_posts/How-to-run-a-Git-server-on-GNU-Linux.md
Normal file
|
@ -0,0 +1,375 @@
|
||||||
|
---
|
||||||
|
title: How to run a Git server on GNU/Linux?
|
||||||
|
tags:
|
||||||
|
- git
|
||||||
|
- vcs
|
||||||
|
- development
|
||||||
|
- server
|
||||||
|
- web
|
||||||
|
- webmaster
|
||||||
|
- gitweb
|
||||||
|
categories: Tips
|
||||||
|
thumbnail: /images/covers/How-to-run-a-Git-server-on-GNU-Linux.png
|
||||||
|
date: 2024-03-11 21:33:10
|
||||||
|
---
|
||||||
|
|
||||||
|
If you want to set up your own version control for a project, but prefer not to host it on a Git hosting service (like GitHub), you can run your own Git server to store your code and act as a central repository for all of collaborators.
|
||||||
|
|
||||||
|
## Why host your own Git server?
|
||||||
|
|
||||||
|
You may run your own Git server, if you don't want to store your code on someone else's servers. You may need to have full control of your version control infrastructure.
|
||||||
|
|
||||||
|
Also, if you're using a Git hosting service, there are some restrictions that may not be ideal. For example, GitHub doesn't allow files above 100 MB, which may be a critical problem for projects with large files. Running your own Git server may allow these larger files.
|
||||||
|
|
||||||
|
## Initializing Git repositories
|
||||||
|
|
||||||
|
First off, you need to install Git. To do that, you can run `sudo apt install git` on Debian-based systems (Debian, Devuan, Ubuntu, Linux Mint), `sudo dnf install git` or `sudo yum install git` on RHEL-based systems (Fedora, CentOS, RHEL), `sudo zypper install git-core` on SUSE-based systems (SUSE Linux Enterprise, openSUSE) or `sudo pacman -S git` on Arch-based systems (Arch Linux, Manjaro).
|
||||||
|
|
||||||
|
Next, add repositories to a Git root directory. In our case we're using `/var/lib/git` directory. For our scenario, we're just making a clone of SVR.JS Git repository:
|
||||||
|
```sh
|
||||||
|
sudo mkdir /var/lib/git
|
||||||
|
cd /var/lib/git
|
||||||
|
sudo git clone --bare https://git.svrjs.org/svrjs.git #You can also clone any other Git repository or just create a new blank repository using `git init --bare myrepo.git`
|
||||||
|
```
|
||||||
|
|
||||||
|
You may need to add `git-daemon-export-ok` to mark Git repositories as safe for export:
|
||||||
|
```sh
|
||||||
|
sudo find /var/lib/git -mindepth 1 -maxdepth 1 -exec touch {}/git-daemon-export-ok \;
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to learn more about Git commands, you can check out [our post about Git commands.](/2024/03/05/Mastering-the-Basics-of-Git-The-Ultimate-Guide-to-Git-Commands-for-Software-Developers/)
|
||||||
|
|
||||||
|
Then create a new Git user (we're using `/var/lib/gituser` directory as a user directory):
|
||||||
|
```sh
|
||||||
|
sudo useradd -s /usr/bin/git-shell -d /var/lib/gituser -m -r git
|
||||||
|
```
|
||||||
|
|
||||||
|
And change the permissions for the Git root directory and Git user directory:
|
||||||
|
```sh
|
||||||
|
sudo chown -hR git:git /var/lib/git
|
||||||
|
sudo chmod -R ug+rw /var/lib/git
|
||||||
|
sudo chown -hR git:git /var/lib/gituser
|
||||||
|
sudo chmod -R g-w /var/lib/gituser
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, change the default file creation mask by adding or replacing the `UMASK` line in the `/etc/login.defs` file:
|
||||||
|
```
|
||||||
|
UMASK 002
|
||||||
|
```
|
||||||
|
|
||||||
|
![The initial setup of a Git server](/images/git-server-initial-setup.png)
|
||||||
|
|
||||||
|
Now you are ready to serve Git to the public through one of three protocols:
|
||||||
|
* Git protocol
|
||||||
|
* SSH
|
||||||
|
* HTTP(S)
|
||||||
|
|
||||||
|
## Hosting through Git protocol
|
||||||
|
|
||||||
|
Git protocol is a protocol used by Git daemon that comes packaged with Git; it listens on a dedicated port (9418) that provides a service similar to one using SSH protocol, but it has absolutely no authenttication or cryptography. That means no uploading files by default. But Git protocol is very efficient.
|
||||||
|
|
||||||
|
To set up Git daemon, first install the Git daemon. You can use `sudo apt install git-daemon-sysvinit` (SystemV init or systemd) or `sudo apt install git-daemon-run` (runit) on Debian-based systems (Debian, Devuan, Ubuntu, Linux Mint), `sudo dnf install git-daemon` or `sudo yum install git-daemon` on RHEL-based systems (Fedora, CentOS, RHEL), `sudo zypper install git-daemon` on SUSE-based systems (SUSE Linux Enterprise, openSUSE). Users of Arch-based systems (Arch Linux, Manjaro) don't need to install an additional package.
|
||||||
|
|
||||||
|
Then change the `/etc/default/git-daemon` file contents to:
|
||||||
|
```sh
|
||||||
|
GIT_DAEMON_ENABLE=true
|
||||||
|
GIT_DAEMON_USER=git
|
||||||
|
GIT_DAEMON_BASE_PATH=/var/lib/git
|
||||||
|
GIT_DAEMON_DIRECTORY=/var/lib/git
|
||||||
|
GIT_DAEMON_OPTIONS=""
|
||||||
|
```
|
||||||
|
|
||||||
|
You may change `GIT_DAEMON_OPTIONS` to enable `git push`, however **anybody can do `git push` and do unauthorized changes**:
|
||||||
|
```sh
|
||||||
|
#WARNING!!! INSECURE!!!
|
||||||
|
GIT_DAEMON_OPTIONS="--enable=receive-pack"
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, restart the daemon with `sudo systemctl restart git-daemon` or `sudo /etc/init.d/git-daemon restart`.
|
||||||
|
|
||||||
|
If you're using a `ufw` firewall, you can use:
|
||||||
|
```sh
|
||||||
|
sudo ufw allow git
|
||||||
|
```
|
||||||
|
|
||||||
|
![The Git protocol setup of a Git server](/images/git-server-git-protocol.png)
|
||||||
|
|
||||||
|
You can test the server by running this command on a client (assuming that the server has `10.0.0.2` address):
|
||||||
|
```sh
|
||||||
|
git clone git://10.0.0.2/svrjs.git
|
||||||
|
#Replace `svrjs.git` with repository name and `10.0.0.2` with your server address.
|
||||||
|
```
|
||||||
|
|
||||||
|
![Test of the Git protocol setup](/images/git-server-git-protocol-test.png)
|
||||||
|
|
||||||
|
Now you have set up a Git server serving through Git protocol!
|
||||||
|
|
||||||
|
## Hosting through SSH
|
||||||
|
|
||||||
|
SSH protocol is a more common protocol for Git servers. This is because SSH access is already set up in many server configurations, and if it isn't, it's easy to set it up. SSH is also authenticated, and it's generally easy to set up and use due to it being ubiquitous.
|
||||||
|
|
||||||
|
If you don't have SSH server, you can install it (in this case we're using OpenSSH). To do that, you can run `sudo apt install openssh-server` on Debian-based systems (Debian, Devuan, Ubuntu, Linux Mint), `sudo dnf install openssh-server` or `sudo yum install openssh-server` on RHEL-based systems (Fedora, CentOS, RHEL), `sudo zypper install openssh` on SUSE-based systems (SUSE Linux Enterprise, openSUSE) or `sudo pacman -S openssh` on Arch-based systems (Arch Linux, Manjaro).
|
||||||
|
|
||||||
|
If you're using a `ufw` firewall, you can use:
|
||||||
|
```sh
|
||||||
|
sudo ufw allow ssh
|
||||||
|
```
|
||||||
|
|
||||||
|
You can either use password or public key for the authentication.
|
||||||
|
|
||||||
|
If you want to use password, you can set it using this command (run it on server):
|
||||||
|
```sh
|
||||||
|
sudo passwd git
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to use public keys, first set up a password for the user. Then you can temporary switch the shell of `git` user to `sh` (run it on server):
|
||||||
|
```sh
|
||||||
|
sudo usermod -s /bin/sh git
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can generate the SSH keys and send it to the server (assuming that `10.0.0.2` is a server address; run it on client):
|
||||||
|
```sh
|
||||||
|
ssh-keygen -o
|
||||||
|
ssh-copy-id git@10.0.0.2
|
||||||
|
#Replace `10.0.0.2` with your server address.
|
||||||
|
```
|
||||||
|
|
||||||
|
![Preparing the SSH keys...](/images/git-server-ssh-protocol-keyprep.png)
|
||||||
|
|
||||||
|
Finally you can revert the shell change (run it on server):
|
||||||
|
```sh
|
||||||
|
sudo usermod -s /usr/bin/git-shell git
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to disable password authentication, you can add this to your `/etc/ssh/sshd_config` file:
|
||||||
|
```
|
||||||
|
Match User git
|
||||||
|
PasswordAuthentication No
|
||||||
|
```
|
||||||
|
And restart the server using either `sudo systemctl restart ssh` or `sudo /etc/init.d/ssh restart`.
|
||||||
|
|
||||||
|
You may also [create a `chroot` environment for a Git server.](https://brnrd.eu/freebsd/2023-08-06/your-private-git-server-in-a-chroot.html)
|
||||||
|
|
||||||
|
![The SSH setup of a Git server](/images/git-server-ssh-protocol.png)
|
||||||
|
|
||||||
|
You can test the server by running this command on a client (assuming that the server has `10.0.0.2` address):
|
||||||
|
```sh
|
||||||
|
git clone ssh://git@10.0.0.2/var/lib/git/svrjs.git
|
||||||
|
#Replace `svrjs.git` with repository name and `10.0.0.2` with your server address.
|
||||||
|
#When using chroot, remove the `/var/lib/git` part.
|
||||||
|
```
|
||||||
|
|
||||||
|
![Test of the SSH setup](/images/git-server-ssh-protocol-test.png)
|
||||||
|
|
||||||
|
Now you have set up a Git server serving through SSH protocol!
|
||||||
|
|
||||||
|
## Hosting through HTTP (along with GitWeb)
|
||||||
|
|
||||||
|
HTTP protocol is also a very common protocol for Git servers. You can use HTTP authentication to protect Git server from unauthorized `git push` operations (if there is no HTTP authentication, `git push` operations are disabled by default). You can also set up to anonymously serve Git repositories for cloning.
|
||||||
|
|
||||||
|
For Git hosting services (like GitHub), the cloning URL is the same as the URL you use to view the repository through the web browser. You will also set up this configuration.
|
||||||
|
|
||||||
|
For this purpose, we're using GitWeb as a Git repository viewer and `git-http-backend` for the Git cloning.
|
||||||
|
|
||||||
|
That means that you can use any web server software, that supports CGI (Common Gateway Interface), since both GitWeb and `git-http-backend` use CGI.
|
||||||
|
|
||||||
|
To set up GitWeb, first install the GitWeb. You can use `sudo apt install gitweb` on Debian-based systems (Debian, Devuan, Ubuntu, Linux Mint), `sudo dnf install gitweb` or `sudo yum install gitweb` on RHEL-based systems (Fedora, CentOS, RHEL), `sudo zypper install git-web` on SUSE-based systems (SUSE Linux Enterprise, openSUSE). Users of Arch-based systems (Arch Linux, Manjaro) may need to install Perl CGI module (a dependency of GitWeb) to use GitWeb using `sudo pacman -S perl-cgi`.
|
||||||
|
|
||||||
|
For the web server, we're using SVR.JS web server with RedBrick mod. First [download SVR.JS web server](https://svrjs.org) and [SVR.JS installer for GNU/Linux](https://downloads.svrjs.org/installer). You can use these commands to install SVR.JS:
|
||||||
|
```sh
|
||||||
|
cd ~
|
||||||
|
wget https://downloads.svrjs.org/svr.js.3.14.5.zip #Replace "3.14.5" with the latest SVR.JS version you can get from the SVR.JS website
|
||||||
|
wget https://downloads.svrjs.org/installer/https://downloads.svrjs.org/installer/svr.js.installer.linux.20240219.zip #You can replace "svr.js.installer.linux.20240219.zip" with latest installer archive you can find on the "installer" directory
|
||||||
|
mkdir installer
|
||||||
|
cd installer
|
||||||
|
unzip ../svr.js.installer.linux.20240219.zip
|
||||||
|
cp ../svr.js.3.14.5.zip svrjs.zip
|
||||||
|
sudo bash installer.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
After installing SVR.JS, start the server using `sudo systemctl start svrjs` or `sudo /etc/init.d/svrjs start`.
|
||||||
|
|
||||||
|
![SVR.JS default page](/images/git-server-http-protocol-welcome.png)
|
||||||
|
|
||||||
|
Later, run these commands to avoid permission conflict with SSH- and Git protocol-based Git services (for web servers other than SVR.JS installed with SVR.JS installer, replace `svrjs` with `www-data`):
|
||||||
|
```sh
|
||||||
|
sudo usermod -aG svrjs git
|
||||||
|
sudo usermod -aG git svrjs
|
||||||
|
```
|
||||||
|
|
||||||
|
If you're using a `ufw` firewall, you can use:
|
||||||
|
```sh
|
||||||
|
sudo ufw allow http
|
||||||
|
```
|
||||||
|
|
||||||
|
For other web servers, you can read [the git-http-backend documentation](https://git-scm.com/docs/git-http-backend) and [the GitWeb documentation](https://git-scm.com/docs/gitweb).
|
||||||
|
|
||||||
|
Now after installing SVR.JS you can delete everything under the `/var/www/svrjs` directory (`sudo rm -rf /var/www/svrjs/*`) and restart the server using `sudo systemctl restart svrjs` or `sudo /etc/init.d/svrjs restart`.
|
||||||
|
|
||||||
|
After the deletion, the only thing that's left is the empty directory listing, when you visit `http://10.0.0.2/` (assuming that the server address is `10.0.0.2`):
|
||||||
|
|
||||||
|
![Empty directory listing on the HTTP server](/images/git-server-http-protocol-empty-dirlisting.png)
|
||||||
|
|
||||||
|
Since SVR.JS itself doesn't have CGI support, you need to install the RedBrick mod. RedBrick basically adds CGI support for the SVR.JS web server. To do that, you can go to the [SVR.JS mods page](https://svrjs.org/mods) and run the commands:
|
||||||
|
```sh
|
||||||
|
cd /usr/lib/svrjs/mods
|
||||||
|
sudo wget https://downloads.svrjs.org/mods/redbrick.cgi.2.6.0.tar.gz #Replace "2.6.0" with the latest version of the RedBrick found on the SVR.JS mods page.
|
||||||
|
```
|
||||||
|
After installing the mod, restart the server using `sudo systemctl restart svrjs` or `sudo /etc/init.d/svrjs restart`.
|
||||||
|
|
||||||
|
Then add files under the `cgi-bin` directory on the web root:
|
||||||
|
```sh
|
||||||
|
cd /var/www/svrjs
|
||||||
|
sudo ln -s /usr/lib/cgi-bin cgi-bin
|
||||||
|
sudo ln -s /usr/share/gitweb/static .
|
||||||
|
cd cgi-bin
|
||||||
|
sudo ln -s /usr/share/gitweb/gitweb.cgi .
|
||||||
|
sudo ln -s /usr/share/gitweb/static .
|
||||||
|
sudo ln -s /usr/lib/git-core/git-http-backend git.cgi
|
||||||
|
```
|
||||||
|
|
||||||
|
After adding the files, stop the SVR.JS service using `sudo systemctl stop svrjs` or `sudo /etc/init.d/svrjs stop` and change the SVR.JS configuration (in `/etc/svrjs-config.json` or `/usr/lib/svrjs/config.json`) like this:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"nonStandardCodes": [
|
||||||
|
{
|
||||||
|
"scode": 301,
|
||||||
|
"regex": "/^\\/git\\/(.*)/",
|
||||||
|
"location": "/$1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scode": 301,
|
||||||
|
"regex": "/^\\/git($|[?#].*)/",
|
||||||
|
"location": "/$1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scode": 401,
|
||||||
|
"realm": "Git Access",
|
||||||
|
"regex": "/^\\/cgi-bin\\/git\\.cgi(?:(\\/.*)?\\/git-receive-pack(?:$|[?#])|(?:\\/[^?]*)?\\?(?:[^#]*[;&?]|)service=git-receive-pack(?:$|[;&?#]))/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"allowStatus": true,
|
||||||
|
"enableCompression": true,
|
||||||
|
"customHeaders": {},
|
||||||
|
"enableLogging": true,
|
||||||
|
"enableDirectoryListing": false,
|
||||||
|
"enableDirectoryListingWithDefaultHead": false,
|
||||||
|
"stackHidden": true,
|
||||||
|
"enableRemoteLogBrowsing": false,
|
||||||
|
"exposeServerVersion": false,
|
||||||
|
"disableServerSideScriptExpose": true,
|
||||||
|
"rewriteMap": [
|
||||||
|
{
|
||||||
|
"definingRegex": "/^\\/(?!git(?:$|[?\\/#])|svrjsstatus\\.svr(?:$|[?#]))(?![^\\/]+\\/(?:branches|hooks|info|logs|objects|refs|config|description|HEAD|git-upload-pack|git-receive-pack)(?:$|[?\\/#]))/",
|
||||||
|
"isNotFile": true,
|
||||||
|
"replacements": [
|
||||||
|
{
|
||||||
|
"regex": "/^\\//",
|
||||||
|
"replacement": "/cgi-bin/gitweb.cgi/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"definingRegex": "/^\\/(?!git(?:$|[?\\/#])|svrjsstatus\\.svr(?:$|[?#]))/",
|
||||||
|
"isNotFile": true,
|
||||||
|
"replacements": [
|
||||||
|
{
|
||||||
|
"regex": "/^\\//",
|
||||||
|
"replacement": "/cgi-bin/git.cgi/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dontCompress": [
|
||||||
|
"/.*\\.ipxe$/",
|
||||||
|
"/.*\\.img$/",
|
||||||
|
"/.*\\.iso$/",
|
||||||
|
"/.*\\.png$/",
|
||||||
|
"/.*\\.woff$/"
|
||||||
|
],
|
||||||
|
"enableIPSpoofing": false,
|
||||||
|
"exposeModsInErrorPages": false,
|
||||||
|
"enableETag": true,
|
||||||
|
"disableUnusedWorkerTermination": false,
|
||||||
|
"rewriteDirtyURLs": false,
|
||||||
|
"wwwroot": "/var/www/svrjs",
|
||||||
|
"disableTrailingSlashRedirects": false,
|
||||||
|
"environmentVariables": {
|
||||||
|
"GIT_PROJECT_ROOT": "/var/lib/git"
|
||||||
|
},
|
||||||
|
"customHeaders": {
|
||||||
|
"Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
|
||||||
|
"X-Frame-Options": "sameorigin",
|
||||||
|
"X-Content-Type-Options": "nosniff",
|
||||||
|
"Content-Security-Policy": "default-src 'self'; object-src 'none'; script-src 'self' 'sha256-dacEZQWGxky95ybZadcNI26RDghVLeVdbdRC/Q3spJQ='; img-src 'self' www.gravatar.com data:"
|
||||||
|
},
|
||||||
|
"allowDoubleSlashes": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Change the `enableIPSpoofing` property to `true`, if your server is behind a reverse proxy. For more configuration options, see the [SVR.JS documentation](https://svrjs.org/docs).
|
||||||
|
|
||||||
|
After the configuration changes, you can add the `git` user to the HTTP server:
|
||||||
|
```sh
|
||||||
|
sudo svrpasswd -a git
|
||||||
|
```
|
||||||
|
|
||||||
|
Later, modify the `/etc/gitweb.conf` file (GitWeb configuarion) like this:
|
||||||
|
```perl
|
||||||
|
$projectroot = "/var/lib/git";
|
||||||
|
$git_temp = "/tmp";
|
||||||
|
@stylesheets = ("/static/gitweb.css");
|
||||||
|
$javascript = "/static/gitweb.js";
|
||||||
|
$logo = "/static/git-logo.js";
|
||||||
|
$favicon = "/static/git-favicon.js";
|
||||||
|
$feature{'pathinfo'}{'default'} = [1];
|
||||||
|
@diff_opts = ();
|
||||||
|
```
|
||||||
|
For more configuration options, see the [`gitweb.conf` documentation](https://git-scm.com/docs/gitweb.conf.html).
|
||||||
|
|
||||||
|
After those modifications, start the server using `sudo systemctl start svrjs` or `sudo /etc/init.d/svrjs start`.
|
||||||
|
|
||||||
|
You have now both Git server and GitWeb running!
|
||||||
|
|
||||||
|
![The HTTP setup of a Git server](/images/git-server-http-protocol.png)
|
||||||
|
|
||||||
|
You can test the server by running this command on a client (assuming that the server has `10.0.0.2` address):
|
||||||
|
```sh
|
||||||
|
git clone http://10.0.0.2/svrjs.git
|
||||||
|
#Replace `svrjs.git` with repository name and `10.0.0.2` with your server address.
|
||||||
|
```
|
||||||
|
|
||||||
|
![Test of the HTTP setup](/images/git-server-http-protocol-test.png)
|
||||||
|
|
||||||
|
You can also test out GitWeb by visiting `http://10.0.0.2/` (assuming that the server address is `10.0.0.2`):
|
||||||
|
|
||||||
|
![The GitWeb view of the `svrjs.git` repository](/images/git-server-http-protocol-gitweb-test.png)
|
||||||
|
|
||||||
|
You may notice this description: _Unnamed repository; edit this file 'description' to name the repository._ In this case you can change it by editing the `description` file in the Git repository directory.
|
||||||
|
|
||||||
|
## Adding HTTP encryption
|
||||||
|
|
||||||
|
You have probably set up service per instructions above, but there may be one problem: there is no encryption! You may need encryption to ensure that no one is eavesdropping for your Git data and credentials you use to push the repositories.
|
||||||
|
|
||||||
|
To do that in SVR.JS, first obtain a TLS certificate from a certificate authority ([you can obtain Let's Encrypt certificates for free using `certbot`](https://certbot.eff.org/)).
|
||||||
|
|
||||||
|
After obtaining a TLS certificate, copy the certificate and the private key, so that it is accessible in those paths:
|
||||||
|
* `/usr/lib/svrjs/cert/cert.crt` - TLS certificate
|
||||||
|
* `/usr/lib/svrjs/cert/key.key` - private key
|
||||||
|
|
||||||
|
After copying the files, stop the SVR.JS service using `sudo systemctl stop svrjs` or `sudo /etc/init.d/svrjs stop` and append to the SVR.JS configuration (in `/etc/svrjs-config.json` or `/usr/lib/svrjs/config.json`) like this:
|
||||||
|
```json
|
||||||
|
...
|
||||||
|
"allowDoubleSlashes": false,
|
||||||
|
"secure": true,
|
||||||
|
"cert": "cert/cert.crt",
|
||||||
|
"key": "cert/key.key"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
After those modifications, start the server using `sudo systemctl start svrjs` or `sudo /etc/init.d/svrjs start`.
|
||||||
|
|
||||||
|
If you're using a `ufw` firewall, you can use:
|
||||||
|
```sh
|
||||||
|
sudo ufw allow https
|
||||||
|
```
|
||||||
|
|
||||||
|
You have now added encryption to the HTTP server!
|
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
title: IMPORTANT! Future versions of SVR.JS may drop support for Node.JS 8.x and 9.x
|
||||||
|
date: 2023-07-09 19:51:53
|
||||||
|
tags:
|
||||||
|
- node.js
|
||||||
|
categories: Notices
|
||||||
|
---
|
||||||
|
|
||||||
|
**IMPORTANT! Future versions of SVR.JS may drop support for Node.JS 8.x and 9.x**
|
||||||
|
|
||||||
|
Because Node.JS 8.x and 9.x are long EOL, we have decided, that future versions of SVR.JS may drop support for these versions.
|
||||||
|
|
||||||
|
If you want to still have up to date SVR.JS, you will need to get Node.JS 10.0.0 or newer.
|
||||||
|
|
||||||
|
Older versions of Node.JS also have various security vulnerabilities. Newer versions should be more secure.
|
|
@ -0,0 +1,19 @@
|
||||||
|
---
|
||||||
|
title: IMPORTANT! Update Node.JS to 18.19.1, 20.11.1, 21.6.2 or newer!
|
||||||
|
tags:
|
||||||
|
- cybersecurity
|
||||||
|
- node.js
|
||||||
|
category: Notices
|
||||||
|
thumbnail: /images/covers/IMPORTANT-Update-Node-JS-to-18-19-1-20-11-1-21-6-2-or-newer.png
|
||||||
|
date: 2024-03-13 03:18:40
|
||||||
|
---
|
||||||
|
|
||||||
|
**IMPORTANT! Update Node.JS to 18.19.1, 20.11.1, 21.6.2 or newer!**
|
||||||
|
|
||||||
|
Older versions of Node.JS had a [CVE-2024-22019 vulnerability](https://nodejs.org/en/blog/vulnerability/february-2024-security-releases#reading-unprocessed-http-request-with-unbounded-chunk-extension-allows-dos-attacks-cve-2024-22019---high), which involves sending specially constructed HTTP request with chunked encoding, which leads to resource exhaustion and denial of service (DoS).
|
||||||
|
|
||||||
|
The original vulnerability description:
|
||||||
|
|
||||||
|
_A vulnerability in Node.js HTTP servers allows an attacker to send a specially crafted HTTP request with chunked encoding, leading to resource exhaustion and denial of service (DoS). The server reads an unbounded number of bytes from a single connection, exploiting the lack of limitations on chunk extension bytes. The issue can cause CPU and network bandwidth exhaustion, bypassing standard safeguards like timeouts and body size limits._
|
||||||
|
|
||||||
|
Future SVR.JS versions will warn you about this vulnerability in server logs, if you're running affected versions of Node.JS.
|
47
source/_posts/JSGI-SVR-JS-has-implemented-it.md
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
---
|
||||||
|
title: JSGI? SVR.JS has implemented it!
|
||||||
|
date: 2023-08-11 19:56:08
|
||||||
|
tags:
|
||||||
|
- jsgi
|
||||||
|
- javascript
|
||||||
|
categories:
|
||||||
|
- [News]
|
||||||
|
- [Tips]
|
||||||
|
---
|
||||||
|
**We have recently added support for PHP-CGI and SCGI. We have implemented JSGI in SVR.JS through new [YellowSquare](https://svrjs.org/dl/mods) mod.** We have more specifically implemented JSGI Level 0/A Draft 2 Proposal (aka JSGI 0.3).
|
||||||
|
JSGI (JavaScript Gateway Interface) is an interface between JavaScript web applications and web servers. It is inspired by Rack by Ruby and WSGI by Python. JSGI is included in and further developed by the CommonJS project.
|
||||||
|
Since SVR.JS is written in JavaScript, we could easily implement JSGI.
|
||||||
|
This blog post will instruct you, how to set up JSGI web applications on SVR.JS.
|
||||||
|
## Setting up JSGI
|
||||||
|
First of all, [download](https://svrjs.org/) and install SVR.JS web server. You can also use `create-svrjs-server` tool or [SVR.JS installer](https://svrjs.org/dl/installer) to install SVR.JS.
|
||||||
|
|
||||||
|
After setting up SVR.JS, download and install [YellowSquare](https://svrjs.org/dl/mods) to _mods_ directory.
|
||||||
|
|
||||||
|
JSGI web applications in YellowSquare have *.jsgi* or *.jsgi.js* extensions and reside in *jsgi-bin* directory in the web root.
|
||||||
|
|
||||||
|
Create an example JSGI web application in *<webroot>/jsgi-bin/hello.jsgi* with those contents:
|
||||||
|
```js
|
||||||
|
var util = require("util");
|
||||||
|
|
||||||
|
exports.app = function(request) {
|
||||||
|
var requestObjectJson = util.inspect(request);
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "text/html"
|
||||||
|
},
|
||||||
|
body: [
|
||||||
|
"<!DOCTYPE html><html><head><title>Hello World!</title></head><body><h1>Hello World!</h1><p>JSGI request object:</p><code>",
|
||||||
|
requestObjectJson.replace(/&/g,"&").replace(/ /g," ").replace(/>/g,">").replace(/</g,"<").replace(/[\r\n]+/g,"<br/>"),
|
||||||
|
"</code></body></html>"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
After creating JSGI application, run SVR.JS using `node svr.js` or `bun run svr.js` and visit JSGI page (for example http://localhost/jsgi-bin/hello.jsgi). You will then see this page:
|
||||||
|
![JSGI "Hello World"](/images/jsgi-hello-world.png)
|
||||||
|
|
||||||
|
**We have set up JSGI!** You need to note that every change in JSGI application will require restart of SVR.JS in order to be applied. Also, when JSGI error occurs, it may be printed into server log, crash the server, or invoke 500 error like one below:
|
||||||
|
![JSGI 500 Internal Server Error](/images/jsgi-error.png)
|
|
@ -0,0 +1,370 @@
|
||||||
|
---
|
||||||
|
title: >-
|
||||||
|
Mastering the Basics of Git: The Ultimate Guide to Git Commands for Software
|
||||||
|
Developers
|
||||||
|
tags:
|
||||||
|
- git
|
||||||
|
- development
|
||||||
|
- vcs
|
||||||
|
category: Tips
|
||||||
|
thumbnail: >-
|
||||||
|
/images/covers/Mastering-the-Basics-of-Git-The-Ultimate-Guide-to-Git-Commands-for-Software-Developers.png
|
||||||
|
date: 2024-03-05 05:13:30
|
||||||
|
---
|
||||||
|
|
||||||
|
Git is a popular distributed version control system (VCS), often used for collaborative software development. It is created in 2005 by Linus Torvalds for use in developing his Linux kernel and it is maintained by Junio Hamano since then.
|
||||||
|
|
||||||
|
As of 2022, nearly 95% of software developers use Git as their primary version control system. There are many popular Git repository hosting services, including GitHub, GitLab, Bitbucket and Sourceforge.
|
||||||
|
|
||||||
|
This post will guide you through `git` commands.
|
||||||
|
|
||||||
|
## Initializing a repository with `git init`
|
||||||
|
|
||||||
|
If you want to initalize an empty Git repository, you can use `git init` command like this:
|
||||||
|
```sh
|
||||||
|
git init <repository_name>
|
||||||
|
```
|
||||||
|
This command creates an empty working tree of a repository. A working tree is what software developer works on. Git servers however have bare repositories, which can be created with `git init` command with a `--bare` parameter like this:
|
||||||
|
```sh
|
||||||
|
git init --bare <repository_name>
|
||||||
|
```
|
||||||
|
The default branch is `master`. If you want to initalize repositories with default branch other than `master`, you can run this command before initializing the repository:
|
||||||
|
```sh
|
||||||
|
git config --global init.defaultBranch <branch>
|
||||||
|
```
|
||||||
|
|
||||||
|
![The result of `git init` command](/images/git-init.png)
|
||||||
|
|
||||||
|
## Setting up the origin...
|
||||||
|
|
||||||
|
If you have an empty remote Git repository and an newly-initalized local repository, you can use this command:
|
||||||
|
```sh
|
||||||
|
git remote add origin <remote_repository_path>
|
||||||
|
```
|
||||||
|
The remote repository path can be accessed through Git protocol (like `git://repo.or.cz/svrjs.git`), SSH (like `ssh://git@repo.or.cz/svrjs.git`), HTTP(S) (like `https://git.svrjs.org/svrjs.git`) or local computer (like `/home/username/mynewrepository`)
|
||||||
|
|
||||||
|
If you want to change the origin, you can use this command:
|
||||||
|
```sh
|
||||||
|
git remote set-url origin <remote_repository_path>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cloning a repository with `git clone`
|
||||||
|
|
||||||
|
Often, you need to clone a Git repository to your development environment, e.g. to contribute to the project, or simply to build some program from source code. In this case you can use this command:
|
||||||
|
```sh
|
||||||
|
git clone <remote_repository_path>
|
||||||
|
```
|
||||||
|
If you want to replicate the repository into a server, you can use this command (it clones into bare repository):
|
||||||
|
```sh
|
||||||
|
git clone --bare <remote_respository_path>
|
||||||
|
```
|
||||||
|
If you want to clone specific branch, you can add `-b <branch>` parameter.
|
||||||
|
|
||||||
|
![The result of `git clone` command](/images/git-clone.png)
|
||||||
|
|
||||||
|
## Changing the code
|
||||||
|
|
||||||
|
After editing the code, you can commit the changes to the repository. But before committing these changes, you will need to include the files to be committed like this:
|
||||||
|
```sh
|
||||||
|
git add <files>
|
||||||
|
```
|
||||||
|
You can also use `git add .` to prepare entire current working directory.
|
||||||
|
|
||||||
|
After including the files, you can commit using this command:
|
||||||
|
```sh
|
||||||
|
git commit
|
||||||
|
```
|
||||||
|
Or with specified commit message:
|
||||||
|
```sh
|
||||||
|
git commit -m <commit_message>
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to also commit without a prior `git add` command, you can use this command:
|
||||||
|
```sh
|
||||||
|
git commit -a
|
||||||
|
```
|
||||||
|
|
||||||
|
If something gone wrong with the commit, you can modify the current commit by adding `--amend` parameter to the `git commit` command.
|
||||||
|
|
||||||
|
If you don't specify a commit message, you will fill it in the editor as shown below:
|
||||||
|
|
||||||
|
![Commit name filled in the editor](/images/git-clone.png)
|
||||||
|
|
||||||
|
You may see "Please tell me who you are" prompt along with commands to set up your name and email address. You can use these commands:
|
||||||
|
```sh
|
||||||
|
git config --global user.email <email_address>
|
||||||
|
git config --global user.name <committer_name>
|
||||||
|
```
|
||||||
|
You can skip `--global` parameter to set it just for one repository.
|
||||||
|
|
||||||
|
You can also define files not to be included in commits through `.gitignore` file in the repository root. This file contains paths to files not included in commits. An example of a `.gitignore` file can be:
|
||||||
|
```sh
|
||||||
|
node_modules
|
||||||
|
package-lock.json
|
||||||
|
```
|
||||||
|
In this case, `node_modules` and `package-lock.json` are not included in commits.
|
||||||
|
|
||||||
|
If you don't want the commit, you can first off check which commit to revert via `git log` command. Once you found out the commit ID, you can use this command:
|
||||||
|
```sh
|
||||||
|
git reset <commit_id>
|
||||||
|
```
|
||||||
|
If you add `--soft` parameter, you will not remove files to be included in commits. If you add `--hard` parameter, any changes to files will disappear.
|
||||||
|
|
||||||
|
You can also use those as a commit ID:
|
||||||
|
* `HEAD` - latest commit
|
||||||
|
* `HEAD^` - next to latest commit
|
||||||
|
* `HEAD~4` - four commits before the latest commit
|
||||||
|
* `origin/HEAD` - latest commit in the origin
|
||||||
|
* `somebranch` - latest commit in the `somebranch` branch.
|
||||||
|
* `sometag` - latest commit tagged with `sometag` tag.
|
||||||
|
|
||||||
|
If you want also to create commit reverting the changes, you can use this command:
|
||||||
|
```sh
|
||||||
|
git revert <commit_id>
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to revert the changes for one file to the latest commit, you can use this command:
|
||||||
|
```sh
|
||||||
|
git checkout -- <file_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pushing and pulling the changes!
|
||||||
|
|
||||||
|
After modfiying the code and committing the changes, you can use this command to push these changes to the remote server:
|
||||||
|
```sh
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
Or if you are pushing a newly-initialized repository:
|
||||||
|
```sh
|
||||||
|
git push --set-upstream origin <current_branch>
|
||||||
|
```
|
||||||
|
Or if you want to push all the branches:
|
||||||
|
```sh
|
||||||
|
git push --all origin
|
||||||
|
```
|
||||||
|
|
||||||
|
You can replace `master` with whatever branch you want to create. You may be asked then for credentials for a remote Git server.
|
||||||
|
|
||||||
|
![This `git push` command has failed.](/images/git-cant-push.png)
|
||||||
|
|
||||||
|
If you want to synchronize your repository with the remote repository (for example in situation shown above), you can use this command:
|
||||||
|
```sh
|
||||||
|
git pull
|
||||||
|
```
|
||||||
|
Or if you don't want to merge the trees:
|
||||||
|
```sh
|
||||||
|
git fetch
|
||||||
|
```
|
||||||
|
|
||||||
|
## Working with branches...
|
||||||
|
|
||||||
|
You can display all the local branches with this command:
|
||||||
|
```sh
|
||||||
|
git branch
|
||||||
|
```
|
||||||
|
You can also display both local and remote repositories using this command:
|
||||||
|
```sh
|
||||||
|
git branch -av
|
||||||
|
```
|
||||||
|
|
||||||
|
![List of branches in a Git repository.](/images/git-branch.png)
|
||||||
|
|
||||||
|
If you want to switch branches, you can use either `git checkout` or `git switch` command like this:
|
||||||
|
```sh
|
||||||
|
git checkout <new_branch>
|
||||||
|
```
|
||||||
|
Or like this:
|
||||||
|
```sh
|
||||||
|
git switch <new_branch>
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to create a new branch, you can use this command:
|
||||||
|
```sh
|
||||||
|
git branch <new_branch>
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to delete the branch, you can use:
|
||||||
|
```sh
|
||||||
|
git branch -d <branch_to_delete>
|
||||||
|
```
|
||||||
|
If you want to force the deletion, replace `-d` with `-D`.
|
||||||
|
|
||||||
|
If you want to move the branch, you can use:
|
||||||
|
```sh
|
||||||
|
git branch -m [<branch_to_move>] <new_branch>
|
||||||
|
```
|
||||||
|
If you skip `<branch_to_move>` parameter, the current branch will be moved. If you want to force the moving, replace `-m` with `-M`.
|
||||||
|
|
||||||
|
If you want to copy the branch to a new one, you can use:
|
||||||
|
```sh
|
||||||
|
git branch -c [<branch_to_copy>] <new_branch>
|
||||||
|
```
|
||||||
|
If you skip `<branch_to_copy>` parameter, the current branch will be copied. If you want to force the copying, replace `-c` with `-C`.
|
||||||
|
|
||||||
|
If you want to merge two branches, you can use this:
|
||||||
|
```sh
|
||||||
|
git checkout <destination_branch>
|
||||||
|
git merge <source_branch>
|
||||||
|
```
|
||||||
|
If you want to merge only one file, you can add `-F <filename>` parameter. Note, that you may need to manually modify files to fix merge conflicts.
|
||||||
|
|
||||||
|
![A Git merge conflict.](/images/git-merge-conflict.png)
|
||||||
|
|
||||||
|
When facing merge conflicts, you may see this if you open a conflicting file:
|
||||||
|
![Marks related to a Git merge conflict.](/images/git-merge-conflict-marks.png)
|
||||||
|
`<<<<<<<` marks the destination branch, `>>>>>>>` marks the source branch, while `=======` separates the conflicting contents. There may be even `|||||||`, which marks original text in the conflicting area.
|
||||||
|
|
||||||
|
You may need to remove those marks and some of confliciting content. After resolving the conflict, you can run `git commit` in order to commit the merge.
|
||||||
|
|
||||||
|
If you want to rebase all commits on one branch to another branch, you can use this command:
|
||||||
|
```sh
|
||||||
|
git rebase <branch_new_base> [<branch_to_rebase>]
|
||||||
|
```
|
||||||
|
If you skip `<branch_to_rebase>` parameter, the current commit will be rebased.
|
||||||
|
|
||||||
|
If you want to tag a commit you can use this command:
|
||||||
|
```sh
|
||||||
|
git tag <tag> [<commit>]
|
||||||
|
```
|
||||||
|
If you skip `<commit>` parameter, the current commit will be tagged.
|
||||||
|
|
||||||
|
If you want to remove the tag, you can use:
|
||||||
|
```sh
|
||||||
|
git tag -d <tag>
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to push these tags, you can use:
|
||||||
|
```sh
|
||||||
|
git push --tags
|
||||||
|
```
|
||||||
|
|
||||||
|
## Observe your repository!
|
||||||
|
|
||||||
|
If you want to see the state of repository or list new or modified files not yet committed, you can use this command:
|
||||||
|
```sh
|
||||||
|
git status
|
||||||
|
```
|
||||||
|
|
||||||
|
![Status of a Git repository.](/images/git-status.png)
|
||||||
|
|
||||||
|
If you want to see changes to files not yet staged, you can use:
|
||||||
|
```sh
|
||||||
|
git diff
|
||||||
|
```
|
||||||
|
Or if you want to see changes to staged files:
|
||||||
|
```sh
|
||||||
|
git diff --cached
|
||||||
|
```
|
||||||
|
Or if you want to see both changes:
|
||||||
|
```sh
|
||||||
|
git HEAD
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to see changes between two commit IDs, then you can use this command:
|
||||||
|
```sh
|
||||||
|
git diff <commit_id_1> <commit_id_2>
|
||||||
|
```
|
||||||
|
|
||||||
|
![Differences between commits.](/images/git-diff.png)
|
||||||
|
|
||||||
|
If you want to see author and dates for each change in the file, you can use:
|
||||||
|
```sh
|
||||||
|
git blame <filename>
|
||||||
|
```
|
||||||
|
|
||||||
|
![Blame view of a file](/images/git-blame.png)
|
||||||
|
|
||||||
|
If you want to show file at the specific commit, you can use:
|
||||||
|
```sh
|
||||||
|
git show <commit_id>:<filename>
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to show entire Git commit history, you can use this command:
|
||||||
|
```sh
|
||||||
|
git log
|
||||||
|
```
|
||||||
|
|
||||||
|
![A Git commit history.](/images/git-log.png)
|
||||||
|
|
||||||
|
## Configuring Git itself with `git config`
|
||||||
|
|
||||||
|
If you want to configure Git, then you can use `git config` command:
|
||||||
|
```sh
|
||||||
|
git config <option> <value>
|
||||||
|
```
|
||||||
|
By default it is per-repository. For per-user configuration you can use `--global` parameter, while for system-wide one you can use `--system` parameter.
|
||||||
|
|
||||||
|
These are some of configuration options:
|
||||||
|
* `user.name` - committer's name
|
||||||
|
* `user.email` - committer's email address
|
||||||
|
* `core.editor` - editor to use
|
||||||
|
* `color.ui` - if set to `true`, then `git` will use colors
|
||||||
|
* `init.defaultBranch` - default branch when initializing a repository, defualt value is `master`
|
||||||
|
* `http.sslVerify` - if set to `false`, then `git` will not verify SSL/TLS certificates
|
||||||
|
|
||||||
|
You can also add `-c <option>=<value>` parameter to any git command (right after `git`, but before command name, such as `clone`, `push` or `add`)
|
||||||
|
|
||||||
|
You can see configuration options in [The Git Book](https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration).
|
||||||
|
|
||||||
|
You can also list configuration parameters using this command:
|
||||||
|
```sh
|
||||||
|
git config --list
|
||||||
|
```
|
||||||
|
|
||||||
|
The repository configuration is stored in `config` file in `.git` directory in the repository directory. Per-user configuration is in `.gitconfig` file in the user home directory. System-wide configuration is stored in the `/etc/gitconfig` file.
|
||||||
|
|
||||||
|
## Git patches
|
||||||
|
|
||||||
|
If you are contributing to open-source project that uses Git as version control system, you may need to send patches to project maintainers. You may need to first format the patches.
|
||||||
|
|
||||||
|
You can create regular patches using this command:
|
||||||
|
```sh
|
||||||
|
git format-patch <commit_id> -o <directory_with_patches>
|
||||||
|
```
|
||||||
|
You can then send these patches to project maintainers.
|
||||||
|
|
||||||
|
Project maintainers will then use the inverse of `git format-patch` command - the `git apply` command. First, the maintainer will check the statistics about patch:
|
||||||
|
```sh
|
||||||
|
git apply --stat <patch>
|
||||||
|
```
|
||||||
|
Then maintainers will perform a dry run to detect errors in the patch:
|
||||||
|
```sh
|
||||||
|
git apply --check <patch>
|
||||||
|
```
|
||||||
|
Finally, maintainers will apply the patch:
|
||||||
|
```sh
|
||||||
|
git apply <patch>
|
||||||
|
```
|
||||||
|
|
||||||
|
Maintainers may also apply series of patches with this command:
|
||||||
|
```sh
|
||||||
|
git am <directory_with_patches>
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to send emails using `git` command, you need to first configure the SMTP server like this:
|
||||||
|
```sh
|
||||||
|
git config sendemail.smtpServer <smtp_server>
|
||||||
|
git config sendemail.smtpServerPort <smtp_port>
|
||||||
|
git config sendemail.smtpUser <smtp_user>
|
||||||
|
git config sendemail.smtpPass <password>
|
||||||
|
git config sendemail.from <email_address>
|
||||||
|
|
||||||
|
#Optional, specifies encryption
|
||||||
|
git config sendemail.smtpEncryption (ssl|tls)
|
||||||
|
```
|
||||||
|
|
||||||
|
Then format the patches with `git format-patch` command and send these patches with `git send-email` command like this:
|
||||||
|
```sh
|
||||||
|
git send-email --to=<destination_email> <patches>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Git command help
|
||||||
|
|
||||||
|
And finally, you can use this command for usage of specific `git` command:
|
||||||
|
```sh
|
||||||
|
git <command> --help
|
||||||
|
```
|
||||||
|
|
||||||
|
![Help for `git branch` command.](/images/git-help.png)
|
||||||
|
|
||||||
|
You may also see [Git documentation](https://git-scm.com/docs) for more information.
|
11
source/_posts/SVR-JS-3-4-2-released.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
title: SVR.JS 3.4.2 released!
|
||||||
|
date: 2023-07-07 15:55:53
|
||||||
|
categories: News
|
||||||
|
---
|
||||||
|
|
||||||
|
**We have just released SVR.JS 3.4.2!**
|
||||||
|
In new version, we fixed bugs with regex-based non-standard HTTP status code settings. These bug have appeared since release of SVR.JS 3.0.0.
|
||||||
|
We encountered this bug, when we set redirect to YaBB (Perl and CGI-based forum software) forum page, when we developed new version of RedBrick CGI engine. It was redirect loop. Now with SVR.JS 3.4.2, this issue is gone!
|
||||||
|
|
||||||
|
You can download new version of SVR.JS [here](http://svrjs.duckdns.org/dl/svr.js.3.4.2.zip).
|
28
source/_posts/SVR-JS-3-4-x-LTS-versions-now-EOL.md
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
---
|
||||||
|
title: SVR.JS 3.4.x LTS versions now EOL
|
||||||
|
date: 2024-02-02 00:07:11
|
||||||
|
tags:
|
||||||
|
- eol
|
||||||
|
categories:
|
||||||
|
- [News]
|
||||||
|
- [Notices]
|
||||||
|
thumbnail: /images/covers/SVR-JS-3-4-x-LTS-versions-now-EOL.png
|
||||||
|
---
|
||||||
|
|
||||||
|
**SVR.JS 3.4.42 was the last supported release from SVR.JS 3.4.x LTS line. and also the last SVR.JS release to ever support Node.JS 8.x and 9.x.** There will be no futher SVR.JS updates for such old Node.JS versions.
|
||||||
|
|
||||||
|
## Why SVR.JS ended support for SVR.JS 3.4.x LTS?
|
||||||
|
|
||||||
|
SVR.JS 3.4.x was one of the last web server software to support Node.JS 8.x and 9.x. OpenJS Foundation itself ended official support for Node.JS 8.x in December 2019 and Node.JS 9.x in June 2018. Unsupported Node.JS versions receive no security updates, have known vulnerabilites, and can be dangerous to use, which makes it difficult to maintain SVR.JS on those versions. [Earlier, we had set the EOL date to Feburary 2024.](https://blog.svrjs.org/2023/07/16/SVR-JS-on-Node-JS-8-x-and-9-x-moving-to-LTS/)
|
||||||
|
|
||||||
|
## Will switching to a different Node.JS-based server software keep me protected?
|
||||||
|
|
||||||
|
No. Most active Node.JS-based server software (such as _http-server_ or _server.js_) have already ended support for Node.JS 8.x and 9.x.
|
||||||
|
|
||||||
|
## Can I still use SVR.JS safely with Node.JS 8.x and 9.x?
|
||||||
|
|
||||||
|
No. Currently, the support for SVR.JS 3.4.x LTS line has ended. Unsupported web server software receive no security updates, have [known vulnerabilites](https://svrjs.org/vulnerabilities), and can be dangerous to use.
|
||||||
|
|
||||||
|
## How can I get the newest features of SVR.JS?
|
||||||
|
|
||||||
|
If you want to keep your SVR.JS up to date, with all the latest features and updates, you need to upgrade your Node.JS runtime to Node.JS 10.0.0 or newer. Sometimes, Node.JS may also stop supporting older hardware architectures and platforms. After upgrading Node.JS, server administrators can easily update SVR.JS software and still host a HTTP server.
|
10
source/_posts/SVR-JS-Blog-created.md
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
---
|
||||||
|
title: SVR.JS Blog created!
|
||||||
|
date: 2023-07-06 03:34:29
|
||||||
|
categories:
|
||||||
|
- News
|
||||||
|
---
|
||||||
|
|
||||||
|
## SVR.JS Blog created!
|
||||||
|
|
||||||
|
We just created official SVR.JS blog. In this blog, there will be posts related with SVR.JS.
|
14
source/_posts/SVR-JS-Forum-announced.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
title: SVR.JS Forum announced
|
||||||
|
date: 2023-07-07 22:13:16
|
||||||
|
tags:
|
||||||
|
categories: News
|
||||||
|
---
|
||||||
|
|
||||||
|
![SVR.JS Forum](/images/forum.png)
|
||||||
|
|
||||||
|
**We announced SVR.JS Forum**
|
||||||
|
|
||||||
|
In SVR.JS forum, you can ask community (or SVR.JS authors) about SVR.JS issues.
|
||||||
|
|
||||||
|
You can go [here](https://forum.svrjs.duckdns.org/cgi-bin/yabb2/YaBB.pl).
|
85
source/_posts/SVR-JS-can-now-do-SCGI.md
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
---
|
||||||
|
title: SVR.JS can now do SCGI!
|
||||||
|
date: 2023-08-11 03:26:06
|
||||||
|
tags:
|
||||||
|
- cgi
|
||||||
|
categories:
|
||||||
|
- [News]
|
||||||
|
- [Tips]
|
||||||
|
---
|
||||||
|
**We have previously made SVR.JS run PHP-CGI and WordPress. Now SVR.JS - a web server software running on Node.JS can connect to SCGI (Simple Common Gateway Interface) servers.**
|
||||||
|
We have developed a new mod to do that, [OrangeCircle](https://svrjs.org/dl/mods) (SCGI client written in JavaScript running on SVR.JS).
|
||||||
|
SCGI (Simple Common Gateway Interface) is a protocol to interface web applications with HTTP servers, which is an alternative to CGI. It is similar to FastCGI, but it is easier to parse and implement. SCGI doesn't have interpreter invoking overhead, unlike CGI.
|
||||||
|
This post will instruct you, how to set up SCGI web applications on SVR.JS.
|
||||||
|
## Setting up SCGI on server-side
|
||||||
|
First of all, [download](https://svrjs.org) and install SVR.JS web server. You can also use `create-svrjs-server` tool or [SVR.JS installer](https://svrjs.org/dl/installer) to install SVR.JS.
|
||||||
|
|
||||||
|
After setting up SVR.JS, download and install [OrangeCircle](https://svrjs.org/dl/mods) to *mods* directory.
|
||||||
|
|
||||||
|
After installing OrangeCircle SCGI client, create *orangecircle-config.json* file in SVR.JS installation directory, and configure it like this:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"path": "/helloworld",
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 4000
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Adjust your mount path, SCGI server host and port settings according to your needs.
|
||||||
|
|
||||||
|
After setting up and configuring SCGI client, run SVR.JS using `node svr.js` or `bun run svr.js` and visit SCGI page (for example http://localhost/helloworld). You will then see this error page:
|
||||||
|
![503 Service Unavailable page displayed when trying to connect to SCGI server](/images/scgi-client-error.png)
|
||||||
|
This is because we didn't set up SCGI server yet. **But we have set up SCGI client!** We need to just set up SCGI server...
|
||||||
|
## Setting up SCGI server
|
||||||
|
To set up SCGI server, first create *scgi-server* directory outside SVR.JS installation directory, and change your working directory to new one. Then install SCGI server module using `npm install scgi-server` (*scgi-server* package is deprecated; we're using it just for example SCGI application) command. After installing SCGI server module, create *index.js* file with those contents:
|
||||||
|
```js
|
||||||
|
var SCGIServer = require('scgi-server');
|
||||||
|
|
||||||
|
var server = SCGIServer(4000);
|
||||||
|
server.on('request', function(error, socket, headers, data) {
|
||||||
|
if (error) {
|
||||||
|
console.error("received invalid scgi request");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
socket.write("Status: 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n");
|
||||||
|
socket.write("<h1>Hello, World!</h1>");
|
||||||
|
socket.end();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
This SCGI server will send "Hello World" message to the web server, which will send it to your web browser.
|
||||||
|
Run it using `node index.js` or `bun run index.js`.
|
||||||
|
|
||||||
|
If you see this error:
|
||||||
|
```
|
||||||
|
/home/ubuntu/scgi-server/index.js:4
|
||||||
|
server.on('request', function(error, socket, headers, data) {
|
||||||
|
^
|
||||||
|
|
||||||
|
TypeError: server.on is not a function
|
||||||
|
at Object.<anonymous> (/home/ubuntu/scgi-server/index.js:4:8)
|
||||||
|
at Module._compile (internal/modules/cjs/loader.js:778:30)
|
||||||
|
at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
|
||||||
|
at Module.load (internal/modules/cjs/loader.js:653:32)
|
||||||
|
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
|
||||||
|
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
|
||||||
|
at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)
|
||||||
|
at startup (internal/bootstrap/node.js:283:19)
|
||||||
|
at bootstrapNodeJSCore (internal/bootstrap/node.js:623:3)
|
||||||
|
```
|
||||||
|
Then open *node_modules/scgi-server/index.js* file and replace this line:
|
||||||
|
```js
|
||||||
|
var self = this
|
||||||
|
EventEmitter.call(this)
|
||||||
|
```
|
||||||
|
with this one:
|
||||||
|
```js
|
||||||
|
var self = new EventEmitter()
|
||||||
|
```
|
||||||
|
Restart the SCGI server, and the error shoudn't occur again.
|
||||||
|
|
||||||
|
After starting up SCGI server, visit SCGI page again, and you'll see "Hello World" message:
|
||||||
|
![SCGI "Hello World"](/images/scgi-hello-world.png)
|
||||||
|
|
||||||
|
**We have finally set up SCGI!**
|
||||||
|
|
||||||
|
SCGI is language-indepedent web application protocol, so you can write SCGI server in JavaScript (Node.JS; *scgi-server* module, deprecated), Python (*scgi* module), Perl (*SCGI* package), PHP (see [this gist](https://gist.github.com/brad-jones/8573837)) and many more programming languages!
|
170
source/_posts/SVR-JS-can-now-run-PHP-and-also-WordPress.md
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
---
|
||||||
|
title: SVR.JS can now run PHP (and also WordPress!)
|
||||||
|
date: 2023-08-02 15:53:31
|
||||||
|
tags:
|
||||||
|
- php
|
||||||
|
- wordpress
|
||||||
|
categories:
|
||||||
|
- [News]
|
||||||
|
- [Tips]
|
||||||
|
---
|
||||||
|
|
||||||
|
**SVR.JS - a web server software running on Node.JS can now run PHP through PHP-CGI and RedBrick mod.**
|
||||||
|
Because SVR.JS can run PHP, SVR.JS can also run WordPress. Keep in mind, that it comes with CGI overhead. This post will instruct you, how to set up PHP and WordPress on SVR.JS.
|
||||||
|
## Installing PHP
|
||||||
|
First, [download](https://svrjs.org) and install SVR.JS per [documentation](https://svrjs.org/docs). You can also use `create-svrjs-server` tool to do that.
|
||||||
|
|
||||||
|
Since neither SVR.JS nor Node.JS has native PHP library, we're using and installing PHP-CGI. To do this, first download and install [RedBrick](https://svrjs.org/dl/mods) (2.3.2 or newer) into *mods* directory. RedBrick is a CGI runtime written in JavaScript running on SVR.JS.
|
||||||
|
|
||||||
|
After we have installed CGI runtime, we're installing PHP-CGI. You can [download PHP manually](https://www.php.net/downloads.php), however in many GNU/Linux distributions it is available in package manager. For Debian/Ubuntu you can run `sudo apt install php-cgi`. For Red Hat/CentOS/Fedora you can run `sudo yum install php-cgi` or `sudo dnf install php-cgi`. For Arch/Manjaro you can run `sudo pacman -S php-cgi`.
|
||||||
|
|
||||||
|
Once you have installed PHP-CGI, you can create *cgi-bin* directory inside the web root and create *test.php* file with those contents:
|
||||||
|
```php
|
||||||
|
<?php phpinfo(); ?>
|
||||||
|
```
|
||||||
|
|
||||||
|
Run SVR.JS using `node svr.js ` or `bun run svr.js` and visit test page, most probably *http://localhost/cgi-bin/test.php*. You will then see this page:
|
||||||
|
![PHP CGI warning](/images/php-cgi-warning.png)
|
||||||
|
|
||||||
|
To enable PHP-CGI, locate PHP-CGI configuration file (in GNU/Linux is usually */etc/php/<php version>/cgi/php.ini*) and set `cgi.force_redirect` property to `0`.
|
||||||
|
|
||||||
|
After configuration, visit test page again, and you will see this:
|
||||||
|
![PHP information page](/images/php-cgi-info.png)
|
||||||
|
|
||||||
|
**We have installed PHP!**
|
||||||
|
|
||||||
|
## Installing WordPress
|
||||||
|
Now we have installed PHP, so let's move on to WordPress installation.
|
||||||
|
|
||||||
|
First, delete *test.php* file that you created earlier. Then [download WordPress .zip file](https://wordpress.org/download/) and extract all of it's contents to *cgi-bin* directory.
|
||||||
|
|
||||||
|
You need to also install MySQL/[MariaDB](https://mariadb.org/download/) relational database server and *mysqli* PHP library. These packages are also available in package manager. For Debian/Ubuntu you can run `sudo apt install php-mysql mariadb-server`. For Red Hat/CentOS/Fedora you can run `sudo yum install php-mysql mysql mysql-server` or `sudo yum install php-mysql mysql mysql-server`. For Arch/Manjaro you can run `sudo pacman -S mysql`.
|
||||||
|
|
||||||
|
Then configure MySQL/MariaDB to listen to port 3306. Add this to configuration file:
|
||||||
|
```
|
||||||
|
port = 3306
|
||||||
|
```
|
||||||
|
|
||||||
|
And restart MySQL/MariaDB using `sudo systemctl restart mysql` or using `/etc/init.d/mysql restart`.
|
||||||
|
|
||||||
|
Then you can run sequence of SQL commands to create database and user using MySQL/MariaDB console (invoked by `sudo mysql`):
|
||||||
|
```sql
|
||||||
|
CREATE DATABASE your_database_name;
|
||||||
|
CREATE USER 'your_username'@'localhost' IDENTIFIED BY 'your_password';
|
||||||
|
CREATE USER 'your_username'@'%' IDENTIFIED BY 'your_password';
|
||||||
|
GRANT ALL ON your_database_name.* TO 'your_username'@'localhost';
|
||||||
|
GRANT ALL ON your_database_name.* TO 'your_username'@'%';
|
||||||
|
```
|
||||||
|
|
||||||
|
After running this sequence, visit WordPress page (in this example *http://localhost/cgi-bin/wordpress/*). You'll see this:
|
||||||
|
![WordPress setup screen](/images/wordpress-setup.png)
|
||||||
|
|
||||||
|
First, click "Let's go!" button. Then put in database data (that points to your database you just created).
|
||||||
|
Example data:
|
||||||
|
- **Database Name** - *your_database_name*
|
||||||
|
- **Username** - *your_username*
|
||||||
|
- **Password** - *your_password*
|
||||||
|
- **Database Host** - *localhost*
|
||||||
|
- **Table Prefix** - *wp_*
|
||||||
|
|
||||||
|
Click "Submit" and then click "Run the installation". In this screen, you will need to input data about your WordPress website.
|
||||||
|
Example data:
|
||||||
|
- **Site Title** - *WordPress blog on SVR.JS*
|
||||||
|
- **Username** - *svrjs-wp*
|
||||||
|
- **Password** - Your chosen password
|
||||||
|
- **Your Email** - Your e-mail address
|
||||||
|
- **Search engine visibility** - Don't discourage search engines from indexing this site
|
||||||
|
|
||||||
|
After inputting the data, click "Install WordPress". **WordPress is now installed!**
|
||||||
|
|
||||||
|
After installation, click "Log in" and type in your chosen username and password.
|
||||||
|
|
||||||
|
After logging in, you'll see WordPress administration panel:
|
||||||
|
![WordPress administration panel](/images/wordpress-admin.png)
|
||||||
|
|
||||||
|
**You can now create posts, manage comments and install WordPress plugins!**
|
||||||
|
|
||||||
|
To visit your WordPress site, navigate yourself into WordPress index page (for example *http://localhost/cgi-bin/wordpress*). You'll see this:
|
||||||
|
![WordPress website home page](/images/wordpress-homepage.png)
|
||||||
|
|
||||||
|
**It's important to kill CGI code execution (except PHP) inside WordPress installation folder. It's also recommended to kill PHP code execution inside wp-content directory.** You can do this using non-standard status code functionality of SVR.JS, or by modifying *redbrick-interpreters.json* configuration file inside SVR.JS installation folder.
|
||||||
|
If you want to edit RedBrick configuration file, you can change interpreters to "UNKNOWN_COMMAND", for example:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
".pl": ["UNKNOWN_COMMAND"],
|
||||||
|
".py": ["UNKNOWN_COMMAND"],
|
||||||
|
".sh": ["UNKNOWN_COMMAND"],
|
||||||
|
".pyw": ["UNKNOWN_COMMAND"],
|
||||||
|
".rb": ["UNKNOWN_COMMAND"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Restart SVR.JS server. After those changes, attempts to execute those files will result in 500 Internal Server Error code. Keep in mind, that it will break CGI scripts outside WordPress installation directory. You can also point those to custom CGI script serving static files instead of "UNKNOWN_COMMAND", for example:
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ -f "$SCRIPT_FILENAME"]; then
|
||||||
|
echo "Status: 200 OK"
|
||||||
|
echo
|
||||||
|
cat "$SCRIPT_FILENAME"
|
||||||
|
else
|
||||||
|
echo "Status: 404 Not Found"
|
||||||
|
echo "Content-Type: text/plain"
|
||||||
|
echo
|
||||||
|
echo "404 Not Found"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
If you want to modify SVR.JS configuration file though, you can add two non-standard code properties (to *nonStandardCodes* property in *config.json* inside SVR.JS installation directory), for example:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
regex: "/^\\/cgi-bin\\/wordpress\\/(?:(?!\\.(?:pl|pyw?|sh|rb)($|[?#]))[^?#])*\\.(?:pl|pyw?|sh|rb)($|[?#])/",
|
||||||
|
scode: 403
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: "/^\\/cgi-bin\\/wordpress\\/wp-content\\/(?:(?!\\.(?:pl|pyw?|sh|rb|php[345]?)($|[?#]))[^?#])*\\.(?:pl|pyw?|sh|rb|php[345])($|[?#])/",
|
||||||
|
scode: 403
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Restart SVR.JS server. After those changes, attempts to execute those files will result in 403 Forbidden code.
|
||||||
|
|
||||||
|
For even more security, you can disable directory listings by setting *enableDirectoryListing* property in *config.json* inside SVR.JS installation directory to `false`.
|
||||||
|
|
||||||
|
You can also make WordPress website home page into for example *http://localhost*, by using URL rewriting feature of SVR.JS and by modifing WordPress settings to point to new URL.
|
||||||
|
To do this, first navigate to WordPress administration panel, then go into Settings -> Permalinks. Then change Permalink structure to Custom Structure, and remove "/index.php" from custom structure field. After applying those settings, go into Settings -> General menu. Then change WordPress address and site address into for example *http://localhost/*. After submitting those settings, you'll see 404 Not Found page. This is because we didn't set up URL rewrites yet. To fix that, add URL rewrite rules to URL rewrite map (*rewriteMap* property in *config.json*), for example:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"definingRegex": "/^\\/(?!favicon\\.ico(?:$|[?#]))/",
|
||||||
|
"replacements": [
|
||||||
|
{
|
||||||
|
"regex": "/^\\/(?!$|[?#]|wp-[a-z]+)(.*)/",
|
||||||
|
"replacement": "/cgi-bin/wordpress/index.php/$1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"regex": "/^\\/($|[?#].*|wp-[a-z]+.*)/",
|
||||||
|
"replacement": "/cgi-bin/wordpress/$1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Restart SVR.JS server. Afterwards, you will get a clean URL for your WordPress website.
|
||||||
|
|
||||||
|
**UPDATE**: In order for *wp-content* to be more secure (prevent execution of ELF executables and scripts with shebang), you can move *wp-content* outside of *cgi-bin* directory and create symlink to moved *wp-content* like this:
|
||||||
|
```bash
|
||||||
|
ubuntu@ubuntu:~/svrjs/cgi-bin/wordpress$ mv wp-content ../../
|
||||||
|
ubuntu@ubuntu:~/svrjs/cgi-bin/wordpress$ ln -s ../../wp-content wp-content
|
||||||
|
```
|
||||||
|
For moved *wp-content* you can use URL rewite rules for SVR.JS like this:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"definingRegex": "/^\\/(?!favicon\\.ico(?:$|[?#])|wp-content(?:$|[?#\\/]))/",
|
||||||
|
"replacements": [
|
||||||
|
{
|
||||||
|
"regex": "/^\\/(?!$|[?#]|wp-[a-z]+)(.*)/",
|
||||||
|
"replacement": "/cgi-bin/wordpress/index.php/$1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"regex": "/^\\/($|[?#].*|wp-[a-z]+.*)/",
|
||||||
|
"replacement": "/cgi-bin/wordpress/$1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
10
source/_posts/SVR-JS-git-server-launched.md
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
---
|
||||||
|
title: SVR.JS git server launched
|
||||||
|
date: 2023-07-29 22:09:04
|
||||||
|
tags:
|
||||||
|
categories: News
|
||||||
|
---
|
||||||
|
|
||||||
|
**We have launched [our SVR.JS git server](https://git.svrjs.org)!**
|
||||||
|
|
||||||
|
If you wanted to contribute to SVR.JS, then the SVR.JS git server is a way for you.
|
19
source/_posts/SVR-JS-now-supports-FastCGI.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
---
|
||||||
|
title: SVR.JS now supports FastCGI!
|
||||||
|
tags:
|
||||||
|
- fastcgi
|
||||||
|
- fcgi
|
||||||
|
- http
|
||||||
|
- web
|
||||||
|
- server
|
||||||
|
category: News
|
||||||
|
thumbnail: /images/covers/SVR-JS-now-supports-FastCGI.png
|
||||||
|
date: 2024-02-19 04:44:13
|
||||||
|
---
|
||||||
|
|
||||||
|
**SVR.JS - a web server software running on Node.JS can now connect to FastCGI (Fast Common Gateway Interface) servers through GreenRhombus mod!** We have specifically implemented the responder role of FastCGI.
|
||||||
|
FastCGI (Fast Common Gateway Interface) is an binary interface between web server and web applications. It aims to reduce overhead related to spawning new processes (as it was in CGI), allowing a web server to process more requests at the time.
|
||||||
|
We did switch from PHP-CGI and RedBrick to PHP-FPM and GreenRhombus on our SVR.JS Forum. Before the switch, the server responded within **300ms.** After the switch it is only **100ms**! We have tested from our premises though.
|
||||||
|
|
||||||
|
[*Download GreenRhombus on SVR.JS mods page*](https://svrjs.org/mods)
|
||||||
|
[*See the notes*](https://svrjs.org/docs#FastCGI-PHP-FPM)
|
16
source/_posts/SVR-JS-on-Node-JS-8-x-and-9-x-moving-to-LTS.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
title: SVR.JS on Node.JS 8.x and 9.x moving to LTS
|
||||||
|
date: 2023-07-16 16:37:44
|
||||||
|
tags:
|
||||||
|
- eol
|
||||||
|
categories: News
|
||||||
|
---
|
||||||
|
SVR.JS 3.4.x will be last of supported SVR.JS versions for Node.JS 8.x and 9.x. If you are using these versions of Node.JS you will be able to use only SVR.JS LTS branch. SVR.JS will provide security updates and bug fixes for servers running those Node.JS versions until February 2024. No updates will be provided after that date.
|
||||||
|
## Why has SVR.JS ended support for Node.JS 8.x and 9.x?
|
||||||
|
OpenJS Foundation ended official support for Node.JS 8.x in December 2019 and Node.JS 9.x in June 2018. Unsupported Node.JS versions receive no security updates and may have known vulnerabilities. With no official support from OpenJS Foundation, maintaining SVR.JS for obsolete Node.JS versions needs many constriants for SVR.JS authors and becomes dangerous for users.
|
||||||
|
## Will switching to a different Node.JS-based server software keep me protected?
|
||||||
|
No. Most active Node.JS-based server software, including [*http-server*](https://github.com/http-party/http-server#readme) (requires at least Node.JS 12.6.0), [*serve*](https://github.com/vercel/serve#readme) (requires at least Node.JS 14.0.0), and [*server.js*](https://serverjs.io) (requires at least Node.JS 10.0.0) have already ended support for Node.JS 8.x and 9.x. Many other Node.JS-based server software, like [*svr*](https://svr.js.org) (not SVR.JS; HTTP developement server), or [*ecstatic*](https://github.com/jfhbrook/node-ecstatic) are no longer maintained, and may have unfixed security vulnerabilties.
|
||||||
|
## Can I still use SVR.JS safely with Node.JS 8.x and 9.x?
|
||||||
|
SVR.JS authors are providing critical security updates through the [SVR.JS LTS](https://svrjs.duckdns.org/dl/svr.js.3.5.0.zip) branch up until the EOL of SVR.JS 3.4.x, February 2024. After this, no security updates are provided and you are strongly encouraged to move to a supported version of Node.JS.
|
||||||
|
## How can I get the newest features of SVR.JS?
|
||||||
|
If you want to keep your SVR.JS up to date, with all the latest feautres and updates, you need to upgrade your Node.JS to Node.JS 10.0.0 or newer. In some cases, Node.JS may stop supporting older platforms. After upgrading, server administrators can easily update SVR.JS and still run HTTP server.
|
21
source/_posts/SVR-JS-to-drop-all-DorianTech-remnants.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
---
|
||||||
|
title: SVR.JS to drop all "DorianTech" remnants
|
||||||
|
date: 2023-12-28 22:03:49
|
||||||
|
tags:
|
||||||
|
categories: News
|
||||||
|
thumbnail: /images/covers/SVR-JS-to-drop-all-DorianTech-remnants.png
|
||||||
|
---
|
||||||
|
|
||||||
|
**SVR.JS had just dropped all remnants of "DorianTech" from its development branch after 5 years since SVR.JS was created.** It will be soon dropped from future SVR.JS release versions and official SVR.JS mods.
|
||||||
|
![A git commit, that removed "DorianTech" from the development branch, viewed from GitWeb.](/images/doriantech-removed-git.png)
|
||||||
|
|
||||||
|
## A brief history of SVR.JS
|
||||||
|
![This is what SVR.JS was in its early days - "DorianTech Node.JS Server"](/images/doriantech-http-server.png)
|
||||||
|
SVR.JS was created in 2018 as "DorianTech Node.JS Server" to serve single website. Since then, SVR.JS is constantly improving. SVR.JS 1.x introduced directory listings, while SVR.JS 2.x introduced multi-clustering, *.tar.gz* SVR.JS mods, and server-side JavaScript. Until its development was suspended in 2020.
|
||||||
|
|
||||||
|
![SVR.JS 2.x had a misspelling in 500 error page: "unexcepted execption". The error page was rewritten in SVR.JS 3.x](/images/unexcepted-execption.png)But SVR.JS 2.x had a lot of problems, like XSS and path traversal security vulnerabilities, "unexcepted execptions", malformed URL crashes, no support for SNI, HTTP compression (it was broken) and URL rewriting. What's worse, SVR.JS has no documentation!
|
||||||
|
|
||||||
|
SVR.JS "woke up" in 2023, when there was a need for another website.
|
||||||
|
SVR.JS 2.x problems were fixed in SVR.JS 3.x, although it still retained the "Welcome to DorianTech SVR.JS Server" message from SVR.JS 2.x. Logo and directory listing icons were also redesigned. The documentation has been written.
|
||||||
|
|
||||||
|
SVR.JS now supports many server-side technologies, such as server-side JavaScript, CGI (through RedBrick), SCGI (through OrangeCircle), JSGI (through YellowSquare) and PHP (through RedBrick)! It also has a installer for GNU/Linux, and can be also configured as a reverse proxy (with reverse-proxy-mod).
|
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
title: We have released new version of RedBrick CGI engine!
|
||||||
|
date: 2023-07-07 16:08:59
|
||||||
|
tags:
|
||||||
|
- cgi
|
||||||
|
categories: News
|
||||||
|
---
|
||||||
|
|
||||||
|
**RedBrick 2.0.0 is released!**
|
||||||
|
|
||||||
|
When we checked RedBrick 1.0.0, it was old, non-compliant, fits only with SVR.JS 2.x... This is why we renewed RedBrick CGI engine. RedBrick 2.0.0 now uses SVR.JS 3.x methods (it's still compatible with SVR.JS 2.x) and it's more compliant with CGI standard.
|
||||||
|
We tried YaBB 2.5.2 on RedBrick. It works, but you need old version of Perl to make it fully work...
|
||||||
|
|
||||||
|
You can get RedBrick mod [here](http://svrjs.duckdns.org/dl/mods/redbrick.cgi.2.0.0.tar.gz).
|
21
source/_posts/Welcome-to-our-new-SVR-JS-forum.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
---
|
||||||
|
title: Welcome to our new SVR.JS forum!
|
||||||
|
date: 2024-02-15 02:00:08
|
||||||
|
tags:
|
||||||
|
- forum
|
||||||
|
- phpbb
|
||||||
|
- phpbb3
|
||||||
|
- server
|
||||||
|
- webmaster
|
||||||
|
categories:
|
||||||
|
- [News]
|
||||||
|
- [Notices]
|
||||||
|
thumbnail: /images/covers/Welcome-to-our-new-SVR-JS-forum.png
|
||||||
|
---
|
||||||
|
**We have migrated from modified version of YaBB 2.7.00 to phpBB!** We have migrated, because YaBB forum software is not actively maintained anymore and our previous forum used tables for layout (which is not recommended from SEO perspective). We had to modify YaBB due to its security vulnerabilities. The new phpBB forum should be fairly secure.
|
||||||
|
|
||||||
|
We have started the migration in February 10th, 2024, and we have finished the forum in February 14th, 2024.
|
||||||
|
|
||||||
|
**Our forum is dedicated to SVR.JS web server and webmaster activities.**
|
||||||
|
|
||||||
|
**You can visit and/or register on the forum at https://forum.svrjs.org. You can also visit it on Tapatalk (search _forum.svrjs.org_).**
|
407
source/_posts/What-is-SQL-injection-How-to-prevent-it.md
Normal file
|
@ -0,0 +1,407 @@
|
||||||
|
---
|
||||||
|
title: What is SQL injection? How to prevent it?
|
||||||
|
date: 2023-12-02 20:25:02
|
||||||
|
tags:
|
||||||
|
- cybersecurity
|
||||||
|
- sql
|
||||||
|
- php
|
||||||
|
categories: Tips
|
||||||
|
thumbnail: /images/covers/What-is-SQL-injection-How-to-prevent-it.png
|
||||||
|
---
|
||||||
|
SQL injection (SQLi) attacks may destroy your database. These attacks are one of the most common attacks on websites (it is in the [OWASP Top Ten](https://owasp.org/www-project-top-ten/)). This attack relies on injecting code into SQL database queries through the user input.
|
||||||
|
|
||||||
|
**WARNING: We're not responsible for damage caused by SQL injection! Malicious hacking is a computer crime and you may face legal consequences! This post is meant to gain awareness about SQL injection and give a way to prevent those vulnerabilities.**
|
||||||
|
|
||||||
|
## The impact of SQL injection
|
||||||
|
SQL injection attacks may result in unauthorized access to sensitive data, such as:
|
||||||
|
|
||||||
|
- Passwords
|
||||||
|
- Credit card information
|
||||||
|
- Personal user information
|
||||||
|
- Information normally hidden from website users
|
||||||
|
|
||||||
|
SQL injection attacks have been used in many data breaches over the years, which then caused damage to the reputation and regulatory fines.
|
||||||
|
|
||||||
|
## SQL injection types
|
||||||
|
SQL injection can be divided to:
|
||||||
|
|
||||||
|
- **Error-based SQL injection** - injection is done with help of error messages.
|
||||||
|
- **Union-based SQL injection** - injection is done with help of ``UNION`` clause.
|
||||||
|
- **Blind SQL injection** - it is possible to extract the data with help of generated content
|
||||||
|
- **Time-based SQL injection** - injection is done with help of response delays
|
||||||
|
|
||||||
|
## Example: vulnerable web application
|
||||||
|
*Sorry, we didn't do it in server-side JavaScript this time...*
|
||||||
|
|
||||||
|
This is an example of PHP web application vulnerable to SQL injection:
|
||||||
|
```php
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Student average grades</title>
|
||||||
|
<meta name="viewport" content="width=device-width, inital-scale=1.0">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<?php
|
||||||
|
mysqli_report(MYSQLI_REPORT_OFF);
|
||||||
|
$conn = mysqli_connect('localhost','dbuser','dbpassword','studentdb') or die("Can't connect to the database!");
|
||||||
|
|
||||||
|
//SQL query error handling
|
||||||
|
function handle_sql_error($conn) {
|
||||||
|
mysqli_close($conn);
|
||||||
|
die("There was a problem with SQL query.");
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<h1>Student average grades</h1>
|
||||||
|
<?php
|
||||||
|
$result = mysqli_query($conn, "SELECT COUNT(id) AS count FROM students;") or handle_sql_error($conn);
|
||||||
|
print "<p>There are ".mysqli_fetch_assoc($result)["count"]." students in the student database.</p>";
|
||||||
|
?>
|
||||||
|
<form method="post">
|
||||||
|
<label for="q">Search query:</label>
|
||||||
|
<input type="text" name="q" id="q">
|
||||||
|
<input type="submit" value="Search!">
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
if(isset($_POST["q"]) and trim($_POST["q"]) != "") {
|
||||||
|
$q = str_replace(array('!','%','_','['), array('!!','!%','!_', '!['), $_POST["q"]); //So that "%" will not break searches
|
||||||
|
//WARNING! Code below is vulnerable to SQL injection!!!
|
||||||
|
$result = mysqli_query($conn, "SELECT students.name AS name, ROUND(AVG(grades.grade), 2) AS average FROM grades INNER JOIN students ON students.id = grades.studentid WHERE students.name LIKE '%$q%' GROUP BY grades.studentid;") or handle_sql_error($conn);
|
||||||
|
$count = mysqli_num_rows($result);
|
||||||
|
print "<p><b>Result count: $count</b></p>";
|
||||||
|
print "<ul>";
|
||||||
|
while($arr = mysqli_fetch_assoc($result)) {
|
||||||
|
//We don't make it vulnerable to XSS for now...
|
||||||
|
$studentname = htmlentities($arr["name"]);
|
||||||
|
$studentaverage = htmlentities($arr["average"]);
|
||||||
|
print " <li><b>$studentname</b> — $studentaverage</li>";
|
||||||
|
}
|
||||||
|
print "</ul>";
|
||||||
|
mysqli_close($conn);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to try it, there is a database structure in SQL, along with mock data (the database name is *studentdb*, DBMS is MySQL/MariaDB):
|
||||||
|
```sql
|
||||||
|
-- phpMyAdmin SQL Dump
|
||||||
|
-- version 5.2.1
|
||||||
|
-- https://www.phpmyadmin.net/
|
||||||
|
--
|
||||||
|
-- Host: localhost
|
||||||
|
-- Generation Time: Dec 02, 2023 at 07:19 PM
|
||||||
|
-- Server version: 10.3.38-MariaDB-0ubuntu0.20.04.1
|
||||||
|
-- PHP Version: 7.4.3-4ubuntu2.19
|
||||||
|
|
||||||
|
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
|
||||||
|
START TRANSACTION;
|
||||||
|
SET time_zone = "+00:00";
|
||||||
|
|
||||||
|
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||||
|
/*!40101 SET NAMES utf8mb4 */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Database: `studentdb`
|
||||||
|
--
|
||||||
|
|
||||||
|
-- --------------------------------------------------------
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `grades`
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE `grades` (
|
||||||
|
`gradeid` int(11) NOT NULL,
|
||||||
|
`studentid` int(11) NOT NULL,
|
||||||
|
`grade` int(11) NOT NULL
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `grades`
|
||||||
|
--
|
||||||
|
|
||||||
|
INSERT INTO `grades` (`gradeid`, `studentid`, `grade`) VALUES
|
||||||
|
(1, 4, 2),
|
||||||
|
(2, 5, 3),
|
||||||
|
(3, 9, 3),
|
||||||
|
(4, 2, 5),
|
||||||
|
(5, 6, 6),
|
||||||
|
(6, 6, 5),
|
||||||
|
(7, 2, 3),
|
||||||
|
(8, 4, 3),
|
||||||
|
(9, 7, 5),
|
||||||
|
(10, 5, 4),
|
||||||
|
(11, 2, 1),
|
||||||
|
(12, 2, 6),
|
||||||
|
(13, 10, 5),
|
||||||
|
(14, 2, 4),
|
||||||
|
(15, 4, 5),
|
||||||
|
(16, 3, 4),
|
||||||
|
(17, 6, 2),
|
||||||
|
(18, 5, 3),
|
||||||
|
(19, 7, 2),
|
||||||
|
(20, 1, 3),
|
||||||
|
(21, 8, 5),
|
||||||
|
(22, 3, 3),
|
||||||
|
(23, 10, 6),
|
||||||
|
(24, 8, 5),
|
||||||
|
(25, 5, 4),
|
||||||
|
(26, 0, 5),
|
||||||
|
(27, 0, 5),
|
||||||
|
(28, 6, 4),
|
||||||
|
(29, 9, 6),
|
||||||
|
(30, 8, 5),
|
||||||
|
(31, 3, 2),
|
||||||
|
(32, 9, 2),
|
||||||
|
(33, 4, 3),
|
||||||
|
(34, 2, 2),
|
||||||
|
(35, 9, 6),
|
||||||
|
(36, 10, 2),
|
||||||
|
(37, 3, 6),
|
||||||
|
(38, 2, 3),
|
||||||
|
(39, 0, 6),
|
||||||
|
(40, 1, 5),
|
||||||
|
(41, 3, 6),
|
||||||
|
(42, 5, 2),
|
||||||
|
(43, 3, 6),
|
||||||
|
(44, 3, 5),
|
||||||
|
(45, 1, 3),
|
||||||
|
(46, 7, 2),
|
||||||
|
(47, 0, 5),
|
||||||
|
(48, 1, 1),
|
||||||
|
(49, 3, 1),
|
||||||
|
(50, 1, 4);
|
||||||
|
|
||||||
|
-- --------------------------------------------------------
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `students`
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE `students` (
|
||||||
|
`id` int(11) NOT NULL,
|
||||||
|
`name` varchar(255) NOT NULL
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `students`
|
||||||
|
--
|
||||||
|
|
||||||
|
INSERT INTO `students` (`id`, `name`) VALUES
|
||||||
|
(1, 'Tasha Martin'),
|
||||||
|
(2, 'Shelly Watts'),
|
||||||
|
(3, 'Mariam Larson'),
|
||||||
|
(4, 'Uma Austin'),
|
||||||
|
(5, 'Malachi Hensley'),
|
||||||
|
(6, 'Mercedes Mcbride'),
|
||||||
|
(7, 'Raja Dominguez'),
|
||||||
|
(8, 'Craig Wooten'),
|
||||||
|
(9, 'Jasmine Hoffman'),
|
||||||
|
(10, 'Micah Salazar');
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Indexes for dumped tables
|
||||||
|
--
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Indexes for table `grades`
|
||||||
|
--
|
||||||
|
ALTER TABLE `grades`
|
||||||
|
ADD PRIMARY KEY (`gradeid`);
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Indexes for table `students`
|
||||||
|
--
|
||||||
|
ALTER TABLE `students`
|
||||||
|
ADD PRIMARY KEY (`id`);
|
||||||
|
|
||||||
|
--
|
||||||
|
-- AUTO_INCREMENT for dumped tables
|
||||||
|
--
|
||||||
|
|
||||||
|
--
|
||||||
|
-- AUTO_INCREMENT for table `grades`
|
||||||
|
--
|
||||||
|
ALTER TABLE `grades`
|
||||||
|
MODIFY `gradeid` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=51;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- AUTO_INCREMENT for table `students`
|
||||||
|
--
|
||||||
|
ALTER TABLE `students`
|
||||||
|
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=11;
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||||
|
```
|
||||||
|
If you type something (for example "John Smith"), then the SQL query will look like this:
|
||||||
|
```sql
|
||||||
|
SELECT students.name AS name, ROUND(AVG(grades.grade), 2) AS average FROM grades INNER JOIN students ON students.id = grades.studentid WHERE students.name LIKE '%John Smith%' GROUP BY grades.studentid;
|
||||||
|
```
|
||||||
|
And the web application will look up student names containing the substring "John Smith". With this example set of data, web application would return 0 results, because there are no matching entries.
|
||||||
|
|
||||||
|
The problem is the lack of database input sanitation. What happens, if the input was ``' OR 1=1#``?
|
||||||
|
The SQL query will then look like this:
|
||||||
|
```sql
|
||||||
|
SELECT students.name AS name, ROUND(AVG(grades.grade), 2) AS average FROM grades INNER JOIN students ON students.id = grades.studentid WHERE students.name LIKE '%' OR 1=1#%' GROUP BY grades.studentid;
|
||||||
|
```
|
||||||
|
There will be then ``OR 1=1`` in the query, which is always true, thus returning all the students' average grades. The result page will display only one result due to removal of ``GROUP BY`` clause. The "#" character makes rest of the query a comment.
|
||||||
|
|
||||||
|
Does the SQL query above look a bit dangerous? What if the input was ``asd' UNION SELECT @@hostname, @@version #``?
|
||||||
|
The SQL query will then look like this:
|
||||||
|
```sql
|
||||||
|
SELECT students.name AS name, ROUND(AVG(grades.grade), 2) AS average FROM grades INNER JOIN students ON students.id = grades.studentid WHERE students.name LIKE '%asd' UNION SELECT @@hostname, @@version #%' GROUP BY grades.studentid;
|
||||||
|
```
|
||||||
|
Then the server hostname and DBMS version will be leaked.
|
||||||
|
|
||||||
|
![Data is leaked via SQL injection.](/images/sqli-leaked-data.png)
|
||||||
|
|
||||||
|
Some websites even allow batched SQL statements like with ``asd'; DROP TABLE students#`` input. The SQL query will then look like this:
|
||||||
|
```sql
|
||||||
|
SELECT students.name AS name, ROUND(AVG(grades.grade), 2) AS average FROM grades INNER JOIN students ON students.id = grades.studentid WHERE students.name LIKE '%asd'; DROP TABLE students#%' GROUP BY grades.studentid;
|
||||||
|
```
|
||||||
|
Then the *students* table will be deleted. **The data will be lost.**
|
||||||
|
|
||||||
|
SQL injection using batched SQL statements is summarized in [this xkcd comic](https://xkcd.com/327/):
|
||||||
|
![xkcd Comic #327: Exploits of a Mom](/images/xkcd-exploits-of-a-mom.png)
|
||||||
|
|
||||||
|
**Oh no! Hackers will hack that site pretty easily! What you can do?**
|
||||||
|
## SQL injection prevention
|
||||||
|
Fortunately there are many ways of protection against SQL injection.
|
||||||
|
|
||||||
|
If you want to use escaping, then it depends on programming language you use. In PHP and *mysqli* driver, you can use ``mysqli_real_escape_string($conn, $input)`` function. In server-side JavaScript and *mysql* library, you can use ``mysql.escape(input)`` function (where ``mysql`` is a instance of *mysql* module).
|
||||||
|
|
||||||
|
There is an example of fixed web application (using *mysqli_real_escape_string* function):
|
||||||
|
```php
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Student average grades</title>
|
||||||
|
<meta name="viewport" content="width=device-width, inital-scale=1.0">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<?php
|
||||||
|
mysqli_report(MYSQLI_REPORT_OFF);
|
||||||
|
$conn = mysqli_connect('localhost','dbuser','dbpassword','studentdb') or die("Can't connect to the database!");
|
||||||
|
|
||||||
|
//SQL query error handling
|
||||||
|
function handle_sql_error($conn) {
|
||||||
|
mysqli_close($conn);
|
||||||
|
die("There was a problem with SQL query.");
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<h1>Student average grades</h1>
|
||||||
|
<?php
|
||||||
|
$result = mysqli_query($conn, "SELECT COUNT(id) AS count FROM students;") or handle_sql_error($conn);
|
||||||
|
print "<p>There are ".mysqli_fetch_assoc($result)["count"]." students in the student database.</p>";
|
||||||
|
?>
|
||||||
|
<form method="post">
|
||||||
|
<label for="q">Search query:</label>
|
||||||
|
<input type="text" name="q" id="q">
|
||||||
|
<input type="submit" value="Search!">
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
if(isset($_POST["q"]) and trim($_POST["q"]) != "") {
|
||||||
|
$q = mysqli_real_escape_string($conn, str_replace(array('!','%','_','['), array('!!','!%','!_', '!['), $_POST["q"])); //So that "%" will not break searches
|
||||||
|
$result = mysqli_query($conn, "SELECT students.name AS name, ROUND(AVG(grades.grade), 2) AS average FROM grades INNER JOIN students ON students.id = grades.studentid WHERE students.name LIKE '%$q%' GROUP BY grades.studentid;") or handle_sql_error($conn);
|
||||||
|
$count = mysqli_num_rows($result);
|
||||||
|
print "<p><b>Result count: $count</b></p>";
|
||||||
|
print "<ul>";
|
||||||
|
while($arr = mysqli_fetch_assoc($result)) {
|
||||||
|
//We don't make it vulnerable to XSS for now...
|
||||||
|
$studentname = htmlentities($arr["name"]);
|
||||||
|
$studentaverage = htmlentities($arr["average"]);
|
||||||
|
print " <li><b>$studentname</b> — $studentaverage</li>";
|
||||||
|
}
|
||||||
|
print "</ul>";
|
||||||
|
mysqli_close($conn);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, *mysqli_real_escape_string* function escapes various characters causing database problems. Escaped characters will then be treated as regular characters instead of string terminators, thus mitigating SQL injection vulnerability.
|
||||||
|
|
||||||
|
But there is one problem, it doesn't work for number inputs. In this case, you can validate the input using a regular expression, like this:
|
||||||
|
```php
|
||||||
|
if(preg_match("/^[0-9]+$/", $input)) {
|
||||||
|
//Do SQL queries...
|
||||||
|
} else {
|
||||||
|
die("Invalid input!");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
This code checks using a regular expression if the input is a number. If it's not a number, then it rejects the input with an *Invalid input!* error message.
|
||||||
|
|
||||||
|
You can also use prepared statements. Prepared statements separates SQL from data, effectively mitigating SQL injection vulnerability. Example code:
|
||||||
|
```php
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Student average grades</title>
|
||||||
|
<meta name="viewport" content="width=device-width, inital-scale=1.0">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<?php
|
||||||
|
mysqli_report(MYSQLI_REPORT_OFF);
|
||||||
|
$conn = mysqli_connect('localhost','dbuser','dbpassword','studentdb') or die("Can't connect to the database!");
|
||||||
|
|
||||||
|
//SQL query error handling
|
||||||
|
function handle_sql_error($conn) {
|
||||||
|
mysqli_close($conn);
|
||||||
|
die("There was a problem with SQL query.");
|
||||||
|
}
|
||||||
|
function handle_stmt_error($stmt, $conn) {
|
||||||
|
mysqli_stmt_close($stmt);
|
||||||
|
handle_sql_error($conn);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<h1>Student average grades</h1>
|
||||||
|
<?php
|
||||||
|
$result = mysqli_query($conn, "SELECT COUNT(id) AS count FROM students;") or handle_sql_error($conn);
|
||||||
|
print "<p>There are ".mysqli_fetch_assoc($result)["count"]." students in the student database.</p>";
|
||||||
|
?>
|
||||||
|
<form method="post">
|
||||||
|
<label for="q">Search query:</label>
|
||||||
|
<input type="text" name="q" id="q">
|
||||||
|
<input type="submit" value="Search!">
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
if(isset($_POST["q"]) and trim($_POST["q"]) != "") {
|
||||||
|
$q = str_replace(array('!','%','_','['), array('!!','!%','!_', '!['), $_POST["q"]); //So that "%" will not break searches
|
||||||
|
$stmt = mysqli_prepare($conn, "SELECT students.name AS name, ROUND(AVG(grades.grade), 2) AS average FROM grades INNER JOIN students ON students.id = grades.studentid WHERE students.name LIKE CONCAT('%',?,'%') GROUP BY grades.studentid;") or handle_stmt_error($stmt, $conn);
|
||||||
|
mysqli_stmt_bind_param($stmt, 's', $q) or handle_stmt_error($stmt, $conn);
|
||||||
|
mysqli_stmt_execute($stmt) or handle_stmt_error($stmt, $conn);
|
||||||
|
$result = mysqli_stmt_get_result($stmt);
|
||||||
|
$count = mysqli_num_rows($result);
|
||||||
|
print "<p><b>Result count: $count</b></p>";
|
||||||
|
print "<ul>";
|
||||||
|
while($arr = mysqli_fetch_assoc($result)) {
|
||||||
|
//We don't make it vulnerable to XSS for now...
|
||||||
|
$studentname = htmlentities($arr["name"]);
|
||||||
|
$studentaverage = htmlentities($arr["average"]);
|
||||||
|
print " <li><b>$studentname</b> — $studentaverage</li>";
|
||||||
|
}
|
||||||
|
print "</ul>";
|
||||||
|
mysqli_stmt_close($stmt);
|
||||||
|
mysqli_close($conn);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
In this example, SQL query and search query data are kept separated, thus mitigating SQL injection.
|
||||||
|
|
||||||
|
**With these mitigations, your website will be harder for hackers to break.**
|
||||||
|
|
||||||
|
_**UPDATE:** PHP code edited to not exhaust MySQL/MariaDB database connection limit._
|
BIN
source/images/covers/How-to-run-a-Git-server-on-GNU-Linux.png
Normal file
After Width: | Height: | Size: 300 KiB |
After Width: | Height: | Size: 64 KiB |
After Width: | Height: | Size: 131 KiB |
BIN
source/images/covers/SVR-JS-3-4-x-LTS-versions-now-EOL.png
Normal file
After Width: | Height: | Size: 181 KiB |
BIN
source/images/covers/SVR-JS-now-supports-FastCGI.png
Normal file
After Width: | Height: | Size: 314 KiB |
BIN
source/images/covers/SVR-JS-to-drop-all-DorianTech-remnants.png
Normal file
After Width: | Height: | Size: 1.5 MiB |
BIN
source/images/covers/Welcome-to-our-new-SVR-JS-forum.png
Normal file
After Width: | Height: | Size: 310 KiB |
BIN
source/images/covers/What-is-SQL-injection-How-to-prevent-it.png
Normal file
After Width: | Height: | Size: 113 KiB |
BIN
source/images/doriantech-http-server.png
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
source/images/doriantech-removed-git.png
Normal file
After Width: | Height: | Size: 242 KiB |
BIN
source/images/forum.png
Normal file
After Width: | Height: | Size: 240 KiB |
BIN
source/images/git-blame.png
Normal file
After Width: | Height: | Size: 267 KiB |
BIN
source/images/git-branch.png
Normal file
After Width: | Height: | Size: 65 KiB |
BIN
source/images/git-cant-push.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
source/images/git-clone.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
source/images/git-commit.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
source/images/git-diff.png
Normal file
After Width: | Height: | Size: 132 KiB |
BIN
source/images/git-help.png
Normal file
After Width: | Height: | Size: 157 KiB |
BIN
source/images/git-init.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
source/images/git-log.png
Normal file
After Width: | Height: | Size: 129 KiB |
BIN
source/images/git-merge-conflict-marks.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
source/images/git-merge-conflict.png
Normal file
After Width: | Height: | Size: 128 KiB |
BIN
source/images/git-server-git-protocol-test.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
source/images/git-server-git-protocol.png
Normal file
After Width: | Height: | Size: 99 KiB |
BIN
source/images/git-server-http-protocol-empty-dirlisting.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
source/images/git-server-http-protocol-gitweb-test.png
Normal file
After Width: | Height: | Size: 271 KiB |
BIN
source/images/git-server-http-protocol-test.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
source/images/git-server-http-protocol-welcome.png
Normal file
After Width: | Height: | Size: 154 KiB |
BIN
source/images/git-server-http-protocol.png
Normal file
After Width: | Height: | Size: 469 KiB |
BIN
source/images/git-server-initial-setup.png
Normal file
After Width: | Height: | Size: 204 KiB |
BIN
source/images/git-server-ssh-protocol-keyprep.png
Normal file
After Width: | Height: | Size: 133 KiB |
BIN
source/images/git-server-ssh-protocol-test.png
Normal file
After Width: | Height: | Size: 71 KiB |
BIN
source/images/git-server-ssh-protocol.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
source/images/git-status.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
source/images/hello-server.png
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
source/images/jsgi-error.png
Normal file
After Width: | Height: | Size: 186 KiB |
BIN
source/images/jsgi-hello-world.png
Normal file
After Width: | Height: | Size: 192 KiB |
BIN
source/images/php-cgi-info.png
Normal file
After Width: | Height: | Size: 435 KiB |
BIN
source/images/php-cgi-warning.png
Normal file
After Width: | Height: | Size: 312 KiB |
BIN
source/images/scgi-client-error.png
Normal file
After Width: | Height: | Size: 138 KiB |
BIN
source/images/scgi-hello-world.png
Normal file
After Width: | Height: | Size: 105 KiB |
BIN
source/images/sqli-leaked-data.png
Normal file
After Width: | Height: | Size: 118 KiB |
BIN
source/images/svrjs-on-bun-404.png
Normal file
After Width: | Height: | Size: 94 KiB |
BIN
source/images/svrjs-on-bun-console.png
Normal file
After Width: | Height: | Size: 133 KiB |
BIN
source/images/svrjs-on-bun-startpage.png
Normal file
After Width: | Height: | Size: 150 KiB |
BIN
source/images/unexcepted-execption.png
Normal file
After Width: | Height: | Size: 195 KiB |
BIN
source/images/wordpress-admin.png
Normal file
After Width: | Height: | Size: 382 KiB |
BIN
source/images/wordpress-homepage.png
Normal file
After Width: | Height: | Size: 308 KiB |
BIN
source/images/wordpress-setup.png
Normal file
After Width: | Height: | Size: 348 KiB |
BIN
source/images/xkcd-exploits-of-a-mom.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
source/images/zsoiebook.png
Normal file
After Width: | Height: | Size: 93 KiB |
20
svrjs-config.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"port": 3000,
|
||||||
|
"nonStandardCodes": [
|
||||||
|
{
|
||||||
|
"url": "/submit_url.txt",
|
||||||
|
"scode": 403
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dontCompress": [
|
||||||
|
"/.*\\.ipxe$/",
|
||||||
|
"/.*\\.img$/",
|
||||||
|
"/.*\\.iso$/",
|
||||||
|
"/.*\\.gz$/",
|
||||||
|
"/.*\\.webp$/",
|
||||||
|
"/.*\\.jpg$/",
|
||||||
|
"/.*\\.png$/",
|
||||||
|
"/.*\\.woff$/"
|
||||||
|
],
|
||||||
|
"wwwroot": "../public"
|
||||||
|
}
|