When you’ve got larger Sitercore installs where there is a team of graphic artists dedicated to maintaining the media library you can get cases of content editors linking to images that haven’t been published yet.

Off the back of those kind of situations I wrote a custom validator to test if the item is published on any of the target databases. It could easily be repurposed for any of the link fields and the warning could be raised to an error if needed.

using System.Collections.Generic;
using Sitecore.Configuration;
using Sitecore.Data;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
using Sitecore.Data.Validators;
using Sitecore.Diagnostics;
using Sitecore.Globalization;
using Sitecore.SecurityModel;
using Sitecore.Shell.Applications.ContentEditor;
namespace example
{
    public class UnpublishedMediaValidator : StandardValidator
    {
        protected override ValidatorResult Evaluate()
        {
            ItemUri itemUri = base.ItemUri;
            if (itemUri != null)
            {
                Field field = base.GetField();
                if (field == null)
                    return ValidatorResult.Valid;
                string str = field.Value;
                if (string.IsNullOrEmpty(str))
                    return ValidatorResult.Valid;
                if (string.Compare(str, "<image />") == 0)
                    return ValidatorResult.Valid;
                string itemId = new XmlValue(str, "image").GetAttribute("mediaid");
                if (string.IsNullOrEmpty(itemId))
                    return ValidatorResult.Valid;
                Database currentDatabase = Factory.GetDatabase(itemUri.DatabaseName);
                Assert.IsNotNull(currentDatabase, itemUri.DatabaseName);
                Item localItem = currentDatabase.GetItem(itemId);
                if (localItem == null)
                    return ValidatorResult.Valid;
                foreach(Database targetDatabase in GetTargets(localItem))
                {
                    Item targetItem = targetDatabase.GetItem(localItem.ID);
                    if(targetItem == null)
                    {
                        base.Text = Translate.Text("The field \"{0}\" links to an unpublished item.", new object[] { field.DisplayName });
                        return base.GetFailedResult(ValidatorResult.Warning);
                    }
                }
            }
            return ValidatorResult.Valid;
        }
        private List<Database> GetTargets(Item item)
        {
            using (new SecurityDisabler())
            {
                Item item2 = item.Database.Items["/sitecore/system/publishing targets"];
                if (item2 != null)
                {
                    List<Database> list = new List<Database>();
                    foreach (Item item3 in item2.Children)
                    {
                        string name = item3["Target database"];
                        if (name.Length > 0)
                        {
                            Database database = Factory.GetDatabase(name, false);
                            if (database != null)
                            {
                                list.Add(database);
                            }
                            else
                            {
                                Log.Warn("Unknown database in PublishAction: " + name, this);
                            }
                        }
                    }
                    return list;
                }
            }
            return new List<Database>();
        }
        protected override ValidatorResult GetMaxValidatorResult()
        {
            return base.GetFailedResult(ValidatorResult.Warning);
        }
        public override string Name
        {
            get
            {
                return "Unpublished Items.";
            }
        }
    }
}

Implementing it is fairly straight forward, just compile the code, create a new Validation Rule item in /sitecore/system/Settings/Validation Rules/Field Rules/ (namespace.class,assembly) and add to the Validator Bar rule on the Image Field Type in /sitecore/system/Settings/Validation Rules/Field Types/Image

As I said it would be easy to repurpose this for any link type, useful in conjunction with more complex workflows.