Compare commits

...

7 Commits

7 changed files with 154 additions and 7 deletions

View File

@ -1,6 +1,8 @@
@page "/blog/{year:int}/{month:int}/{day:int}/{slug}"
@using Humanizer
@using OliverBooth.Data.Blog
@using OliverBooth.Services
@inject IBlogPostService BlogPostService
@model Article
@if (Model.Post is not { } post)
@ -24,6 +26,21 @@
</ol>
</nav>
@switch (post.Visibility)
{
case BlogPostVisibility.Private:
<div class="alert alert-danger" role="alert">
This post is private and can only be viewed by those with the password.
</div>
break;
case BlogPostVisibility.Unlisted:
<div class="alert alert-warning" role="alert">
This post is unlisted and can only be viewed by those with the link.
</div>
break;
}
<h1>@post.Title</h1>
<p class="text-muted">
<img class="blog-author-icon" src="@author.AvatarUrl" alt="@author.DisplayName">
@ -54,6 +71,41 @@
<hr>
<div class="row">
<div class="col-sm-12 col-md-6">
@if (BlogPostService.GetPreviousPost(post) is { } previousPost)
{
<small>Previous Post</small>
<p class="lead">
<a asp-page="Article"
asp-route-year="@previousPost.Published.Year.ToString("0000")"
asp-route-month="@previousPost.Published.Month.ToString("00")"
asp-route-day="@previousPost.Published.Day.ToString("00")"
asp-route-slug="@previousPost.Slug">
@previousPost.Title
</a>
</p>
}
</div>
<div class="col-sm-12 col-md-6" style="text-align: right;">
@if (BlogPostService.GetNextPost(post) is { } nextPost)
{
<small>Next Post</small>
<p class="lead">
<a asp-page="Article"
asp-route-year="@nextPost.Published.Year.ToString("0000")"
asp-route-month="@nextPost.Published.Month.ToString("00")"
asp-route-day="@nextPost.Published.Day.ToString("00")"
asp-route-slug="@nextPost.Slug">
@nextPost.Title
</a>
</p>
}
</div>
</div>
<hr>
@if (post.EnableComments)
{
<div id="disqus_thread"></div>
@ -70,7 +122,7 @@
s.async = true;
s.type = "text/javascript";
s.src = "https://oliverbooth-dev.disqus.com/embed.js";
s.setAttribute("data-timestamp", + new Date());
s.setAttribute("data-timestamp", (+ new Date()).toString());
(d.head || d.body).appendChild(s);
})();
</script>

View File

@ -40,12 +40,11 @@ public class Index : PageModel
{
var route = new
{
area = "blog",
year = post.Published.ToString("yyyy"),
month = post.Published.ToString("MM"),
day = post.Published.ToString("dd"),
slug = post.Slug
};
return Redirect(Url.Page("/Article", route)!);
return Redirect(Url.Page("/Blog/Article", route)!);
}
}

View File

@ -7,16 +7,14 @@
My primary focus is C#, though I have dabbled in several other languages such as Java, Kotlin, VB, C/C++, Python,
and others. Over the years I've built up a collection of projects. Some of which I'm extremely proud of, and others
I've quietly abandoned and buried. I'm currently working on a few projects that I hope to release in the near
future, but in the meantime, feel free to check out some of my <a asp-page="/Projects/Index">previous work.</a>
future, but in the meantime, feel free to check out some of my <a asp-page="/Projects/Index">previous work</a>.
</p>
<p>
I've also written a few <a asp-page="/Tutorials/Index">tutorials</a> on various topics, usually involving
information not readily available elsewhere. I hope you find them useful. On occasion, I also write about other
topics that I find interesting, such as
<a asp-page="/Blog/Index">
my thoughts on the state of the world or the tech industry
</a>.
<a asp-page="/Blog/Index">my thoughts on the state of the world or the tech industry</a>.
</p>
<p>

View File

@ -0,0 +1,47 @@
@page "/psa/binaryformatter"
<div class="alert alert-danger">
<h2 class="alert-heading">⚠️ Stop! This application is unsafe!</h2>
<p>
This application is using an insecure method to read and write data, and needs to be updated
<em>immediately</em>.
</p>
</div>
<div class="alert alert-warning">
<h4 class="alert-heading">I'm a user, what does this mean?</h4>
<p>
If you are seeing this message, it means you loaded a payload that I crafted to exploit this vulnerability. Be
fortunate, because I could have done much worse including stealing your data or installing malware on your
computer.
</p>
<p>
If you're seeing this because you loaded my data from a game, this means it's possible for an attacker to craft
a save file that can, for example, steal your Steam credentials and send them to a remote server. Just because
you loaded - what seemed to be - a save file!
</p>
<hr/>
<p>
<strong>Do not</strong> load any more data into this application until the developer has addressed this issue.
</p>
</div>
<div class="alert alert-info">
<h4 class="alert-heading">I'm a developer, can you explain more?</h4>
<p>
<code>BinaryFormatter</code> is a .NET class that is used to serialize and deserialize data such as game saves
or configuration files. However, it was discovered that this class is vulnerable to remote code execution when
deserializing untrusted data.
</p>
<p>
<strong>Please update your application to use a different serialization method.</strong>
</p>
<hr/>
<p>
For more information, please read the
<a href="https://learn.microsoft.com/en-us/dotnet/standard/serialization/binaryformatter-security-guide">
official security notice
</a>
from Microsoft.
</p>
</div>

View File

@ -0,0 +1,17 @@
@page "/tutorials"
@{
ViewData["Title"] = "Tutorials";
}
<h1 class="display-4">Tutorials</h1>
<p class="lead">Coming Soon</p>
<p>
Due to Unity's poor corporate decision-making, I'm left in a position where I find it infeasible to write Unity
tutorials. I plan to write tutorials for things like Unreal and MonoGame as I learn them, and C# tutorials are
still on the table for sure. But tutorials take a lot of time and effort, so unfortunately it may be a while before
I can get around to publishing them.
</p>
<p>
I'm sorry for the inconvenience, but I hope you understand my position. Watch this space! New tutorials will be
coming. If you have any questions or requests, please feel free to <a asp-page="/Contact/Other">contact me</a>.
</p>

View File

@ -66,6 +66,26 @@ internal sealed class BlogPostService : IBlogPostService
.ToArray().Select(CacheAuthor).ToArray();
}
/// <inheritdoc />
public IBlogPost? GetNextPost(IBlogPost blogPost)
{
using BlogContext context = _dbContextFactory.CreateDbContext();
return context.BlogPosts
.Where(p => p.Visibility == BlogPostVisibility.Published)
.OrderBy(post => post.Published)
.FirstOrDefault(post => post.Published > blogPost.Published);
}
/// <inheritdoc />
public IBlogPost? GetPreviousPost(IBlogPost blogPost)
{
using BlogContext context = _dbContextFactory.CreateDbContext();
return context.BlogPosts
.Where(p => p.Visibility == BlogPostVisibility.Published)
.OrderByDescending(post => post.Published)
.FirstOrDefault(post => post.Published < blogPost.Published);
}
/// <inheritdoc />
public string RenderExcerpt(IBlogPost post, out bool wasTrimmed)
{

View File

@ -34,6 +34,20 @@ public interface IBlogPostService
/// <returns>A collection of blog posts.</returns>
IReadOnlyList<IBlogPost> GetBlogPosts(int page, int pageSize = 10);
/// <summary>
/// Returns the next blog post from the specified blog post.
/// </summary>
/// <param name="blogPost">The blog post whose next post to return.</param>
/// <returns>The next blog post from the specified blog post.</returns>
IBlogPost? GetNextPost(IBlogPost blogPost);
/// <summary>
/// Returns the previous blog post from the specified blog post.
/// </summary>
/// <param name="blogPost">The blog post whose previous post to return.</param>
/// <returns>The previous blog post from the specified blog post.</returns>
IBlogPost? GetPreviousPost(IBlogPost blogPost);
/// <summary>
/// Renders the excerpt of the specified blog post.
/// </summary>